14796Seric # include <ctype.h> 24684Seric # include <sysexits.h> 34865Seric # include "sendmail.h" 44684Seric 55182Seric # ifndef SMTP 6*14913Seric SCCSID(@(#)usersmtp.c 4.4 09/07/83 (no SMTP)); 75182Seric # else SMTP 84684Seric 9*14913Seric SCCSID(@(#)usersmtp.c 4.4 09/07/83); 105182Seric 119391Seric 129391Seric 134684Seric /* 149391Seric ** USERSMTP -- run SMTP protocol from the user end. 159391Seric ** 169391Seric ** This protocol is described in RFC821. 179391Seric */ 189391Seric 199391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 209391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 219391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 229391Seric 2314900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 2410054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 2510054Seric FILE *SmtpOut; /* output file */ 2610054Seric FILE *SmtpIn; /* input file */ 2710054Seric int SmtpPid; /* pid of mailer */ 2811159Seric 2911159Seric /* following represents the state of the SMTP connection */ 3011159Seric int SmtpState; /* connection state, see below */ 3111159Seric 3211159Seric #define SMTP_CLOSED 0 /* connection is closed */ 3311159Seric #define SMTP_OPEN 1 /* connection is open for business */ 3411159Seric #define SMTP_SSD 2 /* service shutting down */ 359391Seric /* 364865Seric ** SMTPINIT -- initialize SMTP. 374684Seric ** 384865Seric ** Opens the connection and sends the initial protocol. 394684Seric ** 404684Seric ** Parameters: 414865Seric ** m -- mailer to create connection to. 424865Seric ** pvp -- pointer to parameter vector to pass to 434865Seric ** the mailer. 444684Seric ** 454684Seric ** Returns: 464865Seric ** appropriate exit status -- EX_OK on success. 47*14913Seric ** If not EX_OK, it should close the connection. 484684Seric ** 494684Seric ** Side Effects: 504865Seric ** creates connection and sends initial protocol. 514684Seric */ 524684Seric 5314886Seric jmp_buf CtxGreeting; 5414886Seric 5510175Seric smtpinit(m, pvp) 564865Seric struct mailer *m; 574865Seric char **pvp; 584684Seric { 594865Seric register int r; 6014886Seric EVENT *gte; 614865Seric char buf[MAXNAME]; 6214886Seric extern greettimeout(); 634684Seric 644865Seric /* 654865Seric ** Open the connection to the mailer. 664865Seric */ 674684Seric 6811159Seric #ifdef DEBUG 6911159Seric if (SmtpState == SMTP_OPEN) 7011159Seric syserr("smtpinit: already open"); 7111159Seric #endif DEBUG 7211159Seric 736051Seric SmtpIn = SmtpOut = NULL; 7411159Seric SmtpState = SMTP_CLOSED; 7510175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 766051Seric if (SmtpPid < 0) 776051Seric { 786051Seric # ifdef DEBUG 797677Seric if (tTd(18, 1)) 809391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 819391Seric pvp[0], ExitStat, errno); 826051Seric # endif DEBUG 836051Seric return (ExitStat); 846051Seric } 8511159Seric SmtpState = SMTP_OPEN; 864796Seric 874865Seric /* 884865Seric ** Get the greeting message. 89*14913Seric ** This should appear spontaneously. Give it five minutes to 9014886Seric ** happen. 914865Seric */ 924797Seric 9314886Seric if (setjmp(CtxGreeting) != 0) 94*14913Seric goto tempfail; 95*14913Seric gte = setevent(300, greettimeout, 0); 9610175Seric r = reply(m); 9714886Seric clrevent(gte); 988005Seric if (r < 0 || REPLYTYPE(r) != 2) 99*14913Seric goto tempfail; 1004684Seric 1014865Seric /* 1024976Seric ** Send the HELO command. 1037963Seric ** My mother taught me to always introduce myself. 1044976Seric */ 1054976Seric 10610175Seric smtpmessage("HELO %s", m, HostName); 10710175Seric r = reply(m); 1088005Seric if (r < 0) 109*14913Seric goto tempfail; 1108005Seric else if (REPLYTYPE(r) == 5) 111*14913Seric goto unavailable; 1127963Seric else if (REPLYTYPE(r) != 2) 113*14913Seric goto tempfail; 1144976Seric 1154976Seric /* 1169315Seric ** If this is expected to be another sendmail, send some internal 1179315Seric ** commands. 1189315Seric */ 1199315Seric 12010688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1219315Seric { 1229315Seric /* tell it to be verbose */ 12310175Seric smtpmessage("VERB", m); 12410175Seric r = reply(m); 1259315Seric if (r < 0) 126*14913Seric goto tempfail; 1279315Seric 1289315Seric /* tell it we will be sending one transaction only */ 12910175Seric smtpmessage("ONEX", m); 13010175Seric r = reply(m); 1319315Seric if (r < 0) 132*14913Seric goto tempfail; 1339315Seric } 1349315Seric 1359315Seric /* 1364865Seric ** Send the MAIL command. 1374865Seric ** Designates the sender. 1384865Seric */ 1394796Seric 1406980Seric expand("$g", buf, &buf[sizeof buf - 1], CurEnv); 1418436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 14210688Seric !bitnset(M_FROMPATH, m->m_flags)) 1438436Seric { 14410308Seric smtpmessage("MAIL From:<%s>", m, buf); 1458436Seric } 1468436Seric else 1478436Seric { 14810175Seric smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 14910308Seric buf[0] == '@' ? ',' : ':', buf); 1508436Seric } 15110175Seric r = reply(m); 1528005Seric if (r < 0 || REPLYTYPE(r) == 4) 153*14913Seric goto tempfail; 1547963Seric else if (r == 250) 1557963Seric return (EX_OK); 1567963Seric else if (r == 552) 157*14913Seric goto unavailable; 158*14913Seric 159*14913Seric /* protocol error -- close up */ 160*14913Seric smtpquit(m); 1617964Seric return (EX_PROTOCOL); 162*14913Seric 163*14913Seric /* signal a temporary failure */ 164*14913Seric tempfail: 165*14913Seric smtpquit(m); 166*14913Seric return (EX_TEMPFAIL); 167*14913Seric 168*14913Seric /* signal service unavailable */ 169*14913Seric unavailable: 170*14913Seric smtpquit(m); 171*14913Seric return (EX_UNAVAILABLE); 1724684Seric } 17314886Seric 17414886Seric 17514886Seric static 17614886Seric greettimeout() 17714886Seric { 17814886Seric /* timeout reading the greeting message */ 17914886Seric longjmp(CtxGreeting, 1); 18014886Seric } 1814684Seric /* 1824976Seric ** SMTPRCPT -- designate recipient. 1834797Seric ** 1844797Seric ** Parameters: 1854865Seric ** to -- address of recipient. 18610175Seric ** m -- the mailer we are sending to. 1874797Seric ** 1884797Seric ** Returns: 1894865Seric ** exit status corresponding to recipient status. 1904797Seric ** 1914797Seric ** Side Effects: 1924865Seric ** Sends the mail via SMTP. 1934797Seric */ 1944797Seric 19510175Seric smtprcpt(to, m) 1964865Seric ADDRESS *to; 19710175Seric register MAILER *m; 1984797Seric { 1994797Seric register int r; 20010308Seric extern char *remotename(); 2014797Seric 20210308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2034865Seric 20410175Seric r = reply(m); 2058005Seric if (r < 0 || REPLYTYPE(r) == 4) 2064865Seric return (EX_TEMPFAIL); 2077963Seric else if (REPLYTYPE(r) == 2) 2087963Seric return (EX_OK); 2097964Seric else if (r == 550 || r == 551 || r == 553) 2107964Seric return (EX_NOUSER); 2117964Seric else if (r == 552 || r == 554) 2127964Seric return (EX_UNAVAILABLE); 2137964Seric return (EX_PROTOCOL); 2144797Seric } 2154797Seric /* 21610175Seric ** SMTPDATA -- send the data and clean up the transaction. 2174684Seric ** 2184684Seric ** Parameters: 2194865Seric ** m -- mailer being sent to. 2206980Seric ** e -- the envelope for this message. 2214684Seric ** 2224684Seric ** Returns: 2234976Seric ** exit status corresponding to DATA command. 2244684Seric ** 2254684Seric ** Side Effects: 2264865Seric ** none. 2274684Seric */ 2284684Seric 22910175Seric smtpdata(m, e) 2304865Seric struct mailer *m; 2316980Seric register ENVELOPE *e; 2324684Seric { 2334684Seric register int r; 2344684Seric 2354797Seric /* 2364797Seric ** Send the data. 23710175Seric ** First send the command and check that it is ok. 23810175Seric ** Then send the data. 23910175Seric ** Follow it up with a dot to terminate. 24010175Seric ** Finally get the results of the transaction. 2414797Seric */ 2424797Seric 24310175Seric /* send the command and check ok to proceed */ 24410175Seric smtpmessage("DATA", m); 24510175Seric r = reply(m); 2468005Seric if (r < 0 || REPLYTYPE(r) == 4) 2474797Seric return (EX_TEMPFAIL); 2487963Seric else if (r == 554) 2497963Seric return (EX_UNAVAILABLE); 2507963Seric else if (r != 354) 2517964Seric return (EX_PROTOCOL); 25210175Seric 25310175Seric /* now output the actual message */ 25410175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 25510175Seric putline("\n", SmtpOut, m); 25610175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 25710175Seric 25810175Seric /* terminate the message */ 25910328Seric fprintf(SmtpOut, ".%s", m->m_eol); 26010215Seric if (Verbose && !HoldErrs) 26110215Seric nmessage(Arpa_Info, ">>> ."); 26210175Seric 26310175Seric /* check for the results of the transaction */ 26410175Seric r = reply(m); 2658005Seric if (r < 0 || REPLYTYPE(r) == 4) 2664797Seric return (EX_TEMPFAIL); 2677963Seric else if (r == 250) 2687963Seric return (EX_OK); 2697963Seric else if (r == 552 || r == 554) 2707963Seric return (EX_UNAVAILABLE); 2717964Seric return (EX_PROTOCOL); 2724684Seric } 2734684Seric /* 2744865Seric ** SMTPQUIT -- close the SMTP connection. 2754865Seric ** 2764865Seric ** Parameters: 2774865Seric ** name -- name of mailer we are quitting. 2784865Seric ** 2794865Seric ** Returns: 2804865Seric ** none. 2814865Seric ** 2824865Seric ** Side Effects: 2834865Seric ** sends the final protocol and closes the connection. 2844865Seric */ 2854865Seric 28610175Seric smtpquit(name, m) 2874865Seric char *name; 28810175Seric register MAILER *m; 2894865Seric { 2909391Seric int i; 2914865Seric 29210148Seric /* if the connection is already closed, don't bother */ 29310148Seric if (SmtpIn == NULL) 29410148Seric return; 29510148Seric 29610148Seric /* send the quit message if not a forced quit */ 29711159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 2989391Seric { 29910175Seric smtpmessage("QUIT", m); 30010175Seric (void) reply(m); 30111159Seric if (SmtpState == SMTP_CLOSED) 30210159Seric return; 3039391Seric } 3049391Seric 30510148Seric /* now actually close the connection */ 3067229Seric (void) fclose(SmtpIn); 3077229Seric (void) fclose(SmtpOut); 30810148Seric SmtpIn = SmtpOut = NULL; 30911159Seric SmtpState = SMTP_CLOSED; 31010148Seric 31110148Seric /* and pick up the zombie */ 3127229Seric i = endmailer(SmtpPid, name); 3139391Seric if (i != EX_OK) 3149391Seric syserr("smtpquit %s: stat %d", name, i); 3154865Seric } 3164865Seric /* 3174684Seric ** REPLY -- read arpanet reply 3184684Seric ** 3194684Seric ** Parameters: 32010175Seric ** m -- the mailer we are reading the reply from. 3214684Seric ** 3224684Seric ** Returns: 3234684Seric ** reply code it reads. 3244684Seric ** 3254684Seric ** Side Effects: 3264684Seric ** flushes the mail file. 3274684Seric */ 3284684Seric 32910175Seric reply(m) 33010688Seric MAILER *m; 3314684Seric { 3324865Seric (void) fflush(SmtpOut); 3334684Seric 3347677Seric if (tTd(18, 1)) 3354796Seric printf("reply\n"); 3364796Seric 3377356Seric /* 3387356Seric ** Read the input line, being careful not to hang. 3397356Seric */ 3407356Seric 3414684Seric for (;;) 3424684Seric { 3434684Seric register int r; 3447356Seric register char *p; 3454684Seric 3467685Seric /* actually do the read */ 3479547Seric if (CurEnv->e_xfp != NULL) 3489547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3497356Seric 35010054Seric /* if we are in the process of closing just give the code */ 35111159Seric if (SmtpState == SMTP_CLOSED) 35210054Seric return (SMTPCLOSING); 35310054Seric 35410054Seric /* get the line from the other side */ 35510054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 35610054Seric if (p == NULL) 35710131Seric { 35810148Seric extern char MsgBuf[]; /* err.c */ 35910148Seric extern char Arpa_TSyserr[]; /* conf.c */ 36010148Seric 36110148Seric message(Arpa_TSyserr, "reply: read error"); 36210420Seric # ifdef DEBUG 36310420Seric /* if debugging, pause so we can see state */ 36410420Seric if (tTd(18, 100)) 36510420Seric pause(); 36610420Seric # endif DEBUG 36710148Seric # ifdef LOG 36810148Seric syslog(LOG_ERR, "%s", &MsgBuf[4]); 36910148Seric # endif LOG 37011159Seric SmtpState = SMTP_CLOSED; 37110175Seric smtpquit("reply error", m); 37210054Seric return (-1); 37310131Seric } 37410054Seric fixcrlf(SmtpReplyBuffer, TRUE); 37510054Seric 37614900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 37714900Seric { 37814900Seric /* serious error -- log the previous command */ 37914900Seric if (SmtpMsgBuffer[0] != '\0') 38014900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 38114900Seric SmtpMsgBuffer[0] = '\0'; 38214900Seric 38314900Seric /* now log the message as from the other side */ 38414900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 38514900Seric } 38614900Seric 38714900Seric /* display the input for verbose mode */ 3887229Seric if (Verbose && !HoldErrs) 3899391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 3907356Seric 3917356Seric /* if continuation is required, we can go on */ 3929391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 3934684Seric continue; 3947356Seric 3957356Seric /* decode the reply code */ 3969391Seric r = atoi(SmtpReplyBuffer); 3977356Seric 3987356Seric /* extra semantics: 0xx codes are "informational" */ 3994684Seric if (r < 100) 4004684Seric continue; 4017356Seric 4029391Seric /* reply code 421 is "Service Shutting Down" */ 40311159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4049391Seric { 40510054Seric /* send the quit protocol */ 40611159Seric SmtpState = SMTP_SSD; 40710175Seric smtpquit("SMTP Shutdown", m); 4089391Seric } 4099391Seric 4104684Seric return (r); 4114684Seric } 4124684Seric } 4134796Seric /* 4144865Seric ** SMTPMESSAGE -- send message to server 4154796Seric ** 4164796Seric ** Parameters: 4174796Seric ** f -- format 41810175Seric ** m -- the mailer to control formatting. 4194796Seric ** a, b, c -- parameters 4204796Seric ** 4214796Seric ** Returns: 4224796Seric ** none. 4234796Seric ** 4244796Seric ** Side Effects: 4254865Seric ** writes message to SmtpOut. 4264796Seric */ 4274796Seric 4284865Seric /*VARARGS1*/ 42910175Seric smtpmessage(f, m, a, b, c) 4304796Seric char *f; 43110175Seric MAILER *m; 4324796Seric { 43314900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4347677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 43514900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 43611159Seric if (SmtpOut != NULL) 43714900Seric fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 4384796Seric } 4395182Seric 4405182Seric # endif SMTP 441