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*65194Seric static char sccsid[] = "@(#)usersmtp.c 8.16 (Berkeley) 12/22/93 (with SMTP)"; 1433731Sbostic #else 15*65194Seric static char sccsid[] = "@(#)usersmtp.c 8.16 (Berkeley) 12/22/93 (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; 29953751Seric char buf[MAXNAME]; 30059285Seric char optbuf[MAXLINE]; 30153751Seric 30263753Seric if (tTd(18, 2)) 30357943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 30457943Seric 30559285Seric /* set up appropriate options to include */ 30664254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 30759285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 30859285Seric else 30959285Seric strcpy(optbuf, ""); 31059285Seric 3119315Seric /* 3124865Seric ** Send the MAIL command. 3134865Seric ** Designates the sender. 3144865Seric */ 3154796Seric 31653751Seric mci->mci_state = MCIS_ACTIVE; 31753751Seric 31859540Seric if (bitset(EF_RESPONSE, e->e_flags) && 31959540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 32058680Seric (void) strcpy(buf, ""); 32158680Seric else 32258680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 32353751Seric if (e->e_from.q_mailer == LocalMailer || 32410688Seric !bitnset(M_FROMPATH, m->m_flags)) 3258436Seric { 32659285Seric smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); 3278436Seric } 3288436Seric else 3298436Seric { 33059285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 33159285Seric buf[0] == '@' ? ',' : ':', buf, optbuf); 3328436Seric } 33361093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 33453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 33559285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3368005Seric if (r < 0 || REPLYTYPE(r) == 4) 33753751Seric { 33853751Seric mci->mci_exitstat = EX_TEMPFAIL; 33953751Seric mci->mci_errno = errno; 34053751Seric smtpquit(m, mci, e); 34153751Seric return EX_TEMPFAIL; 34253751Seric } 3437963Seric else if (r == 250) 34453751Seric { 34553751Seric mci->mci_exitstat = EX_OK; 34653751Seric return EX_OK; 34753751Seric } 3487963Seric else if (r == 552) 34953751Seric { 35053751Seric /* signal service unavailable */ 35153751Seric mci->mci_exitstat = EX_UNAVAILABLE; 35253751Seric smtpquit(m, mci, e); 35353751Seric return EX_UNAVAILABLE; 35453751Seric } 35514913Seric 35658008Seric #ifdef LOG 35758020Seric if (LogLevel > 1) 35858008Seric { 35958008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 36058008Seric e->e_id, SmtpReplyBuffer); 36158008Seric } 36258008Seric #endif 36358008Seric 36414913Seric /* protocol error -- close up */ 36553751Seric smtpquit(m, mci, e); 36653751Seric mci->mci_exitstat = EX_PROTOCOL; 36753751Seric return EX_PROTOCOL; 3684684Seric } 3694684Seric /* 3704976Seric ** SMTPRCPT -- designate recipient. 3714797Seric ** 3724797Seric ** Parameters: 3734865Seric ** to -- address of recipient. 37410175Seric ** m -- the mailer we are sending to. 37557379Seric ** mci -- the connection info for this transaction. 37657379Seric ** e -- the envelope for this transaction. 3774797Seric ** 3784797Seric ** Returns: 3794865Seric ** exit status corresponding to recipient status. 3804797Seric ** 3814797Seric ** Side Effects: 3824865Seric ** Sends the mail via SMTP. 3834797Seric */ 3844797Seric 38553751Seric smtprcpt(to, m, mci, e) 3864865Seric ADDRESS *to; 38710175Seric register MAILER *m; 38854967Seric MCI *mci; 38953751Seric ENVELOPE *e; 3904797Seric { 3914797Seric register int r; 3924797Seric 39353751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 3944865Seric 39561093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 39653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 39759285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 3988005Seric if (r < 0 || REPLYTYPE(r) == 4) 3994865Seric return (EX_TEMPFAIL); 4007963Seric else if (REPLYTYPE(r) == 2) 4017963Seric return (EX_OK); 4027964Seric else if (r == 550 || r == 551 || r == 553) 4037964Seric return (EX_NOUSER); 4047964Seric else if (r == 552 || r == 554) 4057964Seric return (EX_UNAVAILABLE); 40658008Seric 40758008Seric #ifdef LOG 40858020Seric if (LogLevel > 1) 40958008Seric { 41058008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 41158008Seric e->e_id, SmtpReplyBuffer); 41258008Seric } 41358008Seric #endif 41458008Seric 4157964Seric return (EX_PROTOCOL); 4164797Seric } 4174797Seric /* 41810175Seric ** SMTPDATA -- send the data and clean up the transaction. 4194684Seric ** 4204684Seric ** Parameters: 4214865Seric ** m -- mailer being sent to. 4226980Seric ** e -- the envelope for this message. 4234684Seric ** 4244684Seric ** Returns: 4254976Seric ** exit status corresponding to DATA command. 4264684Seric ** 4274684Seric ** Side Effects: 4284865Seric ** none. 4294684Seric */ 4304684Seric 43163753Seric static jmp_buf CtxDataTimeout; 43263937Seric static int datatimeout(); 43363753Seric 43453740Seric smtpdata(m, mci, e) 4354865Seric struct mailer *m; 43654967Seric register MCI *mci; 4376980Seric register ENVELOPE *e; 4384684Seric { 4394684Seric register int r; 44063753Seric register EVENT *ev; 44163753Seric time_t timeout; 4424684Seric 4434797Seric /* 4444797Seric ** Send the data. 44510175Seric ** First send the command and check that it is ok. 44610175Seric ** Then send the data. 44710175Seric ** Follow it up with a dot to terminate. 44810175Seric ** Finally get the results of the transaction. 4494797Seric */ 4504797Seric 45110175Seric /* send the command and check ok to proceed */ 45253751Seric smtpmessage("DATA", m, mci); 45361093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 45453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 45559285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4568005Seric if (r < 0 || REPLYTYPE(r) == 4) 45757990Seric { 45857990Seric smtpquit(m, mci, e); 4594797Seric return (EX_TEMPFAIL); 46057990Seric } 4617963Seric else if (r == 554) 46257990Seric { 46357990Seric smtprset(m, mci, e); 4647963Seric return (EX_UNAVAILABLE); 46557990Seric } 4667963Seric else if (r != 354) 46757990Seric { 46858008Seric #ifdef LOG 46958020Seric if (LogLevel > 1) 47058008Seric { 47158008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 47258008Seric e->e_id, SmtpReplyBuffer); 47358008Seric } 47458008Seric #endif 47557990Seric smtprset(m, mci, e); 4767964Seric return (EX_PROTOCOL); 47757990Seric } 47810175Seric 47963757Seric /* 48063757Seric ** Set timeout around data writes. Make it at least large 48163757Seric ** enough for DNS timeouts on all recipients plus some fudge 48263757Seric ** factor. The main thing is that it should not be infinite. 48363757Seric */ 48463757Seric 48563753Seric if (setjmp(CtxDataTimeout) != 0) 48663753Seric { 48763753Seric mci->mci_errno = errno; 48863753Seric mci->mci_exitstat = EX_TEMPFAIL; 48963753Seric mci->mci_state = MCIS_ERROR; 49063753Seric syserr("451 timeout writing message to %s", mci->mci_host); 49163753Seric smtpquit(m, mci, e); 49263753Seric return EX_TEMPFAIL; 49363753Seric } 49463753Seric 49563787Seric timeout = e->e_msgsize / 16; 49663787Seric if (timeout < (time_t) 60) 49763787Seric timeout = (time_t) 60; 49863787Seric timeout += e->e_nrcpts * 90; 49963753Seric ev = setevent(timeout, datatimeout, 0); 50063753Seric 50110175Seric /* now output the actual message */ 50253751Seric (*e->e_puthdr)(mci->mci_out, m, e); 50353740Seric putline("\n", mci->mci_out, m); 50459730Seric (*e->e_putbody)(mci->mci_out, m, e, NULL); 50510175Seric 50663753Seric clrevent(ev); 50763753Seric 50864718Seric if (ferror(mci->mci_out)) 50964718Seric { 51064718Seric /* error during processing -- don't send the dot */ 51164718Seric mci->mci_errno = EIO; 51264718Seric mci->mci_exitstat = EX_IOERR; 51364718Seric mci->mci_state = MCIS_ERROR; 51464718Seric smtpquit(m, mci, e); 51564718Seric return EX_IOERR; 51664718Seric } 51764718Seric 51810175Seric /* terminate the message */ 51953740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 52063753Seric if (TrafficLogFile != NULL) 52163753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 52258120Seric if (Verbose) 52358151Seric nmessage(">>> ."); 52410175Seric 52510175Seric /* check for the results of the transaction */ 52661093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 52753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 52859285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 52953751Seric if (r < 0) 53057990Seric { 53157990Seric smtpquit(m, mci, e); 5324797Seric return (EX_TEMPFAIL); 53357990Seric } 53453751Seric mci->mci_state = MCIS_OPEN; 53558917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 53653751Seric if (REPLYTYPE(r) == 4) 53753751Seric return (EX_TEMPFAIL); 5387963Seric else if (r == 250) 5397963Seric return (EX_OK); 5407963Seric else if (r == 552 || r == 554) 5417963Seric return (EX_UNAVAILABLE); 54258008Seric #ifdef LOG 54358020Seric if (LogLevel > 1) 54458008Seric { 54558008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 54658008Seric e->e_id, SmtpReplyBuffer); 54758008Seric } 54858008Seric #endif 5497964Seric return (EX_PROTOCOL); 5504684Seric } 55163753Seric 55263753Seric 55363753Seric static int 55463753Seric datatimeout() 55563753Seric { 55663753Seric longjmp(CtxDataTimeout, 1); 55763753Seric } 5584684Seric /* 5594865Seric ** SMTPQUIT -- close the SMTP connection. 5604865Seric ** 5614865Seric ** Parameters: 56215535Seric ** m -- a pointer to the mailer. 5634865Seric ** 5644865Seric ** Returns: 5654865Seric ** none. 5664865Seric ** 5674865Seric ** Side Effects: 5684865Seric ** sends the final protocol and closes the connection. 5694865Seric */ 5704865Seric 57153751Seric smtpquit(m, mci, e) 57253751Seric register MAILER *m; 57354967Seric register MCI *mci; 57453751Seric ENVELOPE *e; 5754865Seric { 57664822Seric bool oldSuprErrs = SuprErrs; 5774865Seric 57864822Seric /* 57964822Seric ** Suppress errors here -- we may be processing a different 58064822Seric ** job when we do the quit connection, and we don't want the 58164822Seric ** new job to be penalized for something that isn't it's 58264822Seric ** problem. 58364822Seric */ 58464822Seric 58564822Seric SuprErrs = TRUE; 58664822Seric 58754967Seric /* send the quit message if we haven't gotten I/O error */ 58853751Seric if (mci->mci_state != MCIS_ERROR) 5899391Seric { 59061093Seric SmtpPhase = "client QUIT"; 59153751Seric smtpmessage("QUIT", m, mci); 59259285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 59364822Seric SuprErrs = oldSuprErrs; 59453740Seric if (mci->mci_state == MCIS_CLOSED) 59564822Seric { 59664822Seric SuprErrs = oldSuprErrs; 59710159Seric return; 59864822Seric } 5999391Seric } 6009391Seric 60152676Seric /* now actually close the connection and pick up the zombie */ 602*65194Seric (void) endmailer(mci, e, NULL); 60364822Seric 60464822Seric SuprErrs = oldSuprErrs; 6054865Seric } 6064865Seric /* 60754967Seric ** SMTPRSET -- send a RSET (reset) command 60854967Seric */ 60954967Seric 61054967Seric smtprset(m, mci, e) 61154967Seric register MAILER *m; 61254967Seric register MCI *mci; 61354967Seric ENVELOPE *e; 61454967Seric { 61554967Seric int r; 61654967Seric 61761093Seric SmtpPhase = "client RSET"; 61854967Seric smtpmessage("RSET", m, mci); 61959285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 62057734Seric if (r < 0) 62157734Seric mci->mci_state = MCIS_ERROR; 62254967Seric else if (REPLYTYPE(r) == 2) 62357734Seric { 62457734Seric mci->mci_state = MCIS_OPEN; 62557734Seric return; 62657734Seric } 62757734Seric smtpquit(m, mci, e); 62854967Seric } 62954967Seric /* 63058867Seric ** SMTPPROBE -- check the connection state 63154967Seric */ 63254967Seric 63358867Seric smtpprobe(mci) 63454967Seric register MCI *mci; 63554967Seric { 63654967Seric int r; 63754967Seric MAILER *m = mci->mci_mailer; 63854967Seric extern ENVELOPE BlankEnvelope; 63954967Seric ENVELOPE *e = &BlankEnvelope; 64054967Seric 64161093Seric SmtpPhase = "client probe"; 64258867Seric smtpmessage("RSET", m, mci); 64359285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 64458061Seric if (r < 0 || REPLYTYPE(r) != 2) 64554967Seric smtpquit(m, mci, e); 64654967Seric return r; 64754967Seric } 64854967Seric /* 6494684Seric ** REPLY -- read arpanet reply 6504684Seric ** 6514684Seric ** Parameters: 65210175Seric ** m -- the mailer we are reading the reply from. 65357379Seric ** mci -- the mailer connection info structure. 65457379Seric ** e -- the current envelope. 65557379Seric ** timeout -- the timeout for reads. 65659285Seric ** pfunc -- processing function for second and subsequent 65759285Seric ** lines of response -- if null, no special 65859285Seric ** processing is done. 6594684Seric ** 6604684Seric ** Returns: 6614684Seric ** reply code it reads. 6624684Seric ** 6634684Seric ** Side Effects: 6644684Seric ** flushes the mail file. 6654684Seric */ 6664684Seric 66759285Seric reply(m, mci, e, timeout, pfunc) 66853751Seric MAILER *m; 66954967Seric MCI *mci; 67053751Seric ENVELOPE *e; 67159285Seric time_t timeout; 67259285Seric void (*pfunc)(); 6734684Seric { 67459014Seric register char *bufp; 67559014Seric register int r; 67659285Seric bool firstline = TRUE; 67758957Seric char junkbuf[MAXLINE]; 67858957Seric 67957379Seric if (mci->mci_out != NULL) 68057379Seric (void) fflush(mci->mci_out); 6814684Seric 6827677Seric if (tTd(18, 1)) 6834796Seric printf("reply\n"); 6844796Seric 6857356Seric /* 6867356Seric ** Read the input line, being careful not to hang. 6877356Seric */ 6887356Seric 68959014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 6904684Seric { 6917356Seric register char *p; 69253751Seric extern time_t curtime(); 6934684Seric 6947685Seric /* actually do the read */ 69553751Seric if (e->e_xfp != NULL) 69653751Seric (void) fflush(e->e_xfp); /* for debugging */ 6977356Seric 69810054Seric /* if we are in the process of closing just give the code */ 69953740Seric if (mci->mci_state == MCIS_CLOSED) 70010054Seric return (SMTPCLOSING); 70110054Seric 70258680Seric if (mci->mci_out != NULL) 70358680Seric fflush(mci->mci_out); 70458680Seric 70510054Seric /* get the line from the other side */ 70661093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 70753751Seric mci->mci_lastuse = curtime(); 70853751Seric 70910054Seric if (p == NULL) 71010131Seric { 71163753Seric bool oldholderrs; 71210148Seric extern char MsgBuf[]; /* err.c */ 71310148Seric 71421065Seric /* if the remote end closed early, fake an error */ 71521065Seric if (errno == 0) 71621065Seric # ifdef ECONNRESET 71721065Seric errno = ECONNRESET; 71856795Seric # else /* ECONNRESET */ 71921065Seric errno = EPIPE; 72056795Seric # endif /* ECONNRESET */ 72121065Seric 72257379Seric mci->mci_errno = errno; 72357642Seric mci->mci_exitstat = EX_TEMPFAIL; 72463753Seric oldholderrs = HoldErrs; 72563753Seric HoldErrs = TRUE; 72663753Seric usrerr("451 reply: read error from %s", mci->mci_host); 72763753Seric 72810420Seric /* if debugging, pause so we can see state */ 72910420Seric if (tTd(18, 100)) 73010420Seric pause(); 73154967Seric mci->mci_state = MCIS_ERROR; 73253751Seric smtpquit(m, mci, e); 73363753Seric #ifdef XDEBUG 73463753Seric { 73563753Seric char wbuf[MAXLINE]; 73664082Seric char *p = wbuf; 73764082Seric if (e->e_to != NULL) 73864082Seric { 73964082Seric sprintf(p, "%s... ", e->e_to); 74064082Seric p += strlen(p); 74164082Seric } 74264082Seric sprintf(p, "reply(%s) during %s", 74364082Seric mci->mci_host, SmtpPhase); 74463753Seric checkfd012(wbuf); 74563753Seric } 74663753Seric #endif 74763753Seric HoldErrs = oldholderrs; 74810054Seric return (-1); 74910131Seric } 75058957Seric fixcrlf(bufp, TRUE); 75110054Seric 75263753Seric /* EHLO failure is not a real error */ 75363753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 75463753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 75514900Seric { 75614900Seric /* serious error -- log the previous command */ 75764071Seric if (SmtpNeedIntro) 75864071Seric { 75964071Seric /* inform user who we are chatting with */ 76064071Seric fprintf(CurEnv->e_xfp, 76164071Seric "... while talking to %s:\n", 76264071Seric CurHostName); 76364071Seric SmtpNeedIntro = FALSE; 76464071Seric } 76559014Seric if (SmtpMsgBuffer[0] != '\0') 76659014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 76759014Seric SmtpMsgBuffer[0] = '\0'; 76814900Seric 76914900Seric /* now log the message as from the other side */ 77058957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 77114900Seric } 77214900Seric 77314900Seric /* display the input for verbose mode */ 77458120Seric if (Verbose) 77559956Seric nmessage("050 %s", bufp); 7767356Seric 77759285Seric /* process the line */ 77859285Seric if (pfunc != NULL && !firstline) 77959285Seric (*pfunc)(bufp, m, mci, e); 78059285Seric 78159285Seric firstline = FALSE; 78259285Seric 7837356Seric /* if continuation is required, we can go on */ 78459014Seric if (bufp[3] == '-') 7854684Seric continue; 7867356Seric 78759014Seric /* ignore improperly formated input */ 78859014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 78959014Seric continue; 79059014Seric 7917356Seric /* decode the reply code */ 79259014Seric r = atoi(bufp); 7937356Seric 7947356Seric /* extra semantics: 0xx codes are "informational" */ 79559014Seric if (r >= 100) 79659014Seric break; 79759014Seric } 7987356Seric 79959014Seric /* 80059014Seric ** Now look at SmtpReplyBuffer -- only care about the first 80159014Seric ** line of the response from here on out. 80259014Seric */ 80358061Seric 80459014Seric /* save temporary failure messages for posterity */ 80559014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 80659014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8079391Seric 80859014Seric /* reply code 421 is "Service Shutting Down" */ 80959014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 81059014Seric { 81159014Seric /* send the quit protocol */ 81259014Seric mci->mci_state = MCIS_SSD; 81359014Seric smtpquit(m, mci, e); 8144684Seric } 81559014Seric 81659014Seric return (r); 8174684Seric } 8184796Seric /* 8194865Seric ** SMTPMESSAGE -- send message to server 8204796Seric ** 8214796Seric ** Parameters: 8224796Seric ** f -- format 82310175Seric ** m -- the mailer to control formatting. 8244796Seric ** a, b, c -- parameters 8254796Seric ** 8264796Seric ** Returns: 8274796Seric ** none. 8284796Seric ** 8294796Seric ** Side Effects: 83053740Seric ** writes message to mci->mci_out. 8314796Seric */ 8324796Seric 8334865Seric /*VARARGS1*/ 83457642Seric #ifdef __STDC__ 83557642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 83657642Seric #else 83757642Seric smtpmessage(f, m, mci, va_alist) 8384796Seric char *f; 83910175Seric MAILER *m; 84054967Seric MCI *mci; 84157642Seric va_dcl 84257642Seric #endif 8434796Seric { 84456852Seric VA_LOCAL_DECL 84556852Seric 84657135Seric VA_START(mci); 84756852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 84856852Seric VA_END; 84958680Seric 85058120Seric if (tTd(18, 1) || Verbose) 85158151Seric nmessage(">>> %s", SmtpMsgBuffer); 85263753Seric if (TrafficLogFile != NULL) 85363753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 85453740Seric if (mci->mci_out != NULL) 85558680Seric { 85653740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 85754967Seric m == NULL ? "\r\n" : m->m_eol); 85858680Seric } 85959149Seric else if (tTd(18, 1)) 86058725Seric { 86159149Seric printf("smtpmessage: NULL mci_out\n"); 86258725Seric } 8634796Seric } 8645182Seric 86556795Seric # endif /* SMTP */ 866