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*67995Seric static char sccsid[] = "@(#)usersmtp.c 8.31 (Berkeley) 11/27/94 (with SMTP)"; 1433731Sbostic #else 15*67995Seric static char sccsid[] = "@(#)usersmtp.c 8.31 (Berkeley) 11/27/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. 22267893Seric ** firstline -- set if this is the first line of the reply. 22360210Seric ** m -- the mailer. 22460210Seric ** mci -- the mailer connection info. 22560210Seric ** e -- the envelope. 22660210Seric ** 22760210Seric ** Returns: 22860210Seric ** none. 22960210Seric */ 23060210Seric 23160210Seric void 23267893Seric esmtp_check(line, firstline, m, mci, e) 23360210Seric char *line; 23467893Seric bool firstline; 23560210Seric MAILER *m; 23660210Seric register MCI *mci; 23760210Seric ENVELOPE *e; 23860210Seric { 23967893Seric while ((line = strchr(++line, 'E')) != NULL) 24067893Seric { 24167893Seric if (strncmp(line, "ESMTP ", 6) == 0) 24267893Seric { 24367893Seric mci->mci_flags |= MCIF_ESMTP; 24467893Seric break; 24567893Seric } 24667893Seric } 24760210Seric } 24860210Seric /* 24959285Seric ** HELO_OPTIONS -- process the options on a HELO line. 25059285Seric ** 25159285Seric ** Parameters: 25259285Seric ** line -- the response line. 25367893Seric ** firstline -- set if this is the first line of the reply. 25459285Seric ** m -- the mailer. 25559285Seric ** mci -- the mailer connection info. 25659285Seric ** e -- the envelope. 25759285Seric ** 25859285Seric ** Returns: 25959285Seric ** none. 26059285Seric */ 26153751Seric 26259285Seric void 26367893Seric helo_options(line, firstline, m, mci, e) 26459285Seric char *line; 26567893Seric bool firstline; 26659285Seric MAILER *m; 26759285Seric register MCI *mci; 26859285Seric ENVELOPE *e; 26959285Seric { 27059285Seric register char *p; 27159285Seric 27267971Seric if (firstline) 27367893Seric return; 27467893Seric 27559285Seric if (strlen(line) < 5) 27659285Seric return; 27759285Seric line += 4; 27859285Seric p = strchr(line, ' '); 27959285Seric if (p != NULL) 28059285Seric *p++ = '\0'; 28159285Seric if (strcasecmp(line, "size") == 0) 28259285Seric { 28359285Seric mci->mci_flags |= MCIF_SIZE; 28459285Seric if (p != NULL) 28559285Seric mci->mci_maxsize = atol(p); 28659285Seric } 28759285Seric else if (strcasecmp(line, "8bitmime") == 0) 28865870Seric { 28959285Seric mci->mci_flags |= MCIF_8BITMIME; 29065870Seric mci->mci_flags &= ~MCIF_7BIT; 29165870Seric } 29259285Seric else if (strcasecmp(line, "expn") == 0) 29359285Seric mci->mci_flags |= MCIF_EXPN; 29467963Seric else if (strcasecmp(line, "x-dsn-1") == 0) 29567880Seric mci->mci_flags |= MCIF_DSN; 29659285Seric } 29759285Seric /* 29859285Seric ** SMTPMAILFROM -- send MAIL command 29959285Seric ** 30059285Seric ** Parameters: 30159285Seric ** m -- the mailer. 30259285Seric ** mci -- the mailer connection structure. 30359285Seric ** e -- the envelope (including the sender to specify). 30459285Seric */ 30559285Seric 30653751Seric smtpmailfrom(m, mci, e) 30753751Seric struct mailer *m; 30854967Seric MCI *mci; 30953751Seric ENVELOPE *e; 31053751Seric { 31153751Seric int r; 31265494Seric char *bufp; 31367887Seric char *bodytype; 31453751Seric char buf[MAXNAME]; 31559285Seric char optbuf[MAXLINE]; 31653751Seric 31763753Seric if (tTd(18, 2)) 31857943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 31957943Seric 32059285Seric /* set up appropriate options to include */ 32164254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 32259285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 32359285Seric else 32459285Seric strcpy(optbuf, ""); 32559285Seric 32667887Seric bodytype = e->e_bodytype; 32767887Seric if (bitset(MCIF_8BITMIME, mci->mci_flags)) 32867417Seric { 32967887Seric if (bodytype == NULL && 33067887Seric bitset(MM_MIME8BIT, MimeMode) && 33167887Seric bitset(EF_HAS8BIT, e->e_flags) && 33267887Seric !bitnset(M_8BITS, m->m_flags)) 33367887Seric bodytype = "8BITMIME"; 33467887Seric if (bodytype != NULL) 33567417Seric { 33667417Seric strcat(optbuf, " BODY="); 33767887Seric strcat(optbuf, bodytype); 33867417Seric } 33967417Seric } 340*67995Seric else if (bitnset(M_8BITS, m->m_flags) || 341*67995Seric !bitset(EF_HAS8BIT, e->e_flags) || 342*67995Seric (e->e_bodytype != NULL && 343*67995Seric strcasecmp(e->e_bodytype, "7bit") == 0)) 34467887Seric { 34567887Seric /* just pass it through */ 34667887Seric } 34767887Seric else if (bitset(MM_CVTMIME, MimeMode) && 348*67995Seric (e->e_bodytype != NULL || !bitset(MM_PASS8BIT, MimeMode))) 34967887Seric { 35067887Seric /* must convert from 8bit MIME format to 7bit encoded */ 35167887Seric mci->mci_flags |= MCIF_CVT8TO7; 35267887Seric } 35367887Seric else if (!bitset(MM_PASS8BIT, MimeMode)) 35467887Seric { 35567887Seric /* cannot just send a 8-bit version */ 35667887Seric usrerr("%s does not support 8BITMIME", mci->mci_host); 35767887Seric return EX_DATAERR; 35867887Seric } 35967417Seric 36067963Seric if (bitset(MCIF_DSN, mci->mci_flags)) 36167880Seric { 36267963Seric if (e->e_envid != NULL) 36367963Seric { 36467963Seric strcat(optbuf, " ENVID="); 36567963Seric strcat(optbuf, e->e_envid); 36667963Seric } 36767963Seric if (e->e_omts != NULL) 36867963Seric { 36967963Seric strcat(optbuf, " OMTS="); 37067963Seric strcat(optbuf, e->e_omts); 37167963Seric } 37267880Seric } 37367880Seric 3749315Seric /* 3754865Seric ** Send the MAIL command. 3764865Seric ** Designates the sender. 3774865Seric */ 3784796Seric 37953751Seric mci->mci_state = MCIS_ACTIVE; 38053751Seric 38159540Seric if (bitset(EF_RESPONSE, e->e_flags) && 38259540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 38358680Seric (void) strcpy(buf, ""); 38458680Seric else 38558680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 38665494Seric if (buf[0] == '<') 38765494Seric { 38865494Seric /* strip off <angle brackets> (put back on below) */ 38965494Seric bufp = &buf[strlen(buf) - 1]; 39065494Seric if (*bufp == '>') 39165494Seric *bufp = '\0'; 39265494Seric bufp = &buf[1]; 39365494Seric } 39465494Seric else 39565494Seric bufp = buf; 39667472Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 39710688Seric !bitnset(M_FROMPATH, m->m_flags)) 3988436Seric { 39965494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 4008436Seric } 4018436Seric else 4028436Seric { 40359285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 40465494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 4058436Seric } 40661093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 40753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 40859285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 4098005Seric if (r < 0 || REPLYTYPE(r) == 4) 41053751Seric { 41153751Seric mci->mci_exitstat = EX_TEMPFAIL; 41253751Seric mci->mci_errno = errno; 41353751Seric smtpquit(m, mci, e); 41453751Seric return EX_TEMPFAIL; 41553751Seric } 4167963Seric else if (r == 250) 41753751Seric { 41853751Seric mci->mci_exitstat = EX_OK; 41953751Seric return EX_OK; 42053751Seric } 4217963Seric else if (r == 552) 42253751Seric { 42353751Seric /* signal service unavailable */ 42453751Seric mci->mci_exitstat = EX_UNAVAILABLE; 42553751Seric smtpquit(m, mci, e); 42653751Seric return EX_UNAVAILABLE; 42753751Seric } 42814913Seric 42958008Seric #ifdef LOG 43058020Seric if (LogLevel > 1) 43158008Seric { 43267860Seric syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s", 43367860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 43458008Seric } 43558008Seric #endif 43658008Seric 43714913Seric /* protocol error -- close up */ 43853751Seric smtpquit(m, mci, e); 43953751Seric mci->mci_exitstat = EX_PROTOCOL; 44053751Seric return EX_PROTOCOL; 4414684Seric } 4424684Seric /* 4434976Seric ** SMTPRCPT -- designate recipient. 4444797Seric ** 4454797Seric ** Parameters: 4464865Seric ** to -- address of recipient. 44710175Seric ** m -- the mailer we are sending to. 44857379Seric ** mci -- the connection info for this transaction. 44957379Seric ** e -- the envelope for this transaction. 4504797Seric ** 4514797Seric ** Returns: 4524865Seric ** exit status corresponding to recipient status. 4534797Seric ** 4544797Seric ** Side Effects: 4554865Seric ** Sends the mail via SMTP. 4564797Seric */ 4574797Seric 45853751Seric smtprcpt(to, m, mci, e) 4594865Seric ADDRESS *to; 46010175Seric register MAILER *m; 46154967Seric MCI *mci; 46253751Seric ENVELOPE *e; 4634797Seric { 4644797Seric register int r; 46567880Seric char optbuf[MAXLINE]; 4664797Seric 46767880Seric strcpy(optbuf, ""); 46867880Seric if (bitset(MCIF_DSN, mci->mci_flags)) 46967880Seric { 47067963Seric bool firstone = TRUE; 47167963Seric 47267987Seric /* NOTIFY= parameter */ 47367880Seric strcat(optbuf, " NOTIFY="); 47467987Seric if (bitset(QPINGONSUCCESS, to->q_flags)) 47567880Seric { 47667987Seric strcat(optbuf, "SUCCESS"); 47767963Seric firstone = FALSE; 47867880Seric } 47967987Seric if (bitset(QPINGONFAILURE, to->q_flags)) 48067880Seric { 48167971Seric if (!firstone) 48267963Seric strcat(optbuf, ","); 48367987Seric strcat(optbuf, "FAILURE"); 48467963Seric firstone = FALSE; 48567880Seric } 48667963Seric if (bitset(QPINGONDELAY, to->q_flags)) 48767880Seric { 48867971Seric if (!firstone) 48967963Seric strcat(optbuf, ","); 49067963Seric strcat(optbuf, "DELAY"); 49167963Seric firstone = FALSE; 49267963Seric } 49367963Seric if (firstone) 49467963Seric strcat(optbuf, "NEVER"); 49567963Seric 49667987Seric /* RET= parameter */ 49767963Seric if (bitset(QHAS_RET_PARAM, to->q_flags)) 49867963Seric { 49967880Seric strcat(optbuf, " RET="); 50067963Seric if (bitset(QRET_HDRS, to->q_flags)) 50167963Seric strcat(optbuf, "HDRS"); 50267880Seric else 50367963Seric strcat(optbuf, "FULL"); 50467880Seric } 50567987Seric 50667987Seric /* ORCPT= parameter */ 50767987Seric if (to->q_orcpt != NULL) 50867987Seric { 50967987Seric strcat(optbuf, " ORCPT="); 51067987Seric strcat(optbuf, to->q_orcpt); 51167987Seric } 51267880Seric } 51367880Seric else if (bitset(QPINGONSUCCESS, to->q_flags)) 51467880Seric { 51567880Seric to->q_flags |= QRELAYED; 51667880Seric fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n", 51767880Seric to->q_paddr); 51867880Seric } 5194865Seric 52067880Seric smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); 52167880Seric 52261093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 52353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 52459285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 52567880Seric setstatus(to, SmtpReplyBuffer); 5268005Seric if (r < 0 || REPLYTYPE(r) == 4) 5274865Seric return (EX_TEMPFAIL); 5287963Seric else if (REPLYTYPE(r) == 2) 5297963Seric return (EX_OK); 5307964Seric else if (r == 550 || r == 551 || r == 553) 5317964Seric return (EX_NOUSER); 5327964Seric else if (r == 552 || r == 554) 5337964Seric return (EX_UNAVAILABLE); 53458008Seric 53558008Seric #ifdef LOG 53658020Seric if (LogLevel > 1) 53758008Seric { 53867860Seric syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s", 53967860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 54058008Seric } 54158008Seric #endif 54258008Seric 5437964Seric return (EX_PROTOCOL); 5444797Seric } 5454797Seric /* 54610175Seric ** SMTPDATA -- send the data and clean up the transaction. 5474684Seric ** 5484684Seric ** Parameters: 5494865Seric ** m -- mailer being sent to. 5506980Seric ** e -- the envelope for this message. 5514684Seric ** 5524684Seric ** Returns: 5534976Seric ** exit status corresponding to DATA command. 5544684Seric ** 5554684Seric ** Side Effects: 5564865Seric ** none. 5574684Seric */ 5584684Seric 55963753Seric static jmp_buf CtxDataTimeout; 56063937Seric static int datatimeout(); 56163753Seric 56253740Seric smtpdata(m, mci, e) 5634865Seric struct mailer *m; 56454967Seric register MCI *mci; 5656980Seric register ENVELOPE *e; 5664684Seric { 5674684Seric register int r; 56863753Seric register EVENT *ev; 56963753Seric time_t timeout; 5704684Seric 5714797Seric /* 5724797Seric ** Send the data. 57310175Seric ** First send the command and check that it is ok. 57410175Seric ** Then send the data. 57510175Seric ** Follow it up with a dot to terminate. 57610175Seric ** Finally get the results of the transaction. 5774797Seric */ 5784797Seric 57910175Seric /* send the command and check ok to proceed */ 58053751Seric smtpmessage("DATA", m, mci); 58161093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 58253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 58359285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 5848005Seric if (r < 0 || REPLYTYPE(r) == 4) 58557990Seric { 58657990Seric smtpquit(m, mci, e); 5874797Seric return (EX_TEMPFAIL); 58857990Seric } 5897963Seric else if (r == 554) 59057990Seric { 59157990Seric smtprset(m, mci, e); 5927963Seric return (EX_UNAVAILABLE); 59357990Seric } 5947963Seric else if (r != 354) 59557990Seric { 59658008Seric #ifdef LOG 59758020Seric if (LogLevel > 1) 59858008Seric { 59967860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s", 60067860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 60158008Seric } 60258008Seric #endif 60357990Seric smtprset(m, mci, e); 6047964Seric return (EX_PROTOCOL); 60557990Seric } 60610175Seric 60763757Seric /* 60863757Seric ** Set timeout around data writes. Make it at least large 60963757Seric ** enough for DNS timeouts on all recipients plus some fudge 61063757Seric ** factor. The main thing is that it should not be infinite. 61163757Seric */ 61263757Seric 61363753Seric if (setjmp(CtxDataTimeout) != 0) 61463753Seric { 61563753Seric mci->mci_errno = errno; 61663753Seric mci->mci_exitstat = EX_TEMPFAIL; 61763753Seric mci->mci_state = MCIS_ERROR; 61863753Seric syserr("451 timeout writing message to %s", mci->mci_host); 61963753Seric smtpquit(m, mci, e); 62063753Seric return EX_TEMPFAIL; 62163753Seric } 62263753Seric 62363787Seric timeout = e->e_msgsize / 16; 62463787Seric if (timeout < (time_t) 60) 62563787Seric timeout = (time_t) 60; 62663787Seric timeout += e->e_nrcpts * 90; 62763753Seric ev = setevent(timeout, datatimeout, 0); 62863753Seric 62967546Seric /* 63067546Seric ** Output the actual message. 63167546Seric */ 63267546Seric 63367936Seric (*e->e_puthdr)(mci, e->e_header, e, 0); 63467936Seric (*e->e_putbody)(mci, e, NULL, 0); 63510175Seric 63667546Seric /* 63767546Seric ** Cleanup after sending message. 63867546Seric */ 63967546Seric 64063753Seric clrevent(ev); 64163753Seric 64264718Seric if (ferror(mci->mci_out)) 64364718Seric { 64464718Seric /* error during processing -- don't send the dot */ 64564718Seric mci->mci_errno = EIO; 64664718Seric mci->mci_exitstat = EX_IOERR; 64764718Seric mci->mci_state = MCIS_ERROR; 64864718Seric smtpquit(m, mci, e); 64964718Seric return EX_IOERR; 65064718Seric } 65164718Seric 65210175Seric /* terminate the message */ 65353740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 65463753Seric if (TrafficLogFile != NULL) 65563753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 65658120Seric if (Verbose) 65758151Seric nmessage(">>> ."); 65810175Seric 65910175Seric /* check for the results of the transaction */ 66061093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 66153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 66259285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 66353751Seric if (r < 0) 66457990Seric { 66557990Seric smtpquit(m, mci, e); 6664797Seric return (EX_TEMPFAIL); 66757990Seric } 66853751Seric mci->mci_state = MCIS_OPEN; 66958917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 67053751Seric if (REPLYTYPE(r) == 4) 67153751Seric return (EX_TEMPFAIL); 6727963Seric else if (r == 250) 6737963Seric return (EX_OK); 6747963Seric else if (r == 552 || r == 554) 6757963Seric return (EX_UNAVAILABLE); 67658008Seric #ifdef LOG 67758020Seric if (LogLevel > 1) 67858008Seric { 67967860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s", 68067860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 68158008Seric } 68258008Seric #endif 6837964Seric return (EX_PROTOCOL); 6844684Seric } 68563753Seric 68663753Seric 68763753Seric static int 68863753Seric datatimeout() 68963753Seric { 69063753Seric longjmp(CtxDataTimeout, 1); 69163753Seric } 6924684Seric /* 6934865Seric ** SMTPQUIT -- close the SMTP connection. 6944865Seric ** 6954865Seric ** Parameters: 69615535Seric ** m -- a pointer to the mailer. 6974865Seric ** 6984865Seric ** Returns: 6994865Seric ** none. 7004865Seric ** 7014865Seric ** Side Effects: 7024865Seric ** sends the final protocol and closes the connection. 7034865Seric */ 7044865Seric 70553751Seric smtpquit(m, mci, e) 70653751Seric register MAILER *m; 70754967Seric register MCI *mci; 70853751Seric ENVELOPE *e; 7094865Seric { 71064822Seric bool oldSuprErrs = SuprErrs; 7114865Seric 71264822Seric /* 71364822Seric ** Suppress errors here -- we may be processing a different 71464822Seric ** job when we do the quit connection, and we don't want the 71564822Seric ** new job to be penalized for something that isn't it's 71664822Seric ** problem. 71764822Seric */ 71864822Seric 71964822Seric SuprErrs = TRUE; 72064822Seric 72154967Seric /* send the quit message if we haven't gotten I/O error */ 72253751Seric if (mci->mci_state != MCIS_ERROR) 7239391Seric { 72461093Seric SmtpPhase = "client QUIT"; 72553751Seric smtpmessage("QUIT", m, mci); 72659285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 72764822Seric SuprErrs = oldSuprErrs; 72853740Seric if (mci->mci_state == MCIS_CLOSED) 72964822Seric { 73064822Seric SuprErrs = oldSuprErrs; 73110159Seric return; 73264822Seric } 7339391Seric } 7349391Seric 73552676Seric /* now actually close the connection and pick up the zombie */ 73665194Seric (void) endmailer(mci, e, NULL); 73764822Seric 73864822Seric SuprErrs = oldSuprErrs; 7394865Seric } 7404865Seric /* 74154967Seric ** SMTPRSET -- send a RSET (reset) command 74254967Seric */ 74354967Seric 74454967Seric smtprset(m, mci, e) 74554967Seric register MAILER *m; 74654967Seric register MCI *mci; 74754967Seric ENVELOPE *e; 74854967Seric { 74954967Seric int r; 75054967Seric 75161093Seric SmtpPhase = "client RSET"; 75254967Seric smtpmessage("RSET", m, mci); 75359285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 75457734Seric if (r < 0) 75557734Seric mci->mci_state = MCIS_ERROR; 75654967Seric else if (REPLYTYPE(r) == 2) 75757734Seric { 75857734Seric mci->mci_state = MCIS_OPEN; 75957734Seric return; 76057734Seric } 76157734Seric smtpquit(m, mci, e); 76254967Seric } 76354967Seric /* 76458867Seric ** SMTPPROBE -- check the connection state 76554967Seric */ 76654967Seric 76758867Seric smtpprobe(mci) 76854967Seric register MCI *mci; 76954967Seric { 77054967Seric int r; 77154967Seric MAILER *m = mci->mci_mailer; 77254967Seric extern ENVELOPE BlankEnvelope; 77354967Seric ENVELOPE *e = &BlankEnvelope; 77454967Seric 77561093Seric SmtpPhase = "client probe"; 77658867Seric smtpmessage("RSET", m, mci); 77759285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 77858061Seric if (r < 0 || REPLYTYPE(r) != 2) 77954967Seric smtpquit(m, mci, e); 78054967Seric return r; 78154967Seric } 78254967Seric /* 7834684Seric ** REPLY -- read arpanet reply 7844684Seric ** 7854684Seric ** Parameters: 78610175Seric ** m -- the mailer we are reading the reply from. 78757379Seric ** mci -- the mailer connection info structure. 78857379Seric ** e -- the current envelope. 78957379Seric ** timeout -- the timeout for reads. 79059285Seric ** pfunc -- processing function for second and subsequent 79159285Seric ** lines of response -- if null, no special 79259285Seric ** processing is done. 7934684Seric ** 7944684Seric ** Returns: 7954684Seric ** reply code it reads. 7964684Seric ** 7974684Seric ** Side Effects: 7984684Seric ** flushes the mail file. 7994684Seric */ 8004684Seric 80159285Seric reply(m, mci, e, timeout, pfunc) 80253751Seric MAILER *m; 80354967Seric MCI *mci; 80453751Seric ENVELOPE *e; 80559285Seric time_t timeout; 80659285Seric void (*pfunc)(); 8074684Seric { 80859014Seric register char *bufp; 80959014Seric register int r; 81059285Seric bool firstline = TRUE; 81158957Seric char junkbuf[MAXLINE]; 81258957Seric 81357379Seric if (mci->mci_out != NULL) 81457379Seric (void) fflush(mci->mci_out); 8154684Seric 8167677Seric if (tTd(18, 1)) 8174796Seric printf("reply\n"); 8184796Seric 8197356Seric /* 8207356Seric ** Read the input line, being careful not to hang. 8217356Seric */ 8227356Seric 82359014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 8244684Seric { 8257356Seric register char *p; 82653751Seric extern time_t curtime(); 8274684Seric 8287685Seric /* actually do the read */ 82953751Seric if (e->e_xfp != NULL) 83053751Seric (void) fflush(e->e_xfp); /* for debugging */ 8317356Seric 83210054Seric /* if we are in the process of closing just give the code */ 83353740Seric if (mci->mci_state == MCIS_CLOSED) 83410054Seric return (SMTPCLOSING); 83510054Seric 83658680Seric if (mci->mci_out != NULL) 83758680Seric fflush(mci->mci_out); 83858680Seric 83910054Seric /* get the line from the other side */ 84061093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 84153751Seric mci->mci_lastuse = curtime(); 84253751Seric 84310054Seric if (p == NULL) 84410131Seric { 84563753Seric bool oldholderrs; 84610148Seric extern char MsgBuf[]; /* err.c */ 84710148Seric 84821065Seric /* if the remote end closed early, fake an error */ 84921065Seric if (errno == 0) 85021065Seric # ifdef ECONNRESET 85121065Seric errno = ECONNRESET; 85256795Seric # else /* ECONNRESET */ 85321065Seric errno = EPIPE; 85456795Seric # endif /* ECONNRESET */ 85521065Seric 85657379Seric mci->mci_errno = errno; 85757642Seric mci->mci_exitstat = EX_TEMPFAIL; 85863753Seric oldholderrs = HoldErrs; 85963753Seric HoldErrs = TRUE; 86063753Seric usrerr("451 reply: read error from %s", mci->mci_host); 86163753Seric 86210420Seric /* if debugging, pause so we can see state */ 86310420Seric if (tTd(18, 100)) 86410420Seric pause(); 86554967Seric mci->mci_state = MCIS_ERROR; 86653751Seric smtpquit(m, mci, e); 86763753Seric #ifdef XDEBUG 86863753Seric { 86963753Seric char wbuf[MAXLINE]; 87064082Seric char *p = wbuf; 87164082Seric if (e->e_to != NULL) 87264082Seric { 87364082Seric sprintf(p, "%s... ", e->e_to); 87464082Seric p += strlen(p); 87564082Seric } 87664082Seric sprintf(p, "reply(%s) during %s", 87764082Seric mci->mci_host, SmtpPhase); 87863753Seric checkfd012(wbuf); 87963753Seric } 88063753Seric #endif 88163753Seric HoldErrs = oldholderrs; 88210054Seric return (-1); 88310131Seric } 88458957Seric fixcrlf(bufp, TRUE); 88510054Seric 88663753Seric /* EHLO failure is not a real error */ 88763753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 88863753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 88914900Seric { 89014900Seric /* serious error -- log the previous command */ 89164071Seric if (SmtpNeedIntro) 89264071Seric { 89364071Seric /* inform user who we are chatting with */ 89464071Seric fprintf(CurEnv->e_xfp, 89564071Seric "... while talking to %s:\n", 89664071Seric CurHostName); 89764071Seric SmtpNeedIntro = FALSE; 89864071Seric } 89959014Seric if (SmtpMsgBuffer[0] != '\0') 90059014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 90159014Seric SmtpMsgBuffer[0] = '\0'; 90214900Seric 90314900Seric /* now log the message as from the other side */ 90458957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 90514900Seric } 90614900Seric 90714900Seric /* display the input for verbose mode */ 90858120Seric if (Verbose) 90959956Seric nmessage("050 %s", bufp); 9107356Seric 91159285Seric /* process the line */ 91267893Seric if (pfunc != NULL) 91367893Seric (*pfunc)(bufp, firstline, m, mci, e); 91459285Seric 91559285Seric firstline = FALSE; 91659285Seric 9177356Seric /* if continuation is required, we can go on */ 91859014Seric if (bufp[3] == '-') 9194684Seric continue; 9207356Seric 92159014Seric /* ignore improperly formated input */ 92259014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 92359014Seric continue; 92459014Seric 9257356Seric /* decode the reply code */ 92659014Seric r = atoi(bufp); 9277356Seric 9287356Seric /* extra semantics: 0xx codes are "informational" */ 92959014Seric if (r >= 100) 93059014Seric break; 93159014Seric } 9327356Seric 93359014Seric /* 93459014Seric ** Now look at SmtpReplyBuffer -- only care about the first 93559014Seric ** line of the response from here on out. 93659014Seric */ 93758061Seric 93859014Seric /* save temporary failure messages for posterity */ 93959014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 94059014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 9419391Seric 94259014Seric /* reply code 421 is "Service Shutting Down" */ 94359014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 94459014Seric { 94559014Seric /* send the quit protocol */ 94659014Seric mci->mci_state = MCIS_SSD; 94759014Seric smtpquit(m, mci, e); 9484684Seric } 94959014Seric 95059014Seric return (r); 9514684Seric } 9524796Seric /* 9534865Seric ** SMTPMESSAGE -- send message to server 9544796Seric ** 9554796Seric ** Parameters: 9564796Seric ** f -- format 95710175Seric ** m -- the mailer to control formatting. 9584796Seric ** a, b, c -- parameters 9594796Seric ** 9604796Seric ** Returns: 9614796Seric ** none. 9624796Seric ** 9634796Seric ** Side Effects: 96453740Seric ** writes message to mci->mci_out. 9654796Seric */ 9664796Seric 9674865Seric /*VARARGS1*/ 96857642Seric #ifdef __STDC__ 96957642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 97057642Seric #else 97157642Seric smtpmessage(f, m, mci, va_alist) 9724796Seric char *f; 97310175Seric MAILER *m; 97454967Seric MCI *mci; 97557642Seric va_dcl 97657642Seric #endif 9774796Seric { 97856852Seric VA_LOCAL_DECL 97956852Seric 98057135Seric VA_START(mci); 98156852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 98256852Seric VA_END; 98358680Seric 98458120Seric if (tTd(18, 1) || Verbose) 98558151Seric nmessage(">>> %s", SmtpMsgBuffer); 98663753Seric if (TrafficLogFile != NULL) 98763753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 98853740Seric if (mci->mci_out != NULL) 98958680Seric { 99053740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 99154967Seric m == NULL ? "\r\n" : m->m_eol); 99258680Seric } 99359149Seric else if (tTd(18, 1)) 99458725Seric { 99559149Seric printf("smtpmessage: NULL mci_out\n"); 99658725Seric } 9974796Seric } 9985182Seric 99956795Seric # endif /* SMTP */ 1000