122716Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362532Sbostic * Copyright (c) 1988, 1993 462532Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642831Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822716Sdist 933731Sbostic # include "sendmail.h" 1022716Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*65870Seric static char sccsid[] = "@(#)usersmtp.c 8.18 (Berkeley) 01/24/94 (with SMTP)"; 1433731Sbostic #else 15*65870Seric static char sccsid[] = "@(#)usersmtp.c 8.18 (Berkeley) 01/24/94 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194684Seric # include <sysexits.h> 2021065Seric # include <errno.h> 214684Seric 2233731Sbostic # ifdef SMTP 234684Seric 244684Seric /* 259391Seric ** USERSMTP -- run SMTP protocol from the user end. 269391Seric ** 279391Seric ** This protocol is described in RFC821. 289391Seric */ 299391Seric 309391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 319391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 329391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 339391Seric 3414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 3510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 3621065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 3710054Seric int SmtpPid; /* pid of mailer */ 3864071Seric bool SmtpNeedIntro; /* need "while talking" in transcript */ 3958671Seric 4058671Seric #ifdef __STDC__ 4158671Seric extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 4258671Seric #endif 439391Seric /* 444865Seric ** SMTPINIT -- initialize SMTP. 454684Seric ** 464865Seric ** Opens the connection and sends the initial protocol. 474684Seric ** 484684Seric ** Parameters: 494865Seric ** m -- mailer to create connection to. 504865Seric ** pvp -- pointer to parameter vector to pass to 514865Seric ** the mailer. 524684Seric ** 534684Seric ** Returns: 5454967Seric ** none. 554684Seric ** 564684Seric ** Side Effects: 574865Seric ** creates connection and sends initial protocol. 584684Seric */ 594684Seric 6054967Seric smtpinit(m, mci, e) 614865Seric struct mailer *m; 6254967Seric register MCI *mci; 6353751Seric ENVELOPE *e; 644684Seric { 654865Seric register int r; 6658957Seric register char *p; 6760210Seric extern void esmtp_check(); 6859285Seric extern void helo_options(); 694684Seric 7063753Seric if (tTd(18, 1)) 7157379Seric { 7257379Seric printf("smtpinit "); 7364731Seric mci_dump(mci, FALSE); 7457379Seric } 7557379Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8021065Seric SmtpError[0] = '\0'; 8157379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 8264071Seric SmtpNeedIntro = TRUE; 8354967Seric switch (mci->mci_state) 846051Seric { 8554967Seric case MCIS_ACTIVE: 8654967Seric /* need to clear old information */ 8754967Seric smtprset(m, mci, e); 8857734Seric /* fall through */ 8915139Seric 9054967Seric case MCIS_OPEN: 9154967Seric return; 9254967Seric 9354967Seric case MCIS_ERROR: 9454967Seric case MCIS_SSD: 9554967Seric /* shouldn't happen */ 9654967Seric smtpquit(m, mci, e); 9757734Seric /* fall through */ 9854967Seric 9954967Seric case MCIS_CLOSED: 10058151Seric syserr("451 smtpinit: state CLOSED"); 10154967Seric return; 10254967Seric 10354967Seric case MCIS_OPENING: 10454967Seric break; 1056051Seric } 1064796Seric 10754967Seric mci->mci_state = MCIS_OPENING; 10854967Seric 1094865Seric /* 1104865Seric ** Get the greeting message. 11114913Seric ** This should appear spontaneously. Give it five minutes to 11214886Seric ** happen. 1134865Seric */ 1144797Seric 11561093Seric SmtpPhase = mci->mci_phase = "client greeting"; 11653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11760210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 11864750Seric if (r < 0 || REPLYTYPE(r) == 4) 11952104Seric goto tempfail1; 12064750Seric if (REPLYTYPE(r) != 2) 12164750Seric goto unavailable; 1224684Seric 1234865Seric /* 1244976Seric ** Send the HELO command. 1257963Seric ** My mother taught me to always introduce myself. 1264976Seric */ 1274976Seric 12859285Seric if (bitnset(M_ESMTP, m->m_flags)) 12959285Seric mci->mci_flags |= MCIF_ESMTP; 13059285Seric 13159285Seric tryhelo: 13259285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 13359285Seric { 13459285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13561093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13659285Seric } 13759285Seric else 13859285Seric { 13959285Seric smtpmessage("HELO %s", m, mci, MyHostName); 14061093Seric SmtpPhase = mci->mci_phase = "client HELO"; 14159285Seric } 14253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 14359285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1448005Seric if (r < 0) 14552104Seric goto tempfail1; 1468005Seric else if (REPLYTYPE(r) == 5) 14759285Seric { 14859285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 14959285Seric { 15059285Seric /* try old SMTP instead */ 15159285Seric mci->mci_flags &= ~MCIF_ESMTP; 15259285Seric goto tryhelo; 15359285Seric } 15414913Seric goto unavailable; 15559285Seric } 1567963Seric else if (REPLYTYPE(r) != 2) 15752104Seric goto tempfail1; 1584976Seric 1594976Seric /* 16058957Seric ** Check to see if we actually ended up talking to ourself. 16158957Seric ** This means we didn't know about an alias or MX, or we managed 16258957Seric ** to connect to an echo server. 16364651Seric ** 16464651Seric ** If this code remains at all, "CheckLoopBack" should be 16564651Seric ** a mailer flag. This is a MAYBENEXTRELEASE feature. 16658957Seric */ 16758957Seric 16859026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16958957Seric if (p != NULL) 17061707Seric *p = '\0'; 17164644Seric if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17258957Seric { 17358957Seric syserr("553 %s config error: mail loops back to myself", 17458957Seric MyHostName); 17558957Seric mci->mci_exitstat = EX_CONFIG; 17658957Seric mci->mci_errno = 0; 17758957Seric smtpquit(m, mci, e); 17858957Seric return; 17958957Seric } 18058957Seric 18158957Seric /* 1829315Seric ** If this is expected to be another sendmail, send some internal 1839315Seric ** commands. 1849315Seric */ 1859315Seric 18610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1879315Seric { 1889315Seric /* tell it to be verbose */ 18953751Seric smtpmessage("VERB", m, mci); 19059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1919315Seric if (r < 0) 19252104Seric goto tempfail2; 1939315Seric } 1949315Seric 19565057Seric if (mci->mci_state != MCIS_CLOSED) 19665057Seric { 19765057Seric mci->mci_state = MCIS_OPEN; 19865057Seric return; 19965057Seric } 20053751Seric 20165057Seric /* got a 421 error code during startup */ 20265057Seric 20353751Seric tempfail1: 20453751Seric tempfail2: 20553751Seric mci->mci_exitstat = EX_TEMPFAIL; 20657379Seric if (mci->mci_errno == 0) 20757379Seric mci->mci_errno = errno; 20857379Seric if (mci->mci_state != MCIS_CLOSED) 20957379Seric smtpquit(m, mci, e); 21054967Seric return; 21153751Seric 21253751Seric unavailable: 21353751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21453751Seric mci->mci_errno = errno; 21553751Seric smtpquit(m, mci, e); 21654967Seric return; 21753751Seric } 21859285Seric /* 21960210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 22060210Seric ** 22160210Seric ** 22260210Seric ** Parameters: 22360210Seric ** line -- the response line. 22460210Seric ** m -- the mailer. 22560210Seric ** mci -- the mailer connection info. 22660210Seric ** e -- the envelope. 22760210Seric ** 22860210Seric ** Returns: 22960210Seric ** none. 23060210Seric */ 23160210Seric 23260210Seric void 23360210Seric esmtp_check(line, m, mci, e) 23460210Seric char *line; 23560210Seric MAILER *m; 23660210Seric register MCI *mci; 23760210Seric ENVELOPE *e; 23860210Seric { 23960210Seric if (strlen(line) < 5) 24060210Seric return; 24160210Seric line += 4; 24260210Seric if (strncmp(line, "ESMTP ", 6) == 0) 24360210Seric mci->mci_flags |= MCIF_ESMTP; 24460210Seric } 24560210Seric /* 24659285Seric ** HELO_OPTIONS -- process the options on a HELO line. 24759285Seric ** 24859285Seric ** Parameters: 24959285Seric ** line -- the response line. 25059285Seric ** m -- the mailer. 25159285Seric ** mci -- the mailer connection info. 25259285Seric ** e -- the envelope. 25359285Seric ** 25459285Seric ** Returns: 25559285Seric ** none. 25659285Seric */ 25753751Seric 25859285Seric void 25959285Seric helo_options(line, m, mci, e) 26059285Seric char *line; 26159285Seric MAILER *m; 26259285Seric register MCI *mci; 26359285Seric ENVELOPE *e; 26459285Seric { 26559285Seric register char *p; 26659285Seric 26759285Seric if (strlen(line) < 5) 26859285Seric return; 26959285Seric line += 4; 27059285Seric p = strchr(line, ' '); 27159285Seric if (p != NULL) 27259285Seric *p++ = '\0'; 27359285Seric if (strcasecmp(line, "size") == 0) 27459285Seric { 27559285Seric mci->mci_flags |= MCIF_SIZE; 27659285Seric if (p != NULL) 27759285Seric mci->mci_maxsize = atol(p); 27859285Seric } 27959285Seric else if (strcasecmp(line, "8bitmime") == 0) 280*65870Seric { 28159285Seric mci->mci_flags |= MCIF_8BITMIME; 282*65870Seric mci->mci_flags &= ~MCIF_7BIT; 283*65870Seric } 28459285Seric else if (strcasecmp(line, "expn") == 0) 28559285Seric mci->mci_flags |= MCIF_EXPN; 28659285Seric } 28759285Seric /* 28859285Seric ** SMTPMAILFROM -- send MAIL command 28959285Seric ** 29059285Seric ** Parameters: 29159285Seric ** m -- the mailer. 29259285Seric ** mci -- the mailer connection structure. 29359285Seric ** e -- the envelope (including the sender to specify). 29459285Seric */ 29559285Seric 29653751Seric smtpmailfrom(m, mci, e) 29753751Seric struct mailer *m; 29854967Seric MCI *mci; 29953751Seric ENVELOPE *e; 30053751Seric { 30153751Seric int r; 30265494Seric char *bufp; 30353751Seric char buf[MAXNAME]; 30459285Seric char optbuf[MAXLINE]; 30553751Seric 30663753Seric if (tTd(18, 2)) 30757943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 30857943Seric 30959285Seric /* set up appropriate options to include */ 31064254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 31159285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 31259285Seric else 31359285Seric strcpy(optbuf, ""); 31459285Seric 3159315Seric /* 3164865Seric ** Send the MAIL command. 3174865Seric ** Designates the sender. 3184865Seric */ 3194796Seric 32053751Seric mci->mci_state = MCIS_ACTIVE; 32153751Seric 32259540Seric if (bitset(EF_RESPONSE, e->e_flags) && 32359540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 32458680Seric (void) strcpy(buf, ""); 32558680Seric else 32658680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 32765494Seric if (buf[0] == '<') 32865494Seric { 32965494Seric /* strip off <angle brackets> (put back on below) */ 33065494Seric bufp = &buf[strlen(buf) - 1]; 33165494Seric if (*bufp == '>') 33265494Seric *bufp = '\0'; 33365494Seric bufp = &buf[1]; 33465494Seric } 33565494Seric else 33665494Seric bufp = buf; 33753751Seric if (e->e_from.q_mailer == LocalMailer || 33810688Seric !bitnset(M_FROMPATH, m->m_flags)) 3398436Seric { 34065494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 3418436Seric } 3428436Seric else 3438436Seric { 34459285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 34565494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 3468436Seric } 34761093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 34853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 34959285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3508005Seric if (r < 0 || REPLYTYPE(r) == 4) 35153751Seric { 35253751Seric mci->mci_exitstat = EX_TEMPFAIL; 35353751Seric mci->mci_errno = errno; 35453751Seric smtpquit(m, mci, e); 35553751Seric return EX_TEMPFAIL; 35653751Seric } 3577963Seric else if (r == 250) 35853751Seric { 35953751Seric mci->mci_exitstat = EX_OK; 36053751Seric return EX_OK; 36153751Seric } 3627963Seric else if (r == 552) 36353751Seric { 36453751Seric /* signal service unavailable */ 36553751Seric mci->mci_exitstat = EX_UNAVAILABLE; 36653751Seric smtpquit(m, mci, e); 36753751Seric return EX_UNAVAILABLE; 36853751Seric } 36914913Seric 37058008Seric #ifdef LOG 37158020Seric if (LogLevel > 1) 37258008Seric { 37358008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 37458008Seric e->e_id, SmtpReplyBuffer); 37558008Seric } 37658008Seric #endif 37758008Seric 37814913Seric /* protocol error -- close up */ 37953751Seric smtpquit(m, mci, e); 38053751Seric mci->mci_exitstat = EX_PROTOCOL; 38153751Seric return EX_PROTOCOL; 3824684Seric } 3834684Seric /* 3844976Seric ** SMTPRCPT -- designate recipient. 3854797Seric ** 3864797Seric ** Parameters: 3874865Seric ** to -- address of recipient. 38810175Seric ** m -- the mailer we are sending to. 38957379Seric ** mci -- the connection info for this transaction. 39057379Seric ** e -- the envelope for this transaction. 3914797Seric ** 3924797Seric ** Returns: 3934865Seric ** exit status corresponding to recipient status. 3944797Seric ** 3954797Seric ** Side Effects: 3964865Seric ** Sends the mail via SMTP. 3974797Seric */ 3984797Seric 39953751Seric smtprcpt(to, m, mci, e) 4004865Seric ADDRESS *to; 40110175Seric register MAILER *m; 40254967Seric MCI *mci; 40353751Seric ENVELOPE *e; 4044797Seric { 4054797Seric register int r; 4064797Seric 40753751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 4084865Seric 40961093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 41053751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 41159285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 4128005Seric if (r < 0 || REPLYTYPE(r) == 4) 4134865Seric return (EX_TEMPFAIL); 4147963Seric else if (REPLYTYPE(r) == 2) 4157963Seric return (EX_OK); 4167964Seric else if (r == 550 || r == 551 || r == 553) 4177964Seric return (EX_NOUSER); 4187964Seric else if (r == 552 || r == 554) 4197964Seric return (EX_UNAVAILABLE); 42058008Seric 42158008Seric #ifdef LOG 42258020Seric if (LogLevel > 1) 42358008Seric { 42458008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 42558008Seric e->e_id, SmtpReplyBuffer); 42658008Seric } 42758008Seric #endif 42858008Seric 4297964Seric return (EX_PROTOCOL); 4304797Seric } 4314797Seric /* 43210175Seric ** SMTPDATA -- send the data and clean up the transaction. 4334684Seric ** 4344684Seric ** Parameters: 4354865Seric ** m -- mailer being sent to. 4366980Seric ** e -- the envelope for this message. 4374684Seric ** 4384684Seric ** Returns: 4394976Seric ** exit status corresponding to DATA command. 4404684Seric ** 4414684Seric ** Side Effects: 4424865Seric ** none. 4434684Seric */ 4444684Seric 44563753Seric static jmp_buf CtxDataTimeout; 44663937Seric static int datatimeout(); 44763753Seric 44853740Seric smtpdata(m, mci, e) 4494865Seric struct mailer *m; 45054967Seric register MCI *mci; 4516980Seric register ENVELOPE *e; 4524684Seric { 4534684Seric register int r; 45463753Seric register EVENT *ev; 45563753Seric time_t timeout; 4564684Seric 4574797Seric /* 4584797Seric ** Send the data. 45910175Seric ** First send the command and check that it is ok. 46010175Seric ** Then send the data. 46110175Seric ** Follow it up with a dot to terminate. 46210175Seric ** Finally get the results of the transaction. 4634797Seric */ 4644797Seric 46510175Seric /* send the command and check ok to proceed */ 46653751Seric smtpmessage("DATA", m, mci); 46761093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 46853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 46959285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4708005Seric if (r < 0 || REPLYTYPE(r) == 4) 47157990Seric { 47257990Seric smtpquit(m, mci, e); 4734797Seric return (EX_TEMPFAIL); 47457990Seric } 4757963Seric else if (r == 554) 47657990Seric { 47757990Seric smtprset(m, mci, e); 4787963Seric return (EX_UNAVAILABLE); 47957990Seric } 4807963Seric else if (r != 354) 48157990Seric { 48258008Seric #ifdef LOG 48358020Seric if (LogLevel > 1) 48458008Seric { 48558008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 48658008Seric e->e_id, SmtpReplyBuffer); 48758008Seric } 48858008Seric #endif 48957990Seric smtprset(m, mci, e); 4907964Seric return (EX_PROTOCOL); 49157990Seric } 49210175Seric 49363757Seric /* 49463757Seric ** Set timeout around data writes. Make it at least large 49563757Seric ** enough for DNS timeouts on all recipients plus some fudge 49663757Seric ** factor. The main thing is that it should not be infinite. 49763757Seric */ 49863757Seric 49963753Seric if (setjmp(CtxDataTimeout) != 0) 50063753Seric { 50163753Seric mci->mci_errno = errno; 50263753Seric mci->mci_exitstat = EX_TEMPFAIL; 50363753Seric mci->mci_state = MCIS_ERROR; 50463753Seric syserr("451 timeout writing message to %s", mci->mci_host); 50563753Seric smtpquit(m, mci, e); 50663753Seric return EX_TEMPFAIL; 50763753Seric } 50863753Seric 50963787Seric timeout = e->e_msgsize / 16; 51063787Seric if (timeout < (time_t) 60) 51163787Seric timeout = (time_t) 60; 51263787Seric timeout += e->e_nrcpts * 90; 51363753Seric ev = setevent(timeout, datatimeout, 0); 51463753Seric 51510175Seric /* now output the actual message */ 516*65870Seric (*e->e_puthdr)(mci, e); 517*65870Seric putline("\n", mci); 518*65870Seric (*e->e_putbody)(mci, e, NULL); 51910175Seric 52063753Seric clrevent(ev); 52163753Seric 52264718Seric if (ferror(mci->mci_out)) 52364718Seric { 52464718Seric /* error during processing -- don't send the dot */ 52564718Seric mci->mci_errno = EIO; 52664718Seric mci->mci_exitstat = EX_IOERR; 52764718Seric mci->mci_state = MCIS_ERROR; 52864718Seric smtpquit(m, mci, e); 52964718Seric return EX_IOERR; 53064718Seric } 53164718Seric 53210175Seric /* terminate the message */ 53353740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 53463753Seric if (TrafficLogFile != NULL) 53563753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 53658120Seric if (Verbose) 53758151Seric nmessage(">>> ."); 53810175Seric 53910175Seric /* check for the results of the transaction */ 54061093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 54153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 54259285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 54353751Seric if (r < 0) 54457990Seric { 54557990Seric smtpquit(m, mci, e); 5464797Seric return (EX_TEMPFAIL); 54757990Seric } 54853751Seric mci->mci_state = MCIS_OPEN; 54958917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 55053751Seric if (REPLYTYPE(r) == 4) 55153751Seric return (EX_TEMPFAIL); 5527963Seric else if (r == 250) 5537963Seric return (EX_OK); 5547963Seric else if (r == 552 || r == 554) 5557963Seric return (EX_UNAVAILABLE); 55658008Seric #ifdef LOG 55758020Seric if (LogLevel > 1) 55858008Seric { 55958008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 56058008Seric e->e_id, SmtpReplyBuffer); 56158008Seric } 56258008Seric #endif 5637964Seric return (EX_PROTOCOL); 5644684Seric } 56563753Seric 56663753Seric 56763753Seric static int 56863753Seric datatimeout() 56963753Seric { 57063753Seric longjmp(CtxDataTimeout, 1); 57163753Seric } 5724684Seric /* 5734865Seric ** SMTPQUIT -- close the SMTP connection. 5744865Seric ** 5754865Seric ** Parameters: 57615535Seric ** m -- a pointer to the mailer. 5774865Seric ** 5784865Seric ** Returns: 5794865Seric ** none. 5804865Seric ** 5814865Seric ** Side Effects: 5824865Seric ** sends the final protocol and closes the connection. 5834865Seric */ 5844865Seric 58553751Seric smtpquit(m, mci, e) 58653751Seric register MAILER *m; 58754967Seric register MCI *mci; 58853751Seric ENVELOPE *e; 5894865Seric { 59064822Seric bool oldSuprErrs = SuprErrs; 5914865Seric 59264822Seric /* 59364822Seric ** Suppress errors here -- we may be processing a different 59464822Seric ** job when we do the quit connection, and we don't want the 59564822Seric ** new job to be penalized for something that isn't it's 59664822Seric ** problem. 59764822Seric */ 59864822Seric 59964822Seric SuprErrs = TRUE; 60064822Seric 60154967Seric /* send the quit message if we haven't gotten I/O error */ 60253751Seric if (mci->mci_state != MCIS_ERROR) 6039391Seric { 60461093Seric SmtpPhase = "client QUIT"; 60553751Seric smtpmessage("QUIT", m, mci); 60659285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 60764822Seric SuprErrs = oldSuprErrs; 60853740Seric if (mci->mci_state == MCIS_CLOSED) 60964822Seric { 61064822Seric SuprErrs = oldSuprErrs; 61110159Seric return; 61264822Seric } 6139391Seric } 6149391Seric 61552676Seric /* now actually close the connection and pick up the zombie */ 61665194Seric (void) endmailer(mci, e, NULL); 61764822Seric 61864822Seric SuprErrs = oldSuprErrs; 6194865Seric } 6204865Seric /* 62154967Seric ** SMTPRSET -- send a RSET (reset) command 62254967Seric */ 62354967Seric 62454967Seric smtprset(m, mci, e) 62554967Seric register MAILER *m; 62654967Seric register MCI *mci; 62754967Seric ENVELOPE *e; 62854967Seric { 62954967Seric int r; 63054967Seric 63161093Seric SmtpPhase = "client RSET"; 63254967Seric smtpmessage("RSET", m, mci); 63359285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 63457734Seric if (r < 0) 63557734Seric mci->mci_state = MCIS_ERROR; 63654967Seric else if (REPLYTYPE(r) == 2) 63757734Seric { 63857734Seric mci->mci_state = MCIS_OPEN; 63957734Seric return; 64057734Seric } 64157734Seric smtpquit(m, mci, e); 64254967Seric } 64354967Seric /* 64458867Seric ** SMTPPROBE -- check the connection state 64554967Seric */ 64654967Seric 64758867Seric smtpprobe(mci) 64854967Seric register MCI *mci; 64954967Seric { 65054967Seric int r; 65154967Seric MAILER *m = mci->mci_mailer; 65254967Seric extern ENVELOPE BlankEnvelope; 65354967Seric ENVELOPE *e = &BlankEnvelope; 65454967Seric 65561093Seric SmtpPhase = "client probe"; 65658867Seric smtpmessage("RSET", m, mci); 65759285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 65858061Seric if (r < 0 || REPLYTYPE(r) != 2) 65954967Seric smtpquit(m, mci, e); 66054967Seric return r; 66154967Seric } 66254967Seric /* 6634684Seric ** REPLY -- read arpanet reply 6644684Seric ** 6654684Seric ** Parameters: 66610175Seric ** m -- the mailer we are reading the reply from. 66757379Seric ** mci -- the mailer connection info structure. 66857379Seric ** e -- the current envelope. 66957379Seric ** timeout -- the timeout for reads. 67059285Seric ** pfunc -- processing function for second and subsequent 67159285Seric ** lines of response -- if null, no special 67259285Seric ** processing is done. 6734684Seric ** 6744684Seric ** Returns: 6754684Seric ** reply code it reads. 6764684Seric ** 6774684Seric ** Side Effects: 6784684Seric ** flushes the mail file. 6794684Seric */ 6804684Seric 68159285Seric reply(m, mci, e, timeout, pfunc) 68253751Seric MAILER *m; 68354967Seric MCI *mci; 68453751Seric ENVELOPE *e; 68559285Seric time_t timeout; 68659285Seric void (*pfunc)(); 6874684Seric { 68859014Seric register char *bufp; 68959014Seric register int r; 69059285Seric bool firstline = TRUE; 69158957Seric char junkbuf[MAXLINE]; 69258957Seric 69357379Seric if (mci->mci_out != NULL) 69457379Seric (void) fflush(mci->mci_out); 6954684Seric 6967677Seric if (tTd(18, 1)) 6974796Seric printf("reply\n"); 6984796Seric 6997356Seric /* 7007356Seric ** Read the input line, being careful not to hang. 7017356Seric */ 7027356Seric 70359014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 7044684Seric { 7057356Seric register char *p; 70653751Seric extern time_t curtime(); 7074684Seric 7087685Seric /* actually do the read */ 70953751Seric if (e->e_xfp != NULL) 71053751Seric (void) fflush(e->e_xfp); /* for debugging */ 7117356Seric 71210054Seric /* if we are in the process of closing just give the code */ 71353740Seric if (mci->mci_state == MCIS_CLOSED) 71410054Seric return (SMTPCLOSING); 71510054Seric 71658680Seric if (mci->mci_out != NULL) 71758680Seric fflush(mci->mci_out); 71858680Seric 71910054Seric /* get the line from the other side */ 72061093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 72153751Seric mci->mci_lastuse = curtime(); 72253751Seric 72310054Seric if (p == NULL) 72410131Seric { 72563753Seric bool oldholderrs; 72610148Seric extern char MsgBuf[]; /* err.c */ 72710148Seric 72821065Seric /* if the remote end closed early, fake an error */ 72921065Seric if (errno == 0) 73021065Seric # ifdef ECONNRESET 73121065Seric errno = ECONNRESET; 73256795Seric # else /* ECONNRESET */ 73321065Seric errno = EPIPE; 73456795Seric # endif /* ECONNRESET */ 73521065Seric 73657379Seric mci->mci_errno = errno; 73757642Seric mci->mci_exitstat = EX_TEMPFAIL; 73863753Seric oldholderrs = HoldErrs; 73963753Seric HoldErrs = TRUE; 74063753Seric usrerr("451 reply: read error from %s", mci->mci_host); 74163753Seric 74210420Seric /* if debugging, pause so we can see state */ 74310420Seric if (tTd(18, 100)) 74410420Seric pause(); 74554967Seric mci->mci_state = MCIS_ERROR; 74653751Seric smtpquit(m, mci, e); 74763753Seric #ifdef XDEBUG 74863753Seric { 74963753Seric char wbuf[MAXLINE]; 75064082Seric char *p = wbuf; 75164082Seric if (e->e_to != NULL) 75264082Seric { 75364082Seric sprintf(p, "%s... ", e->e_to); 75464082Seric p += strlen(p); 75564082Seric } 75664082Seric sprintf(p, "reply(%s) during %s", 75764082Seric mci->mci_host, SmtpPhase); 75863753Seric checkfd012(wbuf); 75963753Seric } 76063753Seric #endif 76163753Seric HoldErrs = oldholderrs; 76210054Seric return (-1); 76310131Seric } 76458957Seric fixcrlf(bufp, TRUE); 76510054Seric 76663753Seric /* EHLO failure is not a real error */ 76763753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 76863753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 76914900Seric { 77014900Seric /* serious error -- log the previous command */ 77164071Seric if (SmtpNeedIntro) 77264071Seric { 77364071Seric /* inform user who we are chatting with */ 77464071Seric fprintf(CurEnv->e_xfp, 77564071Seric "... while talking to %s:\n", 77664071Seric CurHostName); 77764071Seric SmtpNeedIntro = FALSE; 77864071Seric } 77959014Seric if (SmtpMsgBuffer[0] != '\0') 78059014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 78159014Seric SmtpMsgBuffer[0] = '\0'; 78214900Seric 78314900Seric /* now log the message as from the other side */ 78458957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 78514900Seric } 78614900Seric 78714900Seric /* display the input for verbose mode */ 78858120Seric if (Verbose) 78959956Seric nmessage("050 %s", bufp); 7907356Seric 79159285Seric /* process the line */ 79259285Seric if (pfunc != NULL && !firstline) 79359285Seric (*pfunc)(bufp, m, mci, e); 79459285Seric 79559285Seric firstline = FALSE; 79659285Seric 7977356Seric /* if continuation is required, we can go on */ 79859014Seric if (bufp[3] == '-') 7994684Seric continue; 8007356Seric 80159014Seric /* ignore improperly formated input */ 80259014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 80359014Seric continue; 80459014Seric 8057356Seric /* decode the reply code */ 80659014Seric r = atoi(bufp); 8077356Seric 8087356Seric /* extra semantics: 0xx codes are "informational" */ 80959014Seric if (r >= 100) 81059014Seric break; 81159014Seric } 8127356Seric 81359014Seric /* 81459014Seric ** Now look at SmtpReplyBuffer -- only care about the first 81559014Seric ** line of the response from here on out. 81659014Seric */ 81758061Seric 81859014Seric /* save temporary failure messages for posterity */ 81959014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 82059014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8219391Seric 82259014Seric /* reply code 421 is "Service Shutting Down" */ 82359014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 82459014Seric { 82559014Seric /* send the quit protocol */ 82659014Seric mci->mci_state = MCIS_SSD; 82759014Seric smtpquit(m, mci, e); 8284684Seric } 82959014Seric 83059014Seric return (r); 8314684Seric } 8324796Seric /* 8334865Seric ** SMTPMESSAGE -- send message to server 8344796Seric ** 8354796Seric ** Parameters: 8364796Seric ** f -- format 83710175Seric ** m -- the mailer to control formatting. 8384796Seric ** a, b, c -- parameters 8394796Seric ** 8404796Seric ** Returns: 8414796Seric ** none. 8424796Seric ** 8434796Seric ** Side Effects: 84453740Seric ** writes message to mci->mci_out. 8454796Seric */ 8464796Seric 8474865Seric /*VARARGS1*/ 84857642Seric #ifdef __STDC__ 84957642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 85057642Seric #else 85157642Seric smtpmessage(f, m, mci, va_alist) 8524796Seric char *f; 85310175Seric MAILER *m; 85454967Seric MCI *mci; 85557642Seric va_dcl 85657642Seric #endif 8574796Seric { 85856852Seric VA_LOCAL_DECL 85956852Seric 86057135Seric VA_START(mci); 86156852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 86256852Seric VA_END; 86358680Seric 86458120Seric if (tTd(18, 1) || Verbose) 86558151Seric nmessage(">>> %s", SmtpMsgBuffer); 86663753Seric if (TrafficLogFile != NULL) 86763753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 86853740Seric if (mci->mci_out != NULL) 86958680Seric { 87053740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 87154967Seric m == NULL ? "\r\n" : m->m_eol); 87258680Seric } 87359149Seric else if (tTd(18, 1)) 87458725Seric { 87559149Seric printf("smtpmessage: NULL mci_out\n"); 87658725Seric } 8774796Seric } 8785182Seric 87956795Seric # endif /* SMTP */ 880