home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / lan / snuz.arj / SMTP.C < prev    next >
C/C++ Source or Header  |  1991-03-24  |  13KB  |  570 lines

  1. /******** SMTP  **********
  2. * Client process for     *
  3. * Simple Mail Transfer   *
  4. * Protocol               *
  5. * for the IBM PC         *
  6. *************************/
  7.  
  8. /*  Copyright 1987, 1988 by USC Information Sciences Institute (ISI) */
  9. /*  See permission and disclaimer notice in file "isi-note.h"  */
  10. /*#include    "isi-note.h"                                   */
  11.  
  12. /*  Copyright 1984 by the Massachusetts Institute of Technology  */
  13. /*  See permission and disclaimer notice in file "notice.h"  */
  14. /*  #include    <notice.h> */
  15.  
  16. /*#2 4/16/84 Change do_data to do netascii newline xformations.  <DMC>
  17.  *
  18.  *#12 Fixup for loop logic in expect.  5/29/84  dmc 
  19.  *
  20.  *#14 Increase timeout and improve handling.  5/31/84  dmc
  21.  *
  22.  *#16 In tgets, don't allocate resptime every call, do it in main instead;
  23.  *     increase RESPTIME if dbg is on.        6/5/84  dmc
  24.  *
  25.  *#19 Replace wakes of TCPsend with calls to tcp_ex(), which does PUSH.
  26.  *                            6/11/84  dmc
  27.  *
  28.  *#84362 Get mail-from field out of mail file instead of cmd line
  29.  *
  30.  *#85028 Init cirput,cirget to cirbuf!!!  Also replace "short timeout" msg
  31.  *#85035 Init termcode early in main
  32.  *
  33.  *Added some #includes so it'll compile under Microsoft C 3.0. - koji
  34.  *
  35.  *  3/1/1991 JDM changed code substantially to read lists of files to
  36.  *  post and change the way the "to" and "from" lines are handled. The
  37.  *  previous code for this was bizarre to say the least.
  38.  */
  39.  
  40. /* NEEDS THE PCIP PACKAGE (Harvard flavor) TO LINK!!!!   */
  41.  
  42. /* smtp.c
  43.  * 
  44.  * User mode smtp.  Takes a source and a recipient and tries to
  45.  * deliver the mail to each.  Writes the returned error codes out to the
  46.  * standard output for the use of its caller.  Note that this routine uses
  47.  * the small tcp and hence must run under tasking.
  48.  *
  49.  * Calling sequence:
  50.  *     smtp <mail file> <dest-host> <dbg>
  51.  */
  52.  
  53. /* The mail file begins with a single "From: x@a.b.c.d" line and has
  54.  * as many "To: y@q.w.e.r" lines as desired
  55.  */ 
  56.  
  57.  
  58. typedef    long    time_t;            /* ugly! */
  59.  
  60. #include    <stdio.h>
  61. #include    <types.h>
  62. #include    <netq.h>
  63. #include    <task.h>
  64. #include    <ip.h>
  65. #include    <timer.h>
  66. #include    <custom.h>
  67. #include    <string.h>
  68.  
  69. #define    NONE        0        /* no such command */
  70. #define    HELO        1
  71. #define    MAIL        2
  72. #define    RCPT        3
  73. #define    DATA        4
  74. #define    QUIT        5
  75. #define    RSET        6
  76. #define    NOOP        7
  77.  
  78. struct    cmdtab    {
  79.     char    *c_name;        /* command name */
  80.     int    c_len;            /* command length */
  81. } cmdtab[] = {
  82.     { "", 0, },
  83.     { "HELO", 4, },
  84.     { "MAIL FROM:", 10 },
  85.     { "RCPT TO:", 8, },
  86.     { "DATA", 4, },
  87.     { "QUIT", 4, },
  88.     { "RSET", 4, },
  89.     { "NOOP", 4, },
  90.     { 0, 0, }            /* end of table marker */
  91. };
  92.  
  93.  
  94. #define    SMTPSOCK    25        /* smtp server socket */
  95. #define    WINDOW        1000        /* seems reasonable */
  96. #define    RESPTIME    30        /*#14 maximum response timeout */
  97. #define    BUFSIZ        512
  98.  
  99. char    cmdbuf[BUFSIZ];            /* command buffer */
  100. char    mpath[BUFSIZ];            /*#84362 recipient buffer */
  101. int    buflen;                /* size of string in cmd buffer */
  102. char    cirbuf[BUFSIZ];            /* circular buffer for cmd input */
  103. char    *cirput;            /* put ptr for circular buf */
  104. char    *cirget;            /* get ptr for circular buf */
  105. char    termbuf[BUFSIZ];        /* termination line */
  106. char    *smtpaddr;            /* Global holder for rmt host argv */
  107. int    termcode;            /* program termination code */
  108. int    dbg;                /* debugging flag */
  109. int    success;            /* successful transfer flag */
  110. task    *tk_smtp;            /* the smtp user task */
  111. timer    *resptime;            /* current response timer */
  112. event    conn_open;            /* connection open event */
  113. event    in_avail;            /* input available event */
  114. event    spc_avail;            /* output buffer space available */
  115. event    rsptmo;                /* response timeout event */
  116.  
  117. extern    int    cmd_rcv(), sm_tmo();
  118. extern    int    us_opna(), us_cls(), us_tmo(), bfr(), us_yld(), pr_dot();
  119.  
  120.  
  121. /* Insure that the mail file is readable, then open a tcp connection to
  122.  * the server smtp at the destination site.  If successful, send the
  123.  * mail by calling the routine do_cmds().
  124.  */
  125.  
  126. main(argc, argv)
  127.     int    argc;
  128.     char    **argv; {
  129.     FILE    *mlfd, *lffd;            /* mail file desc */
  130.     in_name    fhost;            /* foreign host address */
  131.     int    flag;            /* debug flags */
  132.     char     fbuf[128];
  133.     int    i;
  134.     char     *ptr;
  135.  
  136.     if(argc < 3 || argc > 4) {
  137.         printf("501 usage: smtp <file> <dest-host> [dbg]\n");
  138.         exit(1);
  139.         }
  140.     
  141.     if(argc == 4) {
  142.         flag = atoi(argv[3]);
  143.         dbg = flag & 1;
  144.         printf("Debug mode: %d\n",dbg);
  145.         }
  146.     
  147.     if((lffd = fopen(argv[1], "ra")) == NULL) {
  148.         printf("554 mail file unreadable\n");
  149.         exit(1);
  150.         }
  151.  
  152.     termcode = 1;            /*#85035 Assume failure */
  153.     tcp_init(512, us_opna, cmd_rcv, us_yld, us_cls, us_tmo, pr_dot, bfr);
  154.     
  155.     smtpaddr = argv[2];
  156.     if((fhost = resolve_name(smtpaddr)) == 0L) {
  157.         printf("550 Never heard of host %s\n", smtpaddr);
  158.         exit(1);
  159.         }
  160.     
  161.     
  162.     resptime = tm_alloc();        /*#16 Do this once only, here */
  163.     cirput = cirget = cirbuf;    /*#85028 Init these, too */
  164.     tk_smtp = tk_cur;
  165.     
  166.     tcp_open(&fhost, SMTPSOCK, tcp_sock(), WINDOW, WINDOW/2);
  167.     while(!conn_open)
  168.         tk_block();
  169.     conn_open = 0;
  170.     expect(220);            /* expect a service ready msg */
  171.  
  172.     if(strstr(argv[1],"sendmail.lst") == NULL) {
  173.         do_cmds(lffd);
  174.         fclose(lffd);
  175.     } else {
  176.         while(1){
  177.             if(fgets(fbuf,126,lffd) == NULL)break;
  178.             ptr = fbuf;
  179.             while(*ptr == ' ')ptr++;
  180.             for(i = 0; i < 128 && ptr[i]; i++)if(ptr[i] == '\n')ptr[i] = 0;
  181.             if((mlfd = fopen(ptr,"r")) == NULL)continue;
  182.             do_cmds(mlfd);
  183.             remove(ptr);
  184.         }
  185.         fclose(lffd);
  186.         remove(argv[1]);
  187.     }
  188.  
  189.     
  190.  
  191.  
  192.     tputs("QUIT\n");
  193.     expect(221);
  194.     termcode = 0;
  195.     tcp_close();
  196.     close_wait(RESPTIME);
  197.     }
  198.  
  199.  
  200. /* Do the necessary commands for a smtp transfer.  Start by waiting for the
  201.  * connection to open, then send HELO, MAIL, RCPT, and DATA.  Check the
  202.  * reply codes and give up if needed.
  203.  */
  204.  
  205. do_cmds(mlfd)
  206.     FILE    *mlfd; {            /* mail file descriptor */
  207.  
  208.     tputs("HELO ");
  209.     tputs(custom.c_user);        /* Use name from here */
  210.     tputs("\n");
  211.     expect(250);            /* expect an OK */
  212.  
  213.         if(mparse_path(mlfd,'f') != 1) {
  214.         printf("didn't find \"From:\" \n");
  215.     }
  216.     fseek(mlfd,0l,0);
  217.         if(mparse_path(mlfd,'t') == 0){
  218.         printf("didn't find \"To:\" \n");
  219.     }
  220.     fseek(mlfd,0l,0);
  221.  
  222.     tputs("DATA\n");
  223.  
  224.     expect(354);
  225.     printf("DATA\n");
  226.     do_data(mlfd);
  227.  
  228.     printf("data sent\n");
  229.  
  230.     expect(250);
  231.  
  232.  
  233.     success = TRUE;
  234.     }
  235.  
  236.  
  237. int mparse_path(mfile, flag)
  238.     register FILE    *mfile;
  239.     int flag;
  240.  
  241. {
  242.     char buf[256];
  243.     register char    *p, *q;    
  244.     int i, r;        
  245.     char c;        
  246.  
  247.      i = 0;
  248.         while(1) {
  249.         if(fgets(buf,254,mfile) == NULL)return i;
  250.         if(*buf == '\n') return i;
  251.  
  252.         if(flag == 'f') {
  253.             if(strnicmp("From:",buf,5) == 0){
  254.                 p = &buf[5];
  255.                 while(*p == ' ')p++;
  256.                 q = p;
  257.                 while(*q != ' ' && *q != '\n' && *q != 0)q++;
  258.                 if (p == q) continue;
  259.                 *q = 0;
  260.                 tputs("MAIL FROM:<");
  261.                    tputs(p);
  262.                 tputs(">\n");
  263. printf("MAIL FROM:<%s>\n",p);
  264.                 expect(250);        
  265.                 return 1;
  266.             }
  267.         } else {
  268.             if(strnicmp("To:",buf,3) == 0){
  269.                 p = &buf[3];
  270.                 while(*p == ' ')p++;
  271.                 q = p;
  272.                 while(*q != ' ' && *q != '\n' && *q != 0)q++;
  273.                 if (p == q) continue;
  274.                 *q = 0;
  275.                 tputs("RCPT TO:<");
  276.                    tputs(p);
  277.                 tputs(">\n");
  278. printf("RCPT TO:<%s>\n",p);
  279.                 expect(250);        
  280.                 i++;
  281.             }
  282.         }
  283.     }
  284. }
  285.  
  286.  
  287. /* Send the data from the specified mail file out on the current smtp
  288.  * connection.  Do the appropriate netascii conversion and starting '.'
  289.  * padding.  Send the <CRLF>.<CRLF> at completion.
  290.  */
  291.  
  292. do_data(fd)
  293.     register FILE    *fd; {        /* mail file descriptor */
  294.     register char    c;        /* current character */
  295.      extern    task    *TCPsend;
  296.     event    sendef;
  297.  
  298.     while((c = getc(fd)) != EOF) {    /*#2 Do netascii xformations */
  299.         if(c == '\n')
  300.             tputc('\r');
  301.         tputc(c);
  302.         if(c == '\r')
  303.             tputc('\0');
  304.     }
  305.  
  306.     tputc('\r');
  307.     tputc('\n');
  308.     tputc('.');
  309.     tputc('\r');
  310.     tputc('\n');
  311.     
  312.     tcp_ex();            /*#19 make sure it gets sent */
  313.     }
  314.  
  315.  
  316. expect(code)
  317.  
  318. /* Expect a reply message with the specified code.  If the specified code
  319.  * is received return TRUE; otherwise print the error message out on the
  320.  * standard output and give up.  Note that the reply can be a multiline
  321.  * message.
  322.  *
  323.  * Arguments:
  324.  */
  325.  
  326. int    code;
  327. {
  328.     int    retcd;
  329.     char *data = cmdbuf;
  330.     int i,errtmo;            /*#16 Add errtmo to vary timeout */
  331.     
  332.     for(;;) {        /* get whole reply */
  333.         errtmo = (dbg) ? 4*RESPTIME : RESPTIME;    /*#16 longer if dbg */
  334.         if(tgets(cmdbuf, errtmo) > 0) { /*#16 get input line */
  335.             if(cmdbuf[3] == '-') /* continuation line? */
  336.                 continue;
  337.  
  338.             for(i=0; i<30; i++)
  339.                 if(*data >= '0' && *data <= '9') break;
  340.                 else data++;
  341.  
  342.             retcd = atoi(data);
  343. /*            sscanf(cmdbuf, "%d", &retcd);  no, last line */
  344.             if(retcd == code)    /* If equal, */
  345.                 return;        /*  just return */
  346.             i = code - code % 100;    /*  else check if */
  347.                         /*  in right range */
  348.             if ( retcd >= i && retcd <= i+99)
  349.                 return;        /* It is */
  350.             
  351.             if(dbg)
  352.              printf("Expected: %u, got %u.\n",code, retcd);
  353.             strcpy(termbuf, cmdbuf); /* return the error line */
  354.             tputs("QUIT\n");
  355.             break;    /*#12 Get out of for loop */
  356.  
  357.         } else if(success) {    /* Here if not tgets(... */
  358.                 if(dbg) printf("Expect: Couldnt get input line,    but transfer successful\n");    /*#12*/
  359.                 strcpy(termbuf, "250 OK\n");
  360.                 return;    /*#12 Get out of for loop */
  361.             }
  362.             else {            /* Here if not success */
  363.                 if (dbg) printf("Expect: TGets failed\n");/*#14*/
  364.                 strcpy(termbuf, "451 Receive timeout\n");
  365.                 break;
  366.             }
  367.     }                    /* End for(;;) */
  368.     termcode = !success;            /* error return */
  369.     tcp_close();
  370.     close_wait(RESPTIME);
  371. }
  372.  
  373.  
  374. us_opna()
  375.  
  376. /* Called by the tcp receiver task when a connection is successfully opened.
  377.  * Just wakes up the smtp task and returns
  378.  */
  379. {
  380.     tk_setef(tk_smtp, &conn_open);
  381. }
  382.  
  383.         
  384. cmd_rcv(buf, len, urg)
  385.  
  386. /* Called by the tcp receive task on receipt of a command from the net
  387.  * Just copy the data into the circular buffer and wake up the smtp task
  388.  * to process the data.
  389.  *
  390.  * Arguments:
  391.  */
  392.  
  393. char    *buf;                /* data buffer */
  394. int    len;                /* data length */
  395. int    urg;                /* urgent ptr(unused) */
  396. {
  397.     register char    *p, *q;
  398.     register int    i;
  399.     
  400.     for(p = cirput, q = buf, i = len; i > 0; i--) { /* copy the data */
  401.         if(p == &cirbuf[BUFSIZ])
  402.             p = cirbuf;
  403.         *p++ = *q++;
  404.     }
  405.  
  406.     cirput = p;
  407.     
  408.     tk_setef(tk_smtp, &in_avail);
  409. }
  410.  
  411.  
  412. tgets(buf, tmo)
  413.  
  414. /* Wait for a full command line to be input.  As the characters come in,
  415.  * they are copied into the circular buffer.  This routine takes them
  416.  * out and copies them into the specified buffer for processing.  It
  417.  * performs the netascii conversion on the fly.  Returns the number of
  418.  * characters input, or -1 on response timeout.
  419.  *
  420.  * Arguments:
  421.  */
  422.  
  423. char    *buf;                /* buffer address */
  424. int    tmo;                /* response timeout */
  425. {
  426.     register char    *p, *q;        /* temp pointers */
  427.     register int    i;        /* length counter */
  428.     static    int    crseen = FALSE;    /* for netascii conversion */
  429.     
  430.     for(q = buf, i = 0;;) {
  431.         for(p = cirget; p != cirput; p++) {
  432.             if(p == &cirbuf[BUFSIZ])
  433.                 p = cirbuf;
  434.             if(crseen) {    /* need to de-netascify */
  435.                 crseen = FALSE;
  436.                 if(*p == '\n') {
  437.                     *q++ = '\n'; /* end of line */
  438.                     *q = '\0'; /* null terminate */
  439.                     i++;
  440.                     cirget = ++p; /* clean up */
  441.                     if(dbg)
  442.                         printf("%s", buf);
  443.                     return(i);
  444.                 } else {
  445.                     *q++ = '\r';
  446.                     i++;
  447.                     if(*p != '\0') {
  448.                         *q++ = *p;
  449.                         i++;
  450.                     }
  451.                 }
  452.             } else if(*p == '\r') {
  453.                 crseen = TRUE;
  454.             } else {
  455.                 *q++ = *p;
  456.                 i++;
  457.             }
  458.         }
  459.         
  460.         cirget = p;        /* fix get ptr. back up */
  461.  
  462. /* Full line not in yet; wait for character input */
  463.  
  464.         tm_set(tmo, sm_tmo, 0, resptime);
  465.  
  466.         for(;;) {
  467.             if(in_avail) {
  468.                 in_avail = 0;
  469.                 rsptmo = 0;    /*#14 In case both */
  470.                 tm_clear(resptime);
  471.                 break;
  472.             }
  473.             if(rsptmo) {
  474.                 rsptmo = 0;
  475.                 return(-1);    }
  476.             tk_block();
  477.         }
  478.     }
  479. }
  480.  
  481.  
  482. tputs(str)
  483.  
  484. /* Send the specified string out to the net.  Do the appropriate netascii
  485.  * conversion as it goes.
  486.  *
  487.  * Arguments:
  488.  */
  489.  
  490. register char    *str;
  491. {
  492.      extern    task    *TCPsend;
  493.     event    sendef;
  494.     
  495.     if(dbg)
  496.         printf("%s", str);
  497.         
  498.     for(; *str != '\0'; str++) {
  499.         if(*str == '\n')
  500.             tputc('\r');
  501.         tputc(*str);
  502.         if(*str == '\r')
  503.             tputc('\0');
  504.     }
  505.         
  506.     tcp_ex();            /*#19 make sure it gets sent */
  507.     }
  508.  
  509.  
  510. /* Put the specified character out to tcp.  If the output buffer is full,
  511.  * block until reawakened by the tcp (via the us_space routine).
  512.  */
  513.  
  514. tputc(c)
  515.     char    c; {            /* character to output */
  516.  
  517.     while(tc_put(c)) tk_block();
  518.     }
  519.  
  520.  
  521. us_tmo() {
  522.     printf("451 Host not responding\n");
  523.     exit(1);
  524.     }
  525.  
  526.  
  527. sm_tmo() {
  528.     tk_setef(tk_smtp, &rsptmo);
  529.     }
  530.  
  531. /* Called by the tcp layer when space becomes available in the tcp output
  532.  * buffer.  Just wakes up the smtp task if it's asleep.
  533.  */
  534.  
  535. bfr() {
  536.  
  537.     tk_setef(tk_smtp, &spc_avail);
  538.     }
  539.  
  540. pr_dot() {
  541.     printf(".");
  542.     }
  543.  
  544.  
  545. us_cls() {
  546.     int    opening=0, ourclosing=0, hisclosing=0;
  547.     
  548. /*    if(termbuf[0] == '\0')
  549.         printf("450 termbuf null.\n success = %d, opening = %d,\
  550. ourclosing = %d, hisclosing = %d\n", success, opening, ourclosing, hisclosing);
  551.     else
  552.         printf("%s", termbuf);
  553. */    exit(termcode);
  554.     }
  555.  
  556. us_yld() {
  557.     return 0;
  558.     }
  559.  
  560. close_wait(tmo)
  561.     int tmo; {
  562.  
  563.     rsptmo = 0;
  564.     tm_set(tmo, sm_tmo, 0, resptime);
  565.     while(!rsptmo)
  566.         tk_block();        /* don't come back */
  567.     printf("Timed out waiting for close\n");
  568.     exit(termcode);
  569.     }
  570.