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*68528Seric static char sccsid[] = "@(#)usersmtp.c 8.37 (Berkeley) 03/14/95 (with SMTP)"; 1433731Sbostic #else 15*68528Seric static char sccsid[] = "@(#)usersmtp.c 8.37 (Berkeley) 03/14/95 (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 */ 8268100Seric if (CurHostName == NULL) 8368100Seric CurHostName = MyHostName; 8464071Seric SmtpNeedIntro = TRUE; 8554967Seric switch (mci->mci_state) 866051Seric { 8754967Seric case MCIS_ACTIVE: 8854967Seric /* need to clear old information */ 8954967Seric smtprset(m, mci, e); 9057734Seric /* fall through */ 9115139Seric 9254967Seric case MCIS_OPEN: 9354967Seric return; 9454967Seric 9554967Seric case MCIS_ERROR: 9654967Seric case MCIS_SSD: 9754967Seric /* shouldn't happen */ 9854967Seric smtpquit(m, mci, e); 9957734Seric /* fall through */ 10054967Seric 10154967Seric case MCIS_CLOSED: 10258151Seric syserr("451 smtpinit: state CLOSED"); 10354967Seric return; 10454967Seric 10554967Seric case MCIS_OPENING: 10654967Seric break; 1076051Seric } 1084796Seric 10954967Seric mci->mci_state = MCIS_OPENING; 11054967Seric 1114865Seric /* 1124865Seric ** Get the greeting message. 11314913Seric ** This should appear spontaneously. Give it five minutes to 11414886Seric ** happen. 1154865Seric */ 1164797Seric 11761093Seric SmtpPhase = mci->mci_phase = "client greeting"; 11853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11960210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 12064750Seric if (r < 0 || REPLYTYPE(r) == 4) 12152104Seric goto tempfail1; 12264750Seric if (REPLYTYPE(r) != 2) 12364750Seric goto unavailable; 1244684Seric 1254865Seric /* 1264976Seric ** Send the HELO command. 1277963Seric ** My mother taught me to always introduce myself. 1284976Seric */ 1294976Seric 13059285Seric if (bitnset(M_ESMTP, m->m_flags)) 13159285Seric mci->mci_flags |= MCIF_ESMTP; 13259285Seric 13359285Seric tryhelo: 13459285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 13559285Seric { 13659285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13761093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13859285Seric } 13959285Seric else 14059285Seric { 14159285Seric smtpmessage("HELO %s", m, mci, MyHostName); 14261093Seric SmtpPhase = mci->mci_phase = "client HELO"; 14359285Seric } 14453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 14559285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1468005Seric if (r < 0) 14752104Seric goto tempfail1; 1488005Seric else if (REPLYTYPE(r) == 5) 14959285Seric { 15059285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 15159285Seric { 15259285Seric /* try old SMTP instead */ 15359285Seric mci->mci_flags &= ~MCIF_ESMTP; 15459285Seric goto tryhelo; 15559285Seric } 15614913Seric goto unavailable; 15759285Seric } 1587963Seric else if (REPLYTYPE(r) != 2) 15952104Seric goto tempfail1; 1604976Seric 1614976Seric /* 16258957Seric ** Check to see if we actually ended up talking to ourself. 16358957Seric ** This means we didn't know about an alias or MX, or we managed 16458957Seric ** to connect to an echo server. 16558957Seric */ 16658957Seric 16759026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16858957Seric if (p != NULL) 16961707Seric *p = '\0'; 17067472Seric if (!bitnset(M_NOLOOPCHECK, m->m_flags) && 17167472Seric strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17258957Seric { 17358957Seric syserr("553 %s config error: mail loops back to myself", 17458957Seric MyHostName); 17558957Seric mci->mci_exitstat = EX_CONFIG; 17658957Seric mci->mci_errno = 0; 17758957Seric smtpquit(m, mci, e); 17858957Seric return; 17958957Seric } 18058957Seric 18158957Seric /* 1829315Seric ** If this is expected to be another sendmail, send some internal 1839315Seric ** commands. 1849315Seric */ 1859315Seric 18610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1879315Seric { 1889315Seric /* tell it to be verbose */ 18953751Seric smtpmessage("VERB", m, mci); 19059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1919315Seric if (r < 0) 19252104Seric goto tempfail2; 1939315Seric } 1949315Seric 19565057Seric if (mci->mci_state != MCIS_CLOSED) 19665057Seric { 19765057Seric mci->mci_state = MCIS_OPEN; 19865057Seric return; 19965057Seric } 20053751Seric 20165057Seric /* got a 421 error code during startup */ 20265057Seric 20353751Seric tempfail1: 20453751Seric tempfail2: 20553751Seric mci->mci_exitstat = EX_TEMPFAIL; 20657379Seric if (mci->mci_errno == 0) 20757379Seric mci->mci_errno = errno; 20857379Seric if (mci->mci_state != MCIS_CLOSED) 20957379Seric smtpquit(m, mci, e); 21054967Seric return; 21153751Seric 21253751Seric unavailable: 21353751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21453751Seric mci->mci_errno = errno; 21553751Seric smtpquit(m, mci, e); 21654967Seric return; 21753751Seric } 21859285Seric /* 21960210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 22060210Seric ** 22160210Seric ** 22260210Seric ** Parameters: 22360210Seric ** line -- the response line. 22467893Seric ** firstline -- set if this is the first line of the reply. 22560210Seric ** m -- the mailer. 22660210Seric ** mci -- the mailer connection info. 22760210Seric ** e -- the envelope. 22860210Seric ** 22960210Seric ** Returns: 23060210Seric ** none. 23160210Seric */ 23260210Seric 23360210Seric void 23467893Seric esmtp_check(line, firstline, m, mci, e) 23560210Seric char *line; 23667893Seric bool firstline; 23760210Seric MAILER *m; 23860210Seric register MCI *mci; 23960210Seric ENVELOPE *e; 24060210Seric { 24167893Seric while ((line = strchr(++line, 'E')) != NULL) 24267893Seric { 24367893Seric if (strncmp(line, "ESMTP ", 6) == 0) 24467893Seric { 24567893Seric mci->mci_flags |= MCIF_ESMTP; 24667893Seric break; 24767893Seric } 24867893Seric } 24960210Seric } 25060210Seric /* 25159285Seric ** HELO_OPTIONS -- process the options on a HELO line. 25259285Seric ** 25359285Seric ** Parameters: 25459285Seric ** line -- the response line. 25567893Seric ** firstline -- set if this is the first line of the reply. 25659285Seric ** m -- the mailer. 25759285Seric ** mci -- the mailer connection info. 25859285Seric ** e -- the envelope. 25959285Seric ** 26059285Seric ** Returns: 26159285Seric ** none. 26259285Seric */ 26353751Seric 26459285Seric void 26567893Seric helo_options(line, firstline, m, mci, e) 26659285Seric char *line; 26767893Seric bool firstline; 26859285Seric MAILER *m; 26959285Seric register MCI *mci; 27059285Seric ENVELOPE *e; 27159285Seric { 27259285Seric register char *p; 27359285Seric 27467971Seric if (firstline) 27567893Seric return; 27667893Seric 27759285Seric if (strlen(line) < 5) 27859285Seric return; 27959285Seric line += 4; 28059285Seric p = strchr(line, ' '); 28159285Seric if (p != NULL) 28259285Seric *p++ = '\0'; 28359285Seric if (strcasecmp(line, "size") == 0) 28459285Seric { 28559285Seric mci->mci_flags |= MCIF_SIZE; 28659285Seric if (p != NULL) 28759285Seric mci->mci_maxsize = atol(p); 28859285Seric } 28959285Seric else if (strcasecmp(line, "8bitmime") == 0) 29065870Seric { 29159285Seric mci->mci_flags |= MCIF_8BITMIME; 29265870Seric mci->mci_flags &= ~MCIF_7BIT; 29365870Seric } 29459285Seric else if (strcasecmp(line, "expn") == 0) 29559285Seric mci->mci_flags |= MCIF_EXPN; 29667963Seric else if (strcasecmp(line, "x-dsn-1") == 0) 29767880Seric mci->mci_flags |= MCIF_DSN; 29859285Seric } 29959285Seric /* 30059285Seric ** SMTPMAILFROM -- send MAIL command 30159285Seric ** 30259285Seric ** Parameters: 30359285Seric ** m -- the mailer. 30459285Seric ** mci -- the mailer connection structure. 30559285Seric ** e -- the envelope (including the sender to specify). 30659285Seric */ 30759285Seric 30853751Seric smtpmailfrom(m, mci, e) 30953751Seric struct mailer *m; 31054967Seric MCI *mci; 31153751Seric ENVELOPE *e; 31253751Seric { 31353751Seric int r; 31465494Seric char *bufp; 31567887Seric char *bodytype; 316*68528Seric char buf[MAXNAME + 1]; 31759285Seric char optbuf[MAXLINE]; 31853751Seric 31963753Seric if (tTd(18, 2)) 32057943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 32157943Seric 32259285Seric /* set up appropriate options to include */ 32364254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 32459285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 32559285Seric else 32659285Seric strcpy(optbuf, ""); 32759285Seric 32867887Seric bodytype = e->e_bodytype; 32967887Seric if (bitset(MCIF_8BITMIME, mci->mci_flags)) 33067417Seric { 33167887Seric if (bodytype == NULL && 33267887Seric bitset(MM_MIME8BIT, MimeMode) && 33367887Seric bitset(EF_HAS8BIT, e->e_flags) && 33467887Seric !bitnset(M_8BITS, m->m_flags)) 33567887Seric bodytype = "8BITMIME"; 33667887Seric if (bodytype != NULL) 33767417Seric { 33867417Seric strcat(optbuf, " BODY="); 33967887Seric strcat(optbuf, bodytype); 34067417Seric } 34167417Seric } 34267995Seric else if (bitnset(M_8BITS, m->m_flags) || 34367995Seric !bitset(EF_HAS8BIT, e->e_flags) || 34467995Seric (e->e_bodytype != NULL && 34567995Seric strcasecmp(e->e_bodytype, "7bit") == 0)) 34667887Seric { 34767887Seric /* just pass it through */ 34867887Seric } 34967887Seric else if (bitset(MM_CVTMIME, MimeMode) && 35067995Seric (e->e_bodytype != NULL || !bitset(MM_PASS8BIT, MimeMode))) 35167887Seric { 35267887Seric /* must convert from 8bit MIME format to 7bit encoded */ 35367887Seric mci->mci_flags |= MCIF_CVT8TO7; 35467887Seric } 35567887Seric else if (!bitset(MM_PASS8BIT, MimeMode)) 35667887Seric { 35767887Seric /* cannot just send a 8-bit version */ 35867887Seric usrerr("%s does not support 8BITMIME", mci->mci_host); 35967887Seric return EX_DATAERR; 36067887Seric } 36167417Seric 36267963Seric if (bitset(MCIF_DSN, mci->mci_flags)) 36367880Seric { 36467963Seric if (e->e_envid != NULL) 36567963Seric { 36667963Seric strcat(optbuf, " ENVID="); 36767963Seric strcat(optbuf, e->e_envid); 36867963Seric } 36967880Seric } 37067880Seric 3719315Seric /* 3724865Seric ** Send the MAIL command. 3734865Seric ** Designates the sender. 3744865Seric */ 3754796Seric 37653751Seric mci->mci_state = MCIS_ACTIVE; 37753751Seric 37859540Seric if (bitset(EF_RESPONSE, e->e_flags) && 37959540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 38058680Seric (void) strcpy(buf, ""); 38158680Seric else 382*68528Seric expand("\201g", buf, &buf[sizeof buf], e); 38365494Seric if (buf[0] == '<') 38465494Seric { 38565494Seric /* strip off <angle brackets> (put back on below) */ 38665494Seric bufp = &buf[strlen(buf) - 1]; 38765494Seric if (*bufp == '>') 38865494Seric *bufp = '\0'; 38965494Seric bufp = &buf[1]; 39065494Seric } 39165494Seric else 39265494Seric bufp = buf; 39367472Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 39410688Seric !bitnset(M_FROMPATH, m->m_flags)) 3958436Seric { 39665494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 3978436Seric } 3988436Seric else 3998436Seric { 40059285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 40165494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 4028436Seric } 40361093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 40453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 40559285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 4068005Seric if (r < 0 || REPLYTYPE(r) == 4) 40753751Seric { 40853751Seric mci->mci_exitstat = EX_TEMPFAIL; 40953751Seric mci->mci_errno = errno; 41053751Seric smtpquit(m, mci, e); 41153751Seric return EX_TEMPFAIL; 41253751Seric } 4137963Seric else if (r == 250) 41453751Seric { 41553751Seric return EX_OK; 41653751Seric } 41768075Seric else if (r == 501 || r == 553) 41868075Seric { 41968075Seric /* syntax error in arguments */ 42068075Seric smtpquit(m, mci, e); 42168075Seric return EX_DATAERR; 42268075Seric } 4237963Seric else if (r == 552) 42453751Seric { 42553751Seric /* signal service unavailable */ 42653751Seric smtpquit(m, mci, e); 42753751Seric return EX_UNAVAILABLE; 42853751Seric } 42914913Seric 43058008Seric #ifdef LOG 43158020Seric if (LogLevel > 1) 43258008Seric { 43367860Seric syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s", 43467860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 43558008Seric } 43658008Seric #endif 43758008Seric 43814913Seric /* protocol error -- close up */ 43953751Seric smtpquit(m, mci, e); 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 } 5134865Seric 51467880Seric smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); 51567880Seric 51661093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 51753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 51859285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 51967880Seric setstatus(to, SmtpReplyBuffer); 5208005Seric if (r < 0 || REPLYTYPE(r) == 4) 5214865Seric return (EX_TEMPFAIL); 5227963Seric else if (REPLYTYPE(r) == 2) 5237963Seric return (EX_OK); 5247964Seric else if (r == 550 || r == 551 || r == 553) 5257964Seric return (EX_NOUSER); 5267964Seric else if (r == 552 || r == 554) 5277964Seric return (EX_UNAVAILABLE); 52858008Seric 52958008Seric #ifdef LOG 53058020Seric if (LogLevel > 1) 53158008Seric { 53267860Seric syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s", 53367860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 53458008Seric } 53558008Seric #endif 53658008Seric 5377964Seric return (EX_PROTOCOL); 5384797Seric } 5394797Seric /* 54010175Seric ** SMTPDATA -- send the data and clean up the transaction. 5414684Seric ** 5424684Seric ** Parameters: 5434865Seric ** m -- mailer being sent to. 5446980Seric ** e -- the envelope for this message. 5454684Seric ** 5464684Seric ** Returns: 5474976Seric ** exit status corresponding to DATA command. 5484684Seric ** 5494684Seric ** Side Effects: 5504865Seric ** none. 5514684Seric */ 5524684Seric 55363753Seric static jmp_buf CtxDataTimeout; 55468433Seric static void datatimeout(); 55563753Seric 55653740Seric smtpdata(m, mci, e) 5574865Seric struct mailer *m; 55854967Seric register MCI *mci; 5596980Seric register ENVELOPE *e; 5604684Seric { 5614684Seric register int r; 56263753Seric register EVENT *ev; 56363753Seric time_t timeout; 5644684Seric 5654797Seric /* 5664797Seric ** Send the data. 56710175Seric ** First send the command and check that it is ok. 56810175Seric ** Then send the data. 56910175Seric ** Follow it up with a dot to terminate. 57010175Seric ** Finally get the results of the transaction. 5714797Seric */ 5724797Seric 57310175Seric /* send the command and check ok to proceed */ 57453751Seric smtpmessage("DATA", m, mci); 57561093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 57653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 57759285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 5788005Seric if (r < 0 || REPLYTYPE(r) == 4) 57957990Seric { 58057990Seric smtpquit(m, mci, e); 5814797Seric return (EX_TEMPFAIL); 58257990Seric } 5837963Seric else if (r == 554) 58457990Seric { 58557990Seric smtprset(m, mci, e); 5867963Seric return (EX_UNAVAILABLE); 58757990Seric } 5887963Seric else if (r != 354) 58957990Seric { 59058008Seric #ifdef LOG 59158020Seric if (LogLevel > 1) 59258008Seric { 59367860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s", 59467860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 59558008Seric } 59658008Seric #endif 59757990Seric smtprset(m, mci, e); 5987964Seric return (EX_PROTOCOL); 59957990Seric } 60010175Seric 60163757Seric /* 60263757Seric ** Set timeout around data writes. Make it at least large 60363757Seric ** enough for DNS timeouts on all recipients plus some fudge 60463757Seric ** factor. The main thing is that it should not be infinite. 60563757Seric */ 60663757Seric 60763753Seric if (setjmp(CtxDataTimeout) != 0) 60863753Seric { 60963753Seric mci->mci_errno = errno; 61063753Seric mci->mci_exitstat = EX_TEMPFAIL; 61163753Seric mci->mci_state = MCIS_ERROR; 61263753Seric syserr("451 timeout writing message to %s", mci->mci_host); 61363753Seric smtpquit(m, mci, e); 61463753Seric return EX_TEMPFAIL; 61563753Seric } 61663753Seric 61763787Seric timeout = e->e_msgsize / 16; 61863787Seric if (timeout < (time_t) 60) 61963787Seric timeout = (time_t) 60; 62063787Seric timeout += e->e_nrcpts * 90; 62163753Seric ev = setevent(timeout, datatimeout, 0); 62263753Seric 62367546Seric /* 62467546Seric ** Output the actual message. 62567546Seric */ 62667546Seric 62768228Seric (*e->e_puthdr)(mci, e->e_header, e); 62868228Seric (*e->e_putbody)(mci, e, NULL); 62910175Seric 63067546Seric /* 63167546Seric ** Cleanup after sending message. 63267546Seric */ 63367546Seric 63463753Seric clrevent(ev); 63563753Seric 63664718Seric if (ferror(mci->mci_out)) 63764718Seric { 63864718Seric /* error during processing -- don't send the dot */ 63964718Seric mci->mci_errno = EIO; 64064718Seric mci->mci_exitstat = EX_IOERR; 64164718Seric mci->mci_state = MCIS_ERROR; 64264718Seric smtpquit(m, mci, e); 64364718Seric return EX_IOERR; 64464718Seric } 64564718Seric 64610175Seric /* terminate the message */ 64753740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 64863753Seric if (TrafficLogFile != NULL) 64963753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 65058120Seric if (Verbose) 65158151Seric nmessage(">>> ."); 65210175Seric 65310175Seric /* check for the results of the transaction */ 65461093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 65553751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 65659285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 65753751Seric if (r < 0) 65857990Seric { 65957990Seric smtpquit(m, mci, e); 6604797Seric return (EX_TEMPFAIL); 66157990Seric } 66253751Seric mci->mci_state = MCIS_OPEN; 66358917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 66453751Seric if (REPLYTYPE(r) == 4) 66553751Seric return (EX_TEMPFAIL); 6667963Seric else if (r == 250) 6677963Seric return (EX_OK); 6687963Seric else if (r == 552 || r == 554) 6697963Seric return (EX_UNAVAILABLE); 67058008Seric #ifdef LOG 67158020Seric if (LogLevel > 1) 67258008Seric { 67367860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s", 67467860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 67558008Seric } 67658008Seric #endif 6777964Seric return (EX_PROTOCOL); 6784684Seric } 67963753Seric 68063753Seric 68168433Seric static void 68263753Seric datatimeout() 68363753Seric { 68463753Seric longjmp(CtxDataTimeout, 1); 68563753Seric } 6864684Seric /* 6874865Seric ** SMTPQUIT -- close the SMTP connection. 6884865Seric ** 6894865Seric ** Parameters: 69015535Seric ** m -- a pointer to the mailer. 6914865Seric ** 6924865Seric ** Returns: 6934865Seric ** none. 6944865Seric ** 6954865Seric ** Side Effects: 6964865Seric ** sends the final protocol and closes the connection. 6974865Seric */ 6984865Seric 69953751Seric smtpquit(m, mci, e) 70053751Seric register MAILER *m; 70154967Seric register MCI *mci; 70253751Seric ENVELOPE *e; 7034865Seric { 70464822Seric bool oldSuprErrs = SuprErrs; 7054865Seric 70664822Seric /* 70764822Seric ** Suppress errors here -- we may be processing a different 70864822Seric ** job when we do the quit connection, and we don't want the 70964822Seric ** new job to be penalized for something that isn't it's 71064822Seric ** problem. 71164822Seric */ 71264822Seric 71364822Seric SuprErrs = TRUE; 71464822Seric 71554967Seric /* send the quit message if we haven't gotten I/O error */ 71653751Seric if (mci->mci_state != MCIS_ERROR) 7179391Seric { 71861093Seric SmtpPhase = "client QUIT"; 71953751Seric smtpmessage("QUIT", m, mci); 72059285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 72164822Seric SuprErrs = oldSuprErrs; 72253740Seric if (mci->mci_state == MCIS_CLOSED) 72364822Seric { 72464822Seric SuprErrs = oldSuprErrs; 72510159Seric return; 72664822Seric } 7279391Seric } 7289391Seric 72952676Seric /* now actually close the connection and pick up the zombie */ 73065194Seric (void) endmailer(mci, e, NULL); 73164822Seric 73264822Seric SuprErrs = oldSuprErrs; 7334865Seric } 7344865Seric /* 73554967Seric ** SMTPRSET -- send a RSET (reset) command 73654967Seric */ 73754967Seric 73854967Seric smtprset(m, mci, e) 73954967Seric register MAILER *m; 74054967Seric register MCI *mci; 74154967Seric ENVELOPE *e; 74254967Seric { 74354967Seric int r; 74454967Seric 74561093Seric SmtpPhase = "client RSET"; 74654967Seric smtpmessage("RSET", m, mci); 74759285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 74857734Seric if (r < 0) 74957734Seric mci->mci_state = MCIS_ERROR; 75054967Seric else if (REPLYTYPE(r) == 2) 75157734Seric { 75257734Seric mci->mci_state = MCIS_OPEN; 75357734Seric return; 75457734Seric } 75557734Seric smtpquit(m, mci, e); 75654967Seric } 75754967Seric /* 75858867Seric ** SMTPPROBE -- check the connection state 75954967Seric */ 76054967Seric 76158867Seric smtpprobe(mci) 76254967Seric register MCI *mci; 76354967Seric { 76454967Seric int r; 76554967Seric MAILER *m = mci->mci_mailer; 76654967Seric extern ENVELOPE BlankEnvelope; 76754967Seric ENVELOPE *e = &BlankEnvelope; 76854967Seric 76961093Seric SmtpPhase = "client probe"; 77058867Seric smtpmessage("RSET", m, mci); 77159285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 77258061Seric if (r < 0 || REPLYTYPE(r) != 2) 77354967Seric smtpquit(m, mci, e); 77454967Seric return r; 77554967Seric } 77654967Seric /* 7774684Seric ** REPLY -- read arpanet reply 7784684Seric ** 7794684Seric ** Parameters: 78010175Seric ** m -- the mailer we are reading the reply from. 78157379Seric ** mci -- the mailer connection info structure. 78257379Seric ** e -- the current envelope. 78357379Seric ** timeout -- the timeout for reads. 78459285Seric ** pfunc -- processing function for second and subsequent 78559285Seric ** lines of response -- if null, no special 78659285Seric ** processing is done. 7874684Seric ** 7884684Seric ** Returns: 7894684Seric ** reply code it reads. 7904684Seric ** 7914684Seric ** Side Effects: 7924684Seric ** flushes the mail file. 7934684Seric */ 7944684Seric 79559285Seric reply(m, mci, e, timeout, pfunc) 79653751Seric MAILER *m; 79754967Seric MCI *mci; 79853751Seric ENVELOPE *e; 79959285Seric time_t timeout; 80059285Seric void (*pfunc)(); 8014684Seric { 80259014Seric register char *bufp; 80359014Seric register int r; 80459285Seric bool firstline = TRUE; 80558957Seric char junkbuf[MAXLINE]; 80658957Seric 80757379Seric if (mci->mci_out != NULL) 80857379Seric (void) fflush(mci->mci_out); 8094684Seric 8107677Seric if (tTd(18, 1)) 8114796Seric printf("reply\n"); 8124796Seric 8137356Seric /* 8147356Seric ** Read the input line, being careful not to hang. 8157356Seric */ 8167356Seric 81759014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 8184684Seric { 8197356Seric register char *p; 82053751Seric extern time_t curtime(); 8214684Seric 8227685Seric /* actually do the read */ 82353751Seric if (e->e_xfp != NULL) 82453751Seric (void) fflush(e->e_xfp); /* for debugging */ 8257356Seric 82610054Seric /* if we are in the process of closing just give the code */ 82753740Seric if (mci->mci_state == MCIS_CLOSED) 82810054Seric return (SMTPCLOSING); 82910054Seric 83058680Seric if (mci->mci_out != NULL) 83158680Seric fflush(mci->mci_out); 83258680Seric 83310054Seric /* get the line from the other side */ 83461093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 83553751Seric mci->mci_lastuse = curtime(); 83653751Seric 83710054Seric if (p == NULL) 83810131Seric { 83963753Seric bool oldholderrs; 84010148Seric extern char MsgBuf[]; /* err.c */ 84110148Seric 84221065Seric /* if the remote end closed early, fake an error */ 84321065Seric if (errno == 0) 84421065Seric # ifdef ECONNRESET 84521065Seric errno = ECONNRESET; 84656795Seric # else /* ECONNRESET */ 84721065Seric errno = EPIPE; 84856795Seric # endif /* ECONNRESET */ 84921065Seric 85057379Seric mci->mci_errno = errno; 85157642Seric mci->mci_exitstat = EX_TEMPFAIL; 85263753Seric oldholderrs = HoldErrs; 85363753Seric HoldErrs = TRUE; 85463753Seric usrerr("451 reply: read error from %s", mci->mci_host); 85563753Seric 85610420Seric /* if debugging, pause so we can see state */ 85710420Seric if (tTd(18, 100)) 85810420Seric pause(); 85954967Seric mci->mci_state = MCIS_ERROR; 86053751Seric smtpquit(m, mci, e); 86163753Seric #ifdef XDEBUG 86263753Seric { 86363753Seric char wbuf[MAXLINE]; 86464082Seric char *p = wbuf; 86564082Seric if (e->e_to != NULL) 86664082Seric { 86764082Seric sprintf(p, "%s... ", e->e_to); 86864082Seric p += strlen(p); 86964082Seric } 87064082Seric sprintf(p, "reply(%s) during %s", 87164082Seric mci->mci_host, SmtpPhase); 87263753Seric checkfd012(wbuf); 87363753Seric } 87463753Seric #endif 87563753Seric HoldErrs = oldholderrs; 87610054Seric return (-1); 87710131Seric } 87858957Seric fixcrlf(bufp, TRUE); 87910054Seric 88063753Seric /* EHLO failure is not a real error */ 88163753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 88263753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 88314900Seric { 88414900Seric /* serious error -- log the previous command */ 88564071Seric if (SmtpNeedIntro) 88664071Seric { 88764071Seric /* inform user who we are chatting with */ 88864071Seric fprintf(CurEnv->e_xfp, 88964071Seric "... while talking to %s:\n", 89064071Seric CurHostName); 89164071Seric SmtpNeedIntro = FALSE; 89264071Seric } 89359014Seric if (SmtpMsgBuffer[0] != '\0') 89459014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 89559014Seric SmtpMsgBuffer[0] = '\0'; 89614900Seric 89714900Seric /* now log the message as from the other side */ 89858957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 89914900Seric } 90014900Seric 90114900Seric /* display the input for verbose mode */ 90258120Seric if (Verbose) 90359956Seric nmessage("050 %s", bufp); 9047356Seric 90559285Seric /* process the line */ 90667893Seric if (pfunc != NULL) 90767893Seric (*pfunc)(bufp, firstline, m, mci, e); 90859285Seric 90959285Seric firstline = FALSE; 91059285Seric 9117356Seric /* if continuation is required, we can go on */ 91259014Seric if (bufp[3] == '-') 9134684Seric continue; 9147356Seric 91559014Seric /* ignore improperly formated input */ 91659014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 91759014Seric continue; 91859014Seric 9197356Seric /* decode the reply code */ 92059014Seric r = atoi(bufp); 9217356Seric 9227356Seric /* extra semantics: 0xx codes are "informational" */ 92359014Seric if (r >= 100) 92459014Seric break; 92559014Seric } 9267356Seric 92759014Seric /* 92859014Seric ** Now look at SmtpReplyBuffer -- only care about the first 92959014Seric ** line of the response from here on out. 93059014Seric */ 93158061Seric 93259014Seric /* save temporary failure messages for posterity */ 93359014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 93459014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 9359391Seric 93659014Seric /* reply code 421 is "Service Shutting Down" */ 93759014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 93859014Seric { 93959014Seric /* send the quit protocol */ 94059014Seric mci->mci_state = MCIS_SSD; 94159014Seric smtpquit(m, mci, e); 9424684Seric } 94359014Seric 94459014Seric return (r); 9454684Seric } 9464796Seric /* 9474865Seric ** SMTPMESSAGE -- send message to server 9484796Seric ** 9494796Seric ** Parameters: 9504796Seric ** f -- format 95110175Seric ** m -- the mailer to control formatting. 9524796Seric ** a, b, c -- parameters 9534796Seric ** 9544796Seric ** Returns: 9554796Seric ** none. 9564796Seric ** 9574796Seric ** Side Effects: 95853740Seric ** writes message to mci->mci_out. 9594796Seric */ 9604796Seric 9614865Seric /*VARARGS1*/ 96257642Seric #ifdef __STDC__ 96357642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 96457642Seric #else 96557642Seric smtpmessage(f, m, mci, va_alist) 9664796Seric char *f; 96710175Seric MAILER *m; 96854967Seric MCI *mci; 96957642Seric va_dcl 97057642Seric #endif 9714796Seric { 97256852Seric VA_LOCAL_DECL 97356852Seric 97457135Seric VA_START(mci); 97556852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 97656852Seric VA_END; 97758680Seric 97858120Seric if (tTd(18, 1) || Verbose) 97958151Seric nmessage(">>> %s", SmtpMsgBuffer); 98063753Seric if (TrafficLogFile != NULL) 98163753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 98253740Seric if (mci->mci_out != NULL) 98358680Seric { 98453740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 98554967Seric m == NULL ? "\r\n" : m->m_eol); 98658680Seric } 98759149Seric else if (tTd(18, 1)) 98858725Seric { 98959149Seric printf("smtpmessage: NULL mci_out\n"); 99058725Seric } 9914796Seric } 9925182Seric 99356795Seric # endif /* SMTP */ 994