14796Seric # include <ctype.h> 24684Seric # include <sysexits.h> 34865Seric # include "sendmail.h" 44684Seric 55182Seric # ifndef SMTP 6*14900Seric SCCSID(@(#)usersmtp.c 4.3 09/05/83 (no SMTP)); 75182Seric # else SMTP 84684Seric 9*14900Seric SCCSID(@(#)usersmtp.c 4.3 09/05/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 23*14900Seric 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. 474684Seric ** 484684Seric ** Side Effects: 494865Seric ** creates connection and sends initial protocol. 504684Seric */ 514684Seric 5214886Seric jmp_buf CtxGreeting; 5314886Seric 5410175Seric smtpinit(m, pvp) 554865Seric struct mailer *m; 564865Seric char **pvp; 574684Seric { 584865Seric register int r; 5914886Seric EVENT *gte; 604865Seric char buf[MAXNAME]; 6114886Seric extern greettimeout(); 624684Seric 634865Seric /* 644865Seric ** Open the connection to the mailer. 654865Seric */ 664684Seric 6711159Seric #ifdef DEBUG 6811159Seric if (SmtpState == SMTP_OPEN) 6911159Seric syserr("smtpinit: already open"); 7011159Seric #endif DEBUG 7111159Seric 726051Seric SmtpIn = SmtpOut = NULL; 7311159Seric SmtpState = SMTP_CLOSED; 7410175Seric SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn); 756051Seric if (SmtpPid < 0) 766051Seric { 776051Seric # ifdef DEBUG 787677Seric if (tTd(18, 1)) 799391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 809391Seric pvp[0], ExitStat, errno); 816051Seric # endif DEBUG 826051Seric return (ExitStat); 836051Seric } 8411159Seric SmtpState = SMTP_OPEN; 854796Seric 864865Seric /* 874865Seric ** Get the greeting message. 8814886Seric ** This should appear spontaneously. Give it two minutes to 8914886Seric ** happen. 904865Seric */ 914797Seric 9214886Seric if (setjmp(CtxGreeting) != 0) 9314886Seric return (EX_TEMPFAIL); 9414886Seric gte = setevent(120, greettimeout, 0); 9510175Seric r = reply(m); 9614886Seric clrevent(gte); 978005Seric if (r < 0 || REPLYTYPE(r) != 2) 984865Seric return (EX_TEMPFAIL); 994684Seric 1004865Seric /* 1014976Seric ** Send the HELO command. 1027963Seric ** My mother taught me to always introduce myself. 1034976Seric */ 1044976Seric 10510175Seric smtpmessage("HELO %s", m, HostName); 10610175Seric r = reply(m); 1078005Seric if (r < 0) 1088005Seric return (EX_TEMPFAIL); 1098005Seric else if (REPLYTYPE(r) == 5) 1104976Seric return (EX_UNAVAILABLE); 1117963Seric else if (REPLYTYPE(r) != 2) 1124976Seric return (EX_TEMPFAIL); 1134976Seric 1144976Seric /* 1159315Seric ** If this is expected to be another sendmail, send some internal 1169315Seric ** commands. 1179315Seric */ 1189315Seric 11910688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1209315Seric { 1219315Seric /* tell it to be verbose */ 12210175Seric smtpmessage("VERB", m); 12310175Seric r = reply(m); 1249315Seric if (r < 0) 1259315Seric return (EX_TEMPFAIL); 1269315Seric 1279315Seric /* tell it we will be sending one transaction only */ 12810175Seric smtpmessage("ONEX", m); 12910175Seric r = reply(m); 1309315Seric if (r < 0) 1319315Seric return (EX_TEMPFAIL); 1329315Seric } 1339315Seric 1349315Seric /* 1354865Seric ** Send the MAIL command. 1364865Seric ** Designates the sender. 1374865Seric */ 1384796Seric 1396980Seric expand("$g", buf, &buf[sizeof buf - 1], CurEnv); 1408436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 14110688Seric !bitnset(M_FROMPATH, m->m_flags)) 1428436Seric { 14310308Seric smtpmessage("MAIL From:<%s>", m, buf); 1448436Seric } 1458436Seric else 1468436Seric { 14710175Seric smtpmessage("MAIL From:<@%s%c%s>", m, HostName, 14810308Seric buf[0] == '@' ? ',' : ':', buf); 1498436Seric } 15010175Seric r = reply(m); 1518005Seric if (r < 0 || REPLYTYPE(r) == 4) 1524865Seric return (EX_TEMPFAIL); 1537963Seric else if (r == 250) 1547963Seric return (EX_OK); 1557963Seric else if (r == 552) 1567963Seric return (EX_UNAVAILABLE); 1577964Seric return (EX_PROTOCOL); 1584684Seric } 15914886Seric 16014886Seric 16114886Seric static 16214886Seric greettimeout() 16314886Seric { 16414886Seric /* timeout reading the greeting message */ 16514886Seric longjmp(CtxGreeting, 1); 16614886Seric } 1674684Seric /* 1684976Seric ** SMTPRCPT -- designate recipient. 1694797Seric ** 1704797Seric ** Parameters: 1714865Seric ** to -- address of recipient. 17210175Seric ** m -- the mailer we are sending to. 1734797Seric ** 1744797Seric ** Returns: 1754865Seric ** exit status corresponding to recipient status. 1764797Seric ** 1774797Seric ** Side Effects: 1784865Seric ** Sends the mail via SMTP. 1794797Seric */ 1804797Seric 18110175Seric smtprcpt(to, m) 1824865Seric ADDRESS *to; 18310175Seric register MAILER *m; 1844797Seric { 1854797Seric register int r; 18610308Seric extern char *remotename(); 1874797Seric 18810308Seric smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE)); 1894865Seric 19010175Seric r = reply(m); 1918005Seric if (r < 0 || REPLYTYPE(r) == 4) 1924865Seric return (EX_TEMPFAIL); 1937963Seric else if (REPLYTYPE(r) == 2) 1947963Seric return (EX_OK); 1957964Seric else if (r == 550 || r == 551 || r == 553) 1967964Seric return (EX_NOUSER); 1977964Seric else if (r == 552 || r == 554) 1987964Seric return (EX_UNAVAILABLE); 1997964Seric return (EX_PROTOCOL); 2004797Seric } 2014797Seric /* 20210175Seric ** SMTPDATA -- send the data and clean up the transaction. 2034684Seric ** 2044684Seric ** Parameters: 2054865Seric ** m -- mailer being sent to. 2066980Seric ** e -- the envelope for this message. 2074684Seric ** 2084684Seric ** Returns: 2094976Seric ** exit status corresponding to DATA command. 2104684Seric ** 2114684Seric ** Side Effects: 2124865Seric ** none. 2134684Seric */ 2144684Seric 21510175Seric smtpdata(m, e) 2164865Seric struct mailer *m; 2176980Seric register ENVELOPE *e; 2184684Seric { 2194684Seric register int r; 2204684Seric 2214797Seric /* 2224797Seric ** Send the data. 22310175Seric ** First send the command and check that it is ok. 22410175Seric ** Then send the data. 22510175Seric ** Follow it up with a dot to terminate. 22610175Seric ** Finally get the results of the transaction. 2274797Seric */ 2284797Seric 22910175Seric /* send the command and check ok to proceed */ 23010175Seric smtpmessage("DATA", m); 23110175Seric r = reply(m); 2328005Seric if (r < 0 || REPLYTYPE(r) == 4) 2334797Seric return (EX_TEMPFAIL); 2347963Seric else if (r == 554) 2357963Seric return (EX_UNAVAILABLE); 2367963Seric else if (r != 354) 2377964Seric return (EX_PROTOCOL); 23810175Seric 23910175Seric /* now output the actual message */ 24010175Seric (*e->e_puthdr)(SmtpOut, m, CurEnv); 24110175Seric putline("\n", SmtpOut, m); 24210175Seric (*e->e_putbody)(SmtpOut, m, CurEnv); 24310175Seric 24410175Seric /* terminate the message */ 24510328Seric fprintf(SmtpOut, ".%s", m->m_eol); 24610215Seric if (Verbose && !HoldErrs) 24710215Seric nmessage(Arpa_Info, ">>> ."); 24810175Seric 24910175Seric /* check for the results of the transaction */ 25010175Seric r = reply(m); 2518005Seric if (r < 0 || REPLYTYPE(r) == 4) 2524797Seric return (EX_TEMPFAIL); 2537963Seric else if (r == 250) 2547963Seric return (EX_OK); 2557963Seric else if (r == 552 || r == 554) 2567963Seric return (EX_UNAVAILABLE); 2577964Seric return (EX_PROTOCOL); 2584684Seric } 2594684Seric /* 2604865Seric ** SMTPQUIT -- close the SMTP connection. 2614865Seric ** 2624865Seric ** Parameters: 2634865Seric ** name -- name of mailer we are quitting. 2644865Seric ** 2654865Seric ** Returns: 2664865Seric ** none. 2674865Seric ** 2684865Seric ** Side Effects: 2694865Seric ** sends the final protocol and closes the connection. 2704865Seric */ 2714865Seric 27210175Seric smtpquit(name, m) 2734865Seric char *name; 27410175Seric register MAILER *m; 2754865Seric { 2769391Seric int i; 2774865Seric 27810148Seric /* if the connection is already closed, don't bother */ 27910148Seric if (SmtpIn == NULL) 28010148Seric return; 28110148Seric 28210148Seric /* send the quit message if not a forced quit */ 28311159Seric if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 2849391Seric { 28510175Seric smtpmessage("QUIT", m); 28610175Seric (void) reply(m); 28711159Seric if (SmtpState == SMTP_CLOSED) 28810159Seric return; 2899391Seric } 2909391Seric 29110148Seric /* now actually close the connection */ 2927229Seric (void) fclose(SmtpIn); 2937229Seric (void) fclose(SmtpOut); 29410148Seric SmtpIn = SmtpOut = NULL; 29511159Seric SmtpState = SMTP_CLOSED; 29610148Seric 29710148Seric /* and pick up the zombie */ 2987229Seric i = endmailer(SmtpPid, name); 2999391Seric if (i != EX_OK) 3009391Seric syserr("smtpquit %s: stat %d", name, i); 3014865Seric } 3024865Seric /* 3034684Seric ** REPLY -- read arpanet reply 3044684Seric ** 3054684Seric ** Parameters: 30610175Seric ** m -- the mailer we are reading the reply from. 3074684Seric ** 3084684Seric ** Returns: 3094684Seric ** reply code it reads. 3104684Seric ** 3114684Seric ** Side Effects: 3124684Seric ** flushes the mail file. 3134684Seric */ 3144684Seric 31510175Seric reply(m) 31610688Seric MAILER *m; 3174684Seric { 3184865Seric (void) fflush(SmtpOut); 3194684Seric 3207677Seric if (tTd(18, 1)) 3214796Seric printf("reply\n"); 3224796Seric 3237356Seric /* 3247356Seric ** Read the input line, being careful not to hang. 3257356Seric */ 3267356Seric 3274684Seric for (;;) 3284684Seric { 3294684Seric register int r; 3307356Seric register char *p; 3314684Seric 3327685Seric /* actually do the read */ 3339547Seric if (CurEnv->e_xfp != NULL) 3349547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 3357356Seric 33610054Seric /* if we are in the process of closing just give the code */ 33711159Seric if (SmtpState == SMTP_CLOSED) 33810054Seric return (SMTPCLOSING); 33910054Seric 34010054Seric /* get the line from the other side */ 34110054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 34210054Seric if (p == NULL) 34310131Seric { 34410148Seric extern char MsgBuf[]; /* err.c */ 34510148Seric extern char Arpa_TSyserr[]; /* conf.c */ 34610148Seric 34710148Seric message(Arpa_TSyserr, "reply: read error"); 34810420Seric # ifdef DEBUG 34910420Seric /* if debugging, pause so we can see state */ 35010420Seric if (tTd(18, 100)) 35110420Seric pause(); 35210420Seric # endif DEBUG 35310148Seric # ifdef LOG 35410148Seric syslog(LOG_ERR, "%s", &MsgBuf[4]); 35510148Seric # endif LOG 35611159Seric SmtpState = SMTP_CLOSED; 35710175Seric smtpquit("reply error", m); 35810054Seric return (-1); 35910131Seric } 36010054Seric fixcrlf(SmtpReplyBuffer, TRUE); 36110054Seric 362*14900Seric if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 363*14900Seric { 364*14900Seric /* serious error -- log the previous command */ 365*14900Seric if (SmtpMsgBuffer[0] != '\0') 366*14900Seric fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 367*14900Seric SmtpMsgBuffer[0] = '\0'; 368*14900Seric 369*14900Seric /* now log the message as from the other side */ 370*14900Seric fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 371*14900Seric } 372*14900Seric 373*14900Seric /* display the input for verbose mode */ 3747229Seric if (Verbose && !HoldErrs) 3759391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 3767356Seric 3777356Seric /* if continuation is required, we can go on */ 3789391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 3794684Seric continue; 3807356Seric 3817356Seric /* decode the reply code */ 3829391Seric r = atoi(SmtpReplyBuffer); 3837356Seric 3847356Seric /* extra semantics: 0xx codes are "informational" */ 3854684Seric if (r < 100) 3864684Seric continue; 3877356Seric 3889391Seric /* reply code 421 is "Service Shutting Down" */ 38911159Seric if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 3909391Seric { 39110054Seric /* send the quit protocol */ 39211159Seric SmtpState = SMTP_SSD; 39310175Seric smtpquit("SMTP Shutdown", m); 3949391Seric } 3959391Seric 3964684Seric return (r); 3974684Seric } 3984684Seric } 3994796Seric /* 4004865Seric ** SMTPMESSAGE -- send message to server 4014796Seric ** 4024796Seric ** Parameters: 4034796Seric ** f -- format 40410175Seric ** m -- the mailer to control formatting. 4054796Seric ** a, b, c -- parameters 4064796Seric ** 4074796Seric ** Returns: 4084796Seric ** none. 4094796Seric ** 4104796Seric ** Side Effects: 4114865Seric ** writes message to SmtpOut. 4124796Seric */ 4134796Seric 4144865Seric /*VARARGS1*/ 41510175Seric smtpmessage(f, m, a, b, c) 4164796Seric char *f; 41710175Seric MAILER *m; 4184796Seric { 419*14900Seric (void) sprintf(SmtpMsgBuffer, f, a, b, c); 4207677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 421*14900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 42211159Seric if (SmtpOut != NULL) 423*14900Seric fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol); 4244796Seric } 4255182Seric 4265182Seric # endif SMTP 427