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*63757Seric static char sccsid[] = "@(#)usersmtp.c 8.3 (Berkeley) 07/11/93 (with SMTP)"; 1433731Sbostic #else 15*63757Seric static char sccsid[] = "@(#)usersmtp.c 8.3 (Berkeley) 07/11/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 */ 3858671Seric 3958671Seric #ifdef __STDC__ 4058671Seric extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 4158671Seric #endif 429391Seric /* 434865Seric ** SMTPINIT -- initialize SMTP. 444684Seric ** 454865Seric ** Opens the connection and sends the initial protocol. 464684Seric ** 474684Seric ** Parameters: 484865Seric ** m -- mailer to create connection to. 494865Seric ** pvp -- pointer to parameter vector to pass to 504865Seric ** the mailer. 514684Seric ** 524684Seric ** Returns: 5354967Seric ** none. 544684Seric ** 554684Seric ** Side Effects: 564865Seric ** creates connection and sends initial protocol. 574684Seric */ 584684Seric 5954967Seric smtpinit(m, mci, e) 604865Seric struct mailer *m; 6154967Seric register MCI *mci; 6253751Seric ENVELOPE *e; 634684Seric { 644865Seric register int r; 6558957Seric register char *p; 6660210Seric extern void esmtp_check(); 6759285Seric extern void helo_options(); 684684Seric 6963753Seric if (tTd(18, 1)) 7057379Seric { 7157379Seric printf("smtpinit "); 7257379Seric mci_dump(mci); 7357379Seric } 7457379Seric 754865Seric /* 764865Seric ** Open the connection to the mailer. 774865Seric */ 784684Seric 7921065Seric SmtpError[0] = '\0'; 8057379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 8154967Seric switch (mci->mci_state) 826051Seric { 8354967Seric case MCIS_ACTIVE: 8454967Seric /* need to clear old information */ 8554967Seric smtprset(m, mci, e); 8657734Seric /* fall through */ 8715139Seric 8854967Seric case MCIS_OPEN: 8954967Seric return; 9054967Seric 9154967Seric case MCIS_ERROR: 9254967Seric case MCIS_SSD: 9354967Seric /* shouldn't happen */ 9454967Seric smtpquit(m, mci, e); 9557734Seric /* fall through */ 9654967Seric 9754967Seric case MCIS_CLOSED: 9858151Seric syserr("451 smtpinit: state CLOSED"); 9954967Seric return; 10054967Seric 10154967Seric case MCIS_OPENING: 10254967Seric break; 1036051Seric } 1044796Seric 10554967Seric mci->mci_state = MCIS_OPENING; 10654967Seric 1074865Seric /* 1084865Seric ** Get the greeting message. 10914913Seric ** This should appear spontaneously. Give it five minutes to 11014886Seric ** happen. 1114865Seric */ 1124797Seric 11361093Seric SmtpPhase = mci->mci_phase = "client greeting"; 11453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11560210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 1168005Seric if (r < 0 || REPLYTYPE(r) != 2) 11752104Seric goto tempfail1; 1184684Seric 1194865Seric /* 1204976Seric ** Send the HELO command. 1217963Seric ** My mother taught me to always introduce myself. 1224976Seric */ 1234976Seric 12459285Seric if (bitnset(M_ESMTP, m->m_flags)) 12559285Seric mci->mci_flags |= MCIF_ESMTP; 12659285Seric 12759285Seric tryhelo: 12859285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 12959285Seric { 13059285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13161093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13259285Seric } 13359285Seric else 13459285Seric { 13559285Seric smtpmessage("HELO %s", m, mci, MyHostName); 13661093Seric SmtpPhase = mci->mci_phase = "client HELO"; 13759285Seric } 13853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 13959285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1408005Seric if (r < 0) 14152104Seric goto tempfail1; 1428005Seric else if (REPLYTYPE(r) == 5) 14359285Seric { 14459285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 14559285Seric { 14659285Seric /* try old SMTP instead */ 14759285Seric mci->mci_flags &= ~MCIF_ESMTP; 14859285Seric goto tryhelo; 14959285Seric } 15014913Seric goto unavailable; 15159285Seric } 1527963Seric else if (REPLYTYPE(r) != 2) 15352104Seric goto tempfail1; 1544976Seric 1554976Seric /* 15658957Seric ** Check to see if we actually ended up talking to ourself. 15758957Seric ** This means we didn't know about an alias or MX, or we managed 15858957Seric ** to connect to an echo server. 15958957Seric */ 16058957Seric 16159026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16258957Seric if (p != NULL) 16361707Seric *p = '\0'; 16459026Seric if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 16558957Seric { 16658957Seric syserr("553 %s config error: mail loops back to myself", 16758957Seric MyHostName); 16858957Seric mci->mci_exitstat = EX_CONFIG; 16958957Seric mci->mci_errno = 0; 17058957Seric smtpquit(m, mci, e); 17158957Seric return; 17258957Seric } 17358957Seric 17458957Seric /* 1759315Seric ** If this is expected to be another sendmail, send some internal 1769315Seric ** commands. 1779315Seric */ 1789315Seric 17910688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1809315Seric { 1819315Seric /* tell it to be verbose */ 18253751Seric smtpmessage("VERB", m, mci); 18359285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1849315Seric if (r < 0) 18552104Seric goto tempfail2; 1869315Seric } 1879315Seric 18853751Seric mci->mci_state = MCIS_OPEN; 18954967Seric return; 19053751Seric 19153751Seric tempfail1: 19253751Seric tempfail2: 19353751Seric mci->mci_exitstat = EX_TEMPFAIL; 19457379Seric if (mci->mci_errno == 0) 19557379Seric mci->mci_errno = errno; 19657379Seric if (mci->mci_state != MCIS_CLOSED) 19757379Seric smtpquit(m, mci, e); 19854967Seric return; 19953751Seric 20053751Seric unavailable: 20153751Seric mci->mci_exitstat = EX_UNAVAILABLE; 20253751Seric mci->mci_errno = errno; 20353751Seric smtpquit(m, mci, e); 20454967Seric return; 20553751Seric } 20659285Seric /* 20760210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 20860210Seric ** 20960210Seric ** 21060210Seric ** Parameters: 21160210Seric ** line -- the response line. 21260210Seric ** m -- the mailer. 21360210Seric ** mci -- the mailer connection info. 21460210Seric ** e -- the envelope. 21560210Seric ** 21660210Seric ** Returns: 21760210Seric ** none. 21860210Seric */ 21960210Seric 22060210Seric void 22160210Seric esmtp_check(line, m, mci, e) 22260210Seric char *line; 22360210Seric MAILER *m; 22460210Seric register MCI *mci; 22560210Seric ENVELOPE *e; 22660210Seric { 22760210Seric if (strlen(line) < 5) 22860210Seric return; 22960210Seric line += 4; 23060210Seric if (strncmp(line, "ESMTP ", 6) == 0) 23160210Seric mci->mci_flags |= MCIF_ESMTP; 23260210Seric } 23360210Seric /* 23459285Seric ** HELO_OPTIONS -- process the options on a HELO line. 23559285Seric ** 23659285Seric ** Parameters: 23759285Seric ** line -- the response line. 23859285Seric ** m -- the mailer. 23959285Seric ** mci -- the mailer connection info. 24059285Seric ** e -- the envelope. 24159285Seric ** 24259285Seric ** Returns: 24359285Seric ** none. 24459285Seric */ 24553751Seric 24659285Seric void 24759285Seric helo_options(line, m, mci, e) 24859285Seric char *line; 24959285Seric MAILER *m; 25059285Seric register MCI *mci; 25159285Seric ENVELOPE *e; 25259285Seric { 25359285Seric register char *p; 25459285Seric 25559285Seric if (strlen(line) < 5) 25659285Seric return; 25759285Seric line += 4; 25859285Seric p = strchr(line, ' '); 25959285Seric if (p != NULL) 26059285Seric *p++ = '\0'; 26159285Seric if (strcasecmp(line, "size") == 0) 26259285Seric { 26359285Seric mci->mci_flags |= MCIF_SIZE; 26459285Seric if (p != NULL) 26559285Seric mci->mci_maxsize = atol(p); 26659285Seric } 26759285Seric else if (strcasecmp(line, "8bitmime") == 0) 26859285Seric mci->mci_flags |= MCIF_8BITMIME; 26959285Seric else if (strcasecmp(line, "expn") == 0) 27059285Seric mci->mci_flags |= MCIF_EXPN; 27159285Seric } 27259285Seric /* 27359285Seric ** SMTPMAILFROM -- send MAIL command 27459285Seric ** 27559285Seric ** Parameters: 27659285Seric ** m -- the mailer. 27759285Seric ** mci -- the mailer connection structure. 27859285Seric ** e -- the envelope (including the sender to specify). 27959285Seric */ 28059285Seric 28153751Seric smtpmailfrom(m, mci, e) 28253751Seric struct mailer *m; 28354967Seric MCI *mci; 28453751Seric ENVELOPE *e; 28553751Seric { 28653751Seric int r; 28753751Seric char buf[MAXNAME]; 28859285Seric char optbuf[MAXLINE]; 28953751Seric 29063753Seric if (tTd(18, 2)) 29157943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 29257943Seric 29359285Seric /* set up appropriate options to include */ 29459285Seric if (bitset(MCIF_SIZE, mci->mci_flags)) 29559285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 29659285Seric else 29759285Seric strcpy(optbuf, ""); 29859285Seric 2999315Seric /* 3004865Seric ** Send the MAIL command. 3014865Seric ** Designates the sender. 3024865Seric */ 3034796Seric 30453751Seric mci->mci_state = MCIS_ACTIVE; 30553751Seric 30659540Seric if (bitset(EF_RESPONSE, e->e_flags) && 30759540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 30858680Seric (void) strcpy(buf, ""); 30958680Seric else 31058680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 31153751Seric if (e->e_from.q_mailer == LocalMailer || 31210688Seric !bitnset(M_FROMPATH, m->m_flags)) 3138436Seric { 31459285Seric smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); 3158436Seric } 3168436Seric else 3178436Seric { 31859285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 31959285Seric buf[0] == '@' ? ',' : ':', buf, optbuf); 3208436Seric } 32161093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 32253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 32359285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3248005Seric if (r < 0 || REPLYTYPE(r) == 4) 32553751Seric { 32653751Seric mci->mci_exitstat = EX_TEMPFAIL; 32753751Seric mci->mci_errno = errno; 32853751Seric smtpquit(m, mci, e); 32953751Seric return EX_TEMPFAIL; 33053751Seric } 3317963Seric else if (r == 250) 33253751Seric { 33353751Seric mci->mci_exitstat = EX_OK; 33453751Seric return EX_OK; 33553751Seric } 3367963Seric else if (r == 552) 33753751Seric { 33853751Seric /* signal service unavailable */ 33953751Seric mci->mci_exitstat = EX_UNAVAILABLE; 34053751Seric smtpquit(m, mci, e); 34153751Seric return EX_UNAVAILABLE; 34253751Seric } 34314913Seric 34458008Seric #ifdef LOG 34558020Seric if (LogLevel > 1) 34658008Seric { 34758008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 34858008Seric e->e_id, SmtpReplyBuffer); 34958008Seric } 35058008Seric #endif 35158008Seric 35214913Seric /* protocol error -- close up */ 35353751Seric smtpquit(m, mci, e); 35453751Seric mci->mci_exitstat = EX_PROTOCOL; 35553751Seric return EX_PROTOCOL; 3564684Seric } 3574684Seric /* 3584976Seric ** SMTPRCPT -- designate recipient. 3594797Seric ** 3604797Seric ** Parameters: 3614865Seric ** to -- address of recipient. 36210175Seric ** m -- the mailer we are sending to. 36357379Seric ** mci -- the connection info for this transaction. 36457379Seric ** e -- the envelope for this transaction. 3654797Seric ** 3664797Seric ** Returns: 3674865Seric ** exit status corresponding to recipient status. 3684797Seric ** 3694797Seric ** Side Effects: 3704865Seric ** Sends the mail via SMTP. 3714797Seric */ 3724797Seric 37353751Seric smtprcpt(to, m, mci, e) 3744865Seric ADDRESS *to; 37510175Seric register MAILER *m; 37654967Seric MCI *mci; 37753751Seric ENVELOPE *e; 3784797Seric { 3794797Seric register int r; 3804797Seric 38153751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 3824865Seric 38361093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 38453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 38559285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 3868005Seric if (r < 0 || REPLYTYPE(r) == 4) 3874865Seric return (EX_TEMPFAIL); 3887963Seric else if (REPLYTYPE(r) == 2) 3897963Seric return (EX_OK); 3907964Seric else if (r == 550 || r == 551 || r == 553) 3917964Seric return (EX_NOUSER); 3927964Seric else if (r == 552 || r == 554) 3937964Seric return (EX_UNAVAILABLE); 39458008Seric 39558008Seric #ifdef LOG 39658020Seric if (LogLevel > 1) 39758008Seric { 39858008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 39958008Seric e->e_id, SmtpReplyBuffer); 40058008Seric } 40158008Seric #endif 40258008Seric 4037964Seric return (EX_PROTOCOL); 4044797Seric } 4054797Seric /* 40610175Seric ** SMTPDATA -- send the data and clean up the transaction. 4074684Seric ** 4084684Seric ** Parameters: 4094865Seric ** m -- mailer being sent to. 4106980Seric ** e -- the envelope for this message. 4114684Seric ** 4124684Seric ** Returns: 4134976Seric ** exit status corresponding to DATA command. 4144684Seric ** 4154684Seric ** Side Effects: 4164865Seric ** none. 4174684Seric */ 4184684Seric 41963753Seric static jmp_buf CtxDataTimeout; 42063753Seric 42153740Seric smtpdata(m, mci, e) 4224865Seric struct mailer *m; 42354967Seric register MCI *mci; 4246980Seric register ENVELOPE *e; 4254684Seric { 4264684Seric register int r; 42763753Seric register EVENT *ev; 42863753Seric time_t timeout; 429*63757Seric time_t mintimeout; 43063753Seric static int datatimeout(); 4314684Seric 4324797Seric /* 4334797Seric ** Send the data. 43410175Seric ** First send the command and check that it is ok. 43510175Seric ** Then send the data. 43610175Seric ** Follow it up with a dot to terminate. 43710175Seric ** Finally get the results of the transaction. 4384797Seric */ 4394797Seric 44010175Seric /* send the command and check ok to proceed */ 44153751Seric smtpmessage("DATA", m, mci); 44261093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 44353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 44459285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4458005Seric if (r < 0 || REPLYTYPE(r) == 4) 44657990Seric { 44757990Seric smtpquit(m, mci, e); 4484797Seric return (EX_TEMPFAIL); 44957990Seric } 4507963Seric else if (r == 554) 45157990Seric { 45257990Seric smtprset(m, mci, e); 4537963Seric return (EX_UNAVAILABLE); 45457990Seric } 4557963Seric else if (r != 354) 45657990Seric { 45758008Seric #ifdef LOG 45858020Seric if (LogLevel > 1) 45958008Seric { 46058008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 46158008Seric e->e_id, SmtpReplyBuffer); 46258008Seric } 46358008Seric #endif 46457990Seric smtprset(m, mci, e); 4657964Seric return (EX_PROTOCOL); 46657990Seric } 46710175Seric 468*63757Seric /* 469*63757Seric ** Set timeout around data writes. Make it at least large 470*63757Seric ** enough for DNS timeouts on all recipients plus some fudge 471*63757Seric ** factor. The main thing is that it should not be infinite. 472*63757Seric */ 473*63757Seric 47463753Seric if (setjmp(CtxDataTimeout) != 0) 47563753Seric { 47663753Seric mci->mci_errno = errno; 47763753Seric mci->mci_exitstat = EX_TEMPFAIL; 47863753Seric mci->mci_state = MCIS_ERROR; 47963753Seric syserr("451 timeout writing message to %s", mci->mci_host); 48063753Seric smtpquit(m, mci, e); 48163753Seric return EX_TEMPFAIL; 48263753Seric } 48363753Seric 48463753Seric timeout = e->e_msgsize / 64; 485*63757Seric mintimeout = e->e_nrcpts * 90 + 60; 486*63757Seric if (timeout < mintimeout) 487*63757Seric timeout = mintimeout; 48863753Seric ev = setevent(timeout, datatimeout, 0); 48963753Seric 49010175Seric /* now output the actual message */ 49153751Seric (*e->e_puthdr)(mci->mci_out, m, e); 49253740Seric putline("\n", mci->mci_out, m); 49359730Seric (*e->e_putbody)(mci->mci_out, m, e, NULL); 49410175Seric 49563753Seric clrevent(ev); 49663753Seric 49710175Seric /* terminate the message */ 49853740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 49963753Seric if (TrafficLogFile != NULL) 50063753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 50158120Seric if (Verbose) 50258151Seric nmessage(">>> ."); 50310175Seric 50410175Seric /* check for the results of the transaction */ 50561093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 50653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 50759285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 50853751Seric if (r < 0) 50957990Seric { 51057990Seric smtpquit(m, mci, e); 5114797Seric return (EX_TEMPFAIL); 51257990Seric } 51353751Seric mci->mci_state = MCIS_OPEN; 51458917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 51553751Seric if (REPLYTYPE(r) == 4) 51653751Seric return (EX_TEMPFAIL); 5177963Seric else if (r == 250) 5187963Seric return (EX_OK); 5197963Seric else if (r == 552 || r == 554) 5207963Seric return (EX_UNAVAILABLE); 52158008Seric #ifdef LOG 52258020Seric if (LogLevel > 1) 52358008Seric { 52458008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 52558008Seric e->e_id, SmtpReplyBuffer); 52658008Seric } 52758008Seric #endif 5287964Seric return (EX_PROTOCOL); 5294684Seric } 53063753Seric 53163753Seric 53263753Seric static int 53363753Seric datatimeout() 53463753Seric { 53563753Seric longjmp(CtxDataTimeout, 1); 53663753Seric } 5374684Seric /* 5384865Seric ** SMTPQUIT -- close the SMTP connection. 5394865Seric ** 5404865Seric ** Parameters: 54115535Seric ** m -- a pointer to the mailer. 5424865Seric ** 5434865Seric ** Returns: 5444865Seric ** none. 5454865Seric ** 5464865Seric ** Side Effects: 5474865Seric ** sends the final protocol and closes the connection. 5484865Seric */ 5494865Seric 55053751Seric smtpquit(m, mci, e) 55153751Seric register MAILER *m; 55254967Seric register MCI *mci; 55353751Seric ENVELOPE *e; 5544865Seric { 5559391Seric int i; 5564865Seric 55754967Seric /* send the quit message if we haven't gotten I/O error */ 55853751Seric if (mci->mci_state != MCIS_ERROR) 5599391Seric { 56061093Seric SmtpPhase = "client QUIT"; 56153751Seric smtpmessage("QUIT", m, mci); 56259285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 56353740Seric if (mci->mci_state == MCIS_CLOSED) 56410159Seric return; 5659391Seric } 5669391Seric 56752676Seric /* now actually close the connection and pick up the zombie */ 56858846Seric i = endmailer(mci, e, m->m_argv); 5699391Seric if (i != EX_OK) 57058151Seric syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); 5714865Seric } 5724865Seric /* 57354967Seric ** SMTPRSET -- send a RSET (reset) command 57454967Seric */ 57554967Seric 57654967Seric smtprset(m, mci, e) 57754967Seric register MAILER *m; 57854967Seric register MCI *mci; 57954967Seric ENVELOPE *e; 58054967Seric { 58154967Seric int r; 58254967Seric 58361093Seric SmtpPhase = "client RSET"; 58454967Seric smtpmessage("RSET", m, mci); 58559285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 58657734Seric if (r < 0) 58757734Seric mci->mci_state = MCIS_ERROR; 58854967Seric else if (REPLYTYPE(r) == 2) 58957734Seric { 59057734Seric mci->mci_state = MCIS_OPEN; 59157734Seric return; 59257734Seric } 59357734Seric smtpquit(m, mci, e); 59454967Seric } 59554967Seric /* 59658867Seric ** SMTPPROBE -- check the connection state 59754967Seric */ 59854967Seric 59958867Seric smtpprobe(mci) 60054967Seric register MCI *mci; 60154967Seric { 60254967Seric int r; 60354967Seric MAILER *m = mci->mci_mailer; 60454967Seric extern ENVELOPE BlankEnvelope; 60554967Seric ENVELOPE *e = &BlankEnvelope; 60654967Seric 60761093Seric SmtpPhase = "client probe"; 60858867Seric smtpmessage("RSET", m, mci); 60959285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 61058061Seric if (r < 0 || REPLYTYPE(r) != 2) 61154967Seric smtpquit(m, mci, e); 61254967Seric return r; 61354967Seric } 61454967Seric /* 6154684Seric ** REPLY -- read arpanet reply 6164684Seric ** 6174684Seric ** Parameters: 61810175Seric ** m -- the mailer we are reading the reply from. 61957379Seric ** mci -- the mailer connection info structure. 62057379Seric ** e -- the current envelope. 62157379Seric ** timeout -- the timeout for reads. 62259285Seric ** pfunc -- processing function for second and subsequent 62359285Seric ** lines of response -- if null, no special 62459285Seric ** processing is done. 6254684Seric ** 6264684Seric ** Returns: 6274684Seric ** reply code it reads. 6284684Seric ** 6294684Seric ** Side Effects: 6304684Seric ** flushes the mail file. 6314684Seric */ 6324684Seric 63359285Seric reply(m, mci, e, timeout, pfunc) 63453751Seric MAILER *m; 63554967Seric MCI *mci; 63653751Seric ENVELOPE *e; 63759285Seric time_t timeout; 63859285Seric void (*pfunc)(); 6394684Seric { 64059014Seric register char *bufp; 64159014Seric register int r; 64259285Seric bool firstline = TRUE; 64358957Seric char junkbuf[MAXLINE]; 64458957Seric 64557379Seric if (mci->mci_out != NULL) 64657379Seric (void) fflush(mci->mci_out); 6474684Seric 6487677Seric if (tTd(18, 1)) 6494796Seric printf("reply\n"); 6504796Seric 6517356Seric /* 6527356Seric ** Read the input line, being careful not to hang. 6537356Seric */ 6547356Seric 65559014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 6564684Seric { 6577356Seric register char *p; 65853751Seric extern time_t curtime(); 6594684Seric 6607685Seric /* actually do the read */ 66153751Seric if (e->e_xfp != NULL) 66253751Seric (void) fflush(e->e_xfp); /* for debugging */ 6637356Seric 66410054Seric /* if we are in the process of closing just give the code */ 66553740Seric if (mci->mci_state == MCIS_CLOSED) 66610054Seric return (SMTPCLOSING); 66710054Seric 66858680Seric if (mci->mci_out != NULL) 66958680Seric fflush(mci->mci_out); 67058680Seric 67110054Seric /* get the line from the other side */ 67261093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 67353751Seric mci->mci_lastuse = curtime(); 67453751Seric 67510054Seric if (p == NULL) 67610131Seric { 67763753Seric bool oldholderrs; 67810148Seric extern char MsgBuf[]; /* err.c */ 67910148Seric 68021065Seric /* if the remote end closed early, fake an error */ 68121065Seric if (errno == 0) 68221065Seric # ifdef ECONNRESET 68321065Seric errno = ECONNRESET; 68456795Seric # else /* ECONNRESET */ 68521065Seric errno = EPIPE; 68656795Seric # endif /* ECONNRESET */ 68721065Seric 68857379Seric mci->mci_errno = errno; 68957642Seric mci->mci_exitstat = EX_TEMPFAIL; 69063753Seric oldholderrs = HoldErrs; 69163753Seric HoldErrs = TRUE; 69263753Seric usrerr("451 reply: read error from %s", mci->mci_host); 69363753Seric 69410420Seric /* if debugging, pause so we can see state */ 69510420Seric if (tTd(18, 100)) 69610420Seric pause(); 69754967Seric mci->mci_state = MCIS_ERROR; 69853751Seric smtpquit(m, mci, e); 69963753Seric #ifdef XDEBUG 70063753Seric { 70163753Seric char wbuf[MAXLINE]; 70263753Seric sprintf(wbuf, "%s... reply(%s) during %s", 70363753Seric e->e_to, mci->mci_host, SmtpPhase); 70463753Seric checkfd012(wbuf); 70563753Seric } 70663753Seric #endif 70763753Seric HoldErrs = oldholderrs; 70810054Seric return (-1); 70910131Seric } 71058957Seric fixcrlf(bufp, TRUE); 71110054Seric 71263753Seric /* EHLO failure is not a real error */ 71363753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 71463753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 71514900Seric { 71614900Seric /* serious error -- log the previous command */ 71759014Seric if (SmtpMsgBuffer[0] != '\0') 71859014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 71959014Seric SmtpMsgBuffer[0] = '\0'; 72014900Seric 72114900Seric /* now log the message as from the other side */ 72258957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 72314900Seric } 72414900Seric 72514900Seric /* display the input for verbose mode */ 72658120Seric if (Verbose) 72759956Seric nmessage("050 %s", bufp); 7287356Seric 72959285Seric /* process the line */ 73059285Seric if (pfunc != NULL && !firstline) 73159285Seric (*pfunc)(bufp, m, mci, e); 73259285Seric 73359285Seric firstline = FALSE; 73459285Seric 7357356Seric /* if continuation is required, we can go on */ 73659014Seric if (bufp[3] == '-') 7374684Seric continue; 7387356Seric 73959014Seric /* ignore improperly formated input */ 74059014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 74159014Seric continue; 74259014Seric 7437356Seric /* decode the reply code */ 74459014Seric r = atoi(bufp); 7457356Seric 7467356Seric /* extra semantics: 0xx codes are "informational" */ 74759014Seric if (r >= 100) 74859014Seric break; 74959014Seric } 7507356Seric 75159014Seric /* 75259014Seric ** Now look at SmtpReplyBuffer -- only care about the first 75359014Seric ** line of the response from here on out. 75459014Seric */ 75558061Seric 75659014Seric /* save temporary failure messages for posterity */ 75759014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 75859014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 7599391Seric 76059014Seric /* reply code 421 is "Service Shutting Down" */ 76159014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 76259014Seric { 76359014Seric /* send the quit protocol */ 76459014Seric mci->mci_state = MCIS_SSD; 76559014Seric smtpquit(m, mci, e); 7664684Seric } 76759014Seric 76859014Seric return (r); 7694684Seric } 7704796Seric /* 7714865Seric ** SMTPMESSAGE -- send message to server 7724796Seric ** 7734796Seric ** Parameters: 7744796Seric ** f -- format 77510175Seric ** m -- the mailer to control formatting. 7764796Seric ** a, b, c -- parameters 7774796Seric ** 7784796Seric ** Returns: 7794796Seric ** none. 7804796Seric ** 7814796Seric ** Side Effects: 78253740Seric ** writes message to mci->mci_out. 7834796Seric */ 7844796Seric 7854865Seric /*VARARGS1*/ 78657642Seric #ifdef __STDC__ 78757642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 78857642Seric #else 78957642Seric smtpmessage(f, m, mci, va_alist) 7904796Seric char *f; 79110175Seric MAILER *m; 79254967Seric MCI *mci; 79357642Seric va_dcl 79457642Seric #endif 7954796Seric { 79656852Seric VA_LOCAL_DECL 79756852Seric 79857135Seric VA_START(mci); 79956852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 80056852Seric VA_END; 80158680Seric 80258120Seric if (tTd(18, 1) || Verbose) 80358151Seric nmessage(">>> %s", SmtpMsgBuffer); 80463753Seric if (TrafficLogFile != NULL) 80563753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 80653740Seric if (mci->mci_out != NULL) 80758680Seric { 80853740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 80954967Seric m == NULL ? "\r\n" : m->m_eol); 81058680Seric } 81159149Seric else if (tTd(18, 1)) 81258725Seric { 81359149Seric printf("smtpmessage: NULL mci_out\n"); 81458725Seric } 8154796Seric } 8165182Seric 81756795Seric # endif /* SMTP */ 818