home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR3 / KA9Q212.ZIP / SMTPSERV.17 < prev    next >
Text File  |  1993-04-01  |  28KB  |  1,332 lines

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