home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR3 / KA9Q212.ZIP / SMTPSERV.12 < prev    next >
Text File  |  1992-07-10  |  24KB  |  1,048 lines

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