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*53740Seric static char sccsid[] = "@(#)usersmtp.c 5.20.1.2 (Berkeley) 05/29/92 (with SMTP)"; 1433731Sbostic #else 15*53740Seric static char sccsid[] = "@(#)usersmtp.c 5.20.1.2 (Berkeley) 05/29/92 (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: 494865Seric ** appropriate exit status -- EX_OK on success. 5014913Seric ** If not EX_OK, it should close the connection. 514684Seric ** 524684Seric ** Side Effects: 534865Seric ** creates connection and sends initial protocol. 544684Seric */ 554684Seric 5614886Seric jmp_buf CtxGreeting; 5714886Seric 58*53740Seric smtpinit(m, mci, pvp) 594865Seric struct mailer *m; 60*53740Seric MCONINFO *mci; 614865Seric char **pvp; 624684Seric { 634865Seric register int r; 6414886Seric EVENT *gte; 6552107Seric register STAB *st; 664865Seric char buf[MAXNAME]; 6746928Sbostic static int greettimeout(); 6852107Seric extern STAB *stab(); 69*53740Seric extern MCONINFO *openmailer(); 704684Seric 714865Seric /* 724865Seric ** Open the connection to the mailer. 734865Seric */ 744684Seric 75*53740Seric if (mci->mci_state == MCIS_OPEN) 7611159Seric syserr("smtpinit: already open"); 7711159Seric 78*53740Seric mci->mci_state = MCIS_CLOSED; 7921065Seric SmtpError[0] = '\0'; 8024944Seric SmtpPhase = "user open"; 8136584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); 8252676Seric mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE); 836051Seric if (SmtpPid < 0) 846051Seric { 857677Seric if (tTd(18, 1)) 869391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 879391Seric pvp[0], ExitStat, errno); 8815139Seric if (CurEnv->e_xfp != NULL) 8915139Seric { 9021065Seric register char *p; 9115139Seric extern char *errstring(); 9221065Seric extern char *statstring(); 9315139Seric 9421065Seric if (errno == 0) 9521065Seric { 9621065Seric p = statstring(ExitStat); 9721065Seric fprintf(CurEnv->e_xfp, 9821065Seric "%.3s %s.%s... %s\n", 9921065Seric p, pvp[1], m->m_name, p); 10021065Seric } 10121065Seric else 10221065Seric { 10336584Sbostic r = errno; 10421065Seric fprintf(CurEnv->e_xfp, 10521065Seric "421 %s.%s... Deferred: %s\n", 10621065Seric pvp[1], m->m_name, errstring(errno)); 10736584Sbostic errno = r; 10821065Seric } 10915139Seric } 1106051Seric return (ExitStat); 1116051Seric } 112*53740Seric mci->mci_state = MCIS_OPEN; 1134796Seric 1144865Seric /* 1154865Seric ** Get the greeting message. 11614913Seric ** This should appear spontaneously. Give it five minutes to 11714886Seric ** happen. 1184865Seric */ 1194797Seric 12014886Seric if (setjmp(CtxGreeting) != 0) 12152104Seric goto tempfail1; 12216141Seric gte = setevent((time_t) 300, greettimeout, 0); 12324944Seric SmtpPhase = "greeting wait"; 12436584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 125*53740Seric r = reply(m, mci); 12614886Seric clrevent(gte); 1278005Seric if (r < 0 || REPLYTYPE(r) != 2) 12852104Seric goto tempfail1; 1294684Seric 1304865Seric /* 1314976Seric ** Send the HELO command. 1327963Seric ** My mother taught me to always introduce myself. 1334976Seric */ 1344976Seric 13525050Seric smtpmessage("HELO %s", m, MyHostName); 13624944Seric SmtpPhase = "HELO wait"; 13736584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 138*53740Seric r = reply(m, mci); 1398005Seric if (r < 0) 14052104Seric goto tempfail1; 1418005Seric else if (REPLYTYPE(r) == 5) 14214913Seric goto unavailable; 1437963Seric else if (REPLYTYPE(r) != 2) 14452104Seric goto tempfail1; 1454976Seric 1464976Seric /* 1479315Seric ** If this is expected to be another sendmail, send some internal 1489315Seric ** commands. 1499315Seric */ 1509315Seric 15110688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1529315Seric { 1539315Seric /* tell it to be verbose */ 15410175Seric smtpmessage("VERB", m); 155*53740Seric r = reply(m, mci); 1569315Seric if (r < 0) 15752104Seric goto tempfail2; 1589315Seric 1599315Seric /* tell it we will be sending one transaction only */ 16010175Seric smtpmessage("ONEX", m); 161*53740Seric r = reply(m, mci); 1629315Seric if (r < 0) 16352104Seric goto tempfail2; 1649315Seric } 1659315Seric 1669315Seric /* 1674865Seric ** Send the MAIL command. 1684865Seric ** Designates the sender. 1694865Seric */ 1704796Seric 17151951Seric expand("\001<", buf, &buf[sizeof buf - 1], CurEnv); 1728436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 17310688Seric !bitnset(M_FROMPATH, m->m_flags)) 1748436Seric { 17510308Seric smtpmessage("MAIL From:<%s>", m, buf); 1768436Seric } 1778436Seric else 1788436Seric { 17925050Seric smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, 18010308Seric buf[0] == '@' ? ',' : ':', buf); 1818436Seric } 18224944Seric SmtpPhase = "MAIL wait"; 18336584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 184*53740Seric r = reply(m, mci); 1858005Seric if (r < 0 || REPLYTYPE(r) == 4) 18652104Seric goto tempfail2; 1877963Seric else if (r == 250) 1887963Seric return (EX_OK); 1897963Seric else if (r == 552) 19014913Seric goto unavailable; 19114913Seric 19214913Seric /* protocol error -- close up */ 193*53740Seric smtpquit(m, mci); 1947964Seric return (EX_PROTOCOL); 19514913Seric 19652104Seric tempfail1: 19752107Seric /* log this as an error to avoid sure-to-be-void connections */ 198*53740Seric mci->mci_exitstat = EX_TEMPFAIL; 199*53740Seric mci->mci_errno = errno; 20052104Seric 20152104Seric tempfail2: 20252107Seric /* signal a temporary failure */ 203*53740Seric smtpquit(m, mci); 20414913Seric return (EX_TEMPFAIL); 20514913Seric 20652107Seric unavailable: 20714913Seric /* signal service unavailable */ 208*53740Seric smtpquit(m, mci); 20914913Seric return (EX_UNAVAILABLE); 2104684Seric } 21114886Seric 21214886Seric 21314886Seric static 21414886Seric greettimeout() 21514886Seric { 21614886Seric /* timeout reading the greeting message */ 21714886Seric longjmp(CtxGreeting, 1); 21814886Seric } 2194684Seric /* 2204976Seric ** SMTPRCPT -- designate recipient. 2214797Seric ** 2224797Seric ** Parameters: 2234865Seric ** to -- address of recipient. 22410175Seric ** m -- the mailer we are sending to. 2254797Seric ** 2264797Seric ** Returns: 2274865Seric ** exit status corresponding to recipient status. 2284797Seric ** 2294797Seric ** Side Effects: 2304865Seric ** Sends the mail via SMTP. 2314797Seric */ 2324797Seric 233*53740Seric smtprcpt(to, m, mci) 2344865Seric ADDRESS *to; 23510175Seric register MAILER *m; 236*53740Seric MCONINFO *mci; 2374797Seric { 2384797Seric register int r; 23910308Seric extern char *remotename(); 2404797Seric 24152647Seric smtpmessage("RCPT To:<%s>", m, to->q_user); 2424865Seric 24324944Seric SmtpPhase = "RCPT wait"; 24436584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 245*53740Seric r = reply(m, mci); 2468005Seric if (r < 0 || REPLYTYPE(r) == 4) 2474865Seric return (EX_TEMPFAIL); 2487963Seric else if (REPLYTYPE(r) == 2) 2497963Seric return (EX_OK); 2507964Seric else if (r == 550 || r == 551 || r == 553) 2517964Seric return (EX_NOUSER); 2527964Seric else if (r == 552 || r == 554) 2537964Seric return (EX_UNAVAILABLE); 2547964Seric return (EX_PROTOCOL); 2554797Seric } 2564797Seric /* 25710175Seric ** SMTPDATA -- send the data and clean up the transaction. 2584684Seric ** 2594684Seric ** Parameters: 2604865Seric ** m -- mailer being sent to. 2616980Seric ** e -- the envelope for this message. 2624684Seric ** 2634684Seric ** Returns: 2644976Seric ** exit status corresponding to DATA command. 2654684Seric ** 2664684Seric ** Side Effects: 2674865Seric ** none. 2684684Seric */ 2694684Seric 270*53740Seric smtpdata(m, mci, e) 2714865Seric struct mailer *m; 272*53740Seric register MCONINFO *mci; 2736980Seric register ENVELOPE *e; 2744684Seric { 2754684Seric register int r; 2764684Seric 2774797Seric /* 2784797Seric ** Send the data. 27910175Seric ** First send the command and check that it is ok. 28010175Seric ** Then send the data. 28110175Seric ** Follow it up with a dot to terminate. 28210175Seric ** Finally get the results of the transaction. 2834797Seric */ 2844797Seric 28510175Seric /* send the command and check ok to proceed */ 28610175Seric smtpmessage("DATA", m); 28724944Seric SmtpPhase = "DATA wait"; 28836584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 289*53740Seric r = reply(m, mci); 2908005Seric if (r < 0 || REPLYTYPE(r) == 4) 2914797Seric return (EX_TEMPFAIL); 2927963Seric else if (r == 554) 2937963Seric return (EX_UNAVAILABLE); 2947963Seric else if (r != 354) 2957964Seric return (EX_PROTOCOL); 29610175Seric 29710175Seric /* now output the actual message */ 298*53740Seric (*e->e_puthdr)(mci->mci_out, m, CurEnv); 299*53740Seric putline("\n", mci->mci_out, m); 300*53740Seric (*e->e_putbody)(mci->mci_out, m, CurEnv); 30110175Seric 30210175Seric /* terminate the message */ 303*53740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 30410215Seric if (Verbose && !HoldErrs) 30510215Seric nmessage(Arpa_Info, ">>> ."); 30610175Seric 30710175Seric /* check for the results of the transaction */ 30824944Seric SmtpPhase = "result wait"; 30936584Sbostic setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 310*53740Seric r = reply(m, mci); 3118005Seric if (r < 0 || REPLYTYPE(r) == 4) 3124797Seric return (EX_TEMPFAIL); 3137963Seric else if (r == 250) 3147963Seric return (EX_OK); 3157963Seric else if (r == 552 || r == 554) 3167963Seric return (EX_UNAVAILABLE); 3177964Seric return (EX_PROTOCOL); 3184684Seric } 3194684Seric /* 3204865Seric ** SMTPQUIT -- close the SMTP connection. 3214865Seric ** 3224865Seric ** Parameters: 32315535Seric ** m -- a pointer to the mailer. 3244865Seric ** 3254865Seric ** Returns: 3264865Seric ** none. 3274865Seric ** 3284865Seric ** Side Effects: 3294865Seric ** sends the final protocol and closes the connection. 3304865Seric */ 3314865Seric 332*53740Seric smtpquit(mci, m) 33352676Seric register MCONINFO *mci; 334*53740Seric register MAILER *m; 3354865Seric { 3369391Seric int i; 3374865Seric 33810148Seric /* if the connection is already closed, don't bother */ 339*53740Seric if (mci->mci_state == MCIS_CLOSED) 34010148Seric return; 34110148Seric 34210148Seric /* send the quit message if not a forced quit */ 343*53740Seric if (mci->mci_state == MCIS_OPEN || mci->mci_state == MCIS_SSD) 3449391Seric { 34510175Seric smtpmessage("QUIT", m); 346*53740Seric (void) reply(m, mci); 347*53740Seric if (mci->mci_state == MCIS_CLOSED) 34810159Seric return; 3499391Seric } 3509391Seric 35152676Seric /* now actually close the connection and pick up the zombie */ 35252676Seric i = endmailer(mci, m->m_argv[0]); 3539391Seric if (i != EX_OK) 35415535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3554865Seric } 3564865Seric /* 3574684Seric ** REPLY -- read arpanet reply 3584684Seric ** 3594684Seric ** Parameters: 36010175Seric ** m -- the mailer we are reading the reply from. 3614684Seric ** 3624684Seric ** Returns: 3634684Seric ** reply code it reads. 3644684Seric ** 3654684Seric ** Side Effects: 3664684Seric ** flushes the mail file. 3674684Seric */ 3684684Seric 369*53740Seric reply(mci, m) 370*53740Seric MCONINFO *mci; 37110688Seric MAILER *m; 3724684Seric { 373*53740Seric (void) fflush(mci->mci_out); 3744684Seric 3757677Seric if (tTd(18, 1)) 3764796Seric printf("reply\n"); 3774796Seric 3787356Seric /* 3797356Seric ** Read the input line, being careful not to hang. 3807356Seric */ 3817356Seric 3824684Seric for (;;) 3834684Seric { 3844684Seric register int r; 3857356Seric register char *p; 3864684Seric 3877685Seric /* actually do the read */ 3889547Seric if (CurEnv->e_xfp != NULL) 3899547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3907356Seric 39110054Seric /* if we are in the process of closing just give the code */ 392*53740Seric if (mci->mci_state == MCIS_CLOSED) 39310054Seric return (SMTPCLOSING); 39410054Seric 39510054Seric /* get the line from the other side */ 396*53740Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in); 39710054Seric if (p == NULL) 39810131Seric { 39910148Seric extern char MsgBuf[]; /* err.c */ 40010148Seric extern char Arpa_TSyserr[]; /* conf.c */ 40110148Seric 40221065Seric /* if the remote end closed early, fake an error */ 40321065Seric if (errno == 0) 40421065Seric # ifdef ECONNRESET 40521065Seric errno = ECONNRESET; 40621065Seric # else ECONNRESET 40721065Seric errno = EPIPE; 40821065Seric # endif ECONNRESET 40921065Seric 41010148Seric message(Arpa_TSyserr, "reply: read error"); 41110420Seric /* if debugging, pause so we can see state */ 41210420Seric if (tTd(18, 100)) 41310420Seric pause(); 41410148Seric # ifdef LOG 41536234Skarels syslog(LOG_INFO, "%s", &MsgBuf[4]); 41610148Seric # endif LOG 417*53740Seric mci->mci_state = MCIS_CLOSED; 41815535Seric smtpquit(m); 41910054Seric return (-1); 42010131Seric } 42110054Seric fixcrlf(SmtpReplyBuffer, TRUE); 42210054Seric 42314900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 42414900Seric { 42514900Seric /* serious error -- log the previous command */ 42614900Seric if (SmtpMsgBuffer[0] != '\0') 42714900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 42814900Seric SmtpMsgBuffer[0] = '\0'; 42914900Seric 43014900Seric /* now log the message as from the other side */ 43114900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 43214900Seric } 43314900Seric 43414900Seric /* display the input for verbose mode */ 4357229Seric if (Verbose && !HoldErrs) 4369391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 4377356Seric 4387356Seric /* if continuation is required, we can go on */ 4399391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 4404684Seric continue; 4417356Seric 4427356Seric /* decode the reply code */ 4439391Seric r = atoi(SmtpReplyBuffer); 4447356Seric 4457356Seric /* extra semantics: 0xx codes are "informational" */ 4464684Seric if (r < 100) 4474684Seric continue; 4487356Seric 4499391Seric /* reply code 421 is "Service Shutting Down" */ 450*53740Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 4519391Seric { 45210054Seric /* send the quit protocol */ 453*53740Seric mci->mci_state = MCIS_SSD; 45415535Seric smtpquit(m); 4559391Seric } 4569391Seric 45721065Seric /* save temporary failure messages for posterity */ 45821065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 45921065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 46021065Seric 4614684Seric return (r); 4624684Seric } 4634684Seric } 4644796Seric /* 4654865Seric ** SMTPMESSAGE -- send message to server 4664796Seric ** 4674796Seric ** Parameters: 4684796Seric ** f -- format 46910175Seric ** m -- the mailer to control formatting. 4704796Seric ** a, b, c -- parameters 4714796Seric ** 4724796Seric ** Returns: 4734796Seric ** none. 4744796Seric ** 4754796Seric ** Side Effects: 476*53740Seric ** writes message to mci->mci_out. 4774796Seric */ 4784796Seric 4794865Seric /*VARARGS1*/ 480*53740Seric smtpmessage(f, m, mci, a, b, c) 4814796Seric char *f; 48210175Seric MAILER *m; 483*53740Seric MCONINFO *mci; 4844796Seric { 48514900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4867677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 48714900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 488*53740Seric if (mci->mci_out != NULL) 489*53740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 49040997Sbostic m == 0 ? "\r\n" : m->m_eol); 4914796Seric } 4925182Seric 4935182Seric # endif SMTP 494