14796Seric # include <ctype.h> 24684Seric # include <sysexits.h> 34865Seric # include "sendmail.h" 44684Seric 55182Seric # ifndef SMTP 6*10159Seric SCCSID(@(#)usersmtp.c 3.35 01/05/83 (no SMTP)); 75182Seric # else SMTP 84684Seric 9*10159Seric SCCSID(@(#)usersmtp.c 3.35 01/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 2310054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 2410054Seric FILE *SmtpOut; /* output file */ 2510054Seric FILE *SmtpIn; /* input file */ 2610054Seric int SmtpPid; /* pid of mailer */ 2710054Seric bool SmtpClosing; /* set on a forced close */ 289391Seric /* 294865Seric ** SMTPINIT -- initialize SMTP. 304684Seric ** 314865Seric ** Opens the connection and sends the initial protocol. 324684Seric ** 334684Seric ** Parameters: 344865Seric ** m -- mailer to create connection to. 354865Seric ** pvp -- pointer to parameter vector to pass to 364865Seric ** the mailer. 374865Seric ** ctladdr -- controlling address for this mailer. 384684Seric ** 394684Seric ** Returns: 404865Seric ** appropriate exit status -- EX_OK on success. 414684Seric ** 424684Seric ** Side Effects: 434865Seric ** creates connection and sends initial protocol. 444684Seric */ 454684Seric 464865Seric smtpinit(m, pvp, ctladdr) 474865Seric struct mailer *m; 484865Seric char **pvp; 494865Seric ADDRESS *ctladdr; 504684Seric { 514865Seric register int r; 524865Seric char buf[MAXNAME]; 537685Seric extern char *canonname(); 544684Seric 554865Seric /* 564865Seric ** Open the connection to the mailer. 574865Seric */ 584684Seric 596051Seric SmtpIn = SmtpOut = NULL; 6010148Seric SmtpClosing = FALSE; 614865Seric SmtpPid = openmailer(m, pvp, ctladdr, TRUE, &SmtpOut, &SmtpIn); 626051Seric if (SmtpPid < 0) 636051Seric { 646051Seric # ifdef DEBUG 657677Seric if (tTd(18, 1)) 669391Seric printf("smtpinit: cannot open %s: stat %d errno %d\n", 679391Seric pvp[0], ExitStat, errno); 686051Seric # endif DEBUG 696051Seric return (ExitStat); 706051Seric } 714796Seric 724865Seric /* 734865Seric ** Get the greeting message. 744865Seric ** This should appear spontaneously. 754865Seric */ 764797Seric 774865Seric r = reply(); 788005Seric if (r < 0 || REPLYTYPE(r) != 2) 794865Seric return (EX_TEMPFAIL); 804684Seric 814865Seric /* 824976Seric ** Send the HELO command. 837963Seric ** My mother taught me to always introduce myself. 844976Seric */ 854976Seric 864976Seric smtpmessage("HELO %s", HostName); 874976Seric r = reply(); 888005Seric if (r < 0) 898005Seric return (EX_TEMPFAIL); 908005Seric else if (REPLYTYPE(r) == 5) 914976Seric return (EX_UNAVAILABLE); 927963Seric else if (REPLYTYPE(r) != 2) 934976Seric return (EX_TEMPFAIL); 944976Seric 954976Seric /* 969315Seric ** If this is expected to be another sendmail, send some internal 979315Seric ** commands. 989315Seric */ 999315Seric 1009315Seric if (bitset(M_INTERNAL, m->m_flags)) 1019315Seric { 1029315Seric /* tell it to be verbose */ 1039315Seric smtpmessage("VERB"); 1049315Seric r = reply(); 1059315Seric if (r < 0) 1069315Seric return (EX_TEMPFAIL); 1079315Seric 1089315Seric /* tell it we will be sending one transaction only */ 1099315Seric smtpmessage("ONEX"); 1109315Seric r = reply(); 1119315Seric if (r < 0) 1129315Seric return (EX_TEMPFAIL); 1139315Seric } 1149315Seric 1159315Seric /* 1164865Seric ** Send the MAIL command. 1174865Seric ** Designates the sender. 1184865Seric */ 1194796Seric 1206980Seric expand("$g", buf, &buf[sizeof buf - 1], CurEnv); 1218436Seric if (CurEnv->e_from.q_mailer == LocalMailer || 1228436Seric !bitset(M_FULLSMTP, m->m_flags)) 1238436Seric { 1248436Seric smtpmessage("MAIL From:<%s>", canonname(buf, 1)); 1258436Seric } 1268436Seric else 1278436Seric { 1288436Seric smtpmessage("MAIL From:<@%s%c%s>", HostName, 1298436Seric buf[0] == '@' ? ',' : ':', canonname(buf, 1)); 1308436Seric } 1314865Seric r = reply(); 1328005Seric if (r < 0 || REPLYTYPE(r) == 4) 1334865Seric return (EX_TEMPFAIL); 1347963Seric else if (r == 250) 1357963Seric return (EX_OK); 1367963Seric else if (r == 552) 1377963Seric return (EX_UNAVAILABLE); 1387964Seric return (EX_PROTOCOL); 1394684Seric } 1404684Seric /* 1414976Seric ** SMTPRCPT -- designate recipient. 1424797Seric ** 1434797Seric ** Parameters: 1444865Seric ** to -- address of recipient. 1454797Seric ** 1464797Seric ** Returns: 1474865Seric ** exit status corresponding to recipient status. 1484797Seric ** 1494797Seric ** Side Effects: 1504865Seric ** Sends the mail via SMTP. 1514797Seric */ 1524797Seric 1534976Seric smtprcpt(to) 1544865Seric ADDRESS *to; 1554797Seric { 1564797Seric register int r; 1577685Seric extern char *canonname(); 1584797Seric 1598354Seric smtpmessage("RCPT To:<%s>", canonname(to->q_user, 2)); 1604865Seric 1614797Seric r = reply(); 1628005Seric if (r < 0 || REPLYTYPE(r) == 4) 1634865Seric return (EX_TEMPFAIL); 1647963Seric else if (REPLYTYPE(r) == 2) 1657963Seric return (EX_OK); 1667964Seric else if (r == 550 || r == 551 || r == 553) 1677964Seric return (EX_NOUSER); 1687964Seric else if (r == 552 || r == 554) 1697964Seric return (EX_UNAVAILABLE); 1707964Seric return (EX_PROTOCOL); 1714797Seric } 1724797Seric /* 1734865Seric ** SMTPFINISH -- finish up sending all the SMTP protocol. 1744684Seric ** 1754684Seric ** Parameters: 1764865Seric ** m -- mailer being sent to. 1776980Seric ** e -- the envelope for this message. 1784684Seric ** 1794684Seric ** Returns: 1804976Seric ** exit status corresponding to DATA command. 1814684Seric ** 1824684Seric ** Side Effects: 1834865Seric ** none. 1844684Seric */ 1854684Seric 1866980Seric smtpfinish(m, e) 1874865Seric struct mailer *m; 1886980Seric register ENVELOPE *e; 1894684Seric { 1904684Seric register int r; 1914684Seric 1924797Seric /* 1934797Seric ** Send the data. 1944797Seric ** Dot hiding is done here. 1954797Seric */ 1964797Seric 1974865Seric smtpmessage("DATA"); 1984796Seric r = reply(); 1998005Seric if (r < 0 || REPLYTYPE(r) == 4) 2004797Seric return (EX_TEMPFAIL); 2017963Seric else if (r == 554) 2027963Seric return (EX_UNAVAILABLE); 2037963Seric else if (r != 354) 2047964Seric return (EX_PROTOCOL); 20510067Seric (*e->e_puthdr)(SmtpOut, m, CurEnv, TRUE); 20610067Seric fprintf(SmtpOut, "\r\n"); 20710067Seric (*e->e_putbody)(SmtpOut, m, TRUE, CurEnv, TRUE); 2084865Seric smtpmessage("."); 2094796Seric r = reply(); 2108005Seric if (r < 0 || REPLYTYPE(r) == 4) 2114797Seric return (EX_TEMPFAIL); 2127963Seric else if (r == 250) 2137963Seric return (EX_OK); 2147963Seric else if (r == 552 || r == 554) 2157963Seric return (EX_UNAVAILABLE); 2167964Seric return (EX_PROTOCOL); 2174684Seric } 2184684Seric /* 2194865Seric ** SMTPQUIT -- close the SMTP connection. 2204865Seric ** 2214865Seric ** Parameters: 2224865Seric ** name -- name of mailer we are quitting. 2234865Seric ** 2244865Seric ** Returns: 2254865Seric ** none. 2264865Seric ** 2274865Seric ** Side Effects: 2284865Seric ** sends the final protocol and closes the connection. 2294865Seric */ 2304865Seric 2319391Seric smtpquit(name) 2324865Seric char *name; 2334865Seric { 2349391Seric int i; 2354865Seric 23610148Seric /* if the connection is already closed, don't bother */ 23710148Seric if (SmtpIn == NULL) 23810148Seric return; 23910148Seric 24010148Seric /* send the quit message if not a forced quit */ 24110148Seric if (!SmtpClosing) 2429391Seric { 24310148Seric smtpmessage("QUIT"); 24410148Seric (void) reply(); 245*10159Seric if (SmtpClosing) 246*10159Seric return; 2479391Seric } 2489391Seric 24910148Seric /* now actually close the connection */ 2507229Seric (void) fclose(SmtpIn); 2517229Seric (void) fclose(SmtpOut); 25210148Seric SmtpIn = SmtpOut = NULL; 25310148Seric 25410148Seric /* and pick up the zombie */ 2557229Seric i = endmailer(SmtpPid, name); 2569391Seric if (i != EX_OK) 2579391Seric syserr("smtpquit %s: stat %d", name, i); 2584865Seric } 2594865Seric /* 2604684Seric ** REPLY -- read arpanet reply 2614684Seric ** 2624684Seric ** Parameters: 2634796Seric ** none. 2644684Seric ** 2654684Seric ** Returns: 2664684Seric ** reply code it reads. 2674684Seric ** 2684684Seric ** Side Effects: 2694684Seric ** flushes the mail file. 2704684Seric */ 2714684Seric 2724796Seric reply() 2734684Seric { 2744865Seric (void) fflush(SmtpOut); 2754684Seric 2767677Seric if (tTd(18, 1)) 2774796Seric printf("reply\n"); 2784796Seric 2797356Seric /* 2807356Seric ** Read the input line, being careful not to hang. 2817356Seric */ 2827356Seric 2834684Seric for (;;) 2844684Seric { 2854684Seric register int r; 2867356Seric register char *p; 2874684Seric 2887685Seric /* actually do the read */ 2899547Seric if (CurEnv->e_xfp != NULL) 2909547Seric (void) fflush(CurEnv->e_xfp); /* for debugging */ 2917356Seric 29210054Seric /* if we are in the process of closing just give the code */ 29310054Seric if (SmtpClosing) 29410054Seric return (SMTPCLOSING); 29510054Seric 29610054Seric /* get the line from the other side */ 29710054Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 29810054Seric if (p == NULL) 29910131Seric { 30010148Seric extern char MsgBuf[]; /* err.c */ 30110148Seric extern char Arpa_TSyserr[]; /* conf.c */ 30210148Seric 30310148Seric message(Arpa_TSyserr, "reply: read error"); 30410148Seric # ifdef LOG 30510148Seric syslog(LOG_ERR, "%s", &MsgBuf[4]); 30610148Seric # endif LOG 30710148Seric SmtpClosing = TRUE; 30810148Seric smtpquit("reply error"); 30910054Seric return (-1); 31010131Seric } 31110054Seric fixcrlf(SmtpReplyBuffer, TRUE); 31210054Seric 3137356Seric /* log the input in the transcript for future error returns */ 3147229Seric if (Verbose && !HoldErrs) 3159391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 31610131Seric else if (CurEnv->e_xfp != NULL) 3179547Seric fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer); 3187356Seric 3197356Seric /* if continuation is required, we can go on */ 3209391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 3214684Seric continue; 3227356Seric 3237356Seric /* decode the reply code */ 3249391Seric r = atoi(SmtpReplyBuffer); 3257356Seric 3267356Seric /* extra semantics: 0xx codes are "informational" */ 3274684Seric if (r < 100) 3284684Seric continue; 3297356Seric 3309391Seric /* reply code 421 is "Service Shutting Down" */ 33110054Seric if (r == SMTPCLOSING) 3329391Seric { 33310054Seric /* send the quit protocol */ 3349391Seric smtpquit("SMTP Shutdown"); 3359391Seric SmtpClosing = TRUE; 3369391Seric } 3379391Seric 3384684Seric return (r); 3394684Seric } 3404684Seric } 3414796Seric /* 3424865Seric ** SMTPMESSAGE -- send message to server 3434796Seric ** 3444796Seric ** Parameters: 3454796Seric ** f -- format 3464796Seric ** a, b, c -- parameters 3474796Seric ** 3484796Seric ** Returns: 3494796Seric ** none. 3504796Seric ** 3514796Seric ** Side Effects: 3524865Seric ** writes message to SmtpOut. 3534796Seric */ 3544796Seric 3554865Seric /*VARARGS1*/ 3564865Seric smtpmessage(f, a, b, c) 3574796Seric char *f; 3584796Seric { 3594796Seric char buf[100]; 3604796Seric 3614865Seric (void) sprintf(buf, f, a, b, c); 3627677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 3638237Seric nmessage(Arpa_Info, ">>> %s", buf); 36410131Seric else if (CurEnv->e_xfp != NULL) 3659547Seric fprintf(CurEnv->e_xfp, ">>> %s\n", buf); 3669391Seric if (!SmtpClosing) 3679391Seric fprintf(SmtpOut, "%s\r\n", buf); 3684796Seric } 3695182Seric 3705182Seric # endif SMTP 371