Back to index

courier  0.68.2
dsn.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2012 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 
00010 #include      "courier.h"
00011 #include      "sysconfdir.h"
00012 #include      "moduledel.h"
00013 #include      "maxlongsize.h"
00014 #include      "comsubmitclient.h"
00015 #include      "comfax.h"
00016 #include      "rfc2045/rfc2045.h"
00017 #include      "rfc2045/rfc2045charset.h"
00018 #include      "rfc822/rfc822.h"
00019 #include      "numlib/numlib.h"
00020 #include      <stdlib.h>
00021 #include      <string.h>
00022 #if    HAVE_UNISTD_H
00023 #include      <unistd.h>
00024 #endif
00025 #include      "comctlfile.h"
00026 #include      "comqueuename.h"
00027 #include      "comstrtotime.h"
00028 #include      "comstrtimestamp.h"
00029 #include      <sys/stat.h>
00030 #include      <sys/uio.h>
00031 
00032 /*
00033   The DSN module is used to generate delivery status notifications.
00034   Messages dispatched to the DSN module result in a delivery status
00035   notification being sent to the sender.  The DSN is generated like
00036   a regular message, and is injected via submit.
00037 
00038   When the message is dispatched to this module with the host
00039   parameter set to "deferred", this module generated a delayed
00040   DSN, a DSN that's sent for all recipients who have not been delivered
00041   to, yet.  Otherwise, this is a permanent DSN, either a non-delivery
00042   report or a return receipt (or both!).
00043 
00044 */
00045 
00046 
00047 /* Various bits and pieces of the DSN */
00048 
00049 static char *dsnsubjectwarn;
00050 static char *dsnsubjectnotice;
00051 static char *dsnheader;
00052 static char *dsnfooter;
00053 static char *dsndelayed;
00054 static char *dsnrelayed;
00055 static char *dsndelivered;
00056 static char *dsnfailed;
00057 
00058 static const char *dsnfrom;
00059 static size_t dsnlimit;
00060 
00061 static void dsninit()
00062 {
00063 static struct { char **textptr, *filenameptr; } texts[]={
00064        { &dsnsubjectwarn,   SYSCONFDIR "/dsnsubjectwarn.txt"},
00065        { &dsnsubjectnotice, SYSCONFDIR "/dsnsubjectnotice.txt"},
00066        { &dsnheader,        SYSCONFDIR "/dsnheader.txt"},
00067        { &dsnfooter,        SYSCONFDIR "/dsnfooter.txt"},
00068        { &dsndelayed,              SYSCONFDIR "/dsndelayed.txt"},
00069        { &dsnrelayed,              SYSCONFDIR "/dsnrelayed.txt"},
00070        { &dsndelivered,     SYSCONFDIR "/dsndelivered.txt"},
00071        { &dsnfailed,        SYSCONFDIR "/dsnfailed.txt"},
00072        } ;
00073 int    i;
00074 
00075        for (i=0; i<sizeof(texts)/sizeof(texts[0]); i++)
00076               if ( (*texts[i].textptr=
00077                      i < 2 ? config_read1l(texts[i].filenameptr):
00078                      readfile(texts[i].filenameptr, 0)) == 0)
00079               {
00080                      clog_msg_start_err();
00081                      clog_msg_str("Missing ");
00082                      clog_msg_str(texts[i].filenameptr);
00083                      clog_msg_send();
00084                      exit(1);
00085               }
00086 }
00087 
00088 static int dodsn(struct ctlfile *, FILE *, const char *, int);
00089 
00090 static void defer(struct ctlfile *ctf)
00091 {
00092 struct iovec  iov[3];
00093 static const char completed[1]={COMCTLFILE_DELDEFERRED};
00094 time_t current_time;
00095 const char    *t;
00096 
00097        iov[0].iov_base=(caddr_t)&completed;
00098        iov[0].iov_len=sizeof(completed);
00099 
00100        time(&current_time);
00101        t=strtimestamp(current_time);
00102        iov[1].iov_base=(caddr_t)t;
00103        iov[1].iov_len=strlen(t);
00104        iov[2].iov_base=(caddr_t)"\n";
00105        iov[2].iov_len=1;
00106        ctlfile_appendv(ctf, iov, 3);
00107 }
00108 
00109 int main(int argc, char **argv)
00110 {
00111 struct moduledel *p;
00112 
00113        clog_open_syslog("courierdsn");
00114        dsninit();
00115        if (chdir(getenv("COURIER_HOME")))
00116               clog_msg_errno();
00117 
00118        libmail_changeuidgid(MAILUID, MAILGID);
00119 
00120        dsnfrom=config_dsnfrom();
00121        dsnlimit=config_dsnlimit();
00122 
00123        if (argc > 1) /* For testing only */
00124        {
00125        struct ctlfile ctf;
00126 
00127               if (ctlfile_openi(atol(argv[1]), &ctf, 0))
00128                      clog_msg_errno();
00129               ctlfile_setvhost(&ctf);
00130               dodsn(&ctf, stdout, "sender", 
00131                      argc > 2 ? atoi(argv[2]):0);
00132               ctlfile_close(&ctf);
00133               exit(0);
00134        }
00135 
00136        if (atol(getenv("MAXRCPT")) > 1)
00137        {
00138               clog_msg_start_err();
00139               clog_msg_str("Invalid configuration in config, set MAXRCPT=1");
00140               clog_msg_send();
00141               exit(1);
00142        }
00143 
00144        /* Loop to receive messages from Courier */
00145 
00146        module_init(0);
00147        while ((p=module_getdel()) != NULL)
00148        {
00149        pid_t  pid;
00150        unsigned delid;
00151 
00152               if (p->nreceipients != 1)
00153               {
00154                      clog_msg_start_err();
00155                      clog_msg_str("Internal error - invalid # of receipients.");
00156                      clog_msg_send();
00157                      exit(1);
00158               }
00159 
00160               /*
00161               ** Each DSN is handled by a subproc, whose output
00162               ** is piped to submit.
00163               */
00164 
00165               delid=atol(p->delid);
00166 
00167               if ((pid=module_fork(delid, 0)) == -1)
00168               {
00169                      clog_msg_prerrno();
00170                      module_completed(delid, delid);
00171                      continue;
00172               }
00173 
00174               if (pid == 0)
00175               {
00176                      char   *args[10];
00177 
00178                      /* Arguments to submit to initialize message
00179                      ** metadata */
00180 
00181                      struct ctlfile ctf;
00182                      int    rc;
00183                      int    isdeferred=strcmp(p->host, "deferred") == 0;
00184 
00185                      const char *sec_level_orig;
00186                      char *envp[4];
00187                      int nenvp=0;
00188                      int i;
00189                      int argc;
00190 
00191                      char msgsource[80];
00192 
00193 
00194                      if (ctlfile_openi(p->inum, &ctf, 0))
00195                             clog_msg_errno();
00196 
00197 
00198                      argc=0;
00199 
00200 
00201                      args[argc++]="submit";
00202                      args[argc++]="-delay=0";
00203 
00204                      strcpy(msgsource, "-src=");
00205 
00206                      if ((i=ctlfile_searchfirst(&ctf,
00207                                              COMCTLFILE_MSGSOURCE)) >= 0)
00208                      {
00209                             strncat(msgsource, ctf.lines[i]+1, 20);
00210                             strcat(msgsource, "/");
00211                      }
00212                      strcat(msgsource, "dsn");
00213                      args[argc++]=msgsource;
00214                      args[argc++]="dsn";
00215                      args[argc++]="dns; localhost (localhost [127.0.0.1])";
00216                      args[argc++]="ftp://ftp.isi.edu/in-notes/rfc1894.txt";
00217                      args[argc++]=NULL;
00218 
00219                      sec_level_orig=ctlfile_security(&ctf);
00220                      if (sec_level_orig && *sec_level_orig)
00221                      {
00222                             envp[nenvp]
00223                                    =courier_malloc(strlen(sec_level_orig)
00224                                                  +30);
00225                             strcat(strcat(strcpy(envp[nenvp],
00226                                                "DSNRET=S{"),
00227                                          sec_level_orig), "}");
00228                             ++nenvp;
00229                      }
00230                      envp[nenvp]=0;
00231 
00232                      if (submit_fork(args, envp, 0))
00233                      {
00234                             clog_msg_start_err();
00235                             clog_msg_str("Problems injecting bounce - running submit.");
00236                             clog_msg_send();
00237                             exit(1);
00238                      }
00239 
00240                      if (ctf.sender[0] == '\0')
00241                             submit_write_message(TRIPLEBOUNCE);
00242                             /*
00243                             ** This was a doublebounce, set env sender to
00244                             ** TRIPLEBOUNCE magic.
00245                             */
00246                      else
00247                             submit_write_message("");
00248 
00249                      if (submit_readrc())
00250                      {
00251                             clog_msg_start_err();
00252                             clog_msg_str("Problems injecting bounce - sender rejected.");
00253                             clog_msg_send();
00254                             defer(&ctf);
00255                             exit(1);
00256                      }
00257 
00258                      /* Courier figures out the return address, and gives
00259                      ** it to me as the receipient!!!
00260                      */
00261 
00262                      submit_write_message(p->receipients[1]);
00263                      if (submit_readrc())
00264                      {
00265                             clog_msg_start_err();
00266                             clog_msg_str("Problems injecting bounce - receipient ");
00267                             clog_msg_str(p->receipients[1]);
00268                             clog_msg_str(" rejected.");
00269                             clog_msg_send();
00270                             defer(&ctf);
00271                             exit(1);
00272                      }
00273                      submit_write_message("");
00274 
00275                      rc=dodsn(&ctf, submit_to, p->receipients[1],
00276                             strcmp(p->host, "deferred") == 0);
00277 
00278                      if (rc)
00279                      {
00280                             clog_msg_start_err();
00281                             clog_msg_str("Problems injecting bounce - unable to generate message.");
00282                             clog_msg_send();
00283                      }
00284 
00285                      if (fflush(submit_to) || ferror(submit_to))
00286                      {
00287                             clog_msg_start_err();
00288                             clog_msg_str("Problems injecting bounce - unable to flush message.");
00289                             clog_msg_send();
00290                             rc=1;
00291                      }
00292                      if (fclose(submit_to))
00293                      {
00294                             clog_msg_start_err();
00295                             clog_msg_str("Problems injecting bounce - unable to close message.");
00296                             clog_msg_send();
00297                             rc=1;
00298                      }
00299 
00300                      if (rc)
00301                             submit_cancel();
00302                      else if (submit_wait())
00303                      {
00304                             clog_msg_start_err();
00305                             clog_msg_str("Problems injecting bounce - submit failed.");
00306                             clog_msg_send();
00307                             rc=1;
00308                      }
00309 
00310                      if (rc == 0)
00311                      {
00312                             time_t t;
00313 
00314                             time(&t);
00315 
00316                             /*
00317                             ** After sending a delayed DSN, mark the
00318                             ** message so it doesn't ever get sent again!
00319                             */
00320 
00321                             if (isdeferred)
00322                             {
00323                             char   buf[MAXLONGSIZE+2];
00324 
00325                                    buf[0]=COMCTLFILE_WARNINGSENT;
00326                                    strcat(strcpy(buf+1,
00327                                           strtimestamp(t)), "\n");
00328                                    ctlfile_append(&ctf, buf);
00329                                    ctlfile_close(&ctf);
00330                             }
00331                             else
00332                             {
00333                             /*
00334                             ** For PERMANENT dsns, it is my responsibility
00335                             ** to remove the original message when done!!!
00336                             */
00337 
00338                                    ctlfile_close(&ctf);
00339                                    qmsgunlink(p->inum, t);
00340                                    unlink(qmsgsdatname(p->inum));
00341                                    unlink(qmsgsctlname(p->inum));
00342                             }
00343                      }
00344                      else
00345                      {
00346                             defer(&ctf);
00347                      }
00348                      exit(rc);
00349               }
00350        }
00351        return (0);
00352 }
00353 
00354 /*
00355 ** When building a DSN, we need to find the last delivery info for each
00356 ** recipient.  What we do is search for the first first COMCTLFILE_DELINFO
00357 ** after the last COMCTLFILE_DELDEFERRED record, that is the first info
00358 ** record after the last message deferred marker.
00359 **
00360 ** HOWEVER, we always prefer to take the last record with a
00361 ** COMCTLFILE_DELINFO_PEER, which is generated after we succesfully connect
00362 ** to a remote peer, because we usually get a better error message in that
00363 ** situation (for a flaky peer we're not interested in the last connection
00364 ** refused message, we want to know why the peer deferred us the last time
00365 ** we managed to contact it).
00366 */
00367 
00368 static int find_delivery_info(struct ctlfile *ctf, unsigned nreceipient)
00369 {
00370 int    last_delivery=-1, last_connected_delivery= -1;
00371 int    isnewdelivery=1;
00372 int    i;
00373 
00374        for (i=0; ctf->lines[i]; i++)
00375        {
00376        char   c=ctf->lines[i][0];
00377        const char *p;
00378        unsigned      n;
00379 
00380               if (c != COMCTLFILE_DELDEFERRED && c != COMCTLFILE_DELINFO)
00381                      continue;
00382 
00383               p=ctf->lines[i]+1;
00384               n=0;
00385               while (*p >= '0' && *p <= '9')
00386                      n = n * 10 + (*p++ - '0');
00387               if (n != nreceipient)       continue;
00388 
00389               if (c != COMCTLFILE_DELINFO)
00390               {
00391                      isnewdelivery=1;
00392                      continue;
00393               }
00394               if (isnewdelivery)   last_delivery=i;
00395               isnewdelivery=0;
00396 
00397               while ( *p == ' ')   ++p;
00398               if (*p == COMCTLFILE_DELINFO_PEER)
00399                      last_connected_delivery=last_delivery;
00400        }
00401 
00402        if (last_connected_delivery < 0)
00403               last_connected_delivery=last_delivery;
00404        return (last_connected_delivery);
00405 }
00406 
00407 /*
00408 ** print_del_info is called twice, once to print out a verbose delivery
00409 ** status in the verbose part of the DSN, and the second time to generate
00410 ** the recipient headers in the message/delivery-status.  It picks up after
00411 ** the record returned by find_delivery_info, then for each record for this
00412 ** recipient, up until the next SUCCESS, FAIL, or DEFERRED record, calls
00413 ** a separate function to print this kind of a record.
00414 */
00415 
00416 
00417 static void print_del_info(struct ctlfile *ctf, unsigned nreceipient,
00418        int start,
00419        void (*handler_func)(const char *, const char, void *), void *arg)
00420 {
00421        for ( ; ctf->lines[start] &&
00422               ((ctf->lines[start][0] != COMCTLFILE_DELSUCCESS &&
00423               ctf->lines[start][0] != COMCTLFILE_DELFAIL &&
00424               ctf->lines[start][0] != COMCTLFILE_DELDEFERRED)
00425               || atol(ctf->lines[start]+1) != nreceipient); start++)
00426        {
00427        const char *p;
00428        unsigned      n;
00429        char   type;
00430 
00431               if (ctf->lines[start][0] != COMCTLFILE_DELINFO)  continue;
00432 
00433               p=ctf->lines[start]+1;
00434               n=0;
00435               while (*p >= '0' && *p <= '9')
00436                      n = n * 10 + (*p++ - '0');
00437               if (n != nreceipient)       continue;
00438 
00439               while ( *p == ' ')   ++p;
00440               type= *p;
00441 
00442               if (*p)       p++;
00443               while ( *p == ' ')   ++p;
00444               (*handler_func)(p, type, arg);
00445        }
00446 }
00447 
00448 /*
00449 ** To properly set Content-Transfer-Encoding on our DSN we need to know
00450 ** whether message contents or metadata includes 8bit characters.
00451 */
00452 
00453 
00454 
00455 
00456 static void check8bit(const char *str, const char type, void *flag)
00457 {
00458        for ( ; *str; str++)
00459               if ( (unsigned char)*str & 0x80 )
00460                      *(int *)flag=1;
00461 }
00462 
00463 static int hexconvert(char a, char b)
00464 {
00465        if ( a >= 'A' && a <= 'F')
00466               a -= 'A'-10;
00467        if ( b >= 'A' && b <= 'F')
00468               b -= 'B'-10;
00469 
00470        return ( (a & 15) * 16 + (b & 15));
00471 }
00472 
00473 static int is8bitreceipient(struct ctlfile *ctf, unsigned nreceipient)
00474 {
00475 int    flag=0;
00476 const char *p=ctf->oreceipients[nreceipient];
00477 int    infoptr;
00478 
00479        if (p && *p)
00480               while (*p)
00481               {
00482                      if (*p == '+' && p[1] && p[2] &&
00483                             hexconvert(p[1], p[2]) & 0x80)
00484                      return (1);
00485                      p++;
00486               }
00487        else
00488        {
00489               check8bit(ctf->receipients[nreceipient], 0, &flag);
00490               if (flag)     return (1);
00491        }
00492        infoptr=find_delivery_info(ctf, nreceipient);
00493        if (infoptr >= 0)
00494               print_del_info(ctf, nreceipient, infoptr, &check8bit, &flag);
00495        return (flag);
00496 }
00497 
00498 static int is8bitdsn(struct ctlfile *ctf, int isdelayeddsn)
00499 {
00500 unsigned      n;
00501 int    i, flag=0;
00502 
00503        if ((i=ctlfile_searchfirst(ctf, COMCTLFILE_ENVID)) >= 0)
00504        {
00505               check8bit(ctf->lines[i], 0, &flag);
00506               if (flag)     return (1);
00507        }
00508 
00509        for (n=0; n<ctf->nreceipients; n++)
00510        {
00511               if (!dsn_sender(ctf, n, isdelayeddsn))    continue;
00512               if (is8bitreceipient(ctf, n))      return (1);
00513        }
00514        return (0);
00515 }
00516 
00517 static void print_xtext(FILE *fp, const char *xtext)
00518 {
00519        while (*xtext)
00520        {
00521               if (*xtext != '+' || xtext[1] == 0 || xtext[2] == 0)
00522               {
00523                      putc(*xtext, fp);
00524                      xtext++;
00525               }
00526               else
00527               {
00528                      putc( hexconvert(xtext[1], xtext[2]), fp);
00529                      xtext += 3;
00530               }
00531        }
00532 }
00533 
00534 static void print_receipient(FILE *fp, struct ctlfile *ctf, unsigned n)
00535 {
00536        fputs(" <", fp);
00537        if (ctf->oreceipients[n] &&
00538 #if    HAVE_STRNCASECMP
00539               strncasecmp(ctf->oreceipients[n], "rfc822;", 7)
00540 #else
00541               strnicmp(ctf->oreceipients[n], "rfc822;", 7)
00542 #endif
00543               == 0)
00544               print_xtext(fp, ctf->oreceipients[n]+7);
00545        else
00546               fprintf(fp, "%s", ctf->receipients[n]);
00547        putc('>', fp);
00548 }
00549 
00550 /* Format delivery info in the plain text portion of the response. */
00551 
00552 static void format_del_info(const char *p, const char type, void *q)
00553 {
00554        switch (type) {
00555        case COMCTLFILE_DELINFO_PEER:
00556               if (*p)
00557                      fprintf((FILE *)q, "     %s:\n", p);
00558               break;
00559        case COMCTLFILE_DELINFO_CONNECTIONERROR:
00560               fprintf((FILE *)q, " *** %s\n", p);
00561               break;
00562        case COMCTLFILE_DELINFO_SENT:
00563               fprintf((FILE *)q, " >>> %s\n", p);
00564               break;
00565        case COMCTLFILE_DELINFO_REPLY:
00566               fprintf((FILE *)q, " <<< %-1.500s\n", p);
00567               break;
00568        }
00569 }
00570 
00571 /* Include original message contents in the DSN */
00572 
00573 static int print_message(FILE *datfile, FILE *fp, int fullmsg)
00574 {
00575 char   buf[BUFSIZ];
00576 
00577        if (fseek(datfile, 0L, SEEK_SET) == -1)   return (-1);
00578 
00579        while (fgets(buf, sizeof(buf), datfile))
00580        {
00581               if (!fullmsg && (
00582                      buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')))
00583                      break;
00584               fprintf(fp, "%s", buf);
00585               if (strchr(buf, '\n') == 0)
00586               {
00587               int    c;
00588 
00589                      while ((c=getc(datfile)) >= 0)
00590                      {
00591                             putc(c, fp);
00592                             if (c == '\n')       break;
00593                      }
00594               }
00595        }
00596        return (0);
00597 }
00598 
00599 /*
00600 ** Since the DSN is a MIME message, we must create a MIME boundary, and I
00601 ** do it right by making sure that the MIME boundary is not to be found in
00602 ** the message.
00603 */
00604 
00605 static int search_boundary(FILE *f, const char *b, int *bit8flag)
00606 {
00607 char   buf[BUFSIZ];
00608 size_t l=strlen(b);
00609 const char *p;
00610 int    flag=0;
00611 int    cr;
00612 
00613        if (fseek(f, 0L, SEEK_SET)) return (-1);
00614 
00615        while (fgets(buf, sizeof(buf), f))
00616        {
00617               if (buf[0] == '-' && buf[1] == '-' &&
00618 
00619 #if    HAVE_STRNCASECMP
00620                      strncasecmp(buf+2, b, l)
00621 #else
00622                      strnicmp(buf+2, b, l)
00623 #endif
00624                       == 0) flag=1;
00625 
00626               cr=0;
00627               for (p=buf; *p; p++)
00628                      if (*p == '\n')      cr=1;
00629                      else if ((int)(unsigned char)*p >= 127)
00630                             *bit8flag=1;
00631 
00632               if (!cr)
00633               {
00634               int    c;
00635 
00636                      while ((c=getc(f)) >= 0 && c != '\n')
00637                             if ((int)(unsigned char)c >= 127)
00638                                    *bit8flag=1;
00639               }
00640        }
00641        return (flag);
00642 }
00643 
00644 struct dsndiagnosticinfo {
00645        FILE *fp;
00646        int flag;
00647        char prefix[70];
00648        } ;
00649 
00650 static void printremotemta(const char *p, const char type, void *q)
00651 {
00652 struct dsndiagnosticinfo *i=(struct dsndiagnosticinfo *)q;
00653 
00654        if (type == COMCTLFILE_DELINFO_PEER && i->flag == 0)
00655        {
00656               fprintf(i->fp, "Remote-MTA: dns; %s\n", p);
00657               i->flag=1;
00658        }
00659 }
00660 
00661 static void print_network_error(const char *p, const char type, void *q)
00662 {
00663 struct dsndiagnosticinfo *i=(struct dsndiagnosticinfo *)q;
00664 
00665        if (type == COMCTLFILE_DELINFO_CONNECTIONERROR)
00666        {
00667               i->flag=1;
00668               fprintf(i->fp, "X-Network-Error: %s\n", p);
00669        }
00670 }
00671 
00672 static void set_msg_type(struct dsndiagnosticinfo *p, const char *t)
00673 {
00674        strcpy(p->prefix, "Diagnostic-Code: ");
00675        strncat(p->prefix, t, 20);
00676        strcat(p->prefix, "; ");
00677 }
00678 
00679 static void print_smtp_info(const char *p, const char type, void *q)
00680 {
00681 struct dsndiagnosticinfo *i=(struct dsndiagnosticinfo *)q;
00682 
00683        /* First REPLYTYPE we see becomes the Diagnostic Code reply type */
00684 
00685        if (type == COMCTLFILE_DELINFO_REPLYTYPE && i->prefix[0])
00686               set_msg_type(i, p);
00687 
00688        if (type == COMCTLFILE_DELINFO_REPLY)
00689        {
00690               fprintf(i->fp, "%s%s\n",
00691                      ( i->prefix[0] ? i->prefix:"        "), p);
00692               i->prefix[0]=0;      /* Kill any stray REPLYTYPEs */
00693        }
00694 }
00695 
00696 static int isfax(const char *p)
00697 {
00698        int dummy;
00699 
00700        if ((p=strrchr(p, '@')) == NULL)
00701               return (0);
00702 
00703        if (comgetfaxopts(p+1, &dummy) == 0)
00704               return (1);
00705        return (0);
00706 }
00707 
00708 static void print_header(FILE *f, char *template, const char *me,
00709                       const char *from_time, const char *from_mta)
00710 {
00711         unsigned char c, c2;
00712         int i = 0;
00713 
00714         while ((c=template[i++]) > 0)
00715        {
00716               char        kw[64];
00717               if (c != '[')
00718               {
00719                      putc(c, f);
00720                         continue;
00721               }
00722 
00723               if (template[i] != '#')
00724               {
00725                      putc('[', f);
00726                      continue;
00727               }
00728 
00729               ++i;
00730 
00731               c2=0;
00732               while ((c=template[i]) != 0 && (isalnum(c) || c == '_'))
00733               {
00734                         if (c2 < sizeof(kw)-1)
00735                                 kw[c2++]=c;
00736                      ++i;
00737               }
00738               kw[c2]=0;
00739 
00740               if (c != '#')
00741               {
00742                      fprintf(f, "[#%s", kw);
00743                      continue;
00744               }
00745 
00746               ++i;
00747               if (template[i] != ']')
00748               {
00749                      fprintf(f, "[#%s#", kw);
00750                      continue;
00751               }
00752               ++i;
00753               if (strcmp(kw, "ME") == 0)
00754               {
00755                      fprintf(f, "%s", me);
00756               }
00757               else if (strcmp(kw, "FROMTIME") == 0)
00758               {
00759                      fprintf(f, "%s", from_time);
00760               }
00761               else if (strcmp(kw, "FROMMTA") == 0)
00762               {
00763                      fprintf(f, "%s", from_mta);
00764               }
00765         }
00766 }
00767 
00768 static int dodsn(struct ctlfile *ctf, FILE *fp, const char *sender,
00769               int dodelayed)
00770 {
00771 int    dsn8flag=is8bitdsn(ctf, dodelayed);
00772 const char *datfilename;
00773 FILE   *datfile;
00774 struct stat   stat_buf;
00775 unsigned i;
00776 int    j;
00777 int    returnmsg=0;
00778 char   boundary[MAXLONGSIZE+40];
00779 int    msg8flag=0;
00780 const  char *from_mta;
00781 const  char *from_mta_p;
00782 const  char *from_time;
00783 
00784        datfilename=qmsgsdatname(ctf->n);
00785        if ((datfile=fopen(datfilename, "r")) == 0 ||
00786               fstat(fileno(datfile), &stat_buf))
00787        {
00788               if (datfile)  fclose(datfile);
00789               clog_msg_start_err();
00790               clog_msg_str("Cannot open ");
00791               clog_msg_str(datfilename);
00792               clog_msg_send();
00793               return (0);
00794        }
00795 
00796        /*
00797        ** Decide if we want to return the entire message, or just the
00798        ** headers.  The entire message is returned only if:
00799        **
00800        ** A) This is a double bounce, i.e., a bounce where the envelope
00801        **    sender is null.  We want to know everything about the double
00802        **    bounce.
00803        **
00804        **    OR
00805        **
00806        ** B) This is NOT a delayed DSN, AND, the message size is below
00807        **    a certain limit, and RET=HDRS was NOT specified, AND
00808        **    this message does not include return receipts.
00809        **
00810        **    ( No need to include the whole kit and kaboodle for return
00811        **      receipts )
00812        **
00813        */
00814 
00815        if (ctf->sender[0] == '\0')
00816               returnmsg=1;
00817        else if (!dodelayed && stat_buf.st_size < dsnlimit &&
00818               (j=ctlfile_searchfirst(ctf, COMCTLFILE_DSNFORMAT)) >= 0 &&
00819               strchr(ctf->lines[j], 'H') == 0)
00820        {
00821               for (i=0; i<ctf->nreceipients; i++)
00822               {
00823                      if (ctf->delstatus[i][0] == COMCTLFILE_DELSUCCESS)
00824                             continue; /* never full msg for receipts */
00825 
00826                      if (dsn_sender(ctf, i, 0))
00827                      {
00828                             returnmsg=1;
00829                             break;
00830                      }
00831               }
00832        }
00833 
00834        i=0;
00835        do
00836        {
00837               sprintf(boundary, "=_courier_%u", i++);
00838        } while (search_boundary(datfile, boundary, &msg8flag));
00839 
00840        fprintf(fp, "From: %s\nTo: %s\nSubject: %s\n", dsnfrom, sender,
00841               dodelayed ? dsnsubjectwarn:dsnsubjectnotice);
00842        fprintf(fp, "Mime-Version: 1.0\nContent-Type: multipart/report; report-type=delivery-status;\n    boundary=\"%s\"\nContent-Transfer-Encoding: %s\n\n"
00843               RFC2045MIMEMSG "\n--%s\nContent-Transfer-Encoding: %s\n",
00844               boundary,
00845               dsn8flag || msg8flag ? "8bit":"7bit",
00846               boundary,
00847               dsn8flag ? "8bit":"7bit");
00848 
00849        if ((j=ctlfile_searchfirst(ctf, COMCTLFILE_FROMMTA)) >= 0)
00850               from_mta=ctf->lines[j]+1;
00851        else
00852               from_mta=0;
00853 
00854        if (from_mta && (from_mta_p=strchr(from_mta, ';')) != 0)
00855        {
00856               while (*++from_mta_p == ' ')
00857                      ;
00858        }
00859        else   from_mta_p=0;
00860 
00861        from_time=rfc822_mkdate(stat_buf.st_mtime);
00862 
00863        print_header(fp, dsnheader, config_me(), from_time, 
00864               from_mta_p && *from_mta_p ? from_mta_p:"unknown");
00865 
00866        if (dodelayed)
00867        {
00868               /* This is a delayed DSN */
00869 
00870               fprintf(fp, dsndelayed, config_me());
00871               for (i=0; i<ctf->nreceipients; i++)
00872               {
00873               int    infoptr;
00874 
00875                      if (!dsn_sender(ctf, i, 1)) continue;
00876                      fprintf(fp, "\n");
00877                      print_receipient(fp, ctf, i);
00878                      fprintf(fp, ":\n");
00879                      infoptr=find_delivery_info(ctf, i);
00880                      if (infoptr >= 0)
00881                             print_del_info(ctf, i, infoptr,
00882                                    format_del_info, fp);
00883               }
00884        }
00885        else
00886        {
00887        int    printed_header;
00888        int    pass;
00889 
00890               /* A final DSN can have up to three parts:
00891               **
00892               **    0. Return receipts.
00893               **    1. Mail relayed to a non-DSN gateway, no receipt possible.
00894               **    2. Failed recipients.
00895               **
00896               ** Do each one in turn.  For each pass, search for recipients
00897               ** that fall into this category.  The first time one is found,
00898               ** generate a header.
00899               */
00900 
00901               for (pass=0; pass<3; pass++)
00902               {
00903                      printed_header=0;
00904                      for (i=0; i<ctf->nreceipients; i++)
00905                      {
00906                      int    infoptr;
00907                      const  char *status=ctf->delstatus[i];
00908 
00909                             if (pass == 2)
00910                             {
00911                                    if (*status == COMCTLFILE_DELSUCCESS)
00912                                           continue;
00913                             }
00914                             else if (*status != COMCTLFILE_DELSUCCESS
00915                                    || strchr(status+1,
00916                                           (pass ? 'r':'l')) == 0)
00917                                    continue;
00918                             
00919                             if (!dsn_sender(ctf, i, 0)) continue;
00920                             if (!printed_header)
00921                             {
00922                                    fprintf(fp, "%s",
00923                                           (pass == 2 ? dsnfailed:
00924                                                  pass ? dsnrelayed:
00925                                                         dsndelivered));
00926                                    printed_header=1;
00927                             }
00928                             fprintf(fp, "\n");
00929                             print_receipient(fp, ctf, i);
00930                             fprintf(fp, ":\n");
00931                             infoptr=find_delivery_info(ctf, i);
00932                             if (infoptr >= 0)
00933                                    print_del_info(ctf, i, infoptr,
00934                                           format_del_info, fp);
00935                      }
00936               }
00937        }
00938 
00939        fprintf(fp, "%s\n\n--%s\nContent-Type: message/delivery-status\nContent-Transfer-Encoding: %s\n\nReporting-MTA: dns; %s\nArrival-Date: %s\n",
00940               dsnfooter,
00941               boundary,
00942               dsn8flag ? "8bit":"7bit",
00943               config_me(), from_time);
00944 
00945        if (  (j=ctlfile_searchfirst(ctf, COMCTLFILE_ENVID)) >= 0 &&
00946               ctf->lines[j][1])
00947        {
00948               fprintf(fp, "Original-Envelope-Id: ");
00949               print_xtext(fp, ctf->lines[j]+1);
00950               fprintf(fp, "\n");
00951        }
00952 
00953        if (from_mta && strchr(from_mta, ';') != 0)
00954               fprintf(fp, "Received-From-MTA: %s\n", from_mta);
00955 
00956        for (i=0; i<ctf->nreceipients; i++)
00957        {
00958        const char *action;
00959        const char *status;
00960        const char *p;
00961        int infoptr;
00962 
00963               if (!dsn_sender(ctf, i, dodelayed))       continue;
00964 
00965               p=ctf->delstatus[i];
00966               if (*p == COMCTLFILE_DELSUCCESS)
00967               {
00968                      action="delivered";
00969                      status="2.0.0";
00970                      for ( ++p; *p; ++p)
00971                      {
00972                             if (*p == 'l')
00973                                    break;
00974                             if (*p == 'r')
00975                             {
00976                                    action="relayed";
00977                                    break;
00978                             }
00979                      }
00980                      if (!*p)      continue;
00981               }
00982               else
00983               {
00984                      if (dodelayed)
00985                      {
00986                             action="delayed";
00987                             status="4.0.0";
00988                      }
00989                      else
00990                      {
00991                             action="failed";
00992                             status="5.0.0";
00993                      }
00994               }
00995 
00996               fprintf(fp, "\nFinal-Recipient: rfc822; %s\nAction: %s\n",
00997                      ctf->receipients[i], action);
00998 
00999               if (ctf->oreceipients[i] && ctf->oreceipients[i][0])
01000               {
01001               const char *c;
01002 
01003                      fprintf(fp, "Original-Recipient: ");
01004                      for (c=ctf->oreceipients[i]; *c; c++)
01005                      {
01006                             putc(*c, fp);
01007                             if (*c == ';')
01008                             {
01009                                    putc(' ', fp);
01010                                    c++;
01011                                    break; 
01012                             }
01013                      }
01014                      print_xtext(fp, c);
01015                      fprintf(fp, "\n");
01016               }
01017               fprintf(fp, "Status: %s\n", status);
01018 
01019               if (!dodelayed)
01020               {
01021                      infoptr=find_delivery_info(ctf, i);
01022                      if (infoptr >= 0)
01023                      {
01024                      struct dsndiagnosticinfo info;
01025 
01026                             info.fp=fp;
01027                             info.flag=0;
01028                             print_del_info(ctf, i, infoptr,
01029                                    &printremotemta, &info);
01030 
01031                             info.flag=0;
01032                             set_msg_type(&info, "unknown");
01033 
01034                             print_del_info(ctf, i, infoptr,
01035                                    &print_network_error, &info);
01036 
01037                             if (info.flag == 0)
01038                                    print_del_info(ctf, i, infoptr,
01039                                           &print_smtp_info, &info);
01040                      }
01041               }
01042 
01043               if (dodelayed &&
01044                      (j=ctlfile_searchfirst(ctf,
01045                                           isfax(ctf->receipients[i]) ?
01046                                           COMCTLFILE_FAXEXPIRES:
01047                                           COMCTLFILE_EXPIRES)) >= 0)
01048                      fprintf(fp, "Will-Retry-Until: %s\n",
01049                             rfc822_mkdate(strtotime(ctf->lines[j]+1)));
01050        }
01051        fprintf(fp, "\n--%s\n", boundary);
01052 
01053        fprintf(fp, "Content-Type: %s\nContent-Transfer-Encoding: %s\n\n",
01054               returnmsg ? "message/rfc822":"text/rfc822-headers; charset=us-ascii",
01055               msg8flag ? "8bit":"7bit");
01056 
01057        if (print_message(datfile, fp, returnmsg))
01058        {
01059               fclose(datfile);
01060               return (-1);
01061        }
01062 
01063        fprintf(fp, "\n--%s--\n", boundary);
01064        fclose(datfile);
01065        return (0);
01066 }