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*64718Seric static char sccsid[] = "@(#)usersmtp.c 8.11 (Berkeley) 10/15/93 (with SMTP)"; 1433731Sbostic #else 15*64718Seric static char sccsid[] = "@(#)usersmtp.c 8.11 (Berkeley) 10/15/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 "); 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 */ 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); 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. 16164651Seric ** 16264651Seric ** If this code remains at all, "CheckLoopBack" should be 16364651Seric ** a mailer flag. This is a MAYBENEXTRELEASE feature. 16458957Seric */ 16558957Seric 16659026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16758957Seric if (p != NULL) 16861707Seric *p = '\0'; 16964644Seric if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17058957Seric { 17158957Seric syserr("553 %s config error: mail loops back to myself", 17258957Seric MyHostName); 17358957Seric mci->mci_exitstat = EX_CONFIG; 17458957Seric mci->mci_errno = 0; 17558957Seric smtpquit(m, mci, e); 17658957Seric return; 17758957Seric } 17858957Seric 17958957Seric /* 1809315Seric ** If this is expected to be another sendmail, send some internal 1819315Seric ** commands. 1829315Seric */ 1839315Seric 18410688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1859315Seric { 1869315Seric /* tell it to be verbose */ 18753751Seric smtpmessage("VERB", m, mci); 18859285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1899315Seric if (r < 0) 19052104Seric goto tempfail2; 1919315Seric } 1929315Seric 19353751Seric mci->mci_state = MCIS_OPEN; 19454967Seric return; 19553751Seric 19653751Seric tempfail1: 19753751Seric tempfail2: 19853751Seric mci->mci_exitstat = EX_TEMPFAIL; 19957379Seric if (mci->mci_errno == 0) 20057379Seric mci->mci_errno = errno; 20157379Seric if (mci->mci_state != MCIS_CLOSED) 20257379Seric smtpquit(m, mci, e); 20354967Seric return; 20453751Seric 20553751Seric unavailable: 20653751Seric mci->mci_exitstat = EX_UNAVAILABLE; 20753751Seric mci->mci_errno = errno; 20853751Seric smtpquit(m, mci, e); 20954967Seric return; 21053751Seric } 21159285Seric /* 21260210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 21360210Seric ** 21460210Seric ** 21560210Seric ** Parameters: 21660210Seric ** line -- the response line. 21760210Seric ** m -- the mailer. 21860210Seric ** mci -- the mailer connection info. 21960210Seric ** e -- the envelope. 22060210Seric ** 22160210Seric ** Returns: 22260210Seric ** none. 22360210Seric */ 22460210Seric 22560210Seric void 22660210Seric esmtp_check(line, m, mci, e) 22760210Seric char *line; 22860210Seric MAILER *m; 22960210Seric register MCI *mci; 23060210Seric ENVELOPE *e; 23160210Seric { 23260210Seric if (strlen(line) < 5) 23360210Seric return; 23460210Seric line += 4; 23560210Seric if (strncmp(line, "ESMTP ", 6) == 0) 23660210Seric mci->mci_flags |= MCIF_ESMTP; 23760210Seric } 23860210Seric /* 23959285Seric ** HELO_OPTIONS -- process the options on a HELO line. 24059285Seric ** 24159285Seric ** Parameters: 24259285Seric ** line -- the response line. 24359285Seric ** m -- the mailer. 24459285Seric ** mci -- the mailer connection info. 24559285Seric ** e -- the envelope. 24659285Seric ** 24759285Seric ** Returns: 24859285Seric ** none. 24959285Seric */ 25053751Seric 25159285Seric void 25259285Seric helo_options(line, m, mci, e) 25359285Seric char *line; 25459285Seric MAILER *m; 25559285Seric register MCI *mci; 25659285Seric ENVELOPE *e; 25759285Seric { 25859285Seric register char *p; 25959285Seric 26059285Seric if (strlen(line) < 5) 26159285Seric return; 26259285Seric line += 4; 26359285Seric p = strchr(line, ' '); 26459285Seric if (p != NULL) 26559285Seric *p++ = '\0'; 26659285Seric if (strcasecmp(line, "size") == 0) 26759285Seric { 26859285Seric mci->mci_flags |= MCIF_SIZE; 26959285Seric if (p != NULL) 27059285Seric mci->mci_maxsize = atol(p); 27159285Seric } 27259285Seric else if (strcasecmp(line, "8bitmime") == 0) 27359285Seric mci->mci_flags |= MCIF_8BITMIME; 27459285Seric else if (strcasecmp(line, "expn") == 0) 27559285Seric mci->mci_flags |= MCIF_EXPN; 27659285Seric } 27759285Seric /* 27859285Seric ** SMTPMAILFROM -- send MAIL command 27959285Seric ** 28059285Seric ** Parameters: 28159285Seric ** m -- the mailer. 28259285Seric ** mci -- the mailer connection structure. 28359285Seric ** e -- the envelope (including the sender to specify). 28459285Seric */ 28559285Seric 28653751Seric smtpmailfrom(m, mci, e) 28753751Seric struct mailer *m; 28854967Seric MCI *mci; 28953751Seric ENVELOPE *e; 29053751Seric { 29153751Seric int r; 29253751Seric char buf[MAXNAME]; 29359285Seric char optbuf[MAXLINE]; 29453751Seric 29563753Seric if (tTd(18, 2)) 29657943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 29757943Seric 29859285Seric /* set up appropriate options to include */ 29964254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 30059285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 30159285Seric else 30259285Seric strcpy(optbuf, ""); 30359285Seric 3049315Seric /* 3054865Seric ** Send the MAIL command. 3064865Seric ** Designates the sender. 3074865Seric */ 3084796Seric 30953751Seric mci->mci_state = MCIS_ACTIVE; 31053751Seric 31159540Seric if (bitset(EF_RESPONSE, e->e_flags) && 31259540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 31358680Seric (void) strcpy(buf, ""); 31458680Seric else 31558680Seric expand("\201g", buf, &buf[sizeof buf - 1], e); 31653751Seric if (e->e_from.q_mailer == LocalMailer || 31710688Seric !bitnset(M_FROMPATH, m->m_flags)) 3188436Seric { 31959285Seric smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); 3208436Seric } 3218436Seric else 3228436Seric { 32359285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 32459285Seric buf[0] == '@' ? ',' : ':', buf, optbuf); 3258436Seric } 32661093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 32753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 32859285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 3298005Seric if (r < 0 || REPLYTYPE(r) == 4) 33053751Seric { 33153751Seric mci->mci_exitstat = EX_TEMPFAIL; 33253751Seric mci->mci_errno = errno; 33353751Seric smtpquit(m, mci, e); 33453751Seric return EX_TEMPFAIL; 33553751Seric } 3367963Seric else if (r == 250) 33753751Seric { 33853751Seric mci->mci_exitstat = EX_OK; 33953751Seric return EX_OK; 34053751Seric } 3417963Seric else if (r == 552) 34253751Seric { 34353751Seric /* signal service unavailable */ 34453751Seric mci->mci_exitstat = EX_UNAVAILABLE; 34553751Seric smtpquit(m, mci, e); 34653751Seric return EX_UNAVAILABLE; 34753751Seric } 34814913Seric 34958008Seric #ifdef LOG 35058020Seric if (LogLevel > 1) 35158008Seric { 35258008Seric syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 35358008Seric e->e_id, SmtpReplyBuffer); 35458008Seric } 35558008Seric #endif 35658008Seric 35714913Seric /* protocol error -- close up */ 35853751Seric smtpquit(m, mci, e); 35953751Seric mci->mci_exitstat = EX_PROTOCOL; 36053751Seric return EX_PROTOCOL; 3614684Seric } 3624684Seric /* 3634976Seric ** SMTPRCPT -- designate recipient. 3644797Seric ** 3654797Seric ** Parameters: 3664865Seric ** to -- address of recipient. 36710175Seric ** m -- the mailer we are sending to. 36857379Seric ** mci -- the connection info for this transaction. 36957379Seric ** e -- the envelope for this transaction. 3704797Seric ** 3714797Seric ** Returns: 3724865Seric ** exit status corresponding to recipient status. 3734797Seric ** 3744797Seric ** Side Effects: 3754865Seric ** Sends the mail via SMTP. 3764797Seric */ 3774797Seric 37853751Seric smtprcpt(to, m, mci, e) 3794865Seric ADDRESS *to; 38010175Seric register MAILER *m; 38154967Seric MCI *mci; 38253751Seric ENVELOPE *e; 3834797Seric { 3844797Seric register int r; 3854797Seric 38653751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 3874865Seric 38861093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 38953751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 39059285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 3918005Seric if (r < 0 || REPLYTYPE(r) == 4) 3924865Seric return (EX_TEMPFAIL); 3937963Seric else if (REPLYTYPE(r) == 2) 3947963Seric return (EX_OK); 3957964Seric else if (r == 550 || r == 551 || r == 553) 3967964Seric return (EX_NOUSER); 3977964Seric else if (r == 552 || r == 554) 3987964Seric return (EX_UNAVAILABLE); 39958008Seric 40058008Seric #ifdef LOG 40158020Seric if (LogLevel > 1) 40258008Seric { 40358008Seric syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 40458008Seric e->e_id, SmtpReplyBuffer); 40558008Seric } 40658008Seric #endif 40758008Seric 4087964Seric return (EX_PROTOCOL); 4094797Seric } 4104797Seric /* 41110175Seric ** SMTPDATA -- send the data and clean up the transaction. 4124684Seric ** 4134684Seric ** Parameters: 4144865Seric ** m -- mailer being sent to. 4156980Seric ** e -- the envelope for this message. 4164684Seric ** 4174684Seric ** Returns: 4184976Seric ** exit status corresponding to DATA command. 4194684Seric ** 4204684Seric ** Side Effects: 4214865Seric ** none. 4224684Seric */ 4234684Seric 42463753Seric static jmp_buf CtxDataTimeout; 42563937Seric static int datatimeout(); 42663753Seric 42753740Seric smtpdata(m, mci, e) 4284865Seric struct mailer *m; 42954967Seric register MCI *mci; 4306980Seric register ENVELOPE *e; 4314684Seric { 4324684Seric register int r; 43363753Seric register EVENT *ev; 43463753Seric time_t timeout; 4354684Seric 4364797Seric /* 4374797Seric ** Send the data. 43810175Seric ** First send the command and check that it is ok. 43910175Seric ** Then send the data. 44010175Seric ** Follow it up with a dot to terminate. 44110175Seric ** Finally get the results of the transaction. 4424797Seric */ 4434797Seric 44410175Seric /* send the command and check ok to proceed */ 44553751Seric smtpmessage("DATA", m, mci); 44661093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 44753751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 44859285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 4498005Seric if (r < 0 || REPLYTYPE(r) == 4) 45057990Seric { 45157990Seric smtpquit(m, mci, e); 4524797Seric return (EX_TEMPFAIL); 45357990Seric } 4547963Seric else if (r == 554) 45557990Seric { 45657990Seric smtprset(m, mci, e); 4577963Seric return (EX_UNAVAILABLE); 45857990Seric } 4597963Seric else if (r != 354) 46057990Seric { 46158008Seric #ifdef LOG 46258020Seric if (LogLevel > 1) 46358008Seric { 46458008Seric syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 46558008Seric e->e_id, SmtpReplyBuffer); 46658008Seric } 46758008Seric #endif 46857990Seric smtprset(m, mci, e); 4697964Seric return (EX_PROTOCOL); 47057990Seric } 47110175Seric 47263757Seric /* 47363757Seric ** Set timeout around data writes. Make it at least large 47463757Seric ** enough for DNS timeouts on all recipients plus some fudge 47563757Seric ** factor. The main thing is that it should not be infinite. 47663757Seric */ 47763757Seric 47863753Seric if (setjmp(CtxDataTimeout) != 0) 47963753Seric { 48063753Seric mci->mci_errno = errno; 48163753Seric mci->mci_exitstat = EX_TEMPFAIL; 48263753Seric mci->mci_state = MCIS_ERROR; 48363753Seric syserr("451 timeout writing message to %s", mci->mci_host); 48463753Seric smtpquit(m, mci, e); 48563753Seric return EX_TEMPFAIL; 48663753Seric } 48763753Seric 48863787Seric timeout = e->e_msgsize / 16; 48963787Seric if (timeout < (time_t) 60) 49063787Seric timeout = (time_t) 60; 49163787Seric timeout += e->e_nrcpts * 90; 49263753Seric ev = setevent(timeout, datatimeout, 0); 49363753Seric 49410175Seric /* now output the actual message */ 49553751Seric (*e->e_puthdr)(mci->mci_out, m, e); 49653740Seric putline("\n", mci->mci_out, m); 49759730Seric (*e->e_putbody)(mci->mci_out, m, e, NULL); 49810175Seric 49963753Seric clrevent(ev); 50063753Seric 501*64718Seric if (ferror(mci->mci_out)) 502*64718Seric { 503*64718Seric /* error during processing -- don't send the dot */ 504*64718Seric mci->mci_errno = EIO; 505*64718Seric mci->mci_exitstat = EX_IOERR; 506*64718Seric mci->mci_state = MCIS_ERROR; 507*64718Seric smtpquit(m, mci, e); 508*64718Seric return EX_IOERR; 509*64718Seric } 510*64718Seric 51110175Seric /* terminate the message */ 51253740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 51363753Seric if (TrafficLogFile != NULL) 51463753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 51558120Seric if (Verbose) 51658151Seric nmessage(">>> ."); 51710175Seric 51810175Seric /* check for the results of the transaction */ 51961093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 52053751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 52159285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 52253751Seric if (r < 0) 52357990Seric { 52457990Seric smtpquit(m, mci, e); 5254797Seric return (EX_TEMPFAIL); 52657990Seric } 52753751Seric mci->mci_state = MCIS_OPEN; 52858917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 52953751Seric if (REPLYTYPE(r) == 4) 53053751Seric return (EX_TEMPFAIL); 5317963Seric else if (r == 250) 5327963Seric return (EX_OK); 5337963Seric else if (r == 552 || r == 554) 5347963Seric return (EX_UNAVAILABLE); 53558008Seric #ifdef LOG 53658020Seric if (LogLevel > 1) 53758008Seric { 53858008Seric syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 53958008Seric e->e_id, SmtpReplyBuffer); 54058008Seric } 54158008Seric #endif 5427964Seric return (EX_PROTOCOL); 5434684Seric } 54463753Seric 54563753Seric 54663753Seric static int 54763753Seric datatimeout() 54863753Seric { 54963753Seric longjmp(CtxDataTimeout, 1); 55063753Seric } 5514684Seric /* 5524865Seric ** SMTPQUIT -- close the SMTP connection. 5534865Seric ** 5544865Seric ** Parameters: 55515535Seric ** m -- a pointer to the mailer. 5564865Seric ** 5574865Seric ** Returns: 5584865Seric ** none. 5594865Seric ** 5604865Seric ** Side Effects: 5614865Seric ** sends the final protocol and closes the connection. 5624865Seric */ 5634865Seric 56453751Seric smtpquit(m, mci, e) 56553751Seric register MAILER *m; 56654967Seric register MCI *mci; 56753751Seric ENVELOPE *e; 5684865Seric { 5699391Seric int i; 5704865Seric 57154967Seric /* send the quit message if we haven't gotten I/O error */ 57253751Seric if (mci->mci_state != MCIS_ERROR) 5739391Seric { 57461093Seric SmtpPhase = "client QUIT"; 57553751Seric smtpmessage("QUIT", m, mci); 57659285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 57753740Seric if (mci->mci_state == MCIS_CLOSED) 57810159Seric return; 5799391Seric } 5809391Seric 58152676Seric /* now actually close the connection and pick up the zombie */ 58258846Seric i = endmailer(mci, e, m->m_argv); 5839391Seric if (i != EX_OK) 58458151Seric syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); 5854865Seric } 5864865Seric /* 58754967Seric ** SMTPRSET -- send a RSET (reset) command 58854967Seric */ 58954967Seric 59054967Seric smtprset(m, mci, e) 59154967Seric register MAILER *m; 59254967Seric register MCI *mci; 59354967Seric ENVELOPE *e; 59454967Seric { 59554967Seric int r; 59654967Seric 59761093Seric SmtpPhase = "client RSET"; 59854967Seric smtpmessage("RSET", m, mci); 59959285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 60057734Seric if (r < 0) 60157734Seric mci->mci_state = MCIS_ERROR; 60254967Seric else if (REPLYTYPE(r) == 2) 60357734Seric { 60457734Seric mci->mci_state = MCIS_OPEN; 60557734Seric return; 60657734Seric } 60757734Seric smtpquit(m, mci, e); 60854967Seric } 60954967Seric /* 61058867Seric ** SMTPPROBE -- check the connection state 61154967Seric */ 61254967Seric 61358867Seric smtpprobe(mci) 61454967Seric register MCI *mci; 61554967Seric { 61654967Seric int r; 61754967Seric MAILER *m = mci->mci_mailer; 61854967Seric extern ENVELOPE BlankEnvelope; 61954967Seric ENVELOPE *e = &BlankEnvelope; 62054967Seric 62161093Seric SmtpPhase = "client probe"; 62258867Seric smtpmessage("RSET", m, mci); 62359285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 62458061Seric if (r < 0 || REPLYTYPE(r) != 2) 62554967Seric smtpquit(m, mci, e); 62654967Seric return r; 62754967Seric } 62854967Seric /* 6294684Seric ** REPLY -- read arpanet reply 6304684Seric ** 6314684Seric ** Parameters: 63210175Seric ** m -- the mailer we are reading the reply from. 63357379Seric ** mci -- the mailer connection info structure. 63457379Seric ** e -- the current envelope. 63557379Seric ** timeout -- the timeout for reads. 63659285Seric ** pfunc -- processing function for second and subsequent 63759285Seric ** lines of response -- if null, no special 63859285Seric ** processing is done. 6394684Seric ** 6404684Seric ** Returns: 6414684Seric ** reply code it reads. 6424684Seric ** 6434684Seric ** Side Effects: 6444684Seric ** flushes the mail file. 6454684Seric */ 6464684Seric 64759285Seric reply(m, mci, e, timeout, pfunc) 64853751Seric MAILER *m; 64954967Seric MCI *mci; 65053751Seric ENVELOPE *e; 65159285Seric time_t timeout; 65259285Seric void (*pfunc)(); 6534684Seric { 65459014Seric register char *bufp; 65559014Seric register int r; 65659285Seric bool firstline = TRUE; 65758957Seric char junkbuf[MAXLINE]; 65858957Seric 65957379Seric if (mci->mci_out != NULL) 66057379Seric (void) fflush(mci->mci_out); 6614684Seric 6627677Seric if (tTd(18, 1)) 6634796Seric printf("reply\n"); 6644796Seric 6657356Seric /* 6667356Seric ** Read the input line, being careful not to hang. 6677356Seric */ 6687356Seric 66959014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 6704684Seric { 6717356Seric register char *p; 67253751Seric extern time_t curtime(); 6734684Seric 6747685Seric /* actually do the read */ 67553751Seric if (e->e_xfp != NULL) 67653751Seric (void) fflush(e->e_xfp); /* for debugging */ 6777356Seric 67810054Seric /* if we are in the process of closing just give the code */ 67953740Seric if (mci->mci_state == MCIS_CLOSED) 68010054Seric return (SMTPCLOSING); 68110054Seric 68258680Seric if (mci->mci_out != NULL) 68358680Seric fflush(mci->mci_out); 68458680Seric 68510054Seric /* get the line from the other side */ 68661093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 68753751Seric mci->mci_lastuse = curtime(); 68853751Seric 68910054Seric if (p == NULL) 69010131Seric { 69163753Seric bool oldholderrs; 69210148Seric extern char MsgBuf[]; /* err.c */ 69310148Seric 69421065Seric /* if the remote end closed early, fake an error */ 69521065Seric if (errno == 0) 69621065Seric # ifdef ECONNRESET 69721065Seric errno = ECONNRESET; 69856795Seric # else /* ECONNRESET */ 69921065Seric errno = EPIPE; 70056795Seric # endif /* ECONNRESET */ 70121065Seric 70257379Seric mci->mci_errno = errno; 70357642Seric mci->mci_exitstat = EX_TEMPFAIL; 70463753Seric oldholderrs = HoldErrs; 70563753Seric HoldErrs = TRUE; 70663753Seric usrerr("451 reply: read error from %s", mci->mci_host); 70763753Seric 70810420Seric /* if debugging, pause so we can see state */ 70910420Seric if (tTd(18, 100)) 71010420Seric pause(); 71154967Seric mci->mci_state = MCIS_ERROR; 71253751Seric smtpquit(m, mci, e); 71363753Seric #ifdef XDEBUG 71463753Seric { 71563753Seric char wbuf[MAXLINE]; 71664082Seric char *p = wbuf; 71764082Seric if (e->e_to != NULL) 71864082Seric { 71964082Seric sprintf(p, "%s... ", e->e_to); 72064082Seric p += strlen(p); 72164082Seric } 72264082Seric sprintf(p, "reply(%s) during %s", 72364082Seric mci->mci_host, SmtpPhase); 72463753Seric checkfd012(wbuf); 72563753Seric } 72663753Seric #endif 72763753Seric HoldErrs = oldholderrs; 72810054Seric return (-1); 72910131Seric } 73058957Seric fixcrlf(bufp, TRUE); 73110054Seric 73263753Seric /* EHLO failure is not a real error */ 73363753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 73463753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 73514900Seric { 73614900Seric /* serious error -- log the previous command */ 73764071Seric if (SmtpNeedIntro) 73864071Seric { 73964071Seric /* inform user who we are chatting with */ 74064071Seric fprintf(CurEnv->e_xfp, 74164071Seric "... while talking to %s:\n", 74264071Seric CurHostName); 74364071Seric SmtpNeedIntro = FALSE; 74464071Seric } 74559014Seric if (SmtpMsgBuffer[0] != '\0') 74659014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 74759014Seric SmtpMsgBuffer[0] = '\0'; 74814900Seric 74914900Seric /* now log the message as from the other side */ 75058957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 75114900Seric } 75214900Seric 75314900Seric /* display the input for verbose mode */ 75458120Seric if (Verbose) 75559956Seric nmessage("050 %s", bufp); 7567356Seric 75759285Seric /* process the line */ 75859285Seric if (pfunc != NULL && !firstline) 75959285Seric (*pfunc)(bufp, m, mci, e); 76059285Seric 76159285Seric firstline = FALSE; 76259285Seric 7637356Seric /* if continuation is required, we can go on */ 76459014Seric if (bufp[3] == '-') 7654684Seric continue; 7667356Seric 76759014Seric /* ignore improperly formated input */ 76859014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 76959014Seric continue; 77059014Seric 7717356Seric /* decode the reply code */ 77259014Seric r = atoi(bufp); 7737356Seric 7747356Seric /* extra semantics: 0xx codes are "informational" */ 77559014Seric if (r >= 100) 77659014Seric break; 77759014Seric } 7787356Seric 77959014Seric /* 78059014Seric ** Now look at SmtpReplyBuffer -- only care about the first 78159014Seric ** line of the response from here on out. 78259014Seric */ 78358061Seric 78459014Seric /* save temporary failure messages for posterity */ 78559014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 78659014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 7879391Seric 78859014Seric /* reply code 421 is "Service Shutting Down" */ 78959014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 79059014Seric { 79159014Seric /* send the quit protocol */ 79259014Seric mci->mci_state = MCIS_SSD; 79359014Seric smtpquit(m, mci, e); 7944684Seric } 79559014Seric 79659014Seric return (r); 7974684Seric } 7984796Seric /* 7994865Seric ** SMTPMESSAGE -- send message to server 8004796Seric ** 8014796Seric ** Parameters: 8024796Seric ** f -- format 80310175Seric ** m -- the mailer to control formatting. 8044796Seric ** a, b, c -- parameters 8054796Seric ** 8064796Seric ** Returns: 8074796Seric ** none. 8084796Seric ** 8094796Seric ** Side Effects: 81053740Seric ** writes message to mci->mci_out. 8114796Seric */ 8124796Seric 8134865Seric /*VARARGS1*/ 81457642Seric #ifdef __STDC__ 81557642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 81657642Seric #else 81757642Seric smtpmessage(f, m, mci, va_alist) 8184796Seric char *f; 81910175Seric MAILER *m; 82054967Seric MCI *mci; 82157642Seric va_dcl 82257642Seric #endif 8234796Seric { 82456852Seric VA_LOCAL_DECL 82556852Seric 82657135Seric VA_START(mci); 82756852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 82856852Seric VA_END; 82958680Seric 83058120Seric if (tTd(18, 1) || Verbose) 83158151Seric nmessage(">>> %s", SmtpMsgBuffer); 83263753Seric if (TrafficLogFile != NULL) 83363753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 83453740Seric if (mci->mci_out != NULL) 83558680Seric { 83653740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 83754967Seric m == NULL ? "\r\n" : m->m_eol); 83858680Seric } 83959149Seric else if (tTd(18, 1)) 84058725Seric { 84159149Seric printf("smtpmessage: NULL mci_out\n"); 84258725Seric } 8434796Seric } 8445182Seric 84556795Seric # endif /* SMTP */ 846