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*64822Seric static char sccsid[] = "@(#)usersmtp.c 8.14 (Berkeley) 11/08/93 (with SMTP)"; 1433731Sbostic #else 15*64822Seric static char sccsid[] = "@(#)usersmtp.c 8.14 (Berkeley) 11/08/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 19553751Seric mci->mci_state = MCIS_OPEN; 19654967Seric return; 19753751Seric 19853751Seric tempfail1: 19953751Seric tempfail2: 20053751Seric mci->mci_exitstat = EX_TEMPFAIL; 20157379Seric if (mci->mci_errno == 0) 20257379Seric mci->mci_errno = errno; 20357379Seric if (mci->mci_state != MCIS_CLOSED) 20457379Seric smtpquit(m, mci, e); 20554967Seric return; 20653751Seric 20753751Seric unavailable: 20853751Seric mci->mci_exitstat = EX_UNAVAILABLE; 20953751Seric mci->mci_errno = errno; 21053751Seric smtpquit(m, mci, e); 21154967Seric return; 21253751Seric } 21359285Seric /* 21460210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 21560210Seric ** 21660210Seric ** 21760210Seric ** Parameters: 21860210Seric ** line -- the response line. 21960210Seric ** m -- the mailer. 22060210Seric ** mci -- the mailer connection info. 22160210Seric ** e -- the envelope. 22260210Seric ** 22360210Seric ** Returns: 22460210Seric ** none. 22560210Seric */ 22660210Seric 22760210Seric void 22860210Seric esmtp_check(line, m, mci, e) 22960210Seric char *line; 23060210Seric MAILER *m; 23160210Seric register MCI *mci; 23260210Seric ENVELOPE *e; 23360210Seric { 23460210Seric if (strlen(line) < 5) 23560210Seric return; 23660210Seric line += 4; 23760210Seric if (strncmp(line, "ESMTP ", 6) == 0) 23860210Seric mci->mci_flags |= MCIF_ESMTP; 23960210Seric } 24060210Seric /* 24159285Seric ** HELO_OPTIONS -- process the options on a HELO line. 24259285Seric ** 24359285Seric ** Parameters: 24459285Seric ** line -- the response line. 24559285Seric ** m -- the mailer. 24659285Seric ** mci -- the mailer connection info. 24759285Seric ** e -- the envelope. 24859285Seric ** 24959285Seric ** Returns: 25059285Seric ** none. 25159285Seric */ 25253751Seric 25359285Seric void 25459285Seric helo_options(line, m, mci, e) 25559285Seric char *line; 25659285Seric MAILER *m; 25759285Seric register MCI *mci; 25859285Seric ENVELOPE *e; 25959285Seric { 26059285Seric register char *p; 26159285Seric 26259285Seric if (strlen(line) < 5) 26359285Seric return; 26459285Seric line += 4; 26559285Seric p = strchr(line, ' '); 26659285Seric if (p != NULL) 26759285Seric *p++ = '\0'; 26859285Seric if (strcasecmp(line, "size") == 0) 26959285Seric { 27059285Seric mci->mci_flags |= MCIF_SIZE; 27159285Seric if (p != NULL) 27259285Seric mci->mci_maxsize = atol(p); 27359285Seric } 27459285Seric else if (strcasecmp(line, "8bitmime") == 0) 27559285Seric mci->mci_flags |= MCIF_8BITMIME; 27659285Seric else if (strcasecmp(line, "expn") == 0) 27759285Seric mci->mci_flags |= MCIF_EXPN; 27859285Seric } 27959285Seric /* 28059285Seric ** SMTPMAILFROM -- send MAIL command 28159285Seric ** 28259285Seric ** Parameters: 28359285Seric ** m -- the mailer. 28459285Seric ** mci -- the mailer connection structure. 28559285Seric ** e -- the envelope (including the sender to specify). 28659285Seric */ 28759285Seric 28853751Seric smtpmailfrom(m, mci, e) 28953751Seric struct mailer *m; 29054967Seric MCI *mci; 29153751Seric ENVELOPE *e; 29253751Seric { 29353751Seric int r; 29453751Seric char buf[MAXNAME]; 29559285Seric char optbuf[MAXLINE]; 29653751Seric 29763753Seric if (tTd(18, 2)) 29857943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 29957943Seric 30059285Seric /* set up appropriate options to include */ 30164254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 30259285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 30359285Seric else 30459285Seric strcpy(optbuf, ""); 30559285Seric 3069315Seric /* 3074865Seric ** Send the MAIL command. 3084865Seric ** Designates the sender. 3094865Seric */ 3104796Seric 31153751Seric mci->mci_state = MCIS_ACTIVE; 31253751Seric 31359540Seric if (bitset(EF_RESPONSE, e->e_flags) && 31459540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 31558680Seric (void) strcpy(buf, ""); 31658680Seric else 31758680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 31853751Seric if (e->e_from.q_mailer == LocalMailer || 31910688Seric !bitnset(M_FROMPATH, m->m_flags)) 3208436Seric { 32159285Seric smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); 3228436Seric } 3238436Seric else 3248436Seric { 32559285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 32659285Seric buf[0] == '@' ? ',' : ':', buf, optbuf); 3278436Seric } 32861093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 32953751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 33059285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3318005Seric if (r < 0 || REPLYTYPE(r) == 4) 33253751Seric { 33353751Seric mci->mci_exitstat = EX_TEMPFAIL; 33453751Seric mci->mci_errno = errno; 33553751Seric smtpquit(m, mci, e); 33653751Seric return EX_TEMPFAIL; 33753751Seric } 3387963Seric else if (r == 250) 33953751Seric { 34053751Seric mci->mci_exitstat = EX_OK; 34153751Seric return EX_OK; 34253751Seric } 3437963Seric else if (r == 552) 34453751Seric { 34553751Seric /* signal service unavailable */ 34653751Seric mci->mci_exitstat = EX_UNAVAILABLE; 34753751Seric smtpquit(m, mci, e); 34853751Seric return EX_UNAVAILABLE; 34953751Seric } 35014913Seric 35158008Seric #ifdef LOG 35258020Seric if (LogLevel > 1) 35358008Seric { 35458008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 35558008Seric e->e_id, SmtpReplyBuffer); 35658008Seric } 35758008Seric #endif 35858008Seric 35914913Seric /* protocol error -- close up */ 36053751Seric smtpquit(m, mci, e); 36153751Seric mci->mci_exitstat = EX_PROTOCOL; 36253751Seric return EX_PROTOCOL; 3634684Seric } 3644684Seric /* 3654976Seric ** SMTPRCPT -- designate recipient. 3664797Seric ** 3674797Seric ** Parameters: 3684865Seric ** to -- address of recipient. 36910175Seric ** m -- the mailer we are sending to. 37057379Seric ** mci -- the connection info for this transaction. 37157379Seric ** e -- the envelope for this transaction. 3724797Seric ** 3734797Seric ** Returns: 3744865Seric ** exit status corresponding to recipient status. 3754797Seric ** 3764797Seric ** Side Effects: 3774865Seric ** Sends the mail via SMTP. 3784797Seric */ 3794797Seric 38053751Seric smtprcpt(to, m, mci, e) 3814865Seric ADDRESS *to; 38210175Seric register MAILER *m; 38354967Seric MCI *mci; 38453751Seric ENVELOPE *e; 3854797Seric { 3864797Seric register int r; 3874797Seric 38853751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 3894865Seric 39061093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 39153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 39259285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 3938005Seric if (r < 0 || REPLYTYPE(r) == 4) 3944865Seric return (EX_TEMPFAIL); 3957963Seric else if (REPLYTYPE(r) == 2) 3967963Seric return (EX_OK); 3977964Seric else if (r == 550 || r == 551 || r == 553) 3987964Seric return (EX_NOUSER); 3997964Seric else if (r == 552 || r == 554) 4007964Seric return (EX_UNAVAILABLE); 40158008Seric 40258008Seric #ifdef LOG 40358020Seric if (LogLevel > 1) 40458008Seric { 40558008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 40658008Seric e->e_id, SmtpReplyBuffer); 40758008Seric } 40858008Seric #endif 40958008Seric 4107964Seric return (EX_PROTOCOL); 4114797Seric } 4124797Seric /* 41310175Seric ** SMTPDATA -- send the data and clean up the transaction. 4144684Seric ** 4154684Seric ** Parameters: 4164865Seric ** m -- mailer being sent to. 4176980Seric ** e -- the envelope for this message. 4184684Seric ** 4194684Seric ** Returns: 4204976Seric ** exit status corresponding to DATA command. 4214684Seric ** 4224684Seric ** Side Effects: 4234865Seric ** none. 4244684Seric */ 4254684Seric 42663753Seric static jmp_buf CtxDataTimeout; 42763937Seric static int datatimeout(); 42863753Seric 42953740Seric smtpdata(m, mci, e) 4304865Seric struct mailer *m; 43154967Seric register MCI *mci; 4326980Seric register ENVELOPE *e; 4334684Seric { 4344684Seric register int r; 43563753Seric register EVENT *ev; 43663753Seric time_t timeout; 4374684Seric 4384797Seric /* 4394797Seric ** Send the data. 44010175Seric ** First send the command and check that it is ok. 44110175Seric ** Then send the data. 44210175Seric ** Follow it up with a dot to terminate. 44310175Seric ** Finally get the results of the transaction. 4444797Seric */ 4454797Seric 44610175Seric /* send the command and check ok to proceed */ 44753751Seric smtpmessage("DATA", m, mci); 44861093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 44953751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 45059285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4518005Seric if (r < 0 || REPLYTYPE(r) == 4) 45257990Seric { 45357990Seric smtpquit(m, mci, e); 4544797Seric return (EX_TEMPFAIL); 45557990Seric } 4567963Seric else if (r == 554) 45757990Seric { 45857990Seric smtprset(m, mci, e); 4597963Seric return (EX_UNAVAILABLE); 46057990Seric } 4617963Seric else if (r != 354) 46257990Seric { 46358008Seric #ifdef LOG 46458020Seric if (LogLevel > 1) 46558008Seric { 46658008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 46758008Seric e->e_id, SmtpReplyBuffer); 46858008Seric } 46958008Seric #endif 47057990Seric smtprset(m, mci, e); 4717964Seric return (EX_PROTOCOL); 47257990Seric } 47310175Seric 47463757Seric /* 47563757Seric ** Set timeout around data writes. Make it at least large 47663757Seric ** enough for DNS timeouts on all recipients plus some fudge 47763757Seric ** factor. The main thing is that it should not be infinite. 47863757Seric */ 47963757Seric 48063753Seric if (setjmp(CtxDataTimeout) != 0) 48163753Seric { 48263753Seric mci->mci_errno = errno; 48363753Seric mci->mci_exitstat = EX_TEMPFAIL; 48463753Seric mci->mci_state = MCIS_ERROR; 48563753Seric syserr("451 timeout writing message to %s", mci->mci_host); 48663753Seric smtpquit(m, mci, e); 48763753Seric return EX_TEMPFAIL; 48863753Seric } 48963753Seric 49063787Seric timeout = e->e_msgsize / 16; 49163787Seric if (timeout < (time_t) 60) 49263787Seric timeout = (time_t) 60; 49363787Seric timeout += e->e_nrcpts * 90; 49463753Seric ev = setevent(timeout, datatimeout, 0); 49563753Seric 49610175Seric /* now output the actual message */ 49753751Seric (*e->e_puthdr)(mci->mci_out, m, e); 49853740Seric putline("\n", mci->mci_out, m); 49959730Seric (*e->e_putbody)(mci->mci_out, m, e, NULL); 50010175Seric 50163753Seric clrevent(ev); 50263753Seric 50364718Seric if (ferror(mci->mci_out)) 50464718Seric { 50564718Seric /* error during processing -- don't send the dot */ 50664718Seric mci->mci_errno = EIO; 50764718Seric mci->mci_exitstat = EX_IOERR; 50864718Seric mci->mci_state = MCIS_ERROR; 50964718Seric smtpquit(m, mci, e); 51064718Seric return EX_IOERR; 51164718Seric } 51264718Seric 51310175Seric /* terminate the message */ 51453740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 51563753Seric if (TrafficLogFile != NULL) 51663753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 51758120Seric if (Verbose) 51858151Seric nmessage(">>> ."); 51910175Seric 52010175Seric /* check for the results of the transaction */ 52161093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 52253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 52359285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 52453751Seric if (r < 0) 52557990Seric { 52657990Seric smtpquit(m, mci, e); 5274797Seric return (EX_TEMPFAIL); 52857990Seric } 52953751Seric mci->mci_state = MCIS_OPEN; 53058917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 53153751Seric if (REPLYTYPE(r) == 4) 53253751Seric return (EX_TEMPFAIL); 5337963Seric else if (r == 250) 5347963Seric return (EX_OK); 5357963Seric else if (r == 552 || r == 554) 5367963Seric return (EX_UNAVAILABLE); 53758008Seric #ifdef LOG 53858020Seric if (LogLevel > 1) 53958008Seric { 54058008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 54158008Seric e->e_id, SmtpReplyBuffer); 54258008Seric } 54358008Seric #endif 5447964Seric return (EX_PROTOCOL); 5454684Seric } 54663753Seric 54763753Seric 54863753Seric static int 54963753Seric datatimeout() 55063753Seric { 55163753Seric longjmp(CtxDataTimeout, 1); 55263753Seric } 5534684Seric /* 5544865Seric ** SMTPQUIT -- close the SMTP connection. 5554865Seric ** 5564865Seric ** Parameters: 55715535Seric ** m -- a pointer to the mailer. 5584865Seric ** 5594865Seric ** Returns: 5604865Seric ** none. 5614865Seric ** 5624865Seric ** Side Effects: 5634865Seric ** sends the final protocol and closes the connection. 5644865Seric */ 5654865Seric 56653751Seric smtpquit(m, mci, e) 56753751Seric register MAILER *m; 56854967Seric register MCI *mci; 56953751Seric ENVELOPE *e; 5704865Seric { 571*64822Seric bool oldSuprErrs = SuprErrs; 5724865Seric 573*64822Seric /* 574*64822Seric ** Suppress errors here -- we may be processing a different 575*64822Seric ** job when we do the quit connection, and we don't want the 576*64822Seric ** new job to be penalized for something that isn't it's 577*64822Seric ** problem. 578*64822Seric */ 579*64822Seric 580*64822Seric SuprErrs = TRUE; 581*64822Seric 58254967Seric /* send the quit message if we haven't gotten I/O error */ 58353751Seric if (mci->mci_state != MCIS_ERROR) 5849391Seric { 58561093Seric SmtpPhase = "client QUIT"; 58653751Seric smtpmessage("QUIT", m, mci); 58759285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 588*64822Seric SuprErrs = oldSuprErrs; 58953740Seric if (mci->mci_state == MCIS_CLOSED) 590*64822Seric { 591*64822Seric SuprErrs = oldSuprErrs; 59210159Seric return; 593*64822Seric } 5949391Seric } 5959391Seric 59652676Seric /* now actually close the connection and pick up the zombie */ 597*64822Seric (void) endmailer(mci, e, m->m_argv); 598*64822Seric 599*64822Seric SuprErrs = oldSuprErrs; 6004865Seric } 6014865Seric /* 60254967Seric ** SMTPRSET -- send a RSET (reset) command 60354967Seric */ 60454967Seric 60554967Seric smtprset(m, mci, e) 60654967Seric register MAILER *m; 60754967Seric register MCI *mci; 60854967Seric ENVELOPE *e; 60954967Seric { 61054967Seric int r; 61154967Seric 61261093Seric SmtpPhase = "client RSET"; 61354967Seric smtpmessage("RSET", m, mci); 61459285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 61557734Seric if (r < 0) 61657734Seric mci->mci_state = MCIS_ERROR; 61754967Seric else if (REPLYTYPE(r) == 2) 61857734Seric { 61957734Seric mci->mci_state = MCIS_OPEN; 62057734Seric return; 62157734Seric } 62257734Seric smtpquit(m, mci, e); 62354967Seric } 62454967Seric /* 62558867Seric ** SMTPPROBE -- check the connection state 62654967Seric */ 62754967Seric 62858867Seric smtpprobe(mci) 62954967Seric register MCI *mci; 63054967Seric { 63154967Seric int r; 63254967Seric MAILER *m = mci->mci_mailer; 63354967Seric extern ENVELOPE BlankEnvelope; 63454967Seric ENVELOPE *e = &BlankEnvelope; 63554967Seric 63661093Seric SmtpPhase = "client probe"; 63758867Seric smtpmessage("RSET", m, mci); 63859285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 63958061Seric if (r < 0 || REPLYTYPE(r) != 2) 64054967Seric smtpquit(m, mci, e); 64154967Seric return r; 64254967Seric } 64354967Seric /* 6444684Seric ** REPLY -- read arpanet reply 6454684Seric ** 6464684Seric ** Parameters: 64710175Seric ** m -- the mailer we are reading the reply from. 64857379Seric ** mci -- the mailer connection info structure. 64957379Seric ** e -- the current envelope. 65057379Seric ** timeout -- the timeout for reads. 65159285Seric ** pfunc -- processing function for second and subsequent 65259285Seric ** lines of response -- if null, no special 65359285Seric ** processing is done. 6544684Seric ** 6554684Seric ** Returns: 6564684Seric ** reply code it reads. 6574684Seric ** 6584684Seric ** Side Effects: 6594684Seric ** flushes the mail file. 6604684Seric */ 6614684Seric 66259285Seric reply(m, mci, e, timeout, pfunc) 66353751Seric MAILER *m; 66454967Seric MCI *mci; 66553751Seric ENVELOPE *e; 66659285Seric time_t timeout; 66759285Seric void (*pfunc)(); 6684684Seric { 66959014Seric register char *bufp; 67059014Seric register int r; 67159285Seric bool firstline = TRUE; 67258957Seric char junkbuf[MAXLINE]; 67358957Seric 67457379Seric if (mci->mci_out != NULL) 67557379Seric (void) fflush(mci->mci_out); 6764684Seric 6777677Seric if (tTd(18, 1)) 6784796Seric printf("reply\n"); 6794796Seric 6807356Seric /* 6817356Seric ** Read the input line, being careful not to hang. 6827356Seric */ 6837356Seric 68459014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 6854684Seric { 6867356Seric register char *p; 68753751Seric extern time_t curtime(); 6884684Seric 6897685Seric /* actually do the read */ 69053751Seric if (e->e_xfp != NULL) 69153751Seric (void) fflush(e->e_xfp); /* for debugging */ 6927356Seric 69310054Seric /* if we are in the process of closing just give the code */ 69453740Seric if (mci->mci_state == MCIS_CLOSED) 69510054Seric return (SMTPCLOSING); 69610054Seric 69758680Seric if (mci->mci_out != NULL) 69858680Seric fflush(mci->mci_out); 69958680Seric 70010054Seric /* get the line from the other side */ 70161093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 70253751Seric mci->mci_lastuse = curtime(); 70353751Seric 70410054Seric if (p == NULL) 70510131Seric { 70663753Seric bool oldholderrs; 70710148Seric extern char MsgBuf[]; /* err.c */ 70810148Seric 70921065Seric /* if the remote end closed early, fake an error */ 71021065Seric if (errno == 0) 71121065Seric # ifdef ECONNRESET 71221065Seric errno = ECONNRESET; 71356795Seric # else /* ECONNRESET */ 71421065Seric errno = EPIPE; 71556795Seric # endif /* ECONNRESET */ 71621065Seric 71757379Seric mci->mci_errno = errno; 71857642Seric mci->mci_exitstat = EX_TEMPFAIL; 71963753Seric oldholderrs = HoldErrs; 72063753Seric HoldErrs = TRUE; 72163753Seric usrerr("451 reply: read error from %s", mci->mci_host); 72263753Seric 72310420Seric /* if debugging, pause so we can see state */ 72410420Seric if (tTd(18, 100)) 72510420Seric pause(); 72654967Seric mci->mci_state = MCIS_ERROR; 72753751Seric smtpquit(m, mci, e); 72863753Seric #ifdef XDEBUG 72963753Seric { 73063753Seric char wbuf[MAXLINE]; 73164082Seric char *p = wbuf; 73264082Seric if (e->e_to != NULL) 73364082Seric { 73464082Seric sprintf(p, "%s... ", e->e_to); 73564082Seric p += strlen(p); 73664082Seric } 73764082Seric sprintf(p, "reply(%s) during %s", 73864082Seric mci->mci_host, SmtpPhase); 73963753Seric checkfd012(wbuf); 74063753Seric } 74163753Seric #endif 74263753Seric HoldErrs = oldholderrs; 74310054Seric return (-1); 74410131Seric } 74558957Seric fixcrlf(bufp, TRUE); 74610054Seric 74763753Seric /* EHLO failure is not a real error */ 74863753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 74963753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 75014900Seric { 75114900Seric /* serious error -- log the previous command */ 75264071Seric if (SmtpNeedIntro) 75364071Seric { 75464071Seric /* inform user who we are chatting with */ 75564071Seric fprintf(CurEnv->e_xfp, 75664071Seric "... while talking to %s:\n", 75764071Seric CurHostName); 75864071Seric SmtpNeedIntro = FALSE; 75964071Seric } 76059014Seric if (SmtpMsgBuffer[0] != '\0') 76159014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 76259014Seric SmtpMsgBuffer[0] = '\0'; 76314900Seric 76414900Seric /* now log the message as from the other side */ 76558957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 76614900Seric } 76714900Seric 76814900Seric /* display the input for verbose mode */ 76958120Seric if (Verbose) 77059956Seric nmessage("050 %s", bufp); 7717356Seric 77259285Seric /* process the line */ 77359285Seric if (pfunc != NULL && !firstline) 77459285Seric (*pfunc)(bufp, m, mci, e); 77559285Seric 77659285Seric firstline = FALSE; 77759285Seric 7787356Seric /* if continuation is required, we can go on */ 77959014Seric if (bufp[3] == '-') 7804684Seric continue; 7817356Seric 78259014Seric /* ignore improperly formated input */ 78359014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 78459014Seric continue; 78559014Seric 7867356Seric /* decode the reply code */ 78759014Seric r = atoi(bufp); 7887356Seric 7897356Seric /* extra semantics: 0xx codes are "informational" */ 79059014Seric if (r >= 100) 79159014Seric break; 79259014Seric } 7937356Seric 79459014Seric /* 79559014Seric ** Now look at SmtpReplyBuffer -- only care about the first 79659014Seric ** line of the response from here on out. 79759014Seric */ 79858061Seric 79959014Seric /* save temporary failure messages for posterity */ 80059014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 80159014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8029391Seric 80359014Seric /* reply code 421 is "Service Shutting Down" */ 80459014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 80559014Seric { 80659014Seric /* send the quit protocol */ 80759014Seric mci->mci_state = MCIS_SSD; 80859014Seric smtpquit(m, mci, e); 8094684Seric } 81059014Seric 81159014Seric return (r); 8124684Seric } 8134796Seric /* 8144865Seric ** SMTPMESSAGE -- send message to server 8154796Seric ** 8164796Seric ** Parameters: 8174796Seric ** f -- format 81810175Seric ** m -- the mailer to control formatting. 8194796Seric ** a, b, c -- parameters 8204796Seric ** 8214796Seric ** Returns: 8224796Seric ** none. 8234796Seric ** 8244796Seric ** Side Effects: 82553740Seric ** writes message to mci->mci_out. 8264796Seric */ 8274796Seric 8284865Seric /*VARARGS1*/ 82957642Seric #ifdef __STDC__ 83057642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 83157642Seric #else 83257642Seric smtpmessage(f, m, mci, va_alist) 8334796Seric char *f; 83410175Seric MAILER *m; 83554967Seric MCI *mci; 83657642Seric va_dcl 83757642Seric #endif 8384796Seric { 83956852Seric VA_LOCAL_DECL 84056852Seric 84157135Seric VA_START(mci); 84256852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 84356852Seric VA_END; 84458680Seric 84558120Seric if (tTd(18, 1) || Verbose) 84658151Seric nmessage(">>> %s", SmtpMsgBuffer); 84763753Seric if (TrafficLogFile != NULL) 84863753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 84953740Seric if (mci->mci_out != NULL) 85058680Seric { 85153740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 85254967Seric m == NULL ? "\r\n" : m->m_eol); 85358680Seric } 85459149Seric else if (tTd(18, 1)) 85558725Seric { 85659149Seric printf("smtpmessage: NULL mci_out\n"); 85758725Seric } 8584796Seric } 8595182Seric 86056795Seric # endif /* SMTP */ 861