14796Seric # include <ctype.h> 24684Seric # include <sysexits.h> 34865Seric # include "sendmail.h" 44684Seric 55182Seric # ifndef SMTP 6*16141Seric SCCSID(@(#)usersmtp.c 4.8 03/11/84 (no SMTP)); 75182Seric # else SMTP 84684Seric 9*16141Seric SCCSID(@(#)usersmtp.c 4.8 03/11/84); 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. 4714913Seric ** 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 8315139Seric if (CurEnv->e_xfp != NULL) 8415139Seric { 8515139Seric extern char *errstring(); 8615139Seric 8715323Seric fprintf(CurEnv->e_xfp, "421 %s.%s... Deferred: %s\n", 8815139Seric pvp[1], m->m_name, errstring(errno)); 8915139Seric } 906051Seric return (ExitStat); 916051Seric } 9211159Seric SmtpState = SMTP_OPEN; 934796Seric 944865Seric /* 954865Seric ** Get the greeting message. 9614913Seric ** This should appear spontaneously. Give it five minutes to 9714886Seric ** happen. 984865Seric */ 994797Seric 10014886Seric if (setjmp(CtxGreeting) != 0) 10114913Seric goto tempfail; 102*16141Seric gte = setevent((time_t) 300, greettimeout, 0); 10310175Seric r = reply(m); 10414886Seric clrevent(gte); 1058005Seric if (r < 0 || REPLYTYPE(r) != 2) 10614913Seric goto tempfail; 1074684Seric 1084865Seric /* 1094976Seric ** Send the HELO command. 1107963Seric ** My mother taught me to always introduce myself. 1114976Seric */ 1124976Seric 11310175Seric smtpmessage("HELO %s", m, HostName); 11410175Seric r = reply(m); 1158005Seric if (r < 0) 11614913Seric goto tempfail; 1178005Seric else if (REPLYTYPE(r) == 5) 11814913Seric goto unavailable; 1197963Seric else if (REPLYTYPE(r) != 2) 12014913Seric goto tempfail; 1214976Seric 1224976Seric /* 1239315Seric ** If this is expected to be another sendmail, send some internal 1249315Seric ** commands. 1259315Seric */ 1269315Seric 12710688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1289315Seric { 1299315Seric /* tell it to be verbose */ 13010175Seric smtpmessage("VERB", m); 13110175Seric r = reply(m); 1329315Seric if (r < 0) 13314913Seric goto tempfail; 1349315Seric 1359315Seric /* tell it we will be sending one transaction only */ 13610175Seric smtpmessage("ONEX", m); 13710175Seric r = reply(m); 1389315Seric if (r < 0) 13914913Seric goto tempfail; 1409315Seric } 1419315Seric 1429315Seric /* 1434865Seric ** Send the MAIL command. 1444865Seric ** Designates the sender. 1454865Seric */ 1464796Seric 1476980Seric expand("$g", buf, &buf[sizeof buf - 1], CurEnv); 1488436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 14910688Seric !bitnset(M_FROMPATH, m->m_flags)) 1508436Seric { 15110308Seric smtpmessage("MAIL From:<%s>", m, buf); 1528436Seric } 1538436Seric else 1548436Seric { 15510175Seric smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 15610308Seric buf[0] == '@' ? ',' : ':', buf); 1578436Seric } 15810175Seric r = reply(m); 1598005Seric if (r < 0 || REPLYTYPE(r) == 4) 16014913Seric goto tempfail; 1617963Seric else if (r == 250) 1627963Seric return (EX_OK); 1637963Seric else if (r == 552) 16414913Seric goto unavailable; 16514913Seric 16614913Seric /* protocol error -- close up */ 16714913Seric smtpquit(m); 1687964Seric return (EX_PROTOCOL); 16914913Seric 17014913Seric /* signal a temporary failure */ 17114913Seric tempfail: 17214913Seric smtpquit(m); 17314913Seric return (EX_TEMPFAIL); 17414913Seric 17514913Seric /* signal service unavailable */ 17614913Seric unavailable: 17714913Seric smtpquit(m); 17814913Seric return (EX_UNAVAILABLE); 1794684Seric } 18014886Seric 18114886Seric 18214886Seric static 18314886Seric greettimeout() 18414886Seric { 18514886Seric /* timeout reading the greeting message */ 18614886Seric longjmp(CtxGreeting, 1); 18714886Seric } 1884684Seric /* 1894976Seric ** SMTPRCPT -- designate recipient. 1904797Seric ** 1914797Seric ** Parameters: 1924865Seric ** to -- address of recipient. 19310175Seric ** m -- the mailer we are sending to. 1944797Seric ** 1954797Seric ** Returns: 1964865Seric ** exit status corresponding to recipient status. 1974797Seric ** 1984797Seric ** Side Effects: 1994865Seric ** Sends the mail via SMTP. 2004797Seric */ 2014797Seric 20210175Seric smtprcpt(to, m) 2034865Seric ADDRESS *to; 20410175Seric register MAILER *m; 2054797Seric { 2064797Seric register int r; 20710308Seric extern char *remotename(); 2084797Seric 20910308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 2104865Seric 21110175Seric r = reply(m); 2128005Seric if (r < 0 || REPLYTYPE(r) == 4) 2134865Seric return (EX_TEMPFAIL); 2147963Seric else if (REPLYTYPE(r) == 2) 2157963Seric return (EX_OK); 2167964Seric else if (r == 550 || r == 551 || r == 553) 2177964Seric return (EX_NOUSER); 2187964Seric else if (r == 552 || r == 554) 2197964Seric return (EX_UNAVAILABLE); 2207964Seric return (EX_PROTOCOL); 2214797Seric } 2224797Seric /* 22310175Seric ** SMTPDATA -- send the data and clean up the transaction. 2244684Seric ** 2254684Seric ** Parameters: 2264865Seric ** m -- mailer being sent to. 2276980Seric ** e -- the envelope for this message. 2284684Seric ** 2294684Seric ** Returns: 2304976Seric ** exit status corresponding to DATA command. 2314684Seric ** 2324684Seric ** Side Effects: 2334865Seric ** none. 2344684Seric */ 2354684Seric 23610175Seric smtpdata(m, e) 2374865Seric struct mailer *m; 2386980Seric register ENVELOPE *e; 2394684Seric { 2404684Seric register int r; 2414684Seric 2424797Seric /* 2434797Seric ** Send the data. 24410175Seric ** First send the command and check that it is ok. 24510175Seric ** Then send the data. 24610175Seric ** Follow it up with a dot to terminate. 24710175Seric ** Finally get the results of the transaction. 2484797Seric */ 2494797Seric 25010175Seric /* send the command and check ok to proceed */ 25110175Seric smtpmessage("DATA", m); 25210175Seric r = reply(m); 2538005Seric if (r < 0 || REPLYTYPE(r) == 4) 2544797Seric return (EX_TEMPFAIL); 2557963Seric else if (r == 554) 2567963Seric return (EX_UNAVAILABLE); 2577963Seric else if (r != 354) 2587964Seric return (EX_PROTOCOL); 25910175Seric 26010175Seric /* now output the actual message */ 26110175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 26210175Seric putline("\n", SmtpOut, m); 26310175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 26410175Seric 26510175Seric /* terminate the message */ 26610328Seric fprintf(SmtpOut, ".%s", m->m_eol); 26710215Seric if (Verbose && !HoldErrs) 26810215Seric nmessage(Arpa_Info, ">>> ."); 26910175Seric 27010175Seric /* check for the results of the transaction */ 27110175Seric r = reply(m); 2728005Seric if (r < 0 || REPLYTYPE(r) == 4) 2734797Seric return (EX_TEMPFAIL); 2747963Seric else if (r == 250) 2757963Seric return (EX_OK); 2767963Seric else if (r == 552 || r == 554) 2777963Seric return (EX_UNAVAILABLE); 2787964Seric return (EX_PROTOCOL); 2794684Seric } 2804684Seric /* 2814865Seric ** SMTPQUIT -- close the SMTP connection. 2824865Seric ** 2834865Seric ** Parameters: 28415535Seric ** m -- a pointer to the mailer. 2854865Seric ** 2864865Seric ** Returns: 2874865Seric ** none. 2884865Seric ** 2894865Seric ** Side Effects: 2904865Seric ** sends the final protocol and closes the connection. 2914865Seric */ 2924865Seric 29315535Seric smtpquit(m) 29410175Seric register MAILER *m; 2954865Seric { 2969391Seric int i; 2974865Seric 29810148Seric /* if the connection is already closed, don't bother */ 29910148Seric if (SmtpIn == NULL) 30010148Seric return; 30110148Seric 30210148Seric /* send the quit message if not a forced quit */ 30311159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 3049391Seric { 30510175Seric smtpmessage("QUIT", m); 30610175Seric (void) reply(m); 30711159Seric if (SmtpState == SMTP_CLOSED) 30810159Seric return; 3099391Seric } 3109391Seric 31110148Seric /* now actually close the connection */ 3127229Seric (void) fclose(SmtpIn); 3137229Seric (void) fclose(SmtpOut); 31410148Seric SmtpIn = SmtpOut = NULL; 31511159Seric SmtpState = SMTP_CLOSED; 31610148Seric 31710148Seric /* and pick up the zombie */ 31815535Seric i = endmailer(SmtpPid, m->m_argv[0]); 3199391Seric if (i != EX_OK) 32015535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3214865Seric } 3224865Seric /* 3234684Seric ** REPLY -- read arpanet reply 3244684Seric ** 3254684Seric ** Parameters: 32610175Seric ** m -- the mailer we are reading the reply from. 3274684Seric ** 3284684Seric ** Returns: 3294684Seric ** reply code it reads. 3304684Seric ** 3314684Seric ** Side Effects: 3324684Seric ** flushes the mail file. 3334684Seric */ 3344684Seric 33510175Seric reply(m) 33610688Seric MAILER *m; 3374684Seric { 3384865Seric (void) fflush(SmtpOut); 3394684Seric 3407677Seric if (tTd(18, 1)) 3414796Seric printf("reply\n"); 3424796Seric 3437356Seric /* 3447356Seric ** Read the input line, being careful not to hang. 3457356Seric */ 3467356Seric 3474684Seric for (;;) 3484684Seric { 3494684Seric register int r; 3507356Seric register char *p; 3514684Seric 3527685Seric /* actually do the read */ 3539547Seric if (CurEnv->e_xfp != NULL) 3549547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3557356Seric 35610054Seric /* if we are in the process of closing just give the code */ 35711159Seric if (SmtpState == SMTP_CLOSED) 35810054Seric return (SMTPCLOSING); 35910054Seric 36010054Seric /* get the line from the other side */ 36110054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 36210054Seric if (p == NULL) 36310131Seric { 36410148Seric extern char MsgBuf[]; /* err.c */ 36510148Seric extern char Arpa_TSyserr[]; /* conf.c */ 36610148Seric 36710148Seric message(Arpa_TSyserr, "reply: read error"); 36810420Seric # ifdef DEBUG 36910420Seric /* if debugging, pause so we can see state */ 37010420Seric if (tTd(18, 100)) 37110420Seric pause(); 37210420Seric # endif DEBUG 37310148Seric # ifdef LOG 37410148Seric syslog(LOG_ERR, "%s", &MsgBuf[4]); 37510148Seric # endif LOG 37611159Seric SmtpState = SMTP_CLOSED; 37715535Seric smtpquit(m); 37810054Seric return (-1); 37910131Seric } 38010054Seric fixcrlf(SmtpReplyBuffer, TRUE); 38110054Seric 38214900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 38314900Seric { 38414900Seric /* serious error -- log the previous command */ 38514900Seric if (SmtpMsgBuffer[0] != '\0') 38614900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 38714900Seric SmtpMsgBuffer[0] = '\0'; 38814900Seric 38914900Seric /* now log the message as from the other side */ 39014900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 39114900Seric } 39214900Seric 39314900Seric /* display the input for verbose mode */ 3947229Seric if (Verbose && !HoldErrs) 3959391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 3967356Seric 3977356Seric /* if continuation is required, we can go on */ 3989391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 3994684Seric continue; 4007356Seric 4017356Seric /* decode the reply code */ 4029391Seric r = atoi(SmtpReplyBuffer); 4037356Seric 4047356Seric /* extra semantics: 0xx codes are "informational" */ 4054684Seric if (r < 100) 4064684Seric continue; 4077356Seric 4089391Seric /* reply code 421 is "Service Shutting Down" */ 40911159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 4109391Seric { 41110054Seric /* send the quit protocol */ 41211159Seric SmtpState = SMTP_SSD; 41315535Seric smtpquit(m); 4149391Seric } 4159391Seric 4164684Seric return (r); 4174684Seric } 4184684Seric } 4194796Seric /* 4204865Seric ** SMTPMESSAGE -- send message to server 4214796Seric ** 4224796Seric ** Parameters: 4234796Seric ** f -- format 42410175Seric ** m -- the mailer to control formatting. 4254796Seric ** a, b, c -- parameters 4264796Seric ** 4274796Seric ** Returns: 4284796Seric ** none. 4294796Seric ** 4304796Seric ** Side Effects: 4314865Seric ** writes message to SmtpOut. 4324796Seric */ 4334796Seric 4344865Seric /*VARARGS1*/ 43510175Seric smtpmessage(f, m, a, b, c) 4364796Seric char *f; 43710175Seric MAILER *m; 4384796Seric { 43914900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4407677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 44114900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 44211159Seric if (SmtpOut != NULL) 44314900Seric fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 4444796Seric } 4455182Seric 4465182Seric # endif SMTP 447