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*64071Seric static char sccsid[] = "@(#)usersmtp.c 8.6 (Berkeley) 07/27/93 (with SMTP)"; 1433731Sbostic #else 15*64071Seric static char sccsid[] = "@(#)usersmtp.c 8.6 (Berkeley) 07/27/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 */ 38*64071Seric 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 "); 7357379Seric mci_dump(mci); 7457379Seric } 7557379Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8021065Seric SmtpError[0] = '\0'; 8157379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 82*64071Seric 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); 1188005Seric if (r < 0 || REPLYTYPE(r) != 2) 11952104Seric goto tempfail1; 1204684Seric 1214865Seric /* 1224976Seric ** Send the HELO command. 1237963Seric ** My mother taught me to always introduce myself. 1244976Seric */ 1254976Seric 12659285Seric if (bitnset(M_ESMTP, m->m_flags)) 12759285Seric mci->mci_flags |= MCIF_ESMTP; 12859285Seric 12959285Seric tryhelo: 13059285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 13159285Seric { 13259285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13361093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13459285Seric } 13559285Seric else 13659285Seric { 13759285Seric smtpmessage("HELO %s", m, mci, MyHostName); 13861093Seric SmtpPhase = mci->mci_phase = "client HELO"; 13959285Seric } 14053751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 14159285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1428005Seric if (r < 0) 14352104Seric goto tempfail1; 1448005Seric else if (REPLYTYPE(r) == 5) 14559285Seric { 14659285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 14759285Seric { 14859285Seric /* try old SMTP instead */ 14959285Seric mci->mci_flags &= ~MCIF_ESMTP; 15059285Seric goto tryhelo; 15159285Seric } 15214913Seric goto unavailable; 15359285Seric } 1547963Seric else if (REPLYTYPE(r) != 2) 15552104Seric goto tempfail1; 1564976Seric 1574976Seric /* 15858957Seric ** Check to see if we actually ended up talking to ourself. 15958957Seric ** This means we didn't know about an alias or MX, or we managed 16058957Seric ** to connect to an echo server. 16158957Seric */ 16258957Seric 16359026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16458957Seric if (p != NULL) 16561707Seric *p = '\0'; 16659026Seric if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 16758957Seric { 16858957Seric syserr("553 %s config error: mail loops back to myself", 16958957Seric MyHostName); 17058957Seric mci->mci_exitstat = EX_CONFIG; 17158957Seric mci->mci_errno = 0; 17258957Seric smtpquit(m, mci, e); 17358957Seric return; 17458957Seric } 17558957Seric 17658957Seric /* 1779315Seric ** If this is expected to be another sendmail, send some internal 1789315Seric ** commands. 1799315Seric */ 1809315Seric 18110688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1829315Seric { 1839315Seric /* tell it to be verbose */ 18453751Seric smtpmessage("VERB", m, mci); 18559285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1869315Seric if (r < 0) 18752104Seric goto tempfail2; 1889315Seric } 1899315Seric 19053751Seric mci->mci_state = MCIS_OPEN; 19154967Seric return; 19253751Seric 19353751Seric tempfail1: 19453751Seric tempfail2: 19553751Seric mci->mci_exitstat = EX_TEMPFAIL; 19657379Seric if (mci->mci_errno == 0) 19757379Seric mci->mci_errno = errno; 19857379Seric if (mci->mci_state != MCIS_CLOSED) 19957379Seric smtpquit(m, mci, e); 20054967Seric return; 20153751Seric 20253751Seric unavailable: 20353751Seric mci->mci_exitstat = EX_UNAVAILABLE; 20453751Seric mci->mci_errno = errno; 20553751Seric smtpquit(m, mci, e); 20654967Seric return; 20753751Seric } 20859285Seric /* 20960210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 21060210Seric ** 21160210Seric ** 21260210Seric ** Parameters: 21360210Seric ** line -- the response line. 21460210Seric ** m -- the mailer. 21560210Seric ** mci -- the mailer connection info. 21660210Seric ** e -- the envelope. 21760210Seric ** 21860210Seric ** Returns: 21960210Seric ** none. 22060210Seric */ 22160210Seric 22260210Seric void 22360210Seric esmtp_check(line, m, mci, e) 22460210Seric char *line; 22560210Seric MAILER *m; 22660210Seric register MCI *mci; 22760210Seric ENVELOPE *e; 22860210Seric { 22960210Seric if (strlen(line) < 5) 23060210Seric return; 23160210Seric line += 4; 23260210Seric if (strncmp(line, "ESMTP ", 6) == 0) 23360210Seric mci->mci_flags |= MCIF_ESMTP; 23460210Seric } 23560210Seric /* 23659285Seric ** HELO_OPTIONS -- process the options on a HELO line. 23759285Seric ** 23859285Seric ** Parameters: 23959285Seric ** line -- the response line. 24059285Seric ** m -- the mailer. 24159285Seric ** mci -- the mailer connection info. 24259285Seric ** e -- the envelope. 24359285Seric ** 24459285Seric ** Returns: 24559285Seric ** none. 24659285Seric */ 24753751Seric 24859285Seric void 24959285Seric helo_options(line, m, mci, e) 25059285Seric char *line; 25159285Seric MAILER *m; 25259285Seric register MCI *mci; 25359285Seric ENVELOPE *e; 25459285Seric { 25559285Seric register char *p; 25659285Seric 25759285Seric if (strlen(line) < 5) 25859285Seric return; 25959285Seric line += 4; 26059285Seric p = strchr(line, ' '); 26159285Seric if (p != NULL) 26259285Seric *p++ = '\0'; 26359285Seric if (strcasecmp(line, "size") == 0) 26459285Seric { 26559285Seric mci->mci_flags |= MCIF_SIZE; 26659285Seric if (p != NULL) 26759285Seric mci->mci_maxsize = atol(p); 26859285Seric } 26959285Seric else if (strcasecmp(line, "8bitmime") == 0) 27059285Seric mci->mci_flags |= MCIF_8BITMIME; 27159285Seric else if (strcasecmp(line, "expn") == 0) 27259285Seric mci->mci_flags |= MCIF_EXPN; 27359285Seric } 27459285Seric /* 27559285Seric ** SMTPMAILFROM -- send MAIL command 27659285Seric ** 27759285Seric ** Parameters: 27859285Seric ** m -- the mailer. 27959285Seric ** mci -- the mailer connection structure. 28059285Seric ** e -- the envelope (including the sender to specify). 28159285Seric */ 28259285Seric 28353751Seric smtpmailfrom(m, mci, e) 28453751Seric struct mailer *m; 28554967Seric MCI *mci; 28653751Seric ENVELOPE *e; 28753751Seric { 28853751Seric int r; 28953751Seric char buf[MAXNAME]; 29059285Seric char optbuf[MAXLINE]; 29153751Seric 29263753Seric if (tTd(18, 2)) 29357943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 29457943Seric 29559285Seric /* set up appropriate options to include */ 29659285Seric if (bitset(MCIF_SIZE, mci->mci_flags)) 29759285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 29859285Seric else 29959285Seric strcpy(optbuf, ""); 30059285Seric 3019315Seric /* 3024865Seric ** Send the MAIL command. 3034865Seric ** Designates the sender. 3044865Seric */ 3054796Seric 30653751Seric mci->mci_state = MCIS_ACTIVE; 30753751Seric 30859540Seric if (bitset(EF_RESPONSE, e->e_flags) && 30959540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 31058680Seric (void) strcpy(buf, ""); 31158680Seric else 31258680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 31353751Seric if (e->e_from.q_mailer == LocalMailer || 31410688Seric !bitnset(M_FROMPATH, m->m_flags)) 3158436Seric { 31659285Seric smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); 3178436Seric } 3188436Seric else 3198436Seric { 32059285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 32159285Seric buf[0] == '@' ? ',' : ':', buf, optbuf); 3228436Seric } 32361093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 32453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 32559285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3268005Seric if (r < 0 || REPLYTYPE(r) == 4) 32753751Seric { 32853751Seric mci->mci_exitstat = EX_TEMPFAIL; 32953751Seric mci->mci_errno = errno; 33053751Seric smtpquit(m, mci, e); 33153751Seric return EX_TEMPFAIL; 33253751Seric } 3337963Seric else if (r == 250) 33453751Seric { 33553751Seric mci->mci_exitstat = EX_OK; 33653751Seric return EX_OK; 33753751Seric } 3387963Seric else if (r == 552) 33953751Seric { 34053751Seric /* signal service unavailable */ 34153751Seric mci->mci_exitstat = EX_UNAVAILABLE; 34253751Seric smtpquit(m, mci, e); 34353751Seric return EX_UNAVAILABLE; 34453751Seric } 34514913Seric 34658008Seric #ifdef LOG 34758020Seric if (LogLevel > 1) 34858008Seric { 34958008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 35058008Seric e->e_id, SmtpReplyBuffer); 35158008Seric } 35258008Seric #endif 35358008Seric 35414913Seric /* protocol error -- close up */ 35553751Seric smtpquit(m, mci, e); 35653751Seric mci->mci_exitstat = EX_PROTOCOL; 35753751Seric return EX_PROTOCOL; 3584684Seric } 3594684Seric /* 3604976Seric ** SMTPRCPT -- designate recipient. 3614797Seric ** 3624797Seric ** Parameters: 3634865Seric ** to -- address of recipient. 36410175Seric ** m -- the mailer we are sending to. 36557379Seric ** mci -- the connection info for this transaction. 36657379Seric ** e -- the envelope for this transaction. 3674797Seric ** 3684797Seric ** Returns: 3694865Seric ** exit status corresponding to recipient status. 3704797Seric ** 3714797Seric ** Side Effects: 3724865Seric ** Sends the mail via SMTP. 3734797Seric */ 3744797Seric 37553751Seric smtprcpt(to, m, mci, e) 3764865Seric ADDRESS *to; 37710175Seric register MAILER *m; 37854967Seric MCI *mci; 37953751Seric ENVELOPE *e; 3804797Seric { 3814797Seric register int r; 3824797Seric 38353751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 3844865Seric 38561093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 38653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 38759285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 3888005Seric if (r < 0 || REPLYTYPE(r) == 4) 3894865Seric return (EX_TEMPFAIL); 3907963Seric else if (REPLYTYPE(r) == 2) 3917963Seric return (EX_OK); 3927964Seric else if (r == 550 || r == 551 || r == 553) 3937964Seric return (EX_NOUSER); 3947964Seric else if (r == 552 || r == 554) 3957964Seric return (EX_UNAVAILABLE); 39658008Seric 39758008Seric #ifdef LOG 39858020Seric if (LogLevel > 1) 39958008Seric { 40058008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 40158008Seric e->e_id, SmtpReplyBuffer); 40258008Seric } 40358008Seric #endif 40458008Seric 4057964Seric return (EX_PROTOCOL); 4064797Seric } 4074797Seric /* 40810175Seric ** SMTPDATA -- send the data and clean up the transaction. 4094684Seric ** 4104684Seric ** Parameters: 4114865Seric ** m -- mailer being sent to. 4126980Seric ** e -- the envelope for this message. 4134684Seric ** 4144684Seric ** Returns: 4154976Seric ** exit status corresponding to DATA command. 4164684Seric ** 4174684Seric ** Side Effects: 4184865Seric ** none. 4194684Seric */ 4204684Seric 42163753Seric static jmp_buf CtxDataTimeout; 42263937Seric static int datatimeout(); 42363753Seric 42453740Seric smtpdata(m, mci, e) 4254865Seric struct mailer *m; 42654967Seric register MCI *mci; 4276980Seric register ENVELOPE *e; 4284684Seric { 4294684Seric register int r; 43063753Seric register EVENT *ev; 43163753Seric time_t timeout; 4324684Seric 4334797Seric /* 4344797Seric ** Send the data. 43510175Seric ** First send the command and check that it is ok. 43610175Seric ** Then send the data. 43710175Seric ** Follow it up with a dot to terminate. 43810175Seric ** Finally get the results of the transaction. 4394797Seric */ 4404797Seric 44110175Seric /* send the command and check ok to proceed */ 44253751Seric smtpmessage("DATA", m, mci); 44361093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 44453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 44559285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4468005Seric if (r < 0 || REPLYTYPE(r) == 4) 44757990Seric { 44857990Seric smtpquit(m, mci, e); 4494797Seric return (EX_TEMPFAIL); 45057990Seric } 4517963Seric else if (r == 554) 45257990Seric { 45357990Seric smtprset(m, mci, e); 4547963Seric return (EX_UNAVAILABLE); 45557990Seric } 4567963Seric else if (r != 354) 45757990Seric { 45858008Seric #ifdef LOG 45958020Seric if (LogLevel > 1) 46058008Seric { 46158008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 46258008Seric e->e_id, SmtpReplyBuffer); 46358008Seric } 46458008Seric #endif 46557990Seric smtprset(m, mci, e); 4667964Seric return (EX_PROTOCOL); 46757990Seric } 46810175Seric 46963757Seric /* 47063757Seric ** Set timeout around data writes. Make it at least large 47163757Seric ** enough for DNS timeouts on all recipients plus some fudge 47263757Seric ** factor. The main thing is that it should not be infinite. 47363757Seric */ 47463757Seric 47563753Seric if (setjmp(CtxDataTimeout) != 0) 47663753Seric { 47763753Seric mci->mci_errno = errno; 47863753Seric mci->mci_exitstat = EX_TEMPFAIL; 47963753Seric mci->mci_state = MCIS_ERROR; 48063753Seric syserr("451 timeout writing message to %s", mci->mci_host); 48163753Seric smtpquit(m, mci, e); 48263753Seric return EX_TEMPFAIL; 48363753Seric } 48463753Seric 48563787Seric timeout = e->e_msgsize / 16; 48663787Seric if (timeout < (time_t) 60) 48763787Seric timeout = (time_t) 60; 48863787Seric timeout += e->e_nrcpts * 90; 48963753Seric ev = setevent(timeout, datatimeout, 0); 49063753Seric 49110175Seric /* now output the actual message */ 49253751Seric (*e->e_puthdr)(mci->mci_out, m, e); 49353740Seric putline("\n", mci->mci_out, m); 49459730Seric (*e->e_putbody)(mci->mci_out, m, e, NULL); 49510175Seric 49663753Seric clrevent(ev); 49763753Seric 49810175Seric /* terminate the message */ 49953740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 50063753Seric if (TrafficLogFile != NULL) 50163753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 50258120Seric if (Verbose) 50358151Seric nmessage(">>> ."); 50410175Seric 50510175Seric /* check for the results of the transaction */ 50661093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 50753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 50859285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 50953751Seric if (r < 0) 51057990Seric { 51157990Seric smtpquit(m, mci, e); 5124797Seric return (EX_TEMPFAIL); 51357990Seric } 51453751Seric mci->mci_state = MCIS_OPEN; 51558917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 51653751Seric if (REPLYTYPE(r) == 4) 51753751Seric return (EX_TEMPFAIL); 5187963Seric else if (r == 250) 5197963Seric return (EX_OK); 5207963Seric else if (r == 552 || r == 554) 5217963Seric return (EX_UNAVAILABLE); 52258008Seric #ifdef LOG 52358020Seric if (LogLevel > 1) 52458008Seric { 52558008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 52658008Seric e->e_id, SmtpReplyBuffer); 52758008Seric } 52858008Seric #endif 5297964Seric return (EX_PROTOCOL); 5304684Seric } 53163753Seric 53263753Seric 53363753Seric static int 53463753Seric datatimeout() 53563753Seric { 53663753Seric longjmp(CtxDataTimeout, 1); 53763753Seric } 5384684Seric /* 5394865Seric ** SMTPQUIT -- close the SMTP connection. 5404865Seric ** 5414865Seric ** Parameters: 54215535Seric ** m -- a pointer to the mailer. 5434865Seric ** 5444865Seric ** Returns: 5454865Seric ** none. 5464865Seric ** 5474865Seric ** Side Effects: 5484865Seric ** sends the final protocol and closes the connection. 5494865Seric */ 5504865Seric 55153751Seric smtpquit(m, mci, e) 55253751Seric register MAILER *m; 55354967Seric register MCI *mci; 55453751Seric ENVELOPE *e; 5554865Seric { 5569391Seric int i; 5574865Seric 55854967Seric /* send the quit message if we haven't gotten I/O error */ 55953751Seric if (mci->mci_state != MCIS_ERROR) 5609391Seric { 56161093Seric SmtpPhase = "client QUIT"; 56253751Seric smtpmessage("QUIT", m, mci); 56359285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 56453740Seric if (mci->mci_state == MCIS_CLOSED) 56510159Seric return; 5669391Seric } 5679391Seric 56852676Seric /* now actually close the connection and pick up the zombie */ 56958846Seric i = endmailer(mci, e, m->m_argv); 5709391Seric if (i != EX_OK) 57158151Seric syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); 5724865Seric } 5734865Seric /* 57454967Seric ** SMTPRSET -- send a RSET (reset) command 57554967Seric */ 57654967Seric 57754967Seric smtprset(m, mci, e) 57854967Seric register MAILER *m; 57954967Seric register MCI *mci; 58054967Seric ENVELOPE *e; 58154967Seric { 58254967Seric int r; 58354967Seric 58461093Seric SmtpPhase = "client RSET"; 58554967Seric smtpmessage("RSET", m, mci); 58659285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 58757734Seric if (r < 0) 58857734Seric mci->mci_state = MCIS_ERROR; 58954967Seric else if (REPLYTYPE(r) == 2) 59057734Seric { 59157734Seric mci->mci_state = MCIS_OPEN; 59257734Seric return; 59357734Seric } 59457734Seric smtpquit(m, mci, e); 59554967Seric } 59654967Seric /* 59758867Seric ** SMTPPROBE -- check the connection state 59854967Seric */ 59954967Seric 60058867Seric smtpprobe(mci) 60154967Seric register MCI *mci; 60254967Seric { 60354967Seric int r; 60454967Seric MAILER *m = mci->mci_mailer; 60554967Seric extern ENVELOPE BlankEnvelope; 60654967Seric ENVELOPE *e = &BlankEnvelope; 60754967Seric 60861093Seric SmtpPhase = "client probe"; 60958867Seric smtpmessage("RSET", m, mci); 61059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 61158061Seric if (r < 0 || REPLYTYPE(r) != 2) 61254967Seric smtpquit(m, mci, e); 61354967Seric return r; 61454967Seric } 61554967Seric /* 6164684Seric ** REPLY -- read arpanet reply 6174684Seric ** 6184684Seric ** Parameters: 61910175Seric ** m -- the mailer we are reading the reply from. 62057379Seric ** mci -- the mailer connection info structure. 62157379Seric ** e -- the current envelope. 62257379Seric ** timeout -- the timeout for reads. 62359285Seric ** pfunc -- processing function for second and subsequent 62459285Seric ** lines of response -- if null, no special 62559285Seric ** processing is done. 6264684Seric ** 6274684Seric ** Returns: 6284684Seric ** reply code it reads. 6294684Seric ** 6304684Seric ** Side Effects: 6314684Seric ** flushes the mail file. 6324684Seric */ 6334684Seric 63459285Seric reply(m, mci, e, timeout, pfunc) 63553751Seric MAILER *m; 63654967Seric MCI *mci; 63753751Seric ENVELOPE *e; 63859285Seric time_t timeout; 63959285Seric void (*pfunc)(); 6404684Seric { 64159014Seric register char *bufp; 64259014Seric register int r; 64359285Seric bool firstline = TRUE; 64458957Seric char junkbuf[MAXLINE]; 64558957Seric 64657379Seric if (mci->mci_out != NULL) 64757379Seric (void) fflush(mci->mci_out); 6484684Seric 6497677Seric if (tTd(18, 1)) 6504796Seric printf("reply\n"); 6514796Seric 6527356Seric /* 6537356Seric ** Read the input line, being careful not to hang. 6547356Seric */ 6557356Seric 65659014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 6574684Seric { 6587356Seric register char *p; 65953751Seric extern time_t curtime(); 6604684Seric 6617685Seric /* actually do the read */ 66253751Seric if (e->e_xfp != NULL) 66353751Seric (void) fflush(e->e_xfp); /* for debugging */ 6647356Seric 66510054Seric /* if we are in the process of closing just give the code */ 66653740Seric if (mci->mci_state == MCIS_CLOSED) 66710054Seric return (SMTPCLOSING); 66810054Seric 66958680Seric if (mci->mci_out != NULL) 67058680Seric fflush(mci->mci_out); 67158680Seric 67210054Seric /* get the line from the other side */ 67361093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 67453751Seric mci->mci_lastuse = curtime(); 67553751Seric 67610054Seric if (p == NULL) 67710131Seric { 67863753Seric bool oldholderrs; 67910148Seric extern char MsgBuf[]; /* err.c */ 68010148Seric 68121065Seric /* if the remote end closed early, fake an error */ 68221065Seric if (errno == 0) 68321065Seric # ifdef ECONNRESET 68421065Seric errno = ECONNRESET; 68556795Seric # else /* ECONNRESET */ 68621065Seric errno = EPIPE; 68756795Seric # endif /* ECONNRESET */ 68821065Seric 68957379Seric mci->mci_errno = errno; 69057642Seric mci->mci_exitstat = EX_TEMPFAIL; 69163753Seric oldholderrs = HoldErrs; 69263753Seric HoldErrs = TRUE; 69363753Seric usrerr("451 reply: read error from %s", mci->mci_host); 69463753Seric 69510420Seric /* if debugging, pause so we can see state */ 69610420Seric if (tTd(18, 100)) 69710420Seric pause(); 69854967Seric mci->mci_state = MCIS_ERROR; 69953751Seric smtpquit(m, mci, e); 70063753Seric #ifdef XDEBUG 70163753Seric { 70263753Seric char wbuf[MAXLINE]; 70363753Seric sprintf(wbuf, "%s... reply(%s) during %s", 70463753Seric e->e_to, mci->mci_host, SmtpPhase); 70563753Seric checkfd012(wbuf); 70663753Seric } 70763753Seric #endif 70863753Seric HoldErrs = oldholderrs; 70910054Seric return (-1); 71010131Seric } 71158957Seric fixcrlf(bufp, TRUE); 71210054Seric 71363753Seric /* EHLO failure is not a real error */ 71463753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 71563753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 71614900Seric { 71714900Seric /* serious error -- log the previous command */ 718*64071Seric if (SmtpNeedIntro) 719*64071Seric { 720*64071Seric /* inform user who we are chatting with */ 721*64071Seric fprintf(CurEnv->e_xfp, 722*64071Seric "... while talking to %s:\n", 723*64071Seric CurHostName); 724*64071Seric SmtpNeedIntro = FALSE; 725*64071Seric } 72659014Seric if (SmtpMsgBuffer[0] != '\0') 72759014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 72859014Seric SmtpMsgBuffer[0] = '\0'; 72914900Seric 73014900Seric /* now log the message as from the other side */ 73158957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 73214900Seric } 73314900Seric 73414900Seric /* display the input for verbose mode */ 73558120Seric if (Verbose) 73659956Seric nmessage("050 %s", bufp); 7377356Seric 73859285Seric /* process the line */ 73959285Seric if (pfunc != NULL && !firstline) 74059285Seric (*pfunc)(bufp, m, mci, e); 74159285Seric 74259285Seric firstline = FALSE; 74359285Seric 7447356Seric /* if continuation is required, we can go on */ 74559014Seric if (bufp[3] == '-') 7464684Seric continue; 7477356Seric 74859014Seric /* ignore improperly formated input */ 74959014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 75059014Seric continue; 75159014Seric 7527356Seric /* decode the reply code */ 75359014Seric r = atoi(bufp); 7547356Seric 7557356Seric /* extra semantics: 0xx codes are "informational" */ 75659014Seric if (r >= 100) 75759014Seric break; 75859014Seric } 7597356Seric 76059014Seric /* 76159014Seric ** Now look at SmtpReplyBuffer -- only care about the first 76259014Seric ** line of the response from here on out. 76359014Seric */ 76458061Seric 76559014Seric /* save temporary failure messages for posterity */ 76659014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 76759014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 7689391Seric 76959014Seric /* reply code 421 is "Service Shutting Down" */ 77059014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 77159014Seric { 77259014Seric /* send the quit protocol */ 77359014Seric mci->mci_state = MCIS_SSD; 77459014Seric smtpquit(m, mci, e); 7754684Seric } 77659014Seric 77759014Seric return (r); 7784684Seric } 7794796Seric /* 7804865Seric ** SMTPMESSAGE -- send message to server 7814796Seric ** 7824796Seric ** Parameters: 7834796Seric ** f -- format 78410175Seric ** m -- the mailer to control formatting. 7854796Seric ** a, b, c -- parameters 7864796Seric ** 7874796Seric ** Returns: 7884796Seric ** none. 7894796Seric ** 7904796Seric ** Side Effects: 79153740Seric ** writes message to mci->mci_out. 7924796Seric */ 7934796Seric 7944865Seric /*VARARGS1*/ 79557642Seric #ifdef __STDC__ 79657642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 79757642Seric #else 79857642Seric smtpmessage(f, m, mci, va_alist) 7994796Seric char *f; 80010175Seric MAILER *m; 80154967Seric MCI *mci; 80257642Seric va_dcl 80357642Seric #endif 8044796Seric { 80556852Seric VA_LOCAL_DECL 80656852Seric 80757135Seric VA_START(mci); 80856852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 80956852Seric VA_END; 81058680Seric 81158120Seric if (tTd(18, 1) || Verbose) 81258151Seric nmessage(">>> %s", SmtpMsgBuffer); 81363753Seric if (TrafficLogFile != NULL) 81463753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 81553740Seric if (mci->mci_out != NULL) 81658680Seric { 81753740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 81854967Seric m == NULL ? "\r\n" : m->m_eol); 81958680Seric } 82059149Seric else if (tTd(18, 1)) 82158725Seric { 82259149Seric printf("smtpmessage: NULL mci_out\n"); 82358725Seric } 8244796Seric } 8255182Seric 82656795Seric # endif /* SMTP */ 827