OSSP CVS Repository

ossp - Difference in ossp-pkg/lmtp2nntp/lmtp2nntp.c versions 1.7 and 1.8
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/lmtp2nntp/lmtp2nntp.c 1.7 -> 1.8

--- lmtp2nntp.c  2001/08/01 07:08:31     1.7
+++ lmtp2nntp.c  2001/08/02 14:58:39     1.8
@@ -1,17 +1,14 @@
 /*
- * mail2nntp.c
+ * lmtp2nntp.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 lmtp2nntp program reads mail as a LMTP server and posts it to one or
+ * more newsgroups using NNTP. It delivers the message immediately or fails.
  *
  * The OSSP Project, Cable & Wireless Deutschland GmbH
  * Thomas Lotterer, <thomas.lotterer@cw.com>
  *
  */
 
-
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -54,22 +51,45 @@
 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;
+static int helo_rfc0821domain(char *msg, char **domain);
+static int helo_rfc1035domain(char *msg, char **domain);
+
+struct session {
     int     lhlo_seen;
     char   *lhlo_domain;
+};
+static void resetsession(struct session *session);
+
+
+struct message {
+    char   *rfc822message;
+    char   *mail_from;
     char   *azRcpt;
     size_t  asRcpt;
+};
+static void resetmessage(struct message *message);
+
+typedef struct {
+    int     option_verbose;
+    int     option_tracing;
+    int     option_deliverymode;
+    int     option_groupmode;
+    char   *azArggroups;
+    size_t  asArggroups;
+    struct  session session;
+    struct  message message;
 } lmtp2nntp_t;
 
 enum {
-    MODE_ONCE,
-    MODE_MANY
+    DELIVERYMODE_ONCE,
+    DELIVERYMODE_MANY,
+    DELIVERYMODE_FULL
+};
+
+enum {
+    GROUPMODE_ARG,
+    GROUPMODE_ENVELOPE,
+    GROUPMODE_HEADER
 };
 
 /*
@@ -115,13 +135,15 @@
 
 int main(int argc, char **argv)
 {
-    int rc = 0;
+    //FIXME int rc = 0;
     lmtp_t *lmtp;
     lmtp_io_t lmtp_io;
     lmtp2nntp_t *ctx;
-    int i; /* general purpose scratch int, index ... */
+    int i;             /* general purpose scratch int, index ... */
     char *progname;
 
+#if 0
+    /* begin NNTP posting test */
     {
     int s;
     nntp_t *nntp;
@@ -135,8 +157,8 @@
     nntp_destroy(nntp);
     sock_destroy(s);
     exit(0);
-
     }
+#endif
 
     progname = argv[0];
 
@@ -145,16 +167,13 @@
         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->lhlo_seen = FALSE;
-    ctx->lhlo_domain = "";
-    ctx->azRcpt = NULL;
-    ctx->asRcpt = 0;
+    ctx->option_deliverymode = DELIVERYMODE_MANY;
+    ctx->option_groupmode = GROUPMODE_ARG;
+    ctx->azArggroups = NULL;
+    ctx->asArggroups = 0;
+    resetsession(&ctx->session);
+    resetmessage(&ctx->message);
 
-    /* read in the arguments */
     {
         char buf[1000];
         int bufused = 0;
@@ -167,33 +186,35 @@
             close(tracefile);
         }
     }
-    while ((i = getopt(argc, argv, "p:l:h:m:tvo:c:i:t:")) != -1) {
+
+    /* read in the arguments */
+    while ((i = getopt(argc, argv, "d:g:tv")) != -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]
+            case 'd': /* -d deliverymode */
+                if      (strcasecmp(optarg, "once") == 0)
+                    ctx->option_deliverymode = DELIVERYMODE_ONCE;
+                else if (strcasecmp(optarg, "many") == 0)
+                    ctx->option_deliverymode = DELIVERYMODE_MANY;
+                else if (strcasecmp(optarg, "full") == 0)
+                    ctx->option_deliverymode = DELIVERYMODE_FULL;
+                else {
+                    fprintf(stderr, "%s:Error: Invalid mode \"%s\" to option -d\n", progname, optarg);
+                    exit(ERR_EXECUTION);
+                }
                 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;
+            case 'g': /* -g groupmode */
+                if      (strcasecmp(optarg, "arg") == 0)
+                    ctx->option_groupmode = GROUPMODE_ARG;
+                else if (strcasecmp(optarg, "envelope") == 0)
+                    ctx->option_groupmode = GROUPMODE_ENVELOPE;
+                else if (strcasecmp(optarg, "header") == 0)
+                    ctx->option_groupmode = GROUPMODE_HEADER;
                 else {
-                    fprintf(stderr, "%s:Error: Invalid mode \"%s\" to option -m\n", progname, argv[optind]);
+                    fprintf(stderr, "%s:Error: Invalid mode \"%s\" to option -g\n", progname, optarg);
                     exit(ERR_EXECUTION);
                 }
                 break;
-            case 'd': // -t (tracing)
+            case 't': // -t (tracing)
                 ctx->option_tracing = TRUE;
                 break;
             case 'v': // -v (verbose)
@@ -205,9 +226,9 @@
                 exit(ERR_EXECUTION);
         }
     }
-    /* remaining arguments are newsgroup names */
+    /* remaining arguments are groups */
     for (i = optind; i < argc; i++)
-        argz_add(&ctx->azNewsgroups, &ctx->asNewsgroups, argv[i]);
+        argz_add(&ctx->azArggroups, &ctx->asArggroups, argv[i]);
 
     /* initialize LMTP context */
     lmtp_io.read  = trace_read;
@@ -230,6 +251,41 @@
     return 0;
 }
 
+static void resetsession(struct session *session)
+{
+
+    session->lhlo_seen = FALSE;
+
+    // fprintf(stderr, "DEBUG: session->lhlo_domain=***%s***\n", session->lhlo_domain);
+    if (session->lhlo_domain != NULL)
+        free(session->lhlo_domain); //FIXME what about non-graceful aborts?
+    session->lhlo_domain = NULL;
+
+    return;
+}
+
+static void resetmessage(struct message *message)
+{
+    // fprintf(stderr, "DEBUG: message->mail_from=***%s***\n", message->mail_from);
+    if (message->mail_from != NULL)
+        free(message->mail_from); //FIXME what about non-graceful aborts?
+    message->mail_from = NULL;
+
+    // fprintf(stderr, "DEBUG: message->azRcpt=***%s***\n", message->azRcpt);
+    if (message->azRcpt != NULL)
+        free(message->azRcpt); //FIXME what about non-graceful aborts?
+    message->azRcpt = NULL;
+
+    message->asRcpt = 0;
+
+    // fprintf(stderr, "DEBUG: message->rfc822message=***%s***\n", message->rfc822message);
+    if (message->rfc822message != NULL)
+        free(message->rfc822message); //FIXME what about non-graceful aborts?
+    message->rfc822message = NULL;
+
+    return;
+}
+
 static lmtp_rc_t lmtp_cb_lhlo(lmtp_t *lmtp, lmtp_io_t *io, lmtp_req_t *req, void *_ctx)
 {
     /*  
@@ -255,102 +311,150 @@
     lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
     lmtp_rc_t rc = LMTP_OK;
     lmtp_res_t res;
-    char errorstring[STDSTRLEN];
-    char *cp;
+    char str[STDSTRLEN];
 
-    cp = NULL;
-    if (ctx->lhlo_seen == TRUE) {
+    if (ctx->session.lhlo_seen == TRUE) {
         res.statuscode = "503";
         res.dsncode    = "5.0.0";
         res.statusmsg  = "Duplicate LHLO.";
     } else {
-        if (!str_parse(req->msg, 
-            "^.+ ("
-
-            /*
-             ##
-             ##  The mega Perl regular expression below is generated
-             ##  with the following Perl program. This is only possible
-             ##  because the given grammar is Chomsky-3 (right or left
-             ##  linear grammar, but noth both).
-             ##
-
-             # BNF grammar for <domain> according to RFC 821:
-             # <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
-             # <let-dig-hyp> ::= <a> | <d> | "-"
-             # <let-dig>     ::= <a> | <d>
-             # <ldh-str>     ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-             # <dotnum>      ::= <snum> "." <snum> "." <snum> "." <snum>
-             # <number>      ::= <d> | <d> <number>
-             # <name>        ::= <a> <ldh-str> <let-dig>
-             # <element>     ::= <name> | "#" <number> | "[" <dotnum> "]"
-             # <domain>      ::= <element> | <element> "." <domain>
-             
-   FIXME     # BNF grammar for <domain> according to RFC1035:
-             # <letter>      ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
-             # <digit>       ::= any one of the ten digits 0 through 9
-             # <let-dig>     ::= <letter> | <digit>
-             # <let-dig-hyp> ::= <let-dig> | "-"
-             # <ldh-str>     ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-             # <label>       ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-             # <subdomain>   ::= <label> | <subdomain> "." <label>
-             # <domain>      ::= <subdomain> | " "
-
-             # corresponding Perl regular expression ($domain)
-             $snum        = "(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])";
-             $d           = "[0-9]";
-             $a           = "[A-Za-z]";
-             $let_dig_hyp = "(?:$a|$d|-)";
-             $let_dig     = "(?:$a|$d)";
-             $ldh_str     = "${let_dig_hyp}+";
-             $dotnum      = "$snum\\.$snum\\.$snum\\.$snum";
-             $number      = "$d+";
-             $name        = "$a$ldh_str$let_dig";
-             $element     = "(?:$name|#$number|\\[$dotnum\\])";
-             $domain      = "(?:$element\.)*$element";
-             
-             # translate into C string block suitable for passing to the Perl
-             # Compatible Regular Expressions (PCRE) based string library Str.
-             my $cregex = $domain;
-             $cregex =~ s|\\|\\\\|sg;
-             $cregex =~ s|(.{70})|"$1"\n|sg;
-             $cregex =~ s|\n([^\n]+)$|\n"$1"|s;
-             print "$cregex\n";
-             */
-
-            "(?:(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\[(?:[0"
-            "-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0"
-            "-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0"
-            "-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5"
-            "])\\]).)*(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\"
-            "[(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]"
-            "{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{"
-            "2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|"
-            "25[0-5])\\])"
-
-            ")$", &cp, &ctx->lhlo_domain)) {
+        if (   helo_rfc0821domain(req->msg, &ctx->session.lhlo_domain)
+            || helo_rfc1035domain(req->msg, &ctx->session.lhlo_domain)) {
+            ctx->session.lhlo_seen = TRUE;
+            res.statuscode = "250";
+            res.dsncode    = NULL;             /* DSN not used for greeting */
+            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 */
+                       ctx->session.lhlo_domain);
+            res.statusmsg  = str;
+        } else {
             res.statuscode = "501";
             res.dsncode    = "5.0.0";
-            res.statusmsg  = "Please identify yourself. See <domain> BNF in RFC821.";
-        } else {
-            ctx->lhlo_seen = TRUE;
-            printf("DEBUG: cp=***%s***, v1=***%s***\n", cp, ctx->lhlo_domain);
-            str_format(errorstring, sizeof(errorstring), "Hello ***%s***", ctx->lhlo_domain);
-            res.statuscode = "250";
-            res.dsncode    = NULL; /* DSN not used for greeting */
-            res.statusmsg  = "ENHANCEDSTATUSCODES\nDSN\nPIPELINING\n8BITMIME";
-                /*
-                 *  RFC2034 = EHANCEDSTATUSCODES
-                 *  RFC1894 = DSN
-                 *  RFC1854 = PIPELINING
-                 *  RFC1652 = 8BITMIME
-                 */
+            res.statusmsg  = "Please identify yourself. Domain must match RFC0821/RFC1035.";
         }
     }
     lmtp_response(lmtp, &res);
-    if (cp) free(cp);
+    return rc;
+}
+
+static int helo_rfc0821domain(char *msg, char **domain)
+{
+    int rc;
+
+    rc = str_parse(msg, 
+            "^.+ ("
+    /*
+     ##
+     ##  The mega Perl regular expression below is generated
+     ##  with the following Perl program. This is only possible
+     ##  because the given grammar is Chomsky-3 (right or left
+     ##  linear grammar, but noth both).
+     ##
+    
+     # BNF grammar for <domain> according to RFC 821:
+     # <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
+     # <let-dig-hyp> ::= <a> | <d> | "-"
+     # <let-dig>     ::= <a> | <d>
+     # <ldh-str>     ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+     # <dotnum>      ::= <snum> "." <snum> "." <snum> "." <snum>
+     # <number>      ::= <d> | <d> <number>
+     # <name>        ::= <a> <ldh-str> <let-dig>
+     # <element>     ::= <name> | "#" <number> | "[" <dotnum> "]"
+     # <domain>      ::= <element> | <element> "." <domain>
+     #
+     # corresponding Perl regular expression ($domain)
+     $snum        = "(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])";
+     $d           = "[0-9]";
+     $a           = "[A-Za-z]";
+     $let_dig_hyp = "(?:$a|$d|-)";
+     $let_dig     = "(?:$a|$d)";
+     $ldh_str     = "${let_dig_hyp}+";
+     $dotnum      = "$snum\\.$snum\\.$snum\\.$snum";
+     $number      = "$d+";
+     $name        = "$a$ldh_str$let_dig";
+     $element     = "(?:$name|#$number|\\[$dotnum\\])";
+     $domain      = "(?:$element\.)*$element";
+     #
+     # translate into C string block suitable for passing to the Perl
+     # Compatible Regular Expressions (PCRE) based string library Str.
+     my $cregex = $domain;
+     $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";
+     */
+
+    "(?:(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\[(?:[0"
+    "-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0"
+    "-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0"
+    "-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5"
+    "])\\]).)*(?:[A-Za-z](?:[A-Za-z]|[0-9]|-)+(?:[A-Za-z]|[0-9])|#[0-9]+|\\"
+    "[(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]"
+    "{2}|[0-1][0-9]{2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{"
+    "2}|2[0-4][0-9]|25[0-5])\\.(?:[0-9]|[0-9]{2}|[0-1][0-9]{2}|2[0-4][0-9]|"
+    "25[0-5])\\])"
+
+    ")$", domain);
+    return rc;
+}
+
+static int helo_rfc1035domain(char *msg, char **domain)
+{
+    int rc;
+
+    rc = str_parse(msg, 
+            "^.+ ("
+    /*
+     ##
+     ##  The mega Perl regular expression below is generated
+     ##  with the following Perl program. This is only possible
+     ##  because the given grammar is Chomsky-3 (right or left
+     ##  linear grammar, but noth both).
+     ##
+
+     # BNF grammar for <domain> according to RFC1035:
+     # <letter>      ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
+     # <digit>       ::= any one of the ten digits 0 through 9
+     # <let-dig>     ::= <letter> | <digit>
+     # <let-dig-hyp> ::= <let-dig> | "-"
+     # <ldh-str>     ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+     # <label>       ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+     # <subdomain>   ::= <label> | <subdomain> "." <label>
+     # <domain>      ::= <subdomain> | " "
+     #
+     # corresponding Perl regular expression ($domain)
+     $letter      = "[A-Za-z]";
+     $digit       = "[0-9]";
+     $let_dig     = "(?:$letter|$digit)";
+     $let_dig_hyp = "(?:$let_dig|-)";
+     $ldh_str     = "${let_dig_hyp}+";
+     $label       = "(?:$letter(?:(?:$ldh_str)?$let_dig)?)";
+     $subdomain   = "(?:$label\.)*$label";
+     $domain      = "(?:$subdomain| )";
+     #
+     # translate into C string block suitable for passing to the Perl
+     # Compatible Regular Expressions (PCRE) based string library Str.
+     my $cregex = $domain;
+     $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";
+     */
+
+    "(?:(?:(?:[A-Za-z](?:(?:(?:(?:[A-Za-z]|[0-9])|-)+)?(?:[A-Za-z]|[0-9]))?"
+    ").)*(?:[A-Za-z](?:(?:(?:(?:[A-Za-z]|[0-9])|-)+)?(?:[A-Za-z]|[0-9]))?)|"
+    " )"
+
+    ")$", domain);
     return rc;
 }
 
@@ -360,15 +464,29 @@
     lmtp_rc_t rc = LMTP_OK;
     lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
 
-    if (ctx->lhlo_seen == TRUE) {
-        res.statuscode = "250";
-        res.dsncode    = "2.1.0";
-        res.statusmsg  = "Sender ok FIXME";
-        lmtp_response(lmtp, &res);
-    } else {
+    if (ctx->session.lhlo_seen != TRUE) {
         res.statuscode = "553";
         res.dsncode    = "5.1.8";
-        res.statusmsg  = "friendly people say LHLO to open a transmission channel";
+        res.statusmsg  = "friendly people say LHLO to open a transmission channel.";
+        lmtp_response(lmtp, &res);
+    }
+    else if (ctx->message.mail_from != NULL) {
+        res.statuscode = "503";
+        res.dsncode    = "5.5.0";
+        res.statusmsg  = "Sender already specified.";
+        lmtp_response(lmtp, &res);
+    }
+    else
+        if (!str_parse(req->msg, "m/^MAIL From: <(.+@.+)>$/i", &ctx->message.mail_from)) {
+        res.statuscode = "553";
+        res.dsncode    = "5.5.4";
+        res.statusmsg  = "Domain name required for sender address.";
+        lmtp_response(lmtp, &res);
+    }
+    else {
+        res.statuscode = "250";
+        res.dsncode    = "2.1.0";
+        res.statusmsg  = "Sender ok.";
         lmtp_response(lmtp, &res);
     }
     return rc;
@@ -379,13 +497,68 @@
     lmtp_rc_t rc = LMTP_OK;
     lmtp_res_t res;
     lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx;
+    char *cp;
+    char *group;
 
-    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);
+    if (ctx->message.mail_from == NULL) {
+        res.statuscode = "503";
+        res.dsncode    = "5.0.0";
+        res.statusmsg  = "specify sender with MAIL first.";
+        lmtp_response(lmtp, &res);
+        return LMTP_OK;
+    }
 
+    if (!str_parse(req->msg, "m/^RCPT To: (.+)$/i", &cp)) {
+        res.statuscode = "501";
+        res.dsncode    = "5.5.2";
+        res.statusmsg  = "Syntax error in parameters.";
+        lmtp_response(lmtp, &res);
+        return LMTP_OK;
+    }
+    
+    /* FIXME
+     * 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.
+     */
+
+    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.";
+            lmtp_response(lmtp, &res);
+            return LMTP_OK;
+        }
+        cp = group;
+        // fprintf(stderr, "DEBUG: after  transform cp=***%s***\n", cp);
+        //FIXME do additional transform and checking
+        if (0) {
+            res.statuscode = "550";
+            res.dsncode    = "5.1.1";
+            res.statusmsg  = "unmatched Group.";
+            lmtp_response(lmtp, &res);
+            return LMTP_OK;
+        }
+    }
+        
+    if ((cp == NULL) || (strlen(cp) == 0)) {
+        res.statuscode = "550";
+        res.dsncode    = "5.1.1";
+        res.statusmsg  = "nul Recipient/ Group.";
+        lmtp_response(lmtp, &res);
+        return LMTP_OK;
+    }
+    else {
+        // fprintf(stderr, "DEBUG: cp=***%s***\n", cp);
+        argz_add(&ctx->message.azRcpt, &ctx->message.asRcpt, cp);
+        res.statuscode = "250";
+        res.dsncode    = "2.1.5";
+        res.statusmsg  = "Recipient/ Group accepted";
+        lmtp_response(lmtp, &res);
+    }
     return rc;
 }
 
@@ -399,6 +572,14 @@
     char errorstring[STDSTRLEN];
     char *rcpt;
 
+    if (argz_count(ctx->message.azRcpt, ctx->message.asRcpt) == 0) {
+        res.statuscode = "503";
+        res.dsncode    = "5.0.0";
+        res.statusmsg  = "specify recipient with RCPT first.";
+        lmtp_response(lmtp, &res);
+        return LMTP_OK;
+    }
+
     res.statuscode = "354";
     res.dsncode    = NULL; /* DSN not used for data */
     res.statusmsg  = "Enter mail, end with \".\" on a line by itself";
@@ -406,28 +587,26 @@
     rc = lmtp_readmsg(lmtp, &buf, MESSAGE_MAXLEN);
 
     rcpt = NULL;
-    while ((rcpt = argz_next(ctx->azRcpt, ctx->asRcpt, rcpt)) != NULL)
+    while ((rcpt = argz_next(ctx->message.azRcpt, ctx->message.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);
+            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";
+            res.statusmsg  = "FIXME overflow";
         } 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));
+            str_format(errorstring, sizeof(errorstring), "Message accepted for delivery %s", strerror(errno));
             res.statusmsg  = errorstring;
         }
         lmtp_response(lmtp, &res);
     }
+    resetmessage(&ctx->message);
     return rc;
 }
 
@@ -435,33 +614,36 @@
 {
     lmtp_res_t res;
     lmtp_rc_t rc = LMTP_OK;
+
     res.statuscode = "250";
     res.dsncode    = "2.0.0";
-    res.statusmsg  = "OK";
+    res.statusmsg  = "OK. Nice talking to you.";
     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;
-    }
+    resetmessage(&ctx->message);
     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)
+
+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;
+
+    resetmessage(&ctx->message);
+    resetsession(&ctx->session);
     res.statuscode = "221";
     res.dsncode    = "2.0.0";
     res.statusmsg  = "LMTP Service closing transmission channel.";

CVSTrac 2.0.1