home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 024 / psi110g.zip / SMTPSERV.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  41KB  |  1,298 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  *
  4.  * Mods by G1EMM and PA0GRI
  5.  *
  6.  * Index file support by WG7J
  7.  */
  8. #include <time.h>
  9. #ifdef UNIX
  10. #include <sys/types.h>
  11. #include <fcntl.h>
  12. #endif
  13. #if defined(__STDC__) || defined(__TURBOC__)
  14. #include <stdarg.h>
  15. #endif
  16. #include <dir.h>
  17. #include <ctype.h>
  18. #include <setjmp.h>
  19. #include "global.h"
  20. #include "mbuf.h"
  21. #include "cmdparse.h"
  22. #include "socket.h"
  23. #ifdef  LZW
  24. #include "lzw.h"
  25. #endif
  26. #include "iface.h"
  27. #include "proc.h"
  28. #include "smtp.h"
  29. #include "commands.h"
  30. #include "dirutil.h"
  31. #include "mailbox.h"
  32. #include "mailutil.h"
  33. #include "bm.h"
  34. #include "domain.h"
  35. #include "session.h"
  36. #include "files.h"
  37. #include "index.h"
  38. #ifdef  NNTPS
  39. #include "nntp.h"
  40. #endif
  41.   
  42. char *Days[7] = {  "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  43. extern char *Months[];
  44. #ifdef SMTPTRACE
  45. extern unsigned short Smtptrace;
  46. #endif
  47.   
  48. static struct list *expandalias __ARGS((struct list **head,char *user));
  49. static int  getmsgtxt __ARGS((struct smtpsv *mp));
  50. static struct smtpsv *mail_create __ARGS((void));
  51. static void mail_clean __ARGS((struct smtpsv *mp));
  52. static int mailit __ARGS((FILE *data,char *from,struct list *tolist));
  53. static int router_queue __ARGS((FILE *data,char *from,struct list *to));
  54. static void smtplog __ARGS((char *fmt,...));
  55. static void smtpserv __ARGS((int s,void *unused,void *p));
  56. static int mailuser __ARGS((FILE *data,char *from,char *to));
  57. extern int msgidcheck __ARGS((char *string));
  58.   
  59. #ifdef SMTPSERVER
  60.   
  61. /* Command table */
  62. static char *commands[] = {
  63.     "helo",
  64. #define HELO_CMD    0
  65.     "noop",
  66. #define NOOP_CMD    1
  67.     "mail from:",
  68. #define MAIL_CMD    2
  69.     "quit",
  70. #define QUIT_CMD    3
  71.     "rcpt to:",
  72. #define RCPT_CMD    4
  73.     "help",
  74. #define HELP_CMD    5
  75.     "data",
  76. #define DATA_CMD    6
  77.     "rset",
  78. #define RSET_CMD    7
  79.     "expn",
  80. #define EXPN_CMD    8
  81. #ifdef  LZW
  82.     "xlzw",
  83. #define XLZW_CMD    9
  84. #endif
  85.     NULLCHAR
  86. };
  87.   
  88. int SmtpUsers;
  89.   
  90. /* Reply messages */
  91. static char Banner[] = "220 %s SMTP ready\n";
  92. static char Closing[] = "221 Closing\n";
  93. static char Ok[] = "250 Ok\n";
  94. static char Reset[] = "250 Reset state\n";
  95. static char Sent[] = "250 Sent\n";
  96. static char Ourname[] = "250 %s, Share and Enjoy!\n";
  97. #ifdef  LZW
  98. static char LZWOk[] = "250 %d %d LZW Ok\n";
  99. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN XLZW\n214 End\n";
  100. #else
  101. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
  102. #endif
  103. static char Enter[] = "354 Enter mail, end with .\n";
  104. static char DupBidMsg[] = "550 Duplicate BID in Message-Id line\n";
  105. static char Ioerr[] = "452 Temp file write error\n";
  106. static char Badcmd[] = "500 Command unrecognized\n";
  107. static char Syntax[] = "501 Syntax error\n";
  108. static char Needrcpt[] = "503 Need RCPT (recipient)\n";
  109. static char Unknown[] = "550 <%s> address unknown\n";
  110. static char Noalias[] = "550 No alias for <%s>\n";
  111.   
  112. extern int SmtpServTdisc;
  113. extern int SmtpBidCheck;
  114.   
  115. /* Start up SMTP receiver service */
  116. int
  117. smtp1(argc,argv,p)
  118. int argc;
  119. char *argv[];
  120. void *p;
  121. {
  122.     int16 port;
  123.   
  124.     if(argc < 2)
  125.         port = IPPORT_SMTP;
  126.     else
  127.         port = atoi(argv[1]);
  128.   
  129.     return start_tcp(port,"SMTP Server",smtpserv,2048);
  130. }
  131.   
  132. /* Shutdown SMTP service (existing connections are allowed to finish) */
  133. int
  134. smtp0(argc,argv,p)
  135. int argc;
  136. char *argv[];
  137. void *p;
  138. {
  139.     int16 port;
  140.   
  141.     if(argc < 2)
  142.         port = IPPORT_SMTP;
  143.     else
  144.         port = atoi(argv[1]);
  145.     return stop_tcp(port);
  146. }
  147.   
  148. static void
  149. smtpserv(s,unused,p)
  150. int s;
  151. void *unused;
  152. void *p;
  153. {
  154.     struct smtpsv *mp;
  155.     char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
  156.     struct list *ap,*list;
  157.     int cnt, delay_retry=0;
  158.     char address_type;
  159. #ifdef  LZW
  160.     extern int Smtpslzw;
  161.     int lzwbits, lzwmode;
  162. #endif
  163.   
  164.     sockmode(s,SOCK_ASCII);
  165.     sockowner(s,Curproc);       /* We own it now */
  166.     log(s,"open SMTP");
  167.     SmtpUsers++;
  168.   
  169.     if((mp = mail_create()) == NULLSMTPSV){
  170.         tputs(Nospace);
  171.         log(s,"close SMTP - no space");
  172.         close_s(s);
  173.         SmtpUsers--;
  174.         return;
  175.     }
  176.     mp->s = s;
  177.   
  178.     (void) usprintf(s,Banner,Hostname);
  179.   
  180.     loop:
  181.     /* Time-out after some inactivity time - WG7J */
  182.     alarm((long)(SmtpServTdisc*1000L));
  183.     if ((cnt = recvline(s,buf,sizeof(buf))) == -1){
  184.         /* He closed on us */
  185.         goto quit;
  186.     }
  187.     alarm(0L);
  188.     if(cnt < 4){
  189.         /* Can't be a legal command */
  190.         usputs(mp->s,Badcmd);
  191.         goto loop;
  192.     }
  193.     rip(buf);
  194.     cmd = buf;
  195.   
  196.     /* Translate entire buffer to lower case */
  197.     for(cp = cmd;*cp != '\0';cp++)
  198.         *cp = tolower(*cp);
  199.   
  200.     /* Find command in table; if not present, return syntax error */
  201.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  202.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  203.             break;
  204.     if(*cmdp == NULLCHAR){
  205.         (void) usputs(mp->s,Badcmd);
  206.         goto loop;
  207.     }
  208.     arg = &cmd[strlen(*cmdp)];
  209.     /* Skip spaces after command */
  210.     while(*arg == ' ')
  211.         arg++;
  212.     /* Execute specific command */
  213.     switch(cmdp-commands){
  214. #ifdef  LZW
  215.         case XLZW_CMD:
  216.             if(!Smtpslzw) {
  217.                 usputs(mp->s,Badcmd);
  218.             } else {
  219.                 lzwmode = lzwbits = 0;
  220.                 sscanf(arg,"%d %d",&lzwbits,&lzwmode);
  221.                 if(!((lzwmode == 0 || lzwmode == 1)
  222.                 && (lzwbits > 8 && lzwbits < 17))) {
  223.                     lzwmode = LZWCOMPACT;
  224.                     lzwbits = LZWBITS;
  225.                     usputs(mp->s,Badcmd);
  226.                 } else {
  227.                     usprintf(mp->s,LZWOk,lzwbits,lzwmode);
  228.                     lzwinit(mp->s,lzwbits,lzwmode);
  229.                 }
  230.             }
  231.             break;
  232. #endif
  233.         case HELO_CMD:
  234.             free(mp->system);
  235.             mp->system = strdup(arg);
  236.             (void) usprintf(mp->s,Ourname,Hostname);
  237.             break;
  238.         case NOOP_CMD:
  239.             (void) usputs(mp->s,Ok);
  240.             break;
  241.         case MAIL_CMD:
  242.             if((cp = getname(arg)) == NULLCHAR){
  243.                 (void) usputs(mp->s,Syntax);
  244.                 break;
  245.             }
  246.             free(mp->from);
  247.             mp->from = strdup(cp);
  248.             (void) usputs(mp->s,Ok);
  249.             break;
  250.         case QUIT_CMD:
  251.             (void) usputs(mp->s,Closing);
  252.             goto quit;
  253.         case RCPT_CMD:  /* Specify recipient */
  254.             if((cp = getname(arg)) == NULLCHAR || !*cp){
  255.                 (void) usputs(mp->s,Syntax);
  256.                 break;
  257.             }
  258.         /* rewrite address if possible */
  259.             if((newaddr = rewrite_address(cp)) != NULLCHAR){
  260.                 strcpy(buf,newaddr);
  261.                 cp = buf;
  262.                 free(newaddr);
  263.             }
  264.   
  265.         /* check if address is ok */
  266.             if ((address_type = validate_address(cp)) == BADADDR){
  267.                 (void) usprintf(mp->s,Unknown,cp);
  268.                 break;
  269.             }
  270.         /* if a local address check for an alias */
  271.             if (address_type == LOCAL)
  272.                 expandalias(&mp->to, cp);
  273.             else
  274.             /* a remote address is added to the list */
  275.                 addlist(&mp->to, cp, address_type);
  276.   
  277.             (void) usputs(mp->s,Ok);
  278.             break;
  279.         case HELP_CMD:
  280.             (void) usputs(mp->s,Help);
  281.             break;
  282.         case DATA_CMD:
  283.             if(mp->to == NULLLIST)
  284.                 (void) usputs(mp->s,Needrcpt);
  285.             else if ((mp->data = tmpfile()) == NULLFILE)
  286.                 (void) usputs(mp->s,Ioerr), ++delay_retry;
  287.             else
  288.                 if (getmsgtxt(mp) == 2) ++delay_retry;
  289.             break;
  290.         case RSET_CMD:
  291.             del_list(mp->to);
  292.             mp->to = NULLLIST;
  293.             (void) usputs(mp->s,Reset);
  294.             break;
  295.         case EXPN_CMD:
  296.             if (*arg == '\0'){
  297.                 (void) usputs(mp->s,Syntax);
  298.                 break;
  299.             }
  300.   
  301.             list = NULLLIST;
  302.         /* rewrite address if possible */
  303.             if((newaddr = rewrite_address(arg)) != NULLCHAR)
  304.                 if(strcmp(newaddr,arg) == 0){
  305.                     free(newaddr);
  306.                     newaddr = NULLCHAR;
  307.                 } else {
  308.                     strcpy(buf,newaddr);
  309.                     arg = buf;
  310.                 }
  311.             list = NULLLIST;
  312.             expandalias(&list,arg);
  313.             if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
  314.                 if(newaddr == NULLCHAR){
  315.                     (void) usprintf(mp->s,Noalias,arg);
  316.                     del_list(list);
  317.                     break;
  318.                 }
  319.             ap = list;
  320.             while (ap->next != NULLLIST){
  321.                 (void) usprintf(mp->s,"250-%s\n",ap->val);
  322.                 ap = ap->next;
  323.             }
  324.             usprintf(mp->s,"250 %s\n",ap->val);
  325.             del_list(list);
  326.             free(newaddr);
  327.             break;
  328.     }
  329.     goto loop;
  330.   
  331.     quit:
  332.     log(mp->s,"close SMTP");
  333.     SmtpUsers--;
  334.     close_s(mp->s);
  335.     mail_clean(mp);
  336.  
  337.     /* N5KNX: If we tried to deliver to an offline/unready printer, or to a
  338.        locked mailbox, we sent back code 452/Ioerr and the job remains in the
  339.        queue.  If we invoke smtpkick() now to reprocess the queue, we'll have
  340.        a tight loop awaiting a successful delivery.
  341.        Instead, we'll just process the queue later, in due course [provided
  342.        smtp timer was set].  The down side is we are less responsive processing
  343.        non-local email if sent in the same session that incurred an Ioerr.
  344.     */
  345.     if (!delay_retry) smtptick(NULL);         /* start SMTP daemon immediately */
  346. }
  347.   
  348. extern char shortversion[];
  349. extern char *Mbfwdinfo;
  350.   
  351. /* read the message text
  352.  * This now checks for Message-Id's that could be bids
  353.  * and deletes the message if so. - WG7J
  354.  * Returns: 0 => no errors, 1 => permanent error, 2 => temporary error (Ioerr)
  355.  */
  356. static int
  357. getmsgtxt(mp)
  358. struct smtpsv *mp;
  359. {
  360.     char buf[LINELEN];
  361.     char *p = buf;
  362.     int  mailed_ok=0;
  363.     long t;
  364.     char *cp;
  365. #ifdef MBFWD
  366.     int idnotfound;
  367.     time_t now;
  368.     char bid[LINELEN];
  369.     FILE *fp;
  370. #endif
  371.   
  372.     /* Add timestamp; ptime adds newline */
  373.     time(&t);
  374.     fprintf(mp->data,Hdrs[RECEIVED]);
  375.     if(mp->system != NULLCHAR)
  376.         fprintf(mp->data,"from %s ",mp->system);
  377. #ifdef MBFWD
  378.     fprintf(mp->data,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
  379.     Hostname, (Mbfwdinfo != NULLCHAR) ? Mbfwdinfo : shortversion, \
  380.     get_msgid(), ptime(&t));
  381. #else
  382.     fprintf(mp->data,"by %s (%s) with SMTP\n\tid AA%ld ; %s",
  383.     Hostname, shortversion, get_msgid(), ptime(&t));
  384. #endif
  385.     if(ferror(mp->data)){
  386.         (void) usputs(mp->s,Ioerr);
  387.         return 2;  /* retryable */
  388.     } else {
  389.         (void) usputs(mp->s,Enter);
  390.     }
  391. #ifdef MBFWD
  392.     idnotfound = 1;
  393. #endif
  394.     while(1){
  395.         /* Timeout after inactivity - WG7J */
  396.         alarm((long)(SmtpServTdisc*1000L));
  397.         if(recvline(mp->s,p,sizeof(buf)) == -1){
  398.             return 1;
  399.         }
  400.         alarm(0L);
  401.         rip(p);
  402.         /* check for end of message ie a . or escaped .. */
  403.         if (*p == '.'){
  404.             if (*++p == '\0'){
  405. #ifdef MBFWD
  406.                 /* Also sends appropriate response */
  407.                 /* if duplicate BID, do not send - WG7J/N9HKM */
  408.                 if(SmtpBidCheck && mp->dupbid) {
  409.                     (void) usputs(mp->s,DupBidMsg);
  410.                     mailed_ok=1;
  411.                 }
  412.                 else
  413. #endif
  414.                     if(mailit(mp->data,mp->from,mp->to) != 0) {
  415.                         (void) usputs(mp->s,Ioerr);
  416.                         mailed_ok=2;
  417.                     }
  418.                     else
  419.                         (void) usputs(mp->s,Sent);
  420.                 fclose(mp->data);
  421.                 mp->data = NULLFILE;
  422.                 del_list(mp->to);
  423.                 mp->to = NULLLIST;
  424. #ifdef MBFWD
  425.                 /* If there is a BID set, save it in the history file - WG7J */
  426.                 if(mp->bid != NULLCHAR)
  427.                     if((fp = fopen(Historyfile,APPEND_TEXT)) != NULL) {
  428.                         /* Timestamp added to allow automatic expiry
  429.                          * of bid file - WG7J
  430.                          */
  431.                         time(&now);
  432.                         fprintf(fp,"%s %ld\n",mp->bid,now); /* Save BID */
  433.                         fclose(fp);
  434.                         /* Free this bid ! */
  435.                         free(mp->bid);
  436.                         mp->bid = NULL;
  437.                     }
  438. #endif
  439.                 return mailed_ok;
  440.             } else if (!(*p == '.' && *(p+1) == '\0'))
  441.                 p--;
  442.         }
  443.         /* for UNIX mail compatiblity */
  444.         if (strncmp(p,"From ",5) == 0)
  445.             (void) putc('>',mp->data);
  446.         /* Append to data file */
  447. #ifdef MSDOS
  448.         /* Get rid of Control-Z's in the line */
  449.         cp = p;
  450.         while(*cp) {
  451.             if(*cp == CTLZ)
  452.                 *cp = '\n';
  453.             cp++;
  454.         }
  455. #endif
  456.         if(fprintf(mp->data,"%s\n",p) < 0){
  457.             (void) usputs(mp->s,Ioerr);
  458.             return 2;
  459.         }
  460. #ifdef MBFWD
  461.         /* Check for Message-Id string - WG7J */
  462.         if(idnotfound && !strnicmp(p,Hdrs[MSGID],11)) {
  463.             if((cp = getname(p)) == NULLCHAR)
  464.                 continue;
  465.             idnotfound = 0;
  466.             strcpy(bid,cp);
  467.             if((cp = strchr(bid,'@')) == NULLCHAR)
  468.                 continue;
  469.             /* A trailing ".bbs" indicates that the Message-ID was generated
  470.              * from a BBS style message, and not a RFC-822 message.
  471.              */
  472.             if(stricmp(&bid[strlen(bid) - 4], ".bbs") == 0) {
  473.                 *cp = '\0'; /*retain the bid given by user*/
  474.                 bid[13] = '\0'; /* BIDs should be no longer than 13 bytes */
  475.                 /* now check it, and save if not duplicate - WG7J */
  476.                 if((mp->dupbid = msgidcheck(bid)) == 0)
  477.                     mp->bid = strdup(bid);
  478.             }
  479.         }
  480. #endif
  481.     }
  482. }
  483.   
  484. /* Create control block, initialize */
  485. static struct smtpsv *
  486. mail_create()
  487. {
  488.     register struct smtpsv *mp;
  489.   
  490.     mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
  491.     mp->from = strdup("");  /* Default to null From address */
  492.     return mp;
  493. }
  494.   
  495. /* Free resources, delete control block */
  496. static void
  497. mail_clean(mp)
  498. register struct smtpsv *mp;
  499. {
  500.     if (mp == NULLSMTPSV)
  501.         return;
  502.     free(mp->system);
  503.     free(mp->from);
  504.     free(mp->bid);
  505.     if(mp->data != NULLFILE)
  506.         fclose(mp->data);
  507.     del_list(mp->to);
  508.     free((char *)mp);
  509. }
  510. #endif /* SMTPSERVER */
  511.   
  512. /* General mailit function. It takes a list of addresses which have already
  513. ** been verified and expanded for aliases. Base on the current mode the message
  514. ** is place in an mbox, the outbound smtp queue or the rqueue interface
  515. */
  516. /* Modified to touch the timestamp file for new-message tracking on local
  517.  * deliveries... - WG7J.
  518.  */
  519. /* Supports mail index files - WG7J */
  520. static int
  521. mailit(data,from,tolist)
  522. FILE *data;
  523. char *from;
  524. struct list *tolist;
  525. {
  526.     struct list *ap, *dlist = NULLLIST;
  527.     FILE *fp;
  528.     char mailbox[50], *cp0, *cp, *host, *qhost;
  529.     int c, fail = 0;
  530.     time_t t;
  531.     extern int Smtpquiet;
  532.     int index,type;
  533.     char *s;
  534.     long start;
  535.     struct mailindex ind;
  536.     char buf[128];
  537.   
  538.     if ((Smtpmode & QUEUE) != 0)
  539.         return(router_queue(data,from,tolist));
  540.   
  541.     do {
  542.         qhost = NULLCHAR;
  543.         for(ap = tolist;ap != NULLLIST;ap = ap->next)
  544.             if (ap->type == DOMAIN){
  545.                 if ((host = strrchr(ap->val,'@')) != NULLCHAR)
  546.                     host++;
  547.                 else
  548.                     host = Hostname;
  549.                 if(qhost == NULLCHAR)
  550.                     qhost = host;
  551.                 if(stricmp(qhost,host) == 0){
  552.                     ap->type = BADADDR;
  553.                     addlist(&dlist,ap->val,0);
  554.                 }
  555.             }
  556.         if(qhost != NULLCHAR){
  557.             rewind(data);
  558.             queuejob(data,qhost,dlist,from);
  559.             del_list(dlist);
  560.             dlist = NULLLIST;
  561.         }
  562.     } while(qhost != NULLCHAR);
  563.   
  564. #ifdef  NNTPS
  565.     for(ap = tolist;ap != NULLLIST;ap = ap->next){
  566.         if(ap->type != NNTP_GATE)
  567.             continue;
  568.         nnGpost(data,from,ap);
  569.         ap->type = BADADDR;
  570.     }
  571. #endif
  572.   
  573.     /* Clear the index to start */
  574.     memset(&ind,0,sizeof(ind));
  575.   
  576.     index = 0;
  577.     for(ap = tolist;ap != NULLLIST;ap = ap->next,index++){
  578.         if(ap->type != LOCAL){
  579.             ap->type = DOMAIN;
  580.             continue;
  581.         }
  582.         rewind(data);
  583.         /* strip off host name of LOCAL addresses */
  584.         if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  585.             *cp = '\0';
  586.   
  587.         /* replace '\' and '.' with '/', and create subdirs.
  588.          * this allows mailing into subdirs of spool/mail - WG7J
  589.          */
  590.         for(cp=ap->val;*cp != '\0'; cp++)
  591.             if((*cp == '.') || (*cp == '\\') || *cp == '/') {
  592.                 /* Now create sub directories in the message name - WG7J */
  593.                 *cp = '\0';
  594.                 sprintf(buf,"%s/%s",Mailspool,ap->val);
  595.                 mkdir(buf);
  596.                 *cp = '/';
  597.             }
  598.   
  599.         /* if mail file is busy save it in our smtp queue
  600.          * and let the smtp daemon try later.
  601.          */
  602.         if (mlock(Mailspool,ap->val)){
  603.             addlist(&dlist,ap->val,0);
  604.             fail = queuejob(data,Hostname,dlist,from);
  605.             del_list(dlist);
  606.             dlist = NULLLIST;
  607.         } else {
  608.             char buf[LINELEN];
  609.             int tocnt = 0;
  610.             SyncIndex(ap->val); /* ensure index file is current */
  611.             sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
  612. #ifndef AMIGA
  613.             if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE){
  614. #else
  615.                 if((fp = fopen(mailbox,"r+")) != NULLFILE){
  616. #endif
  617.   
  618.                     default_index(ap->val,&ind);
  619.                     time(&t);
  620.                     fseek(fp,0,SEEK_END);
  621.                     start = ftell(fp);
  622.                     fprintf(fp,"From %s %s",from,ctime(&t));
  623.                     host = NULLCHAR;
  624.                 /* Read the SMTP header, but first
  625.                  * read the 'Received...' and 'ID... lines'
  626.                  * to get the msgid - WG7J
  627.                  */
  628.                     fgets(buf,sizeof(buf),data);
  629.                     fputs(buf,fp);
  630.                     fgets(buf,sizeof(buf),data);
  631.                     fputs(buf,fp);
  632.                     if((cp=strstr(buf,"AA")) != NULLCHAR)
  633.                     /*what follows is the message-number*/
  634.                         ind.msgid = atol(cp+2);
  635.                     if((cp=strchr(buf,';')) != NULL)
  636.                         ind.mydate = mydate(cp+2);
  637.   
  638.                     while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  639.                         if(buf[0] == '\n'){
  640.                         /* End of headers */
  641.                             if(tocnt == 0) {
  642.                                 fprintf(fp,"%s%s\n",
  643.                                 Hdrs[APPARTO],
  644.                                 ap->val);
  645.                                 if(ind.to)
  646.                                     free(ind.to);
  647.                                 ind.to = strdup(ap->val);
  648.                             }
  649.                             fputc('\n',fp);
  650.                             break;
  651.                         }
  652.                         type = htype(buf);
  653.                     /* get a unique, new id for all messages - from KO4KS */
  654.                         if(index && (type == MSGID)) {
  655.                         /* KD4CIM */
  656.                             if(strstr(buf,Hostname))    /* Is it our id ? */
  657.                             /* Yes, give it a new one */
  658.                                 sprintf(buf,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  659.                         }
  660.                         fputs(buf,fp);
  661.                         rip(buf);
  662.                         set_index(buf,&ind);
  663.                         switch(type){
  664.                             case TO:
  665.                             case CC:
  666.                                 ++tocnt;
  667.                                 break;
  668.                             case RRECEIPT:
  669.                                 if((cp = getaddress(buf,0)) != NULLCHAR){
  670.                                     free(host);
  671.                                     host = strdup(cp);
  672.                                 }
  673.                                 break;
  674.                         }
  675.                     }
  676.                 /* Now the remaining data */
  677.                     while((c = fread(buf,1,sizeof(buf),data)) > 0)
  678.                         if(fwrite(buf,1,c,fp) != c)
  679.                             break;
  680.                     if(ferror(fp))
  681.                         fail = 1;
  682.                     else
  683.                     /* Leave a blank line between msgs */
  684.                         fputc('\n',fp);
  685.                     ind.size = ftell(fp) - start;
  686.                     type = 1;   /* assume disk file, not a printer device */
  687. #ifdef PRINTEROK
  688. #if defined(MSDOS)
  689. /* we now allow a printer device to be opened for output (see newfopen() in
  690.    main.c.  If we've just now written to a printer, there's no point in
  691.    writing the index, and in fact we should emit a FormFeed.  -- n5knx
  692. */
  693.                     c = ioctl(fileno(fp), 0 /* get status */);
  694.                     if (c != -1 && (c&0x9f) == 0x80) {  /* device, console,clock,nul flags */
  695.                         type=0;     /* NOT a disk file, DON'T write the index */
  696.                         fputc('\f', fp);
  697.                     }
  698. #elif defined(linux)
  699. /* need some linux code here...*/
  700. #endif
  701. #endif    /* PRINTOK */
  702.                     fclose(fp);
  703. #ifdef SMTPTRACE
  704.                   if (Smtptrace) {
  705. /* If we use tprintf here, instead of printf, flowcontrol
  706.  * in the command screen is used; if the system is unattended for
  707.  * more then 24 messages coming in, it will lock up mail delivery.
  708.  * Make sure this only goes to the command screen - WG7J
  709.  */
  710. #ifdef LINUX
  711. /* true, but we defeat that when using the trace interface anyway.  KF8NH */
  712.                     CmdOverride = 1;
  713.                     tcmdprintf(stdout,"New mail for %s from <%s>%c\n",ap->val,from,(Smtpquiet? ' ': '\007'));
  714. #else
  715.                     if(Current->output == Command->output)
  716.                         printf("New mail for %s from <%s>%c\n",ap->val,from, Smtpquiet ? ' ' : '\007');
  717. #endif
  718.                     if(host != NULLCHAR){
  719.                         rewind(data); /* Send return receipt */
  720.                         mdaemon(data,host,NULLLIST,0);
  721.                         free(host);
  722.                     }
  723.                   }
  724. #endif
  725.                 } else
  726.                     fail = 1;
  727.  
  728.                 if (fail) {
  729.                     (void) rmlock(Mailspool,ap->val);
  730.                     break;
  731.                 }
  732.   
  733. #ifdef USERLOG
  734.             /* Now touch the timestamp file if it's an area - WG7J */
  735.                 if(isarea(ap->val)) {
  736.                     sprintf(mailbox,"%s/%s.inf",Mailspool,ap->val);
  737.                     fclose(fopen(mailbox,"w"));
  738.                 }
  739. #endif
  740.             /* Update the index file */
  741.                 if (type && write_index(ap->val,&ind) == -1)
  742.                     log(-1,"smtpserv: can't update index for %s", ap->val);
  743.   
  744.             (void) rmlock(Mailspool,ap->val);
  745.  
  746.             /* make a log entry */
  747.                 smtplog("deliver: To: %s From: %s",ap->val,from);
  748.             }
  749.         }
  750.   
  751.     /* Free remaining data in index structure */
  752.         default_index("",&ind);
  753.   
  754.         return fail;
  755.     }
  756.   
  757. /* Return Date/Time in Arpanet format in passed string */
  758.     char *
  759.     ptime(t)
  760.     long *t;
  761.     {
  762.     /* Print out the time and date field as
  763.      *      "DAY day MONTH year hh:mm:ss ZONE"
  764.      */
  765.         register struct tm *ltm;
  766.         static char tz[4];
  767.         static char str[40];
  768.         char *p;
  769.     /* Read the system time */
  770.         ltm = localtime(t);
  771.   
  772.         if (*tz == '\0')
  773.             if ((p = getenv("TZ")) == NULL)
  774.                 strcpy(tz,"UTC");
  775.             else if (ltm->tm_isdst) {
  776.            /* I hope your runtime DST changeover dates match your government's ! */
  777.                 while (*p && (*p < '0' || *p > '9')) p++;  /* skip past std time name and sign offset */
  778.                 while (*p >= '0' && *p <= '9') p++; /* and GMT offset */
  779.                 strncpy(tz,p,3);
  780.             }
  781.             else strncpy(tz,p,3);
  782.   
  783.     /* rfc 822 format */
  784.         sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
  785.         Days[ltm->tm_wday],
  786.         ltm->tm_mday,
  787.         Months[ltm->tm_mon],
  788.         ltm->tm_year,
  789.         ltm->tm_hour,
  790.         ltm->tm_min,
  791.         ltm->tm_sec,
  792.         tz);
  793.         return(str);
  794.     }
  795.   
  796.     long
  797.     get_msgid()
  798.     {
  799.         char sfilename[LINELEN];
  800.         char s[20];
  801.         register long sequence = 0;
  802.         FILE *sfile;
  803.   
  804. #ifdef LINUX
  805.         int lfd, cnt;
  806.         long pid;
  807.   
  808.     /*
  809.      * I have a filter (u2j) which injects messages into JNOS for SMTP
  810.      * delivery.  It's a good idea to make sure the sequence file is locked
  811.      * while we update it, so JNOS/Linux and u2j don't get into a race for
  812.      * the next message ID.
  813.      */
  814.         sprintf(sfilename, "%s/sequence.lck", Mailqdir);
  815.         while ((lfd = open(sfilename, O_WRONLY|O_CREAT|O_EXCL, 0600)) == -1)
  816.         {
  817.             if (errno != EEXIST || ++cnt == 5)
  818.             {
  819.                 log(-1, "can't lock sequence file %s: %s", sfilename,
  820.                 strerror(errno));
  821.                 where_outta_here(1);
  822.             }
  823.             if ((lfd = open(sfilename, O_RDONLY)) != -1)
  824.             {
  825.                 sfile = fdopen(lfd, "r");
  826.                 fscanf(sfile, "%ld", &pid);
  827.                 fclose(sfile);
  828.                 if (kill(pid, 0) == -1 && errno == ESRCH)
  829.                 {
  830.                     unlink(sfilename);
  831.                     continue;
  832.                 }
  833.             }
  834.             pause(500);
  835.         }
  836.         sprintf(sfilename, "%10ld\n", getpid());
  837.         write(lfd, sfilename, strlen(sfilename));
  838.         close(lfd);
  839. #endif /* LINUX */
  840.   
  841.         sprintf(sfilename,"%s/sequence.seq",Mailqdir);
  842.         sfile = fopen(sfilename,READ_TEXT);
  843.   
  844.     /* if sequence file exists, get the value, otherwise set it */
  845.         if (sfile != NULL){
  846.             (void) fgets(s,sizeof(s),sfile);
  847.             sequence = atol(s);
  848.         /* Keep it in range of a 5 digit number to use for dos name prefix. */
  849. #ifdef LINUX
  850.         /* The bbs spec states that msg#'s on the R: line should be 0-99999
  851.          * inclusive; this violates that 'non-standard' 8-) - WG7J
  852.          */
  853.             if (sequence < 1L || sequence > 999999999L)
  854. #else
  855.                 if (sequence < 1L || sequence > 99999L )
  856. #endif
  857.                     sequence = 1;
  858.             fclose(sfile);
  859.         }
  860.   
  861.     /* increment sequence number, and write to sequence file */
  862.         sfile = fopen(sfilename,WRITE_TEXT);
  863.         fprintf(sfile,"%ld",++sequence);
  864.         fclose(sfile);
  865. #ifdef LINUX
  866.         sprintf(sfilename, "%s/sequence.lck", Mailqdir);
  867.         unlink(sfilename);
  868. #endif
  869.         return sequence;
  870.     }
  871.   
  872. /* test if mail address is valid */
  873.     int
  874.     validate_address(s)
  875.     char *s;
  876.     {
  877.         char *cp;
  878.         int32 addr;
  879.   
  880.         if(*s == '!'){
  881. #ifdef  NNTPS
  882.             if((cp = strpbrk(s,"%@.,/")) != NULLCHAR)
  883.                 *cp = '\0';
  884.             return NNTP_GATE;
  885. #else
  886.             return BADADDR;
  887. #endif
  888.         }
  889.     /* if address has @ in it then check dest address */
  890.         if ((cp = strrchr(s,'@')) != NULLCHAR){
  891.             cp++;
  892.         /* 1st check if it is our hostname.
  893.         * if not then check the hosts file and see if we can
  894.         * resolve the address to a know site or one of our aliases.
  895.         */
  896.             if(stricmp(cp,Hostname) != 0){
  897.                 if ((addr = mailroute(cp)) == 0
  898.                     && (Smtpmode & QUEUE) == 0)
  899.                     return BADADDR;
  900.                 if (ismyaddr(addr) == NULLIF)
  901.                     return DOMAIN;
  902.             }
  903.   
  904.         /* on a local address remove the host name part */
  905.             *--cp = '\0';
  906.         }
  907.   
  908.     /* if using an external router leave address alone */
  909.         if ((Smtpmode & QUEUE) != 0)
  910.             return LOCAL;
  911.   
  912.     /* check for the user%host hack */
  913.         if ((cp = strrchr(s,'%')) != NULLCHAR){
  914.             *cp = '@';
  915.             cp++;
  916.         /* reroute based on host name following the % seperator */
  917.             if (mailroute(cp) == 0)
  918.                 return BADADDR;
  919.             else
  920.                 return DOMAIN;
  921.         }
  922.   
  923. #ifdef MSDOS    /* dos file name checks */
  924.     /* Check for characters illegal in MS-DOS file names */
  925.         for(cp = s;*cp != '\0';cp++){
  926.         /* Accept '.', '/', and '\' !
  927.          * that way we can mail into subdirs - WG7J
  928.          */
  929.             if(*cp == '.' || *cp == '\\' || *cp == '/')
  930.                 continue;
  931.             if(dosfnchr(*cp) == 0){
  932.                 return BADADDR;
  933.             }
  934.         }
  935. #endif
  936.         return LOCAL;
  937.     }
  938.   
  939. /* place a mail job in the outbound queue */
  940.     int
  941.     queuejob(dfile,host,to,from)
  942.     FILE *dfile;
  943.     char *host;
  944.     struct list *to;
  945.     char *from;
  946.     {
  947.         FILE *fp;
  948.         struct list *ap;
  949.         char tmpstring[50], prefix[9], buf[LINELEN];
  950.         register int cnt;
  951.   
  952.         sprintf(prefix,"%ld",get_msgid());
  953.         mlock(Mailqdir,prefix);
  954.         sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
  955.         if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  956.             (void) rmlock(Mailqdir,prefix);
  957.             return 1;
  958.         }
  959.         while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
  960.             if(fwrite(buf, 1, cnt, fp) != cnt)
  961.                 break;
  962.         if(ferror(fp)){
  963.             fclose(fp);
  964.             (void) rmlock(Mailqdir,prefix);
  965.             return 1;
  966.         }
  967.         fclose(fp);
  968.         sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
  969.         if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  970.             (void) rmlock(Mailqdir,prefix);
  971.             return 1;
  972.         }
  973.         fprintf(fp,"%s\n%s\n",host,from);
  974.         for(ap = to; ap != NULLLIST; ap = ap->next){
  975.             fprintf(fp,"%s\n",ap->val);
  976.             smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
  977.         }
  978.         fclose(fp);
  979.         (void) rmlock(Mailqdir,prefix);
  980.         return 0;
  981.     }
  982.   
  983. /* Deliver mail to the appropriate mail boxes */
  984.     static int
  985.     router_queue(data,from,to)
  986.     FILE *data;
  987.     char *from;
  988.     struct list *to;
  989.     {
  990.         int c;
  991.         register struct list *ap;
  992.         FILE *fp;
  993.         char tmpstring[50];
  994.         char prefix[9];
  995.   
  996.         sprintf(prefix,"%ld",get_msgid());
  997.         mlock(Routeqdir,prefix);
  998.         sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
  999.         if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  1000.             (void) rmlock(Routeqdir,prefix);
  1001.             return 1;
  1002.         }
  1003.         rewind(data);
  1004.         while((c = getc(data)) != EOF)
  1005.             if(putc(c,fp) == EOF)
  1006.                 break;
  1007.         if(ferror(fp)){
  1008.             fclose(fp);
  1009.             (void) rmlock(Routeqdir,prefix);
  1010.             return 1;
  1011.         }
  1012.         fclose(fp);
  1013.         sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
  1014.         if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE){
  1015.             (void) rmlock(Routeqdir,prefix);
  1016.             return 1;
  1017.         }
  1018.         fprintf(fp,"From: %s\n",from);
  1019.         for(ap = to;ap != NULLLIST;ap = ap->next){
  1020.             fprintf(fp,"To: %s\n",ap->val);
  1021.         }
  1022.         fclose(fp);
  1023.         (void) rmlock(Routeqdir,prefix);
  1024.         smtplog("rqueue job %s From: %s",prefix,from);
  1025.         return 0;
  1026.     }
  1027.   
  1028. /* add an element to the front of the list pointed to by head
  1029. ** return NULLLIST if out of memory.
  1030. */
  1031.     struct list *
  1032.     addlist(head,val,type)
  1033.     struct list **head;
  1034.     char *val;
  1035.     int type;
  1036.     {
  1037.         register struct list *tp;
  1038.   
  1039.         tp = (struct list *)callocw(1,sizeof(struct list));
  1040.   
  1041.         tp->next = NULLLIST;
  1042.   
  1043.     /* allocate storage for the char string */
  1044.         tp->val = strdup(val);
  1045.         tp->type = type;
  1046.   
  1047.     /* add entry to front of existing list */
  1048.         if (*head == NULLLIST)
  1049.             *head = tp;
  1050.         else {
  1051.             tp->next = *head;
  1052.             *head = tp;
  1053.         }
  1054.         return tp;
  1055.   
  1056.     }
  1057.   
  1058. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  1059. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  1060.   
  1061. /* check for and alias and expand alias into a address list */
  1062.     static struct list *
  1063.     expandalias(head, user)
  1064.     struct list **head;
  1065.     char *user;
  1066.     {
  1067.         FILE *fp;
  1068.         register char *s,*p;
  1069.         struct rr *rrp, *rrlp;
  1070.         int inalias = 0;
  1071.         struct list *tp;
  1072.         char buf[LINELEN];
  1073.   
  1074.     /* no alias file found */
  1075.         if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE){
  1076.         /* Try MB, MG or MR domain name records */
  1077.             rrlp = rrp = resolve_mailb(user);
  1078.             while(rrp != NULLRR){
  1079.                 if(rrp->rdlength > 0){
  1080.                 /* remove the trailing dot */
  1081.                     rrp->rdata.name[rrp->rdlength-1] = '\0';
  1082.                 /* replace first dot with @ if there is no @ */
  1083.                     if(strchr(rrp->rdata.name,'@') == NULLCHAR
  1084.                         && (p = strchr(rrp->rdata.name,'.')) !=
  1085.                         NULLCHAR)
  1086.                         *p = '@';
  1087.                     if(strchr(rrp->rdata.name,'@') != NULLCHAR)
  1088.                         tp = addlist(head,rrp->rdata.name,
  1089.                         DOMAIN);
  1090.                     else
  1091.                         tp = addlist(head,rrp->rdata.name,
  1092.                         LOCAL);
  1093.                     ++inalias;
  1094.                 }
  1095.                 rrp = rrp->next;
  1096.             }
  1097.             free_rr(rrlp);
  1098.             if(inalias)
  1099.                 return tp;
  1100.             else
  1101.                 return addlist(head, user, LOCAL);
  1102.         }
  1103.   
  1104.         while (fgets(buf,LINELEN,fp) != NULLCHAR){
  1105.             p = buf;
  1106.             if ( *p == '#' || *p == '\0')
  1107.                 continue;
  1108.             rip(p);
  1109.   
  1110.         /* if not in an matching entry skip continuation lines */
  1111.             if (!inalias && isspace(*p))
  1112.                 continue;
  1113.   
  1114.         /* when processing an active alias check for a continuation */
  1115.             if (inalias){
  1116.                 if (!isspace(*p))
  1117.                     break;  /* done */
  1118.             } else {
  1119.                 s = p;
  1120.                 SKIPWORD(p);
  1121.                 *p++ = '\0';    /* end the alias name */
  1122.                 if (strcmp(s,user) != 0)
  1123.                     continue;   /* no match go on */
  1124.                 inalias = 1;
  1125.             }
  1126.   
  1127.         /* process the recipients on the alias line */
  1128.             SKIPSPACE(p);
  1129.             while(*p != '\0' && *p != '#'){
  1130.                 s = p;
  1131.                 SKIPWORD(p);
  1132.                 if (*p != '\0')
  1133.                     *p++ = '\0';
  1134.   
  1135.             /* find hostname */
  1136. #ifdef  NNTPS
  1137.                 if(*s == '!')
  1138.                     tp = addlist(head,s,NNTP_GATE);
  1139.                 else
  1140. #endif
  1141.                     if (strchr(s,'@') != NULLCHAR)
  1142.                         tp = addlist(head,s,DOMAIN);
  1143.                     else
  1144.                         tp = addlist(head,s,LOCAL);
  1145.                 SKIPSPACE(p);
  1146.             }
  1147.         }
  1148.         (void) fclose(fp);
  1149.   
  1150.         if (inalias)    /* found and processed and alias. */
  1151.             return tp;
  1152.   
  1153.     /* no alias found treat as a local address */
  1154.         return addlist(head, user, LOCAL);
  1155.     }
  1156.   
  1157. #if defined(ANSIPROTO)
  1158.     static void
  1159.     smtplog(char *fmt, ...)
  1160.     {
  1161.         va_list ap;
  1162.         char *cp;
  1163.         long t;
  1164.         FILE *fp;
  1165.   
  1166.         if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  1167.             return;
  1168.         time(&t);
  1169.         cp = ctime(&t);
  1170.         rip(cp);
  1171.         fprintf(fp,"%s ",cp);
  1172.         va_start(ap,fmt);
  1173.         vfprintf(fp,fmt,ap);
  1174.         va_end(ap);
  1175.         fprintf(fp,"\n");
  1176.         fclose(fp);
  1177.     }
  1178.   
  1179. #else
  1180.   
  1181.     static void
  1182.     smtplog(fmt,arg1,arg2,arg3,arg4)
  1183.     char *fmt;
  1184.     int arg1,arg2,arg3,arg4;
  1185.     {
  1186.         char *cp;
  1187.         long t;
  1188.         FILE *fp;
  1189.   
  1190.         if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  1191.             return;
  1192.         time(&t);
  1193.         cp = ctime(&t);
  1194.         rip(cp);
  1195.         fprintf(fp,"%s ",cp);
  1196.         fprintf(fp,fmt,arg1,arg2,arg3,arg4);
  1197.         fprintf(fp,"\n");
  1198.         fclose(fp);
  1199.     }
  1200. #endif
  1201.   
  1202. /* send mail to a single user. Can be called from the ax25 mailbox or
  1203. ** from the return mail function in the smtp client
  1204. */
  1205.     static int
  1206.     mailuser(data,from,to)
  1207.     FILE *data;
  1208.     char *from;
  1209.     char *to;
  1210.     {
  1211.   
  1212.         int address_type, ret;
  1213.         struct list *tolist = NULLLIST;
  1214.   
  1215.         /* check if address is ok */
  1216.         if ((address_type = validate_address(to)) == BADADDR){
  1217.             return 1;
  1218.         }
  1219.         /* if a local address check for an alias */
  1220.         if (address_type == LOCAL)
  1221.             expandalias(&tolist, to);
  1222.         else
  1223.             /* a remote address is added to the list */
  1224.             addlist(&tolist, to, address_type);
  1225.         ret = mailit(data,from,tolist);
  1226.         del_list(tolist);
  1227.         return ret;
  1228.   
  1229.     }
  1230.   
  1231. /* Mailer daemon return mail mechanism */
  1232.     int
  1233.     mdaemon(data,to,lp,bounce)
  1234.     FILE *data;     /* pointer to rewound data file */
  1235.     char *to;       /* Overridden by Errors-To: line if bounce is true */
  1236.     struct list *lp;    /* error log for failed mail */
  1237.     int bounce;     /* True for failed mail, otherwise return receipt */
  1238.     {
  1239.         time_t t;
  1240.         FILE *tfile;
  1241.         char buf[LINELEN], *cp, *newto = NULLCHAR;
  1242.         int cnt;
  1243.         if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce){
  1244.             while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1245.                 if(buf[0] == '\n')
  1246.                     break;
  1247.             /* Look for Errors-To: */
  1248.                 if(htype(buf) == ERRORSTO &&
  1249.                 (cp = getaddress(buf,0)) != NULLCHAR){
  1250.                     free(newto);
  1251.                     newto = strdup(cp);
  1252.                     break;
  1253.                 }
  1254.             }
  1255.             if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
  1256.                 to == NULLCHAR))
  1257.                 return -1;
  1258.             rewind(data);
  1259.         }
  1260.         if((tfile = tmpfile()) == NULLFILE)
  1261.             return -1;
  1262.         time(&t);
  1263.         fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
  1264.         fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  1265.         fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
  1266.         Hdrs[FROM],Hostname);
  1267.         fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
  1268.         fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
  1269.         bounce ? "Failed mail" : "Return receipt");
  1270.         if(bounce){
  1271.             fprintf(tfile,"  ===== transcript follows =====\n\n");
  1272.             for (; lp != NULLLIST; lp = lp->next)
  1273.                 fprintf(tfile,"%s\n",lp->val);
  1274.             fprintf(tfile,"\n");
  1275.         }
  1276.         fprintf(tfile,"  ===== %s follows ====\n",
  1277.         bounce ? "Unsent message" : "Message header");
  1278.   
  1279.         while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1280.             if(buf[0] == '\n')
  1281.                 break;
  1282.             fputs(buf,tfile);
  1283.         }
  1284.         if(bounce){
  1285.             fputc('\n',tfile);
  1286.             while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
  1287.                 fwrite(buf,1,cnt,tfile);
  1288.         }
  1289.         fseek(tfile,0L,0);
  1290.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  1291.         (void) mailuser(tfile,"",newto != NULLCHAR ? newto : to);
  1292.         fclose(tfile);
  1293.         free(newto);
  1294.         return 0;
  1295.     }
  1296.   
  1297.   
  1298.