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*67860Seric static char sccsid[] = "@(#)usersmtp.c 8.22 (Berkeley) 10/26/94 (with SMTP)"; 1433731Sbostic #else 15*67860Seric static char sccsid[] = "@(#)usersmtp.c 8.22 (Berkeley) 10/26/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'; 16867472Seric if (!bitnset(M_NOLOOPCHECK, m->m_flags) && 16967472Seric 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 } 32067546Seric else if (!bitset(MM_CVTMIME, MimeMode) && 32167546Seric strcasecmp(e->e_bodytype, "7bit") != 0) 32267417Seric { 32367417Seric /* cannot just send a 7-bit version */ 32467417Seric usrerr("%s does not support 8BITMIME", mci->mci_host); 32567417Seric return EX_DATAERR; 32667417Seric } 32767417Seric } 32867417Seric 3299315Seric /* 3304865Seric ** Send the MAIL command. 3314865Seric ** Designates the sender. 3324865Seric */ 3334796Seric 33453751Seric mci->mci_state = MCIS_ACTIVE; 33553751Seric 33659540Seric if (bitset(EF_RESPONSE, e->e_flags) && 33759540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 33858680Seric (void) strcpy(buf, ""); 33958680Seric else 34058680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 34165494Seric if (buf[0] == '<') 34265494Seric { 34365494Seric /* strip off <angle brackets> (put back on below) */ 34465494Seric bufp = &buf[strlen(buf) - 1]; 34565494Seric if (*bufp == '>') 34665494Seric *bufp = '\0'; 34765494Seric bufp = &buf[1]; 34865494Seric } 34965494Seric else 35065494Seric bufp = buf; 35167472Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 35210688Seric !bitnset(M_FROMPATH, m->m_flags)) 3538436Seric { 35465494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 3558436Seric } 3568436Seric else 3578436Seric { 35859285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 35965494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 3608436Seric } 36161093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 36253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 36359285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3648005Seric if (r < 0 || REPLYTYPE(r) == 4) 36553751Seric { 36653751Seric mci->mci_exitstat = EX_TEMPFAIL; 36753751Seric mci->mci_errno = errno; 36853751Seric smtpquit(m, mci, e); 36953751Seric return EX_TEMPFAIL; 37053751Seric } 3717963Seric else if (r == 250) 37253751Seric { 37353751Seric mci->mci_exitstat = EX_OK; 37453751Seric return EX_OK; 37553751Seric } 3767963Seric else if (r == 552) 37753751Seric { 37853751Seric /* signal service unavailable */ 37953751Seric mci->mci_exitstat = EX_UNAVAILABLE; 38053751Seric smtpquit(m, mci, e); 38153751Seric return EX_UNAVAILABLE; 38253751Seric } 38314913Seric 38458008Seric #ifdef LOG 38558020Seric if (LogLevel > 1) 38658008Seric { 387*67860Seric syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s", 388*67860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 38958008Seric } 39058008Seric #endif 39158008Seric 39214913Seric /* protocol error -- close up */ 39353751Seric smtpquit(m, mci, e); 39453751Seric mci->mci_exitstat = EX_PROTOCOL; 39553751Seric return EX_PROTOCOL; 3964684Seric } 3974684Seric /* 3984976Seric ** SMTPRCPT -- designate recipient. 3994797Seric ** 4004797Seric ** Parameters: 4014865Seric ** to -- address of recipient. 40210175Seric ** m -- the mailer we are sending to. 40357379Seric ** mci -- the connection info for this transaction. 40457379Seric ** e -- the envelope for this transaction. 4054797Seric ** 4064797Seric ** Returns: 4074865Seric ** exit status corresponding to recipient status. 4084797Seric ** 4094797Seric ** Side Effects: 4104865Seric ** Sends the mail via SMTP. 4114797Seric */ 4124797Seric 41353751Seric smtprcpt(to, m, mci, e) 4144865Seric ADDRESS *to; 41510175Seric register MAILER *m; 41654967Seric MCI *mci; 41753751Seric ENVELOPE *e; 4184797Seric { 4194797Seric register int r; 4204797Seric 42153751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 4224865Seric 42361093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 42453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 42559285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 4268005Seric if (r < 0 || REPLYTYPE(r) == 4) 4274865Seric return (EX_TEMPFAIL); 4287963Seric else if (REPLYTYPE(r) == 2) 4297963Seric return (EX_OK); 4307964Seric else if (r == 550 || r == 551 || r == 553) 4317964Seric return (EX_NOUSER); 4327964Seric else if (r == 552 || r == 554) 4337964Seric return (EX_UNAVAILABLE); 43458008Seric 43558008Seric #ifdef LOG 43658020Seric if (LogLevel > 1) 43758008Seric { 438*67860Seric syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s", 439*67860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 44058008Seric } 44158008Seric #endif 44258008Seric 4437964Seric return (EX_PROTOCOL); 4444797Seric } 4454797Seric /* 44610175Seric ** SMTPDATA -- send the data and clean up the transaction. 4474684Seric ** 4484684Seric ** Parameters: 4494865Seric ** m -- mailer being sent to. 4506980Seric ** e -- the envelope for this message. 4514684Seric ** 4524684Seric ** Returns: 4534976Seric ** exit status corresponding to DATA command. 4544684Seric ** 4554684Seric ** Side Effects: 4564865Seric ** none. 4574684Seric */ 4584684Seric 45963753Seric static jmp_buf CtxDataTimeout; 46063937Seric static int datatimeout(); 46163753Seric 46253740Seric smtpdata(m, mci, e) 4634865Seric struct mailer *m; 46454967Seric register MCI *mci; 4656980Seric register ENVELOPE *e; 4664684Seric { 4674684Seric register int r; 46863753Seric register EVENT *ev; 46963753Seric time_t timeout; 4704684Seric 4714797Seric /* 4724797Seric ** Send the data. 47310175Seric ** First send the command and check that it is ok. 47410175Seric ** Then send the data. 47510175Seric ** Follow it up with a dot to terminate. 47610175Seric ** Finally get the results of the transaction. 4774797Seric */ 4784797Seric 47910175Seric /* send the command and check ok to proceed */ 48053751Seric smtpmessage("DATA", m, mci); 48161093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 48253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 48359285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4848005Seric if (r < 0 || REPLYTYPE(r) == 4) 48557990Seric { 48657990Seric smtpquit(m, mci, e); 4874797Seric return (EX_TEMPFAIL); 48857990Seric } 4897963Seric else if (r == 554) 49057990Seric { 49157990Seric smtprset(m, mci, e); 4927963Seric return (EX_UNAVAILABLE); 49357990Seric } 4947963Seric else if (r != 354) 49557990Seric { 49658008Seric #ifdef LOG 49758020Seric if (LogLevel > 1) 49858008Seric { 499*67860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s", 500*67860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 50158008Seric } 50258008Seric #endif 50357990Seric smtprset(m, mci, e); 5047964Seric return (EX_PROTOCOL); 50557990Seric } 50610175Seric 50763757Seric /* 50863757Seric ** Set timeout around data writes. Make it at least large 50963757Seric ** enough for DNS timeouts on all recipients plus some fudge 51063757Seric ** factor. The main thing is that it should not be infinite. 51163757Seric */ 51263757Seric 51363753Seric if (setjmp(CtxDataTimeout) != 0) 51463753Seric { 51563753Seric mci->mci_errno = errno; 51663753Seric mci->mci_exitstat = EX_TEMPFAIL; 51763753Seric mci->mci_state = MCIS_ERROR; 51863753Seric syserr("451 timeout writing message to %s", mci->mci_host); 51963753Seric smtpquit(m, mci, e); 52063753Seric return EX_TEMPFAIL; 52163753Seric } 52263753Seric 52363787Seric timeout = e->e_msgsize / 16; 52463787Seric if (timeout < (time_t) 60) 52563787Seric timeout = (time_t) 60; 52663787Seric timeout += e->e_nrcpts * 90; 52763753Seric ev = setevent(timeout, datatimeout, 0); 52863753Seric 52967546Seric /* 53067546Seric ** Output the actual message. 53167546Seric */ 53267546Seric 53367546Seric if (!bitset(MCIF_8BITMIME, mci->mci_flags) && 53467546Seric e->e_bodytype != NULL && 53567546Seric strcasecmp(e->e_bodytype, "7bit") != 0) 53667546Seric { 53767546Seric /* must convert from 8bit MIME format to 7bit encoded */ 53867546Seric mci->mci_flags |= MCIF_CVT8TO7; 53967546Seric } 54067546Seric 54167546Seric (*e->e_puthdr)(mci, e->e_header, e); 54265870Seric (*e->e_putbody)(mci, e, NULL); 54310175Seric 54467546Seric /* 54567546Seric ** Cleanup after sending message. 54667546Seric */ 54767546Seric 54863753Seric clrevent(ev); 54963753Seric 55064718Seric if (ferror(mci->mci_out)) 55164718Seric { 55264718Seric /* error during processing -- don't send the dot */ 55364718Seric mci->mci_errno = EIO; 55464718Seric mci->mci_exitstat = EX_IOERR; 55564718Seric mci->mci_state = MCIS_ERROR; 55664718Seric smtpquit(m, mci, e); 55764718Seric return EX_IOERR; 55864718Seric } 55964718Seric 56010175Seric /* terminate the message */ 56153740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 56263753Seric if (TrafficLogFile != NULL) 56363753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 56458120Seric if (Verbose) 56558151Seric nmessage(">>> ."); 56610175Seric 56710175Seric /* check for the results of the transaction */ 56861093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 56953751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 57059285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 57153751Seric if (r < 0) 57257990Seric { 57357990Seric smtpquit(m, mci, e); 5744797Seric return (EX_TEMPFAIL); 57557990Seric } 57653751Seric mci->mci_state = MCIS_OPEN; 57758917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 57853751Seric if (REPLYTYPE(r) == 4) 57953751Seric return (EX_TEMPFAIL); 5807963Seric else if (r == 250) 5817963Seric return (EX_OK); 5827963Seric else if (r == 552 || r == 554) 5837963Seric return (EX_UNAVAILABLE); 58458008Seric #ifdef LOG 58558020Seric if (LogLevel > 1) 58658008Seric { 587*67860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s", 588*67860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 58958008Seric } 59058008Seric #endif 5917964Seric return (EX_PROTOCOL); 5924684Seric } 59363753Seric 59463753Seric 59563753Seric static int 59663753Seric datatimeout() 59763753Seric { 59863753Seric longjmp(CtxDataTimeout, 1); 59963753Seric } 6004684Seric /* 6014865Seric ** SMTPQUIT -- close the SMTP connection. 6024865Seric ** 6034865Seric ** Parameters: 60415535Seric ** m -- a pointer to the mailer. 6054865Seric ** 6064865Seric ** Returns: 6074865Seric ** none. 6084865Seric ** 6094865Seric ** Side Effects: 6104865Seric ** sends the final protocol and closes the connection. 6114865Seric */ 6124865Seric 61353751Seric smtpquit(m, mci, e) 61453751Seric register MAILER *m; 61554967Seric register MCI *mci; 61653751Seric ENVELOPE *e; 6174865Seric { 61864822Seric bool oldSuprErrs = SuprErrs; 6194865Seric 62064822Seric /* 62164822Seric ** Suppress errors here -- we may be processing a different 62264822Seric ** job when we do the quit connection, and we don't want the 62364822Seric ** new job to be penalized for something that isn't it's 62464822Seric ** problem. 62564822Seric */ 62664822Seric 62764822Seric SuprErrs = TRUE; 62864822Seric 62954967Seric /* send the quit message if we haven't gotten I/O error */ 63053751Seric if (mci->mci_state != MCIS_ERROR) 6319391Seric { 63261093Seric SmtpPhase = "client QUIT"; 63353751Seric smtpmessage("QUIT", m, mci); 63459285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 63564822Seric SuprErrs = oldSuprErrs; 63653740Seric if (mci->mci_state == MCIS_CLOSED) 63764822Seric { 63864822Seric SuprErrs = oldSuprErrs; 63910159Seric return; 64064822Seric } 6419391Seric } 6429391Seric 64352676Seric /* now actually close the connection and pick up the zombie */ 64465194Seric (void) endmailer(mci, e, NULL); 64564822Seric 64664822Seric SuprErrs = oldSuprErrs; 6474865Seric } 6484865Seric /* 64954967Seric ** SMTPRSET -- send a RSET (reset) command 65054967Seric */ 65154967Seric 65254967Seric smtprset(m, mci, e) 65354967Seric register MAILER *m; 65454967Seric register MCI *mci; 65554967Seric ENVELOPE *e; 65654967Seric { 65754967Seric int r; 65854967Seric 65961093Seric SmtpPhase = "client RSET"; 66054967Seric smtpmessage("RSET", m, mci); 66159285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 66257734Seric if (r < 0) 66357734Seric mci->mci_state = MCIS_ERROR; 66454967Seric else if (REPLYTYPE(r) == 2) 66557734Seric { 66657734Seric mci->mci_state = MCIS_OPEN; 66757734Seric return; 66857734Seric } 66957734Seric smtpquit(m, mci, e); 67054967Seric } 67154967Seric /* 67258867Seric ** SMTPPROBE -- check the connection state 67354967Seric */ 67454967Seric 67558867Seric smtpprobe(mci) 67654967Seric register MCI *mci; 67754967Seric { 67854967Seric int r; 67954967Seric MAILER *m = mci->mci_mailer; 68054967Seric extern ENVELOPE BlankEnvelope; 68154967Seric ENVELOPE *e = &BlankEnvelope; 68254967Seric 68361093Seric SmtpPhase = "client probe"; 68458867Seric smtpmessage("RSET", m, mci); 68559285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 68658061Seric if (r < 0 || REPLYTYPE(r) != 2) 68754967Seric smtpquit(m, mci, e); 68854967Seric return r; 68954967Seric } 69054967Seric /* 6914684Seric ** REPLY -- read arpanet reply 6924684Seric ** 6934684Seric ** Parameters: 69410175Seric ** m -- the mailer we are reading the reply from. 69557379Seric ** mci -- the mailer connection info structure. 69657379Seric ** e -- the current envelope. 69757379Seric ** timeout -- the timeout for reads. 69859285Seric ** pfunc -- processing function for second and subsequent 69959285Seric ** lines of response -- if null, no special 70059285Seric ** processing is done. 7014684Seric ** 7024684Seric ** Returns: 7034684Seric ** reply code it reads. 7044684Seric ** 7054684Seric ** Side Effects: 7064684Seric ** flushes the mail file. 7074684Seric */ 7084684Seric 70959285Seric reply(m, mci, e, timeout, pfunc) 71053751Seric MAILER *m; 71154967Seric MCI *mci; 71253751Seric ENVELOPE *e; 71359285Seric time_t timeout; 71459285Seric void (*pfunc)(); 7154684Seric { 71659014Seric register char *bufp; 71759014Seric register int r; 71859285Seric bool firstline = TRUE; 71958957Seric char junkbuf[MAXLINE]; 72058957Seric 72157379Seric if (mci->mci_out != NULL) 72257379Seric (void) fflush(mci->mci_out); 7234684Seric 7247677Seric if (tTd(18, 1)) 7254796Seric printf("reply\n"); 7264796Seric 7277356Seric /* 7287356Seric ** Read the input line, being careful not to hang. 7297356Seric */ 7307356Seric 73159014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 7324684Seric { 7337356Seric register char *p; 73453751Seric extern time_t curtime(); 7354684Seric 7367685Seric /* actually do the read */ 73753751Seric if (e->e_xfp != NULL) 73853751Seric (void) fflush(e->e_xfp); /* for debugging */ 7397356Seric 74010054Seric /* if we are in the process of closing just give the code */ 74153740Seric if (mci->mci_state == MCIS_CLOSED) 74210054Seric return (SMTPCLOSING); 74310054Seric 74458680Seric if (mci->mci_out != NULL) 74558680Seric fflush(mci->mci_out); 74658680Seric 74710054Seric /* get the line from the other side */ 74861093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 74953751Seric mci->mci_lastuse = curtime(); 75053751Seric 75110054Seric if (p == NULL) 75210131Seric { 75363753Seric bool oldholderrs; 75410148Seric extern char MsgBuf[]; /* err.c */ 75510148Seric 75621065Seric /* if the remote end closed early, fake an error */ 75721065Seric if (errno == 0) 75821065Seric # ifdef ECONNRESET 75921065Seric errno = ECONNRESET; 76056795Seric # else /* ECONNRESET */ 76121065Seric errno = EPIPE; 76256795Seric # endif /* ECONNRESET */ 76321065Seric 76457379Seric mci->mci_errno = errno; 76557642Seric mci->mci_exitstat = EX_TEMPFAIL; 76663753Seric oldholderrs = HoldErrs; 76763753Seric HoldErrs = TRUE; 76863753Seric usrerr("451 reply: read error from %s", mci->mci_host); 76963753Seric 77010420Seric /* if debugging, pause so we can see state */ 77110420Seric if (tTd(18, 100)) 77210420Seric pause(); 77354967Seric mci->mci_state = MCIS_ERROR; 77453751Seric smtpquit(m, mci, e); 77563753Seric #ifdef XDEBUG 77663753Seric { 77763753Seric char wbuf[MAXLINE]; 77864082Seric char *p = wbuf; 77964082Seric if (e->e_to != NULL) 78064082Seric { 78164082Seric sprintf(p, "%s... ", e->e_to); 78264082Seric p += strlen(p); 78364082Seric } 78464082Seric sprintf(p, "reply(%s) during %s", 78564082Seric mci->mci_host, SmtpPhase); 78663753Seric checkfd012(wbuf); 78763753Seric } 78863753Seric #endif 78963753Seric HoldErrs = oldholderrs; 79010054Seric return (-1); 79110131Seric } 79258957Seric fixcrlf(bufp, TRUE); 79310054Seric 79463753Seric /* EHLO failure is not a real error */ 79563753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 79663753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 79714900Seric { 79814900Seric /* serious error -- log the previous command */ 79964071Seric if (SmtpNeedIntro) 80064071Seric { 80164071Seric /* inform user who we are chatting with */ 80264071Seric fprintf(CurEnv->e_xfp, 80364071Seric "... while talking to %s:\n", 80464071Seric CurHostName); 80564071Seric SmtpNeedIntro = FALSE; 80664071Seric } 80759014Seric if (SmtpMsgBuffer[0] != '\0') 80859014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 80959014Seric SmtpMsgBuffer[0] = '\0'; 81014900Seric 81114900Seric /* now log the message as from the other side */ 81258957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 81314900Seric } 81414900Seric 81514900Seric /* display the input for verbose mode */ 81658120Seric if (Verbose) 81759956Seric nmessage("050 %s", bufp); 8187356Seric 81959285Seric /* process the line */ 82059285Seric if (pfunc != NULL && !firstline) 82159285Seric (*pfunc)(bufp, m, mci, e); 82259285Seric 82359285Seric firstline = FALSE; 82459285Seric 8257356Seric /* if continuation is required, we can go on */ 82659014Seric if (bufp[3] == '-') 8274684Seric continue; 8287356Seric 82959014Seric /* ignore improperly formated input */ 83059014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 83159014Seric continue; 83259014Seric 8337356Seric /* decode the reply code */ 83459014Seric r = atoi(bufp); 8357356Seric 8367356Seric /* extra semantics: 0xx codes are "informational" */ 83759014Seric if (r >= 100) 83859014Seric break; 83959014Seric } 8407356Seric 84159014Seric /* 84259014Seric ** Now look at SmtpReplyBuffer -- only care about the first 84359014Seric ** line of the response from here on out. 84459014Seric */ 84558061Seric 84659014Seric /* save temporary failure messages for posterity */ 84759014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 84859014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8499391Seric 85059014Seric /* reply code 421 is "Service Shutting Down" */ 85159014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 85259014Seric { 85359014Seric /* send the quit protocol */ 85459014Seric mci->mci_state = MCIS_SSD; 85559014Seric smtpquit(m, mci, e); 8564684Seric } 85759014Seric 85859014Seric return (r); 8594684Seric } 8604796Seric /* 8614865Seric ** SMTPMESSAGE -- send message to server 8624796Seric ** 8634796Seric ** Parameters: 8644796Seric ** f -- format 86510175Seric ** m -- the mailer to control formatting. 8664796Seric ** a, b, c -- parameters 8674796Seric ** 8684796Seric ** Returns: 8694796Seric ** none. 8704796Seric ** 8714796Seric ** Side Effects: 87253740Seric ** writes message to mci->mci_out. 8734796Seric */ 8744796Seric 8754865Seric /*VARARGS1*/ 87657642Seric #ifdef __STDC__ 87757642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 87857642Seric #else 87957642Seric smtpmessage(f, m, mci, va_alist) 8804796Seric char *f; 88110175Seric MAILER *m; 88254967Seric MCI *mci; 88357642Seric va_dcl 88457642Seric #endif 8854796Seric { 88656852Seric VA_LOCAL_DECL 88756852Seric 88857135Seric VA_START(mci); 88956852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 89056852Seric VA_END; 89158680Seric 89258120Seric if (tTd(18, 1) || Verbose) 89358151Seric nmessage(">>> %s", SmtpMsgBuffer); 89463753Seric if (TrafficLogFile != NULL) 89563753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 89653740Seric if (mci->mci_out != NULL) 89758680Seric { 89853740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 89954967Seric m == NULL ? "\r\n" : m->m_eol); 90058680Seric } 90159149Seric else if (tTd(18, 1)) 90258725Seric { 90359149Seric printf("smtpmessage: NULL mci_out\n"); 90458725Seric } 9054796Seric } 9065182Seric 90756795Seric # endif /* SMTP */ 908