home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 5 / DATAFILE_PDCD5.iso / internet / netlite2 / NET / c / SMTPSERV < prev    next >
Encoding:
Text File  |  1993-03-27  |  23.3 KB  |  729 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <time.h>
  8. #include <ctype.h>
  9. #include "global.h"
  10. #include "mbuf.h"
  11. #include "netuser.h"
  12. #include "timer.h"
  13. #include "tcp.h"
  14. #include "smtp.h"
  15. #include "misc.h"
  16.  
  17. static void s_mail(struct tcb *, char, char);
  18. static void r_mail(struct tcb *, int16);
  19. static void doline(struct mail *);
  20. static struct mail *mail_create(struct tcb *);
  21. static void mail_delete(struct mail *);
  22. static void docommand(struct mail *);
  23. static char *getname(char *);
  24. static void deliver(struct mail *);
  25.  
  26.  
  27. /* Command table */
  28. static char *commands[] = {
  29.         "helo",
  30. #define HELO_CMD        0
  31.         "noop",
  32. #define NOOP_CMD        1
  33.         "mail from:",
  34. #define MAIL_CMD        2
  35.         "quit",
  36. #define QUIT_CMD        3
  37.         "rcpt to:",
  38. #define RCPT_CMD        4
  39.         "help",
  40. #define HELP_CMD        5
  41.         "data",
  42. #define DATA_CMD        6
  43.         "rset",
  44. #define RSET_CMD        7
  45.         NULLCHAR
  46. };
  47.  
  48. /* Reply messages */
  49. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  50. static char banner[] = "220 %s SMTP ready\r\n";
  51. static char closing[] = "221 Closing\r\n";
  52. static char ok[] = "250 Ok\r\n";
  53. static char reset[] = "250 Reset state\r\n";
  54. static char sent[] = "250 Sent\r\n";
  55. static char ourname[] = "250 %s, Share and Enjoy!\r\n";
  56. static char enter[] = "354 Enter mail, end with .\r\n";
  57. static char ioerr[] = "452 Temp file write error\r\n";
  58. static char mboxerr[] = "452 Mailbox write error\r\n";
  59. static char badcmd[] = "500 Command unrecognized\r\n";
  60. static char syntax[] = "501 Syntax error\r\n";
  61. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  62. static char unknown[] = "550 <%s> address unknown\r\n";
  63.  
  64. static struct tcb *smtp_tcb;
  65. /* Start up SMTP receiver service */
  66. int smtp1(int argc, char **argv)
  67. {
  68.         struct socket lsocket;
  69.  
  70.         lsocket.address = ip_addr;
  71.         if(argc < 2)
  72.                 lsocket.port = SMTP_PORT;
  73.         else
  74.                 lsocket.port = atoi(argv[1]);
  75.  
  76.         smtp_tcb = open_tcp(&lsocket,NULLSOCK,
  77.                 TCP_SERVER,0,(void(*)())r_mail,NULLVFP,(void(*)())s_mail,0,(char *)NULL);
  78.         return(0);
  79. }
  80.  
  81. /* Shutdown SMTP service (existing connections are allowed to finish) */
  82. int smtp0(void)
  83. {
  84.         if(smtp_tcb != NULLTCB)
  85.                 close_tcp(smtp_tcb);
  86.         return(0);
  87. }
  88.  
  89. /* SMTP connection state change upcall handler */
  90. static void s_mail(struct tcb *tcb, char old, char new)
  91. {
  92.         struct mail *mp;
  93.  
  94.         old = old;
  95.  
  96.         switch(new){
  97. #ifdef  QUICKSTART
  98.         case SYN_RECEIVED:
  99. #else
  100.         case ESTABLISHED:
  101. #endif
  102.                 if((mp = mail_create(tcb)) == NULLMAIL){
  103.                         close_tcp(tcb);
  104.                         break;
  105.                 }
  106.                 tprintf(mp->tcb,banner,hostname);
  107.                 break;          
  108.         case CLOSE_WAIT:
  109.                 close_tcp(tcb);
  110.                 break;
  111.         case CLOSED:
  112.                 mp = (struct mail *)tcb->user;
  113.                 mail_delete(mp);                                
  114.                 del_tcp(tcb);
  115.                 /* Check if server is being shut down */
  116.                 if(tcb == smtp_tcb)
  117.                         smtp_tcb = NULLTCB;
  118.                 break;
  119.         }
  120. }
  121.  
  122. /* SMTP receiver upcall handler */
  123. static void r_mail(struct tcb *tcb, int16 cnt)
  124. {
  125.         register struct mail *mp;
  126.         char c;
  127.         struct mbuf *bp;
  128.  
  129.         if((mp = (struct mail *)tcb->user) == NULLMAIL){
  130.                 /* Unknown session */
  131.                 close_tcp(tcb);
  132.                 return;
  133.         }
  134.         recv_tcp(tcb,&bp,cnt);
  135.         /* Assemble an input line in the session buffer.
  136.          * Return if incomplete
  137.          */
  138.         while(pullup(&bp,&c,1) == 1){
  139.                 switch(c){
  140.                 case '\r':      /* Strip cr's */
  141.                         continue;
  142.                 case '\n':      /* Complete line; process it */
  143.                         mp->buf[mp->cnt] = '\0';
  144.                         doline(mp);
  145.                         break;
  146.                 default:        /* Assemble line */
  147.                         if(mp->cnt != LINELEN-1)
  148.                                 mp->buf[mp->cnt++] = c;
  149.                         break;
  150.                 }
  151.         }
  152. }
  153. /* Process a line read on an SMTP connection (any state) */
  154. static void doline(register struct mail *mp)
  155. {
  156.         switch(mp->state){
  157.         case COMMAND_STATE:
  158.                 docommand(mp);
  159.                 break;
  160.         case DATA_STATE:
  161.                 tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  162.                 if(mp->buf[0] == '.' && mp->buf[1] == '\0'){
  163.                         mp->state = COMMAND_STATE;
  164.                 /* Also sends appropriate response */
  165.                         deliver(mp);
  166.                         fclose(mp->data);
  167.                         mp->data = NULLFILE;
  168.                         del_list(mp->to);
  169.                         mp->to = NULLLIST;
  170.                         break;
  171.                 }
  172.                 /* for UNIX mail compatiblity */
  173.                 if (strncmp(mp->buf, "From ", 5) == 0)
  174.                         putc('>', mp->data);
  175.                 /* Append to data file - JSN 27/03/93 */
  176.                 if (strncmp(mp->buf, "..", 2) == 0) {
  177.                         if (fprintf(mp->data, "%s\n", mp->buf + 1) < 0) {
  178.                                 mp->state = COMMAND_STATE;
  179.                                 tprintf(mp->tcb, ioerr);
  180.                         }
  181.                 } else {
  182.                         if (fprintf(mp->data, "%s\n", mp->buf) < 0) {
  183.                                 mp->state = COMMAND_STATE;
  184.                                 tprintf(mp->tcb, ioerr);
  185.                         }
  186.                 }
  187.                 break;
  188.         }
  189.         mp->cnt = 0;
  190. }
  191. /* Create control block, initialize */
  192. static struct mail *mail_create(register struct tcb *tcb)
  193. {
  194.         register struct mail *mp;
  195.  
  196.         if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL)
  197.                 return NULLMAIL;
  198.         mp->tcb = tcb;          /* Downward pointer */
  199.         tcb->user = (char *)mp; /* Upward pointer */
  200.         return mp;
  201. }
  202.  
  203. /* Free resources, delete control block */
  204. static void mail_delete(register struct mail *mp)
  205. {
  206.  
  207.         if (mp == NULLMAIL)
  208.                 return;
  209.         if(mp->system != NULLCHAR)
  210.                 free(mp->system);
  211.         if(mp->from != NULLCHAR)
  212.                 free(mp->from);
  213.         if(mp->data != NULLFILE)
  214.                 fclose(mp->data);
  215.         del_list(mp->to);
  216.         free((char *)mp);
  217. }
  218.  
  219. /* Parse and execute mail commands */
  220. static void docommand(register struct mail *mp)
  221. {
  222.         register char **cmdp,*arg,*cp,*cmd;
  223.         struct list *ap;
  224.         time_t t;
  225.         char address_type;
  226.  
  227.         cmd = mp->buf;
  228.         if(mp->cnt < 4){
  229.                 /* Can't be a legal SMTP command */
  230.                 tprintf(mp->tcb,badcmd);
  231.                 return;
  232.         }       
  233.         cmd = mp->buf;
  234.  
  235.         /* Translate entire buffer to lower case */
  236.         for(cp = cmd;*cp != '\0';cp++)
  237.                 *cp = tolower(*cp);
  238.  
  239.         /* Find command in table; if not present, return syntax error */
  240.         for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  241.                 if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  242.                         break;
  243.         if(*cmdp == NULLCHAR){
  244.                 tprintf(mp->tcb,badcmd);
  245.                 return;
  246.         }
  247.         arg = &cmd[strlen(*cmdp)];
  248.         /* Skip spaces after command */
  249.         while(*arg == ' ')
  250.                 arg++;
  251.         /* Execute specific command */
  252.         switch(cmdp-commands){
  253.         case HELO_CMD:
  254.                 if(mp->system != NULLCHAR)
  255.                         free(mp->system);
  256.                 if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  257.                         /* If the system is out of memory, just close */
  258.                         close_tcp(mp->tcb);
  259.                         break;                  
  260.                 } else {
  261.                         strcpy(mp->system,arg);
  262.                         tprintf(mp->tcb,ourname,hostname);
  263.                 }
  264.                 break;
  265.         case NOOP_CMD:
  266.                 tprintf(mp->tcb,ok);
  267.                 break;
  268.         case MAIL_CMD:
  269.                 if(mp->from != NULLCHAR)
  270.                         free(mp->from);
  271.                 if((mp->from = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  272.                         /* If the system is out of memory, just close */
  273.                         close_tcp(mp->tcb);
  274.                         break;                  
  275.                 } else {
  276.                         if((cp = getname(arg)) == NULLCHAR){
  277.                                 tprintf(mp->tcb,syntax);
  278.                                 break;
  279.                         }
  280.                         strcpy(mp->from,cp);
  281.                         tprintf(mp->tcb,ok);
  282.                 }
  283.                 break;
  284.         case QUIT_CMD:
  285.                 tprintf(mp->tcb,closing);
  286.                 close_tcp(mp->tcb);
  287.                 break;
  288.         case RCPT_CMD:  /* Specify recipient */
  289.                 if((cp = getname(arg)) == NULLCHAR){
  290.                         tprintf(mp->tcb,syntax);
  291.                         break;
  292.                 }
  293.  
  294.                 /* check if address is ok */
  295.                 if ((address_type = validate_address(cp)) == BADADDR) {
  296.                         tprintf(mp->tcb,unknown,cp);
  297.                         break;
  298.                 }
  299.                 /* if a local address check for an alias */
  300.                 if (address_type == LOCAL)
  301.                         expandalias(&mp->to, cp);
  302.                 else
  303.                         /* a remote address is added to the list */
  304.                         addlist(&mp->to, cp, address_type);
  305.  
  306.                 tprintf(mp->tcb,ok);
  307.                 break;
  308.         case HELP_CMD:
  309.                 tprintf(mp->tcb,help);
  310.                 break;
  311.         case DATA_CMD:
  312.                 if(mp->to == NULLLIST){
  313.                         tprintf(mp->tcb,needrcpt);
  314.                         break;
  315.                 }
  316.                 tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  317.                 if((mp->data = tmpfile()) == NULLFILE){
  318.                         tprintf(mp->tcb,ioerr);
  319.                         break;
  320.                 }
  321.                 /* Add timestamp; ptime adds newline */
  322.                 time(&t);
  323.                 fprintf(mp->data,"Received: ");
  324.                 if(mp->system != NULLCHAR)
  325.                         fprintf(mp->data,"from %s ",mp->system);
  326.                 ap = mp->to;
  327.                 fprintf(mp->data,"by %s with SMTP\n\tid AA%ld for %s ; %s",
  328.                                 hostname, get_msgid(), ap->val, ptime(&t));
  329.                 if(ferror(mp->data)){
  330.                         tprintf(mp->tcb,ioerr);
  331.                 } else {
  332.                         mp->state = DATA_STATE;
  333.                         tprintf(mp->tcb,enter);
  334.                 }
  335.                 break;
  336.         case RSET_CMD:
  337.                 del_list(mp->to);
  338.                 mp->to = NULLLIST;
  339.                 mp->state = COMMAND_STATE;
  340.                 tprintf(mp->tcb,reset);
  341.                 break;
  342.         }
  343. }
  344. /* Given a string of the form <user@host>, extract the part inside the
  345.  * brackets and return a pointer to it.
  346.  */
  347. static char *getname(register char *cp)
  348. {
  349.         register char *cp1;
  350.  
  351.         if((cp = strchr(cp,'<')) == NULLCHAR)
  352.                 return NULLCHAR;
  353.         cp++;   /* cp -> first char of name */
  354.         if((cp1 = strchr(cp,'>')) == NULLCHAR)
  355.                 return NULLCHAR;
  356.         *cp1 = '\0';
  357.         return cp;
  358. }
  359.  
  360. /* Deliver mail to the appropriate mail boxes and delete temp file */
  361. static void deliver(register struct mail *mp)
  362. {
  363.         int ret;
  364.  
  365.         /* send to the rqueue */
  366.         if ((smtpmode & QUEUE) != 0) {
  367.                 ret = router_queue(mp->tcb,mp->data,mp->from,mp->to);
  368.                 if (ret != 0)
  369.                         tprintf(mp->tcb,ioerr);
  370.         } else {
  371.                 ret = mailit(mp->tcb,mp->data,mp->from,mp->to);
  372.                 if (ret != 0)
  373.                         tprintf(mp->tcb,mboxerr);
  374.         }
  375.         if (ret == 0)
  376.                 tprintf(mp->tcb,sent);
  377.                 
  378. }
  379.  
  380. /* used to save local mail or reroute remote mail */
  381. int mailit(struct tcb *tcb, FILE *data, char *from, struct list *to)
  382. {
  383.         register struct list *ap;
  384.         register FILE *fp;
  385.         int c;
  386.         char    mailbox[50];
  387.         char    *cp;
  388.         char    *desthost;
  389.         int     fail = 0;
  390.         time_t  t;
  391.         for(ap = to;ap != NULLLIST;ap = ap->next) {
  392.  
  393.                 fseek(data,0L,0);       /* rewind */
  394.  
  395.                 /* non local mail queue it */
  396.                 if (ap->type == DOMAIN) {
  397.                         if ((desthost = strchr(ap->val,'@')) != NULLCHAR);
  398.                                 desthost++;
  399.                         fail = queuejob(tcb,data,desthost,ap->val,from);
  400.                 } else {
  401.                         /* strip off host name */
  402.                         if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  403.                                 *cp = '\0';
  404.  
  405.                         /* truncate long user names */
  406.                         if (strlen(ap->val) > MBOXLEN)
  407.                                 ap->val[MBOXLEN] = '\0';
  408.  
  409.                         /* if mail file is busy save it in our smtp queue
  410.                          * and let the smtp daemon try later.
  411.                          */
  412.                         if (mlock(mailspool,ap->val))
  413.                                 fail = queuejob(tcb,data,hostname,ap->val,from);
  414.                         else {
  415.                                 sprintf(mailbox,"%s.text.%s",mailspool,ap->val);
  416.                                 if((fp = fopen(mailbox,"a+")) != NULLFILE) {
  417.                                         time(&t);
  418.                                         fprintf(fp,"From %s %s",from,ctime(&t));
  419.                                         while((c = getc(data)) != EOF)
  420.                                                 if(putc(c,fp) == EOF)
  421.                                                         break;
  422.                                         if(ferror(fp))
  423.                                                 fail = 1;
  424.                                         else
  425.                                                 fprintf(fp,"\n");
  426.                                         /* Leave a blank line between msgs */
  427.                                         fclose(fp);
  428.                                 } else 
  429.                                         fail = 1;
  430.                                 rmlock(mailspool,ap->val);
  431.                                 if (fail)
  432.                                         break;
  433.                         }
  434.                 }
  435.         }
  436.         return(fail);
  437. }
  438.  
  439. /* Return Date/Time in Arpanet format in passed string */
  440. char *ptime(time_t *t)
  441. {
  442.         /* Print out the time and date field as
  443.          *              "DAY day MONTH year hh:mm:ss ZONE"
  444.          */
  445.         register struct tm *ltm;
  446.         static char str[40];
  447.         static char *days[7] = {
  448.                 "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  449.  
  450.         static char *months[12] = {
  451.                 "Jan","Feb","Mar","Apr","May","Jun",
  452.                 "Jul","Aug","Sep","Oct","Nov","Dec" };
  453.  
  454.         /* Read the system time */
  455.         ltm = localtime(t);
  456.  
  457.         /* rfc 822 format */
  458.         sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d GMT\n",
  459.                 days[ltm->tm_wday],
  460.                 ltm->tm_mday,
  461.                 months[ltm->tm_mon],
  462.                 ltm->tm_year,
  463.                 ltm->tm_hour,
  464.                 ltm->tm_min,
  465.                 ltm->tm_sec);
  466.         return(str);
  467. }
  468.  
  469. long  get_msgid(void)
  470. {
  471.         char sfilename[LINELEN];
  472.         char s[20];
  473.         register long sequence = 0;
  474.         FILE *sfile;
  475.  
  476.         sprintf(sfilename,"%s.sequence",mailqdir);
  477.         sfile = fopen(sfilename,"r");
  478.  
  479.         /* if sequence file exists, get the value, otherwise set it */
  480.         if (sfile != NULL) {
  481.                 fgets(s,sizeof(s),sfile);
  482.                 sequence = atol(s);
  483.         /* Keep it in range of and 8 digit number to use for dos name prefix. */
  484.                 if (sequence < 0L || sequence > 99999999L )
  485.                         sequence = 0;
  486.                 fclose(sfile);
  487.         }
  488.  
  489.         /* increment sequence number, and write to sequence file */
  490.         sfile = fopen(sfilename,"w");
  491.         fprintf(sfile,"%ld",++sequence);
  492.         fclose(sfile);
  493.         return sequence;
  494. }
  495.  
  496. #ifdef  MSDOS
  497. /* Illegal characters in a DOS filename */
  498. static char baddoschars[] = "\"[]:|<>+=;,";
  499. #endif
  500.  
  501. /* test if mail address is valid */
  502. int validate_address(char *s)
  503. {
  504.         char *cp;
  505.         int32 addr;
  506.         char    address_type;
  507.  
  508.         /* if address has @ in it the check dest address */
  509.         if ((cp = strchr(s,'@')) != NULLCHAR) {
  510.                 cp++;
  511.                 /* 1st check if its our hostname
  512.                 * if not then check the hosts file and see
  513.                 * if we can resolve ther address to a know site
  514.                 * or one of our aliases
  515.                 */
  516.                 if (strcmp(cp,hostname) != 0) {
  517.                         if ((addr = mailroute(cp)) == 0
  518.                                 && (smtpmode & QUEUE) == 0)
  519.                                 return BADADDR;
  520.                         if (addr != ip_addr)
  521.                                 return DOMAIN;
  522.                 }
  523.                 
  524.                 /* on a local address remove the host name part */
  525.                 *--cp = '\0';
  526.         }
  527.  
  528.         /* if using an external router leave address alone */
  529.         if ((smtpmode & QUEUE) != 0)
  530.                 return LOCAL;
  531.  
  532.  
  533.         /* check for the user%host hack */
  534.         if ((cp = strchr(s,'%')) != NULLCHAR) {
  535.                 *cp = '@';
  536.                 cp++;
  537.                 /* reroute based on host name following the % seperator */
  538.                 if (mailroute(cp) == 0)
  539.                         return BADADDR;
  540.                 else
  541.                         return DOMAIN;
  542.         }
  543.         address_type = LOCAL;
  544.  
  545. #ifdef MSDOS    /* dos file name checks */
  546.         /* Check for characters illegal in MS-DOS file names */
  547.         for(cp = baddoschars;*cp != '\0';cp++){
  548.                 if(strchr(s,*cp) != NULLCHAR)
  549.                         return BADADDR; 
  550.         }
  551. #endif
  552.         return LOCAL;
  553. }
  554.  
  555. /* place a mail job in the outbound queue */
  556. int queuejob(struct tcb *tcb, FILE *dfile, char *host, char *to, char *from)
  557. {
  558.         FILE *fp;
  559.         char tmpstring[50];
  560.         char prefix[9];
  561.         register int c;
  562.  
  563.         tcb = tcb;
  564.         sprintf(prefix,"%ld",get_msgid());
  565.         mlock(mailqdir,prefix);
  566.         sprintf(tmpstring,"%s.text.%s",mailqdir,prefix);
  567.         if((fp = fopen(tmpstring,"w")) == NULLFILE) {
  568.                 rmlock(mailqdir,prefix);
  569.                 return 1;
  570.         }
  571.         while((c = getc(dfile)) != EOF)
  572.                 if(putc(c,fp) == EOF)
  573.                         break;
  574.         if(ferror(fp)){
  575.                 fclose(fp);
  576.                 rmlock(mailqdir,prefix);
  577.                 return 1;
  578.         }
  579.         fclose(fp);
  580.         sprintf(tmpstring,"%s.work.%s",mailqdir,prefix);
  581.         if((fp = fopen(tmpstring,"w")) == NULLFILE) {
  582.                 rmlock(mailqdir,prefix);
  583.                 return 1;
  584.         }
  585.         fprintf(fp,"%s\n%s\n%s\n",host,from,to);
  586.         fclose(fp);
  587.         rmlock(mailqdir,prefix);
  588.         return 0;
  589. }
  590.  
  591. /* Deliver mail to the appropriate mail boxes */
  592. int router_queue(struct tcb *tcb, FILE *data, char *from, struct list *to)
  593. {
  594.         int c;
  595.         register struct list *ap;
  596.         FILE *fp;
  597.         char tmpstring[50];
  598.         char prefix[9];
  599.  
  600.         tcb = tcb;
  601.         sprintf(prefix,"%ld",get_msgid());
  602.         mlock(routeqdir,prefix);
  603.         sprintf(tmpstring,"%s.text.%s",routeqdir,prefix);
  604.         if((fp = fopen(tmpstring,"w")) == NULLFILE) {
  605.                 rmlock(routeqdir,prefix);
  606.                 return 1;
  607.         }
  608.         fseek(data,0L,0);       /* rewind */
  609.         while((c = getc(data)) != EOF)
  610.                 if(putc(c,fp) == EOF)
  611.                         break;
  612.         if(ferror(fp)){
  613.                 fclose(fp);
  614.                 rmlock(routeqdir,prefix);
  615.                 return 1;
  616.         }
  617.         fclose(fp);
  618.         sprintf(tmpstring,"%s.work.%s",routeqdir,prefix);
  619.         if((fp = fopen(tmpstring,"w")) == NULLFILE) {
  620.                 rmlock(routeqdir,prefix);
  621.                 return 1;
  622.         }
  623.         fprintf(fp,"From: %s\n",from);
  624.         for(ap = to;ap != NULLLIST;ap = ap->next) {
  625.                 fprintf(fp,"To: %s\n",ap->val);
  626.         }
  627.         fclose(fp);
  628.         rmlock(routeqdir,prefix);
  629.         return 0;
  630. }
  631.  
  632. /* add an element to the front of the list pointed to by head 
  633. ** return NULLLIST if out of memory.
  634. */
  635. struct list *addlist(struct list **head, char *val, int type)
  636. {
  637.         register struct list *tp;
  638.  
  639.         tp = (struct list *)calloc(1,sizeof(struct list));
  640.         if (tp == NULLLIST)
  641.                 return NULLLIST;
  642.  
  643.         tp->next = NULLLIST;
  644.  
  645.         /* allocate storage for the char string */
  646.         if ((tp->val = malloc((unsigned)strlen(val)+1)) == NULLCHAR) {
  647.                 free((char *)tp);
  648.                 return NULLLIST;
  649.         }
  650.         strcpy(tp->val,val);
  651.         tp->type = type;
  652.  
  653.         /* add entry to front of existing list */
  654.         if (*head == NULLLIST)
  655.                 *head = tp;
  656.         else {
  657.                 tp->next = *head;
  658.                 *head = tp;
  659.         }
  660.         return tp;
  661.  
  662. }
  663.  
  664. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  665. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  666.  
  667. /* check for and alias and expand alias into a address list */
  668. struct list *expandalias(struct list **head, char *user)
  669. {
  670.         FILE *fp;
  671.         register char *s,*p,*h;
  672.         int inalias;
  673.         struct list *tp;
  674.         char buf[LINELEN];
  675.         
  676.         
  677.                 /* no alias file found */
  678.         if ((fp = fopen(alias, "r")) == NULLFILE)
  679.                 return addlist(head, user, LOCAL);
  680.  
  681.         inalias = 0;
  682.         while (fgets(buf,LINELEN,fp) != NULLCHAR) {
  683.                 p = buf;
  684.                 if ( *p == '#' || *p == '\0')
  685.                         continue;
  686.                 rip(p);
  687.  
  688.                 /* if not in an matching entry skip continuation lines */
  689.                 if (!inalias && isspace(*p))
  690.                         continue;
  691.  
  692.                 /* when processing an active alias check for a continuation */
  693.                 if (inalias) {
  694.                         if (!isspace(*p)) 
  695.                                 break;  /* done */
  696.                 } else {
  697.                         s = p;
  698.                         SKIPWORD(p);
  699.                         *p++ = '\0';    /* end the alias name */
  700.                         if (strcmp(s,user) != 0)
  701.                                 continue;       /* no match go on */
  702.                         inalias = 1;
  703.                 }
  704.  
  705.                 /* process the recipients on the alias line */
  706.                 SKIPSPACE(p);
  707.                 while(*p != '\0' && *p != '#') {
  708.                         s = p;
  709.                         SKIPWORD(p);
  710.                         if (*p != '\0')
  711.                                 *p++ = '\0';
  712.  
  713.                         /* find hostname */
  714.                         if ((h = strchr(s,'@')) != NULLCHAR)
  715.                                 tp = addlist(head,s,DOMAIN);
  716.                         else
  717.                                 tp = addlist(head,s,LOCAL);
  718.                         SKIPSPACE(p);
  719.                 }
  720.         }
  721.         fclose(fp);
  722.  
  723.         if (inalias)    /* found and processed and alias. */
  724.                 return tp;
  725.  
  726.         /* no alias found treat as a local address */
  727.         return addlist(head, user, LOCAL);
  728. }
  729.