home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / src / smtp / smtpsrvr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-28  |  29.1 KB  |  1,242 lines

  1. /*
  2.  *                      S M T P S R V R . C
  3.  *
  4.  *              SMTP Mail Server for MMDF under 4.2bsd
  5.  *
  6.  * Eric Shienbrood (BBN) 3 Apr 81 - SMTP server, based on BBN FTP server
  7.  * Modified to talk to MMDF Jun 82 by Donn Neuhengen.
  8.  * Doug Kingston, BRL: Took a machete to large chunks of unnecessary code.
  9.  *      Reimplemented the interface to MMDF and used 4.2BSD style networking.
  10.  *      Usage:  smtpsrvr <them> <us> <channel>
  11.  */
  12.  
  13. #include "util.h"
  14. #include "mmdf.h"
  15. #include "ch.h"
  16. #include "ap.h"
  17. #include "phs.h"
  18. #include "smtp.h"
  19. #include <stdio.h>
  20. #include <signal.h>
  21. #include <sys/stat.h>
  22. #include <pwd.h>
  23. #ifndef NOFCNTL
  24. #include <fcntl.h>
  25. #endif
  26.  
  27. #include "ns.h"
  28.  
  29. #define BYTESIZE        8
  30.  
  31. /* mmdf configuration variables */
  32. extern LLog *logptr;
  33. extern char *supportaddr;
  34. extern int errno;               /* Has Unix error codes */
  35. extern int numfds;
  36.  
  37. extern char *getline();
  38. extern char *index();
  39. extern char *rindex();
  40. extern char *malloc();
  41. extern char *strdup();
  42. extern struct passwd *getpwmid();
  43. extern char *multcat();
  44. extern Chan *ch_h2chan();
  45.  
  46. char *progname, *us, *them, *channel;   /* Arguments to program */
  47. char *sender = 0;                       /* address of mail sender */
  48. Chan *chanptr;                          /* pointer to incoming channel */
  49.  
  50. #define BUFL 600                /* length of buf */
  51. char    buf[BUFL];              /* general usage */
  52. char    netbuf[BUFL];           /* the place that has the valid characters */
  53. char    saveaddr[BUFL];         /* save previous address in expn_save */
  54. int     savecode;               /* save previous result code in expn_save */
  55. int     expn_count;             /* number of expn expansions */
  56. int     netcount = 0;           /* number of valid characters in netbuf */
  57. char    *netptr  = netbuf;      /* next character to come out of netbuf */
  58. char    *arg;                   /* zero if no argument - pts to comm param */
  59. int     dont_mung;              /* used by getline() to limit munging */
  60. int     numrecipients = 0;      /* number of valid recipients accepted */
  61. int     stricked;               /* force rejection of non validated hosts */
  62. #ifdef NODOMLIT
  63. int    themknown=TRUE;        /* do we have symbolic name for them? */
  64. #endif /* NODOMLIT */
  65. char    *addrfix();
  66. int     vrfy_child;             /* pid of child that handles vrfy requests */
  67. int     vrfy_p2c[2];            /* fd's for vrfy's parent-to-child pipe */
  68. int     vrfy_c2p[2];            /* fd's for vrfy's child-to-parent pipe */
  69. FILE    *vrfy_out;              /* fd for vrfy's parent to write to child */
  70. FILE    *vrfy_in;               /* fd for vrfy's parent to read from child */
  71.  
  72. /* character defines */
  73. #define CR      '\r'    /* carriage return */
  74. #define LF      '\n'    /* line feed */
  75. #define CNULL   '\0'    /* null */
  76.  
  77.  
  78. /****************************************************************
  79.  *                                                              *
  80.  *      C O M M A N D   D I S P A T C H   T A B L E             *
  81.  *                                                              *
  82.  ****************************************************************/
  83.  
  84. int helo(), mail(), quit(), help(), rcpt(), confirm();
  85. int data(), rset(), reject(), expn(), vrfy();
  86.  
  87. struct comarr           /* format of the command table */
  88. {
  89.     char *cmdname;          /* ascii name */
  90.     int (*cmdfunc)();       /* command procedure to call */
  91. } commands[] = {
  92.     "helo", helo,           "noop", confirm,
  93.     "mail", mail,           "data", data,
  94.     "rcpt", rcpt,           "help", help,
  95.     "quit", quit,           "rset", rset,
  96.         "expn", expn,           "vrfy", vrfy,
  97.     NULL, NULL
  98. };
  99.  
  100.  
  101. /*
  102.  *              M A I N
  103.  *
  104.  *      Takes commands from the assumed network connection (file desc 0)
  105.  *      under the assumption that they follow the ARPA network mail
  106.  *      transfer protocol RFC 788 and appropriate modifications.
  107.  *      Command responses are returned via file desc 1.
  108.  *
  109.  *      There is a small daemon waiting for connections to be
  110.  *      satisfied on socket 25 from any host.  As connections
  111.  *      are completed by the ncpdaemon, the returned file descriptor
  112.  *      is setup as the standard input (file desc 0) and standard
  113.  *      output (file desc 1) and this program is executed to
  114.  *      deal with each specific connection.  This allows multiple
  115.  *      server connections, and minimizes the system overhead
  116.  *      when connections are not in progress.  File descriptors
  117.  *      zero and one are used to ease production debugging and
  118.  *      to allow the forking of other relavent Unix programs
  119.  *      with comparative ease.
  120.  *
  121.  *              while commands can be gotten
  122.  *                      find command procedure
  123.  *                              call command procedure
  124.  *                              get next command
  125.  *                      command not found
  126.  *
  127.  */
  128. main (argc, argv)
  129. int argc;
  130. char **argv;
  131. {
  132.     register struct comarr *comp;
  133.     char    replybuf[128];
  134.     char *i;
  135.     long atime;
  136.     Chan *curchan;
  137.     char    tmp_buf[LINESIZE];
  138.     char    tmpstr[LINESIZE];
  139.     char    *Ags[20];
  140.     int     n, Agc;
  141.  
  142.     progname = argv[0];
  143.     mmdf_init( progname );
  144.  
  145.     if (argc != 4){
  146.         ll_log( logptr, LLOGFAT, "wrong number of args!" );
  147.         exit(NOTOK);
  148.     }
  149.  
  150.     /* force rejection of unknown hosts */
  151.     if (*progname == 'r')
  152.         stricked++;
  153.  
  154.     /*
  155.      * first look up address of sender. Used to be in server but
  156.      * now must be in this routine so that we can send a reject
  157.      * reply.
  158.      */
  159.     them = argv[1];
  160.     us = argv[2];
  161.                 /* A numeric address - don't like it */
  162.     if(*them >= '0' && *them <= '9')
  163.         if ( stricked){
  164.         ll_log (logptr, LLOGTMP, "smtpsrvr can't lookup '%s'", them);
  165.         sprintf(replybuf,
  166.                "421 %s: Cannot resolve your address. '%s'\r\n",us,them);
  167.         netreply (replybuf);
  168.         exit (-1);
  169.         }
  170.         else {   /* make into [x.x.x.x] format */
  171.         them = strdup(them);
  172.             strcpy(tmpstr, them);  
  173.         sprintf(them, "[%s]", tmpstr);
  174. #ifdef NODOMLIT
  175.         themknown = FALSE;
  176. #endif /* NODOMLIT */
  177.         }
  178.         
  179.     /*
  180.      * found out who you are I might even believe you.
  181.      */
  182.  
  183.     /*
  184.      * the channel arg is now a comma seperated list of channels
  185.      * useful for multiple sources ( As on UCL's ether )
  186.      */
  187.     strcpy (tmp_buf, argv[3]);
  188.     Agc = str2arg (tmp_buf, 20, Ags, (char *)0);
  189.     channel = Ags[Agc-1];
  190.     for(chanptr = (Chan *)0, n = 0 ; n < Agc ; n++){
  191.         if ( (curchan = ch_nm2struct(Ags[n])) == (Chan *) NOTOK) {
  192.             ll_log (logptr, LLOGTMP, "smtpsrvr (%s) bad channel",
  193.                 Ags[n]);
  194.             continue;
  195.         }
  196.         /*
  197.          * Is this a valid host for this channel ?
  198.          */
  199.         switch(tb_k2val (curchan -> ch_table, TRUE, them, tmpstr)){
  200.         default:        /* Either NOTOK or MAYBE */
  201.             if ((n != (Agc-1)) || stricked)
  202.                 continue;
  203.             /* fall through so we get some channel name to use */
  204.         case OK:
  205.             chanptr = curchan;
  206.             channel = curchan -> ch_name;
  207.             break;
  208.         }
  209.         break;
  210.     }
  211.     time(&atime);
  212.  
  213.     if (chanptr == (Chan *) 0){
  214.         ll_log (logptr, LLOGTMP, "smtpsrvr (%s) no channel for host",
  215.                                     them);
  216.         sprintf (replybuf,
  217.             "421 %s: Your name, '%s', is unknown to us.\r\n",
  218.             us, them);
  219.         netreply (replybuf);
  220.         exit (-1);
  221.     }
  222.     ch_llinit (chanptr);
  223.     ll_log( logptr, LLOGGEN, "OPEN: %s %.19s (%s)",
  224.             them, ctime(&atime), channel);
  225.     phs_note(chanptr, PHS_RESTRT);
  226.  
  227.     mmdfstart();
  228.  
  229.     /* say we're listening */
  230.     sprintf (replybuf, "220 %s Server SMTP (Complaints/bugs to:  %s)\r\n",
  231.             us, supportaddr);
  232.     netreply (replybuf);
  233.  
  234. nextcomm:
  235.     while (i = getline())
  236.     {
  237.         if (i == (char *)NOTOK)         /* handle error ??? */
  238.             byebye( 1 );
  239.  
  240.         /* find and call command procedure */
  241.         comp = commands;
  242.         while( comp->cmdname != NULL)   /* while there are names */
  243.         {
  244.             if (strcmp(buf, comp->cmdname) == 0) /* a winner */
  245.             {
  246.             (*comp->cmdfunc)();     /* call comm proc */
  247.             goto nextcomm;          /* back for more */
  248.             }
  249.             comp++;             /* bump to next candidate */
  250.         }
  251.         netreply("500 Unknown or unimplemented command\r\n" );
  252.         ll_log(logptr, LLOGBTR, "unknown command '%s'", buf);
  253.     }
  254.     byebye(0);
  255. }
  256.  
  257. /*name:
  258.     getline
  259.  
  260. function:
  261.     get commands from the standard input terminated by <cr><lf>.
  262.     afix a pointer( arg ) to any arguments passed.
  263.     ignore carriage returns
  264.     map UPPER case to lower case.
  265.     manage the netptr and netcount variables
  266.  
  267. algorithm:
  268.     while we havent received a line feed and buffer not full
  269.  
  270.         if netcount is zero or less
  271.             get more data from net
  272.                 error: return 0
  273.         check for delimiter character
  274.             null terminate first string
  275.             set arg pointer to next character
  276.         check for carriage return
  277.             ignore it
  278.         if looking at command name
  279.             convert upper case to lower case
  280.  
  281.     if command line (not mail)
  282.         null terminate last token
  283.     manage netptr
  284.  
  285. returns:
  286.     0 for EOF
  287.     -1 when an error occurs on network connection
  288.     ptr to last character (null) in command line
  289.  
  290. globals:
  291.     dont_mung
  292.     netcount=
  293.     netptr =
  294.     buf=
  295. */
  296.  
  297. char *
  298. getline()
  299. {
  300.     register char *inp;     /* input pointer in netbuf */
  301.     register char *outp;    /* output pointer in buf */
  302.     register int c;         /* temporary char */
  303.     extern char *progname;
  304.  
  305.     inp = netptr;
  306.     outp = buf;
  307.     arg = 0;
  308.  
  309.     do
  310.     {
  311.         if( --netcount <= 0 )
  312.         {
  313.             if (setjmp(timerest)) {
  314.                 ll_log( logptr, LLOGTMP,
  315.                     "%s net input read error (%d)",
  316.                     them, errno);
  317.                 return((char *)NOTOK);
  318.             }
  319.             s_alarm (NTIMEOUT);
  320.             netcount = read (0, netbuf, 512);
  321.             s_alarm( 0 );
  322.             if (netcount == 0)      /* EOF */
  323.                 return( 0 );
  324.             if (netcount < 0) {     /* error */
  325.                 ll_log( logptr, LLOGTMP,
  326.                     "%s net input read error (%d)",
  327.                     them, errno);
  328.                 return((char *)NOTOK);
  329.             }
  330.             inp = netbuf;
  331.         }
  332.         c = *inp++ & 0377;
  333.  
  334.         if (c == '\r' ||        /* ignore CR */
  335.             c >= 0200)          /* or any telnet codes that */
  336.             continue;       /*  try to sneak through */
  337.         if (dont_mung == 0 && arg == NULL)
  338.         {
  339.             /* if char is a delim afix token */
  340.             if (c == ' ' || c == ',')
  341.             {
  342.                 c = CNULL;       /* make null term'ed */
  343.                 arg = outp + 1; /* set arg ptr */
  344.             }
  345.             else if (c >= 'A' && c <= 'Z')
  346.             /* do case mapping (UPPER -> lower) */
  347.                 c += 'a' - 'A';
  348.         }
  349.         *outp++ = c;
  350.     } while( c != '\n' && outp < &buf[BUFL] );
  351.  
  352.     if( dont_mung == 0 )
  353.         *--outp = 0;            /* null term the last token */
  354.  
  355.     /* scan off blanks in argument */
  356.     if (arg) {
  357.         while (*arg == ' ')
  358.             arg++;
  359.         if (*arg == '\0')
  360.             arg = 0;        /* if all blanks, no argument */
  361.     }
  362.     if (dont_mung == 0)
  363.         ll_log( logptr, LLOGFTR, "'%s', '%s'", buf,
  364.             arg == 0 ? "<noarg>" : arg );
  365.  
  366.     /* reset netptr for next trip in */
  367.     netptr = inp;
  368.     /* return success */
  369.     return (outp);
  370. }
  371.  
  372. /*
  373.  *  Process the HELO command
  374.  */
  375. helo()
  376. {
  377.     char replybuf[128];
  378.  
  379. /*
  380.  * Use this if you wish to be forgiving of hosts who don't announce
  381.  * their full domain name:
  382.  *
  383.  *      (void) sprintf(replybuf, "%s.%s.%s", them, locname, locdomain);
  384.  *      if(arg == 0 || !lexequ(arg, them) && !lexequ(arg, replybuf))
  385.  *              sprintf(replybuf, "250 %s - you are a charlatan\r\n", us);
  386.  *      else  {                
  387.  *        if (lexequ(arg, replybuf))
  388.  *                      them = strdup(replybuf);
  389.  *              sprintf (replybuf, "250 %s\r\n", us);
  390.  *      }                      
  391.  *      netreply (replybuf);   
  392.  *
  393.  * -- DSH (suggested by: Hans van Staveren <sater@cs.vu.nl>)
  394.  */
  395.  
  396.     if(arg == 0 || !lexequ(arg, them))
  397.         sprintf(replybuf, "250 %s - you are a charlatan\r\n", us);
  398.     else 
  399.         sprintf (replybuf, "250 %s\r\n", us);
  400.     netreply (replybuf);
  401. }
  402.  
  403. /*
  404.  *      mail
  405.  *
  406.  *      handle the MAIL command  ("MAIL from:<user@host>")
  407.  */
  408. mail()
  409. {
  410.     char    replybuf[256];
  411.     char    info[128];
  412.     char    *lastdmn;
  413.     struct rp_bufstruct thereply;
  414.     int    len;
  415.     AP_ptr  domain, route, mbox, themap, ap_sender;
  416.  
  417.     if (arg == 0 || *arg == 0) {
  418.         netreply("501 No argument supplied\r\n");
  419.         return;
  420.     } else if( sender ) {
  421.         netreply("503 MAIL command already accepted\r\n");
  422.         return;
  423.     } else if (!equal(arg, "from:", 5)) {
  424.         netreply("501 No sender named\r\n");
  425.         return;
  426.     }
  427.  
  428.     ll_log( logptr, LLOGFTR, "mail from: '%s'", arg );
  429.  
  430.     /* Scan FROM: parts of arg */
  431.     sender = index (arg, ':') + 1;
  432.     sender = addrfix( sender );
  433.     /*
  434.      * If the From part is not the same as where it came from
  435.      * then add on the extra part of the route.
  436.      */
  437.  
  438. #ifdef NODOMLIT
  439.     if(themknown && ((ap_sender = ap_s2tree(sender)) != (AP_ptr)NOTOK)){
  440. #else
  441.     if ((ap_sender = ap_s2tree(sender)) != (AP_ptr)NOTOK){
  442. #endif /* NODOMLIT */
  443.         /*
  444.          * this must be a bit of a sledge hammer approach ??
  445.          */
  446.         ap_t2parts(ap_sender, (AP_ptr *)0, (AP_ptr *)0,
  447.                         &mbox, &domain, &route);
  448.         themap = ap_new(APV_DOMN, them);
  449.         if(ap_dmnormalize(themap, (Chan *)0) == MAYBE)
  450.             goto tout;
  451.         if(route != (AP_ptr)0){
  452.             /*
  453.              * only normalize the bits that we need
  454.              */
  455.             if(ap_dmnormalize(route, (Chan *)0) == MAYBE)
  456.                 goto tout;
  457.             lastdmn = route->ap_obvalue;
  458.             }
  459.         else if(domain != (AP_ptr)0) {
  460.             if(ap_dmnormalize(domain, (Chan *)0) == MAYBE)
  461.                 goto tout;
  462.             lastdmn = domain->ap_obvalue;
  463.         }
  464.         else /* is this a protocol violation? - add themap as domain? */
  465.             lastdmn = (char *)0;
  466.  
  467.         /*
  468.          * Check the from part here. Make exceptions for local machines
  469.          */
  470.         if (    lexequ(themap->ap_obvalue, lastdmn)
  471.              || islochost(themap->ap_obvalue, lastdmn))
  472.                 ap_free(themap);
  473.         else {
  474.             if (route != (AP_ptr)0)
  475.                 themap->ap_chain = route;
  476.                 route = themap;
  477.         }
  478.         sender = ap_p2s((AP_ptr)0, (AP_ptr)0, mbox, domain, route);
  479.         if(sender == (char *)MAYBE){    /* help !! */
  480.     tout:;
  481.             sender = (char *) 0;
  482.             netreply("451 Nameserver timeout during parsing\r\n");
  483.             return;
  484.         }
  485.         strcpy(arg, sender);
  486.         free(sender);
  487.         sender = arg;
  488.     }
  489.  
  490.     /* Supply necessary flags, "tiCHANNEL" will be supplied by winit */
  491.     if (*sender == '\0') {
  492.         /* No return mail */
  493. /*  until mailing list fix is done -- er.. *WHAT* mailing list fix -- [DSH]
  494.         sprintf( info, "mvqdh%s*k%d*", them, NS_NETTIME ); */
  495.         sprintf( info, "mvqh%s*k%d*", them, NS_NETTIME );
  496.         sender = "Orphanage";           /* Placeholder */
  497.     } else
  498. /*  until mailing list fix is done 
  499.         sprintf( info, "mvdh%s*k%d*", them, NS_NETTIME ); */
  500.         sprintf( info, "mvh%s*k%d*", them, NS_NETTIME );
  501.  
  502.     if( rp_isbad( mm_winit(channel, info, sender))) {
  503.         netreply("451 Temporary problem initializing\r\n");
  504.         sender = (char *) 0;
  505.         mm_end( NOTOK );
  506.         mmdfstart();
  507.     } else if( rp_isbad( mm_rrply( &thereply, &len ))) {
  508.         netreply( "451 Temporary problem initializing\r\n" );
  509.         sender = (char *) 0;
  510.         mm_end( NOTOK );
  511.         mmdfstart();
  512.     } else if( rp_gbval( thereply.rp_val ) == RP_BNO) {
  513.         sprintf (replybuf, "501 %s\r\n", thereply.rp_line);
  514.         netreply (replybuf);
  515.         sender = (char *) 0;
  516.         mm_end( NOTOK );
  517.         mmdfstart();
  518.     } else if( rp_gbval( thereply.rp_val ) == RP_BTNO) {
  519.         sprintf (replybuf, "451 %s\r\n", thereply.rp_line);
  520.         netreply (replybuf);
  521.         sender = (char *) 0;
  522.         mm_end( NOTOK );
  523.         mmdfstart();
  524.     } else
  525.         netreply("250 OK\r\n");
  526.     numrecipients = 0;
  527. }
  528.  
  529. islochost(them, from)
  530. char *them, *from;
  531. {
  532.     int lo, lt;
  533.     extern char *locfullmachine, *locfullname;
  534.  
  535.     if (locfullmachine == (char *)0)
  536.         return(0);
  537.     lo = strlen(locfullname);
  538.     lt = strlen(them);
  539.     return(
  540.         /* return true if they claim to be us */
  541.         lexequ(from, locfullname)
  542.         /* and are one of our local machines */
  543.          && lt > lo
  544.          && them[lt - lo - 1] == '.'
  545.          && lexequ(&them[lt - lo], locfullname)
  546.     );
  547. }
  548.  
  549. /*
  550.  *  Process the RCPT command  ("RCPT TO:<forward-path>")
  551.  */
  552. rcpt()
  553. {
  554.     register char *p;
  555.     struct rp_bufstruct thereply;
  556.     char    replybuf[256];
  557.     int     len;
  558.  
  559.     /* parse destination arg */
  560.     if( sender == (char *)0 ) {
  561.         netreply("503 You must give a MAIL command first\r\n");
  562.         return;
  563.     } else if (arg == (char *)0 || !equal(arg, "to:", 3)) {
  564.         netreply("501 No recipient named.\r\n");
  565.         return;
  566.     }
  567.     p = index( arg, ':' ) + 1;
  568.     p = addrfix( p );
  569.  
  570.     if (setjmp(timerest)) {
  571.         netreply( "451 Mail system problem\r\n" );
  572.         return;
  573.     }
  574.     s_alarm (DTIMEOUT);
  575.     if( rp_isbad( mm_wadr( (char *)0, p ))) {
  576.         if( rp_isbad( mm_rrply( &thereply, &len )))
  577.             netreply( "451 Mail system problem\r\n" );
  578.         else {
  579.             sprintf (replybuf, "451 %s\r\n", thereply.rp_line);
  580.             netreply (replybuf);
  581.         }
  582.     } else {
  583.         if( rp_isbad( mm_rrply( &thereply, &len )))
  584.             netreply("451 Mail system problem\r\n");
  585.         else if( rp_gbval( thereply.rp_val ) == RP_BNO) {
  586.             sprintf (replybuf, "550 %s\r\n", thereply.rp_line);
  587.             netreply (replybuf);
  588.         }
  589.         else if( rp_gbval( thereply.rp_val ) == RP_BTNO) {
  590.             sprintf (replybuf, "451 %s\r\n", thereply.rp_line);
  591.             netreply (replybuf);
  592.         }
  593.         else {
  594.             netreply("250 Recipient OK.\r\n");
  595.             numrecipients++;
  596.         }
  597.     }
  598.     s_alarm (0);
  599. }
  600.  
  601. /*
  602.  *      ADDRFIX()  --  This function takes the SMTP "path" and removes
  603.  *      the leading and trailing "<>"'s which would make the address
  604.  *      illegal to RFC822 mailers.  Note that although the spec states
  605.  *      that the angle brackets are required, we will accept addresses
  606.  *      without them.   (DPK@BRL, 4 Jan 83)
  607.  */
  608. char *
  609. addrfix( addrp )
  610. char *addrp;
  611. {
  612.     register char   *cp;
  613.  
  614.     if( cp = index( addrp, '<' )) {
  615.         addrp = ++cp;
  616.         if( cp = rindex( addrp, '>' ))
  617.             *cp = 0;
  618.     }
  619.     compress (addrp, addrp);
  620. #ifdef DEBUG
  621.     ll_log( logptr, LLOGFTR, "addrfix(): '%s'", addrp );
  622. #endif
  623.     return( addrp );
  624. }
  625.  
  626. /*
  627.  *  Process the DATA command.  Send text to MMDF.
  628.  */
  629. data()
  630. {
  631.     register char *p, *bufptr;
  632.     time_t  tyme;
  633.     int     errflg, werrflg;
  634.     struct rp_bufstruct thereply;
  635.     int     len, msglen;
  636.  
  637.     errflg = werrflg = msglen = 0;
  638.     if (numrecipients == 0) {
  639.         netreply("503 No recipients have been specified.\r\n");
  640.         return;
  641.     }
  642.  
  643.     if (setjmp(timerest)) {
  644.         netreply( "451 Mail system problem\r\n" );
  645.         return;
  646.     }
  647.     s_alarm (DTIMEOUT);
  648.     if( rp_isbad(mm_waend())) {
  649.         netreply("451 Unknown mail system trouble.\r\n");
  650.         return;
  651.     }
  652.     s_alarm (0);
  653.  
  654.     netreply ("354 Enter Mail, end by a line with only '.'\r\n");
  655.  
  656.     dont_mung = 1;      /* tell getline only to drop cr */
  657. #ifdef DEBUG
  658.     ll_log( logptr, LLOGFTR, "... body of message ..." );
  659. #endif
  660.     while (1) {             /* forever */
  661.         if ((p = getline()) == 0) {
  662.             p = "\n***Sender closed connection***\n";
  663.             mm_wtxt( p , strlen(p) );
  664.             errflg++;
  665.             break;
  666.         }
  667.         if (p == (char *)NOTOK) {
  668.             p = "\n***Error on net connection***\n";
  669.             mm_wtxt( p , strlen(p) );
  670.             if (!errflg++)
  671.                 ll_log(logptr, LLOGTMP,
  672.                     "netread error from host %s", them);
  673.             break;
  674.         }
  675.  
  676.         /* are we done? */
  677.         if (buf[0] == '.')
  678.             if (buf[1] == '\n')
  679.                 break;          /* yep */
  680.             else
  681.                 bufptr = &buf[1];       /* skip leading . */
  682.         else
  683.             bufptr = &buf[0];
  684.         /* If write error occurs, stop writing but keep reading. */
  685.         if (!werrflg) {
  686.             if (setjmp(timerest)) {
  687.                 netreply( "451 Mail system problem\r\n" );
  688.                 return;
  689.             }
  690.             s_alarm (DTIMEOUT);
  691.             msglen += (len = p-bufptr);
  692.             if( rp_isbad(mm_wtxt( bufptr, len ))) {
  693.                 werrflg++;
  694.                 ll_log( logptr, LLOGTMP, "error from submit");
  695.             }
  696.             s_alarm (0);
  697.         }
  698.     }
  699.     dont_mung = 0;  /* set getline to normal operation */
  700. #ifdef DEBUG
  701.     ll_log( logptr, LLOGBTR, "Finished receiving text." );
  702. #endif
  703.  
  704.     if (werrflg) {
  705.         netreply("451-Mail trouble (write error to mailsystem)\r\n");
  706.         netreply("451 Please try again later.\r\n");
  707.         byebye( 1 );
  708.     }
  709.     if (errflg) {
  710.         time (&tyme);
  711.         byebye(1);
  712.     }
  713.  
  714.     if (setjmp(timerest)) {
  715.         netreply( "451 Mail system problem\r\n" );
  716.         return;
  717.     }
  718.     s_alarm (DTIMEOUT);
  719.     if( rp_isbad(mm_wtend()) || rp_isbad( mm_rrply( &thereply, &len)))
  720.         netreply("451 Unknown mail trouble, try later\r\n");
  721.     else if( rp_isgood(thereply.rp_val)) {
  722.         sprintf (buf, "250 %s\r\n", thereply.rp_line);
  723.         netreply (buf);
  724.         phs_msg(chanptr, numrecipients, (long) msglen);
  725.     }
  726.     else if( rp_gbval(thereply.rp_val) == RP_BNO) {
  727.         sprintf (buf, "554 %s\r\n", thereply.rp_line);
  728.         netreply (buf);
  729.     }
  730.     else {
  731.         sprintf (buf, "451 %s\r\n", thereply.rp_line);
  732.         netreply (buf);
  733.     }
  734.     s_alarm (0);
  735.     sender = (char *) 0;
  736.     numrecipients = 0;
  737. }
  738.  
  739. /*
  740.  *  Process the RSET command
  741.  */
  742. rset()
  743. {
  744.     mm_end( NOTOK );
  745.     sender = (char *)0;
  746.     mmdfstart();
  747.     confirm();
  748. }
  749.  
  750. mmdfstart()
  751. {
  752.     if( rp_isbad( mm_init() ) || rp_isbad( mm_sbinit() )) {
  753.         ll_log( logptr, LLOGFAT, "can't reinitialize mail system" );
  754.         netreply("421 Server can't initialize mail system (mmdf)\r\n");
  755.         byebye( 2 );
  756.     }
  757.     numrecipients = 0;
  758. }
  759.  
  760. /*
  761.  *  handle the QUIT command
  762.  */
  763. quit()
  764. {
  765.     time_t  timenow;
  766.  
  767.     time (&timenow);
  768.     sprintf (buf, "221 %s says goodbye to %s at %.19s.\r\n",
  769.         us, them, ctime(&timenow));
  770.     netreply(buf);
  771.     byebye( 0 );
  772. }
  773.  
  774. byebye( retval )
  775. int retval;
  776. {
  777.     if (retval == OK) {
  778.         mm_sbend();
  779.         phs_note(chanptr, PHS_REEND);
  780.     }
  781.     mm_end( retval == 0 ? OK : NOTOK );
  782.     exit( retval );
  783. }
  784.  
  785. /*
  786.  *  Reply that the current command has been logged and noted
  787.  */
  788. confirm()
  789. {
  790.     netreply("250 OK\r\n");
  791. }
  792.  
  793. /*
  794.  *  Process the HELP command by giving a list of valid commands
  795.  */
  796. help()
  797. {
  798.     register i;
  799.     register struct comarr *p;
  800.     char    replybuf[256];
  801.  
  802.     netreply("214-The following commands are accepted:\r\n214-" );
  803.     for(p = commands, i = 1; p->cmdname; p++, i++) {
  804.         sprintf (replybuf, "%s%s", p->cmdname, ((i%10)?" ":"\r\n214-") );
  805.         netreply (replybuf);
  806.     }
  807.     sprintf (replybuf, "\r\n214 Send complaints/bugs to:  %s\r\n", supportaddr);
  808.     netreply (replybuf);
  809. }
  810.  
  811. /*
  812.  *  Send appropriate ascii responses over the network connection.
  813.  */
  814.  
  815. netreply(string)
  816. char *string;
  817. {
  818.     if (setjmp(timerest)) {
  819.         byebye( 1 );
  820.     }
  821.     s_alarm (NTIMEOUT);
  822.     if(write(1,string, strlen(string)) < 0){
  823.         s_alarm( 0 );
  824.         ll_log( logptr, LLOGFST,
  825.             "(netreply) error (%d) in writing [%s] ...",
  826.             errno, string);
  827.         byebye( 1 );
  828.     }
  829.     s_alarm( 0 );
  830. #ifdef DEBUG
  831.     ll_log( logptr, LLOGFTR, "%s", string);
  832. #endif /* DEBUG */
  833. }
  834.  
  835. /*
  836.  *      expn
  837.  *
  838.  *      handle the EXPN command  ("EXPN user@host")
  839.  */
  840. expn()
  841. {
  842.  
  843.     if (arg == 0 || *arg == 0) {
  844.         netreply("501 No argument supplied\r\n");
  845.         return;
  846.     }
  847.  
  848.     expn_count = 0;
  849.     expn_str( addrfix(arg) );  /* feed fixed address into the expander */
  850.     expn_dump();               /* dump the saved (last) address */
  851.     return;
  852.  
  853. expn_str( arg )
  854. register char *arg;
  855. {
  856.     char    buf[LINESIZE];
  857.     char    alstr[LINESIZE];
  858.     char    comment[LINESIZE];
  859.     register char    *p, *q;
  860.     FILE    *fp;
  861.     int    ret, gotalias, flags;
  862.     Chan    *thechan;
  863.     struct passwd     *pw;
  864.  
  865.     gotalias=1;
  866.     if ((ret = aliasfetch(TRUE, arg, buf, &flags)) != OK) {
  867.         if (ret == MAYBE) {
  868.             expn_save(MAYBE,"%s (Nameserver Timeout)",arg);
  869.             return;
  870.         }
  871.         if ((pw = getpwmid (arg)) != (struct passwd *) NULL) {
  872.             expn_save(OK,"%s <%s@%s>", pw->pw_gecos,
  873.                 pw->pw_name,us);
  874.             return;
  875.         }
  876.         strcpy(buf, arg);
  877.         gotalias--;
  878.     }
  879.  
  880.     strcpy(alstr, arg);
  881.  
  882.     /* just say OK if it is a private alias */
  883.     if (gotalias && (flags & AL_PUBLIC) != AL_PUBLIC) {
  884.         expn_save(OK,"<%s@%s>",alstr,us);
  885.         return;
  886.     }
  887.  
  888.     /* check for a simple comma-separated list of addresses */
  889.     if ((p = index(buf, ',')) != 0 && strindex (":include:", buf) < 0 &&
  890.         index (buf, '<') == 0 && index (buf, '|') == 0 && gotalias)
  891.     {
  892.         /* q is start of substring; it lags behind p */
  893.         for (q=buf; p != 0; q=p, p=index(p, ',')) {
  894.             *p++ = '\0';  /* null the comma */
  895.             expn_str(q);
  896.         }
  897.         return;
  898.     }
  899.  
  900.     /* check for a simple RHS with no @'s and no /'s (e.g. foo:bar) */
  901.     if (index (buf, '/') == 0)
  902.     {
  903.         /* check for a simple RHS (e.g. foo:bar) */
  904.         if ((p = index (buf, '@')) == 0)
  905.         {
  906.             if( buf[0] == '~' ) {
  907.                 if ((pw=getpwmid (buf[1])) != (struct passwd *) NULL) {
  908.                     expn_save(OK,"%s <%s@%s>", pw->pw_gecos,
  909.                         pw->pw_name,us);
  910.                     return;
  911.                 }
  912.                 expn_save(NOTOK,"%s (Unknown address)",arg);
  913.                 return;
  914.             }
  915.             if (gotalias)
  916.                 return(expn_str(buf));  /* recurse */
  917.             else {
  918.                 expn_save(NOTOK,"%s (Unknown address)",arg);
  919.                 return;
  920.             }
  921.  
  922.         }
  923.  
  924.         /* check for aliases of the form: user@domain */
  925.         *p++ = '\0';
  926.         thechan=ch_h2chan(p,1);
  927.         switch ((int) thechan) {
  928.             case OK:
  929.                 return(expn_str(buf));  /* user@US -- recurse */
  930.             case NOTOK:
  931.             case MAYBE:
  932.                 break;
  933.             default:
  934.                 /* check if list/bboard type channel */
  935.                 if (isstr(thechan->ch_host) &&
  936.                     (thechan = ch_h2chan(thechan->ch_host,1))
  937.                                 == (Chan *) OK)
  938.                     return(expn_str(buf));  /* recurse */
  939.         }
  940.         if (thechan == (Chan *) MAYBE) {
  941.             expn_save(MAYBE,"%s@%s (Nameserver Timeout)",buf,p);
  942.             return;
  943.         }
  944.         expn_save(OK,"<%s@%s>",buf,p);
  945.         return;
  946.     }
  947.  
  948.     if (!gotalias) {
  949.         expn_save(NOTOK,"%s (Unknown Address)", arg);
  950.         return;
  951.     }
  952.  
  953.     /* Assume if multiple entries, that only the first one is used. */
  954.     if ((q = index (buf, ',')) != 0)
  955.         *q = '\0';
  956.  
  957.     /* check for aliases of the form: [user]|program */
  958.     if ((q = index (buf, '|')) != 0) {
  959.         *q++ = '\0';
  960.         expn_save (OK,"%s@%s (Mail piped into process: %s)", 
  961.               alstr, us, q);
  962.         return;
  963.     }
  964.  
  965.     if ((q = index (buf, '/')) == 0) {
  966.             expn_save(NOTOK,"%s (Bad format for alias is %s)",alstr,buf);
  967.         return;
  968.     }
  969.  
  970.     /* check for < and :include: */
  971.     if (index (buf, '<') != 0 || strindex (":include:", buf) >= 0) {
  972.         if ((p = index (buf, '@')) != 0) {
  973.             *p++ = '\0';
  974.             if (ch_h2chan (p, 1)  != (Chan *) OK) {  /* not local */
  975.                 expn_save (OK,"<%s@%s>",buf,p);
  976.                 return;
  977.             }
  978.  
  979.         }
  980.         if ((fp = fopen (q, "r")) == NULL) {
  981.             expn_save (NOTOK,"%s@%s (Unable to open file %s)",
  982.                   alstr,us,q);
  983.             return;
  984.         }
  985.         while (fgets (buf, LINESIZE, fp) != NULL) {
  986.             *(buf+strlen(buf)-1) = '\0';
  987.             expn_str(buf);
  988.         }
  989.         fclose (fp);
  990.         return;
  991.     }
  992.  
  993.     /* assume alias of the form:  [user]/file */
  994.     *q++ = '\0';
  995.     expn_save (OK,"%s@%s (Mail filed into %s)",
  996.             alstr, us, q);
  997.     return;
  998.  
  999.  
  1000. }
  1001.  
  1002. expn_save(code,pattern,a1,a2,a3,a4,a5,a6,a7)
  1003. int code;
  1004. char *pattern,
  1005.      *a1,*a2,*a3,*a4,*a5,*a6,*a7;
  1006. {
  1007.     if (expn_count > 0) {
  1008.         sprintf(buf,"250-%s\r\n",saveaddr);
  1009.         netreply(buf);
  1010.     }
  1011.  
  1012.     sprintf(saveaddr,pattern,a1,a2,a3,a4,a5,a6,a7);
  1013.     if (expn_count == 0)
  1014.         savecode=code;
  1015.     else
  1016.         if (code != savecode)
  1017.             savecode=OK;
  1018.     expn_count++;
  1019. }
  1020.  
  1021. expn_dump()
  1022. {
  1023.  
  1024.     if (expn_count == 1 && savecode == NOTOK)
  1025.         sprintf(buf,"550 %s\r\n", saveaddr);
  1026.     else
  1027.         sprintf(buf,"250 %s\r\n", saveaddr);
  1028.  
  1029.     netreply(buf);
  1030. }
  1031.  
  1032. /*
  1033.  *      vrfy
  1034.  *
  1035.  *      handle the VRFY command  ("VRFY user@host")
  1036.  */
  1037. vrfy()
  1038. {
  1039.     register int fd;
  1040.     register char *cp;
  1041.     char linebuf[LINESIZE];
  1042.     char replybuf[LINESIZE];
  1043.     struct rp_bufstruct thereply;
  1044.     int len;
  1045.  
  1046.     if (arg == 0 || *arg == 0) {
  1047.         netreply("501 No argument supplied\r\n");
  1048.         return;
  1049.     }
  1050.  
  1051.     if (!vrfy_child) {
  1052.  
  1053.         /* Start up the verifying child */
  1054.  
  1055.         if (pipe (vrfy_p2c) < OK || pipe (vrfy_c2p) < OK) {
  1056.             netreply("550 Mail system problem1.\r\n");
  1057.             return;
  1058.         }
  1059.  
  1060.         switch (vrfy_child = fork()) {
  1061.  
  1062.             case NOTOK:
  1063.             ll_log(logptr, LLOGFST, "Fork of vrfy child failed.");
  1064.             netreply("550 Mail system problem2.\r\n");
  1065.             return;
  1066.  
  1067.             default:
  1068.             /* parent */
  1069.             vrfy_out = fdopen(vrfy_p2c[1],"w");
  1070.             vrfy_in  = fdopen(vrfy_c2p[0],"r");
  1071.             break;
  1072.  
  1073.             case 0:
  1074.             /* child */
  1075. #ifndef NODUP2
  1076.             dup2(vrfy_p2c[0],0);
  1077.             dup2(vrfy_c2p[1],1);
  1078. #else
  1079.             (void) close(0);
  1080.             (void) close(1);
  1081. #ifndef NOFCNTL
  1082.             fcntl(vrfy_p2c[0], F_DUPFD, 0);
  1083.             fcntl(vrfy_c2p[1], F_DUPFD, 1);
  1084. #else
  1085.             /* something else */
  1086. #endif
  1087. #endif
  1088.             for (fd = 2; fd < numfds; fd++)
  1089.                 (void)close(fd);
  1090.  
  1091.             if (rp_isbad(mm_init()) || rp_isbad(mm_sbinit()) ||
  1092.                 rp_isbad(mm_winit ((char *)0, "vmr", (char *)0)) ||
  1093.                 rp_isbad(mm_rrply( &thereply, &len )) ||
  1094.                 rp_isbad(thereply.rp_val)) {
  1095.                 ll_log(logptr, LLOGFST, 
  1096.                     "Vrfy child couldn't start up submit.");
  1097.                 exit(1);
  1098.             }
  1099.  
  1100.             while (fgets (linebuf, LINESIZE, stdin)) {
  1101.                 if (cp = rindex(linebuf, '\n'))
  1102.                     *cp-- = 0;
  1103.                 verify(linebuf);
  1104.             }
  1105.  
  1106.             mm_end(NOTOK);
  1107.             exit(0);
  1108.  
  1109.         }
  1110.  
  1111.     }
  1112.  
  1113.     if (setjmp(timerest)) {
  1114.         ll_log(logptr,LLOGFST,"Timeout waiting for vrfy child.");
  1115.         netreply("550 Mail system problem.\r\n");
  1116.         vrfy_kill();
  1117.         return;
  1118.     }
  1119.  
  1120.     s_alarm(DTIMEOUT);
  1121.     fprintf(vrfy_out,"%s\n",arg);
  1122.     fflush(vrfy_out);
  1123.     if (ferror(vrfy_out)) {
  1124.         ll_log(logptr,LLOGFST,"Unable to write to vrfy child.");
  1125.         vrfy_kill();
  1126.         netreply("550 Mail system problem.\r\n");
  1127.         return;
  1128.     }
  1129.     if (fgets (linebuf, LINESIZE, vrfy_in) == NULL) {
  1130.         ll_log(logptr,LLOGFST,"Unable to read from vrfy child.");
  1131.         vrfy_kill();
  1132.         netreply("550 Mail system problem.\r\n");
  1133.         return;
  1134.     }
  1135.     s_alarm(0);
  1136.  
  1137.     if (cp = rindex(linebuf, '\n'))
  1138.         *cp-- = 0;
  1139.     sprintf(replybuf,"%s\r\n",linebuf);
  1140.     netreply(replybuf);
  1141.  
  1142.     return;
  1143. }
  1144.  
  1145. verify(p)
  1146. char *p;
  1147. {
  1148.     register char *l, *r;
  1149.     struct rp_bufstruct thereply;
  1150.     char replybuf[256];
  1151.     int len;
  1152.  
  1153.     if (setjmp(timerest)) {
  1154.         ll_log(logptr,LLOGFST,
  1155.                      "Timeout in vrfy child waiting for submit.");
  1156.             exit(1);
  1157.     }
  1158.     s_alarm (DTIMEOUT);
  1159.     if( rp_isbad( mm_wadr( (char *)0, p ))) {
  1160.         if( rp_isbad( mm_rrply( &thereply, &len ))) {
  1161.             ll_log(logptr,LLOGFST,
  1162.                   "Error in vrfy child reading reply from submit.");
  1163.                exit(1);
  1164.         } else {
  1165.             sprintf (replybuf, "550 %s\r\n", thereply.rp_line);
  1166.             vrfyreply (replybuf);
  1167.         }
  1168.     } else {
  1169.         if( rp_isbad( mm_rrply( &thereply, &len ))) {
  1170.             ll_log(logptr,LLOGFST,
  1171.                  "Error in vrfy child reading reply from submit.");
  1172.             exit(1);
  1173.         } else if (rp_isbad(thereply.rp_val)) {
  1174.             sprintf (replybuf, "550 %s\r\n", thereply.rp_line);
  1175.             vrfyreply (replybuf);
  1176.         } else {
  1177.             if ((l=index(thereply.rp_line, '"')) &&
  1178.                 (r=rindex(thereply.rp_line, '"')) &&
  1179.                 (l != r) ) {
  1180.                 *l = '<';
  1181.                     *r = '>';
  1182.             }
  1183.             if (l && r && !index(l,'@')) {
  1184.                 *l++ = '\0';
  1185.                 *r++ = '\0';
  1186.                 sprintf (replybuf, "250 %s<%s@%s>%s\r\n",
  1187.                     thereply.rp_line, l, us, r);
  1188.             } else
  1189.                 sprintf (replybuf, "250 %s\r\n", thereply.rp_line);
  1190.             vrfyreply (replybuf);
  1191.         }
  1192.     }
  1193.     s_alarm (0);
  1194.  
  1195.     return;
  1196. }
  1197.  
  1198. vrfyreply(string)
  1199. char *string;
  1200. {
  1201.     if (setjmp(timerest)) {
  1202.         exit(1);
  1203.     }
  1204.     s_alarm (NTIMEOUT);
  1205.     if(write(1,string, strlen(string)) < 0) {
  1206.         s_alarm(0);
  1207.         ll_log( logptr, LLOGFST,
  1208.             "(vrfyreply) error (%d) in writing [%s] ...",
  1209.             errno, string);
  1210.         exit(1);
  1211.     }
  1212.     s_alarm(0);
  1213. #ifdef DEBUG
  1214.     ll_log( logptr, LLOGFTR, "child writing: %s", string);
  1215. #endif /* DEBUG */
  1216. }
  1217.  
  1218. vrfy_kill()
  1219. {
  1220.     ll_log( logptr, LLOGFST, "Killing vrfy child.");
  1221.  
  1222.     if (vrfy_child) {
  1223.         kill (vrfy_child, 9);
  1224.         vrfy_child = 0;
  1225.     }
  1226.  
  1227.     if (vrfy_in != (FILE *) EOF && vrfy_in != NULL) {
  1228.         (void) close (fileno (vrfy_in));
  1229.         fclose(vrfy_in);
  1230.     }
  1231.  
  1232.     if (vrfy_out != (FILE *) EOF && vrfy_out != NULL) {
  1233.         (void) close (fileno (vrfy_out));
  1234.         fclose(vrfy_out);
  1235.     }
  1236.  
  1237.     vrfy_in = vrfy_out = NULL;
  1238.  
  1239.     return;
  1240. }
  1241.