--- 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 <errno.h>
#include <string.h>
#include <fcntl.h>
+#include <sys/utsname.h>
/* 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 <SP> <domain> <CRLF>
+ * MAIL <SP> FROM:<reverse-path> <CRLF>
+ * RCPT <SP> TO:<forward-path> <CRLF>
+ * DATA <CRLF>
+ * RSET <CRLF>
+ * NOOP <CRLF>
+ * QUIT <CRLF>
+ */
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 <domain> 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 <domain> 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 <domain> according to RFC 821:
+ # BNF grammar for <domain> according to RFC0821:
# <snum> ::= one, two, or three digits representing a decimal integer value in the range 0 through 255
# <a> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
# <d> ::= 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:<foo@bar>"
- * "MAIL From:<foo@bar> BODY=8BITMIME"
- * "MAIL From:<foo@bar> 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:<foo@bar>"
+ * "MAIL From:<foo@bar> BODY=8BITMIME"
+ * "MAIL From:<foo@bar> 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 <CRLF>.<CRLF>
+ */
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 <domain> 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);
+}
|