home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / internet / tcpip / src205 / TCPIP_Src / SMTP / c / smtpcli next >
Encoding:
Text File  |  1995-06-06  |  27.1 KB  |  1,194 lines

  1. /*
  2.  *      Client routines for Simple Mail Transfer Protocol ala RFC821
  3.  *      A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *      Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *      Permission granted for non-commercial copying and use, provided
  6.  *      this notice is retained.
  7.  *      Modified 14 June 1987 by P. Karn for symbolic target addresses,
  8.  *      also rebuilt locking mechanism
  9.  *      Limit on max simultaneous sessions, reuse of connections - 12/87 NN2Z
  10.  *      Added return of mail to sender as well as batching of commands 1/88 nn2z
  11.  */
  12. #include <ctype.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <stdarg.h>
  17. #include <time.h>
  18. #include "global.h"
  19. #include "netuser.h"
  20. #include "mbuf.h"
  21. #include "domain.h"
  22. #include "timer.h"
  23. #include "tcp.h"
  24. #include "smtp.h"
  25. #include "trace.h"
  26. #include "cmdparse.h"
  27. #include "misc.h"
  28. #include "arc.h"
  29. #include "resuser.h"
  30. #include "Terminal.h"
  31. #include "vterm.h"
  32.  
  33. static int dosep(int, char **);
  34. static int dosmtpmaxcli(int, char **);
  35. static int setsmtpmode(int, char **);
  36. static int dogateway(int, char **);
  37. static int dosmtptrace(int, char **);
  38. static int dotimer(int, char **);
  39. static void abort_trans(struct smtp_cb *);
  40. static void quit(struct smtp_cb *);
  41. static void smtp_rec(struct tcb *, int);
  42. static void smtp_cts(struct tcb *, int);
  43. static int sendwindow(struct smtp_cb *, struct mbuf *, int);
  44. static void smtp_state(struct tcb *, char, char);
  45. static void sendit(struct smtp_cb *, char *, ...);
  46. static void del_session(struct smtp_cb *);
  47. static void retmail(struct smtp_cb *);
  48. static struct smtp_cb *lookup(int32);
  49. static struct smtp_cb *newcb(void);
  50. static void execjobs(void);
  51. static int nextjob(struct smtp_cb *);
  52. static void logerr(struct smtp_cb *);
  53. static int setsmtplate(int argc, char **argv);
  54.  
  55. int inourdomain(char *host);
  56.  
  57. static int smtp_delay_close = 0;     /* off */
  58.  
  59. extern int lport;                     /* local port placeholder */
  60. static struct timer smtpcli_t;
  61. int32 gateway;
  62.  
  63. #ifdef SMTPTRACE
  64. static int   smtptrace = 0;                  /* used for trace level */
  65. #endif
  66.  
  67. int smtpmaxcli  = MAXSESSIONS;      /* the max client connections allowed */
  68. int smtpsessions = 0;               /* number of client connections
  69.                                            currently open */
  70. int smtpmode = 0;
  71.  
  72. int smtp_sep = sep_UNIX;
  73.  
  74. static Terminal *smtp_win = NULL;
  75.  
  76. static struct smtp_cb *cli_session[MAXSESSIONS]; /* queue of client sessions  */
  77.  
  78. static char quitcmd[] = "QUIT\r\n";
  79. static char eom[] = "\r\n.\r\n";
  80. static char *seps[] = {"From", "Ctrl-A", "#! rnews", "#! rmail"};
  81.  
  82. struct cmds smtpcmds[] = {
  83.   "gateway",      dogateway,      0,      NULLCHAR,       NULLCHAR,
  84.   "mode",         setsmtpmode,    0,      NULLCHAR,       NULLCHAR,
  85.   "delay",        setsmtplate,    0,      NULLCHAR,       NULLCHAR,
  86.   "kick",         smtptick,       0,      NULLCHAR,       NULLCHAR,
  87.   "maxclients",   dosmtpmaxcli,   0,      NULLCHAR,       NULLCHAR,
  88.   "separator",    dosep,          0,      NULLCHAR,       NULLCHAR,
  89.   "timer",        dotimer,        0,      NULLCHAR,       NULLCHAR,
  90. #ifdef SMTPTRACE
  91.   "trace",        dosmtptrace,    0,      NULLCHAR,       NULLCHAR,
  92. #endif
  93.   NULLCHAR,       NULLFP,         0,
  94.   "subcommands: delay gateway mode kick maxclients separator timer trace",
  95.   NULLCHAR,
  96. };
  97.  
  98. int dosmtp(int argc, char **argv)
  99. {
  100.   return subcmd(smtpcmds, argc, argv);
  101. }
  102.  
  103. static int dosmtpmaxcli(int argc, char **argv)
  104. {
  105.   int x;
  106.   if (argc < 2)
  107.     cwprintf(NULL, "%d\r\n",smtpmaxcli);
  108.   else {
  109.     x = atoi(argv[1]);
  110.     if (x > MAXSESSIONS)
  111.       cwprintf(NULL, "max clients must be <= %d\r\n",MAXSESSIONS);
  112.     else
  113.         smtpmaxcli = x;
  114.   }
  115.   return 0;
  116. }
  117.  
  118. static int setsmtpmode(int argc, char **argv)
  119. {
  120.   if (argc < 2)
  121.   {
  122.     cwprintf(NULL, "smtp mode: %s\r\n",
  123.     (smtpmode & QUEUE) ? "queue" : "route");
  124.   }
  125.   else
  126.   {
  127.     switch(*argv[1])
  128.     {
  129.     case 'q':
  130.       smtpmode |= QUEUE;
  131.       break;
  132.     case 'r':
  133.       smtpmode &= ~QUEUE;
  134.       break;
  135.     default:
  136.       cwprintf(NULL, "Usage: smtp mode [queue | route]\r\n");
  137.       break;
  138.     }
  139.   }
  140.   return 0;
  141. }
  142.  
  143. static int setsmtplate(int argc, char **argv)
  144. {
  145.   if (argc < 2)
  146.     cwprintf(NULL, "smtp delay shutdown: %s\r\n", (smtp_delay_close) ? "yes" : "no");
  147.   else
  148.   {
  149.     switch(*argv[1])
  150.     {
  151.     case 'y':
  152.       smtp_delay_close = 1;
  153.       break;
  154.     case 'n':
  155.       smtp_delay_close = 0;
  156.       break;
  157.     default:
  158.       cwprintf(NULL, "Usage: smtp delay [yes | no]\r\n");
  159.       break;
  160.     }
  161.   }
  162.   return 0;
  163. }
  164.  
  165. static int dogateway(int argc, char **argv)
  166. {
  167.   int32 n;
  168.   extern char badhost[];
  169.  
  170.   if(argc < 2)
  171.   {
  172.     cwprintf(NULL, "%s\r\n", inet_ntoa(gateway));
  173.   }
  174.   else if ((n = resolve(argv[1])) == 0)
  175.   {
  176.     cwprintf(NULL, badhost,argv[1]);
  177.     return 1;
  178.   }
  179.   else
  180.     gateway = n;
  181.   return 0;
  182. }
  183.  
  184. #ifdef SMTPTRACE
  185. static int dosmtptrace(int argc, char **argv)
  186. {
  187.   if (argc < 2)
  188.     cwprintf(NULL, "%d\r\n",smtptrace);
  189.   else
  190.     smtptrace = atoi(argv[1]);
  191.   return 0;
  192. }
  193. #endif
  194.  
  195. /* Set outbound spool poll interval */
  196. static int dotimer(int argc, char **argv)
  197. {
  198.   if(argc < 2)
  199.   {
  200.     cwprintf(NULL, "%lu/%lu\r\n",
  201.     (smtpcli_t.start - smtpcli_t.count)/(1000/MSPTICK),
  202.     smtpcli_t.start/(1000/MSPTICK));
  203.     return 0;
  204.   }
  205.   smtpcli_t.func = (void (*)())smtptick;/* what to call on timeout */
  206.   smtpcli_t.arg = NULLCHAR;               /* dummy value */
  207.   smtpcli_t.start = (int32)atoi(argv[1])*(1000/MSPTICK); /* set timer duration */
  208.   start_timer(&smtpcli_t);                /* and fire it up */
  209.   return 0;
  210. }
  211.  
  212. /* this is the routine that gets called every so often to do outgoing mail
  213.    processing */
  214. int smtptick(void)
  215. {
  216.   register struct smtp_cb *cb;
  217.   struct smtp_job *jp;
  218.   struct list *ap;
  219.   char    tmpstring[LINELEN], wfilename[13];
  220.   char    from[LINELEN], to[LINELEN];
  221.   int32 destaddr;
  222.   FILE *wfile;
  223.  
  224.   smtp_win = Window_Open(NULL, "SMTP", term_NO_INPUT | term_DONT_DESTROY);
  225.   vterm_visible(smtp_win->vt, 40, 8);
  226.   vterm_setflags(smtp_win->vt, VTSW_WRAP, VTSW_WRAP);
  227.  
  228. #ifdef SMTPTRACE
  229.   if (smtptrace > 5)
  230.   {
  231.     cwprintf(smtp_win, "smtp daemon entered\r\n");
  232.   }
  233. #endif
  234.  
  235.   for (filedir(mailqueue, 0, wfilename); wfilename[0] != '\0'; filedir(mailqueue, 1, wfilename))
  236.   {
  237.     /* lock this file from the smtp daemon */
  238.     if (mlock(mailqdir, wfilename))
  239.       continue;
  240.  
  241.     sprintf(tmpstring,"%s.work.%s",mailqdir,wfilename);
  242.     if ((wfile = fopen(tmpstring,"r")) == NULLFILE)
  243.     {
  244.       /* probably too many open files */
  245.       rmlock(mailqdir, wfilename);
  246.       /* continue to next message. The failure
  247.          may be temporary */
  248.       continue;
  249.     }
  250.  
  251.     fgets(tmpstring, LINELEN, wfile);  /* read target host */
  252.     rip(tmpstring);
  253.  
  254.     if ((destaddr = mailroute(tmpstring)) == 0)
  255.     {
  256.       fclose(wfile);
  257.       cwprintf(smtp_win, "** smtp: Unknown address %s\r\n",tmpstring);
  258.       rmlock(mailqdir, wfilename);
  259.       continue;
  260.     }
  261.  
  262.     if ((cb = lookup(destaddr)) == NULLCB)
  263.     {
  264.       /* there are enough processes running already */
  265.       if (smtpsessions >= smtpmaxcli)
  266.       {
  267. #ifdef SMTPTRACE
  268.         if (smtptrace)
  269.         {
  270.           cwprintf(smtp_win, "smtp daemon: too many processes\r\n");
  271.         }
  272. #endif
  273.         fclose(wfile);
  274.         rmlock(mailqdir, wfilename);
  275.         break;
  276.       }
  277.       if ((cb = newcb()) == NULLCB)
  278.       {
  279.         fclose(wfile);
  280.         rmlock(mailqdir, wfilename);
  281.         break;
  282.       }
  283.       cb->ipdest = destaddr;
  284.     }
  285.     else
  286.     {
  287.       /* This system is already is sending mail lets not
  288.          interfere with its send queue.*/
  289.       if (cb->state != CLI_INIT_STATE)
  290.       {
  291.         fclose(wfile);
  292.         rmlock(mailqdir, wfilename);
  293.         continue;
  294.       }
  295.     }
  296.  
  297.     fgets(from,LINELEN,wfile);       /* read from */
  298.     rip(from);
  299.     if ((jp = setupjob(cb, wfilename, from)) == NULLJOB)
  300.     {
  301.       fclose(wfile);
  302.       rmlock(mailqdir, wfilename);
  303.       del_session(cb);
  304.       break;
  305.     }
  306.     while (fgets(to, LINELEN, wfile) != NULLCHAR)
  307.     {
  308.       rip(to);
  309.       if (addlist(&jp->to, to, DOMAIN) == NULLLIST)
  310.       {
  311.         fclose(wfile);
  312.         del_session(cb);
  313.       }
  314.     }
  315.     fclose(wfile);
  316. #ifdef SMTPTRACE
  317.     if (smtptrace > 1)
  318.     {
  319.       cwprintf(smtp_win, "queue job %s From: %s To:", wfilename, from);
  320.       for (ap = jp->to; ap != NULLLIST; ap = ap->next)
  321.         cwprintf(smtp_win, " %s",ap->val);
  322.       cwprintf(smtp_win, "\r\n");
  323.     }
  324. #endif
  325.   }
  326.   if (smtp_win && smtpsessions == 0)
  327.   {
  328.     smtp_win->Flags.flags.dont_destroy = FALSE;
  329.     Window_Close(smtp_win);
  330.     smtp_win = NULL;
  331.   }
  332.  
  333.   /* start sending that mail */
  334.   execjobs();
  335.  
  336.   /* Restart timer */
  337.   start_timer(&smtpcli_t);
  338.   return(0);
  339. }
  340.  
  341. /* this is the master state machine that handles a single SMTP transaction */
  342. void smtp_transaction(register struct smtp_cb *cb)
  343. {
  344.   register char reply;
  345.   register struct list *tp;
  346.   int cnt;
  347.   struct mbuf *bp,*bpl;
  348.   char tbuf[LINELEN];
  349.   int rcode;
  350.  
  351. #ifdef SMTPTRACE
  352.   if (smtptrace > 5)
  353.     cwprintf(smtp_win, "smtp_transaction() enter state=%u\r\n",cb->state);
  354.   if (smtptrace)
  355.   {
  356.     cwprintf(smtp_win, "%s\r\n", cb->buf);
  357.   }
  358. #endif
  359.   /* Another line follows; ignore this one */
  360.   if(cb->buf[0] == '0' || cb->buf[3] == '-')
  361.     return;
  362.  
  363.   reply = cb->buf[0];
  364.   rcode = atoi(cb->buf);
  365.  
  366.   /* if service shuting down */
  367.   if (rcode == 421 || (rcode == 221 && smtp_delay_close))
  368.   {
  369.     quit(cb);
  370.     return;
  371.   }
  372.  
  373.   switch(cb->state)
  374.   {
  375.   case CLI_OPEN_STATE:
  376.     if (reply != '2')
  377.     {
  378.       quit(cb);
  379.     }
  380.     else
  381.     {
  382.       cb->state = CLI_HELO_STATE;
  383.       sendit(cb, "HELO %s\r\nMAIL FROM:<%s>\r\n",
  384.       hostname, cb->jobq->from);
  385.     }
  386.     break;
  387.  
  388.   case CLI_HELO_STATE:
  389.     if (reply != '2')
  390.       quit(cb);
  391.     else
  392.       cb->state = CLI_MAIL_STATE;
  393.     break;
  394.  
  395.   case CLI_MAIL_STATE:
  396.     if (reply != '2')
  397.       quit(cb);
  398.     else
  399.     {
  400.       cb->state = CLI_RCPT_STATE;
  401.       cb->rcpts = 0;
  402.       bpl = NULLBUF;
  403.       for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next)
  404.       {
  405.         sprintf(tbuf, "RCPT TO:<%s>\r\n", tp->val);
  406.         bp = qdata(tbuf, strlen(tbuf));
  407.         if (bp == NULLBUF)
  408.         {
  409.           free_p(bpl);
  410.           quit(cb);
  411.           return;
  412.         }
  413.         append(&bpl,bp);
  414.         cb->rcpts++;
  415. #ifdef SMTPTRACE
  416.         if (smtptrace)
  417.         {
  418.           cwprintf(smtp_win, ">>> %s\r\n",tbuf);
  419.         }
  420. #endif
  421.       }
  422.       send_tcp(cb->tcb,bpl);
  423.     }
  424.     break;
  425.   case CLI_RCPT_STATE:
  426.     if (reply == '5')
  427.     {
  428.       logerr(cb);
  429.     }
  430.     else if (reply == '2')
  431.     {
  432.       cb->goodrcpt =1;
  433.     }
  434.     else
  435.     {
  436.       /* some kind of temporary failure */
  437.       abort_trans(cb);
  438.       break;
  439.     }
  440.     /* if more rcpts stay in this state */
  441.     if (--cb->rcpts != 0)
  442.       break;
  443.  
  444.     /* check for no good rcpt on the list */
  445.     if (cb->goodrcpt == 0)
  446.     {
  447.       if (cb->errlog != NULLLIST)
  448.         retmail(cb);
  449.       remove(cb->wname);       /* unlink workfile */
  450.       remove(cb->tname);       /* unlink text */
  451.       newsbase_update(0);
  452.       abort_trans(cb);
  453.       break;
  454.     }
  455.     /* if this file open fails abort */
  456.     if ((cb->tfile = fopen(cb->tname, "r")) == NULLFILE)
  457.       abort_trans(cb);
  458.     else
  459.     {
  460.       /* optimize for slow packet links by sending
  461.          DATA cmd and the 1st window of text */
  462.       if (cb->tcb->window <= cb->tcb->sndcnt)
  463.         cnt = 0;
  464.       else
  465.         cnt = cb->tcb->window - cb->tcb->sndcnt;
  466.       bp = qdata("DATA\r\n", 6);
  467.       cb->cts = 1;
  468.       cb->state = CLI_SEND_STATE;
  469.       if (sendwindow(cb, bp, cnt) == EOF)
  470.         cb->cts = 0;
  471.     }
  472.     break;
  473.  
  474.   case CLI_SEND_STATE:
  475.     if (reply == '3')
  476.     {
  477.       cb->state = CLI_UNLK_STATE;
  478.     }
  479.     else
  480.     {
  481.       /* change cts to transmit upcall queueing more data */
  482.       cb->cts = 0;
  483.       quit(cb);
  484.     }
  485.     break;
  486.  
  487.   case CLI_UNLK_STATE:
  488.     /* if a good transfer or permanent failure remove job */
  489.     if (reply == '2' || reply == '5')
  490.     {
  491.       if (reply == '5')
  492.         logerr(cb);
  493.       /* close and unlink the textfile */
  494.       if(cb->tfile != NULLFILE)
  495.       {
  496.         fclose(cb->tfile);
  497.         cb->tfile = NULLFILE;
  498.       }
  499.       if (cb->errlog != NULLLIST)
  500.         retmail(cb);
  501.       remove(cb->tname);
  502.       remove(cb->wname);       /* unlink workfile */
  503.       log_event(cb->tcb,"SMTP sent job %s To: %s From: %s",
  504.       cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
  505.       newsbase_update(0);
  506.     }
  507.     if (nextjob(cb))
  508.     {
  509.       cb->state = CLI_MAIL_STATE;
  510.       sendit(cb,"MAIL FROM:<%s>\r\n",cb->jobq->from);
  511.     }
  512.     else
  513.     {
  514.       /* the quit sent already in smtp_cts */
  515.       cb->state = CLI_QUIT_STATE;
  516.       /* ... unless delay close set */
  517.       if (smtp_delay_close)
  518.         sendit(cb, quitcmd);
  519.     }
  520.     break;
  521.  
  522.   case CLI_IDLE_STATE:    /* used after a RSET and more mail to send */
  523.     if (reply != '2')
  524.       quit(cb);
  525.     else
  526.     {
  527.       cb->state = CLI_MAIL_STATE;
  528.       sendit(cb,"MAIL FROM:<%s>\r\n",cb->jobq->from);
  529.     }
  530.     break;
  531.  
  532.   case CLI_QUIT_STATE:
  533.     break;
  534.   }
  535. }
  536.  
  537. /* abort the currrent job.
  538.    If more work exists set up the next job if
  539.    not then shut down. */
  540. static void abort_trans(register struct smtp_cb *cb)
  541. {
  542.   if(cb->tfile != NULLFILE)
  543.   {
  544.     fclose(cb->tfile);
  545.     cb->tfile = NULLFILE;
  546.   }
  547.   if (nextjob(cb))
  548.   {
  549.     sendit(cb,"RSET\r\n");
  550.     cb->state = CLI_IDLE_STATE;
  551.   }
  552.   else
  553.     quit(cb);
  554. }
  555.  
  556. /* close down link after a failure */
  557. static void quit(struct smtp_cb *cb)
  558. {
  559.   cb->state = CLI_QUIT_STATE;
  560.   sendit(cb,quitcmd);             /* issue a quit command */
  561.   close_tcp(cb->tcb);           /* close up connection */
  562. }
  563.  
  564. /* smtp receiver upcall routine.  fires up the state machine to parse input */
  565. static void smtp_rec(struct tcb *tcb, int cnt)
  566. {
  567.   BOOL incomplete = FALSE;
  568.   register struct smtp_cb *cb;
  569.   char c;
  570.   struct mbuf *bp;
  571.  
  572. #ifdef SMTPTRACE
  573.   if (smtptrace > 7)
  574.   {
  575.     cwprintf(smtp_win, "smtp_rec called\r\n");
  576.   }
  577. #endif
  578.   cb = (struct smtp_cb *) tcb->user;       /* point to our struct */
  579.   recv_tcp(tcb, &bp, cnt);  /* suck up chars from low level routine */
  580.  
  581.   /* Assemble input line in buffer, return if incomplete */
  582.   while(pullone(&bp, &c) == 1)
  583.   {
  584.     switch(c)
  585.     {
  586.     case '\r':      /* strip cr's */
  587.       continue;
  588.  
  589.     case '\n':      /* line is finished, go do it! */
  590.       cb->buf[cb->cnt] = '\0';
  591.       smtp_transaction(cb);
  592.       cb->cnt = 0;
  593.       incomplete = FALSE;
  594.       break;
  595.  
  596.     default:        /* other chars get added to buffer */
  597.       cb->buf[cb->cnt++] = c;
  598.       if (cb->cnt == 1 && incomplete && c == '.')
  599.       {
  600.         cb->buf[cb->cnt++] = c;
  601.         incomplete = FALSE;
  602.       }
  603.       if(cb->cnt > LINELEN - 2)
  604.       {
  605.         cb->buf[cb->cnt] = '\0';
  606.         smtp_transaction(cb);
  607.         cb->cnt = 0;
  608.         incomplete = TRUE;
  609.       }
  610.       break;
  611.     }
  612.   }
  613. }
  614.  
  615. /* smtp transmitter ready upcall routine.  twiddles cts flag */
  616. static void smtp_cts(struct tcb *tcb, int cnt)
  617. {
  618.   register struct smtp_cb *cb;
  619.  
  620. #ifdef SMTPTRACE
  621.   if (smtptrace > 7)
  622.   {
  623.     cwprintf(smtp_win, "smtp_cts called avail %d\r\n",cnt);
  624.   }
  625. #endif
  626.   cb = (struct smtp_cb *)tcb->user;       /* point to our struct */
  627.  
  628.   /* don't do anything until/unless we're supposed to be sending */
  629.   if(cb->cts == 0)
  630.     return;
  631.  
  632.   if (sendwindow(cb, NULLBUF, cnt) == EOF)
  633.     cb->cts = 0;
  634. }
  635.  
  636. /* fill the rest of the window with data and send out the eof commands.
  637.    It is done this way to minimize the number of packets sent. */
  638. static int sendwindow(register struct smtp_cb *cb, struct mbuf *ibp, int cnt)
  639. {
  640.   struct mbuf *bpl;
  641.   register struct mbuf *bp;
  642.   char *cp;
  643.   int c;
  644.  
  645.   bpl = ibp;
  646.   if ((bp = alloc_mbuf(cnt)) == NULLBUF)
  647.   {
  648.     /* Hard to know what to do here */
  649.     return EOF;
  650.   }
  651.   cp = bp->data;
  652.   while(cnt > 1 && (c = getc(cb->tfile)) != EOF)
  653.   {
  654.     if (c == '\n')
  655.     {
  656.       *cp++ = '\r';
  657.       bp->cnt++;
  658.       cnt--;
  659.     }
  660.     *cp++ = c;
  661.     bp->cnt++;
  662.     cnt--;
  663.  
  664.     if (c == '\n')
  665.     {
  666.       if (c = getc(cb->tfile), c == '.')
  667.       {
  668.         *cp++ = '.';
  669.         bp->cnt++;
  670.         cnt--;
  671.       }
  672.       ungetc(c, cb->tfile);
  673.     }
  674.   }
  675.   append(&bpl, bp);
  676.   if (cnt > 1)
  677.   {  /* EOF seen */
  678.     fclose(cb->tfile);
  679.     cb->tfile = NULLFILE;
  680.     /* send the end of data character. */
  681.     if (cnt < sizeof(eom) - 1)
  682.     {
  683.       bp = qdata(eom, 5);
  684.       append(&bpl,bp);
  685.       cnt = 0;        /* dont let anyone else in */
  686.     }
  687.     else
  688.     {
  689.       memcpy(&bp->data[bp->cnt], eom, sizeof(eom) - 1);
  690.       bp->cnt += sizeof(eom) - 1;
  691.       cnt -= sizeof(eom) - 1;
  692.     }
  693.     /* send the quit in this packet if last job */
  694.     if (cb->jobq->next == NULLJOB && !smtp_delay_close)
  695.     {
  696.       if (cnt < sizeof(quitcmd) - 1)
  697.       {
  698.         bp = qdata(quitcmd,sizeof(quitcmd) - 1);
  699.         append(&bpl,bp);
  700.       }
  701.       else
  702.       {
  703.         memcpy(&bp->data[bp->cnt],
  704.         quitcmd,sizeof(quitcmd) - 1);
  705.         bp->cnt += sizeof(quitcmd) - 1;
  706.       }
  707.     }
  708.     send_tcp(cb->tcb, bpl);
  709.     if (cb->jobq->next == NULLJOB)
  710.     {
  711.       close_tcp(cb->tcb);     /* close up connection */
  712.     }
  713.     return EOF;
  714.   }
  715.   else
  716.   {
  717.     send_tcp(cb->tcb, bpl);
  718.     return 0;
  719.   }
  720. }
  721.  
  722. /* smtp state change upcall routine. */
  723. static void smtp_state(register struct tcb *tcb, char old, char new)
  724. {
  725.   register struct smtp_cb *cb;
  726.   extern char *tcpstates[];
  727.  
  728. #ifdef SMTPTRACE
  729.   if (smtptrace > 7)
  730.   {
  731.     cwprintf(smtp_win, "smtp_state called: %s\r\n",tcpstates[new]);
  732.   }
  733. #endif
  734.   cb = (struct smtp_cb *)tcb->user;
  735.   switch(new)
  736.   {
  737.   case ESTABLISHED:
  738.     cb->state = CLI_OPEN_STATE;     /* shouldn't be needed */
  739.     break;
  740.   case CLOSE_WAIT:
  741.     close_tcp(tcb);                 /* shut things down */
  742.     break;
  743.   case CLOSED:
  744.     /* if this close was not done by us ie. a RST */
  745.     if(cb->tfile != NULLFILE)
  746.       fclose(cb->tfile);
  747.     if (old == CLI_OPEN_STATE && cb->ipdest != gateway && gateway != NULL)
  748.     {
  749.       struct socket lsocket, fsocket;
  750.  
  751.       del_tcp(tcb);
  752.       fsocket.address = gateway;
  753.       fsocket.port    = SMTP_PORT;
  754.       lsocket.address = ip_addr;
  755.       lsocket.port    = lport++;
  756. #ifdef SMTPTRACE
  757.       if (smtptrace)
  758.       {
  759.         cwprintf(smtp_win, "SMTP: Trying Connection to %s\r\n", inet_ntoa(fsocket.address));
  760.       }
  761. #endif
  762.       cb->state = CLI_OPEN_STATE;
  763.       cb->tcb   = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, tcp_window,
  764.       (void(*)())smtp_rec, (void(*)())smtp_cts, (void(*)())smtp_state, 0, (char *)cb);
  765.       cb->tcb->user = (char *)cb;
  766.     }
  767.     else
  768.     {
  769.       del_session(cb);
  770.       del_tcp(tcb);
  771.     }
  772.     break;
  773.   }
  774. }
  775.  
  776. /* Send message back to server */
  777. static void sendit(struct smtp_cb *cb, char *fmt, ...)
  778. {
  779.   va_list argptr;
  780.   struct mbuf *bp;
  781.   char tmpstring[256];
  782.  
  783.   va_start(argptr,fmt);
  784.   vsprintf(tmpstring,fmt,argptr);
  785.   va_end(argptr);
  786.  
  787. #ifdef SMTPTRACE
  788.   if (smtptrace) {
  789.     cwprintf(smtp_win, ">>> %s\r\n", tmpstring);
  790.   }
  791. #endif
  792.   bp = qdata(tmpstring, strlen(tmpstring));
  793.   send_tcp(cb->tcb, bp);
  794. }
  795.  
  796. /* create mail lockfile */
  797. int mlock(char *dir, char *id)
  798. {
  799.   char lockname[LINELEN];
  800.   FILE *fp;
  801.   /* Try to create the lock file in an atomic operation */
  802.   sprintf(lockname,"%s.lock.%s",dir,id);
  803.   if((fp = fopen(lockname, "w")) == NULL)
  804.     return -1;
  805.   fclose(fp);
  806.   return 0;
  807. }
  808.  
  809. /* remove mail lockfile */
  810. int rmlock(char *dir, char *id)
  811. {
  812.   char lockname[LINELEN];
  813.   sprintf(lockname,"%s.lock.%s",dir,id);
  814.   return(remove(lockname));
  815. }
  816.  
  817. /* free the message struct and data */
  818. static void del_session(register struct smtp_cb *cb)
  819. {
  820.   register struct smtp_job *jp,*tp;
  821.   register int i;
  822.  
  823.   if (cb == NULLCB)
  824.     return;
  825.   for (i = 0; i < MAXSESSIONS; i++)
  826.     if (cli_session[i] == cb)
  827.     {
  828.       cli_session[i] = NULLCB;
  829.       break;
  830.     }
  831.  
  832.   if (cb->wname != NULLCHAR)
  833.     free(cb->wname);
  834.   if (cb->tname != NULLCHAR)
  835.     free(cb->tname);
  836.   for (jp = cb->jobq; jp != NULLJOB;jp = tp)
  837.   {
  838.     tp = jp->next;
  839.     del_job(jp);
  840.   }
  841.   del_list(cb->errlog);
  842.   free((char *)cb);
  843.   smtpsessions--; /* number of connections active */
  844.   if (smtp_win && smtpsessions == 0)
  845.   {
  846.     smtp_win->Flags.flags.dont_destroy = FALSE;
  847.     Window_Close(smtp_win);
  848.     smtp_win = NULL;
  849.   }
  850. }
  851.  
  852. void del_job(register struct smtp_job *jp)
  853. {
  854.   if ( *jp->jobname != '\0')
  855.     rmlock(mailqdir,jp->jobname);
  856.   if(jp->from != NULLCHAR)
  857.     free(jp->from);
  858.   del_list(jp->to);
  859.   free((char *)jp);
  860. }
  861.  
  862. /* delete a list of list structs */
  863. void del_list(struct list *lp)
  864. {
  865.   register struct list *tp, *tp1;
  866.   for (tp = lp; tp != NULLLIST; tp = tp1) {
  867.     tp1 = tp->next;
  868.     if(tp->val != NULLCHAR)
  869.       free(tp->val);
  870.     free((char *)tp);
  871.   }
  872. }
  873.  
  874. /* return message to sender */
  875. static void retmail(struct smtp_cb *cb)
  876. {
  877.   register struct list *lp;
  878.   register FILE *tfile;
  879.   register int c;
  880.   FILE *infile;
  881.   char *host,*to;
  882.   time_t t;
  883. #ifdef SMTPTRACE
  884.   if (smtptrace > 5) {
  885.     cwprintf(smtp_win, "smtp job %s returned to sender\r\n",cb->wname);
  886.   }
  887. #endif
  888.   /* A null From<> so no looping replys to MAIL-DAEMONS */
  889.   to = cb->jobq->from;
  890.   if (*to == '\0')
  891.     return;
  892.   if ((host = strchr(to,'@')) == NULLCHAR)
  893.     host = hostname;
  894.   else
  895.     host++;
  896.   if ((infile = fopen(cb->tname,"r")) == NULLFILE)
  897.     return;
  898.   if ((tfile = tmpfile()) == NULLFILE) {
  899.     fclose(infile);
  900.     return;
  901.   }
  902.   time(&t);
  903.   fprintf(tfile,"Date: %s",ptime(&t));
  904.   fprintf(tfile,"Message-Id: <%ld@%s>\n",get_msgid(),hostname);
  905.   fprintf(tfile,"From: MAILER-DAEMON@%s\n",hostname);
  906.   fprintf(tfile,"To: %s\n",to);
  907.   fprintf(tfile,"Subject: Failed mail\n\n");
  908.   fprintf(tfile,"  ===== transcript follows =====\n\n");
  909.  
  910.   for (lp = cb->errlog; lp != NULLLIST; lp = lp->next)
  911.     fprintf(tfile,"%s\n",lp->val);
  912.  
  913.   fprintf(tfile,"\n  ===== Unsent message follows ====\n");
  914.  
  915.   while((c = getc(infile)) != EOF)
  916.     if (putc(c,tfile) == EOF)
  917.       break;
  918.   fclose(infile);
  919.   fseek(tfile,0L,0);
  920.   if ((smtpmode & QUEUE) != 0)
  921.     router_queue(cb->tcb,tfile,"",(struct list *)to);
  922.   else
  923.       queuejob(cb->tcb,tfile,host,to,"");
  924.   fclose(tfile);
  925. }
  926.  
  927. /* look to see if a smtp control block exists for this ipdest */
  928. static struct smtp_cb *lookup(int32 destaddr)
  929. {
  930.   register int i;
  931.  
  932.   for(i=0; i<MAXSESSIONS; i++) {
  933.     if (cli_session[i] == NULLCB)
  934.       continue;
  935.     if(cli_session[i]->ipdest == destaddr)
  936.       return cli_session[i];
  937.   }
  938.   return NULLCB;
  939. }
  940.  
  941. /* create a new  smtp control block */
  942. static struct smtp_cb *newcb(void)
  943. {
  944.   register int i;
  945.   register struct smtp_cb *cb;
  946.  
  947.   for (i = 0; i < MAXSESSIONS; i++)
  948.   {
  949.     if (cli_session[i] == NULLCB)
  950.     {
  951.       cb = (struct smtp_cb *)calloc(1,sizeof(struct smtp_cb));
  952.       if (cb == NULLCB)
  953.         return(NULLCB);
  954.       cb->wname = malloc((unsigned)strlen(mailqdir) + JOBNAME + 6);
  955.       if (cb->wname == NULLCHAR)
  956.       {
  957.         free((char *)cb);
  958.         return(NULLCB);
  959.       }
  960.       cb->tname = malloc((unsigned) strlen(mailqdir) + JOBNAME + 6);
  961.       if (cb->tname == NULLCHAR)
  962.       {
  963.         free(cb->wname);
  964.         free((char *)cb);
  965.         return(NULLCB);
  966.       }
  967.       cb->state = CLI_INIT_STATE;
  968.       cli_session[i] = cb;
  969.       smtpsessions++; /* number of connections active */
  970.       return(cb);
  971.     }
  972.   }
  973.   return NULLCB;
  974. }
  975.  
  976. static void execjobs(void)
  977. {
  978.   struct socket lsocket, fsocket;
  979.   register struct smtp_cb *cb;
  980.   register int i;
  981.  
  982.   for (i = 0; i < MAXSESSIONS; i++)
  983.   {
  984.     cb = cli_session[i];
  985.     if (cb == NULLCB)
  986.       continue;
  987.     if(cb->state != CLI_INIT_STATE)
  988.       continue;
  989.  
  990.     sprintf(cb->tname, "%s.text.%s", mailqdir, cb->jobq->jobname);
  991.     sprintf(cb->wname, "%s.work.%s", mailqdir, cb->jobq->jobname);
  992.  
  993.     /* setup the socket */
  994.     fsocket.address = cb->ipdest;
  995.     fsocket.port    = SMTP_PORT;
  996.     lsocket.address = ip_addr;      /* our ip address */
  997.     lsocket.port    = lport++;      /* next unused port */
  998. #ifdef SMTPTRACE
  999.     if (smtptrace)
  1000.     {
  1001.       cwprintf(smtp_win, "SMTP: Trying Connection to %s\r\n",inet_ntoa(fsocket.address));
  1002.     }
  1003. #endif
  1004.  
  1005.     /* open smtp connection */
  1006.     cb->state = CLI_OPEN_STATE;     /* init state placeholder */
  1007.     cb->tcb   = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, tcp_window,
  1008.     (void(*)())smtp_rec, (void(*)())smtp_cts, (void(*)())smtp_state, 0, (char *)cb);
  1009.     cb->tcb->user = (char *)cb;     /* Upward pointer */
  1010.   }
  1011. }
  1012.  
  1013. /* add this job to control block queue */
  1014. struct smtp_job *setupjob(struct smtp_cb *cb, char *id, char *from)
  1015. {
  1016.   register struct smtp_job *p1,*p2;
  1017.  
  1018.   p1 = (struct smtp_job *)calloc(1,sizeof(struct smtp_job));
  1019.   if (p1 == NULLJOB)
  1020.     return NULLJOB;
  1021.   p1->from = malloc((unsigned)strlen(from) + 1);
  1022.   if (p1->from == NULLCHAR)
  1023.   {
  1024.     free((char *)p1);
  1025.     return NULLJOB;
  1026.   }
  1027.   strcpy(p1->from,from);
  1028.   strcpy(p1->jobname,id);
  1029.   /* now add to end of jobq */
  1030.   if ((p2 = cb->jobq) == NULLJOB)
  1031.     cb->jobq = p1;
  1032.   else
  1033.   {
  1034.     while(p2->next != NULLJOB)
  1035.       p2 = p2->next;
  1036.     p2->next = p1;
  1037.   }
  1038.   return p1;
  1039. }
  1040.  
  1041. /* called to advance to the next job */
  1042. static int nextjob(register struct smtp_cb *cb)
  1043. {
  1044.   register struct smtp_job *jp;
  1045.  
  1046.  
  1047.   jp = cb->jobq->next;
  1048.   del_job(cb->jobq);
  1049.   if (jp == NULLJOB)
  1050.   {
  1051.     cb->jobq = NULLJOB;
  1052.     return 0;
  1053.   }
  1054.   /* remove the error log of previous message */
  1055.   del_list(cb->errlog);
  1056.   cb->errlog = NULLLIST;
  1057.   cb->goodrcpt = 0;
  1058.   cb->jobq = jp;
  1059.   sprintf(cb->tname,"%s.text.%s",mailqdir,jp->jobname);
  1060.   sprintf(cb->wname,"%s.work.%s",mailqdir,jp->jobname);
  1061. #ifdef SMTPTRACE
  1062.   if (smtptrace > 5)
  1063.   {
  1064.     cwprintf(smtp_win, "sending job %s\r\n",jp->jobname);
  1065.   }
  1066. #endif
  1067.   return 1;
  1068.  
  1069. }
  1070.  
  1071.  
  1072. /* mail routing funtion. For now just used the hosts file */
  1073. int32 mailroute(char *dest)
  1074. {
  1075.   int32 destaddr;
  1076.  
  1077.   /* KA9Q doesn't work out that an address is unreachable.
  1078.      Kludgy fix: use the gateway if one exists and assume that its
  1079.      router is more clever. */
  1080.  
  1081. /*  if ((destaddr = mxresolve(dest)) == 0)
  1082.   {
  1083.     if (gateway != 0)
  1084.       destaddr = gateway;
  1085.   } */
  1086.  
  1087. /* Yet another hack - 
  1088.    if the dest is in our domain, direct it to us
  1089.  */
  1090.   if (inourdomain(dest))
  1091.     destaddr = ip_addr;
  1092.  
  1093.   destaddr = resolve(dest);
  1094.  
  1095. /*
  1096.   cwprintf(NULL, "Mailroute to: %s %.8x\n", dest, destaddr);
  1097.   cwprintf(NULL, "Mailroute ip addr: %.8x\n", ip_addr);
  1098.   cwprintf(NULL, "Mailroute gateway: %.8x\n", gateway);
  1099. */
  1100.  
  1101.   if (destaddr == ip_addr)
  1102.     return destaddr;
  1103.  
  1104.   else if (gateway != 0)
  1105.     destaddr = gateway;
  1106.  
  1107.   else
  1108.   {
  1109.     if ((destaddr = mxresolve(dest)) == 0)
  1110.       if (gateway != 0)
  1111.         destaddr = gateway;
  1112.   }
  1113.   return destaddr;
  1114. }
  1115.  
  1116. /* save error reply for in error list */
  1117. static void logerr(struct smtp_cb *cb)
  1118. {
  1119.   register struct list *lp,*tp;
  1120.   if ((tp = (struct list *)calloc(1,sizeof(struct list))) == NULLLIST)
  1121.     return;
  1122.   if ((tp->val = malloc((unsigned)strlen(cb->buf)+1)) == NULLCHAR) {
  1123.     free((char *)tp);
  1124.     return;
  1125.   }
  1126.   /* find end of list */
  1127.   if ((lp = cb->errlog) == NULLLIST)
  1128.     cb->errlog = tp;
  1129.   else {
  1130.     while(lp->next != NULLLIST)
  1131.       lp = lp->next;
  1132.     lp->next = tp;
  1133.   }
  1134.   strcpy(tp->val,cb->buf);
  1135. }
  1136.  
  1137. static int dosep(int argc, char **argv)
  1138. {
  1139.   if (argc < 2)
  1140.   {
  1141.     cwprintf(NULL, "smtp separator: %s\r\n", seps[smtp_sep]);
  1142.   }
  1143.   else
  1144.   {
  1145.     switch(tolower(*argv[1]))
  1146.     {
  1147.     case '^':
  1148.     case 'a':
  1149.       smtp_sep = sep_CTLA;
  1150.       break;
  1151.     case 'r':
  1152.       if (tolower(*(argv[1]+1))=='m')
  1153.         smtp_sep = sep_RMAIL;
  1154.       else
  1155.         smtp_sep = sep_RNEWS;
  1156.       break;
  1157.     default:
  1158.       smtp_sep = sep_UNIX;
  1159.       break;
  1160.     }
  1161.   }
  1162.   return 0;
  1163. }
  1164.  
  1165. /* Return true if host is a possible sub-domain
  1166.    of our domain - for eg
  1167.    user.ourhost.domain is acceptable, but
  1168.    hourhost.domain is not.
  1169.  */
  1170. int inourdomain(char *host)
  1171. {
  1172.   int n1 = strlen(host);
  1173.   int n2 = strlen(hostname); /* actually our domain */
  1174.   
  1175.   if (n1<n2)
  1176.     return (!stricmp(hostname, host+(n1-n2)) && host[(n1-n2)-1]=='.');
  1177.  
  1178.   return 0;
  1179. }
  1180.  
  1181. void newsbase_update(int type)
  1182. {
  1183.   wimp_msgstr msg;
  1184.  
  1185.   msg.hdr.size      = 20+12+8;
  1186.   msg.hdr.your_ref  = 0;
  1187.   msg.hdr.action    = 0xfeed12;   /* Newsbase Queue Update */
  1188.   msg.data.words[0] = 11;
  1189.   msg.data.words[1] = type;
  1190.   msg.data.words[2] = -1;
  1191.   sprintf(msg.data.chars+12, "ka9q");
  1192.   wimp_sendmessage(wimp_ESEND, &msg, 0);
  1193. }
  1194.