home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / sendmail / sendmail-5.65 / src / usersmtp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-05  |  11.5 KB  |  508 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted provided
  7.  * that: (1) source distributions retain this entire copyright notice and
  8.  * comment, and (2) distributions including binaries display the following
  9.  * acknowledgement:  ``This product includes software developed by the
  10.  * University of California, Berkeley and its contributors'' in the
  11.  * documentation or other materials provided with the distribution and in
  12.  * all advertising materials mentioning features or use of this software.
  13.  * Neither the name of the University nor the names of its contributors may
  14.  * be used to endorse or promote products derived from this software without
  15.  * specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  17.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  18.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  */
  20.  
  21. # include "sendmail.h"
  22.  
  23. #ifndef lint
  24. #ifdef SMTP
  25. static char sccsid[] = "@(#)usersmtp.c    5.15 (Berkeley) 6/1/90 (with SMTP)";
  26. #else
  27. static char sccsid[] = "@(#)usersmtp.c    5.15 (Berkeley) 6/1/90 (without SMTP)";
  28. #endif
  29. #endif /* not lint */
  30.  
  31. # include <sysexits.h>
  32. # include <errno.h>
  33.  
  34. # ifdef SMTP
  35.  
  36. /*
  37. **  USERSMTP -- run SMTP protocol from the user end.
  38. **
  39. **    This protocol is described in RFC821.
  40. */
  41.  
  42. #define REPLYTYPE(r)    ((r) / 100)        /* first digit of reply code */
  43. #define REPLYCLASS(r)    (((r) / 10) % 10)    /* second digit of reply code */
  44. #define SMTPCLOSING    421            /* "Service Shutting Down" */
  45.  
  46. char    SmtpMsgBuffer[MAXLINE];        /* buffer for commands */
  47. char    SmtpReplyBuffer[MAXLINE];    /* buffer for replies */
  48. char    SmtpError[MAXLINE] = "";    /* save failure error messages */
  49. FILE    *SmtpOut;            /* output file */
  50. FILE    *SmtpIn;            /* input file */
  51. int    SmtpPid;            /* pid of mailer */
  52.  
  53. /* following represents the state of the SMTP connection */
  54. int    SmtpState;            /* connection state, see below */
  55.  
  56. #define SMTP_CLOSED    0        /* connection is closed */
  57. #define SMTP_OPEN    1        /* connection is open for business */
  58. #define SMTP_SSD    2        /* service shutting down */
  59. /*
  60. **  SMTPINIT -- initialize SMTP.
  61. **
  62. **    Opens the connection and sends the initial protocol.
  63. **
  64. **    Parameters:
  65. **        m -- mailer to create connection to.
  66. **        pvp -- pointer to parameter vector to pass to
  67. **            the mailer.
  68. **
  69. **    Returns:
  70. **        appropriate exit status -- EX_OK on success.
  71. **        If not EX_OK, it should close the connection.
  72. **
  73. **    Side Effects:
  74. **        creates connection and sends initial protocol.
  75. */
  76.  
  77. jmp_buf    CtxGreeting;
  78.  
  79. smtpinit(m, pvp)
  80.     struct mailer *m;
  81.     char **pvp;
  82. {
  83.     register int r;
  84.     EVENT *gte;
  85.     char buf[MAXNAME];
  86.     extern greettimeout();
  87.  
  88.     /*
  89.     **  Open the connection to the mailer.
  90.     */
  91.  
  92.     if (SmtpState == SMTP_OPEN)
  93.         syserr("smtpinit: already open");
  94.  
  95.     SmtpIn = SmtpOut = NULL;
  96.     SmtpState = SMTP_CLOSED;
  97.     SmtpError[0] = '\0';
  98.     SmtpPhase = "user open";
  99.     setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
  100.     SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
  101.     if (SmtpPid < 0)
  102.     {
  103.         if (tTd(18, 1))
  104.             printf("smtpinit: cannot open %s: stat %d errno %d\n",
  105.                pvp[0], ExitStat, errno);
  106.         if (CurEnv->e_xfp != NULL)
  107.         {
  108.             register char *p;
  109.             extern char *errstring();
  110.             extern char *statstring();
  111.  
  112.             if (errno == 0)
  113.             {
  114.                 p = statstring(ExitStat);
  115.                 fprintf(CurEnv->e_xfp,
  116.                     "%.3s %s.%s... %s\n",
  117.                     p, pvp[1], m->m_name, p);
  118.             }
  119.             else
  120.             {
  121.                 r = errno;
  122.                 fprintf(CurEnv->e_xfp,
  123.                     "421 %s.%s... Deferred: %s\n",
  124.                     pvp[1], m->m_name, errstring(errno));
  125.                 errno = r;
  126.             }
  127.         }
  128.         return (ExitStat);
  129.     }
  130.     SmtpState = SMTP_OPEN;
  131.  
  132.     /*
  133.     **  Get the greeting message.
  134.     **    This should appear spontaneously.  Give it five minutes to
  135.     **    happen.
  136.     */
  137.  
  138.     if (setjmp(CtxGreeting) != 0)
  139.         goto tempfail;
  140.     gte = setevent((time_t) 300, greettimeout, 0);
  141.     SmtpPhase = "greeting wait";
  142.     setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
  143.     r = reply(m);
  144.     clrevent(gte);
  145.     if (r < 0 || REPLYTYPE(r) != 2)
  146.         goto tempfail;
  147.  
  148.     /*
  149.     **  Send the HELO command.
  150.     **    My mother taught me to always introduce myself.
  151.     */
  152.  
  153.     smtpmessage("HELO %s", m, MyHostName);
  154.     SmtpPhase = "HELO wait";
  155.     setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
  156.     r = reply(m);
  157.     if (r < 0)
  158.         goto tempfail;
  159.     else if (REPLYTYPE(r) == 5)
  160.         goto unavailable;
  161.     else if (REPLYTYPE(r) != 2)
  162.         goto tempfail;
  163.  
  164.     /*
  165.     **  If this is expected to be another sendmail, send some internal
  166.     **  commands.
  167.     */
  168.  
  169.     if (bitnset(M_INTERNAL, m->m_flags))
  170.     {
  171.         /* tell it to be verbose */
  172.         smtpmessage("VERB", m);
  173.         r = reply(m);
  174.         if (r < 0)
  175.             goto tempfail;
  176.  
  177.         /* tell it we will be sending one transaction only */
  178.         smtpmessage("ONEX", m);
  179.         r = reply(m);
  180.         if (r < 0)
  181.             goto tempfail;
  182.     }
  183.  
  184.     /*
  185.     **  Send the MAIL command.
  186.     **    Designates the sender.
  187.     */
  188.  
  189.     expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
  190.     if (CurEnv->e_from.q_mailer == LocalMailer ||
  191.         !bitnset(M_FROMPATH, m->m_flags))
  192.     {
  193.         smtpmessage("MAIL From:<%s>", m, buf);
  194.     }
  195.     else
  196.     {
  197.         smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
  198.             buf[0] == '@' ? ',' : ':', buf);
  199.     }
  200.     SmtpPhase = "MAIL wait";
  201.     setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
  202.     r = reply(m);
  203.     if (r < 0 || REPLYTYPE(r) == 4)
  204.         goto tempfail;
  205.     else if (r == 250)
  206.         return (EX_OK);
  207.     else if (r == 552)
  208.         goto unavailable;
  209.  
  210.     /* protocol error -- close up */
  211.     smtpquit(m);
  212.     return (EX_PROTOCOL);
  213.  
  214.     /* signal a temporary failure */
  215.   tempfail:
  216.     smtpquit(m);
  217.     return (EX_TEMPFAIL);
  218.  
  219.     /* signal service unavailable */
  220.   unavailable:
  221.     smtpquit(m);
  222.     return (EX_UNAVAILABLE);
  223. }
  224.  
  225.  
  226. static
  227. greettimeout()
  228. {
  229.     /* timeout reading the greeting message */
  230.     longjmp(CtxGreeting, 1);
  231. }
  232. /*
  233. **  SMTPRCPT -- designate recipient.
  234. **
  235. **    Parameters:
  236. **        to -- address of recipient.
  237. **        m -- the mailer we are sending to.
  238. **
  239. **    Returns:
  240. **        exit status corresponding to recipient status.
  241. **
  242. **    Side Effects:
  243. **        Sends the mail via SMTP.
  244. */
  245.  
  246. smtprcpt(to, m)
  247.     ADDRESS *to;
  248.     register MAILER *m;
  249. {
  250.     register int r;
  251.     extern char *remotename();
  252.  
  253.     smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
  254.  
  255.     SmtpPhase = "RCPT wait";
  256.     setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
  257.     r = reply(m);
  258.     if (r < 0 || REPLYTYPE(r) == 4)
  259.         return (EX_TEMPFAIL);
  260.     else if (REPLYTYPE(r) == 2)
  261.         return (EX_OK);
  262.     else if (r == 550 || r == 551 || r == 553)
  263.         return (EX_NOUSER);
  264.     else if (r == 552 || r == 554)
  265.         return (EX_UNAVAILABLE);
  266.     return (EX_PROTOCOL);
  267. }
  268. /*
  269. **  SMTPDATA -- send the data and clean up the transaction.
  270. **
  271. **    Parameters:
  272. **        m -- mailer being sent to.
  273. **        e -- the envelope for this message.
  274. **
  275. **    Returns:
  276. **        exit status corresponding to DATA command.
  277. **
  278. **    Side Effects:
  279. **        none.
  280. */
  281.  
  282. smtpdata(m, e)
  283.     struct mailer *m;
  284.     register ENVELOPE *e;
  285. {
  286.     register int r;
  287.  
  288.     /*
  289.     **  Send the data.
  290.     **    First send the command and check that it is ok.
  291.     **    Then send the data.
  292.     **    Follow it up with a dot to terminate.
  293.     **    Finally get the results of the transaction.
  294.     */
  295.  
  296.     /* send the command and check ok to proceed */
  297.     smtpmessage("DATA", m);
  298.     SmtpPhase = "DATA wait";
  299.     setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
  300.     r = reply(m);
  301.     if (r < 0 || REPLYTYPE(r) == 4)
  302.         return (EX_TEMPFAIL);
  303.     else if (r == 554)
  304.         return (EX_UNAVAILABLE);
  305.     else if (r != 354)
  306.         return (EX_PROTOCOL);
  307.  
  308.     /* now output the actual message */
  309.     (*e->e_puthdr)(SmtpOut, m, CurEnv);
  310.     putline("\n", SmtpOut, m);
  311.     (*e->e_putbody)(SmtpOut, m, CurEnv);
  312.  
  313.     /* terminate the message */
  314.     fprintf(SmtpOut, ".%s", m->m_eol);
  315.     if (Verbose && !HoldErrs)
  316.         nmessage(Arpa_Info, ">>> .");
  317.  
  318.     /* check for the results of the transaction */
  319.     SmtpPhase = "result wait";
  320.     setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
  321.     r = reply(m);
  322.     if (r < 0 || REPLYTYPE(r) == 4)
  323.         return (EX_TEMPFAIL);
  324.     else if (r == 250)
  325.         return (EX_OK);
  326.     else if (r == 552 || r == 554)
  327.         return (EX_UNAVAILABLE);
  328.     return (EX_PROTOCOL);
  329. }
  330. /*
  331. **  SMTPQUIT -- close the SMTP connection.
  332. **
  333. **    Parameters:
  334. **        m -- a pointer to the mailer.
  335. **
  336. **    Returns:
  337. **        none.
  338. **
  339. **    Side Effects:
  340. **        sends the final protocol and closes the connection.
  341. */
  342.  
  343. smtpquit(m)
  344.     register MAILER *m;
  345. {
  346.     int i;
  347.  
  348.     /* if the connection is already closed, don't bother */
  349.     if (SmtpIn == NULL)
  350.         return;
  351.  
  352.     /* send the quit message if not a forced quit */
  353.     if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
  354.     {
  355.         smtpmessage("QUIT", m);
  356.         (void) reply(m);
  357.         if (SmtpState == SMTP_CLOSED)
  358.             return;
  359.     }
  360.  
  361.     /* now actually close the connection */
  362.     (void) fclose(SmtpIn);
  363.     (void) fclose(SmtpOut);
  364.     SmtpIn = SmtpOut = NULL;
  365.     SmtpState = SMTP_CLOSED;
  366.  
  367.     /* and pick up the zombie */
  368.     i = endmailer(SmtpPid, m->m_argv[0]);
  369.     if (i != EX_OK)
  370.         syserr("smtpquit %s: stat %d", m->m_argv[0], i);
  371. }
  372. /*
  373. **  REPLY -- read arpanet reply
  374. **
  375. **    Parameters:
  376. **        m -- the mailer we are reading the reply from.
  377. **
  378. **    Returns:
  379. **        reply code it reads.
  380. **
  381. **    Side Effects:
  382. **        flushes the mail file.
  383. */
  384.  
  385. reply(m)
  386.     MAILER *m;
  387. {
  388.     (void) fflush(SmtpOut);
  389.  
  390.     if (tTd(18, 1))
  391.         printf("reply\n");
  392.  
  393.     /*
  394.     **  Read the input line, being careful not to hang.
  395.     */
  396.  
  397.     for (;;)
  398.     {
  399.         register int r;
  400.         register char *p;
  401.  
  402.         /* actually do the read */
  403.         if (CurEnv->e_xfp != NULL)
  404.             (void) fflush(CurEnv->e_xfp);    /* for debugging */
  405.  
  406.         /* if we are in the process of closing just give the code */
  407.         if (SmtpState == SMTP_CLOSED)
  408.             return (SMTPCLOSING);
  409.  
  410.         /* get the line from the other side */
  411.         p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
  412.         if (p == NULL)
  413.         {
  414.             extern char MsgBuf[];        /* err.c */
  415.             extern char Arpa_TSyserr[];    /* conf.c */
  416.  
  417.             /* if the remote end closed early, fake an error */
  418.             if (errno == 0)
  419. # ifdef ECONNRESET
  420.                 errno = ECONNRESET;
  421. # else ECONNRESET
  422.                 errno = EPIPE;
  423. # endif ECONNRESET
  424.  
  425.             message(Arpa_TSyserr, "reply: read error");
  426.             /* if debugging, pause so we can see state */
  427.             if (tTd(18, 100))
  428.                 pause();
  429. # ifdef LOG
  430.             syslog(LOG_INFO, "%s", &MsgBuf[4]);
  431. # endif LOG
  432.             SmtpState = SMTP_CLOSED;
  433.             smtpquit(m);
  434.             return (-1);
  435.         }
  436.         fixcrlf(SmtpReplyBuffer, TRUE);
  437.  
  438.         if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
  439.         {
  440.             /* serious error -- log the previous command */
  441.             if (SmtpMsgBuffer[0] != '\0')
  442.                 fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
  443.             SmtpMsgBuffer[0] = '\0';
  444.  
  445.             /* now log the message as from the other side */
  446.             fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
  447.         }
  448.  
  449.         /* display the input for verbose mode */
  450.         if (Verbose && !HoldErrs)
  451.             nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
  452.  
  453.         /* if continuation is required, we can go on */
  454.         if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
  455.             continue;
  456.  
  457.         /* decode the reply code */
  458.         r = atoi(SmtpReplyBuffer);
  459.  
  460.         /* extra semantics: 0xx codes are "informational" */
  461.         if (r < 100)
  462.             continue;
  463.  
  464.         /* reply code 421 is "Service Shutting Down" */
  465.         if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
  466.         {
  467.             /* send the quit protocol */
  468.             SmtpState = SMTP_SSD;
  469.             smtpquit(m);
  470.         }
  471.  
  472.         /* save temporary failure messages for posterity */
  473.         if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
  474.             (void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
  475.  
  476.         return (r);
  477.     }
  478. }
  479. /*
  480. **  SMTPMESSAGE -- send message to server
  481. **
  482. **    Parameters:
  483. **        f -- format
  484. **        m -- the mailer to control formatting.
  485. **        a, b, c -- parameters
  486. **
  487. **    Returns:
  488. **        none.
  489. **
  490. **    Side Effects:
  491. **        writes message to SmtpOut.
  492. */
  493.  
  494. /*VARARGS1*/
  495. smtpmessage(f, m, a, b, c)
  496.     char *f;
  497.     MAILER *m;
  498. {
  499.     (void) sprintf(SmtpMsgBuffer, f, a, b, c);
  500.     if (tTd(18, 1) || (Verbose && !HoldErrs))
  501.         nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
  502.     if (SmtpOut != NULL)
  503.         fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
  504.             m == 0 ? "\r\n" : m->m_eol);
  505. }
  506.  
  507. # endif SMTP
  508.