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*64750Seric static char sccsid[] = "@(#)usersmtp.c 8.13 (Berkeley) 10/24/93 (with SMTP)"; 1433731Sbostic #else 15*64750Seric static char sccsid[] = "@(#)usersmtp.c 8.13 (Berkeley) 10/24/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); 118*64750Seric if (r < 0 || REPLYTYPE(r) == 4) 11952104Seric goto tempfail1; 120*64750Seric if (REPLYTYPE(r) != 2) 121*64750Seric 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 { 5719391Seric int i; 5724865Seric 57354967Seric /* send the quit message if we haven't gotten I/O error */ 57453751Seric if (mci->mci_state != MCIS_ERROR) 5759391Seric { 57661093Seric SmtpPhase = "client QUIT"; 57753751Seric smtpmessage("QUIT", m, mci); 57859285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 57953740Seric if (mci->mci_state == MCIS_CLOSED) 58010159Seric return; 5819391Seric } 5829391Seric 58352676Seric /* now actually close the connection and pick up the zombie */ 58458846Seric i = endmailer(mci, e, m->m_argv); 5859391Seric if (i != EX_OK) 58658151Seric syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); 5874865Seric } 5884865Seric /* 58954967Seric ** SMTPRSET -- send a RSET (reset) command 59054967Seric */ 59154967Seric 59254967Seric smtprset(m, mci, e) 59354967Seric register MAILER *m; 59454967Seric register MCI *mci; 59554967Seric ENVELOPE *e; 59654967Seric { 59754967Seric int r; 59854967Seric 59961093Seric SmtpPhase = "client RSET"; 60054967Seric smtpmessage("RSET", m, mci); 60159285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 60257734Seric if (r < 0) 60357734Seric mci->mci_state = MCIS_ERROR; 60454967Seric else if (REPLYTYPE(r) == 2) 60557734Seric { 60657734Seric mci->mci_state = MCIS_OPEN; 60757734Seric return; 60857734Seric } 60957734Seric smtpquit(m, mci, e); 61054967Seric } 61154967Seric /* 61258867Seric ** SMTPPROBE -- check the connection state 61354967Seric */ 61454967Seric 61558867Seric smtpprobe(mci) 61654967Seric register MCI *mci; 61754967Seric { 61854967Seric int r; 61954967Seric MAILER *m = mci->mci_mailer; 62054967Seric extern ENVELOPE BlankEnvelope; 62154967Seric ENVELOPE *e = &BlankEnvelope; 62254967Seric 62361093Seric SmtpPhase = "client probe"; 62458867Seric smtpmessage("RSET", m, mci); 62559285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 62658061Seric if (r < 0 || REPLYTYPE(r) != 2) 62754967Seric smtpquit(m, mci, e); 62854967Seric return r; 62954967Seric } 63054967Seric /* 6314684Seric ** REPLY -- read arpanet reply 6324684Seric ** 6334684Seric ** Parameters: 63410175Seric ** m -- the mailer we are reading the reply from. 63557379Seric ** mci -- the mailer connection info structure. 63657379Seric ** e -- the current envelope. 63757379Seric ** timeout -- the timeout for reads. 63859285Seric ** pfunc -- processing function for second and subsequent 63959285Seric ** lines of response -- if null, no special 64059285Seric ** processing is done. 6414684Seric ** 6424684Seric ** Returns: 6434684Seric ** reply code it reads. 6444684Seric ** 6454684Seric ** Side Effects: 6464684Seric ** flushes the mail file. 6474684Seric */ 6484684Seric 64959285Seric reply(m, mci, e, timeout, pfunc) 65053751Seric MAILER *m; 65154967Seric MCI *mci; 65253751Seric ENVELOPE *e; 65359285Seric time_t timeout; 65459285Seric void (*pfunc)(); 6554684Seric { 65659014Seric register char *bufp; 65759014Seric register int r; 65859285Seric bool firstline = TRUE; 65958957Seric char junkbuf[MAXLINE]; 66058957Seric 66157379Seric if (mci->mci_out != NULL) 66257379Seric (void) fflush(mci->mci_out); 6634684Seric 6647677Seric if (tTd(18, 1)) 6654796Seric printf("reply\n"); 6664796Seric 6677356Seric /* 6687356Seric ** Read the input line, being careful not to hang. 6697356Seric */ 6707356Seric 67159014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 6724684Seric { 6737356Seric register char *p; 67453751Seric extern time_t curtime(); 6754684Seric 6767685Seric /* actually do the read */ 67753751Seric if (e->e_xfp != NULL) 67853751Seric (void) fflush(e->e_xfp); /* for debugging */ 6797356Seric 68010054Seric /* if we are in the process of closing just give the code */ 68153740Seric if (mci->mci_state == MCIS_CLOSED) 68210054Seric return (SMTPCLOSING); 68310054Seric 68458680Seric if (mci->mci_out != NULL) 68558680Seric fflush(mci->mci_out); 68658680Seric 68710054Seric /* get the line from the other side */ 68861093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 68953751Seric mci->mci_lastuse = curtime(); 69053751Seric 69110054Seric if (p == NULL) 69210131Seric { 69363753Seric bool oldholderrs; 69410148Seric extern char MsgBuf[]; /* err.c */ 69510148Seric 69621065Seric /* if the remote end closed early, fake an error */ 69721065Seric if (errno == 0) 69821065Seric # ifdef ECONNRESET 69921065Seric errno = ECONNRESET; 70056795Seric # else /* ECONNRESET */ 70121065Seric errno = EPIPE; 70256795Seric # endif /* ECONNRESET */ 70321065Seric 70457379Seric mci->mci_errno = errno; 70557642Seric mci->mci_exitstat = EX_TEMPFAIL; 70663753Seric oldholderrs = HoldErrs; 70763753Seric HoldErrs = TRUE; 70863753Seric usrerr("451 reply: read error from %s", mci->mci_host); 70963753Seric 71010420Seric /* if debugging, pause so we can see state */ 71110420Seric if (tTd(18, 100)) 71210420Seric pause(); 71354967Seric mci->mci_state = MCIS_ERROR; 71453751Seric smtpquit(m, mci, e); 71563753Seric #ifdef XDEBUG 71663753Seric { 71763753Seric char wbuf[MAXLINE]; 71864082Seric char *p = wbuf; 71964082Seric if (e->e_to != NULL) 72064082Seric { 72164082Seric sprintf(p, "%s... ", e->e_to); 72264082Seric p += strlen(p); 72364082Seric } 72464082Seric sprintf(p, "reply(%s) during %s", 72564082Seric mci->mci_host, SmtpPhase); 72663753Seric checkfd012(wbuf); 72763753Seric } 72863753Seric #endif 72963753Seric HoldErrs = oldholderrs; 73010054Seric return (-1); 73110131Seric } 73258957Seric fixcrlf(bufp, TRUE); 73310054Seric 73463753Seric /* EHLO failure is not a real error */ 73563753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 73663753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 73714900Seric { 73814900Seric /* serious error -- log the previous command */ 73964071Seric if (SmtpNeedIntro) 74064071Seric { 74164071Seric /* inform user who we are chatting with */ 74264071Seric fprintf(CurEnv->e_xfp, 74364071Seric "... while talking to %s:\n", 74464071Seric CurHostName); 74564071Seric SmtpNeedIntro = FALSE; 74664071Seric } 74759014Seric if (SmtpMsgBuffer[0] != '\0') 74859014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 74959014Seric SmtpMsgBuffer[0] = '\0'; 75014900Seric 75114900Seric /* now log the message as from the other side */ 75258957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 75314900Seric } 75414900Seric 75514900Seric /* display the input for verbose mode */ 75658120Seric if (Verbose) 75759956Seric nmessage("050 %s", bufp); 7587356Seric 75959285Seric /* process the line */ 76059285Seric if (pfunc != NULL && !firstline) 76159285Seric (*pfunc)(bufp, m, mci, e); 76259285Seric 76359285Seric firstline = FALSE; 76459285Seric 7657356Seric /* if continuation is required, we can go on */ 76659014Seric if (bufp[3] == '-') 7674684Seric continue; 7687356Seric 76959014Seric /* ignore improperly formated input */ 77059014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 77159014Seric continue; 77259014Seric 7737356Seric /* decode the reply code */ 77459014Seric r = atoi(bufp); 7757356Seric 7767356Seric /* extra semantics: 0xx codes are "informational" */ 77759014Seric if (r >= 100) 77859014Seric break; 77959014Seric } 7807356Seric 78159014Seric /* 78259014Seric ** Now look at SmtpReplyBuffer -- only care about the first 78359014Seric ** line of the response from here on out. 78459014Seric */ 78558061Seric 78659014Seric /* save temporary failure messages for posterity */ 78759014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 78859014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 7899391Seric 79059014Seric /* reply code 421 is "Service Shutting Down" */ 79159014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 79259014Seric { 79359014Seric /* send the quit protocol */ 79459014Seric mci->mci_state = MCIS_SSD; 79559014Seric smtpquit(m, mci, e); 7964684Seric } 79759014Seric 79859014Seric return (r); 7994684Seric } 8004796Seric /* 8014865Seric ** SMTPMESSAGE -- send message to server 8024796Seric ** 8034796Seric ** Parameters: 8044796Seric ** f -- format 80510175Seric ** m -- the mailer to control formatting. 8064796Seric ** a, b, c -- parameters 8074796Seric ** 8084796Seric ** Returns: 8094796Seric ** none. 8104796Seric ** 8114796Seric ** Side Effects: 81253740Seric ** writes message to mci->mci_out. 8134796Seric */ 8144796Seric 8154865Seric /*VARARGS1*/ 81657642Seric #ifdef __STDC__ 81757642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 81857642Seric #else 81957642Seric smtpmessage(f, m, mci, va_alist) 8204796Seric char *f; 82110175Seric MAILER *m; 82254967Seric MCI *mci; 82357642Seric va_dcl 82457642Seric #endif 8254796Seric { 82656852Seric VA_LOCAL_DECL 82756852Seric 82857135Seric VA_START(mci); 82956852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 83056852Seric VA_END; 83158680Seric 83258120Seric if (tTd(18, 1) || Verbose) 83358151Seric nmessage(">>> %s", SmtpMsgBuffer); 83463753Seric if (TrafficLogFile != NULL) 83563753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 83653740Seric if (mci->mci_out != NULL) 83758680Seric { 83853740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 83954967Seric m == NULL ? "\r\n" : m->m_eol); 84058680Seric } 84159149Seric else if (tTd(18, 1)) 84258725Seric { 84359149Seric printf("smtpmessage: NULL mci_out\n"); 84458725Seric } 8454796Seric } 8465182Seric 84756795Seric # endif /* SMTP */ 848