Index: ossp-pkg/lmtp2nntp/lmtp2nntp.c RCS File: /v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp2nntp.c,v rcsdiff -q -kk '-r1.23' '-r1.24' -u '/v/ossp/cvs/ossp-pkg/lmtp2nntp/Attic/lmtp2nntp.c,v' 2>/dev/null --- lmtp2nntp.c 2001/08/21 08:56:41 1.23 +++ lmtp2nntp.c 2001/08/23 07:55:53 1.24 @@ -15,10 +15,12 @@ #include #include #include +#include /* third party */ #include "str.h" #include "argz.h" +#include "shpat_match.h" /* own headers */ #include "lmtp.h" @@ -64,6 +66,7 @@ static void initsession(struct session *session); static void resetsession(struct session *session); +int groupmatch(char *, size_t, char *); struct ns { char *h; /* host */ @@ -71,23 +74,29 @@ sa_t *sa; int s; /* socket */ nntp_t *nntp; + nntp_rc_t rc; }; typedef struct { - int option_verbose; - int option_tracing; - int option_groupmode; - int option_deliverymode; - char *option_deliverymodefakestatus; - char *option_deliverymodefakedsn; - int nsc; - struct ns ns[MAXNEWSSERVICES]; - char *azGroupargs; - size_t asGroupargs; - struct session session; - msg_t *msg; + int option_verbose; + int option_tracing; + int option_groupmode; + int option_deliverymode; + char *option_deliverymodefakestatus; + char *option_deliverymodefakedsn; + int nsc; + struct ns ns[MAXNEWSSERVICES]; + char *azGroupargs; + size_t asGroupargs; + struct session session; + msg_t *msg; + struct utsname uname; } lmtp2nntp_t; +static void lmtp_gfs_lhlo(lmtp2nntp_t *ctx); +static void lmtp_gfs_rset(lmtp2nntp_t *ctx); +static void lmtp_gfs_quit(lmtp2nntp_t *ctx); + enum { GROUPMODE_ARG, GROUPMODE_ENVELOPE, @@ -124,16 +133,6 @@ char *cpPort; sa_t *sa; -#if 0 - /* begin NNTP posting test */ - { - nntp_post(nntp, "..."); - nntp_destroy(nntp); - sock_destroy(s); - exit(0); - } -#endif - progname = argv[0]; /* create application context */ @@ -147,14 +146,21 @@ ctx->option_deliverymodefakedsn = "5.7.1"; /* Delivery not authorized, message refused */ ctx->nsc = 0; for (i=0; i < MAXNEWSSERVICES; i++) { - ctx->ns[i].h = ""; + ctx->ns[i].h = NULL; + ctx->ns[i].p = NULL; + ctx->ns[i].sa = NULL; ctx->ns[i].s = -1; ctx->ns[i].nntp = NULL; + ctx->ns[i].rc = LMTP_ERR_UNKNOWN; } ctx->azGroupargs = NULL; ctx->asGroupargs = 0; initsession(&ctx->session); ctx->msg = NULL; + if (uname(&ctx->uname) != 0) { + fprintf(stderr, "%s:Error: uname failed \"%s\"\n", progname, strerror(errno)); + exit(ERR_EXECUTION); + } #if 1 { @@ -253,6 +259,7 @@ strerror(errno)); exit(ERR_EXECUTION); } + ctx->ns[ctx->nsc].sa = sa; if ((ctx->ns[ctx->nsc].s = socket(sa->sa_buf->sa_family, SOCK_STREAM, sa->sa_proto)) == -1) { fprintf(stderr, "%s:Error: Creating TCP socket failed for \"%s:%s\": %s\n", @@ -262,8 +269,6 @@ strerror(errno)); exit(ERR_EXECUTION); } - ctx->ns[ctx->nsc].sa = sa; - /*FIXME sa_destroy(sa); */ ctx->ns[ctx->nsc].nntp = NULL; ctx->nsc++; break; @@ -299,25 +304,43 @@ fprintf(stderr, "%s:Error: Unable to initialize LMTP library\n", progname); exit(ERR_EXECUTION); } + /* RFC0821, 4.5.1. MINIMUM IMPLEMENTATION + * In order to make SMTP workable, the following minimum implementation + * is required for all receivers: [...] + * RFC0821, 4.1.2. COMMAND SYNTAX + * + * Verb Parameter + * ----+------------------------------- + * HELO + * MAIL FROM: + * RCPT TO: + * DATA + * RSET + * NOOP + * QUIT + */ 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, "NOOP", lmtp_cb_noop, ctx, NULL, NULL); lmtp_register(lmtp, "QUIT", lmtp_cb_quit, ctx, NULL, NULL); /* loop for LMTP protocol */ lmtp_loop(lmtp); + /* graceful shutdown */ + lmtp_gfs_quit(ctx); + lmtp_gfs_lhlo(ctx); + return rc; } static void resetsession(struct session *session) { - /*FIXME what about non-graceful aborts? */ if (session->lhlo_domain != NULL) - free(session->lhlo_domain); + free(session->lhlo_domain); initsession(session); return; } @@ -332,7 +355,7 @@ 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 + * RFC0821 [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 @@ -363,6 +386,10 @@ nntp_io.read = trace_read; nntp_io.write = trace_write; + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 503 Bad sequence of commands + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ if (ctx->session.lhlo_seen == TRUE) { res.statuscode = "503"; res.dsncode = "5.0.0"; @@ -371,9 +398,12 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 501 Syntax error in parameters or arguments + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ if (! ( helo_rfc0821domain(req->msg, &ctx->session.lhlo_domain) - || helo_rfc1035domain(req->msg, &ctx->session.lhlo_domain) - )) { + || helo_rfc1035domain(req->msg, &ctx->session.lhlo_domain))) { res.statuscode = "501"; res.dsncode = "5.0.0"; res.statusmsg = "Please identify yourself. Domain must match RFC0821/RFC1035."; @@ -381,10 +411,14 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 451 Requested action aborted: local error in processing + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.5 System incorrectly configured + */ if (ctx->nsc == 0) { - res.statuscode = "501"; - res.dsncode = "5.0.0"; - res.statusmsg = "No valid NNTP Services specified."; + res.statuscode = "451"; + res.dsncode = "4.3.5"; + res.statusmsg = "No valid NNTP services configured."; lmtp_response(lmtp, &res); return LMTP_OK; } @@ -414,38 +448,56 @@ } } while (i < ctx->nsc); - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS - * "421 Service not available, closing transmission channel [This - * may be a reply to any command if the service knows it must shut down]" - * - * RFC1893 2. Status Codes, 3.5 Network and Routing Status - * 4.X.X Persistent Transient Failure - * X.4.1 No answer from host + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 421 Service not available + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.4.1 No answer from host */ if (ctx->nsc == 0) { res.statuscode = "421"; res.dsncode = "4.4.1"; - res.statusmsg = "No connection to any NNTP Service."; /*FIXME add error strings from above DEBUGs */ + res.statusmsg = "All attempts connecting to NNTP services failed."; lmtp_response(lmtp, &res); return LMTP_OK; } ctx->session.lhlo_seen = TRUE; + + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + */ str_format(str, sizeof(str), - "FIXME.dev.de.cw.net" /* RFC2821 4.1.1.1 */ - " Hello %s, pleased to meet you.\n" - "ENHANCEDSTATUSCODES\n" /* RFC2034 */ - "DSN\n" /* RFC1894 */ - "PIPELINING\n" /* RFC1854 */ - "8BITMIME", /* RFC1652 */ + "%s Hello %s, pleased to meet you.\n" /* RFC2821 4.1.1.1 */ + "ENHANCEDSTATUSCODES\n" /* RFC2034 */ + "DSN\n" /* RFC1894 */ + "PIPELINING\n" /* RFC1854 */ + "8BITMIME", /* RFC1652 */ + ctx->uname.nodename, ctx->session.lhlo_domain); res.statuscode = "250"; - res.dsncode = NULL; /* DSN not used for greeting */ + res.dsncode = NULL; /* DSN not used for greeting */ res.statusmsg = str; lmtp_response(lmtp, &res); return LMTP_OK; } +static void lmtp_gfs_lhlo(lmtp2nntp_t *ctx) +{ + /* graceful shutdown */ + int i; + + for (i = 0; i < ctx->nsc; i++) { + if (ctx->ns[i].nntp != NULL) + nntp_destroy(ctx->ns[i].nntp); + if (ctx->ns[i].s != -1) + close(ctx->ns[i].s); + if (ctx->ns[i].sa != NULL) + sa_destroy(ctx->ns[ctx->nsc].sa); + if (ctx->ns[i].p != NULL) + free(ctx->ns[i].p); + if (ctx->ns[i].h != NULL) + free(ctx->ns[i].h); + } +} + static int helo_rfc0821domain(char *msg, char **domain) { int rc; @@ -460,7 +512,7 @@ ## linear grammar, but noth both). ## - # BNF grammar for according to RFC 821: + # BNF grammar for according to RFC0821: # ::= one, two, or three digits representing a decimal integer value in the range 0 through 255 # ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case # ::= any one of the ten digits 0 through 9 @@ -489,11 +541,12 @@ # translate into C string block suitable for passing to the Perl # Compatible Regular Expressions (PCRE) based string library Str. my $cregex = $domain; + $cregex .= "\n"; $cregex =~ s|\\|\\\\|sg; - $cregex =~ s|(.{70})|"$1"\n|sg; - $cregex =~ s|\n([^\n]+)$|\n"$1"|s; #FIXME this fails when last - #FIXME line matches linelength exacly - print "$cregex\n"; + $cregex =~ s|(.{17})|$1\n|sg; + $cregex =~ s|([^\n]+)\n|"$1"\n|sg; + $cregex =~ s|\n\n|\n|sg; + print "$cregex"; */ "(?:(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\[(?:[0" @@ -547,11 +600,12 @@ # translate into C string block suitable for passing to the Perl # Compatible Regular Expressions (PCRE) based string library Str. my $cregex = $domain; + $cregex .= "\n"; $cregex =~ s|\\|\\\\|sg; - $cregex =~ s|(.{70})|"$1"\n|sg; - $cregex =~ s|\n([^\n]+)$|\n"$1"|s; #FIXME this fails when last - #FIXME line matches linelength exacly - print "$cregex\n"; + $cregex =~ s|(.{17})|$1\n|sg; + $cregex =~ s|([^\n]+)\n|"$1"\n|sg; + $cregex =~ s|\n\n|\n|sg; + print "$cregex"; */ "(?:(?:(?:[A-Za-z](?:(?:(?:(?:[A-Za-z]|[0-9])|-)+)?(?:[A-Za-z]|[0-9]))?" @@ -567,6 +621,10 @@ lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx; lmtp_res_t res; + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 553 Requested action not taken: mailbox name not allowed + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.1.8 Bad sender's system address + */ if (ctx->session.lhlo_seen != TRUE) { res.statuscode = "553"; res.dsncode = "5.1.8"; @@ -575,6 +633,10 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 503 Bad sequence of commands + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.5.0 Other or undefined protocol status + */ if (ctx->msg != NULL) { res.statuscode = "503"; res.dsncode = "5.5.0"; @@ -583,36 +645,48 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 452 Requested action not taken: insufficient system storage + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.1 Mail system full + */ if ((ctx->msg = msg_create()) == NULL) { - res.statuscode = "503"; /* FIXME */ - res.dsncode = "5.5.0"; + res.statuscode = "452"; + res.dsncode = "4.3.1"; res.statusmsg = "Internal error - memory."; lmtp_response(lmtp, &res); return LMTP_ERR_MEM; } - /* RFC1652 2. Framework for the 8bit MIME Transport Extension - * (4) one optional parameter using the keyword BODY is added to the MAIL - * FROM command. The value associated with this parameter is a keyword - * indicating whether a 7bit message [...] or a MIME message [...] is - * being sent. The syntax of the value is as follows, using the ABNF - * notation [...] - * - * body-value ::= "7BIT" / "8BITMIME" - * - * "MAIL From:" - * "MAIL From: BODY=8BITMIME" - * "MAIL From: BODY=7BIT" + /* RFC1652 2. Framework for the 8bit MIME Transport Extension + * (4) one optional parameter using the keyword BODY is added to the MAIL + * FROM command. The value associated with this parameter is a keyword + * indicating whether a 7bit message [...] or a MIME message [...] is + * being sent. The syntax of the value is as follows, using the ABNF + * notation [...] + * + * body-value ::= "7BIT" / "8BITMIME" + * + * "MAIL From:" + * "MAIL From: BODY=8BITMIME" + * "MAIL From: BODY=7BIT" + * + * RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 553 Requested action not taken: mailbox name not allowed + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.1.7 Bad sender's mailbox address syntax */ if (!str_parse(req->msg, "m/^MAIL From:\\s*<(?:.+@.+)>/i")) { res.statuscode = "553"; - res.dsncode = "5.5.4"; + res.dsncode = "5.1.7"; res.statusmsg = "Domain name required for sender address."; lmtp_response(lmtp, &res); msg_destroy(ctx->msg); ctx->msg = NULL; return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 501 Syntax error in parameters or arguments + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.5.4 Invalid command arguments + */ if (!str_parse(req->msg, "m/^MAIL From:\\s*<(.+@.+)>" "(?:\\s+BODY=(?:7BIT|8BITMIME)\\s*){0,1}$/i", &ctx->msg->mail_from)) { @@ -625,6 +699,10 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + * RFC1893 2. Status Codes 2.X.X Success + * RFC1893 3.5 Network and Routing Status X.1.0 Other address status + */ res.statuscode = "250"; res.dsncode = "2.1.0"; res.statusmsg = "Sender ok."; @@ -639,16 +717,23 @@ char *cp; char *group; + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 503 Bad sequence of commands + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.5.0 Other or undefined protocol status + */ if (ctx->msg->mail_from == NULL) { res.statuscode = "503"; - res.dsncode = "5.0.0"; + res.dsncode = "5.5.0"; res.statusmsg = "specify sender with MAIL first."; lmtp_response(lmtp, &res); return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 501 Syntax error in parameters or arguments + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.5.2 Syntax error + */ if (!str_parse(req->msg, "m/^RCPT To:\\s*(.+)$/i", &cp)) { - /*FIXME ^^^^ is this space required/ valid? Sendmail skips them! */ res.statuscode = "501"; res.dsncode = "5.5.2"; res.statusmsg = "Syntax error in parameters."; @@ -656,6 +741,10 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 550 Requested action not taken: mailbox unavailable + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.1.1 Bad destination mailbox address + */ if ((cp == NULL) || (strlen(cp) == 0)) { res.statuscode = "550"; res.dsncode = "5.1.1"; @@ -664,61 +753,87 @@ return LMTP_OK; } - /* FIXME - * in GROUPMODE = ARG|HEADER recipient must be acknowledged and stored to + /* in GROUPMODE = ARG|HEADER recipient must be acknowledged and stored to * give proper pipelining responses. in GROUPMODE = ENVELOPE recipient is * transformed into a group and matched against groupfilter. Only valid * groups are stored to give proper pipelining responses. + * + * RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 550 Requested action not taken: mailbox unavailable + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.1.1 Bad destination mailbox address + * X.7.2 Mailing list expansion prohibited */ - if (ctx->option_groupmode == GROUPMODE_ENVELOPE) { - /*fprintf(stderr, "DEBUG: before transform cp=***%s***\n", cp); */ if (!str_parse(cp, "m/^<(.+)?@[^@]+>$/i", &group)) { res.statuscode = "550"; res.dsncode = "5.1.1"; - res.statusmsg = "Recipient did not transform into Group."; + res.statusmsg = "Recipient did not transform into group."; lmtp_response(lmtp, &res); return LMTP_OK; } - /*fprintf(stderr, "DEBUG: after transform group=***%s***\n", group); */ - /*FIXME do additional transform and checking */ - if (0) { + if (groupmatch(ctx->azGroupargs, ctx->asGroupargs, group) != TRUE) { res.statuscode = "550"; - res.dsncode = "5.1.1"; + res.dsncode = "5.7.2"; res.statusmsg = "unmatched Group."; lmtp_response(lmtp, &res); return LMTP_OK; } argz_add(&ctx->msg->azEnvgroups, &ctx->msg->asEnvgroups, group); } - - /*fprintf(stderr, "DEBUG: cp=***%s***\n", cp); */ argz_add(&ctx->msg->azRcpt, &ctx->msg->asRcpt, cp); + + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + * RFC1893 2. Status Codes 2.X.X Success + * RFC1893 3.5 Network and Routing Status X.1.5 Destination address valid + */ res.statuscode = "250"; res.dsncode = "2.1.5"; - res.statusmsg = "Recipient/ Group accepted"; + res.statusmsg = ctx->option_groupmode == GROUPMODE_ENVELOPE ? "Group accepted." : "Recipient accepted."; lmtp_response(lmtp, &res); return LMTP_OK; } +int groupmatch(char *azPattern, size_t asPattern, char *cpGroup) +{ + int bGroupmatch; + char *cpGroupmatch; + + bGroupmatch = FALSE; + cpGroupmatch = NULL; + while ((cpGroupmatch = argz_next(azPattern, asPattern, cpGroupmatch)) != NULL) { + if (shpat_match(cpGroupmatch, cpGroup, 0) == 0) + bGroupmatch = TRUE; + } + return bGroupmatch; +} + 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 *azErr; + size_t asErr; char errorstring[STDSTRLEN]; char *rcpt; int i; int bSuccess; + char *cp; + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 503 Bad sequence of commands + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.5.0 Other or undefined protocol status + */ if (argz_count(ctx->msg->azRcpt, ctx->msg->asRcpt) == 0) { res.statuscode = "503"; - res.dsncode = "5.0.0"; + res.dsncode = "5.5.0"; res.statusmsg = "specify recipient with RCPT first."; lmtp_response(lmtp, &res); return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 354 Start mail input; end with . + */ res.statuscode = "354"; res.dsncode = NULL; /* DSN not used for data */ res.statusmsg = "Enter mail, end with \".\" on a line by itself"; @@ -726,11 +841,15 @@ rc = lmtp_readmsg(lmtp, &ctx->msg->cpMsg, MESSAGE_MAXLEN); + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 552 Requested mail action aborted: exceeded storage allocation + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.3.4 Message too big for system + */ if (rc == LMTP_ERR_OVERFLOW) { str_format(errorstring, sizeof(errorstring), "Overflow reading message: %s", lmtp_error(rc)); - res.statuscode = "500"; - res.dsncode = "5.0.0"; - res.statusmsg = errorstring; /*FIXME temp or perm error? */ + res.statuscode = "552"; + res.dsncode = "5.3.4"; + res.statusmsg = errorstring; rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { lmtp_response(lmtp, &res); @@ -738,10 +857,14 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 451 Requested action aborted: local error in processing + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.2 System not accepting network messages + */ if (rc == LMTP_ERR_SYSTEM) { str_format(errorstring, sizeof(errorstring), "System error reading message: %s", strerror(errno)); - res.statuscode = "500"; - res.dsncode = "5.0.0"; + res.statuscode = "451"; + res.dsncode = "4.3.2"; res.statusmsg = errorstring; rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { @@ -750,10 +873,14 @@ return LMTP_OK; } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 451 Requested action aborted: local error in processing + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.2 System not accepting network messages + */ if(rc != LMTP_OK) { str_format(errorstring, sizeof(errorstring), "Unknown error reading message: %s", lmtp_error(rc)); - res.statuscode = "500"; - res.dsncode = "5.0.0"; + res.statuscode = "451"; + res.dsncode = "4.3.2"; res.statusmsg = errorstring; rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { @@ -762,11 +889,14 @@ return LMTP_OK; } - /*fprintf(stderr, "DEBUG: before msg_split\n"); */ + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 554 Transaction failed + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.6.5 Conversion Failed + */ if ((rc = msg_split(ctx->msg)) != MSG_OK) { str_format(errorstring, sizeof(errorstring), "Error splitting message: %s", msg_error(rc)); - res.statuscode = "500"; - res.dsncode = "5.0.0"; + res.statuscode = "554"; + res.dsncode = "5.6.5"; res.statusmsg = errorstring; rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { @@ -774,30 +904,86 @@ } return LMTP_OK; } - /*fprintf(stderr, "DEBUG: after msg_split\n"); */ if (ctx->option_groupmode == GROUPMODE_ENVELOPE) { - ctx->msg->azNewsgroups = memcpy(malloc(ctx->msg->asEnvgroups + 1), ctx->msg->azEnvgroups, ctx->msg->asEnvgroups); /*FIXME strdup == NULL */ - ctx->msg->asNewsgroups = ctx->msg->asEnvgroups; + if ((cp = malloc(ctx->msg->asEnvgroups + 1)) == NULL) { + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 452 Requested action not taken: insufficient system storage + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.1 Mail system full + */ + if ((ctx->msg = msg_create()) == NULL) { + res.statuscode = "452"; + res.dsncode = "4.3.1"; + res.statusmsg = "Internal error - memory."; + lmtp_response(lmtp, &res); + return LMTP_ERR_MEM; + } + } + ctx->msg->azNewsgroups = memcpy(cp, ctx->msg->azEnvgroups, ctx->msg->asEnvgroups); + ctx->msg->asNewsgroups = ctx->msg->asEnvgroups; } else if (ctx->option_groupmode == GROUPMODE_ARG) { - ctx->msg->azNewsgroups = memcpy(malloc(ctx->asGroupargs + 1), ctx->azGroupargs, ctx->asGroupargs); /*FIXME strdup == NULL */ - ctx->msg->asNewsgroups = ctx->asGroupargs; + if ((cp = malloc(ctx->asGroupargs + 1)) == NULL) { + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 452 Requested action not taken: insufficient system storage + * RFC1893 2. Status Codes 4.X.X Persistent Transient Failure + * RFC1893 3.5 Network and Routing Status X.3.1 Mail system full + */ + if ((ctx->msg = msg_create()) == NULL) { + res.statuscode = "452"; + res.dsncode = "4.3.1"; + res.statusmsg = "Internal error - memory."; + lmtp_response(lmtp, &res); + return LMTP_ERR_MEM; + } + } + ctx->msg->azNewsgroups = memcpy(cp, ctx->azGroupargs, ctx->asGroupargs); + ctx->msg->asNewsgroups = ctx->asGroupargs; + } + else { /* == GROUPMODE_HEADER */ + cp = ctx->msg->azNewsgroups; + while (cp != NULL) { + if (groupmatch(ctx->azGroupargs, ctx->asGroupargs, cp) != TRUE) { + if (argz_next(ctx->msg->azNewsgroups, ctx->msg->asNewsgroups, cp) == NULL) { + argz_delete(&ctx->msg->azNewsgroups, &ctx->msg->asNewsgroups, cp); + break; + } + else + argz_delete(&ctx->msg->azNewsgroups, &ctx->msg->asNewsgroups, cp); + } else { + cp = argz_next(ctx->msg->azNewsgroups, ctx->msg->asNewsgroups, cp); + } + } + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 550 Requested action not taken: mailbox unavailable + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.7.2 Mailing list expansion prohibited + */ + if (ctx->msg->asNewsgroups == 0) { + rcpt = NULL; + while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { + res.statuscode = "550"; + res.dsncode = "5.7.2"; + res.statusmsg = "Header did not match any valid group."; + lmtp_response(lmtp, &res); + } + return LMTP_OK; + } } - /* else keep == GROUPMODE_HEADERS */ -#if 0 /*FIXME debug paragraph */ +#if 0 /*DEBUG and LOGGING paragraph */ rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azNewsgroups, ctx->msg->asNewsgroups, rcpt)) != NULL) { fprintf(stderr, "DEBUG: commited group ***%s***\n", rcpt); } #endif - /*fprintf(stderr, "DEBUG: before msg_join\n"); */ + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 554 Transaction failed + * RFC1893 2. Status Codes 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.6.5 Conversion Failed + */ if ((rc = msg_join(ctx->msg)) != MSG_OK) { str_format(errorstring, sizeof(errorstring), "Error joining message: %s", msg_error(rc)); - res.statuscode = "500"; - res.dsncode = "5.0.0"; + res.statuscode = "554"; + res.dsncode = "5.6.5"; res.statusmsg = errorstring; rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { @@ -814,67 +1000,83 @@ case DELIVERYMODE_FAKE: break; case DELIVERYMODE_POST: - rc = nntp_post(ctx->ns[i].nntp, ctx->msg); + ctx->ns[i].rc = nntp_post(ctx->ns[i].nntp, ctx->msg); break; case DELIVERYMODE_FEED: - rc = nntp_feed(ctx->ns[i].nntp, ctx->msg); + ctx->ns[i].rc = nntp_feed(ctx->ns[i].nntp, ctx->msg); break; } - if (rc == NNTP_OK) + if (ctx->ns[i].rc == NNTP_OK) bSuccess = NNTP_OK; if ( bSuccess != NNTP_OK && ( - (rc == NNTP_TIMEOUT) - || (rc == NNTP_ERR_SYSTEM) - || (rc == NNTP_DEFER) + (ctx->ns[i].rc == NNTP_TIMEOUT) + || (ctx->ns[i].rc == NNTP_ERR_SYSTEM) + || (ctx->ns[i].rc == NNTP_DEFER) ) ) bSuccess = NNTP_DEFER; } - /*FIXME rc has only last error */ - /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS - * "250 Requested mail action okay, completed" - * "451 Requested action aborted: local error in processing" - * "554 Transaction failed" - * - * RFC1893 2. Status Codes - * 2.X.X Success - * 4.X.X Persistent Transient Failure - * 5.X.X Permanent Failure - * - * RFC1893 3.5 Network and Routing Status - * X.0.0 Other undefined Status - * X.4.2 Bad connection + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + * 451 Requested action aborted: local error in processing + * 554 Transaction failed + * RFC1893 2. Status Codes 2.X.X Success + * 4.X.X Persistent Transient Failure + * 5.X.X Permanent Failure + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + * X.4.2 Bad connection */ rcpt = NULL; while ((rcpt = argz_next(ctx->msg->azRcpt, ctx->msg->asRcpt, rcpt)) != NULL) { if (ctx->option_deliverymode == DELIVERYMODE_FAKE) { res.statuscode = ctx->option_deliverymodefakestatus; res.dsncode = ctx->option_deliverymodefakedsn; - res.statusmsg = "NNTP noop fake return"; + str_format(errorstring, sizeof(errorstring), + "NNTP noop fake return for %s", rcpt); } else { switch (bSuccess) { case NNTP_OK: - str_format(errorstring, sizeof(errorstring), "Message accepted for delivery to %s", rcpt); + str_format(errorstring, sizeof(errorstring), + "Message accepted for delivery to %s", rcpt); res.statuscode = "250"; res.dsncode = "2.0.0"; - res.statusmsg = errorstring; break; case NNTP_DEFER: + str_format(errorstring, sizeof(errorstring), + "Requested action aborted for %s, local error in processing.", rcpt); res.statuscode = "451"; res.dsncode = "4.4.2"; - res.statusmsg = "Requested action aborted, local error in processing."; - lmtp_response(lmtp, &res); break; default: - str_format(errorstring, sizeof(errorstring), "Last error posting message: %s", nntp_error(rc)); + str_format(errorstring, sizeof(errorstring), + "Error sending article for %s.", rcpt); res.statuscode = "554"; res.dsncode = "5.4.2"; - res.statusmsg = errorstring; + break; } } - lmtp_response(lmtp, &res); + azErr = NULL; + asErr = 0; + argz_add(&azErr, &asErr, errorstring); + for (i = 0; i < ctx->nsc; i++) { + if (ctx->ns[i].rc != NNTP_OK) { + str_format(errorstring, sizeof(errorstring), "%s:%s returned %s", ctx->ns[i].h, ctx->ns[i].p, nntp_error(ctx->ns[i].rc)); + argz_add(&azErr, &asErr, errorstring); + } + } + if (azErr != NULL) { + argz_stringify(azErr, asErr, '\n'); + res.statusmsg = azErr; + lmtp_response(lmtp, &res); + free(azErr); + azErr = NULL; + asErr = 0; + } + else { + res.statusmsg = errorstring; + lmtp_response(lmtp, &res); + } } msg_destroy(ctx->msg); @@ -888,6 +1090,10 @@ lmtp_res_t res; lmtp_rc_t rc = LMTP_OK; + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + * RFC1893 2. Status Codes 2.X.X Success + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ res.statuscode = "250"; res.dsncode = "2.0.0"; res.statusmsg = "OK. Nice talking to you."; @@ -901,9 +1107,12 @@ lmtp_res_t res; lmtp_rc_t rc = LMTP_OK; - msg_destroy(ctx->msg); - ctx->msg = NULL; + lmtp_gfs_rset(ctx); + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 250 Requested mail action okay, completed + * RFC1893 2. Status Codes 2.X.X Success + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ res.statuscode = "250"; res.dsncode = "2.0.0"; res.statusmsg = "Reset state."; @@ -911,16 +1120,27 @@ return rc; } +static void lmtp_gfs_rset(lmtp2nntp_t *ctx) +{ + /* graceful shutdown */ + if (ctx->msg != NULL) { + msg_destroy(ctx->msg); + ctx->msg = NULL; + } +} + static lmtp_rc_t lmtp_cb_quit(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_EOF; - msg_destroy(ctx->msg); - ctx->msg = NULL; + lmtp_gfs_quit(ctx); - resetsession(&ctx->session); + /* RFC0821 4.2.1. REPLY CODES BY FUNCTION GROUPS 221 Service closing transmission channel + * RFC1893 2. Status Codes 2.X.X Success + * RFC1893 3.5 Network and Routing Status X.0.0 Other undefined Status + */ res.statuscode = "221"; res.dsncode = "2.0.0"; res.statusmsg = "LMTP Service closing transmission channel."; @@ -928,3 +1148,9 @@ return rc; } +static void lmtp_gfs_quit(lmtp2nntp_t *ctx) +{ + /* graceful shutdown */ + lmtp_gfs_rset(ctx); + resetsession(&ctx->session); +}