"Fossies" - the Fresh Open Source Software Archive

Member "mlmmj-1.3.0/src/mlmmj-send.c" (2 Oct 2016, 36895 Bytes) of package /linux/privat/mlmmj-1.3.0.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mlmmj-send.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.19.0_vs_1.3.0.

    1 /* Copyright (C) 2004, 2003, 2004 Mads Martin Joergensen <mmj at mmj.dk>
    2  *
    3  * $Id$
    4  *
    5  * Permission is hereby granted, free of charge, to any person obtaining a copy
    6  * of this software and associated documentation files (the "Software"), to
    7  * deal in the Software without restriction, including without limitation the
    8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    9  * sell copies of the Software, and to permit persons to whom the Software is
   10  * furnished to do so, subject to the following conditions:
   11  *
   12  * The above copyright notice and this permission notice shall be included in
   13  * all copies or substantial portions of the Software.
   14  *
   15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   21  * IN THE SOFTWARE.
   22  */
   23 
   24 #include <stdio.h>
   25 #include <stdlib.h>
   26 #include <unistd.h>
   27 #include <errno.h>
   28 #include <string.h>
   29 #include <sys/stat.h>
   30 #include <fcntl.h>
   31 #include <sys/types.h>
   32 #include <dirent.h>
   33 #include <sys/wait.h>
   34 #include <signal.h>
   35 #include <libgen.h>
   36 #include <syslog.h>
   37 #include <stdarg.h>
   38 #include <sys/mman.h>
   39 #include <limits.h>
   40 #include <netdb.h>
   41 #include <sys/socket.h>
   42 #include <netinet/in.h>
   43 #include <arpa/inet.h>
   44 #include <signal.h>
   45 
   46 #include "mlmmj.h"
   47 #include "mlmmj-send.h"
   48 #include "mail-functions.h"
   49 #include "itoa.h"
   50 #include "incindexfile.h"
   51 #include "chomp.h"
   52 #include "checkwait_smtpreply.h"
   53 #include "getlistaddr.h"
   54 #include "getlistdelim.h"
   55 #include "init_sockfd.h"
   56 #include "strgen.h"
   57 #include "log_error.h"
   58 #include "mygetline.h"
   59 #include "wrappers.h"
   60 #include "memory.h"
   61 #include "statctrl.h"
   62 #include "ctrlvalue.h"
   63 #include "mylocking.h"
   64 #include "getaddrsfromfd.h"
   65 
   66 static int addtohdr = 0;
   67 static int prepmailinmem = 0;
   68 static int maxverprecips = MAXVERPRECIPS;
   69 static int gotsigterm = 0;
   70 
   71 void catch_sig_term(int sig)
   72 {
   73     gotsigterm = 1;
   74 }
   75 
   76 char *get_index_from_filename(const char *filename)
   77 {
   78     char *myfilename, *indexstr, *ret;
   79     size_t len;
   80 
   81     myfilename = mystrdup(filename);
   82     if (!myfilename) {
   83         return NULL;
   84     }
   85 
   86     len = strlen(myfilename);
   87     if (len > 9 && (strcmp(myfilename + len - 9, "/mailfile") == 0)) {
   88         myfilename[len - 9] = '\0';
   89     }
   90 
   91     indexstr = strrchr(myfilename, '/');
   92     if (indexstr) {
   93         indexstr++;  /* skip the slash */
   94     } else {
   95         indexstr = myfilename;
   96     }
   97 
   98     ret = mystrdup(indexstr);
   99     myfree(myfilename);
  100 
  101     return ret;
  102 }
  103 
  104 char *bounce_from_adr(const char *recipient, const char *listadr,
  105               const char *listdelim, const char *mailfilename,
  106               const char *listdir)
  107 {
  108     char *bounceaddr, *myrecipient, *mylistadr;
  109     char *indexstr, *listdomain, *a = NULL, *mymailfilename;
  110     char *staticbounceaddr, *staticbounceaddr_localpart;
  111     char *staticbounceaddr_domain;
  112     size_t len;
  113 
  114     mymailfilename = mystrdup(mailfilename);
  115     if (!mymailfilename) {
  116         return NULL;
  117     }
  118 
  119     indexstr = get_index_from_filename(mymailfilename);
  120     if (!indexstr) {
  121         myfree(mymailfilename);
  122         return NULL;
  123     }
  124 
  125     myrecipient = mystrdup(recipient);
  126     if (!myrecipient) {
  127         myfree(mymailfilename);
  128         return NULL;
  129     }
  130     a = strchr(myrecipient, '@');
  131     if (a)
  132         *a = '=';
  133 
  134     mylistadr = mystrdup(listadr);
  135     if (!mylistadr) {
  136         myfree(mymailfilename);
  137         myfree(myrecipient);
  138         return NULL;
  139     }
  140 
  141     listdomain = strchr(mylistadr, '@');
  142     if (!listdomain) {
  143         myfree(mymailfilename);
  144         myfree(myrecipient);
  145         myfree(mylistadr);
  146         return NULL;
  147     }
  148     *listdomain++ = '\0';
  149 
  150     /* 11 = "bounces-" + "-" + "@" + NUL */
  151     len = strlen(mylistadr) + strlen(listdelim) + strlen(myrecipient)
  152            + strlen(indexstr) + strlen(listdomain) + 11;
  153 
  154     staticbounceaddr = ctrlvalue(listdir, "staticbounceaddr");
  155     if (staticbounceaddr) {
  156         staticbounceaddr_localpart = genlistname(staticbounceaddr);
  157         staticbounceaddr_domain = genlistfqdn(staticbounceaddr);
  158 
  159         /* localpart + "-" + domain */
  160         len += strlen(staticbounceaddr_localpart) + 1
  161                 + strlen(staticbounceaddr_domain);
  162     } else {
  163         staticbounceaddr_localpart = NULL;
  164         staticbounceaddr_domain = NULL;
  165     }
  166 
  167     bounceaddr = mymalloc(len);
  168     if (!bounceaddr) {
  169         myfree(staticbounceaddr);
  170         myfree(staticbounceaddr_localpart);
  171         myfree(staticbounceaddr_domain);
  172         myfree(myrecipient);
  173         myfree(mylistadr);
  174         return NULL;
  175     }
  176 
  177     if (staticbounceaddr) {
  178         snprintf(bounceaddr, len, "%s%s%s-bounces-%s-%s@%s", 
  179             staticbounceaddr_localpart, listdelim, mylistadr,
  180             indexstr, myrecipient, staticbounceaddr_domain);
  181 
  182         myfree(staticbounceaddr);
  183         myfree(staticbounceaddr_localpart);
  184         myfree(staticbounceaddr_domain);
  185     } else {
  186         snprintf(bounceaddr, len, "%s%sbounces-%s-%s@%s", mylistadr, listdelim,
  187             indexstr, myrecipient, listdomain);
  188     }
  189 
  190     myfree(myrecipient);
  191     myfree(mylistadr);
  192     myfree(indexstr);
  193     myfree(mymailfilename);
  194 
  195     return bounceaddr;
  196 }
  197 
  198 int bouncemail(const char *listdir, const char *mlmmjbounce, const char *from)
  199 {
  200     char *myfrom = mystrdup(from);
  201     char *listdelim = getlistdelim(listdir);
  202     char *addr, *num, *c;
  203     size_t len;
  204     pid_t pid = 0;
  205 
  206     if((c = strchr(myfrom, '@')) == NULL) {
  207         myfree(myfrom);
  208         myfree(listdelim);
  209         return 0; /* Success when malformed 'from' */
  210     }
  211     *c = '\0';
  212     num = strrchr(myfrom, '-');
  213     num++;
  214     c = strstr(myfrom, listdelim);
  215     myfrom = strchr(c, '-');
  216     myfrom++;
  217     len = num - myfrom - 1;
  218     addr = mymalloc(len + 1);
  219     addr[len] = '\0';
  220     strncpy(addr, myfrom, len);
  221 
  222     myfree(listdelim);
  223 
  224     pid = fork();
  225     
  226     if(pid < 0) {
  227         log_error(LOG_ARGS, "fork() failed!");
  228         return 1;
  229     }
  230     
  231     if(pid > 0)
  232         return 0;
  233     
  234     execlp(mlmmjbounce, mlmmjbounce,
  235             "-L", listdir,
  236             "-a", num,
  237             "-n", addr, (char *)NULL);
  238 
  239     log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjbounce);
  240 
  241     return 1;
  242 }
  243 
  244 int send_mail(int sockfd, const char *from, const char *to,
  245           const char *replyto, char *mailmap, size_t mailsize,
  246           const char *listdir, const char *mlmmjbounce,
  247           const char *hdrs, size_t hdrslen, const char *body,
  248           size_t bodylen)
  249 {
  250     int retval = 0;
  251     char *reply, *reply2, *tohdr;
  252 
  253     if(sockfd == -1)
  254         return EBADF;
  255 
  256     if(strchr(to, '@') == NULL) {
  257         errno = 0;
  258         log_error(LOG_ARGS, "No @ in address, ignoring %s",
  259                 to);
  260         return 0;
  261     }
  262     
  263     retval = write_mail_from(sockfd, from, "");
  264     if(retval) {
  265         log_error(LOG_ARGS, "Could not write MAIL FROM\n");
  266         return retval;
  267     }
  268     reply = checkwait_smtpreply(sockfd, MLMMJ_FROM);
  269     if(reply) {
  270         log_error(LOG_ARGS, "Error in MAIL FROM. Reply = [%s]",
  271                 reply);
  272         myfree(reply);
  273         write_rset(sockfd);
  274         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  275         if (reply2 != NULL) myfree(reply2);
  276         return MLMMJ_FROM;
  277     }
  278     retval = write_rcpt_to(sockfd, to);
  279     if(retval) {
  280         log_error(LOG_ARGS, "Could not write RCPT TO:\n");
  281         return retval;
  282     }
  283 
  284     reply = checkwait_smtpreply(sockfd, MLMMJ_RCPTTO);
  285     if(reply) {
  286         write_rset(sockfd);
  287         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  288         if (reply2 != NULL) myfree(reply2);
  289         if(mlmmjbounce && ((reply[0] == '4') || (reply[0] == '5'))
  290                 && (reply[1] == '5')) {
  291             myfree(reply);
  292             return bouncemail(listdir, mlmmjbounce, from);
  293         } else {
  294             log_error(LOG_ARGS, "Error in RCPT TO. Reply = [%s]",
  295                     reply);
  296             myfree(reply);
  297             return MLMMJ_RCPTTO;
  298         }
  299     }
  300 
  301     retval = write_data(sockfd);
  302     if(retval) {
  303         log_error(LOG_ARGS, "Could not write DATA\b");
  304         return retval;
  305     }
  306 
  307     reply = checkwait_smtpreply(sockfd, MLMMJ_DATA);
  308     if(reply) {
  309         log_error(LOG_ARGS, "Error with DATA. Reply = [%s]", reply);
  310         myfree(reply);
  311         write_rset(sockfd);
  312         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  313         if (reply2 != NULL) myfree(reply2);
  314         return MLMMJ_DATA;
  315     }
  316 
  317     if(replyto) {
  318         retval = write_replyto(sockfd, replyto);
  319         if(retval) {
  320             log_error(LOG_ARGS, "Could not write reply-to addr.\n");
  321             return retval;
  322         }
  323     }
  324 
  325     if(addtohdr)
  326         tohdr = concatstr(3, "To: ", to, "\r\n");
  327     else
  328         tohdr = NULL;
  329 
  330     if(prepmailinmem) {
  331         retval = writen(sockfd, hdrs, hdrslen);
  332         if(retval < 0) {
  333             log_error(LOG_ARGS, "Could not write mailheaders.\n");
  334             return retval;
  335         }
  336         if(tohdr) {
  337             retval = writen(sockfd, tohdr, strlen(tohdr));
  338             if(retval < 0) {
  339                 log_error(LOG_ARGS, "Could not write To:.\n");
  340                 return retval;
  341             }
  342             myfree(tohdr);
  343         }
  344         retval = writen(sockfd, body, bodylen);
  345         if(retval < 0) {
  346             log_error(LOG_ARGS, "Could not write mailbody.\n");
  347             return retval;
  348         }
  349     } else {
  350         retval = write_mailbody_from_map(sockfd, mailmap, mailsize,
  351                          tohdr);
  352         if(retval) {
  353             log_error(LOG_ARGS, "Could not write mail\n");
  354             return retval;
  355         }
  356     }
  357 
  358     retval = write_dot(sockfd);
  359     if(retval) {
  360         log_error(LOG_ARGS, "Could not write <CR><LF>.<CR><LF>\n");
  361         return retval;
  362     }
  363 
  364     reply = checkwait_smtpreply(sockfd, MLMMJ_DOT);
  365     if(reply) {
  366         log_error(LOG_ARGS, "Mailserver did not ack end of mail.\n"
  367                 "<CR><LF>.<CR><LF> was written, to no"
  368                 "avail. Reply = [%s]", reply);
  369         myfree(reply);
  370         write_rset(sockfd);
  371         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  372         if (reply2 != NULL) myfree(reply2);
  373         return MLMMJ_DOT;
  374     }
  375 
  376     return 0;
  377 }
  378 
  379 int initsmtp(int *sockfd, const char *relayhost, unsigned short port, const char *heloname)
  380 {
  381     int retval = 0;
  382     int try_ehlo = 1;
  383     char *reply = NULL;
  384 
  385     do {
  386         init_sockfd(sockfd, relayhost, port);
  387 
  388         if(*sockfd == -1) {
  389             retval = EBADF;
  390             break;
  391         }
  392 
  393         if((reply = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != NULL) {
  394             log_error(LOG_ARGS, "No proper greeting to our connect"
  395                     "Reply: [%s]", reply);
  396             myfree(reply);
  397             retval = MLMMJ_CONNECT;
  398             /* FIXME: Queue etc. */
  399             break;
  400         }
  401 
  402         if (try_ehlo) {
  403             write_ehlo(*sockfd, heloname);
  404             if((reply = checkwait_smtpreply(*sockfd, MLMMJ_EHLO))
  405                     == NULL) {
  406                 /* EHLO successful don't try more */
  407                 break;
  408             }
  409 
  410             /* RFC 1869 - 4.5. - In the case of any error response,
  411              * the client SMTP should issue either the HELO or QUIT
  412              * command.
  413              * RFC 1869 - 4.5. - If the server SMTP recognizes the
  414              * EHLO command, but the command argument is
  415              * unacceptable, it will return code 501.
  416              */
  417             if (strncmp(reply, "501", 3) == 0) {
  418                 myfree(reply);
  419                 /* Commmand unacceptable; we choose to QUIT but
  420                  * ignore any QUIT errors; return that EHLO was
  421                  * the error.
  422                  */
  423                 endsmtp(sockfd);
  424                 retval = MLMMJ_EHLO;
  425                 break;
  426             }
  427 
  428             /* RFC 1869 - 4.6. - A server SMTP that conforms to RFC
  429              * 821 but does not support the extensions specified
  430              * here will not recognize the EHLO command and will
  431              * consequently return code 500, as specified in RFC
  432              * 821.  The server SMTP should stay in the same state
  433              * after returning this code (see section 4.1.1 of RFC
  434              * 821).  The client SMTP may then issue either a HELO
  435              * or a QUIT command.
  436              */
  437 
  438             if (reply[0] != '5') {
  439                 myfree(reply);
  440                 /* Server doesn't understand EHLO, but gives a
  441                  * broken response. Try with new connection.
  442                  */
  443                 endsmtp(sockfd);
  444                 try_ehlo = 0;
  445                 continue;
  446             }
  447 
  448             myfree(reply);
  449 
  450             /* RFC 1869 - 4.7. - Other improperly-implemented
  451              * servers will not accept a HELO command after EHLO has
  452              * been sent and rejected.  In some cases, this problem
  453              * can be worked around by sending a RSET after the
  454              * failure response to EHLO, then sending the HELO.
  455              */
  456             write_rset(*sockfd);
  457             reply = checkwait_smtpreply(*sockfd, MLMMJ_RSET);
  458 
  459             /* RFC 1869 - 4.7. - Clients that do this should be
  460              * aware that many implementations will return a failure
  461              * code (e.g., 503 Bad sequence of commands) in response
  462              * to the RSET. This code can be safely ignored.
  463              */
  464             myfree(reply);
  465 
  466             /* Try HELO on the same connection
  467              */
  468         }
  469 
  470         write_helo(*sockfd, heloname);
  471         if((reply = checkwait_smtpreply(*sockfd, MLMMJ_HELO))
  472                 == NULL) {
  473             /* EHLO successful don't try more */
  474             break;
  475         }
  476         if (try_ehlo) {
  477             myfree(reply);
  478             /* We reused a connection we tried EHLO on. Maybe
  479              * that's why it failed. Try with new connection.
  480              */
  481             endsmtp(sockfd);
  482             try_ehlo = 0;
  483             continue;
  484         }
  485 
  486         log_error(LOG_ARGS, "Error with HELO. Reply: "
  487                 "[%s]", reply);
  488         myfree(reply);
  489         /* FIXME: quit and tell admin to configure
  490          * correctly */
  491         retval = MLMMJ_HELO;
  492         break;
  493 
  494     } while (1);
  495 
  496     return retval;
  497 }
  498 
  499 int endsmtp(int *sockfd)
  500 {
  501     int retval = 0;
  502     char *reply = NULL;
  503 
  504     if(*sockfd == -1)
  505         return retval;
  506     
  507     write_quit(*sockfd);
  508     reply = checkwait_smtpreply(*sockfd, MLMMJ_QUIT);
  509     if(reply) {
  510         printf("reply from quit: %s\n", reply);
  511         log_error(LOG_ARGS, "Mailserver would not let us QUIT. "
  512               "We close the socket anyway though. "
  513               "Mailserver reply = [%s]", reply);
  514         myfree(reply);
  515         retval = MLMMJ_QUIT;
  516     }
  517 
  518     close(*sockfd);
  519     *sockfd = -1;
  520 
  521     return retval;
  522 }
  523 
  524 int send_mail_verp(int sockfd, struct strlist *addrs, char *mailmap,
  525            size_t mailsize, const char *from, const char *listdir,
  526            const char *hdrs, size_t hdrslen, const char *body,
  527            size_t bodylen, const char *verpextra)
  528 {
  529     int retval, i;
  530     char *reply, *reply2;
  531 
  532     if(sockfd == -1)
  533         return EBADF;
  534 
  535     retval = write_mail_from(sockfd, from, verpextra);
  536     if(retval) {
  537         log_error(LOG_ARGS, "Could not write MAIL FROM\n");
  538         return retval;
  539     }
  540     reply = checkwait_smtpreply(sockfd, MLMMJ_FROM);
  541     if(reply) {
  542         log_error(LOG_ARGS, "Error in MAIL FROM. Reply = [%s]",
  543                 reply);
  544         myfree(reply);
  545         write_rset(sockfd);
  546         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  547         if (reply2 != NULL) myfree(reply2);
  548         return MLMMJ_FROM;
  549     }
  550     for(i = 0; i < addrs->count; i++) {
  551         if(gotsigterm) {
  552             log_error(LOG_ARGS, "TERM signal received, "
  553                     "shutting down.");
  554             return -1;
  555         }
  556         if(strchr(addrs->strs[i], '@') == NULL) {
  557             errno = 0;
  558             log_error(LOG_ARGS, "No @ in address, ignoring %s",
  559                     addrs->strs[i]);
  560             continue;
  561         }
  562         retval = write_rcpt_to(sockfd, addrs->strs[i]);
  563         if(retval) {
  564             log_error(LOG_ARGS, "Could not write RCPT TO:\n");
  565             return retval;
  566         }
  567 
  568         reply = checkwait_smtpreply(sockfd, MLMMJ_RCPTTO);
  569         if(reply) {
  570             log_error(LOG_ARGS, "Error in RCPT TO. Reply = [%s]",
  571                     reply);
  572             myfree(reply);
  573             return MLMMJ_RCPTTO;
  574         }
  575     }
  576 
  577     retval = write_data(sockfd);
  578     if(retval) {
  579         log_error(LOG_ARGS, "Could not write DATA\b");
  580         return retval;
  581     }
  582 
  583     reply = checkwait_smtpreply(sockfd, MLMMJ_DATA);
  584     if(reply) {
  585         log_error(LOG_ARGS, "Error with DATA. Reply = [%s]", reply);
  586         myfree(reply);
  587         write_rset(sockfd);
  588         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  589         if (reply2 != NULL) myfree(reply2);
  590         return MLMMJ_DATA;
  591     }
  592 
  593     if(prepmailinmem) {
  594         retval = writen(sockfd, hdrs, hdrslen);
  595         if(retval < 0) {
  596             log_error(LOG_ARGS, "Could not write mailheaders.\n");
  597             return retval;
  598         }
  599         retval = writen(sockfd, body, bodylen);
  600         if(retval < 0) {
  601             log_error(LOG_ARGS, "Could not write mailbody.\n");
  602             return retval;
  603         }
  604     } else {
  605         retval = write_mailbody_from_map(sockfd, mailmap, mailsize,
  606                          NULL);
  607         if(retval) {
  608             log_error(LOG_ARGS, "Could not write mail\n");
  609             return retval;
  610         }
  611     }
  612 
  613     retval = write_dot(sockfd);
  614     if(retval) {
  615         log_error(LOG_ARGS, "Could not write <CR><LF>.<CR><LF>\n");
  616         return retval;
  617     }
  618 
  619     reply = checkwait_smtpreply(sockfd, MLMMJ_DOT);
  620     if(reply) {
  621         log_error(LOG_ARGS, "Mailserver did not ack end of mail.\n"
  622                 "<CR><LF>.<CR><LF> was written, to no"
  623                 "avail. Reply = [%s]", reply);
  624         myfree(reply);
  625         write_rset(sockfd);
  626         reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
  627         if (reply2 != NULL) myfree(reply2);
  628         return MLMMJ_DOT;
  629     }
  630 
  631     return 0;
  632 }
  633 
  634 int send_mail_many_fd(int sockfd, const char *from, const char *replyto,
  635               char *mailmap, size_t mailsize, int subfd,
  636               const char *listaddr, const char *listdelim,
  637               const char *archivefilename, const char *listdir,
  638               const char *mlmmjbounce, const char *hdrs, size_t hdrslen,
  639               const char *body, size_t bodylen)
  640 {
  641     int res, ret, i;
  642     struct strlist stl;
  643 
  644     stl.strs = NULL;
  645     stl.count = 0;
  646 
  647     do {
  648         res = getaddrsfromfd(&stl, subfd, maxverprecips);
  649         if(stl.count == maxverprecips) {
  650             ret = send_mail_many_list(sockfd, from, replyto,
  651                     mailmap, mailsize, &stl, listaddr,
  652                     listdelim, archivefilename, listdir,
  653                     mlmmjbounce, hdrs, hdrslen,
  654                     body, bodylen);
  655             for(i = 0; i < stl.count; i++)
  656                 myfree(stl.strs[i]);
  657             if(ret < 0)
  658                 return ret;
  659             stl.count = 0;
  660         }
  661     } while(res > 0);
  662 
  663     if(stl.count) {
  664         ret = send_mail_many_list(sockfd, from, replyto, mailmap,
  665                 mailsize, &stl, listaddr, listdelim,
  666                 archivefilename, listdir, mlmmjbounce,
  667                 hdrs, hdrslen, body, bodylen);
  668         for(i = 0; i < stl.count; i++)
  669             myfree(stl.strs[i]);
  670         stl.count = 0;
  671         return ret;
  672     }
  673 
  674     return 0;
  675 }
  676 
  677 int requeuemail(const char *listdir, const char *index, struct strlist *addrs,
  678         int addrcount)
  679 {
  680     int addrfd, i;
  681     char *dirname, *addrfilename, *addr;
  682     
  683     dirname = concatstr(3, listdir, "/requeue/", index);
  684     if(mkdir(dirname, 0750) < 0 && errno != EEXIST) {
  685         log_error(LOG_ARGS, "Could not mkdir(%s) for "
  686                 "requeueing. Mail cannot "
  687                 "be requeued.", dirname);
  688         myfree(dirname);
  689         return -1;
  690     }
  691     addrfilename = concatstr(2, dirname, "/subscribers");
  692     myfree(dirname);
  693     addrfd = open(addrfilename, O_WRONLY|O_CREAT|O_APPEND,
  694             S_IRUSR|S_IWUSR);
  695     if(addrfd < 0) {
  696         log_error(LOG_ARGS, "Could not open %s",
  697                 addrfilename);
  698         myfree(addrfilename);
  699         return -1;
  700     } else {
  701         /* Dump the remaining addresses. We dump the remaining before
  702          * we write the failing address to ensure the potential good
  703          * ones will be tried first when mlmmj-maintd sends out mails
  704          * that have been requeued. addrcount was so far we were */
  705         for(i = addrcount + 1; i < addrs->count; i++) {
  706             addr = concatstr(2, addrs->strs[i], "\n");
  707             if(writen(addrfd, addr, strlen(addr)) < 0) {
  708                 log_error(LOG_ARGS, "Could not add [%s] "
  709                             "to requeue file", addr);
  710                 return -1;
  711             }
  712             myfree(addr);
  713         }
  714         addr = concatstr(2, addrs->strs[addrcount], "\n");
  715         if(writen(addrfd, addr, strlen(addr)) < 0) {
  716             log_error(LOG_ARGS, "Could not add [%s] to requeue "
  717                     "file", addr);
  718             return -1;
  719         }
  720         myfree(addr);
  721     }
  722     myfree(addrfilename);
  723     close(addrfd);
  724 
  725     return 0;
  726 }
  727 
  728 int send_mail_many_list(int sockfd, const char *from, const char *replyto,
  729            char *mailmap, size_t mailsize, struct strlist *addrs,
  730            const char *listaddr, const char *listdelim,
  731            const char *archivefilename, const char *listdir,
  732            const char *mlmmjbounce, const char *hdrs, size_t hdrslen,
  733            const char *body, size_t bodylen)
  734 {
  735     int res = 0, i, status;
  736     char *bounceaddr, *addr, *index;
  737 
  738     for(i = 0; i < addrs->count; i++) {
  739         addr = addrs->strs[i];
  740         if(strchr(addr, '@') == NULL) {
  741             errno = 0;
  742             log_error(LOG_ARGS, "No @ in address, ignoring %s",
  743                     addr);
  744             continue;
  745         }
  746         if(gotsigterm && listaddr && archivefilename) {
  747             /* we got SIGTERM, so save the addresses and bail */
  748             log_error(LOG_ARGS, "TERM signal received, "
  749                         "shutting down.");
  750             index = get_index_from_filename(archivefilename);
  751             status = requeuemail(listdir, index, addrs, i);
  752             myfree(index);
  753             return status;
  754         }
  755         if(from) {
  756             res = send_mail(sockfd, from, addr, replyto,
  757                         mailmap, mailsize, listdir, NULL,
  758                         hdrs, hdrslen, body, bodylen);
  759         } else {
  760             bounceaddr = bounce_from_adr(addr, listaddr, listdelim,
  761                              archivefilename, listdir);
  762             res = send_mail(sockfd, bounceaddr, addr, replyto,
  763                   mailmap, mailsize, listdir, mlmmjbounce,
  764                   hdrs, hdrslen, body, bodylen);
  765             myfree(bounceaddr);
  766         }
  767         if(res && listaddr && archivefilename) {
  768             /* we failed, so save the addresses and bail */
  769             index = get_index_from_filename(archivefilename);
  770             status = requeuemail(listdir, index, addrs, i);
  771             myfree(index);
  772             return status;
  773         }
  774     }
  775     return 0;
  776 }   
  777 
  778 static void print_help(const char *prg)
  779 {
  780     printf("Usage: %s [-L /path/to/list -m /path/to/mail | -l listctrl]\n"
  781            "       [-a] [-D] [-F sender@example.org] [-h] [-o address@example.org]\n"
  782            "       [-r 127.0.0.1] [-R reply@example.org] [-s /path/to/subscribers]\n"
  783            "       [-T recipient@example.org] [-V]\n"
  784            " -a: Don't archive the mail\n"
  785            " -D: Don't delete the mail after it's sent\n"
  786            " -F: What to use as MAIL FROM:\n"
  787            " -h: This help\n"
  788            " -l: List control variable:\n", prg);
  789     printf("    '1' means 'send a single mail'\n"
  790            "    '2' means 'mail to moderators'\n"
  791            "    '3' means 'resend failed list mail'\n"
  792            "    '4' means 'send to file with recipients'\n"
  793            "    '5' means 'bounceprobe'\n"
  794            "    '6' means 'single listmail to single recipient'\n"
  795            "    '7' means 'digest'\n");
  796     printf(" -L: Full path to list directory\n"
  797            " -m: Full path to mail file\n"
  798            " -o: Address to omit from distribution (normal mail only)\n"
  799            " -r: Relayhost IP address (defaults to 127.0.0.1)\n"
  800            " -R: What to use as Reply-To: header\n"
  801            " -s: Subscribers file name\n"
  802            " -T: What to use as RCPT TO:\n"
  803            " -V: Print version\n");
  804     exit(EXIT_SUCCESS);
  805 }
  806 
  807 int main(int argc, char **argv)
  808 {
  809     size_t len = 0, hdrslen, bodylen;
  810     int sockfd = -1, mailfd = 0, opt, mindex = 0, subfd = 0, tmpfd, i;
  811     int deletewhensent = 1, sendres = 0, archive = 1, digest = 0;
  812     int ctrlarchive, res;
  813     char *listaddr = NULL, *listdelim = NULL;
  814     char *mailfilename = NULL, *subfilename = NULL, *omit = NULL;
  815     char *replyto = NULL, *bounceaddr = NULL, *to_addr = NULL;
  816     char *relayhost = NULL, *archivefilename = NULL, *tmpstr;
  817     char *listctrl = NULL, *subddirname = NULL, *listdir = NULL;
  818     char *mlmmjbounce = NULL, *bindir, *mailmap, *probefile, *a;
  819     char *body = NULL, *hdrs = NULL, *memmailsizestr = NULL, *verp = NULL;
  820     char relay[16], *listname, *listfqdn, *verpfrom, *maxverprecipsstr;
  821     char strindex[32], *reply, *strport, *smtphelo, *requeuefilename;
  822     ssize_t memmailsize = 0;
  823     DIR *subddir;
  824     struct dirent *dp;
  825     struct stat st;
  826     struct hostent *relayent;
  827     uid_t uid;
  828     struct strlist stl;
  829     unsigned short smtpport = 25;
  830     struct sigaction sigact;
  831 
  832     CHECKFULLPATH(argv[0]);
  833     
  834     log_set_name(argv[0]);
  835 
  836     bindir = mydirname(argv[0]);
  837     mlmmjbounce = concatstr(2, bindir, "/mlmmj-bounce");
  838     myfree(bindir);
  839     
  840     /* install signal handler for SIGTERM */
  841     sigact.sa_handler = catch_sig_term;
  842     sigemptyset(&sigact.sa_mask);
  843     sigact.sa_flags = 0;
  844     if(sigaction(SIGTERM, &sigact, NULL) < 0)
  845         log_error(LOG_ARGS, "Could not install SIGTERM handler!");
  846 
  847     while ((opt = getopt(argc, argv, "aVDhm:l:L:R:F:T:r:s:o:")) != -1){
  848         switch(opt) {
  849         case 'a':
  850             archive = 0;
  851             break;
  852         case 'D':
  853             deletewhensent = 0;
  854             break;
  855         case 'F':
  856             bounceaddr = optarg;
  857             break;
  858         case 'h':
  859             print_help(argv[0]);
  860             break;
  861         case 'l':
  862             listctrl = optarg;
  863             break;
  864         case 'L':
  865             listdir = optarg;
  866             break;
  867         case 'm':
  868             mailfilename = optarg;
  869             break;
  870         case 'o':
  871             omit = optarg;
  872             break;
  873         case 'r':
  874             relayhost = optarg;
  875             break;
  876         case 'R':
  877             replyto = optarg;
  878             break;
  879         case 's':
  880             subfilename = optarg;
  881             break;
  882         case 'T':
  883             to_addr = optarg;
  884             break;
  885         case 'V':
  886             print_version(argv[0]);
  887             exit(EXIT_SUCCESS);
  888         }
  889     }
  890 
  891     if(mailfilename == NULL || (listdir == NULL && listctrl == NULL)) {
  892         fprintf(stderr, "You have to specify -m and -L or -l\n");
  893         fprintf(stderr, "%s -h for help\n", argv[0]);
  894         exit(EXIT_FAILURE);
  895     }
  896 
  897     /* Lets make sure no random user tries to send mail to the list */
  898     if(listdir) {
  899         if(stat(listdir, &st) == 0) {
  900             uid = getuid();
  901             if(uid && uid != st.st_uid) {
  902                 log_error(LOG_ARGS,
  903                     "Have to invoke either as root "
  904                     "or as the user owning listdir");
  905                 writen(STDERR_FILENO,
  906                     "Have to invoke either as root "
  907                     "or as the user owning listdir\n", 60);
  908                 exit(EXIT_FAILURE);
  909             }
  910         } else {
  911             log_error(LOG_ARGS, "Could not stat %s", listdir);
  912             exit(EXIT_FAILURE);
  913         }
  914     }
  915 
  916     if(!listctrl)
  917         listctrl = mystrdup("0");
  918 
  919     
  920     /* get the list address */
  921     if(listctrl[0] == '1' && (bounceaddr == NULL || to_addr == NULL)) {
  922         fprintf(stderr, "With -l 1 you need -F and -T\n");
  923         exit(EXIT_FAILURE);
  924     }
  925 
  926     if((listctrl[0] == '2' && (listdir == NULL || bounceaddr == NULL))) {
  927         fprintf(stderr, "With -l 2 you need -L and -F\n");
  928         exit(EXIT_FAILURE);
  929     }
  930 
  931     if((listctrl[0] == '7' && listdir == NULL)) {
  932         fprintf(stderr, "With -l 7 you need -L\n");
  933         exit(EXIT_FAILURE);
  934     }
  935 
  936     verp = ctrlvalue(listdir, "verp");
  937     if(verp == NULL)
  938         if(statctrl(listdir, "verp") == 1)
  939             verp = mystrdup("");
  940 
  941     maxverprecipsstr = ctrlvalue(listdir, "maxverprecips");
  942     if(verp && maxverprecipsstr) {
  943         maxverprecips = atol(maxverprecipsstr);
  944         myfree(maxverprecipsstr);
  945     }
  946 
  947     if(maxverprecips <= 0)
  948         maxverprecips = MAXVERPRECIPS;
  949 
  950     stl.strs = NULL;
  951     stl.count = 0;
  952 
  953     switch(listctrl[0]) {
  954         case '1':
  955         case '2':
  956         case '3':
  957         case '4':
  958         case '5':
  959         case '6':
  960         case '7':
  961             archive = 0;
  962         default:
  963             break;
  964     }
  965 
  966     if(listdir && listctrl[0] != '5')
  967         listaddr = getlistaddr(listdir);
  968     
  969     /* initialize file with mail to send */
  970 
  971     if((mailfd = open(mailfilename, O_RDWR)) < 0) {
  972             log_error(LOG_ARGS, "Could not open '%s'", mailfilename);
  973         exit(EXIT_FAILURE);
  974     }
  975 
  976     if(myexcllock(mailfd) < 0) {
  977         log_error(LOG_ARGS, "Could not lock '%s'."
  978                 "Mail not sent!", mailfilename);
  979         exit(EXIT_FAILURE);
  980     }
  981 
  982     if(fstat(mailfd, &st) < 0) {
  983         log_error(LOG_ARGS, "Could not stat mailfd");
  984         exit(EXIT_FAILURE);
  985     }
  986 
  987     memmailsizestr = ctrlvalue(listdir, "memorymailsize");
  988     ctrlarchive = statctrl(listdir, "noarchive");
  989     if(memmailsizestr) {
  990         memmailsize = strtol(memmailsizestr, NULL, 10);
  991         myfree(memmailsizestr);
  992     }
  993 
  994     if(memmailsize == 0)
  995         memmailsize = MEMORYMAILSIZE;
  996 
  997     if(st.st_size > memmailsize) {
  998         prepmailinmem = 0;
  999         errno = 0;
 1000         log_error(LOG_ARGS, "Not preparing in memory. "
 1001                     "Mail is %ld bytes", (long)st.st_size);
 1002     } else
 1003         prepmailinmem = 1;
 1004 
 1005     mailmap = mmap(0, st.st_size, PROT_READ, MAP_SHARED, mailfd, 0);
 1006     if(mailmap == MAP_FAILED) {
 1007         log_error(LOG_ARGS, "Could not mmap mailfd");
 1008         exit(EXIT_FAILURE);
 1009     }
 1010 
 1011     if(prepmailinmem) {
 1012         hdrs = get_preppedhdrs_from_map(mailmap, &hdrslen);
 1013         if(hdrs == NULL) {
 1014             log_error(LOG_ARGS, "Could not prepare headers");
 1015             exit(EXIT_FAILURE);
 1016         }
 1017         body = get_prepped_mailbody_from_map(mailmap, st.st_size,
 1018                              &bodylen);
 1019         if(body == NULL) {
 1020             log_error(LOG_ARGS, "Could not prepare mailbody");
 1021             myfree(hdrs);
 1022             exit(EXIT_FAILURE);
 1023         }
 1024     }
 1025 
 1026     if(listdir)
 1027         listdelim = getlistdelim(listdir);
 1028 
 1029     switch(listctrl[0]) {
 1030     case '1': /* A single mail is to be sent, do nothing */
 1031     case '5':
 1032         break;
 1033     case '2': /* Moderators */
 1034         subfilename = concatstr(2, listdir, "/control/moderators");
 1035         if((subfd = open(subfilename, O_RDONLY)) < 0) {
 1036             log_error(LOG_ARGS, "Could not open '%s':",
 1037                         subfilename);
 1038             myfree(hdrs);
 1039             myfree(body);
 1040             myfree(subfilename);
 1041             myfree(listdelim);
 1042             /* No moderators is no error. Could be the sysadmin
 1043              * likes to do it manually.
 1044              */
 1045             exit(EXIT_SUCCESS);
 1046         }
 1047         break;
 1048     case '3':
 1049         addtohdr = statctrl(listdir, "addtohdr");
 1050     case '4': /* sending mails to subfile */
 1051         if((subfd = open(subfilename, O_RDONLY)) < 0) {
 1052             log_error(LOG_ARGS, "Could not open '%s':",
 1053                         subfilename);
 1054             myfree(hdrs);
 1055             myfree(body);
 1056             myfree(listdelim);
 1057             exit(EXIT_FAILURE);
 1058         }
 1059         break;
 1060     case '6':
 1061         archive = 0;
 1062         deletewhensent = 0;
 1063         archivefilename = mystrdup(mailfilename);
 1064         bounceaddr = bounce_from_adr(to_addr, listaddr, listdelim,
 1065                         archivefilename, listdir);
 1066         break;
 1067     default: /* normal list mail -- now handled when forking */
 1068         addtohdr = statctrl(listdir, "addtohdr");
 1069         break;
 1070     }
 1071 
 1072     /* initialize the archive filename */
 1073     if(archive) {
 1074         mindex = incindexfile((const char *)listdir);
 1075         len = strlen(listdir) + 9 + 20;
 1076         archivefilename = mymalloc(len);
 1077         snprintf(archivefilename, len, "%s/archive/%d", listdir,
 1078              mindex);
 1079     }
 1080 
 1081     itoa(mindex, strindex);
 1082 
 1083     if(!relayhost) {
 1084         relayhost = ctrlvalue(listdir, "relayhost");
 1085     }
 1086     if(!relayhost)
 1087         strncpy(relay, RELAYHOST, sizeof(relay));
 1088     else {
 1089         relayent = gethostbyname(relayhost);
 1090         if(relayent == NULL) {
 1091             strncpy(relay, RELAYHOST, sizeof(relay));
 1092         } else {
 1093             if(inet_ntop(relayent->h_addrtype,
 1094                      relayent->h_addr_list[0],
 1095                      relay, sizeof(relay)) == NULL)
 1096                 strncpy(relay, RELAYHOST, sizeof(relay));
 1097         }
 1098     }
 1099     strport = ctrlvalue(listdir, "smtpport");
 1100     if(strport)
 1101         smtpport = (unsigned short)atol(strport);
 1102 
 1103     if ((smtphelo = ctrlvalue(listdir, "smtphelo")) == NULL) {
 1104         smtphelo = hostnamestr();
 1105     }
 1106 
 1107     switch(listctrl[0]) {
 1108     case '1': /* A single mail is to be sent */
 1109     case '6':
 1110         initsmtp(&sockfd, relay, smtpport, smtphelo);
 1111         if(send_mail(sockfd, bounceaddr, to_addr, replyto,
 1112                 mailmap, st.st_size, listdir, NULL,
 1113                 hdrs, hdrslen, body, bodylen)) {
 1114             close(sockfd);
 1115             sockfd = -1;
 1116             /* error, so keep it in the queue */
 1117             deletewhensent = 0;
 1118             /* dump data we want when resending first check
 1119              * if it already exists. In that case continue */
 1120             tmpstr = concatstr(2, mailfilename, ".mailfrom");
 1121             if(stat(tmpstr, &st) == 0) {
 1122                 myfree(tmpstr);
 1123                 break;
 1124             }
 1125             tmpfd = open(tmpstr, O_WRONLY|O_CREAT|O_TRUNC,
 1126                         S_IRUSR|S_IWUSR);
 1127             myfree(tmpstr);
 1128             if(tmpfd >= 0) {
 1129                 writen(tmpfd, bounceaddr, strlen(bounceaddr));
 1130                 fsync(tmpfd);
 1131             }
 1132             close(tmpfd);
 1133             tmpstr = concatstr(2, mailfilename, ".reciptto");
 1134             if(stat(tmpstr, &st) == 0) {
 1135                 myfree(tmpstr);
 1136                 break;
 1137             }
 1138             tmpfd = open(tmpstr, O_WRONLY|O_CREAT|O_TRUNC,
 1139                         S_IRUSR|S_IWUSR);
 1140             myfree(tmpstr);
 1141             if(tmpfd >= 0) {
 1142                 writen(tmpfd, to_addr, strlen(to_addr));
 1143                 fsync(tmpfd);
 1144             }
 1145             close(tmpfd);
 1146             if(replyto) {
 1147                 tmpstr = concatstr(2, mailfilename,
 1148                               ".reply-to");
 1149                 if(stat(tmpstr, &st) == 0) {
 1150                     myfree(tmpstr);
 1151                     break;
 1152                 }
 1153                 tmpfd = open(tmpstr, O_WRONLY|O_CREAT|O_TRUNC,
 1154                             S_IRUSR|S_IWUSR);
 1155                 myfree(tmpstr);
 1156                 if(tmpfd >= 0) {
 1157                     writen(tmpfd, replyto,
 1158                         strlen(replyto));
 1159                     fsync(tmpfd);
 1160                 }
 1161                 close(tmpfd);
 1162             }
 1163         } else {
 1164             endsmtp(&sockfd);
 1165         }
 1166         break;
 1167     case '2': /* Moderators */
 1168         initsmtp(&sockfd, relay, smtpport, smtphelo);
 1169         if(send_mail_many_fd(sockfd, bounceaddr, NULL, mailmap,
 1170                      st.st_size, subfd, NULL, NULL, NULL,
 1171                      listdir, NULL, hdrs, hdrslen,
 1172                      body, bodylen)) {
 1173             close(sockfd);
 1174             sockfd = -1;
 1175         } else {
 1176             endsmtp(&sockfd);
 1177         }
 1178         break;
 1179     case '3': /* resending earlier failed mails */
 1180         initsmtp(&sockfd, relay, smtpport, smtphelo);
 1181         if(send_mail_many_fd(sockfd, NULL, NULL, mailmap, st.st_size,
 1182                 subfd, listaddr, listdelim, mailfilename,
 1183                 listdir, mlmmjbounce, hdrs, hdrslen,
 1184                 body, bodylen)) {
 1185             close(sockfd);
 1186             sockfd = -1;
 1187         } else {
 1188             endsmtp(&sockfd);
 1189         }
 1190         unlink(subfilename);
 1191         break;
 1192     case '4': /* send mails to owner */
 1193         initsmtp(&sockfd, relay, smtpport, smtphelo);
 1194         if(send_mail_many_fd(sockfd, bounceaddr, NULL, mailmap,
 1195                 st.st_size, subfd, listaddr, listdelim,
 1196                 mailfilename, listdir, mlmmjbounce,
 1197                 hdrs, hdrslen, body, bodylen)) {
 1198             close(sockfd);
 1199             sockfd = -1;
 1200         } else {
 1201             endsmtp(&sockfd);
 1202         }
 1203         break;
 1204     case '5': /* bounceprobe - handle relayhost local users bouncing*/
 1205         initsmtp(&sockfd, relay, smtpport, smtphelo);
 1206         if(send_mail(sockfd, bounceaddr, to_addr, replyto,
 1207                 mailmap, st.st_size, listdir, NULL,
 1208                 hdrs, hdrslen, body, bodylen)) {
 1209             close(sockfd);
 1210             sockfd = -1;
 1211             /* error, so remove the probefile */
 1212             tmpstr = mystrdup(to_addr);
 1213             a = strchr(tmpstr, '@');
 1214             MY_ASSERT(a);
 1215             *a = '=';
 1216             probefile = concatstr(4, listdir, "/bounce/", tmpstr,
 1217                     "-probe");
 1218             unlink(probefile);
 1219             myfree(probefile);
 1220             myfree(tmpstr);
 1221         } else {
 1222             endsmtp(&sockfd);
 1223         }
 1224         break;
 1225     case '7':
 1226         digest = 1;
 1227         addtohdr = 1;
 1228         archivefilename = "digest";
 1229         /* fall through */
 1230     default: /* normal list mail */
 1231         if (!digest) {
 1232             subddirname = concatstr(2, listdir, "/subscribers.d/");
 1233         } else {
 1234             subddirname = concatstr(2, listdir, "/digesters.d/");
 1235         }
 1236         if((subddir = opendir(subddirname)) == NULL) {
 1237             log_error(LOG_ARGS, "Could not opendir(%s)",
 1238                         subddirname);
 1239             myfree(listdelim);
 1240             myfree(subddirname);
 1241             myfree(hdrs);
 1242             myfree(body);
 1243             exit(EXIT_FAILURE);
 1244         }
 1245 
 1246         listdelim = getlistdelim(listdir);
 1247         listname = genlistname(listaddr);   
 1248         listfqdn = genlistfqdn(listaddr);   
 1249         verpfrom = concatstr(6, listname, listdelim, "bounces-",
 1250                 strindex, "@", listfqdn);
 1251         myfree(listname);
 1252         myfree(listfqdn);
 1253 
 1254         if(digest)
 1255             verp = NULL;
 1256 
 1257         if(verp && (strcmp(verp, "postfix") == 0)) {
 1258             myfree(verp);
 1259             verp = mystrdup("XVERP=-=");
 1260         }
 1261 
 1262         if(addtohdr && verp) {
 1263             log_error(LOG_ARGS, "Cannot use VERP and add "
 1264                     "To: header. Not sending with "
 1265                     "VERP.");
 1266             verp = NULL;
 1267         }
 1268         
 1269         if(verp) {
 1270             initsmtp(&sockfd, relay, smtpport, smtphelo);
 1271             if(sockfd > -1) {
 1272                 if(write_mail_from(sockfd, verpfrom, verp)) {
 1273                 log_error(LOG_ARGS,
 1274                         "Could not write VERP MAIL FROM. "
 1275                         "Not sending with VERP.");
 1276                 verp = NULL;
 1277                 } else {
 1278                 reply = checkwait_smtpreply(sockfd, MLMMJ_FROM);
 1279                 if(reply) {
 1280                     log_error(LOG_ARGS,
 1281                         "Mailserver did not "
 1282                         "accept VERP MAIL FROM. "
 1283                         "Not sending with VERP.");
 1284                     myfree(reply);
 1285                     verp = NULL;
 1286                 }
 1287                 }
 1288                 /* We can't be in SMTP DATA state or anything like
 1289                  * that, so should be able to safely QUIT. */
 1290                 endsmtp(&sockfd);
 1291             } else {
 1292                 log_error(LOG_ARGS,
 1293                     "Could not connect to "
 1294                     "write VERP MAIL FROM. "
 1295                     "Not sending with VERP.");
 1296                 verp = NULL;
 1297             }
 1298         }
 1299 
 1300         while((dp = readdir(subddir)) != NULL) {
 1301             if(!strcmp(dp->d_name, "."))
 1302                 continue;
 1303             if(!strcmp(dp->d_name, ".."))
 1304                 continue;
 1305             subfilename = concatstr(2, subddirname, dp->d_name);
 1306             if((subfd = open(subfilename, O_RDONLY)) < 0) {
 1307                 log_error(LOG_ARGS, "Could not open '%s'",
 1308                             subfilename);
 1309                 myfree(subfilename);
 1310                 continue;
 1311             }
 1312             do {
 1313                 i = stl.count;
 1314                 res = getaddrsfromfd(&stl, subfd,
 1315                         maxverprecips);
 1316                 if(omit != NULL) {
 1317                     while(i < stl.count) {
 1318                         if(strcmp(stl.strs[i], omit)
 1319                             == 0) {
 1320                             myfree(stl.strs[i]);
 1321                             stl.count--;
 1322                             while (i < stl.count) {
 1323                             stl.strs[i] =
 1324                                 stl.strs[i+1];
 1325                             i++;
 1326                             }
 1327                             stl.strs[stl.count] = NULL;
 1328                             break;
 1329                         }
 1330                         i++;
 1331                     }
 1332                 }
 1333                 if(stl.count == maxverprecips) {
 1334                     initsmtp(&sockfd, relay, smtpport, smtphelo);
 1335                     if(verp) {
 1336                         sendres = send_mail_verp(
 1337                                 sockfd, &stl,
 1338                                 mailmap,
 1339                                 st.st_size,
 1340                                 verpfrom,
 1341                                 listdir, hdrs,
 1342                                 hdrslen, body,
 1343                                 bodylen, verp);
 1344                         if(sendres)
 1345                             requeuemail(listdir,
 1346                                 strindex,
 1347                                 &stl, 0);
 1348                     } else {
 1349                         sendres = send_mail_many_list(
 1350                                 sockfd, NULL,
 1351                                 NULL, mailmap,
 1352                                 st.st_size,
 1353                                 &stl,
 1354                                 listaddr,
 1355                                 listdelim,
 1356                                 archivefilename,
 1357                                 listdir,
 1358                                 mlmmjbounce,
 1359                                 hdrs, hdrslen,
 1360                                 body, bodylen);
 1361                     }
 1362                         if (sendres) {
 1363                             close(sockfd);
 1364                             sockfd = -1;
 1365                         } else {
 1366                             endsmtp(&sockfd);
 1367                         }
 1368                     for(i = 0; i < stl.count; i++)
 1369                         myfree(stl.strs[i]);
 1370                     stl.count = 0;
 1371                 }
 1372             } while(res > 0);
 1373             myfree(subfilename);
 1374             close(subfd);
 1375 
 1376         }
 1377         if(stl.count) {
 1378             initsmtp(&sockfd, relay, smtpport, smtphelo);
 1379             if(verp) {
 1380                 sendres = send_mail_verp(sockfd, &stl, mailmap,
 1381                         st.st_size, verpfrom, listdir,
 1382                         hdrs, hdrslen, body, bodylen,
 1383                         verp);
 1384                 if(sendres)
 1385                     requeuemail(listdir, strindex, &stl,
 1386                             0);
 1387             } else {
 1388                 sendres = send_mail_many_list(sockfd, NULL,
 1389                         NULL, mailmap, st.st_size,
 1390                         &stl, listaddr, listdelim,
 1391                         archivefilename, listdir,
 1392                         mlmmjbounce, hdrs, hdrslen,
 1393                         body, bodylen);
 1394             }
 1395             if (sendres) {
 1396                 close(sockfd);
 1397                 sockfd = -1;
 1398             } else {
 1399                 endsmtp(&sockfd);
 1400             }
 1401             for(i = 0; i < stl.count; i++)
 1402                 myfree(stl.strs[i]);
 1403             stl.count = 0;
 1404         }
 1405         myfree(stl.strs);
 1406         myfree(verpfrom);
 1407         closedir(subddir);
 1408         myfree(subddirname);
 1409         break;
 1410     }
 1411     
 1412     myfree(listdelim);
 1413     myfree(hdrs);
 1414     myfree(body);
 1415     myfree(mlmmjbounce);
 1416     munmap(mailmap, st.st_size);
 1417     close(mailfd);
 1418     myfree(verp);
 1419     myfree(smtphelo);
 1420 
 1421     if(archive) {
 1422         if(!ctrlarchive) {
 1423             if(rename(mailfilename, archivefilename) < 0) {
 1424                 log_error(LOG_ARGS,
 1425                         "Could not rename(%s,%s);",
 1426                         mailfilename,
 1427                         archivefilename);
 1428             }
 1429         } else {
 1430             len = strlen(listdir) + 9 + 20 + 9;
 1431             requeuefilename = mymalloc(len);
 1432             snprintf(requeuefilename, len, "%s/requeue/%d",
 1433                 listdir, mindex);
 1434             if(stat(requeuefilename, &st) < 0) {
 1435                 /* Nothing was requeued and we don't keep
 1436                  * mail for a noarchive list. */
 1437                 unlink(mailfilename);
 1438             } else {
 1439                 snprintf(requeuefilename, len,
 1440                     "%s/requeue/%d/mailfile",
 1441                     listdir, mindex);
 1442                 if (rename(mailfilename, requeuefilename) < 0) {
 1443                     log_error(LOG_ARGS,
 1444                             "Could not rename(%s,%s);",
 1445                             mailfilename,
 1446                             requeuefilename);
 1447                 }
 1448             }
 1449             myfree(requeuefilename);
 1450         }
 1451         myfree(archivefilename);
 1452     } else if(deletewhensent)
 1453         unlink(mailfilename);
 1454 
 1455     return EXIT_SUCCESS;
 1456 }