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*65494Seric static char sccsid[] = "@(#)usersmtp.c 8.17 (Berkeley) 01/05/94 (with SMTP)"; 1433731Sbostic #else 15*65494Seric static char sccsid[] = "@(#)usersmtp.c 8.17 (Berkeley) 01/05/94 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194684Seric # include <sysexits.h> 2021065Seric # include <errno.h> 214684Seric 2233731Sbostic # ifdef SMTP 234684Seric 244684Seric /* 259391Seric ** USERSMTP -- run SMTP protocol from the user end. 269391Seric ** 279391Seric ** This protocol is described in RFC821. 289391Seric */ 299391Seric 309391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 319391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 329391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 339391Seric 3414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 3510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 3621065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 3710054Seric int SmtpPid; /* pid of mailer */ 3864071Seric bool SmtpNeedIntro; /* need "while talking" in transcript */ 3958671Seric 4058671Seric #ifdef __STDC__ 4158671Seric extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 4258671Seric #endif 439391Seric /* 444865Seric ** SMTPINIT -- initialize SMTP. 454684Seric ** 464865Seric ** Opens the connection and sends the initial protocol. 474684Seric ** 484684Seric ** Parameters: 494865Seric ** m -- mailer to create connection to. 504865Seric ** pvp -- pointer to parameter vector to pass to 514865Seric ** the mailer. 524684Seric ** 534684Seric ** Returns: 5454967Seric ** none. 554684Seric ** 564684Seric ** Side Effects: 574865Seric ** creates connection and sends initial protocol. 584684Seric */ 594684Seric 6054967Seric smtpinit(m, mci, e) 614865Seric struct mailer *m; 6254967Seric register MCI *mci; 6353751Seric ENVELOPE *e; 644684Seric { 654865Seric register int r; 6658957Seric register char *p; 6760210Seric extern void esmtp_check(); 6859285Seric extern void helo_options(); 694684Seric 7063753Seric if (tTd(18, 1)) 7157379Seric { 7257379Seric printf("smtpinit "); 7364731Seric mci_dump(mci, FALSE); 7457379Seric } 7557379Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8021065Seric SmtpError[0] = '\0'; 8157379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 8264071Seric SmtpNeedIntro = TRUE; 8354967Seric switch (mci->mci_state) 846051Seric { 8554967Seric case MCIS_ACTIVE: 8654967Seric /* need to clear old information */ 8754967Seric smtprset(m, mci, e); 8857734Seric /* fall through */ 8915139Seric 9054967Seric case MCIS_OPEN: 9154967Seric return; 9254967Seric 9354967Seric case MCIS_ERROR: 9454967Seric case MCIS_SSD: 9554967Seric /* shouldn't happen */ 9654967Seric smtpquit(m, mci, e); 9757734Seric /* fall through */ 9854967Seric 9954967Seric case MCIS_CLOSED: 10058151Seric syserr("451 smtpinit: state CLOSED"); 10154967Seric return; 10254967Seric 10354967Seric case MCIS_OPENING: 10454967Seric break; 1056051Seric } 1064796Seric 10754967Seric mci->mci_state = MCIS_OPENING; 10854967Seric 1094865Seric /* 1104865Seric ** Get the greeting message. 11114913Seric ** This should appear spontaneously. Give it five minutes to 11214886Seric ** happen. 1134865Seric */ 1144797Seric 11561093Seric SmtpPhase = mci->mci_phase = "client greeting"; 11653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11760210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 11864750Seric if (r < 0 || REPLYTYPE(r) == 4) 11952104Seric goto tempfail1; 12064750Seric if (REPLYTYPE(r) != 2) 12164750Seric goto unavailable; 1224684Seric 1234865Seric /* 1244976Seric ** Send the HELO command. 1257963Seric ** My mother taught me to always introduce myself. 1264976Seric */ 1274976Seric 12859285Seric if (bitnset(M_ESMTP, m->m_flags)) 12959285Seric mci->mci_flags |= MCIF_ESMTP; 13059285Seric 13159285Seric tryhelo: 13259285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 13359285Seric { 13459285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13561093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13659285Seric } 13759285Seric else 13859285Seric { 13959285Seric smtpmessage("HELO %s", m, mci, MyHostName); 14061093Seric SmtpPhase = mci->mci_phase = "client HELO"; 14159285Seric } 14253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 14359285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1448005Seric if (r < 0) 14552104Seric goto tempfail1; 1468005Seric else if (REPLYTYPE(r) == 5) 14759285Seric { 14859285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 14959285Seric { 15059285Seric /* try old SMTP instead */ 15159285Seric mci->mci_flags &= ~MCIF_ESMTP; 15259285Seric goto tryhelo; 15359285Seric } 15414913Seric goto unavailable; 15559285Seric } 1567963Seric else if (REPLYTYPE(r) != 2) 15752104Seric goto tempfail1; 1584976Seric 1594976Seric /* 16058957Seric ** Check to see if we actually ended up talking to ourself. 16158957Seric ** This means we didn't know about an alias or MX, or we managed 16258957Seric ** to connect to an echo server. 16364651Seric ** 16464651Seric ** If this code remains at all, "CheckLoopBack" should be 16564651Seric ** a mailer flag. This is a MAYBENEXTRELEASE feature. 16658957Seric */ 16758957Seric 16859026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16958957Seric if (p != NULL) 17061707Seric *p = '\0'; 17164644Seric if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17258957Seric { 17358957Seric syserr("553 %s config error: mail loops back to myself", 17458957Seric MyHostName); 17558957Seric mci->mci_exitstat = EX_CONFIG; 17658957Seric mci->mci_errno = 0; 17758957Seric smtpquit(m, mci, e); 17858957Seric return; 17958957Seric } 18058957Seric 18158957Seric /* 1829315Seric ** If this is expected to be another sendmail, send some internal 1839315Seric ** commands. 1849315Seric */ 1859315Seric 18610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1879315Seric { 1889315Seric /* tell it to be verbose */ 18953751Seric smtpmessage("VERB", m, mci); 19059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1919315Seric if (r < 0) 19252104Seric goto tempfail2; 1939315Seric } 1949315Seric 19565057Seric if (mci->mci_state != MCIS_CLOSED) 19665057Seric { 19765057Seric mci->mci_state = MCIS_OPEN; 19865057Seric return; 19965057Seric } 20053751Seric 20165057Seric /* got a 421 error code during startup */ 20265057Seric 20353751Seric tempfail1: 20453751Seric tempfail2: 20553751Seric mci->mci_exitstat = EX_TEMPFAIL; 20657379Seric if (mci->mci_errno == 0) 20757379Seric mci->mci_errno = errno; 20857379Seric if (mci->mci_state != MCIS_CLOSED) 20957379Seric smtpquit(m, mci, e); 21054967Seric return; 21153751Seric 21253751Seric unavailable: 21353751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21453751Seric mci->mci_errno = errno; 21553751Seric smtpquit(m, mci, e); 21654967Seric return; 21753751Seric } 21859285Seric /* 21960210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 22060210Seric ** 22160210Seric ** 22260210Seric ** Parameters: 22360210Seric ** line -- the response line. 22460210Seric ** m -- the mailer. 22560210Seric ** mci -- the mailer connection info. 22660210Seric ** e -- the envelope. 22760210Seric ** 22860210Seric ** Returns: 22960210Seric ** none. 23060210Seric */ 23160210Seric 23260210Seric void 23360210Seric esmtp_check(line, m, mci, e) 23460210Seric char *line; 23560210Seric MAILER *m; 23660210Seric register MCI *mci; 23760210Seric ENVELOPE *e; 23860210Seric { 23960210Seric if (strlen(line) < 5) 24060210Seric return; 24160210Seric line += 4; 24260210Seric if (strncmp(line, "ESMTP ", 6) == 0) 24360210Seric mci->mci_flags |= MCIF_ESMTP; 24460210Seric } 24560210Seric /* 24659285Seric ** HELO_OPTIONS -- process the options on a HELO line. 24759285Seric ** 24859285Seric ** Parameters: 24959285Seric ** line -- the response line. 25059285Seric ** m -- the mailer. 25159285Seric ** mci -- the mailer connection info. 25259285Seric ** e -- the envelope. 25359285Seric ** 25459285Seric ** Returns: 25559285Seric ** none. 25659285Seric */ 25753751Seric 25859285Seric void 25959285Seric helo_options(line, m, mci, e) 26059285Seric char *line; 26159285Seric MAILER *m; 26259285Seric register MCI *mci; 26359285Seric ENVELOPE *e; 26459285Seric { 26559285Seric register char *p; 26659285Seric 26759285Seric if (strlen(line) < 5) 26859285Seric return; 26959285Seric line += 4; 27059285Seric p = strchr(line, ' '); 27159285Seric if (p != NULL) 27259285Seric *p++ = '\0'; 27359285Seric if (strcasecmp(line, "size") == 0) 27459285Seric { 27559285Seric mci->mci_flags |= MCIF_SIZE; 27659285Seric if (p != NULL) 27759285Seric mci->mci_maxsize = atol(p); 27859285Seric } 27959285Seric else if (strcasecmp(line, "8bitmime") == 0) 28059285Seric mci->mci_flags |= MCIF_8BITMIME; 28159285Seric else if (strcasecmp(line, "expn") == 0) 28259285Seric mci->mci_flags |= MCIF_EXPN; 28359285Seric } 28459285Seric /* 28559285Seric ** SMTPMAILFROM -- send MAIL command 28659285Seric ** 28759285Seric ** Parameters: 28859285Seric ** m -- the mailer. 28959285Seric ** mci -- the mailer connection structure. 29059285Seric ** e -- the envelope (including the sender to specify). 29159285Seric */ 29259285Seric 29353751Seric smtpmailfrom(m, mci, e) 29453751Seric struct mailer *m; 29554967Seric MCI *mci; 29653751Seric ENVELOPE *e; 29753751Seric { 29853751Seric int r; 299*65494Seric char *bufp; 30053751Seric char buf[MAXNAME]; 30159285Seric char optbuf[MAXLINE]; 30253751Seric 30363753Seric if (tTd(18, 2)) 30457943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 30557943Seric 30659285Seric /* set up appropriate options to include */ 30764254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 30859285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 30959285Seric else 31059285Seric strcpy(optbuf, ""); 31159285Seric 3129315Seric /* 3134865Seric ** Send the MAIL command. 3144865Seric ** Designates the sender. 3154865Seric */ 3164796Seric 31753751Seric mci->mci_state = MCIS_ACTIVE; 31853751Seric 31959540Seric if (bitset(EF_RESPONSE, e->e_flags) && 32059540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 32158680Seric (void) strcpy(buf, ""); 32258680Seric else 32358680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 324*65494Seric if (buf[0] == '<') 325*65494Seric { 326*65494Seric /* strip off <angle brackets> (put back on below) */ 327*65494Seric bufp = &buf[strlen(buf) - 1]; 328*65494Seric if (*bufp == '>') 329*65494Seric *bufp = '\0'; 330*65494Seric bufp = &buf[1]; 331*65494Seric } 332*65494Seric else 333*65494Seric bufp = buf; 33453751Seric if (e->e_from.q_mailer == LocalMailer || 33510688Seric !bitnset(M_FROMPATH, m->m_flags)) 3368436Seric { 337*65494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 3388436Seric } 3398436Seric else 3408436Seric { 34159285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 342*65494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 3438436Seric } 34461093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 34553751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 34659285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3478005Seric if (r < 0 || REPLYTYPE(r) == 4) 34853751Seric { 34953751Seric mci->mci_exitstat = EX_TEMPFAIL; 35053751Seric mci->mci_errno = errno; 35153751Seric smtpquit(m, mci, e); 35253751Seric return EX_TEMPFAIL; 35353751Seric } 3547963Seric else if (r == 250) 35553751Seric { 35653751Seric mci->mci_exitstat = EX_OK; 35753751Seric return EX_OK; 35853751Seric } 3597963Seric else if (r == 552) 36053751Seric { 36153751Seric /* signal service unavailable */ 36253751Seric mci->mci_exitstat = EX_UNAVAILABLE; 36353751Seric smtpquit(m, mci, e); 36453751Seric return EX_UNAVAILABLE; 36553751Seric } 36614913Seric 36758008Seric #ifdef LOG 36858020Seric if (LogLevel > 1) 36958008Seric { 37058008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 37158008Seric e->e_id, SmtpReplyBuffer); 37258008Seric } 37358008Seric #endif 37458008Seric 37514913Seric /* protocol error -- close up */ 37653751Seric smtpquit(m, mci, e); 37753751Seric mci->mci_exitstat = EX_PROTOCOL; 37853751Seric return EX_PROTOCOL; 3794684Seric } 3804684Seric /* 3814976Seric ** SMTPRCPT -- designate recipient. 3824797Seric ** 3834797Seric ** Parameters: 3844865Seric ** to -- address of recipient. 38510175Seric ** m -- the mailer we are sending to. 38657379Seric ** mci -- the connection info for this transaction. 38757379Seric ** e -- the envelope for this transaction. 3884797Seric ** 3894797Seric ** Returns: 3904865Seric ** exit status corresponding to recipient status. 3914797Seric ** 3924797Seric ** Side Effects: 3934865Seric ** Sends the mail via SMTP. 3944797Seric */ 3954797Seric 39653751Seric smtprcpt(to, m, mci, e) 3974865Seric ADDRESS *to; 39810175Seric register MAILER *m; 39954967Seric MCI *mci; 40053751Seric ENVELOPE *e; 4014797Seric { 4024797Seric register int r; 4034797Seric 40453751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 4054865Seric 40661093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 40753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 40859285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 4098005Seric if (r < 0 || REPLYTYPE(r) == 4) 4104865Seric return (EX_TEMPFAIL); 4117963Seric else if (REPLYTYPE(r) == 2) 4127963Seric return (EX_OK); 4137964Seric else if (r == 550 || r == 551 || r == 553) 4147964Seric return (EX_NOUSER); 4157964Seric else if (r == 552 || r == 554) 4167964Seric return (EX_UNAVAILABLE); 41758008Seric 41858008Seric #ifdef LOG 41958020Seric if (LogLevel > 1) 42058008Seric { 42158008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 42258008Seric e->e_id, SmtpReplyBuffer); 42358008Seric } 42458008Seric #endif 42558008Seric 4267964Seric return (EX_PROTOCOL); 4274797Seric } 4284797Seric /* 42910175Seric ** SMTPDATA -- send the data and clean up the transaction. 4304684Seric ** 4314684Seric ** Parameters: 4324865Seric ** m -- mailer being sent to. 4336980Seric ** e -- the envelope for this message. 4344684Seric ** 4354684Seric ** Returns: 4364976Seric ** exit status corresponding to DATA command. 4374684Seric ** 4384684Seric ** Side Effects: 4394865Seric ** none. 4404684Seric */ 4414684Seric 44263753Seric static jmp_buf CtxDataTimeout; 44363937Seric static int datatimeout(); 44463753Seric 44553740Seric smtpdata(m, mci, e) 4464865Seric struct mailer *m; 44754967Seric register MCI *mci; 4486980Seric register ENVELOPE *e; 4494684Seric { 4504684Seric register int r; 45163753Seric register EVENT *ev; 45263753Seric time_t timeout; 4534684Seric 4544797Seric /* 4554797Seric ** Send the data. 45610175Seric ** First send the command and check that it is ok. 45710175Seric ** Then send the data. 45810175Seric ** Follow it up with a dot to terminate. 45910175Seric ** Finally get the results of the transaction. 4604797Seric */ 4614797Seric 46210175Seric /* send the command and check ok to proceed */ 46353751Seric smtpmessage("DATA", m, mci); 46461093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 46553751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 46659285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4678005Seric if (r < 0 || REPLYTYPE(r) == 4) 46857990Seric { 46957990Seric smtpquit(m, mci, e); 4704797Seric return (EX_TEMPFAIL); 47157990Seric } 4727963Seric else if (r == 554) 47357990Seric { 47457990Seric smtprset(m, mci, e); 4757963Seric return (EX_UNAVAILABLE); 47657990Seric } 4777963Seric else if (r != 354) 47857990Seric { 47958008Seric #ifdef LOG 48058020Seric if (LogLevel > 1) 48158008Seric { 48258008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 48358008Seric e->e_id, SmtpReplyBuffer); 48458008Seric } 48558008Seric #endif 48657990Seric smtprset(m, mci, e); 4877964Seric return (EX_PROTOCOL); 48857990Seric } 48910175Seric 49063757Seric /* 49163757Seric ** Set timeout around data writes. Make it at least large 49263757Seric ** enough for DNS timeouts on all recipients plus some fudge 49363757Seric ** factor. The main thing is that it should not be infinite. 49463757Seric */ 49563757Seric 49663753Seric if (setjmp(CtxDataTimeout) != 0) 49763753Seric { 49863753Seric mci->mci_errno = errno; 49963753Seric mci->mci_exitstat = EX_TEMPFAIL; 50063753Seric mci->mci_state = MCIS_ERROR; 50163753Seric syserr("451 timeout writing message to %s", mci->mci_host); 50263753Seric smtpquit(m, mci, e); 50363753Seric return EX_TEMPFAIL; 50463753Seric } 50563753Seric 50663787Seric timeout = e->e_msgsize / 16; 50763787Seric if (timeout < (time_t) 60) 50863787Seric timeout = (time_t) 60; 50963787Seric timeout += e->e_nrcpts * 90; 51063753Seric ev = setevent(timeout, datatimeout, 0); 51163753Seric 51210175Seric /* now output the actual message */ 51353751Seric (*e->e_puthdr)(mci->mci_out, m, e); 51453740Seric putline("\n", mci->mci_out, m); 51559730Seric (*e->e_putbody)(mci->mci_out, m, e, NULL); 51610175Seric 51763753Seric clrevent(ev); 51863753Seric 51964718Seric if (ferror(mci->mci_out)) 52064718Seric { 52164718Seric /* error during processing -- don't send the dot */ 52264718Seric mci->mci_errno = EIO; 52364718Seric mci->mci_exitstat = EX_IOERR; 52464718Seric mci->mci_state = MCIS_ERROR; 52564718Seric smtpquit(m, mci, e); 52664718Seric return EX_IOERR; 52764718Seric } 52864718Seric 52910175Seric /* terminate the message */ 53053740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 53163753Seric if (TrafficLogFile != NULL) 53263753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 53358120Seric if (Verbose) 53458151Seric nmessage(">>> ."); 53510175Seric 53610175Seric /* check for the results of the transaction */ 53761093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 53853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 53959285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 54053751Seric if (r < 0) 54157990Seric { 54257990Seric smtpquit(m, mci, e); 5434797Seric return (EX_TEMPFAIL); 54457990Seric } 54553751Seric mci->mci_state = MCIS_OPEN; 54658917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 54753751Seric if (REPLYTYPE(r) == 4) 54853751Seric return (EX_TEMPFAIL); 5497963Seric else if (r == 250) 5507963Seric return (EX_OK); 5517963Seric else if (r == 552 || r == 554) 5527963Seric return (EX_UNAVAILABLE); 55358008Seric #ifdef LOG 55458020Seric if (LogLevel > 1) 55558008Seric { 55658008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 55758008Seric e->e_id, SmtpReplyBuffer); 55858008Seric } 55958008Seric #endif 5607964Seric return (EX_PROTOCOL); 5614684Seric } 56263753Seric 56363753Seric 56463753Seric static int 56563753Seric datatimeout() 56663753Seric { 56763753Seric longjmp(CtxDataTimeout, 1); 56863753Seric } 5694684Seric /* 5704865Seric ** SMTPQUIT -- close the SMTP connection. 5714865Seric ** 5724865Seric ** Parameters: 57315535Seric ** m -- a pointer to the mailer. 5744865Seric ** 5754865Seric ** Returns: 5764865Seric ** none. 5774865Seric ** 5784865Seric ** Side Effects: 5794865Seric ** sends the final protocol and closes the connection. 5804865Seric */ 5814865Seric 58253751Seric smtpquit(m, mci, e) 58353751Seric register MAILER *m; 58454967Seric register MCI *mci; 58553751Seric ENVELOPE *e; 5864865Seric { 58764822Seric bool oldSuprErrs = SuprErrs; 5884865Seric 58964822Seric /* 59064822Seric ** Suppress errors here -- we may be processing a different 59164822Seric ** job when we do the quit connection, and we don't want the 59264822Seric ** new job to be penalized for something that isn't it's 59364822Seric ** problem. 59464822Seric */ 59564822Seric 59664822Seric SuprErrs = TRUE; 59764822Seric 59854967Seric /* send the quit message if we haven't gotten I/O error */ 59953751Seric if (mci->mci_state != MCIS_ERROR) 6009391Seric { 60161093Seric SmtpPhase = "client QUIT"; 60253751Seric smtpmessage("QUIT", m, mci); 60359285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 60464822Seric SuprErrs = oldSuprErrs; 60553740Seric if (mci->mci_state == MCIS_CLOSED) 60664822Seric { 60764822Seric SuprErrs = oldSuprErrs; 60810159Seric return; 60964822Seric } 6109391Seric } 6119391Seric 61252676Seric /* now actually close the connection and pick up the zombie */ 61365194Seric (void) endmailer(mci, e, NULL); 61464822Seric 61564822Seric SuprErrs = oldSuprErrs; 6164865Seric } 6174865Seric /* 61854967Seric ** SMTPRSET -- send a RSET (reset) command 61954967Seric */ 62054967Seric 62154967Seric smtprset(m, mci, e) 62254967Seric register MAILER *m; 62354967Seric register MCI *mci; 62454967Seric ENVELOPE *e; 62554967Seric { 62654967Seric int r; 62754967Seric 62861093Seric SmtpPhase = "client RSET"; 62954967Seric smtpmessage("RSET", m, mci); 63059285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 63157734Seric if (r < 0) 63257734Seric mci->mci_state = MCIS_ERROR; 63354967Seric else if (REPLYTYPE(r) == 2) 63457734Seric { 63557734Seric mci->mci_state = MCIS_OPEN; 63657734Seric return; 63757734Seric } 63857734Seric smtpquit(m, mci, e); 63954967Seric } 64054967Seric /* 64158867Seric ** SMTPPROBE -- check the connection state 64254967Seric */ 64354967Seric 64458867Seric smtpprobe(mci) 64554967Seric register MCI *mci; 64654967Seric { 64754967Seric int r; 64854967Seric MAILER *m = mci->mci_mailer; 64954967Seric extern ENVELOPE BlankEnvelope; 65054967Seric ENVELOPE *e = &BlankEnvelope; 65154967Seric 65261093Seric SmtpPhase = "client probe"; 65358867Seric smtpmessage("RSET", m, mci); 65459285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 65558061Seric if (r < 0 || REPLYTYPE(r) != 2) 65654967Seric smtpquit(m, mci, e); 65754967Seric return r; 65854967Seric } 65954967Seric /* 6604684Seric ** REPLY -- read arpanet reply 6614684Seric ** 6624684Seric ** Parameters: 66310175Seric ** m -- the mailer we are reading the reply from. 66457379Seric ** mci -- the mailer connection info structure. 66557379Seric ** e -- the current envelope. 66657379Seric ** timeout -- the timeout for reads. 66759285Seric ** pfunc -- processing function for second and subsequent 66859285Seric ** lines of response -- if null, no special 66959285Seric ** processing is done. 6704684Seric ** 6714684Seric ** Returns: 6724684Seric ** reply code it reads. 6734684Seric ** 6744684Seric ** Side Effects: 6754684Seric ** flushes the mail file. 6764684Seric */ 6774684Seric 67859285Seric reply(m, mci, e, timeout, pfunc) 67953751Seric MAILER *m; 68054967Seric MCI *mci; 68153751Seric ENVELOPE *e; 68259285Seric time_t timeout; 68359285Seric void (*pfunc)(); 6844684Seric { 68559014Seric register char *bufp; 68659014Seric register int r; 68759285Seric bool firstline = TRUE; 68858957Seric char junkbuf[MAXLINE]; 68958957Seric 69057379Seric if (mci->mci_out != NULL) 69157379Seric (void) fflush(mci->mci_out); 6924684Seric 6937677Seric if (tTd(18, 1)) 6944796Seric printf("reply\n"); 6954796Seric 6967356Seric /* 6977356Seric ** Read the input line, being careful not to hang. 6987356Seric */ 6997356Seric 70059014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 7014684Seric { 7027356Seric register char *p; 70353751Seric extern time_t curtime(); 7044684Seric 7057685Seric /* actually do the read */ 70653751Seric if (e->e_xfp != NULL) 70753751Seric (void) fflush(e->e_xfp); /* for debugging */ 7087356Seric 70910054Seric /* if we are in the process of closing just give the code */ 71053740Seric if (mci->mci_state == MCIS_CLOSED) 71110054Seric return (SMTPCLOSING); 71210054Seric 71358680Seric if (mci->mci_out != NULL) 71458680Seric fflush(mci->mci_out); 71558680Seric 71610054Seric /* get the line from the other side */ 71761093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 71853751Seric mci->mci_lastuse = curtime(); 71953751Seric 72010054Seric if (p == NULL) 72110131Seric { 72263753Seric bool oldholderrs; 72310148Seric extern char MsgBuf[]; /* err.c */ 72410148Seric 72521065Seric /* if the remote end closed early, fake an error */ 72621065Seric if (errno == 0) 72721065Seric # ifdef ECONNRESET 72821065Seric errno = ECONNRESET; 72956795Seric # else /* ECONNRESET */ 73021065Seric errno = EPIPE; 73156795Seric # endif /* ECONNRESET */ 73221065Seric 73357379Seric mci->mci_errno = errno; 73457642Seric mci->mci_exitstat = EX_TEMPFAIL; 73563753Seric oldholderrs = HoldErrs; 73663753Seric HoldErrs = TRUE; 73763753Seric usrerr("451 reply: read error from %s", mci->mci_host); 73863753Seric 73910420Seric /* if debugging, pause so we can see state */ 74010420Seric if (tTd(18, 100)) 74110420Seric pause(); 74254967Seric mci->mci_state = MCIS_ERROR; 74353751Seric smtpquit(m, mci, e); 74463753Seric #ifdef XDEBUG 74563753Seric { 74663753Seric char wbuf[MAXLINE]; 74764082Seric char *p = wbuf; 74864082Seric if (e->e_to != NULL) 74964082Seric { 75064082Seric sprintf(p, "%s... ", e->e_to); 75164082Seric p += strlen(p); 75264082Seric } 75364082Seric sprintf(p, "reply(%s) during %s", 75464082Seric mci->mci_host, SmtpPhase); 75563753Seric checkfd012(wbuf); 75663753Seric } 75763753Seric #endif 75863753Seric HoldErrs = oldholderrs; 75910054Seric return (-1); 76010131Seric } 76158957Seric fixcrlf(bufp, TRUE); 76210054Seric 76363753Seric /* EHLO failure is not a real error */ 76463753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 76563753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 76614900Seric { 76714900Seric /* serious error -- log the previous command */ 76864071Seric if (SmtpNeedIntro) 76964071Seric { 77064071Seric /* inform user who we are chatting with */ 77164071Seric fprintf(CurEnv->e_xfp, 77264071Seric "... while talking to %s:\n", 77364071Seric CurHostName); 77464071Seric SmtpNeedIntro = FALSE; 77564071Seric } 77659014Seric if (SmtpMsgBuffer[0] != '\0') 77759014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 77859014Seric SmtpMsgBuffer[0] = '\0'; 77914900Seric 78014900Seric /* now log the message as from the other side */ 78158957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 78214900Seric } 78314900Seric 78414900Seric /* display the input for verbose mode */ 78558120Seric if (Verbose) 78659956Seric nmessage("050 %s", bufp); 7877356Seric 78859285Seric /* process the line */ 78959285Seric if (pfunc != NULL && !firstline) 79059285Seric (*pfunc)(bufp, m, mci, e); 79159285Seric 79259285Seric firstline = FALSE; 79359285Seric 7947356Seric /* if continuation is required, we can go on */ 79559014Seric if (bufp[3] == '-') 7964684Seric continue; 7977356Seric 79859014Seric /* ignore improperly formated input */ 79959014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 80059014Seric continue; 80159014Seric 8027356Seric /* decode the reply code */ 80359014Seric r = atoi(bufp); 8047356Seric 8057356Seric /* extra semantics: 0xx codes are "informational" */ 80659014Seric if (r >= 100) 80759014Seric break; 80859014Seric } 8097356Seric 81059014Seric /* 81159014Seric ** Now look at SmtpReplyBuffer -- only care about the first 81259014Seric ** line of the response from here on out. 81359014Seric */ 81458061Seric 81559014Seric /* save temporary failure messages for posterity */ 81659014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 81759014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8189391Seric 81959014Seric /* reply code 421 is "Service Shutting Down" */ 82059014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 82159014Seric { 82259014Seric /* send the quit protocol */ 82359014Seric mci->mci_state = MCIS_SSD; 82459014Seric smtpquit(m, mci, e); 8254684Seric } 82659014Seric 82759014Seric return (r); 8284684Seric } 8294796Seric /* 8304865Seric ** SMTPMESSAGE -- send message to server 8314796Seric ** 8324796Seric ** Parameters: 8334796Seric ** f -- format 83410175Seric ** m -- the mailer to control formatting. 8354796Seric ** a, b, c -- parameters 8364796Seric ** 8374796Seric ** Returns: 8384796Seric ** none. 8394796Seric ** 8404796Seric ** Side Effects: 84153740Seric ** writes message to mci->mci_out. 8424796Seric */ 8434796Seric 8444865Seric /*VARARGS1*/ 84557642Seric #ifdef __STDC__ 84657642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 84757642Seric #else 84857642Seric smtpmessage(f, m, mci, va_alist) 8494796Seric char *f; 85010175Seric MAILER *m; 85154967Seric MCI *mci; 85257642Seric va_dcl 85357642Seric #endif 8544796Seric { 85556852Seric VA_LOCAL_DECL 85656852Seric 85757135Seric VA_START(mci); 85856852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 85956852Seric VA_END; 86058680Seric 86158120Seric if (tTd(18, 1) || Verbose) 86258151Seric nmessage(">>> %s", SmtpMsgBuffer); 86363753Seric if (TrafficLogFile != NULL) 86463753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 86553740Seric if (mci->mci_out != NULL) 86658680Seric { 86753740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 86854967Seric m == NULL ? "\r\n" : m->m_eol); 86958680Seric } 87059149Seric else if (tTd(18, 1)) 87158725Seric { 87259149Seric printf("smtpmessage: NULL mci_out\n"); 87358725Seric } 8744796Seric } 8755182Seric 87656795Seric # endif /* SMTP */ 877