ossp-pkg/lmtp2nntp/lmtp2nntp.c
1.5
/*
* mail2nntp.c
*
* The mail2nntp program reads mail from stdin and posts it
* to one or more newsgroups using NNTP. It delivers the mes-
* sage immediately or fails. If queuing is desired it can
* operate as a LMTP server.
*
* The OSSP Project, Cable & Wireless Deutschland GmbH
* Thomas Lotterer, <thomas.lotterer@cw.com>
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
/* third party */
#include "str.h"
#include "argz.h"
/* own headers */
#include "lmtp.h"
#ifndef FALSE
#define FALSE (1 != 1)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#define ERR_EXECUTION -1
#define ERR_DELIVERY -2
#define MESSAGE_MAXLEN 8*1024*1024
extern void lmtp_debug_dumplmtp(lmtp_t *lmtp);
static ssize_t trace_read(int d, void *buf, size_t nbytes);
static ssize_t trace_write(int d, const void *buf, size_t nbytes);
static lmtp_rc_t lmtp_cb_lhlo (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
static lmtp_rc_t lmtp_cb_mail (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
static lmtp_rc_t lmtp_cb_rcpt (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
static lmtp_rc_t lmtp_cb_data (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
static lmtp_rc_t lmtp_cb_noop (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
static lmtp_rc_t lmtp_cb_rset (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
static lmtp_rc_t lmtp_cb_quit (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx);
typedef struct {
int option_verbose;
int option_tracing;
int option_mode;
char *azNewsgroups;
size_t asNewsgroups;
char *rfc822message;
int saw_lhlo;
char *azRcpt;
size_t asRcpt;
} lmtp2nntp_t;
enum {
MODE_ONCE,
MODE_MANY
};
/*
* tracing
*/
ssize_t trace_read(int d, void *buf, size_t nbytes)
{
ssize_t rc;
int tracefile;
rc = read(d, buf, nbytes);
if ((tracefile = open("/tmp/t", O_CREAT|O_WRONLY|O_APPEND, 0664)) != -1) {
write(tracefile, buf, rc);
close(tracefile);
}
return rc;
}
ssize_t trace_write(int d, const void *buf, size_t nbytes)
{
ssize_t rc;
int tracefile;
rc = write(d, buf, nbytes);
if ((tracefile = open("/tmp/t", O_CREAT|O_WRONLY|O_APPEND, 0664)) != -1) {
write(tracefile, buf, rc);
close(tracefile);
}
return rc;
}
/*
* print usage information
*/
static void usage(char *command)
{
fprintf(stderr,
"USAGE: %s [-p protocol] [-l logtarget] "
"[-h host[:port]] [-m mode] [-t] [-v] newsgroup [newsgroup ...]\n",
command);
return;
}
int main(int argc, char **argv)
{
int rc = 0;
lmtp_t *lmtp;
lmtp_io_t lmtp_io;
lmtp2nntp_t *ctx;
int i; /* general purpose scratch int, index ... */
char *progname = argv[0];
/* create application context */
if ((ctx = (lmtp2nntp_t *)malloc(sizeof(lmtp2nntp_t))) == NULL)
exit(ERR_EXECUTION);
ctx->option_verbose = FALSE;
ctx->option_tracing = FALSE;
ctx->option_mode = MODE_MANY;
ctx->azNewsgroups = NULL;
ctx->asNewsgroups = 0;
ctx->rfc822message = NULL;
ctx->saw_lhlo = FALSE;
ctx->azRcpt = NULL;
ctx->asRcpt = 0;
/* read in the arguments */
{
char buf[1000];
int bufused = 0;
int tracefile;
for (i=0; i<argc; i++)
bufused+=sprintf(buf+bufused, "[%d]=\"%s\"\n", i, argv[i]);
if ((tracefile = open("/tmp/t", O_CREAT|O_WRONLY|O_APPEND, 0664)) != -1) {
write(tracefile, buf, bufused);
close(tracefile);
}
}
while ((i = getopt(argc, argv, "p:l:h:m:tvo:c:i:t:")) != -1) {
switch (i) {
case 'p': // -p protocol
break;
case 'l': // -l logtarget
break;
case 'o': // FIXME
break;
case 'c': // FIXME
break;
case 'i': // FIXME
break;
case 't': // FIXME
break;
case 'h': // -h host[:port]
break;
case 'm': // -m mode
if (strcasecmp(argv[optind], "once") == 0)
ctx->option_mode = MODE_ONCE;
else if (strcasecmp(argv[optind], "many") == 0)
ctx->option_mode = MODE_MANY;
else {
fprintf(stderr, "%s:Error: Invalid mode \"%s\" to option -m\n", progname, argv[optind]);
exit(ERR_EXECUTION);
}
break;
case 'd': // -t (tracing)
ctx->option_tracing = TRUE;
break;
case 'v': // -v (verbose)
ctx->option_verbose = TRUE;
break;
case '?':
default:
usage(progname);
exit(ERR_EXECUTION);
}
}
/* remaining arguments are newsgroup names */
for (i = optind; i < argc; i++)
argz_add(&ctx->azNewsgroups, &ctx->asNewsgroups, argv[i]);
/* initialize LMTP context */
lmtp_io.read = trace_read;
lmtp_io.write = trace_write;
if ((lmtp = lmtp_create(STDIN_FILENO, STDOUT_FILENO, &lmtp_io)) == NULL) {
fprintf(stderr, "%s:Error: Unable to initialize LMTP library\n", progname);
exit(ERR_EXECUTION);
}
lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, ctx, NULL, NULL);
lmtp_register(lmtp, "MAIL", lmtp_cb_mail, ctx, NULL, NULL);
lmtp_register(lmtp, "RCPT", lmtp_cb_rcpt, ctx, NULL, NULL);
lmtp_register(lmtp, "DATA", lmtp_cb_data, ctx, NULL, NULL);
lmtp_register(lmtp, "NOOP", lmtp_cb_noop, ctx, NULL, NULL);
lmtp_register(lmtp, "RSET", lmtp_cb_rset, ctx, NULL, NULL);
lmtp_register(lmtp, "QUIT", lmtp_cb_quit, ctx, NULL, NULL);
/* loop for LMTP protocol */
lmtp_loop(lmtp);
return 0;
}
static lmtp_rc_t lmtp_cb_lhlo(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx)
{
/*
* RFC821 [excerpt] 4.1. SMTP COMMANDS
* 4.1.1. COMMAND SEMANTICS, HELO
* This command and an OK reply to it confirm that both the sender-SMTP
* and the receiver-SMTP are in the initial state, that is, there is no
* transaction in progress and all state tables and buffers are cleared.
*
*/
lmtp_rc_t rc = LMTP_OK;
lmtp_res_t res;
lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
//FIXME use rset() not lmtp_cb_rset()
ctx->saw_lhlo = TRUE;
res.statuscode = "250";
res.dsncode = NULL; /* DSN not used for greeting */
res.statusmsg = "ENHANCEDSTATUSCODES\nDSN\nPIPELINING\n8BITMIME";
res.statusmsg = "ENHANCEDSTATUSCODES\nDSN\n8BITMIME";
/*
* RFC2034 = EHANCEDSTATUSCODES
* RFC1894 = DSN
* RFC1854 = PIPELINING
* RFC1652 = 8BITMIME
*/
lmtp_response(lmtp, &res);
return rc;
}
static lmtp_rc_t lmtp_cb_mail(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx)
{
lmtp_res_t res;
lmtp_rc_t rc = LMTP_OK;
lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
if (ctx->saw_lhlo == TRUE) {
res.statuscode = "250";
res.dsncode = "2.1.0";
res.statusmsg = "Sender ok FIXME";
lmtp_response(lmtp, &res);
} else {
res.statuscode = "553";
res.dsncode = "5.1.8";
res.statusmsg = "friendly people say LHLO to open a transmission channel";
lmtp_response(lmtp, &res);
}
return rc;
}
static lmtp_rc_t lmtp_cb_rcpt(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx)
{
lmtp_rc_t rc = LMTP_OK;
lmtp_res_t res;
lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
argz_add(&ctx->azRcpt, &ctx->asRcpt, req->msg);
res.statuscode = "250";
res.dsncode = "2.1.5";
res.statusmsg = "Recipient ok FIXME";
lmtp_response(lmtp, &res);
return rc;
}
static lmtp_rc_t lmtp_cb_data(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx)
{
lmtp_res_t res;
lmtp_rc_t rc = LMTP_OK;
lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
char *buf;
char errorstring[128];
char *rcpt;
res.statuscode = "354";
res.dsncode = NULL; /* DSN not used for data */
res.statusmsg = "Enter mail, end with \".\" on a line by itself";
lmtp_response(lmtp, &res);
rc = lmtp_readmsg(lmtp, &buf, MESSAGE_MAXLEN);
rcpt = NULL;
while ((rcpt = argz_next(ctx->azRcpt, ctx->asRcpt, rcpt)) != NULL)
{
if(rc == LMTP_OK) {
res.statuscode = "250";
res.dsncode = "2.0.0";
// res.statusmsg = "Message accepted for delivery";
str_format(errorstring, sizeof(errorstring),
"Message accepted for delivery to %s", rcpt);
res.statusmsg = errorstring;
} else if (rc == LMTP_ERR_OVERFLOW) {
res.statuscode = "500";
res.dsncode = "5.0.0";
res.statusmsg = "Message accepted for delivery";
} else if (rc == LMTP_ERR_SYSTEM) {
res.statuscode = "500";
res.dsncode = "5.0.0";
str_format(errorstring, sizeof(errorstring),
"Message accepted for delivery %s", strerror(errno));
res.statusmsg = errorstring;
}
lmtp_response(lmtp, &res);
}
return rc;
}
static lmtp_rc_t lmtp_cb_noop(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx)
{
lmtp_res_t res;
lmtp_rc_t rc = LMTP_OK;
res.statuscode = "250";
res.dsncode = "2.0.0";
res.statusmsg = "OK";
lmtp_response(lmtp, &res);
return rc;
}
static lmtp_rc_t lmtp_cb_rset(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx)
{
lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
lmtp_res_t res;
lmtp_rc_t rc = LMTP_OK;
if (ctx->azRcpt != NULL) {
free(ctx->azRcpt);
ctx->azRcpt = NULL;
ctx->asRcpt = 0;
}
res.statuscode = "250";
res.dsncode = "2.0.0";
res.statusmsg = "Reset state.";
lmtp_response(lmtp, &res);
return rc;
}
static lmtp_rc_t lmtp_cb_quit(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx)
{
lmtp_res_t res;
lmtp_rc_t rc = LMTP_EOF;
res.statuscode = "221";
res.dsncode = "2.0.0";
res.statusmsg = "Closing connection.";
lmtp_response(lmtp, &res);
return rc;
}