Index: ossp-pkg/lmtp2nntp/00TODO RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/00TODO,v rcsdiff -q -kk '-r1.3' '-r1.4' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/00TODO,v' 2>/dev/null --- 00TODO 2001/07/23 12:14:06 1.3 +++ 00TODO 2001/07/24 11:32:06 1.4 @@ -46,3 +46,15 @@ rfc822 header parsing from petidomo string functions from http://www.engelschall.com/sw/str + +o posting@news.de.cw.net + Newsgroup: cw.alert + +o posting+cw.alert@news.de.cw.net + +o cw.alert@news.de.cw.net + cw.alert: "|lmtp2nntp ..." + +o cw.alert@news.de.cw.net + cw.alert: "|lmtp2nntp ... -d cw.alert" + Index: ossp-pkg/lmtp2nntp/lmtp.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.c,v rcsdiff -q -kk '-r1.2' '-r1.3' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.c,v' 2>/dev/null --- lmtp.c 2001/07/23 12:14:06 1.2 +++ lmtp.c 2001/07/24 11:32:07 1.3 @@ -1,67 +1,21 @@ +/* standard headers */ #include #include #include #include #include +/* third-party headers */ +// #include "str.h" + +/* own headers */ #include "lmtp.h" #include "lmtp_p.h" -lmtp_rc_t lmtp_cb_default(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); -lmtp_rc_t lmtp_cb_lhlo(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); -lmtp_rc_t lmtp_cb_quit(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); - int verbindex(lmtp_t *lmtp, char *verb); +lmtp_rc_t lmtp_cb_default(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); -/* - * test a function - * - */ - -void lmtp_debug_dumplmtp(lmtp_t *lmtp); -void test(void) -{ - lmtp_t *lmtp; - lmtp_rc_t rc; - lmtp_req_t req; - lmtp_res_t res; -#define BUFLEN 100 - char buf[BUFLEN]; - - - printf("DEBUG: 01 lmtp_create\n"); lmtp = lmtp_create(STDIN_FILENO, STDOUT_FILENO, NULL); - // printf("DEBUG: 02\n"); lmtp_debug_dumplmtp(lmtp); - printf("DEBUG: 03 lmtp_register\n"); lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, NULL, NULL, NULL); - printf("DEBUG: 03 lmtp_register\n"); lmtp_register(lmtp, "QUIT", lmtp_cb_quit, NULL, NULL, NULL); - // printf("DEBUG: 04\n"); lmtp_debug_dumplmtp(lmtp); - - /* - printf("DEBUG: 05 lmtp_response\n"); res.statuscode="123"; - res.dsncode="1.2.3"; - res.statusmsg="Hello,\nthis is a\nmultiline\nmessage"; - lmtp_response(lmtp, &res); - */ - - /* - do { - rc = lmtp_readline(lmtp, buf, BUFLEN); - printf("DEBUG: 06 lmtp_readline=%d ***%s***\n", rc, buf); - } while(rc == LMTP_OK); - */ - - /* - do { - rc = lmtp_request(lmtp, &req); - printf("DEBUG: 07 lmtp_request=%d ***%s***%s***\n", rc, req.verb, req.msg); - } while(rc == LMTP_OK || rc == LMTP_ERR_VERB); - */ - - printf("DEBUG: 08 lmtp_loop=%d\n", lmtp_loop(lmtp)); - - printf("DEBUG: 99.\n"); - return; -} void lmtp_debug_dumplmtp(lmtp_t *lmtp) { @@ -88,10 +42,10 @@ /*************************************************************************/ /* - * return > 0 number of chars read - * return = 0 end of file, no chars inside - * return = -1 buffer overrun, chars inside but no newline seen - * return = -2 io error (errno), buffer content undefined + * return >= 0 number of chars read, zero is empty line + * return = -1 end of file, no chars inside + * return = -2 buffer overrun, chars inside but no newline seen + * return = -3 io error (errno), buffer content undefined */ static int readline(lmtp_t *lmtp, char *buf, size_t buflen) { @@ -104,9 +58,9 @@ /* fetch one character (but read more) */ if (rl->rl_cnt <= 0) { if ((rl->rl_cnt = lmtp->io.read(lmtp->rfd, rl->rl_buf, LMTP_LINE_MAXLEN)) < 0) - return -2; /* error see errno */ + return -3; /* error see errno */ if (rl->rl_cnt == 0) - return 0; /* EOF */ + return -1; /* EOF */ rl->rl_bufptr = rl->rl_buf; } @@ -121,7 +75,7 @@ } buf[n] = '\0'; /* string termination */ - return (n == (buflen-1)) ? -1 : n; + return (n == (buflen-1)) ? -2 : n; } int verbindex(lmtp_t *lmtp, char *verb) @@ -144,12 +98,12 @@ if(io == NULL) { lmtp->io.select = select; - lmtp->io.read = read; - lmtp->io.write = write; + lmtp->io.read = read; + lmtp->io.write = write; } else { - lmtp->io.select = io->select; - lmtp->io.read = io->read; - lmtp->io.write = io->write; + lmtp->io.select = io->select ? io->select : select; + lmtp->io.read = io->read ? io->read : read; + lmtp->io.write = io->write ? io->write : write; } lmtp->rl.rl_cnt = 0; lmtp->rl.rl_bufptr = NULL; @@ -162,6 +116,8 @@ lmtp->dispatch[0] = NULL; + lmtp_register(lmtp, "", lmtp_cb_default, NULL, NULL, NULL); + return lmtp; } @@ -176,13 +132,13 @@ lmtp_rc_t rc; rc = readline(lmtp, buf, buflen); - if(rc == -2) return LMTP_ERR_SYSTEM; /* io error (errno), buffer content undefined */ - if(rc == -1) return LMTP_ERR_OVERFLOW; /* buffer overrun, chars inside but no newline seen */ - if(rc == 0) return LMTP_EOF; /* end of file, no chars inside */ + if(rc == -3) return LMTP_ERR_SYSTEM; /* io error (errno), buffer content undefined */ + if(rc == -2) return LMTP_ERR_OVERFLOW; /* buffer overrun, chars inside but no newline seen */ + if(rc == -1) return LMTP_EOF; /* end of file, no chars inside */ return LMTP_OK; } -lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char *buf, size_t buflen) +lmtp_rc_t lmtp_readmsg(lmtp_t *lmtp, char **cppBuf, size_t maxlen) { /* read lines until end of message, unescape dots * @@ -194,12 +150,57 @@ */ lmtp_rc_t rc = LMTP_OK; - do { - rc = lmtp_readline(lmtp, buf, buflen); - printf("DEBUG: 06 lmtp_readline=%d ***%s***\n", rc, buf); - if (strcmp(buf, ".") == 0) break; - } while(rc == LMTP_OK); -/*FIXME escaping and more missing*/ + char *cpBuf; /* buffer as a whole */ + char *cpPtr; /* write cursor */ + char *cpLine; /* start of the current line (see offsetline) */ + size_t nBuf; /* size of buffer, doubled through realloc until maximum reached */ + size_t offset; /* required when cpBuf changed through realloc */ + size_t offsetline; /* memorizing start of line when reallocing in the middle of a line */ + + nBuf = 4096; + if ((cpBuf = (char *)malloc(nBuf)) == NULL) + return LMTP_ERR_MEM; + *cppBuf = cpBuf; + cpPtr = cpBuf; + cpLine = cpBuf; + while (1) { +// printf("DEBUG: cpPtr-cpBuf=%d, nBuf=%d, nBuf-(cpPtr-cpBuf)=%d\n", cpPtr-cpBuf, nBuf, nBuf-(cpPtr-cpBuf)); + rc = lmtp_readline(lmtp, cpPtr, nBuf-(cpPtr-cpBuf)); +// printf("DEBUG: lmtp_readline()=***%s***, rc=%d\n", cpPtr, rc); + if (rc == LMTP_ERR_OVERFLOW) { + if (nBuf == maxlen) + return LMTP_ERR_OVERFLOW; + offset = nBuf-1; + offsetline = cpLine - cpBuf; + nBuf *= 2; + if (nBuf > maxlen) + nBuf = maxlen; + if ((cpBuf = (char *)realloc(cpBuf, nBuf)) == NULL) { + free(cpBuf); + return LMTP_ERR_MEM; + } + *cppBuf = cpBuf; + cpPtr = cpBuf + offset; + cpLine = cpBuf + offsetline; + } + else if (rc == LMTP_OK) { + if (strcmp(cpLine, ".") == 0) { +// printf("DEBUG: \".\" found ***%s***\n", cpLine); + *cpLine = '\0'; + break; + } + if (*cpLine == '.') + memmove(cpLine, cpLine+1, strlen(cpLine+1)+1); /* escaped dot */ + cpPtr += strlen(cpPtr); + *cpPtr++ = '\n'; + *cpPtr = '\0'; + cpLine = cpPtr; + } + else { + /* rc == LMTP_ERR_SYSTEM, etc. */ + break; + } + } return rc; } @@ -230,8 +231,9 @@ for (i = 0; (i < LMTP_MAXVERBS) && (lmtp->dispatch[i] != NULL); i++) { if ((verb = lmtp->dispatch[i]->verb) != NULL) { - verblen = strlen(verb); - if( (strlen(req->msg) >= verblen) + if ((verblen = strlen(verb)) == 0) + continue; + if( (strlen(req->msg) >= verblen) //FIXME check for verb\s && (strncasecmp(req->msg, verb, verblen) == 0) ) { req->verb = verb; @@ -318,7 +320,7 @@ int i; for (i = 0; lmtp->dispatch[i] != NULL; i++) { - if (strcmp(verb, lmtp->dispatch[i]->verb) == 0) { + if (strcasecmp(verb, lmtp->dispatch[i]->verb) == 0) { overload=1; if(oldcb != NULL) *oldcb = lmtp->dispatch[i]->cb; if(oldctx != NULL) *oldctx = lmtp->dispatch[i]->ctx; @@ -348,46 +350,36 @@ { lmtp_req_t req; lmtp_res_t res; - lmtp_rc_t rc; + lmtp_rc_t rc = LMTP_OK; + char *verb; int i; + + res.statuscode = "220"; + res.dsncode = NULL; + res.statusmsg = "LMTP server ready (lmtp2nntp)"; + if ((rc = lmtp_response(lmtp, &res)) != LMTP_OK) return rc; + while ((rc = lmtp_request(lmtp, &req)) == LMTP_OK || (rc == LMTP_ERR_VERB)) { - if (rc != LMTP_ERR_VERB) { - if ((i = verbindex(lmtp, req.verb)) == -1) - return LMTP_ERR_VERB; - rc = lmtp->dispatch[i]->cb(&lmtp->io, &req, &res, lmtp->dispatch[i]->ctx); - } else { - rc = lmtp_cb_default(&lmtp->io, &req, &res, NULL); - } - if (lmtp_response(lmtp, &res) != LMTP_OK) break; - if (rc != LMTP_OK) break; + verb = req.verb; + if (rc == LMTP_ERR_VERB) + verb = ""; + if ((i = verbindex(lmtp, verb)) == -1) + return LMTP_ERR_VERB; + rc = lmtp->dispatch[i]->cb(lmtp, &lmtp->io, &req, lmtp->dispatch[i]->ctx); + if (rc != LMTP_OK) + break; } - return LMTP_OK; -} - -lmtp_rc_t lmtp_cb_default(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx) -{ - lmtp_rc_t rc = LMTP_OK; - res->statuscode = "500"; - res->dsncode = "5.5.1"; - res->statusmsg = "Command unrecognized."; return rc; } -lmtp_rc_t lmtp_cb_lhlo(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx) +lmtp_rc_t lmtp_cb_default(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 = NULL; /* DSN not used for greeting */ - res->statusmsg = "ENHANCEDSTATUSCODES\nDSN"; /* RFC2034, RFC1894 */ - return rc; -} - -lmtp_rc_t lmtp_cb_quit(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx) -{ - lmtp_rc_t rc = LMTP_EOF; - res->statuscode = "221"; - res->dsncode = "2.0.0"; - res->statusmsg = "Closing connection."; + res.statuscode = "500"; + res.dsncode = "5.5.1"; + res.statusmsg = "Command unrecognized."; + lmtp_response(lmtp, &res); return rc; } Index: ossp-pkg/lmtp2nntp/lmtp.h RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.h,v rcsdiff -q -kk '-r1.2' '-r1.3' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp.h,v' 2>/dev/null --- lmtp.h 2001/07/23 12:14:06 1.2 +++ lmtp.h 2001/07/24 11:32:07 1.3 @@ -38,12 +38,12 @@ LMTP_ERR_UNKNOWN /* guru meditation */ } lmtp_rc_t; -typedef lmtp_rc_t (*lmtp_cb_t)(lmtp_io_t *io, lmtp_req_t *req, lmtp_res_t *res, void *ctx); +typedef lmtp_rc_t (*lmtp_cb_t)(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); lmtp_t *lmtp_create (int rfd, int wfd, lmtp_io_t *io); void lmtp_destroy (lmtp_t *lmtp); lmtp_rc_t lmtp_readline(lmtp_t *lmtp, char *buf, size_t buflen); -lmtp_rc_t lmtp_readmsg (lmtp_t *lmtp, char *buf, size_t buflen); +lmtp_rc_t lmtp_readmsg (lmtp_t *lmtp, char **buf, size_t maxlen); lmtp_rc_t lmtp_request (lmtp_t *lmtp, lmtp_req_t *req); lmtp_rc_t lmtp_response(lmtp_t *lmtp, lmtp_res_t *res); char **lmtp_message (lmtp_t *lmtp, char *verb); Index: ossp-pkg/lmtp2nntp/lmtp2nntp.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp2nntp.c,v rcsdiff -q -kk '-r1.2' '-r1.3' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp2nntp.c,v' 2>/dev/null --- lmtp2nntp.c 2001/07/23 12:14:06 1.2 +++ lmtp2nntp.c 2001/07/24 11:32:07 1.3 @@ -12,14 +12,35 @@ * */ + #include #include +#include +#include +#include + +/* third party */ +#include "str.h" + +/* own headers */ +#include "lmtp.h" #define ERR_EXECUTION -1 #define ERR_DELIVERY -2 -void usage(char *command); -void test(void); +#define MESSAGE_MAXLEN 8*1024*1024 + +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_quit (lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *ctx); + +static void usage(char *command); +static void test(void); int main(int argc, char **argv) { @@ -43,14 +64,14 @@ break; case '?': default: - usage(argv[0]); - exit(ERR_EXECUTION); + //FIXME usage(argv[0]); + //FIXME exit(ERR_EXECUTION); } argc -= optind; argv += optind; // remaining args is/are newsgroup/s - printf("Hello, World!\n"); + //FIXME printf("Hello, World!\n"); test(); - printf("Hello, Again!\n"); + //FIXME printf("Hello, Again!\n"); return 0; } @@ -59,7 +80,7 @@ * */ -void usage(char *command) +static void usage(char *command) { fprintf(stderr, "USAGE: %s [-p protocol] [-l logtarget] "\ "[-h host[:port]] [-m mode] [-t] [-v] newsgroup [newsgroup ...]\n", @@ -67,4 +88,168 @@ return; } +/* + * 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; +} + +/* + * test a function + * + */ + +extern void lmtp_debug_dumplmtp(lmtp_t *lmtp); +static void test(void) +{ + lmtp_t *lmtp; + lmtp_rc_t rc; + lmtp_req_t req; + lmtp_res_t res; + lmtp_io_t io; +#define BUFLEN 100 + char buf[BUFLEN]; + + + io.read=trace_read; + io.write=trace_write; + io.select=select; + lmtp = lmtp_create(STDIN_FILENO, STDOUT_FILENO, &io); + // lmtp_debug_dumplmtp(lmtp); + lmtp_register(lmtp, "LHLO", lmtp_cb_lhlo, NULL, NULL, NULL); + lmtp_register(lmtp, "MAIL", lmtp_cb_mail, NULL, NULL, NULL); + lmtp_register(lmtp, "RCPT", lmtp_cb_rcpt, NULL, NULL, NULL); + lmtp_register(lmtp, "DATA", lmtp_cb_data, NULL, NULL, NULL); + lmtp_register(lmtp, "QUIT", lmtp_cb_quit, NULL, NULL, NULL); + // lmtp_debug_dumplmtp(lmtp); + + /* + printf("DEBUG: 05 lmtp_response\n"); res.statuscode="123"; + res.dsncode="1.2.3"; + res.statusmsg="Hello,\nthis is a\nmultiline\nmessage"; + lmtp_response(lmtp, &res); + */ + + /* + do { + rc = lmtp_readline(lmtp, buf, BUFLEN); + printf("DEBUG: 06 lmtp_readline=%d ***%s***\n", rc, buf); + } while(rc == LMTP_OK); + */ + + /* + do { + rc = lmtp_request(lmtp, &req); + printf("DEBUG: 07 lmtp_request=%d ***%s***%s***\n", rc, req.verb, req.msg); + } while(rc == LMTP_OK || rc == LMTP_ERR_VERB); + */ + + /* printf("DEBUG: 08 lmtp_loop=%d\n", */ lmtp_loop(lmtp) /*)*/; + + /* printf("DEBUG: 99.\n"); */ + return; +} +static lmtp_rc_t lmtp_cb_lhlo(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 = NULL; /* DSN not used for greeting */ + res.statusmsg = "ENHANCEDSTATUSCODES\nDSN"; /* RFC2034, RFC1894 */ + 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; + res.statuscode = "553"; + res.dsncode = "5.1.8"; + res.statusmsg = "bad sender FIXME"; + res.statuscode = "250"; + res.dsncode = "2.1.0"; + res.statusmsg = "sender ok FIXME"; + 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_res_t res; + lmtp_rc_t rc = LMTP_OK; + 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; + char *buf; + char errorstring[128]; + + 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); + if(rc == LMTP_OK) { +// printf("DEBUG: message=***%s***\n", buf); + res.statuscode = "250"; + res.dsncode = "2.0.0"; + res.statusmsg = "Message accepted for delivery"; + } 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_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; +} Index: ossp-pkg/lmtp2nntp/lmtp2nntp.pod RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/lmtp2nntp.pod,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/lmtp2nntp.pod,v' 2>/dev/null --- lmtp2nntp.pod 2001/07/17 12:40:07 1.1 +++ lmtp2nntp.pod 2001/07/24 11:32:07 1.2 @@ -8,6 +8,10 @@ =head1 SYNOPSIS B +[B<-o> I] +[B<-j> I] +[B<-i> I] +[B<-t> I] [B<-p> I] [B<-l> I] [B<-h> I[I<:port>]] @@ -27,6 +31,22 @@ =over 4 +=item B<-o> I + +Outgoing network IP address or hostname to bind to. + +=item B<-j> I + +Own FQDN used in LMTP and NNTP protocols. + +=item B<-i> I + +Message id of MTA (for logging purposes only). + +=item B<-t> I + +LMTP and NNTP protocol timeouts (in seconds). + =item B<-p> I Incoming protocol. Default is C which means B @@ -118,6 +138,11 @@ delivery status is part of the LMTP protocol +=head1 STANDARDS + + RFC1891 Delivery Status Notification (DSN) + FIXME + =head1 AUTHOR The OSSP Project