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*67472Seric static char sccsid[] = "@(#)usersmtp.c 8.20 (Berkeley) 07/03/94 (with SMTP)"; 1433731Sbostic #else 15*67472Seric static char sccsid[] = "@(#)usersmtp.c 8.20 (Berkeley) 07/03/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. 16358957Seric */ 16458957Seric 16559026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16658957Seric if (p != NULL) 16761707Seric *p = '\0'; 168*67472Seric if (!bitnset(M_NOLOOPCHECK, m->m_flags) && 169*67472Seric strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17058957Seric { 17158957Seric syserr("553 %s config error: mail loops back to myself", 17258957Seric MyHostName); 17358957Seric mci->mci_exitstat = EX_CONFIG; 17458957Seric mci->mci_errno = 0; 17558957Seric smtpquit(m, mci, e); 17658957Seric return; 17758957Seric } 17858957Seric 17958957Seric /* 1809315Seric ** If this is expected to be another sendmail, send some internal 1819315Seric ** commands. 1829315Seric */ 1839315Seric 18410688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1859315Seric { 1869315Seric /* tell it to be verbose */ 18753751Seric smtpmessage("VERB", m, mci); 18859285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1899315Seric if (r < 0) 19052104Seric goto tempfail2; 1919315Seric } 1929315Seric 19365057Seric if (mci->mci_state != MCIS_CLOSED) 19465057Seric { 19565057Seric mci->mci_state = MCIS_OPEN; 19665057Seric return; 19765057Seric } 19853751Seric 19965057Seric /* got a 421 error code during startup */ 20065057Seric 20153751Seric tempfail1: 20253751Seric tempfail2: 20353751Seric mci->mci_exitstat = EX_TEMPFAIL; 20457379Seric if (mci->mci_errno == 0) 20557379Seric mci->mci_errno = errno; 20657379Seric if (mci->mci_state != MCIS_CLOSED) 20757379Seric smtpquit(m, mci, e); 20854967Seric return; 20953751Seric 21053751Seric unavailable: 21153751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21253751Seric mci->mci_errno = errno; 21353751Seric smtpquit(m, mci, e); 21454967Seric return; 21553751Seric } 21659285Seric /* 21760210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 21860210Seric ** 21960210Seric ** 22060210Seric ** Parameters: 22160210Seric ** line -- the response line. 22260210Seric ** m -- the mailer. 22360210Seric ** mci -- the mailer connection info. 22460210Seric ** e -- the envelope. 22560210Seric ** 22660210Seric ** Returns: 22760210Seric ** none. 22860210Seric */ 22960210Seric 23060210Seric void 23160210Seric esmtp_check(line, m, mci, e) 23260210Seric char *line; 23360210Seric MAILER *m; 23460210Seric register MCI *mci; 23560210Seric ENVELOPE *e; 23660210Seric { 23760210Seric if (strlen(line) < 5) 23860210Seric return; 23960210Seric line += 4; 24060210Seric if (strncmp(line, "ESMTP ", 6) == 0) 24160210Seric mci->mci_flags |= MCIF_ESMTP; 24260210Seric } 24360210Seric /* 24459285Seric ** HELO_OPTIONS -- process the options on a HELO line. 24559285Seric ** 24659285Seric ** Parameters: 24759285Seric ** line -- the response line. 24859285Seric ** m -- the mailer. 24959285Seric ** mci -- the mailer connection info. 25059285Seric ** e -- the envelope. 25159285Seric ** 25259285Seric ** Returns: 25359285Seric ** none. 25459285Seric */ 25553751Seric 25659285Seric void 25759285Seric helo_options(line, m, mci, e) 25859285Seric char *line; 25959285Seric MAILER *m; 26059285Seric register MCI *mci; 26159285Seric ENVELOPE *e; 26259285Seric { 26359285Seric register char *p; 26459285Seric 26559285Seric if (strlen(line) < 5) 26659285Seric return; 26759285Seric line += 4; 26859285Seric p = strchr(line, ' '); 26959285Seric if (p != NULL) 27059285Seric *p++ = '\0'; 27159285Seric if (strcasecmp(line, "size") == 0) 27259285Seric { 27359285Seric mci->mci_flags |= MCIF_SIZE; 27459285Seric if (p != NULL) 27559285Seric mci->mci_maxsize = atol(p); 27659285Seric } 27759285Seric else if (strcasecmp(line, "8bitmime") == 0) 27865870Seric { 27959285Seric mci->mci_flags |= MCIF_8BITMIME; 28065870Seric mci->mci_flags &= ~MCIF_7BIT; 28165870Seric } 28259285Seric else if (strcasecmp(line, "expn") == 0) 28359285Seric mci->mci_flags |= MCIF_EXPN; 28459285Seric } 28559285Seric /* 28659285Seric ** SMTPMAILFROM -- send MAIL command 28759285Seric ** 28859285Seric ** Parameters: 28959285Seric ** m -- the mailer. 29059285Seric ** mci -- the mailer connection structure. 29159285Seric ** e -- the envelope (including the sender to specify). 29259285Seric */ 29359285Seric 29453751Seric smtpmailfrom(m, mci, e) 29553751Seric struct mailer *m; 29654967Seric MCI *mci; 29753751Seric ENVELOPE *e; 29853751Seric { 29953751Seric int r; 30065494Seric char *bufp; 30153751Seric char buf[MAXNAME]; 30259285Seric char optbuf[MAXLINE]; 30353751Seric 30463753Seric if (tTd(18, 2)) 30557943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 30657943Seric 30759285Seric /* set up appropriate options to include */ 30864254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 30959285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 31059285Seric else 31159285Seric strcpy(optbuf, ""); 31259285Seric 31367417Seric if (e->e_bodytype != NULL) 31467417Seric { 31567417Seric if (bitset(MCIF_8BITMIME, mci->mci_flags)) 31667417Seric { 31767417Seric strcat(optbuf, " BODY="); 31867417Seric strcat(optbuf, e->e_bodytype); 31967417Seric } 32067417Seric else if (strcasecmp(e->e_bodytype, "7bit") != 0) 32167417Seric { 32267417Seric /* cannot just send a 7-bit version */ 32367417Seric usrerr("%s does not support 8BITMIME", mci->mci_host); 32467417Seric return EX_DATAERR; 32567417Seric } 32667417Seric } 32767417Seric 3289315Seric /* 3294865Seric ** Send the MAIL command. 3304865Seric ** Designates the sender. 3314865Seric */ 3324796Seric 33353751Seric mci->mci_state = MCIS_ACTIVE; 33453751Seric 33559540Seric if (bitset(EF_RESPONSE, e->e_flags) && 33659540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 33758680Seric (void) strcpy(buf, ""); 33858680Seric else 33958680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 34065494Seric if (buf[0] == '<') 34165494Seric { 34265494Seric /* strip off <angle brackets> (put back on below) */ 34365494Seric bufp = &buf[strlen(buf) - 1]; 34465494Seric if (*bufp == '>') 34565494Seric *bufp = '\0'; 34665494Seric bufp = &buf[1]; 34765494Seric } 34865494Seric else 34965494Seric bufp = buf; 350*67472Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 35110688Seric !bitnset(M_FROMPATH, m->m_flags)) 3528436Seric { 35365494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 3548436Seric } 3558436Seric else 3568436Seric { 35759285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 35865494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 3598436Seric } 36061093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 36153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 36259285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3638005Seric if (r < 0 || REPLYTYPE(r) == 4) 36453751Seric { 36553751Seric mci->mci_exitstat = EX_TEMPFAIL; 36653751Seric mci->mci_errno = errno; 36753751Seric smtpquit(m, mci, e); 36853751Seric return EX_TEMPFAIL; 36953751Seric } 3707963Seric else if (r == 250) 37153751Seric { 37253751Seric mci->mci_exitstat = EX_OK; 37353751Seric return EX_OK; 37453751Seric } 3757963Seric else if (r == 552) 37653751Seric { 37753751Seric /* signal service unavailable */ 37853751Seric mci->mci_exitstat = EX_UNAVAILABLE; 37953751Seric smtpquit(m, mci, e); 38053751Seric return EX_UNAVAILABLE; 38153751Seric } 38214913Seric 38358008Seric #ifdef LOG 38458020Seric if (LogLevel > 1) 38558008Seric { 38658008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 38758008Seric e->e_id, SmtpReplyBuffer); 38858008Seric } 38958008Seric #endif 39058008Seric 39114913Seric /* protocol error -- close up */ 39253751Seric smtpquit(m, mci, e); 39353751Seric mci->mci_exitstat = EX_PROTOCOL; 39453751Seric return EX_PROTOCOL; 3954684Seric } 3964684Seric /* 3974976Seric ** SMTPRCPT -- designate recipient. 3984797Seric ** 3994797Seric ** Parameters: 4004865Seric ** to -- address of recipient. 40110175Seric ** m -- the mailer we are sending to. 40257379Seric ** mci -- the connection info for this transaction. 40357379Seric ** e -- the envelope for this transaction. 4044797Seric ** 4054797Seric ** Returns: 4064865Seric ** exit status corresponding to recipient status. 4074797Seric ** 4084797Seric ** Side Effects: 4094865Seric ** Sends the mail via SMTP. 4104797Seric */ 4114797Seric 41253751Seric smtprcpt(to, m, mci, e) 4134865Seric ADDRESS *to; 41410175Seric register MAILER *m; 41554967Seric MCI *mci; 41653751Seric ENVELOPE *e; 4174797Seric { 4184797Seric register int r; 4194797Seric 42053751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 4214865Seric 42261093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 42353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 42459285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 4258005Seric if (r < 0 || REPLYTYPE(r) == 4) 4264865Seric return (EX_TEMPFAIL); 4277963Seric else if (REPLYTYPE(r) == 2) 4287963Seric return (EX_OK); 4297964Seric else if (r == 550 || r == 551 || r == 553) 4307964Seric return (EX_NOUSER); 4317964Seric else if (r == 552 || r == 554) 4327964Seric return (EX_UNAVAILABLE); 43358008Seric 43458008Seric #ifdef LOG 43558020Seric if (LogLevel > 1) 43658008Seric { 43758008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 43858008Seric e->e_id, SmtpReplyBuffer); 43958008Seric } 44058008Seric #endif 44158008Seric 4427964Seric return (EX_PROTOCOL); 4434797Seric } 4444797Seric /* 44510175Seric ** SMTPDATA -- send the data and clean up the transaction. 4464684Seric ** 4474684Seric ** Parameters: 4484865Seric ** m -- mailer being sent to. 4496980Seric ** e -- the envelope for this message. 4504684Seric ** 4514684Seric ** Returns: 4524976Seric ** exit status corresponding to DATA command. 4534684Seric ** 4544684Seric ** Side Effects: 4554865Seric ** none. 4564684Seric */ 4574684Seric 45863753Seric static jmp_buf CtxDataTimeout; 45963937Seric static int datatimeout(); 46063753Seric 46153740Seric smtpdata(m, mci, e) 4624865Seric struct mailer *m; 46354967Seric register MCI *mci; 4646980Seric register ENVELOPE *e; 4654684Seric { 4664684Seric register int r; 46763753Seric register EVENT *ev; 46863753Seric time_t timeout; 4694684Seric 4704797Seric /* 4714797Seric ** Send the data. 47210175Seric ** First send the command and check that it is ok. 47310175Seric ** Then send the data. 47410175Seric ** Follow it up with a dot to terminate. 47510175Seric ** Finally get the results of the transaction. 4764797Seric */ 4774797Seric 47810175Seric /* send the command and check ok to proceed */ 47953751Seric smtpmessage("DATA", m, mci); 48061093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 48153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 48259285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4838005Seric if (r < 0 || REPLYTYPE(r) == 4) 48457990Seric { 48557990Seric smtpquit(m, mci, e); 4864797Seric return (EX_TEMPFAIL); 48757990Seric } 4887963Seric else if (r == 554) 48957990Seric { 49057990Seric smtprset(m, mci, e); 4917963Seric return (EX_UNAVAILABLE); 49257990Seric } 4937963Seric else if (r != 354) 49457990Seric { 49558008Seric #ifdef LOG 49658020Seric if (LogLevel > 1) 49758008Seric { 49858008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 49958008Seric e->e_id, SmtpReplyBuffer); 50058008Seric } 50158008Seric #endif 50257990Seric smtprset(m, mci, e); 5037964Seric return (EX_PROTOCOL); 50457990Seric } 50510175Seric 50663757Seric /* 50763757Seric ** Set timeout around data writes. Make it at least large 50863757Seric ** enough for DNS timeouts on all recipients plus some fudge 50963757Seric ** factor. The main thing is that it should not be infinite. 51063757Seric */ 51163757Seric 51263753Seric if (setjmp(CtxDataTimeout) != 0) 51363753Seric { 51463753Seric mci->mci_errno = errno; 51563753Seric mci->mci_exitstat = EX_TEMPFAIL; 51663753Seric mci->mci_state = MCIS_ERROR; 51763753Seric syserr("451 timeout writing message to %s", mci->mci_host); 51863753Seric smtpquit(m, mci, e); 51963753Seric return EX_TEMPFAIL; 52063753Seric } 52163753Seric 52263787Seric timeout = e->e_msgsize / 16; 52363787Seric if (timeout < (time_t) 60) 52463787Seric timeout = (time_t) 60; 52563787Seric timeout += e->e_nrcpts * 90; 52663753Seric ev = setevent(timeout, datatimeout, 0); 52763753Seric 52810175Seric /* now output the actual message */ 52965870Seric (*e->e_puthdr)(mci, e); 53065870Seric putline("\n", mci); 53165870Seric (*e->e_putbody)(mci, e, NULL); 53210175Seric 53363753Seric clrevent(ev); 53463753Seric 53564718Seric if (ferror(mci->mci_out)) 53664718Seric { 53764718Seric /* error during processing -- don't send the dot */ 53864718Seric mci->mci_errno = EIO; 53964718Seric mci->mci_exitstat = EX_IOERR; 54064718Seric mci->mci_state = MCIS_ERROR; 54164718Seric smtpquit(m, mci, e); 54264718Seric return EX_IOERR; 54364718Seric } 54464718Seric 54510175Seric /* terminate the message */ 54653740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 54763753Seric if (TrafficLogFile != NULL) 54863753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 54958120Seric if (Verbose) 55058151Seric nmessage(">>> ."); 55110175Seric 55210175Seric /* check for the results of the transaction */ 55361093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 55453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 55559285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 55653751Seric if (r < 0) 55757990Seric { 55857990Seric smtpquit(m, mci, e); 5594797Seric return (EX_TEMPFAIL); 56057990Seric } 56153751Seric mci->mci_state = MCIS_OPEN; 56258917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 56353751Seric if (REPLYTYPE(r) == 4) 56453751Seric return (EX_TEMPFAIL); 5657963Seric else if (r == 250) 5667963Seric return (EX_OK); 5677963Seric else if (r == 552 || r == 554) 5687963Seric return (EX_UNAVAILABLE); 56958008Seric #ifdef LOG 57058020Seric if (LogLevel > 1) 57158008Seric { 57258008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 57358008Seric e->e_id, SmtpReplyBuffer); 57458008Seric } 57558008Seric #endif 5767964Seric return (EX_PROTOCOL); 5774684Seric } 57863753Seric 57963753Seric 58063753Seric static int 58163753Seric datatimeout() 58263753Seric { 58363753Seric longjmp(CtxDataTimeout, 1); 58463753Seric } 5854684Seric /* 5864865Seric ** SMTPQUIT -- close the SMTP connection. 5874865Seric ** 5884865Seric ** Parameters: 58915535Seric ** m -- a pointer to the mailer. 5904865Seric ** 5914865Seric ** Returns: 5924865Seric ** none. 5934865Seric ** 5944865Seric ** Side Effects: 5954865Seric ** sends the final protocol and closes the connection. 5964865Seric */ 5974865Seric 59853751Seric smtpquit(m, mci, e) 59953751Seric register MAILER *m; 60054967Seric register MCI *mci; 60153751Seric ENVELOPE *e; 6024865Seric { 60364822Seric bool oldSuprErrs = SuprErrs; 6044865Seric 60564822Seric /* 60664822Seric ** Suppress errors here -- we may be processing a different 60764822Seric ** job when we do the quit connection, and we don't want the 60864822Seric ** new job to be penalized for something that isn't it's 60964822Seric ** problem. 61064822Seric */ 61164822Seric 61264822Seric SuprErrs = TRUE; 61364822Seric 61454967Seric /* send the quit message if we haven't gotten I/O error */ 61553751Seric if (mci->mci_state != MCIS_ERROR) 6169391Seric { 61761093Seric SmtpPhase = "client QUIT"; 61853751Seric smtpmessage("QUIT", m, mci); 61959285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 62064822Seric SuprErrs = oldSuprErrs; 62153740Seric if (mci->mci_state == MCIS_CLOSED) 62264822Seric { 62364822Seric SuprErrs = oldSuprErrs; 62410159Seric return; 62564822Seric } 6269391Seric } 6279391Seric 62852676Seric /* now actually close the connection and pick up the zombie */ 62965194Seric (void) endmailer(mci, e, NULL); 63064822Seric 63164822Seric SuprErrs = oldSuprErrs; 6324865Seric } 6334865Seric /* 63454967Seric ** SMTPRSET -- send a RSET (reset) command 63554967Seric */ 63654967Seric 63754967Seric smtprset(m, mci, e) 63854967Seric register MAILER *m; 63954967Seric register MCI *mci; 64054967Seric ENVELOPE *e; 64154967Seric { 64254967Seric int r; 64354967Seric 64461093Seric SmtpPhase = "client RSET"; 64554967Seric smtpmessage("RSET", m, mci); 64659285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 64757734Seric if (r < 0) 64857734Seric mci->mci_state = MCIS_ERROR; 64954967Seric else if (REPLYTYPE(r) == 2) 65057734Seric { 65157734Seric mci->mci_state = MCIS_OPEN; 65257734Seric return; 65357734Seric } 65457734Seric smtpquit(m, mci, e); 65554967Seric } 65654967Seric /* 65758867Seric ** SMTPPROBE -- check the connection state 65854967Seric */ 65954967Seric 66058867Seric smtpprobe(mci) 66154967Seric register MCI *mci; 66254967Seric { 66354967Seric int r; 66454967Seric MAILER *m = mci->mci_mailer; 66554967Seric extern ENVELOPE BlankEnvelope; 66654967Seric ENVELOPE *e = &BlankEnvelope; 66754967Seric 66861093Seric SmtpPhase = "client probe"; 66958867Seric smtpmessage("RSET", m, mci); 67059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 67158061Seric if (r < 0 || REPLYTYPE(r) != 2) 67254967Seric smtpquit(m, mci, e); 67354967Seric return r; 67454967Seric } 67554967Seric /* 6764684Seric ** REPLY -- read arpanet reply 6774684Seric ** 6784684Seric ** Parameters: 67910175Seric ** m -- the mailer we are reading the reply from. 68057379Seric ** mci -- the mailer connection info structure. 68157379Seric ** e -- the current envelope. 68257379Seric ** timeout -- the timeout for reads. 68359285Seric ** pfunc -- processing function for second and subsequent 68459285Seric ** lines of response -- if null, no special 68559285Seric ** processing is done. 6864684Seric ** 6874684Seric ** Returns: 6884684Seric ** reply code it reads. 6894684Seric ** 6904684Seric ** Side Effects: 6914684Seric ** flushes the mail file. 6924684Seric */ 6934684Seric 69459285Seric reply(m, mci, e, timeout, pfunc) 69553751Seric MAILER *m; 69654967Seric MCI *mci; 69753751Seric ENVELOPE *e; 69859285Seric time_t timeout; 69959285Seric void (*pfunc)(); 7004684Seric { 70159014Seric register char *bufp; 70259014Seric register int r; 70359285Seric bool firstline = TRUE; 70458957Seric char junkbuf[MAXLINE]; 70558957Seric 70657379Seric if (mci->mci_out != NULL) 70757379Seric (void) fflush(mci->mci_out); 7084684Seric 7097677Seric if (tTd(18, 1)) 7104796Seric printf("reply\n"); 7114796Seric 7127356Seric /* 7137356Seric ** Read the input line, being careful not to hang. 7147356Seric */ 7157356Seric 71659014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 7174684Seric { 7187356Seric register char *p; 71953751Seric extern time_t curtime(); 7204684Seric 7217685Seric /* actually do the read */ 72253751Seric if (e->e_xfp != NULL) 72353751Seric (void) fflush(e->e_xfp); /* for debugging */ 7247356Seric 72510054Seric /* if we are in the process of closing just give the code */ 72653740Seric if (mci->mci_state == MCIS_CLOSED) 72710054Seric return (SMTPCLOSING); 72810054Seric 72958680Seric if (mci->mci_out != NULL) 73058680Seric fflush(mci->mci_out); 73158680Seric 73210054Seric /* get the line from the other side */ 73361093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 73453751Seric mci->mci_lastuse = curtime(); 73553751Seric 73610054Seric if (p == NULL) 73710131Seric { 73863753Seric bool oldholderrs; 73910148Seric extern char MsgBuf[]; /* err.c */ 74010148Seric 74121065Seric /* if the remote end closed early, fake an error */ 74221065Seric if (errno == 0) 74321065Seric # ifdef ECONNRESET 74421065Seric errno = ECONNRESET; 74556795Seric # else /* ECONNRESET */ 74621065Seric errno = EPIPE; 74756795Seric # endif /* ECONNRESET */ 74821065Seric 74957379Seric mci->mci_errno = errno; 75057642Seric mci->mci_exitstat = EX_TEMPFAIL; 75163753Seric oldholderrs = HoldErrs; 75263753Seric HoldErrs = TRUE; 75363753Seric usrerr("451 reply: read error from %s", mci->mci_host); 75463753Seric 75510420Seric /* if debugging, pause so we can see state */ 75610420Seric if (tTd(18, 100)) 75710420Seric pause(); 75854967Seric mci->mci_state = MCIS_ERROR; 75953751Seric smtpquit(m, mci, e); 76063753Seric #ifdef XDEBUG 76163753Seric { 76263753Seric char wbuf[MAXLINE]; 76364082Seric char *p = wbuf; 76464082Seric if (e->e_to != NULL) 76564082Seric { 76664082Seric sprintf(p, "%s... ", e->e_to); 76764082Seric p += strlen(p); 76864082Seric } 76964082Seric sprintf(p, "reply(%s) during %s", 77064082Seric mci->mci_host, SmtpPhase); 77163753Seric checkfd012(wbuf); 77263753Seric } 77363753Seric #endif 77463753Seric HoldErrs = oldholderrs; 77510054Seric return (-1); 77610131Seric } 77758957Seric fixcrlf(bufp, TRUE); 77810054Seric 77963753Seric /* EHLO failure is not a real error */ 78063753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 78163753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 78214900Seric { 78314900Seric /* serious error -- log the previous command */ 78464071Seric if (SmtpNeedIntro) 78564071Seric { 78664071Seric /* inform user who we are chatting with */ 78764071Seric fprintf(CurEnv->e_xfp, 78864071Seric "... while talking to %s:\n", 78964071Seric CurHostName); 79064071Seric SmtpNeedIntro = FALSE; 79164071Seric } 79259014Seric if (SmtpMsgBuffer[0] != '\0') 79359014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 79459014Seric SmtpMsgBuffer[0] = '\0'; 79514900Seric 79614900Seric /* now log the message as from the other side */ 79758957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 79814900Seric } 79914900Seric 80014900Seric /* display the input for verbose mode */ 80158120Seric if (Verbose) 80259956Seric nmessage("050 %s", bufp); 8037356Seric 80459285Seric /* process the line */ 80559285Seric if (pfunc != NULL && !firstline) 80659285Seric (*pfunc)(bufp, m, mci, e); 80759285Seric 80859285Seric firstline = FALSE; 80959285Seric 8107356Seric /* if continuation is required, we can go on */ 81159014Seric if (bufp[3] == '-') 8124684Seric continue; 8137356Seric 81459014Seric /* ignore improperly formated input */ 81559014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 81659014Seric continue; 81759014Seric 8187356Seric /* decode the reply code */ 81959014Seric r = atoi(bufp); 8207356Seric 8217356Seric /* extra semantics: 0xx codes are "informational" */ 82259014Seric if (r >= 100) 82359014Seric break; 82459014Seric } 8257356Seric 82659014Seric /* 82759014Seric ** Now look at SmtpReplyBuffer -- only care about the first 82859014Seric ** line of the response from here on out. 82959014Seric */ 83058061Seric 83159014Seric /* save temporary failure messages for posterity */ 83259014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 83359014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8349391Seric 83559014Seric /* reply code 421 is "Service Shutting Down" */ 83659014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 83759014Seric { 83859014Seric /* send the quit protocol */ 83959014Seric mci->mci_state = MCIS_SSD; 84059014Seric smtpquit(m, mci, e); 8414684Seric } 84259014Seric 84359014Seric return (r); 8444684Seric } 8454796Seric /* 8464865Seric ** SMTPMESSAGE -- send message to server 8474796Seric ** 8484796Seric ** Parameters: 8494796Seric ** f -- format 85010175Seric ** m -- the mailer to control formatting. 8514796Seric ** a, b, c -- parameters 8524796Seric ** 8534796Seric ** Returns: 8544796Seric ** none. 8554796Seric ** 8564796Seric ** Side Effects: 85753740Seric ** writes message to mci->mci_out. 8584796Seric */ 8594796Seric 8604865Seric /*VARARGS1*/ 86157642Seric #ifdef __STDC__ 86257642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 86357642Seric #else 86457642Seric smtpmessage(f, m, mci, va_alist) 8654796Seric char *f; 86610175Seric MAILER *m; 86754967Seric MCI *mci; 86857642Seric va_dcl 86957642Seric #endif 8704796Seric { 87156852Seric VA_LOCAL_DECL 87256852Seric 87357135Seric VA_START(mci); 87456852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 87556852Seric VA_END; 87658680Seric 87758120Seric if (tTd(18, 1) || Verbose) 87858151Seric nmessage(">>> %s", SmtpMsgBuffer); 87963753Seric if (TrafficLogFile != NULL) 88063753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 88153740Seric if (mci->mci_out != NULL) 88258680Seric { 88353740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 88454967Seric m == NULL ? "\r\n" : m->m_eol); 88558680Seric } 88659149Seric else if (tTd(18, 1)) 88758725Seric { 88859149Seric printf("smtpmessage: NULL mci_out\n"); 88958725Seric } 8904796Seric } 8915182Seric 89256795Seric # endif /* SMTP */ 893