122716Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 642831Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822716Sdist 933731Sbostic # include "sendmail.h" 1022716Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*57734Seric static char sccsid[] = "@(#)usersmtp.c 6.4 (Berkeley) 01/28/93 (with SMTP)"; 1433731Sbostic #else 15*57734Seric static char sccsid[] = "@(#)usersmtp.c 6.4 (Berkeley) 01/28/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 */ 389391Seric /* 394865Seric ** SMTPINIT -- initialize SMTP. 404684Seric ** 414865Seric ** Opens the connection and sends the initial protocol. 424684Seric ** 434684Seric ** Parameters: 444865Seric ** m -- mailer to create connection to. 454865Seric ** pvp -- pointer to parameter vector to pass to 464865Seric ** the mailer. 474684Seric ** 484684Seric ** Returns: 4954967Seric ** none. 504684Seric ** 514684Seric ** Side Effects: 524865Seric ** creates connection and sends initial protocol. 534684Seric */ 544684Seric 5554967Seric smtpinit(m, mci, e) 564865Seric struct mailer *m; 5754967Seric register MCI *mci; 5853751Seric ENVELOPE *e; 594684Seric { 604865Seric register int r; 6114886Seric EVENT *gte; 6252107Seric extern STAB *stab(); 6354967Seric extern MCI *openmailer(); 644684Seric 6557379Seric if (tTd(17, 1)) 6657379Seric { 6757379Seric printf("smtpinit "); 6857379Seric mci_dump(mci); 6957379Seric } 7057379Seric 714865Seric /* 724865Seric ** Open the connection to the mailer. 734865Seric */ 744684Seric 7521065Seric SmtpError[0] = '\0'; 7657379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 7754967Seric switch (mci->mci_state) 786051Seric { 7954967Seric case MCIS_ACTIVE: 8054967Seric /* need to clear old information */ 8154967Seric smtprset(m, mci, e); 82*57734Seric /* fall through */ 8315139Seric 8454967Seric case MCIS_OPEN: 8554967Seric return; 8654967Seric 8754967Seric case MCIS_ERROR: 8854967Seric case MCIS_SSD: 8954967Seric /* shouldn't happen */ 9054967Seric smtpquit(m, mci, e); 91*57734Seric /* fall through */ 9254967Seric 9354967Seric case MCIS_CLOSED: 9454967Seric syserr("smtpinit: state CLOSED"); 9554967Seric return; 9654967Seric 9754967Seric case MCIS_OPENING: 9854967Seric break; 996051Seric } 1004796Seric 10157379Seric SmtpPhase = mci->mci_phase = "user open"; 10254967Seric mci->mci_state = MCIS_OPENING; 10354967Seric 1044865Seric /* 1054865Seric ** Get the greeting message. 10614913Seric ** This should appear spontaneously. Give it five minutes to 10714886Seric ** happen. 1084865Seric */ 1094797Seric 11057379Seric SmtpPhase = mci->mci_phase = "greeting wait"; 11153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11257379Seric r = reply(m, mci, e, (time_t) 300); 1138005Seric if (r < 0 || REPLYTYPE(r) != 2) 11452104Seric goto tempfail1; 1154684Seric 1164865Seric /* 1174976Seric ** Send the HELO command. 1187963Seric ** My mother taught me to always introduce myself. 1194976Seric */ 1204976Seric 12153751Seric smtpmessage("HELO %s", m, mci, MyHostName); 12257379Seric SmtpPhase = mci->mci_phase = "HELO wait"; 12353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 12457379Seric r = reply(m, mci, e, ReadTimeout); 1258005Seric if (r < 0) 12652104Seric goto tempfail1; 1278005Seric else if (REPLYTYPE(r) == 5) 12814913Seric goto unavailable; 1297963Seric else if (REPLYTYPE(r) != 2) 13052104Seric goto tempfail1; 1314976Seric 1324976Seric /* 1339315Seric ** If this is expected to be another sendmail, send some internal 1349315Seric ** commands. 1359315Seric */ 1369315Seric 13710688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1389315Seric { 1399315Seric /* tell it to be verbose */ 14053751Seric smtpmessage("VERB", m, mci); 14157379Seric r = reply(m, mci, e, ReadTimeout); 1429315Seric if (r < 0) 14352104Seric goto tempfail2; 1449315Seric } 1459315Seric 14653751Seric mci->mci_state = MCIS_OPEN; 14754967Seric return; 14853751Seric 14953751Seric tempfail1: 15053751Seric tempfail2: 15153751Seric mci->mci_exitstat = EX_TEMPFAIL; 15257379Seric if (mci->mci_errno == 0) 15357379Seric mci->mci_errno = errno; 15457379Seric if (mci->mci_state != MCIS_CLOSED) 15557379Seric smtpquit(m, mci, e); 15654967Seric return; 15753751Seric 15853751Seric unavailable: 15953751Seric mci->mci_exitstat = EX_UNAVAILABLE; 16053751Seric mci->mci_errno = errno; 16153751Seric smtpquit(m, mci, e); 16254967Seric return; 16353751Seric } 16453751Seric 16553751Seric smtpmailfrom(m, mci, e) 16653751Seric struct mailer *m; 16754967Seric MCI *mci; 16853751Seric ENVELOPE *e; 16953751Seric { 17053751Seric int r; 17153751Seric char buf[MAXNAME]; 17253751Seric 1739315Seric /* 1744865Seric ** Send the MAIL command. 1754865Seric ** Designates the sender. 1764865Seric */ 1774796Seric 17853751Seric mci->mci_state = MCIS_ACTIVE; 17953751Seric 18053751Seric expand("\001<", buf, &buf[sizeof buf - 1], e); 18153751Seric if (e->e_from.q_mailer == LocalMailer || 18210688Seric !bitnset(M_FROMPATH, m->m_flags)) 1838436Seric { 18453751Seric smtpmessage("MAIL From:<%s>", m, mci, buf); 1858436Seric } 1868436Seric else 1878436Seric { 18853751Seric smtpmessage("MAIL From:<@%s%c%s>", m, mci, MyHostName, 18910308Seric buf[0] == '@' ? ',' : ':', buf); 1908436Seric } 19157379Seric SmtpPhase = mci->mci_phase = "MAIL wait"; 19253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 19357379Seric r = reply(m, mci, e, ReadTimeout); 1948005Seric if (r < 0 || REPLYTYPE(r) == 4) 19553751Seric { 19653751Seric mci->mci_exitstat = EX_TEMPFAIL; 19753751Seric mci->mci_errno = errno; 19853751Seric smtpquit(m, mci, e); 19953751Seric return EX_TEMPFAIL; 20053751Seric } 2017963Seric else if (r == 250) 20253751Seric { 20353751Seric mci->mci_exitstat = EX_OK; 20453751Seric return EX_OK; 20553751Seric } 2067963Seric else if (r == 552) 20753751Seric { 20853751Seric /* signal service unavailable */ 20953751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21053751Seric smtpquit(m, mci, e); 21153751Seric return EX_UNAVAILABLE; 21253751Seric } 21314913Seric 21414913Seric /* protocol error -- close up */ 21553751Seric smtpquit(m, mci, e); 21653751Seric mci->mci_exitstat = EX_PROTOCOL; 21753751Seric return EX_PROTOCOL; 2184684Seric } 2194684Seric /* 2204976Seric ** SMTPRCPT -- designate recipient. 2214797Seric ** 2224797Seric ** Parameters: 2234865Seric ** to -- address of recipient. 22410175Seric ** m -- the mailer we are sending to. 22557379Seric ** mci -- the connection info for this transaction. 22657379Seric ** e -- the envelope for this transaction. 2274797Seric ** 2284797Seric ** Returns: 2294865Seric ** exit status corresponding to recipient status. 2304797Seric ** 2314797Seric ** Side Effects: 2324865Seric ** Sends the mail via SMTP. 2334797Seric */ 2344797Seric 23553751Seric smtprcpt(to, m, mci, e) 2364865Seric ADDRESS *to; 23710175Seric register MAILER *m; 23854967Seric MCI *mci; 23953751Seric ENVELOPE *e; 2404797Seric { 2414797Seric register int r; 2424797Seric 24353751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 2444865Seric 24557379Seric SmtpPhase = mci->mci_phase = "RCPT wait"; 24653751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 24757379Seric r = reply(m, mci, e, ReadTimeout); 2488005Seric if (r < 0 || REPLYTYPE(r) == 4) 2494865Seric return (EX_TEMPFAIL); 2507963Seric else if (REPLYTYPE(r) == 2) 2517963Seric return (EX_OK); 2527964Seric else if (r == 550 || r == 551 || r == 553) 2537964Seric return (EX_NOUSER); 2547964Seric else if (r == 552 || r == 554) 2557964Seric return (EX_UNAVAILABLE); 2567964Seric return (EX_PROTOCOL); 2574797Seric } 2584797Seric /* 25910175Seric ** SMTPDATA -- send the data and clean up the transaction. 2604684Seric ** 2614684Seric ** Parameters: 2624865Seric ** m -- mailer being sent to. 2636980Seric ** e -- the envelope for this message. 2644684Seric ** 2654684Seric ** Returns: 2664976Seric ** exit status corresponding to DATA command. 2674684Seric ** 2684684Seric ** Side Effects: 2694865Seric ** none. 2704684Seric */ 2714684Seric 27253740Seric smtpdata(m, mci, e) 2734865Seric struct mailer *m; 27454967Seric register MCI *mci; 2756980Seric register ENVELOPE *e; 2764684Seric { 2774684Seric register int r; 2784684Seric 2794797Seric /* 2804797Seric ** Send the data. 28110175Seric ** First send the command and check that it is ok. 28210175Seric ** Then send the data. 28310175Seric ** Follow it up with a dot to terminate. 28410175Seric ** Finally get the results of the transaction. 2854797Seric */ 2864797Seric 28710175Seric /* send the command and check ok to proceed */ 28853751Seric smtpmessage("DATA", m, mci); 28957379Seric SmtpPhase = mci->mci_phase = "DATA wait"; 29053751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 29157379Seric r = reply(m, mci, e, ReadTimeout); 2928005Seric if (r < 0 || REPLYTYPE(r) == 4) 2934797Seric return (EX_TEMPFAIL); 2947963Seric else if (r == 554) 2957963Seric return (EX_UNAVAILABLE); 2967963Seric else if (r != 354) 2977964Seric return (EX_PROTOCOL); 29810175Seric 29910175Seric /* now output the actual message */ 30053751Seric (*e->e_puthdr)(mci->mci_out, m, e); 30153740Seric putline("\n", mci->mci_out, m); 30253751Seric (*e->e_putbody)(mci->mci_out, m, e); 30310175Seric 30410175Seric /* terminate the message */ 30553740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 30610215Seric if (Verbose && !HoldErrs) 30710215Seric nmessage(Arpa_Info, ">>> ."); 30810175Seric 30910175Seric /* check for the results of the transaction */ 31057379Seric SmtpPhase = mci->mci_phase = "result wait"; 31153751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 31257379Seric r = reply(m, mci, e, ReadTimeout); 31353751Seric if (r < 0) 3144797Seric return (EX_TEMPFAIL); 31553751Seric mci->mci_state = MCIS_OPEN; 31653751Seric if (REPLYTYPE(r) == 4) 31753751Seric return (EX_TEMPFAIL); 3187963Seric else if (r == 250) 3197963Seric return (EX_OK); 3207963Seric else if (r == 552 || r == 554) 3217963Seric return (EX_UNAVAILABLE); 3227964Seric return (EX_PROTOCOL); 3234684Seric } 3244684Seric /* 3254865Seric ** SMTPQUIT -- close the SMTP connection. 3264865Seric ** 3274865Seric ** Parameters: 32815535Seric ** m -- a pointer to the mailer. 3294865Seric ** 3304865Seric ** Returns: 3314865Seric ** none. 3324865Seric ** 3334865Seric ** Side Effects: 3344865Seric ** sends the final protocol and closes the connection. 3354865Seric */ 3364865Seric 33753751Seric smtpquit(m, mci, e) 33853751Seric register MAILER *m; 33954967Seric register MCI *mci; 34053751Seric ENVELOPE *e; 3414865Seric { 3429391Seric int i; 3434865Seric 34454967Seric /* send the quit message if we haven't gotten I/O error */ 34553751Seric if (mci->mci_state != MCIS_ERROR) 3469391Seric { 34753751Seric smtpmessage("QUIT", m, mci); 34857379Seric (void) reply(m, mci, e, ReadTimeout); 34953740Seric if (mci->mci_state == MCIS_CLOSED) 35010159Seric return; 3519391Seric } 3529391Seric 35352676Seric /* now actually close the connection and pick up the zombie */ 35452676Seric i = endmailer(mci, m->m_argv[0]); 3559391Seric if (i != EX_OK) 35615535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3574865Seric } 3584865Seric /* 35954967Seric ** SMTPRSET -- send a RSET (reset) command 36054967Seric */ 36154967Seric 36254967Seric smtprset(m, mci, e) 36354967Seric register MAILER *m; 36454967Seric register MCI *mci; 36554967Seric ENVELOPE *e; 36654967Seric { 36754967Seric int r; 36854967Seric 36954967Seric smtpmessage("RSET", m, mci); 370*57734Seric r = reply(m, mci, e, (time_t) 300); 371*57734Seric if (r < 0) 372*57734Seric mci->mci_state = MCIS_ERROR; 37354967Seric else if (REPLYTYPE(r) == 2) 374*57734Seric { 375*57734Seric mci->mci_state = MCIS_OPEN; 376*57734Seric return; 377*57734Seric } 378*57734Seric smtpquit(m, mci, e); 37954967Seric } 38054967Seric /* 38154967Seric ** SMTPNOOP -- send a NOOP (no operation) command to check the connection state 38254967Seric */ 38354967Seric 38454967Seric smtpnoop(mci) 38554967Seric register MCI *mci; 38654967Seric { 38754967Seric int r; 38854967Seric MAILER *m = mci->mci_mailer; 38954967Seric extern ENVELOPE BlankEnvelope; 39054967Seric ENVELOPE *e = &BlankEnvelope; 39154967Seric 39254967Seric smtpmessage("NOOP", m, mci); 39357379Seric r = reply(m, mci, e, ReadTimeout); 39454967Seric if (REPLYTYPE(r) != 2) 39554967Seric smtpquit(m, mci, e); 39654967Seric return r; 39754967Seric } 39854967Seric /* 3994684Seric ** REPLY -- read arpanet reply 4004684Seric ** 4014684Seric ** Parameters: 40210175Seric ** m -- the mailer we are reading the reply from. 40357379Seric ** mci -- the mailer connection info structure. 40457379Seric ** e -- the current envelope. 40557379Seric ** timeout -- the timeout for reads. 4064684Seric ** 4074684Seric ** Returns: 4084684Seric ** reply code it reads. 4094684Seric ** 4104684Seric ** Side Effects: 4114684Seric ** flushes the mail file. 4124684Seric */ 4134684Seric 41457379Seric reply(m, mci, e, timeout) 41553751Seric MAILER *m; 41654967Seric MCI *mci; 41753751Seric ENVELOPE *e; 4184684Seric { 41957379Seric if (mci->mci_out != NULL) 42057379Seric (void) fflush(mci->mci_out); 4214684Seric 4227677Seric if (tTd(18, 1)) 4234796Seric printf("reply\n"); 4244796Seric 4257356Seric /* 4267356Seric ** Read the input line, being careful not to hang. 4277356Seric */ 4287356Seric 4294684Seric for (;;) 4304684Seric { 4314684Seric register int r; 4327356Seric register char *p; 43353751Seric extern time_t curtime(); 4344684Seric 4357685Seric /* actually do the read */ 43653751Seric if (e->e_xfp != NULL) 43753751Seric (void) fflush(e->e_xfp); /* for debugging */ 4387356Seric 43910054Seric /* if we are in the process of closing just give the code */ 44053740Seric if (mci->mci_state == MCIS_CLOSED) 44110054Seric return (SMTPCLOSING); 44210054Seric 44310054Seric /* get the line from the other side */ 44457379Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in, 44557379Seric timeout); 44653751Seric mci->mci_lastuse = curtime(); 44753751Seric 44810054Seric if (p == NULL) 44910131Seric { 45010148Seric extern char MsgBuf[]; /* err.c */ 45110148Seric extern char Arpa_TSyserr[]; /* conf.c */ 45210148Seric 45321065Seric /* if the remote end closed early, fake an error */ 45421065Seric if (errno == 0) 45521065Seric # ifdef ECONNRESET 45621065Seric errno = ECONNRESET; 45756795Seric # else /* ECONNRESET */ 45821065Seric errno = EPIPE; 45956795Seric # endif /* ECONNRESET */ 46021065Seric 46157379Seric mci->mci_errno = errno; 46257642Seric mci->mci_exitstat = EX_TEMPFAIL; 46357642Seric message(Arpa_TSyserr, "%s: reply: read error from %s", 46457642Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 46557203Seric mci->mci_host); 46610420Seric /* if debugging, pause so we can see state */ 46710420Seric if (tTd(18, 100)) 46810420Seric pause(); 46910148Seric # ifdef LOG 47057203Seric if (LogLevel > 0) 47157203Seric syslog(LOG_INFO, "%s", &MsgBuf[4]); 47256795Seric # endif /* LOG */ 47354967Seric mci->mci_state = MCIS_ERROR; 47453751Seric smtpquit(m, mci, e); 47510054Seric return (-1); 47610131Seric } 47710054Seric fixcrlf(SmtpReplyBuffer, TRUE); 47810054Seric 47956795Seric if (e->e_xfp != NULL && strchr("45", SmtpReplyBuffer[0]) != NULL) 48014900Seric { 48114900Seric /* serious error -- log the previous command */ 48214900Seric if (SmtpMsgBuffer[0] != '\0') 48353751Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 48414900Seric SmtpMsgBuffer[0] = '\0'; 48514900Seric 48614900Seric /* now log the message as from the other side */ 48753751Seric fprintf(e->e_xfp, "<<< %s\n", SmtpReplyBuffer); 48814900Seric } 48914900Seric 49014900Seric /* display the input for verbose mode */ 4917229Seric if (Verbose && !HoldErrs) 4929391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4937356Seric 4947356Seric /* if continuation is required, we can go on */ 4959391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4964684Seric continue; 4977356Seric 4987356Seric /* decode the reply code */ 4999391Seric r = atoi(SmtpReplyBuffer); 5007356Seric 5017356Seric /* extra semantics: 0xx codes are "informational" */ 5024684Seric if (r < 100) 5034684Seric continue; 5047356Seric 5059391Seric /* reply code 421 is "Service Shutting Down" */ 50653740Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 5079391Seric { 50810054Seric /* send the quit protocol */ 50953740Seric mci->mci_state = MCIS_SSD; 51053751Seric smtpquit(m, mci, e); 5119391Seric } 5129391Seric 51321065Seric /* save temporary failure messages for posterity */ 51421065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 51521065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 51621065Seric 5174684Seric return (r); 5184684Seric } 5194684Seric } 5204796Seric /* 5214865Seric ** SMTPMESSAGE -- send message to server 5224796Seric ** 5234796Seric ** Parameters: 5244796Seric ** f -- format 52510175Seric ** m -- the mailer to control formatting. 5264796Seric ** a, b, c -- parameters 5274796Seric ** 5284796Seric ** Returns: 5294796Seric ** none. 5304796Seric ** 5314796Seric ** Side Effects: 53253740Seric ** writes message to mci->mci_out. 5334796Seric */ 5344796Seric 5354865Seric /*VARARGS1*/ 53657642Seric #ifdef __STDC__ 53757642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 53857642Seric #else 53957642Seric smtpmessage(f, m, mci, va_alist) 5404796Seric char *f; 54110175Seric MAILER *m; 54254967Seric MCI *mci; 54357642Seric va_dcl 54457642Seric #endif 5454796Seric { 54656852Seric VA_LOCAL_DECL 54756852Seric 54857135Seric VA_START(mci); 54956852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 55056852Seric VA_END; 5517677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 55214900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 55353740Seric if (mci->mci_out != NULL) 55453740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 55554967Seric m == NULL ? "\r\n" : m->m_eol); 5564796Seric } 5575182Seric 55856795Seric # endif /* SMTP */ 559