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*67417Seric static char sccsid[] = "@(#)usersmtp.c 8.19 (Berkeley) 06/17/94 (with SMTP)"; 1433731Sbostic #else 15*67417Seric static char sccsid[] = "@(#)usersmtp.c 8.19 (Berkeley) 06/17/94 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194684Seric # include <sysexits.h> 2021065Seric # include <errno.h> 214684Seric 2233731Sbostic # ifdef SMTP 234684Seric 244684Seric /* 259391Seric ** USERSMTP -- run SMTP protocol from the user end. 269391Seric ** 279391Seric ** This protocol is described in RFC821. 289391Seric */ 299391Seric 309391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 319391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 329391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 339391Seric 3414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 3510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 3621065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 3710054Seric int SmtpPid; /* pid of mailer */ 3864071Seric bool SmtpNeedIntro; /* need "while talking" in transcript */ 3958671Seric 4058671Seric #ifdef __STDC__ 4158671Seric extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 4258671Seric #endif 439391Seric /* 444865Seric ** SMTPINIT -- initialize SMTP. 454684Seric ** 464865Seric ** Opens the connection and sends the initial protocol. 474684Seric ** 484684Seric ** Parameters: 494865Seric ** m -- mailer to create connection to. 504865Seric ** pvp -- pointer to parameter vector to pass to 514865Seric ** the mailer. 524684Seric ** 534684Seric ** Returns: 5454967Seric ** none. 554684Seric ** 564684Seric ** Side Effects: 574865Seric ** creates connection and sends initial protocol. 584684Seric */ 594684Seric 6054967Seric smtpinit(m, mci, e) 614865Seric struct mailer *m; 6254967Seric register MCI *mci; 6353751Seric ENVELOPE *e; 644684Seric { 654865Seric register int r; 6658957Seric register char *p; 6760210Seric extern void esmtp_check(); 6859285Seric extern void helo_options(); 694684Seric 7063753Seric if (tTd(18, 1)) 7157379Seric { 7257379Seric printf("smtpinit "); 7364731Seric mci_dump(mci, FALSE); 7457379Seric } 7557379Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8021065Seric SmtpError[0] = '\0'; 8157379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 8264071Seric SmtpNeedIntro = TRUE; 8354967Seric switch (mci->mci_state) 846051Seric { 8554967Seric case MCIS_ACTIVE: 8654967Seric /* need to clear old information */ 8754967Seric smtprset(m, mci, e); 8857734Seric /* fall through */ 8915139Seric 9054967Seric case MCIS_OPEN: 9154967Seric return; 9254967Seric 9354967Seric case MCIS_ERROR: 9454967Seric case MCIS_SSD: 9554967Seric /* shouldn't happen */ 9654967Seric smtpquit(m, mci, e); 9757734Seric /* fall through */ 9854967Seric 9954967Seric case MCIS_CLOSED: 10058151Seric syserr("451 smtpinit: state CLOSED"); 10154967Seric return; 10254967Seric 10354967Seric case MCIS_OPENING: 10454967Seric break; 1056051Seric } 1064796Seric 10754967Seric mci->mci_state = MCIS_OPENING; 10854967Seric 1094865Seric /* 1104865Seric ** Get the greeting message. 11114913Seric ** This should appear spontaneously. Give it five minutes to 11214886Seric ** happen. 1134865Seric */ 1144797Seric 11561093Seric SmtpPhase = mci->mci_phase = "client greeting"; 11653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11760210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 11864750Seric if (r < 0 || REPLYTYPE(r) == 4) 11952104Seric goto tempfail1; 12064750Seric if (REPLYTYPE(r) != 2) 12164750Seric goto unavailable; 1224684Seric 1234865Seric /* 1244976Seric ** Send the HELO command. 1257963Seric ** My mother taught me to always introduce myself. 1264976Seric */ 1274976Seric 12859285Seric if (bitnset(M_ESMTP, m->m_flags)) 12959285Seric mci->mci_flags |= MCIF_ESMTP; 13059285Seric 13159285Seric tryhelo: 13259285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 13359285Seric { 13459285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13561093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13659285Seric } 13759285Seric else 13859285Seric { 13959285Seric smtpmessage("HELO %s", m, mci, MyHostName); 14061093Seric SmtpPhase = mci->mci_phase = "client HELO"; 14159285Seric } 14253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 14359285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1448005Seric if (r < 0) 14552104Seric goto tempfail1; 1468005Seric else if (REPLYTYPE(r) == 5) 14759285Seric { 14859285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 14959285Seric { 15059285Seric /* try old SMTP instead */ 15159285Seric mci->mci_flags &= ~MCIF_ESMTP; 15259285Seric goto tryhelo; 15359285Seric } 15414913Seric goto unavailable; 15559285Seric } 1567963Seric else if (REPLYTYPE(r) != 2) 15752104Seric goto tempfail1; 1584976Seric 1594976Seric /* 16058957Seric ** Check to see if we actually ended up talking to ourself. 16158957Seric ** This means we didn't know about an alias or MX, or we managed 16258957Seric ** to connect to an echo server. 16364651Seric ** 16464651Seric ** If this code remains at all, "CheckLoopBack" should be 16564651Seric ** a mailer flag. This is a MAYBENEXTRELEASE feature. 16658957Seric */ 16758957Seric 16859026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16958957Seric if (p != NULL) 17061707Seric *p = '\0'; 17164644Seric if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17258957Seric { 17358957Seric syserr("553 %s config error: mail loops back to myself", 17458957Seric MyHostName); 17558957Seric mci->mci_exitstat = EX_CONFIG; 17658957Seric mci->mci_errno = 0; 17758957Seric smtpquit(m, mci, e); 17858957Seric return; 17958957Seric } 18058957Seric 18158957Seric /* 1829315Seric ** If this is expected to be another sendmail, send some internal 1839315Seric ** commands. 1849315Seric */ 1859315Seric 18610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1879315Seric { 1889315Seric /* tell it to be verbose */ 18953751Seric smtpmessage("VERB", m, mci); 19059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1919315Seric if (r < 0) 19252104Seric goto tempfail2; 1939315Seric } 1949315Seric 19565057Seric if (mci->mci_state != MCIS_CLOSED) 19665057Seric { 19765057Seric mci->mci_state = MCIS_OPEN; 19865057Seric return; 19965057Seric } 20053751Seric 20165057Seric /* got a 421 error code during startup */ 20265057Seric 20353751Seric tempfail1: 20453751Seric tempfail2: 20553751Seric mci->mci_exitstat = EX_TEMPFAIL; 20657379Seric if (mci->mci_errno == 0) 20757379Seric mci->mci_errno = errno; 20857379Seric if (mci->mci_state != MCIS_CLOSED) 20957379Seric smtpquit(m, mci, e); 21054967Seric return; 21153751Seric 21253751Seric unavailable: 21353751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21453751Seric mci->mci_errno = errno; 21553751Seric smtpquit(m, mci, e); 21654967Seric return; 21753751Seric } 21859285Seric /* 21960210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 22060210Seric ** 22160210Seric ** 22260210Seric ** Parameters: 22360210Seric ** line -- the response line. 22460210Seric ** m -- the mailer. 22560210Seric ** mci -- the mailer connection info. 22660210Seric ** e -- the envelope. 22760210Seric ** 22860210Seric ** Returns: 22960210Seric ** none. 23060210Seric */ 23160210Seric 23260210Seric void 23360210Seric esmtp_check(line, m, mci, e) 23460210Seric char *line; 23560210Seric MAILER *m; 23660210Seric register MCI *mci; 23760210Seric ENVELOPE *e; 23860210Seric { 23960210Seric if (strlen(line) < 5) 24060210Seric return; 24160210Seric line += 4; 24260210Seric if (strncmp(line, "ESMTP ", 6) == 0) 24360210Seric mci->mci_flags |= MCIF_ESMTP; 24460210Seric } 24560210Seric /* 24659285Seric ** HELO_OPTIONS -- process the options on a HELO line. 24759285Seric ** 24859285Seric ** Parameters: 24959285Seric ** line -- the response line. 25059285Seric ** m -- the mailer. 25159285Seric ** mci -- the mailer connection info. 25259285Seric ** e -- the envelope. 25359285Seric ** 25459285Seric ** Returns: 25559285Seric ** none. 25659285Seric */ 25753751Seric 25859285Seric void 25959285Seric helo_options(line, m, mci, e) 26059285Seric char *line; 26159285Seric MAILER *m; 26259285Seric register MCI *mci; 26359285Seric ENVELOPE *e; 26459285Seric { 26559285Seric register char *p; 26659285Seric 26759285Seric if (strlen(line) < 5) 26859285Seric return; 26959285Seric line += 4; 27059285Seric p = strchr(line, ' '); 27159285Seric if (p != NULL) 27259285Seric *p++ = '\0'; 27359285Seric if (strcasecmp(line, "size") == 0) 27459285Seric { 27559285Seric mci->mci_flags |= MCIF_SIZE; 27659285Seric if (p != NULL) 27759285Seric mci->mci_maxsize = atol(p); 27859285Seric } 27959285Seric else if (strcasecmp(line, "8bitmime") == 0) 28065870Seric { 28159285Seric mci->mci_flags |= MCIF_8BITMIME; 28265870Seric mci->mci_flags &= ~MCIF_7BIT; 28365870Seric } 28459285Seric else if (strcasecmp(line, "expn") == 0) 28559285Seric mci->mci_flags |= MCIF_EXPN; 28659285Seric } 28759285Seric /* 28859285Seric ** SMTPMAILFROM -- send MAIL command 28959285Seric ** 29059285Seric ** Parameters: 29159285Seric ** m -- the mailer. 29259285Seric ** mci -- the mailer connection structure. 29359285Seric ** e -- the envelope (including the sender to specify). 29459285Seric */ 29559285Seric 29653751Seric smtpmailfrom(m, mci, e) 29753751Seric struct mailer *m; 29854967Seric MCI *mci; 29953751Seric ENVELOPE *e; 30053751Seric { 30153751Seric int r; 30265494Seric char *bufp; 30353751Seric char buf[MAXNAME]; 30459285Seric char optbuf[MAXLINE]; 30553751Seric 30663753Seric if (tTd(18, 2)) 30757943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 30857943Seric 30959285Seric /* set up appropriate options to include */ 31064254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 31159285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 31259285Seric else 31359285Seric strcpy(optbuf, ""); 31459285Seric 315*67417Seric if (e->e_bodytype != NULL) 316*67417Seric { 317*67417Seric if (bitset(MCIF_8BITMIME, mci->mci_flags)) 318*67417Seric { 319*67417Seric strcat(optbuf, " BODY="); 320*67417Seric strcat(optbuf, e->e_bodytype); 321*67417Seric } 322*67417Seric else if (strcasecmp(e->e_bodytype, "7bit") != 0) 323*67417Seric { 324*67417Seric /* cannot just send a 7-bit version */ 325*67417Seric usrerr("%s does not support 8BITMIME", mci->mci_host); 326*67417Seric return EX_DATAERR; 327*67417Seric } 328*67417Seric } 329*67417Seric 3309315Seric /* 3314865Seric ** Send the MAIL command. 3324865Seric ** Designates the sender. 3334865Seric */ 3344796Seric 33553751Seric mci->mci_state = MCIS_ACTIVE; 33653751Seric 33759540Seric if (bitset(EF_RESPONSE, e->e_flags) && 33859540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 33958680Seric (void) strcpy(buf, ""); 34058680Seric else 34158680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 34265494Seric if (buf[0] == '<') 34365494Seric { 34465494Seric /* strip off <angle brackets> (put back on below) */ 34565494Seric bufp = &buf[strlen(buf) - 1]; 34665494Seric if (*bufp == '>') 34765494Seric *bufp = '\0'; 34865494Seric bufp = &buf[1]; 34965494Seric } 35065494Seric else 35165494Seric bufp = buf; 35253751Seric if (e->e_from.q_mailer == LocalMailer || 35310688Seric !bitnset(M_FROMPATH, m->m_flags)) 3548436Seric { 35565494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 3568436Seric } 3578436Seric else 3588436Seric { 35959285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 36065494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 3618436Seric } 36261093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 36353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 36459285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3658005Seric if (r < 0 || REPLYTYPE(r) == 4) 36653751Seric { 36753751Seric mci->mci_exitstat = EX_TEMPFAIL; 36853751Seric mci->mci_errno = errno; 36953751Seric smtpquit(m, mci, e); 37053751Seric return EX_TEMPFAIL; 37153751Seric } 3727963Seric else if (r == 250) 37353751Seric { 37453751Seric mci->mci_exitstat = EX_OK; 37553751Seric return EX_OK; 37653751Seric } 3777963Seric else if (r == 552) 37853751Seric { 37953751Seric /* signal service unavailable */ 38053751Seric mci->mci_exitstat = EX_UNAVAILABLE; 38153751Seric smtpquit(m, mci, e); 38253751Seric return EX_UNAVAILABLE; 38353751Seric } 38414913Seric 38558008Seric #ifdef LOG 38658020Seric if (LogLevel > 1) 38758008Seric { 38858008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 38958008Seric e->e_id, SmtpReplyBuffer); 39058008Seric } 39158008Seric #endif 39258008Seric 39314913Seric /* protocol error -- close up */ 39453751Seric smtpquit(m, mci, e); 39553751Seric mci->mci_exitstat = EX_PROTOCOL; 39653751Seric return EX_PROTOCOL; 3974684Seric } 3984684Seric /* 3994976Seric ** SMTPRCPT -- designate recipient. 4004797Seric ** 4014797Seric ** Parameters: 4024865Seric ** to -- address of recipient. 40310175Seric ** m -- the mailer we are sending to. 40457379Seric ** mci -- the connection info for this transaction. 40557379Seric ** e -- the envelope for this transaction. 4064797Seric ** 4074797Seric ** Returns: 4084865Seric ** exit status corresponding to recipient status. 4094797Seric ** 4104797Seric ** Side Effects: 4114865Seric ** Sends the mail via SMTP. 4124797Seric */ 4134797Seric 41453751Seric smtprcpt(to, m, mci, e) 4154865Seric ADDRESS *to; 41610175Seric register MAILER *m; 41754967Seric MCI *mci; 41853751Seric ENVELOPE *e; 4194797Seric { 4204797Seric register int r; 4214797Seric 42253751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 4234865Seric 42461093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 42553751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 42659285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 4278005Seric if (r < 0 || REPLYTYPE(r) == 4) 4284865Seric return (EX_TEMPFAIL); 4297963Seric else if (REPLYTYPE(r) == 2) 4307963Seric return (EX_OK); 4317964Seric else if (r == 550 || r == 551 || r == 553) 4327964Seric return (EX_NOUSER); 4337964Seric else if (r == 552 || r == 554) 4347964Seric return (EX_UNAVAILABLE); 43558008Seric 43658008Seric #ifdef LOG 43758020Seric if (LogLevel > 1) 43858008Seric { 43958008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 44058008Seric e->e_id, SmtpReplyBuffer); 44158008Seric } 44258008Seric #endif 44358008Seric 4447964Seric return (EX_PROTOCOL); 4454797Seric } 4464797Seric /* 44710175Seric ** SMTPDATA -- send the data and clean up the transaction. 4484684Seric ** 4494684Seric ** Parameters: 4504865Seric ** m -- mailer being sent to. 4516980Seric ** e -- the envelope for this message. 4524684Seric ** 4534684Seric ** Returns: 4544976Seric ** exit status corresponding to DATA command. 4554684Seric ** 4564684Seric ** Side Effects: 4574865Seric ** none. 4584684Seric */ 4594684Seric 46063753Seric static jmp_buf CtxDataTimeout; 46163937Seric static int datatimeout(); 46263753Seric 46353740Seric smtpdata(m, mci, e) 4644865Seric struct mailer *m; 46554967Seric register MCI *mci; 4666980Seric register ENVELOPE *e; 4674684Seric { 4684684Seric register int r; 46963753Seric register EVENT *ev; 47063753Seric time_t timeout; 4714684Seric 4724797Seric /* 4734797Seric ** Send the data. 47410175Seric ** First send the command and check that it is ok. 47510175Seric ** Then send the data. 47610175Seric ** Follow it up with a dot to terminate. 47710175Seric ** Finally get the results of the transaction. 4784797Seric */ 4794797Seric 48010175Seric /* send the command and check ok to proceed */ 48153751Seric smtpmessage("DATA", m, mci); 48261093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 48353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 48459285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4858005Seric if (r < 0 || REPLYTYPE(r) == 4) 48657990Seric { 48757990Seric smtpquit(m, mci, e); 4884797Seric return (EX_TEMPFAIL); 48957990Seric } 4907963Seric else if (r == 554) 49157990Seric { 49257990Seric smtprset(m, mci, e); 4937963Seric return (EX_UNAVAILABLE); 49457990Seric } 4957963Seric else if (r != 354) 49657990Seric { 49758008Seric #ifdef LOG 49858020Seric if (LogLevel > 1) 49958008Seric { 50058008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 50158008Seric e->e_id, SmtpReplyBuffer); 50258008Seric } 50358008Seric #endif 50457990Seric smtprset(m, mci, e); 5057964Seric return (EX_PROTOCOL); 50657990Seric } 50710175Seric 50863757Seric /* 50963757Seric ** Set timeout around data writes. Make it at least large 51063757Seric ** enough for DNS timeouts on all recipients plus some fudge 51163757Seric ** factor. The main thing is that it should not be infinite. 51263757Seric */ 51363757Seric 51463753Seric if (setjmp(CtxDataTimeout) != 0) 51563753Seric { 51663753Seric mci->mci_errno = errno; 51763753Seric mci->mci_exitstat = EX_TEMPFAIL; 51863753Seric mci->mci_state = MCIS_ERROR; 51963753Seric syserr("451 timeout writing message to %s", mci->mci_host); 52063753Seric smtpquit(m, mci, e); 52163753Seric return EX_TEMPFAIL; 52263753Seric } 52363753Seric 52463787Seric timeout = e->e_msgsize / 16; 52563787Seric if (timeout < (time_t) 60) 52663787Seric timeout = (time_t) 60; 52763787Seric timeout += e->e_nrcpts * 90; 52863753Seric ev = setevent(timeout, datatimeout, 0); 52963753Seric 53010175Seric /* now output the actual message */ 53165870Seric (*e->e_puthdr)(mci, e); 53265870Seric putline("\n", mci); 53365870Seric (*e->e_putbody)(mci, e, NULL); 53410175Seric 53563753Seric clrevent(ev); 53663753Seric 53764718Seric if (ferror(mci->mci_out)) 53864718Seric { 53964718Seric /* error during processing -- don't send the dot */ 54064718Seric mci->mci_errno = EIO; 54164718Seric mci->mci_exitstat = EX_IOERR; 54264718Seric mci->mci_state = MCIS_ERROR; 54364718Seric smtpquit(m, mci, e); 54464718Seric return EX_IOERR; 54564718Seric } 54664718Seric 54710175Seric /* terminate the message */ 54853740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 54963753Seric if (TrafficLogFile != NULL) 55063753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 55158120Seric if (Verbose) 55258151Seric nmessage(">>> ."); 55310175Seric 55410175Seric /* check for the results of the transaction */ 55561093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 55653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 55759285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 55853751Seric if (r < 0) 55957990Seric { 56057990Seric smtpquit(m, mci, e); 5614797Seric return (EX_TEMPFAIL); 56257990Seric } 56353751Seric mci->mci_state = MCIS_OPEN; 56458917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 56553751Seric if (REPLYTYPE(r) == 4) 56653751Seric return (EX_TEMPFAIL); 5677963Seric else if (r == 250) 5687963Seric return (EX_OK); 5697963Seric else if (r == 552 || r == 554) 5707963Seric return (EX_UNAVAILABLE); 57158008Seric #ifdef LOG 57258020Seric if (LogLevel > 1) 57358008Seric { 57458008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 57558008Seric e->e_id, SmtpReplyBuffer); 57658008Seric } 57758008Seric #endif 5787964Seric return (EX_PROTOCOL); 5794684Seric } 58063753Seric 58163753Seric 58263753Seric static int 58363753Seric datatimeout() 58463753Seric { 58563753Seric longjmp(CtxDataTimeout, 1); 58663753Seric } 5874684Seric /* 5884865Seric ** SMTPQUIT -- close the SMTP connection. 5894865Seric ** 5904865Seric ** Parameters: 59115535Seric ** m -- a pointer to the mailer. 5924865Seric ** 5934865Seric ** Returns: 5944865Seric ** none. 5954865Seric ** 5964865Seric ** Side Effects: 5974865Seric ** sends the final protocol and closes the connection. 5984865Seric */ 5994865Seric 60053751Seric smtpquit(m, mci, e) 60153751Seric register MAILER *m; 60254967Seric register MCI *mci; 60353751Seric ENVELOPE *e; 6044865Seric { 60564822Seric bool oldSuprErrs = SuprErrs; 6064865Seric 60764822Seric /* 60864822Seric ** Suppress errors here -- we may be processing a different 60964822Seric ** job when we do the quit connection, and we don't want the 61064822Seric ** new job to be penalized for something that isn't it's 61164822Seric ** problem. 61264822Seric */ 61364822Seric 61464822Seric SuprErrs = TRUE; 61564822Seric 61654967Seric /* send the quit message if we haven't gotten I/O error */ 61753751Seric if (mci->mci_state != MCIS_ERROR) 6189391Seric { 61961093Seric SmtpPhase = "client QUIT"; 62053751Seric smtpmessage("QUIT", m, mci); 62159285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 62264822Seric SuprErrs = oldSuprErrs; 62353740Seric if (mci->mci_state == MCIS_CLOSED) 62464822Seric { 62564822Seric SuprErrs = oldSuprErrs; 62610159Seric return; 62764822Seric } 6289391Seric } 6299391Seric 63052676Seric /* now actually close the connection and pick up the zombie */ 63165194Seric (void) endmailer(mci, e, NULL); 63264822Seric 63364822Seric SuprErrs = oldSuprErrs; 6344865Seric } 6354865Seric /* 63654967Seric ** SMTPRSET -- send a RSET (reset) command 63754967Seric */ 63854967Seric 63954967Seric smtprset(m, mci, e) 64054967Seric register MAILER *m; 64154967Seric register MCI *mci; 64254967Seric ENVELOPE *e; 64354967Seric { 64454967Seric int r; 64554967Seric 64661093Seric SmtpPhase = "client RSET"; 64754967Seric smtpmessage("RSET", m, mci); 64859285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 64957734Seric if (r < 0) 65057734Seric mci->mci_state = MCIS_ERROR; 65154967Seric else if (REPLYTYPE(r) == 2) 65257734Seric { 65357734Seric mci->mci_state = MCIS_OPEN; 65457734Seric return; 65557734Seric } 65657734Seric smtpquit(m, mci, e); 65754967Seric } 65854967Seric /* 65958867Seric ** SMTPPROBE -- check the connection state 66054967Seric */ 66154967Seric 66258867Seric smtpprobe(mci) 66354967Seric register MCI *mci; 66454967Seric { 66554967Seric int r; 66654967Seric MAILER *m = mci->mci_mailer; 66754967Seric extern ENVELOPE BlankEnvelope; 66854967Seric ENVELOPE *e = &BlankEnvelope; 66954967Seric 67061093Seric SmtpPhase = "client probe"; 67158867Seric smtpmessage("RSET", m, mci); 67259285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 67358061Seric if (r < 0 || REPLYTYPE(r) != 2) 67454967Seric smtpquit(m, mci, e); 67554967Seric return r; 67654967Seric } 67754967Seric /* 6784684Seric ** REPLY -- read arpanet reply 6794684Seric ** 6804684Seric ** Parameters: 68110175Seric ** m -- the mailer we are reading the reply from. 68257379Seric ** mci -- the mailer connection info structure. 68357379Seric ** e -- the current envelope. 68457379Seric ** timeout -- the timeout for reads. 68559285Seric ** pfunc -- processing function for second and subsequent 68659285Seric ** lines of response -- if null, no special 68759285Seric ** processing is done. 6884684Seric ** 6894684Seric ** Returns: 6904684Seric ** reply code it reads. 6914684Seric ** 6924684Seric ** Side Effects: 6934684Seric ** flushes the mail file. 6944684Seric */ 6954684Seric 69659285Seric reply(m, mci, e, timeout, pfunc) 69753751Seric MAILER *m; 69854967Seric MCI *mci; 69953751Seric ENVELOPE *e; 70059285Seric time_t timeout; 70159285Seric void (*pfunc)(); 7024684Seric { 70359014Seric register char *bufp; 70459014Seric register int r; 70559285Seric bool firstline = TRUE; 70658957Seric char junkbuf[MAXLINE]; 70758957Seric 70857379Seric if (mci->mci_out != NULL) 70957379Seric (void) fflush(mci->mci_out); 7104684Seric 7117677Seric if (tTd(18, 1)) 7124796Seric printf("reply\n"); 7134796Seric 7147356Seric /* 7157356Seric ** Read the input line, being careful not to hang. 7167356Seric */ 7177356Seric 71859014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 7194684Seric { 7207356Seric register char *p; 72153751Seric extern time_t curtime(); 7224684Seric 7237685Seric /* actually do the read */ 72453751Seric if (e->e_xfp != NULL) 72553751Seric (void) fflush(e->e_xfp); /* for debugging */ 7267356Seric 72710054Seric /* if we are in the process of closing just give the code */ 72853740Seric if (mci->mci_state == MCIS_CLOSED) 72910054Seric return (SMTPCLOSING); 73010054Seric 73158680Seric if (mci->mci_out != NULL) 73258680Seric fflush(mci->mci_out); 73358680Seric 73410054Seric /* get the line from the other side */ 73561093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 73653751Seric mci->mci_lastuse = curtime(); 73753751Seric 73810054Seric if (p == NULL) 73910131Seric { 74063753Seric bool oldholderrs; 74110148Seric extern char MsgBuf[]; /* err.c */ 74210148Seric 74321065Seric /* if the remote end closed early, fake an error */ 74421065Seric if (errno == 0) 74521065Seric # ifdef ECONNRESET 74621065Seric errno = ECONNRESET; 74756795Seric # else /* ECONNRESET */ 74821065Seric errno = EPIPE; 74956795Seric # endif /* ECONNRESET */ 75021065Seric 75157379Seric mci->mci_errno = errno; 75257642Seric mci->mci_exitstat = EX_TEMPFAIL; 75363753Seric oldholderrs = HoldErrs; 75463753Seric HoldErrs = TRUE; 75563753Seric usrerr("451 reply: read error from %s", mci->mci_host); 75663753Seric 75710420Seric /* if debugging, pause so we can see state */ 75810420Seric if (tTd(18, 100)) 75910420Seric pause(); 76054967Seric mci->mci_state = MCIS_ERROR; 76153751Seric smtpquit(m, mci, e); 76263753Seric #ifdef XDEBUG 76363753Seric { 76463753Seric char wbuf[MAXLINE]; 76564082Seric char *p = wbuf; 76664082Seric if (e->e_to != NULL) 76764082Seric { 76864082Seric sprintf(p, "%s... ", e->e_to); 76964082Seric p += strlen(p); 77064082Seric } 77164082Seric sprintf(p, "reply(%s) during %s", 77264082Seric mci->mci_host, SmtpPhase); 77363753Seric checkfd012(wbuf); 77463753Seric } 77563753Seric #endif 77663753Seric HoldErrs = oldholderrs; 77710054Seric return (-1); 77810131Seric } 77958957Seric fixcrlf(bufp, TRUE); 78010054Seric 78163753Seric /* EHLO failure is not a real error */ 78263753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 78363753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 78414900Seric { 78514900Seric /* serious error -- log the previous command */ 78664071Seric if (SmtpNeedIntro) 78764071Seric { 78864071Seric /* inform user who we are chatting with */ 78964071Seric fprintf(CurEnv->e_xfp, 79064071Seric "... while talking to %s:\n", 79164071Seric CurHostName); 79264071Seric SmtpNeedIntro = FALSE; 79364071Seric } 79459014Seric if (SmtpMsgBuffer[0] != '\0') 79559014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 79659014Seric SmtpMsgBuffer[0] = '\0'; 79714900Seric 79814900Seric /* now log the message as from the other side */ 79958957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 80014900Seric } 80114900Seric 80214900Seric /* display the input for verbose mode */ 80358120Seric if (Verbose) 80459956Seric nmessage("050 %s", bufp); 8057356Seric 80659285Seric /* process the line */ 80759285Seric if (pfunc != NULL && !firstline) 80859285Seric (*pfunc)(bufp, m, mci, e); 80959285Seric 81059285Seric firstline = FALSE; 81159285Seric 8127356Seric /* if continuation is required, we can go on */ 81359014Seric if (bufp[3] == '-') 8144684Seric continue; 8157356Seric 81659014Seric /* ignore improperly formated input */ 81759014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 81859014Seric continue; 81959014Seric 8207356Seric /* decode the reply code */ 82159014Seric r = atoi(bufp); 8227356Seric 8237356Seric /* extra semantics: 0xx codes are "informational" */ 82459014Seric if (r >= 100) 82559014Seric break; 82659014Seric } 8277356Seric 82859014Seric /* 82959014Seric ** Now look at SmtpReplyBuffer -- only care about the first 83059014Seric ** line of the response from here on out. 83159014Seric */ 83258061Seric 83359014Seric /* save temporary failure messages for posterity */ 83459014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 83559014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 8369391Seric 83759014Seric /* reply code 421 is "Service Shutting Down" */ 83859014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 83959014Seric { 84059014Seric /* send the quit protocol */ 84159014Seric mci->mci_state = MCIS_SSD; 84259014Seric smtpquit(m, mci, e); 8434684Seric } 84459014Seric 84559014Seric return (r); 8464684Seric } 8474796Seric /* 8484865Seric ** SMTPMESSAGE -- send message to server 8494796Seric ** 8504796Seric ** Parameters: 8514796Seric ** f -- format 85210175Seric ** m -- the mailer to control formatting. 8534796Seric ** a, b, c -- parameters 8544796Seric ** 8554796Seric ** Returns: 8564796Seric ** none. 8574796Seric ** 8584796Seric ** Side Effects: 85953740Seric ** writes message to mci->mci_out. 8604796Seric */ 8614796Seric 8624865Seric /*VARARGS1*/ 86357642Seric #ifdef __STDC__ 86457642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 86557642Seric #else 86657642Seric smtpmessage(f, m, mci, va_alist) 8674796Seric char *f; 86810175Seric MAILER *m; 86954967Seric MCI *mci; 87057642Seric va_dcl 87157642Seric #endif 8724796Seric { 87356852Seric VA_LOCAL_DECL 87456852Seric 87557135Seric VA_START(mci); 87656852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 87756852Seric VA_END; 87858680Seric 87958120Seric if (tTd(18, 1) || Verbose) 88058151Seric nmessage(">>> %s", SmtpMsgBuffer); 88163753Seric if (TrafficLogFile != NULL) 88263753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 88353740Seric if (mci->mci_out != NULL) 88458680Seric { 88553740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 88654967Seric m == NULL ? "\r\n" : m->m_eol); 88758680Seric } 88859149Seric else if (tTd(18, 1)) 88958725Seric { 89059149Seric printf("smtpmessage: NULL mci_out\n"); 89158725Seric } 8924796Seric } 8935182Seric 89456795Seric # endif /* SMTP */ 895