home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / telecomm / ka9q_src / smtpserv.c < prev    next >
Encoding:
Text File  |  1988-07-28  |  12.5 KB  |  452 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  * Very simple implementation; no forwarding allowed
  3.  * (who wants to re-create "sendmail" ??)
  4.  *  enhanced 12/87 Dave Trulli nn2z
  5.  */
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <time.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.  
  16. #ifndef DFLT_MODE
  17. #define DFLT_MODE 0660            /* use this instead of user's umask */
  18. #endif
  19.  
  20. char *ptime(), *getname();
  21. void mail_delete(), del_rcpt();
  22. static int queuejob(),checkaddress();
  23. int32 get_msgid();
  24.  
  25. /* Command table */
  26. static char *commands[] = {
  27.     "helo",
  28. #define HELO_CMD    0
  29.     "noop",
  30. #define NOOP_CMD    1
  31.     "mail from:",
  32. #define MAIL_CMD    2
  33.     "quit",
  34. #define QUIT_CMD    3
  35.     "rcpt to:",
  36. #define RCPT_CMD    4
  37.     "help",
  38. #define HELP_CMD    5
  39.     "data",
  40. #define DATA_CMD    6
  41.     "rset",
  42. #define RSET_CMD    7
  43.     NULLCHAR
  44. };
  45.  
  46. /* Reply messages */
  47. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  48. static char banner[] = "220 %s SMTP ready\r\n";
  49. static char closing[] = "221 Closing\r\n";
  50. static char ok[] = "250 Ok\r\n";
  51. static char reset[] = "250 Reset state\r\n";
  52. static char sent[] = "250 Sent\r\n";
  53. static char ourname[] = "250 %s, \"Gateway to the universe!\"\r\n"; /*Share and Enjoy!\r\n";*/
  54. static char enter[] = "354 Enter mail, end with .\r\n";
  55. static char ioerr[] = "452 Temp file write error\r\n";
  56. static char mboxerr[] = "452 Mailbox %s write error\r\n";
  57. static char badcmd[] = "500 Command unrecognized\r\n";
  58. static char syntax[] = "501 Syntax error\r\n";
  59. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  60. static char badname[] = "550 Can't open mailbox for %s\r\n";
  61.  
  62. static struct tcb *smtp_tcb;
  63. /* Start up SMTP receiver service */
  64. smtp_start(argc,argv)
  65. int argc;
  66. char *argv[];
  67. {
  68.     struct socket lsocket;
  69.     void r_mail(),s_mail();
  70.  
  71.     lsocket.address = ip_addr;
  72.     if(argc < 2)
  73.         lsocket.port = SMTP_PORT;
  74.     else
  75.         lsocket.port = atoi(argv[1]);
  76.  
  77.     smtp_tcb = open_tcp(&lsocket,NULLSOCK,
  78.         TCP_SERVER,0,r_mail,NULLVFP,s_mail,0,(char *)NULL);
  79. }
  80.  
  81. /* Shutdown SMTP service (existing connections are allowed to finish) */
  82. smtp_stop()
  83. {
  84.     if(smtp_tcb != NULLTCB)
  85.         close_tcp(smtp_tcb);
  86. }
  87.  
  88. /* SMTP connection state change upcall handler */
  89. static void
  90. s_mail(tcb,old,new)
  91. struct tcb *tcb;
  92. char old,new;
  93. {
  94.     struct mail *mp,*mail_create();
  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.         (void) tprintf(mp->tcb,banner,hostname);
  107.         log(tcb,"open SMTP");
  108.         break;        
  109.     case CLOSE_WAIT:
  110.         close_tcp(tcb);
  111.         break;
  112.     case CLOSED:
  113.         log(tcb,"close SMTP");
  114.         mp = (struct mail *)tcb->user;
  115.         mail_delete(mp);                
  116.         del_tcp(tcb);
  117.         /* Check if server is being shut down */
  118.         if(tcb == smtp_tcb)
  119.             smtp_tcb = NULLTCB;
  120.         break;
  121.     }
  122. }
  123.  
  124. /* SMTP receiver upcall handler */
  125. static void
  126. r_mail(tcb,cnt)
  127. struct tcb *tcb;
  128. int16 cnt;
  129. {
  130.     register struct mail *mp;
  131.     char *inet_ntoa(),c;
  132.     struct mbuf *bp;
  133.     void docommand(),deliver(),doline();
  134.  
  135.     if((mp = (struct mail *)tcb->user) == NULLMAIL){
  136.         /* Unknown sessioo */
  137.         close_tcp(tcb);
  138.         return;
  139.     }
  140.     recv_tcp(tcb,&bp,cnt);
  141.     /* Assemble an input line in the session buffer.
  142.      * Return if incomplete
  143.      */
  144.     while(pullup(&bp,&c,1) == 1){
  145.         switch(c){
  146.         case '\r':    /* Strip cr's */
  147. #ifdef MSDOS
  148.         case '\032':    /* Strip ctrl/Z's */
  149. #endif
  150.             continue;
  151.         case '\n':    /* Complete line; process it */
  152.             mp->buf[mp->cnt] = '\0';
  153.             doline(mp);
  154.             break;
  155.         default:    /* Assemble line */
  156.             if(mp->cnt != LINELEN-1)
  157.                 mp->buf[mp->cnt++] = c;
  158.             break;
  159.         }
  160.     }
  161. }
  162. /* Process Process Process Process Process ipient */
  163.         if((cp = getname(arg)) == NULLCHAR){
  164.             (void) tprintf(mp->tcb,syntax);
  165.             break;
  166.         }
  167.         if (checkaddress(cp)) {
  168.             (void) tprintf(mp->tcb,badname,cp);
  169.             break;
  170.         }
  171.         /* Allocate an entry on the recipient list. This
  172.          * assembles the list backwards, but what the heck.
  173.          */
  174.         if((ap = (struct addr *)malloc(sizeof(struct addr))) == NULLADDR){
  175.             close_tcp(mp->tcb);
  176.             break;
  177.         }
  178.         if((ap->val = malloc((unsigned)strlen(cp)+1)) == NULLCHAR){
  179.             free((char *)ap);
  180.             close_tcp(mp->tcb);
  181.             break;
  182.         }
  183.         strcpy(ap->val,cp);
  184.         ap->next = mp->to;
  185.         mp->to = ap;
  186.         (void) tprintf(mp->tcb,ok);
  187.         break;
  188.     case HELP_CMD:
  189.         (void) tprintf(mp->tcb,help);
  190.         break;
  191.     case DATA_CMD:
  192.         if(mp->to == NULLADDR){
  193.             (void) tprintf(mp->tcb,needrcpt);
  194.             break;
  195.         }
  196.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  197.         if((mp->data = tmpfile()) == NULLFILE){
  198.             (void) tprintf(mp->tcb,ioerr);
  199.             break;
  200.         }
  201.         /* Add timestamp; ptime adds newline */
  202.         mp->seqn = get_msgid();
  203.         time(&t);
  204.         fprintf(mp->data,"Received: ");
  205.         if(mp->system != NULLCHAR)
  206.             fprintf(mp->data,"from %s ",mp->system);
  207.         fprintf(mp->data,"by %s with SMTP (871225.4/ST)\n\tid %ld; %s",
  208.                 hostname,
  209.                 mp->seqn,
  210.                 ptime(&t));
  211.         if(ferror(mp->data)){
  212.             (void) tprintf(mp->tcb,ioerr);
  213.         } else {
  214.             mp->state = DATA_STATE;
  215.             (void) tprintf(mp->tcb,enter);
  216.         }
  217.         break;
  218.     case RSET_CMD:
  219.         del_rcpt(mp->to);
  220.         mp->to = NULLADDR;
  221.         mp->state = COMMAND_STATE;
  222.         (void) tprintf(mp->tcb,reset);
  223.         break;
  224.     }
  225. }
  226. /* Given a string of the form <user@host>, extract the part inside the
  227.  * brackets and return a pointer to it.
  228.  */
  229. static
  230. char *
  231. getname(cp)
  232. register char *cp;
  233. {
  234.     register char *cp1;
  235.  
  236.     if((cp = index(cp,'<')) == NULLCHAR){
  237.         return NULLCHAR;
  238.     }
  239.     cp++;    /* cp -> first char of name */
  240.     if((cp1 = index(cp,'>')) == NULLCHAR){
  241.         return NULLCHAR;
  242.     }
  243.     *cp1 = '\0';
  244.     return cp;
  245. }
  246. /* Deliver mail to the appropriate mail boxes and delete temp file */
  247. static
  248. void
  249. deliver(mp)
  250. register struct mail *mp;
  251. {
  252.     int c;
  253.     register struct addr *ap;
  254.     register FILE *fp;
  255.     char    mailbox[50];
  256.     char    *cp;
  257.     int    fail = 0;
  258.  
  259.     for(ap = mp->to;ap != NULLADDR;ap = ap->next) {
  260.  
  261.         /*
  262.          * For now just look at the user name of the address
  263.          * more in next release. nn2z
  264.          */
  265.         if ((cp = index(ap->val,'@')) != NULLCHAR)
  266.             *cp = '\0';
  267.  
  268.         cp = ap->val;
  269.         while( *cp && isalnum(*cp))
  270.             cp++;
  271.         *cp = '\0';
  272.         
  273.         fseek(mp->data,0L,0);    /* rewind */
  274.  
  275.         /* if mail file is busy save it in out smtp queue
  276.          * and let the smtp daemon try later.
  277.          */
  278.         if (mlock(mailspool,ap->val))
  279.             fail = queuejob(mp->data,hostname,ap->val,mp->from,mp->seqn);
  280.         else {
  281.             sprintf(mailbox,"%s%s.txt",mailspool,ap->val);
  282.             if((fp = fopen(mailbox,"a+")) != NULLFILE) {
  283.                 while((c = getc(mp->data)) != EOF)
  284.                     if(putc(c,fp) == EOF)
  285.                         break;
  286.                 if(ferror(fp))
  287.                     fail = 1;
  288.                 /* Leave a blank line between msgs */
  289.                 fprintf(mp->data,"\n");
  290.                 fclose(fp);
  291. #ifdef UNIX
  292.                 chmod(mailbox, DFLT_MODE);
  293. #endif
  294.             } else 
  295.                 fail = 1;
  296.             (void) rmlock(mailspool,ap->val);
  297.             if (fail)
  298.                 break;
  299.         }
  300.     }
  301.     if (fail)
  302.         (void) tprintf(mp->tcb,mboxerr,ap->val);
  303.     else
  304.         (void) tprintf(mp->tcb,sent);
  305.     fclose(mp->data);
  306.     mp->data = NULLFILE;
  307.     del_rcpt(mp->to);
  308.     mp->to = NULLADDR;
  309. }
  310.  
  311. /* Return Date/Time in Arpanet format in passed string */
  312. char *
  313. ptime(t)
  314. long *t;
  315. {
  316.     register struct tm *ltm;
  317.     struct tm *gmtime();
  318.     static char timezone[4];
  319.     static char str[40];
  320.     extern char *getenv();
  321.     /* Print out the time and date field as
  322.      *        "DAY day MONTH year hh:mm:ss ZONE"
  323.      */
  324.     char *p;
  325.     static char *days[7] = {
  326.         "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  327.  
  328.     static char *months[12] = {
  329.         "Jan","Feb","Mar","Apr","May","Jun",
  330.         "Jul","Aug","Sep","Oct","Nov","Dec" };
  331.  
  332.     /* Read the system time */
  333.     ltm = gmtime(t);
  334.  
  335.     if (*timezone == '\0')
  336.         if ((p = getenv("TZ")) == NULL)
  337.             strcpy(timezone,"GMT");
  338.         else
  339.             strncpy(timezone,p,3);
  340.  
  341.     /* rfc 822 format */
  342.     sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
  343.         days[ltm->tm_wday],
  344.         ltm->tm_mday,
  345.         months[ltm->tm_mon],
  346.         ltm->tm_year,
  347.         ltm->tm_hour,
  348.         ltm->tm_min,
  349.         ltm->tm_sec,
  350.         timezone);
  351.     return(str);
  352. }
  353.  
  354. int32 
  355. get_msgid()
  356. {
  357.     char sfilename[LINELEN];
  358.     char s[20];
  359.     long sequence = 0;
  360.     FILE *sfile;
  361.     long atol();
  362.  
  363.     strcpy(sfilename,mailqdir);
  364.     strcat(sfilename,"sequence.seq");
  365.     sfile = fopen(sfilename,"r");
  366.  
  367.     /* if sequence file exists, get the value, otherwise set it */
  368.     if (sfile != NULL) {
  369.         (void) fgets(s,sizeof(s),sfile);
  370.         sequence = atol(s);
  371.     /* Keep it in range of and 8 digit number to use for dos name prefix. */
  372.         if (sequence < 0L || sequence > 99999999L )
  373.             sequence = 0;
  374.         fclose(sfile);
  375.     }
  376.  
  377.     /* increment sequence number, and write to sequence file */
  378.     sfile = fopen(sfilename,"w");
  379.     fprintf(sfile,"%ld",++sequence);
  380.     fclose(sfile);
  381. #ifdef UNIX
  382.     chmod(sfile, DFLT_MODE);
  383. #endif
  384.     return sequence;
  385. }
  386.  
  387. /* test if mail address is valid - to be improved in next release */
  388. static int
  389. checkaddress(s)
  390. char *s;
  391. {
  392.     FILE *fp;
  393.     char mailbox[50];
  394.     char *cp;
  395.  
  396.     strcpy(mailbox,mailspool);
  397.     cp = mailbox;
  398.     while (*cp)    /* find end of string */
  399.         cp++;
  400.  
  401.     while ( *s && isalnum(*s))    /*GRI MOD append the user name */
  402.         *cp++ = *s++;
  403.  
  404.     *cp = '\0';
  405.     strcat(mailbox,".txt");     /* and file type */
  406.     /* Check to see if we can open the mailbox */
  407.     if ((fp = fopen(mailbox,"a+")) == NULLFILE)
  408.         return 1;
  409.     fclose(fp);
  410.     return 0;
  411. }
  412.  
  413. /* place a mail job in the outbound queue */
  414. static int
  415. queuejob(dfile,host,to,from,id)
  416. FILE *dfile;
  417. char *host,*to,*from;
  418. int32 id;
  419. {
  420.     FILE *fp;
  421.     char tmpstring[50];
  422.     char prefix[9];
  423.     int c;
  424.  
  425.     sprintf(prefix,"%.8d",id);
  426.     mlock(mailqdir,prefix);
  427.     sprintf(tmpstring,"%s%s.txt",mailqdir,prefix);
  428.     if((fp = fopen(tmpstring,"w")) == NULLFILE) {
  429.         (void) rmlock(mailqdir,prefix);
  430.         return 1;
  431.     }
  432.     while((c = getc(dfile)) != EOF)
  433.         if(putc(c,fp) == EOF)
  434.             break;
  435.     if(ferror(fp)){
  436.         fclose(fp);
  437.         (void) rmlock(mailqdir,prefix);
  438.         return 1;
  439.     }
  440.     fclose(fp);
  441.     sprintf(tmpstring,"%s%s.wrk",mailqdir,prefix);
  442.     if((fp = fopen(tmpstring,"w")) == NULLFILE) {
  443.         (void) rmlock(mailqdir,prefix);
  444.         return 1;
  445.     }
  446.     fprintf(fp,"%s\n%s\n%s\n",host,from,to);
  447.     fclose(fp);
  448.     (void) rmlock(mailqdir,prefix);
  449.     return 0;
  450. }
  451.  
  452.