122716Sdist /* 268839Seric * Copyright (c) 1983, 1995 Eric P. Allman 362532Sbostic * Copyright (c) 1988, 1993 462532Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642831Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822716Sdist 933731Sbostic # include "sendmail.h" 1022716Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*68884Seric static char sccsid[] = "@(#)usersmtp.c 8.49 (Berkeley) 04/25/95 (with SMTP)"; 1433731Sbostic #else 15*68884Seric static char sccsid[] = "@(#)usersmtp.c 8.49 (Berkeley) 04/25/95 (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 */ 3864071Seric bool SmtpNeedIntro; /* need "while talking" in transcript */ 3958671Seric 4058671Seric #ifdef __STDC__ 4158671Seric extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 4258671Seric #endif 439391Seric /* 444865Seric ** SMTPINIT -- initialize SMTP. 454684Seric ** 464865Seric ** Opens the connection and sends the initial protocol. 474684Seric ** 484684Seric ** Parameters: 494865Seric ** m -- mailer to create connection to. 504865Seric ** pvp -- pointer to parameter vector to pass to 514865Seric ** the mailer. 524684Seric ** 534684Seric ** Returns: 5454967Seric ** none. 554684Seric ** 564684Seric ** Side Effects: 574865Seric ** creates connection and sends initial protocol. 584684Seric */ 594684Seric 6054967Seric smtpinit(m, mci, e) 614865Seric struct mailer *m; 6254967Seric register MCI *mci; 6353751Seric ENVELOPE *e; 644684Seric { 654865Seric register int r; 6658957Seric register char *p; 6760210Seric extern void esmtp_check(); 6859285Seric extern void helo_options(); 694684Seric 7063753Seric if (tTd(18, 1)) 7157379Seric { 7257379Seric printf("smtpinit "); 7364731Seric mci_dump(mci, FALSE); 7457379Seric } 7557379Seric 764865Seric /* 774865Seric ** Open the connection to the mailer. 784865Seric */ 794684Seric 8021065Seric SmtpError[0] = '\0'; 8157379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 8268100Seric if (CurHostName == NULL) 8368100Seric CurHostName = MyHostName; 8464071Seric SmtpNeedIntro = TRUE; 8554967Seric switch (mci->mci_state) 866051Seric { 8754967Seric case MCIS_ACTIVE: 8854967Seric /* need to clear old information */ 8954967Seric smtprset(m, mci, e); 9057734Seric /* fall through */ 9115139Seric 9254967Seric case MCIS_OPEN: 9354967Seric return; 9454967Seric 9554967Seric case MCIS_ERROR: 9654967Seric case MCIS_SSD: 9754967Seric /* shouldn't happen */ 9854967Seric smtpquit(m, mci, e); 9957734Seric /* fall through */ 10054967Seric 10154967Seric case MCIS_CLOSED: 10258151Seric syserr("451 smtpinit: state CLOSED"); 10354967Seric return; 10454967Seric 10554967Seric case MCIS_OPENING: 10654967Seric break; 1076051Seric } 1084796Seric 10954967Seric mci->mci_state = MCIS_OPENING; 11054967Seric 1114865Seric /* 1124865Seric ** Get the greeting message. 11314913Seric ** This should appear spontaneously. Give it five minutes to 11414886Seric ** happen. 1154865Seric */ 1164797Seric 11761093Seric SmtpPhase = mci->mci_phase = "client greeting"; 11853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11960210Seric r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 12064750Seric if (r < 0 || REPLYTYPE(r) == 4) 12152104Seric goto tempfail1; 12264750Seric if (REPLYTYPE(r) != 2) 12364750Seric goto unavailable; 1244684Seric 1254865Seric /* 1264976Seric ** Send the HELO command. 1277963Seric ** My mother taught me to always introduce myself. 1284976Seric */ 1294976Seric 13059285Seric if (bitnset(M_ESMTP, m->m_flags)) 13159285Seric mci->mci_flags |= MCIF_ESMTP; 13259285Seric 13359285Seric tryhelo: 13459285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 13559285Seric { 13659285Seric smtpmessage("EHLO %s", m, mci, MyHostName); 13761093Seric SmtpPhase = mci->mci_phase = "client EHLO"; 13859285Seric } 13959285Seric else 14059285Seric { 14159285Seric smtpmessage("HELO %s", m, mci, MyHostName); 14261093Seric SmtpPhase = mci->mci_phase = "client HELO"; 14359285Seric } 14453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 14559285Seric r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 1468005Seric if (r < 0) 14752104Seric goto tempfail1; 1488005Seric else if (REPLYTYPE(r) == 5) 14959285Seric { 15059285Seric if (bitset(MCIF_ESMTP, mci->mci_flags)) 15159285Seric { 15259285Seric /* try old SMTP instead */ 15359285Seric mci->mci_flags &= ~MCIF_ESMTP; 15459285Seric goto tryhelo; 15559285Seric } 15614913Seric goto unavailable; 15759285Seric } 1587963Seric else if (REPLYTYPE(r) != 2) 15952104Seric goto tempfail1; 1604976Seric 1614976Seric /* 16258957Seric ** Check to see if we actually ended up talking to ourself. 16358957Seric ** This means we didn't know about an alias or MX, or we managed 16458957Seric ** to connect to an echo server. 16558957Seric */ 16658957Seric 16759026Seric p = strchr(&SmtpReplyBuffer[4], ' '); 16858957Seric if (p != NULL) 16961707Seric *p = '\0'; 17067472Seric if (!bitnset(M_NOLOOPCHECK, m->m_flags) && 17167472Seric strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 17258957Seric { 17358957Seric syserr("553 %s config error: mail loops back to myself", 17458957Seric MyHostName); 17558957Seric mci->mci_exitstat = EX_CONFIG; 17658957Seric mci->mci_errno = 0; 17758957Seric smtpquit(m, mci, e); 17858957Seric return; 17958957Seric } 18058957Seric 18158957Seric /* 1829315Seric ** If this is expected to be another sendmail, send some internal 1839315Seric ** commands. 1849315Seric */ 1859315Seric 18610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1879315Seric { 1889315Seric /* tell it to be verbose */ 18953751Seric smtpmessage("VERB", m, mci); 19059285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 1919315Seric if (r < 0) 19252104Seric goto tempfail2; 1939315Seric } 1949315Seric 19565057Seric if (mci->mci_state != MCIS_CLOSED) 19665057Seric { 19765057Seric mci->mci_state = MCIS_OPEN; 19865057Seric return; 19965057Seric } 20053751Seric 20165057Seric /* got a 421 error code during startup */ 20265057Seric 20353751Seric tempfail1: 20453751Seric tempfail2: 20553751Seric mci->mci_exitstat = EX_TEMPFAIL; 20657379Seric if (mci->mci_errno == 0) 20757379Seric mci->mci_errno = errno; 20857379Seric if (mci->mci_state != MCIS_CLOSED) 20957379Seric smtpquit(m, mci, e); 21054967Seric return; 21153751Seric 21253751Seric unavailable: 21353751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21453751Seric mci->mci_errno = errno; 21553751Seric smtpquit(m, mci, e); 21654967Seric return; 21753751Seric } 21859285Seric /* 21960210Seric ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 22060210Seric ** 22160210Seric ** Parameters: 22260210Seric ** line -- the response line. 22367893Seric ** firstline -- set if this is the first line of the reply. 22460210Seric ** m -- the mailer. 22560210Seric ** mci -- the mailer connection info. 22660210Seric ** e -- the envelope. 22760210Seric ** 22860210Seric ** Returns: 22960210Seric ** none. 23060210Seric */ 23160210Seric 23260210Seric void 23367893Seric esmtp_check(line, firstline, m, mci, e) 23460210Seric char *line; 23567893Seric bool firstline; 23660210Seric MAILER *m; 23760210Seric register MCI *mci; 23860210Seric ENVELOPE *e; 23960210Seric { 24068559Seric register char *l; 24168559Seric 24268559Seric for (l = line; (l = strchr(++l, 'E')) != NULL; ) 24367893Seric { 24468559Seric if (strncmp(l, "ESMTP ", 6) == 0) 24567893Seric { 24667893Seric mci->mci_flags |= MCIF_ESMTP; 24767893Seric break; 24867893Seric } 24967893Seric } 25068559Seric for (l = line; (l = strchr(++l, '8')) != NULL; ) 25168559Seric { 25268559Seric if (strncmp(l, "8BIT OK", 7) == 0) 25368559Seric { 25468559Seric mci->mci_flags |= MCIF_8BITOK; 25568559Seric break; 25668559Seric } 25768559Seric } 25860210Seric } 25960210Seric /* 26059285Seric ** HELO_OPTIONS -- process the options on a HELO line. 26159285Seric ** 26259285Seric ** Parameters: 26359285Seric ** line -- the response line. 26467893Seric ** firstline -- set if this is the first line of the reply. 26559285Seric ** m -- the mailer. 26659285Seric ** mci -- the mailer connection info. 26759285Seric ** e -- the envelope. 26859285Seric ** 26959285Seric ** Returns: 27059285Seric ** none. 27159285Seric */ 27253751Seric 27359285Seric void 27467893Seric helo_options(line, firstline, m, mci, e) 27559285Seric char *line; 27667893Seric bool firstline; 27759285Seric MAILER *m; 27859285Seric register MCI *mci; 27959285Seric ENVELOPE *e; 28059285Seric { 28159285Seric register char *p; 28259285Seric 28367971Seric if (firstline) 28467893Seric return; 28567893Seric 28668706Seric if (strlen(line) < (SIZE_T) 5) 28759285Seric return; 28859285Seric line += 4; 28959285Seric p = strchr(line, ' '); 29059285Seric if (p != NULL) 29159285Seric *p++ = '\0'; 29259285Seric if (strcasecmp(line, "size") == 0) 29359285Seric { 29459285Seric mci->mci_flags |= MCIF_SIZE; 29559285Seric if (p != NULL) 29659285Seric mci->mci_maxsize = atol(p); 29759285Seric } 29859285Seric else if (strcasecmp(line, "8bitmime") == 0) 29965870Seric { 30059285Seric mci->mci_flags |= MCIF_8BITMIME; 30165870Seric mci->mci_flags &= ~MCIF_7BIT; 30265870Seric } 30359285Seric else if (strcasecmp(line, "expn") == 0) 30459285Seric mci->mci_flags |= MCIF_EXPN; 30568606Seric else if (strcasecmp(line, "x-dsn-03") == 0) 30667880Seric mci->mci_flags |= MCIF_DSN; 30759285Seric } 30859285Seric /* 30959285Seric ** SMTPMAILFROM -- send MAIL command 31059285Seric ** 31159285Seric ** Parameters: 31259285Seric ** m -- the mailer. 31359285Seric ** mci -- the mailer connection structure. 31459285Seric ** e -- the envelope (including the sender to specify). 31559285Seric */ 31659285Seric 31753751Seric smtpmailfrom(m, mci, e) 31853751Seric struct mailer *m; 31954967Seric MCI *mci; 32053751Seric ENVELOPE *e; 32153751Seric { 32253751Seric int r; 32365494Seric char *bufp; 32467887Seric char *bodytype; 32568528Seric char buf[MAXNAME + 1]; 32659285Seric char optbuf[MAXLINE]; 32753751Seric 32863753Seric if (tTd(18, 2)) 32957943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 33057943Seric 33159285Seric /* set up appropriate options to include */ 33264254Seric if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 33359285Seric sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 33459285Seric else 33559285Seric strcpy(optbuf, ""); 33659285Seric 33767887Seric bodytype = e->e_bodytype; 33867887Seric if (bitset(MCIF_8BITMIME, mci->mci_flags)) 33967417Seric { 34067887Seric if (bodytype == NULL && 34167887Seric bitset(MM_MIME8BIT, MimeMode) && 34267887Seric bitset(EF_HAS8BIT, e->e_flags) && 34367887Seric !bitnset(M_8BITS, m->m_flags)) 34467887Seric bodytype = "8BITMIME"; 34567887Seric if (bodytype != NULL) 34667417Seric { 34767417Seric strcat(optbuf, " BODY="); 34867887Seric strcat(optbuf, bodytype); 34967417Seric } 35067417Seric } 35167995Seric else if (bitnset(M_8BITS, m->m_flags) || 35267995Seric !bitset(EF_HAS8BIT, e->e_flags) || 35367995Seric (e->e_bodytype != NULL && 35467995Seric strcasecmp(e->e_bodytype, "7bit") == 0)) 35567887Seric { 35667887Seric /* just pass it through */ 35767887Seric } 35867887Seric else if (bitset(MM_CVTMIME, MimeMode) && 359*68884Seric (!bitset(MM_PASS8BIT, MimeMode) || 360*68884Seric bitset(EF_IS_MIME, e->e_flags))) 36167887Seric { 36267887Seric /* must convert from 8bit MIME format to 7bit encoded */ 36367887Seric mci->mci_flags |= MCIF_CVT8TO7; 36467887Seric } 36567887Seric else if (!bitset(MM_PASS8BIT, MimeMode)) 36667887Seric { 36767887Seric /* cannot just send a 8-bit version */ 36867887Seric usrerr("%s does not support 8BITMIME", mci->mci_host); 36968857Seric mci->mci_status = "5.6.3"; 37067887Seric return EX_DATAERR; 37167887Seric } 37267417Seric 37367963Seric if (bitset(MCIF_DSN, mci->mci_flags)) 37467880Seric { 37567963Seric if (e->e_envid != NULL) 37667963Seric { 37767963Seric strcat(optbuf, " ENVID="); 37867963Seric strcat(optbuf, e->e_envid); 37967963Seric } 38068559Seric 38168559Seric /* RET= parameter */ 38268559Seric if (bitset(EF_RET_PARAM, e->e_flags)) 38368559Seric { 38468559Seric strcat(optbuf, " RET="); 38568559Seric if (bitset(EF_NO_BODY_RETN, e->e_flags)) 38668559Seric strcat(optbuf, "HDRS"); 38768559Seric else 38868559Seric strcat(optbuf, "FULL"); 38968559Seric } 39067880Seric } 39167880Seric 3929315Seric /* 3934865Seric ** Send the MAIL command. 3944865Seric ** Designates the sender. 3954865Seric */ 3964796Seric 39753751Seric mci->mci_state = MCIS_ACTIVE; 39853751Seric 39959540Seric if (bitset(EF_RESPONSE, e->e_flags) && 40059540Seric !bitnset(M_NO_NULL_FROM, m->m_flags)) 40158680Seric (void) strcpy(buf, ""); 40258680Seric else 40368529Seric expand("\201g", buf, sizeof buf, e); 40465494Seric if (buf[0] == '<') 40565494Seric { 40665494Seric /* strip off <angle brackets> (put back on below) */ 40765494Seric bufp = &buf[strlen(buf) - 1]; 40865494Seric if (*bufp == '>') 40965494Seric *bufp = '\0'; 41065494Seric bufp = &buf[1]; 41165494Seric } 41265494Seric else 41365494Seric bufp = buf; 41467472Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 41510688Seric !bitnset(M_FROMPATH, m->m_flags)) 4168436Seric { 41765494Seric smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 4188436Seric } 4198436Seric else 4208436Seric { 42159285Seric smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 42265494Seric *bufp == '@' ? ',' : ':', bufp, optbuf); 4238436Seric } 42461093Seric SmtpPhase = mci->mci_phase = "client MAIL"; 42553751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 42659285Seric r = reply(m, mci, e, TimeOuts.to_mail, NULL); 42768811Seric if (r < 0 || r == 421) 42853751Seric { 42968811Seric /* communications failure/service shutting down */ 43053751Seric mci->mci_exitstat = EX_TEMPFAIL; 43153751Seric mci->mci_errno = errno; 43253751Seric smtpquit(m, mci, e); 43353751Seric return EX_TEMPFAIL; 43453751Seric } 43568811Seric else if (REPLYTYPE(r) == 4) 43668811Seric { 43768811Seric return EX_TEMPFAIL; 43868811Seric } 4397963Seric else if (r == 250) 44053751Seric { 44153751Seric return EX_OK; 44253751Seric } 44368857Seric else if (r == 501) 44468075Seric { 44568857Seric /* syntax error in arguments */ 44668857Seric mci->mci_status = "5.5.2"; 44768075Seric return EX_DATAERR; 44868075Seric } 44968857Seric else if (r == 553) 45068857Seric { 45168857Seric /* mailbox name not allowed */ 45268857Seric mci->mci_status = "5.1.3"; 45368857Seric return EX_DATAERR; 45468857Seric } 4557963Seric else if (r == 552) 45653751Seric { 45768811Seric /* exceeded storage allocation */ 45868857Seric mci->mci_status = "5.2.2"; 45953751Seric return EX_UNAVAILABLE; 46053751Seric } 46114913Seric 46258008Seric #ifdef LOG 46358020Seric if (LogLevel > 1) 46458008Seric { 46567860Seric syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s", 46667860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 46758008Seric } 46858008Seric #endif 46958008Seric 47014913Seric /* protocol error -- close up */ 47153751Seric smtpquit(m, mci, e); 47253751Seric return EX_PROTOCOL; 4734684Seric } 4744684Seric /* 4754976Seric ** SMTPRCPT -- designate recipient. 4764797Seric ** 4774797Seric ** Parameters: 4784865Seric ** to -- address of recipient. 47910175Seric ** m -- the mailer we are sending to. 48057379Seric ** mci -- the connection info for this transaction. 48157379Seric ** e -- the envelope for this transaction. 4824797Seric ** 4834797Seric ** Returns: 4844865Seric ** exit status corresponding to recipient status. 4854797Seric ** 4864797Seric ** Side Effects: 4874865Seric ** Sends the mail via SMTP. 4884797Seric */ 4894797Seric 49053751Seric smtprcpt(to, m, mci, e) 4914865Seric ADDRESS *to; 49210175Seric register MAILER *m; 49354967Seric MCI *mci; 49453751Seric ENVELOPE *e; 4954797Seric { 4964797Seric register int r; 49767880Seric char optbuf[MAXLINE]; 49868857Seric extern char *smtptodsn(); 4994797Seric 50067880Seric strcpy(optbuf, ""); 50167880Seric if (bitset(MCIF_DSN, mci->mci_flags)) 50267880Seric { 50367987Seric /* NOTIFY= parameter */ 50468603Seric if (bitset(QHASNOTIFY, to->q_flags) && 50568603Seric bitset(QPRIMARY, to->q_flags)) 50667880Seric { 50768595Seric bool firstone = TRUE; 50868595Seric 50968595Seric strcat(optbuf, " NOTIFY="); 51068595Seric if (bitset(QPINGONSUCCESS, to->q_flags)) 51168595Seric { 51268595Seric strcat(optbuf, "SUCCESS"); 51368595Seric firstone = FALSE; 51468595Seric } 51568595Seric if (bitset(QPINGONFAILURE, to->q_flags)) 51668595Seric { 51768595Seric if (!firstone) 51868595Seric strcat(optbuf, ","); 51968595Seric strcat(optbuf, "FAILURE"); 52068595Seric firstone = FALSE; 52168595Seric } 52268595Seric if (bitset(QPINGONDELAY, to->q_flags)) 52368595Seric { 52468595Seric if (!firstone) 52568595Seric strcat(optbuf, ","); 52668595Seric strcat(optbuf, "DELAY"); 52768595Seric firstone = FALSE; 52868595Seric } 52968595Seric if (firstone) 53068595Seric strcat(optbuf, "NEVER"); 53167880Seric } 53267963Seric 53367987Seric /* ORCPT= parameter */ 53467987Seric if (to->q_orcpt != NULL) 53567987Seric { 53667987Seric strcat(optbuf, " ORCPT="); 53767987Seric strcat(optbuf, to->q_orcpt); 53867987Seric } 53967880Seric } 5404865Seric 54167880Seric smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); 54267880Seric 54361093Seric SmtpPhase = mci->mci_phase = "client RCPT"; 54453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 54559285Seric r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 54668867Seric to->q_rstatus = newstr(SmtpReplyBuffer); 54768857Seric to->q_status = smtptodsn(r); 5488005Seric if (r < 0 || REPLYTYPE(r) == 4) 54968857Seric return EX_TEMPFAIL; 5507963Seric else if (REPLYTYPE(r) == 2) 55168857Seric return EX_OK; 5527964Seric else if (r == 550 || r == 551 || r == 553) 55368857Seric return EX_NOUSER; 5547964Seric else if (r == 552 || r == 554) 55568857Seric return EX_UNAVAILABLE; 55658008Seric 55758008Seric #ifdef LOG 55858020Seric if (LogLevel > 1) 55958008Seric { 56067860Seric syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s", 56167860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 56258008Seric } 56358008Seric #endif 56458008Seric 5657964Seric return (EX_PROTOCOL); 5664797Seric } 5674797Seric /* 56810175Seric ** SMTPDATA -- send the data and clean up the transaction. 5694684Seric ** 5704684Seric ** Parameters: 5714865Seric ** m -- mailer being sent to. 5726980Seric ** e -- the envelope for this message. 5734684Seric ** 5744684Seric ** Returns: 5754976Seric ** exit status corresponding to DATA command. 5764684Seric ** 5774684Seric ** Side Effects: 5784865Seric ** none. 5794684Seric */ 5804684Seric 58163753Seric static jmp_buf CtxDataTimeout; 58268433Seric static void datatimeout(); 58363753Seric 58453740Seric smtpdata(m, mci, e) 5854865Seric struct mailer *m; 58654967Seric register MCI *mci; 5876980Seric register ENVELOPE *e; 5884684Seric { 5894684Seric register int r; 59063753Seric register EVENT *ev; 59163753Seric time_t timeout; 5924684Seric 5934797Seric /* 5944797Seric ** Send the data. 59510175Seric ** First send the command and check that it is ok. 59610175Seric ** Then send the data. 59710175Seric ** Follow it up with a dot to terminate. 59810175Seric ** Finally get the results of the transaction. 5994797Seric */ 6004797Seric 60110175Seric /* send the command and check ok to proceed */ 60253751Seric smtpmessage("DATA", m, mci); 60361093Seric SmtpPhase = mci->mci_phase = "client DATA 354"; 60453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 60559285Seric r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 6068005Seric if (r < 0 || REPLYTYPE(r) == 4) 60757990Seric { 60857990Seric smtpquit(m, mci, e); 6094797Seric return (EX_TEMPFAIL); 61057990Seric } 6117963Seric else if (r == 554) 61257990Seric { 61357990Seric smtprset(m, mci, e); 6147963Seric return (EX_UNAVAILABLE); 61557990Seric } 6167963Seric else if (r != 354) 61757990Seric { 61858008Seric #ifdef LOG 61958020Seric if (LogLevel > 1) 62058008Seric { 62167860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s", 62267860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 62358008Seric } 62458008Seric #endif 62557990Seric smtprset(m, mci, e); 6267964Seric return (EX_PROTOCOL); 62757990Seric } 62810175Seric 62963757Seric /* 63063757Seric ** Set timeout around data writes. Make it at least large 63163757Seric ** enough for DNS timeouts on all recipients plus some fudge 63263757Seric ** factor. The main thing is that it should not be infinite. 63363757Seric */ 63463757Seric 63563753Seric if (setjmp(CtxDataTimeout) != 0) 63663753Seric { 63763753Seric mci->mci_errno = errno; 63863753Seric mci->mci_exitstat = EX_TEMPFAIL; 63963753Seric mci->mci_state = MCIS_ERROR; 64063753Seric syserr("451 timeout writing message to %s", mci->mci_host); 64163753Seric smtpquit(m, mci, e); 64263753Seric return EX_TEMPFAIL; 64363753Seric } 64463753Seric 64563787Seric timeout = e->e_msgsize / 16; 64668740Seric if (timeout < (time_t) 600) 64768740Seric timeout = (time_t) 600; 64868740Seric timeout += e->e_nrcpts * 300; 64963753Seric ev = setevent(timeout, datatimeout, 0); 65063753Seric 65167546Seric /* 65267546Seric ** Output the actual message. 65367546Seric */ 65467546Seric 65568228Seric (*e->e_puthdr)(mci, e->e_header, e); 65668228Seric (*e->e_putbody)(mci, e, NULL); 65710175Seric 65867546Seric /* 65967546Seric ** Cleanup after sending message. 66067546Seric */ 66167546Seric 66263753Seric clrevent(ev); 66363753Seric 66464718Seric if (ferror(mci->mci_out)) 66564718Seric { 66664718Seric /* error during processing -- don't send the dot */ 66764718Seric mci->mci_errno = EIO; 66864718Seric mci->mci_exitstat = EX_IOERR; 66964718Seric mci->mci_state = MCIS_ERROR; 67064718Seric smtpquit(m, mci, e); 67164718Seric return EX_IOERR; 67264718Seric } 67364718Seric 67410175Seric /* terminate the message */ 67553740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 67663753Seric if (TrafficLogFile != NULL) 67763753Seric fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 67858120Seric if (Verbose) 67958151Seric nmessage(">>> ."); 68010175Seric 68110175Seric /* check for the results of the transaction */ 68261093Seric SmtpPhase = mci->mci_phase = "client DATA 250"; 68353751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 68459285Seric r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 68553751Seric if (r < 0) 68657990Seric { 68757990Seric smtpquit(m, mci, e); 6884797Seric return (EX_TEMPFAIL); 68957990Seric } 69053751Seric mci->mci_state = MCIS_OPEN; 69158917Seric e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 69253751Seric if (REPLYTYPE(r) == 4) 69353751Seric return (EX_TEMPFAIL); 6947963Seric else if (r == 250) 6957963Seric return (EX_OK); 6967963Seric else if (r == 552 || r == 554) 6977963Seric return (EX_UNAVAILABLE); 69858008Seric #ifdef LOG 69958020Seric if (LogLevel > 1) 70058008Seric { 70167860Seric syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s", 70267860Seric e->e_id, mci->mci_host, SmtpReplyBuffer); 70358008Seric } 70458008Seric #endif 7057964Seric return (EX_PROTOCOL); 7064684Seric } 70763753Seric 70863753Seric 70968433Seric static void 71063753Seric datatimeout() 71163753Seric { 71263753Seric longjmp(CtxDataTimeout, 1); 71363753Seric } 7144684Seric /* 7154865Seric ** SMTPQUIT -- close the SMTP connection. 7164865Seric ** 7174865Seric ** Parameters: 71815535Seric ** m -- a pointer to the mailer. 7194865Seric ** 7204865Seric ** Returns: 7214865Seric ** none. 7224865Seric ** 7234865Seric ** Side Effects: 7244865Seric ** sends the final protocol and closes the connection. 7254865Seric */ 7264865Seric 72753751Seric smtpquit(m, mci, e) 72853751Seric register MAILER *m; 72954967Seric register MCI *mci; 73053751Seric ENVELOPE *e; 7314865Seric { 73264822Seric bool oldSuprErrs = SuprErrs; 7334865Seric 73464822Seric /* 73564822Seric ** Suppress errors here -- we may be processing a different 73664822Seric ** job when we do the quit connection, and we don't want the 73764822Seric ** new job to be penalized for something that isn't it's 73864822Seric ** problem. 73964822Seric */ 74064822Seric 74164822Seric SuprErrs = TRUE; 74264822Seric 74354967Seric /* send the quit message if we haven't gotten I/O error */ 74453751Seric if (mci->mci_state != MCIS_ERROR) 7459391Seric { 74661093Seric SmtpPhase = "client QUIT"; 74753751Seric smtpmessage("QUIT", m, mci); 74859285Seric (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 74964822Seric SuprErrs = oldSuprErrs; 75053740Seric if (mci->mci_state == MCIS_CLOSED) 75164822Seric { 75264822Seric SuprErrs = oldSuprErrs; 75310159Seric return; 75464822Seric } 7559391Seric } 7569391Seric 75752676Seric /* now actually close the connection and pick up the zombie */ 75865194Seric (void) endmailer(mci, e, NULL); 75964822Seric 76064822Seric SuprErrs = oldSuprErrs; 7614865Seric } 7624865Seric /* 76354967Seric ** SMTPRSET -- send a RSET (reset) command 76454967Seric */ 76554967Seric 76654967Seric smtprset(m, mci, e) 76754967Seric register MAILER *m; 76854967Seric register MCI *mci; 76954967Seric ENVELOPE *e; 77054967Seric { 77154967Seric int r; 77254967Seric 77361093Seric SmtpPhase = "client RSET"; 77454967Seric smtpmessage("RSET", m, mci); 77559285Seric r = reply(m, mci, e, TimeOuts.to_rset, NULL); 77657734Seric if (r < 0) 77757734Seric mci->mci_state = MCIS_ERROR; 77854967Seric else if (REPLYTYPE(r) == 2) 77957734Seric { 78057734Seric mci->mci_state = MCIS_OPEN; 78157734Seric return; 78257734Seric } 78357734Seric smtpquit(m, mci, e); 78454967Seric } 78554967Seric /* 78658867Seric ** SMTPPROBE -- check the connection state 78754967Seric */ 78854967Seric 78958867Seric smtpprobe(mci) 79054967Seric register MCI *mci; 79154967Seric { 79254967Seric int r; 79354967Seric MAILER *m = mci->mci_mailer; 79454967Seric extern ENVELOPE BlankEnvelope; 79554967Seric ENVELOPE *e = &BlankEnvelope; 79654967Seric 79761093Seric SmtpPhase = "client probe"; 79858867Seric smtpmessage("RSET", m, mci); 79959285Seric r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 80058061Seric if (r < 0 || REPLYTYPE(r) != 2) 80154967Seric smtpquit(m, mci, e); 80254967Seric return r; 80354967Seric } 80454967Seric /* 8054684Seric ** REPLY -- read arpanet reply 8064684Seric ** 8074684Seric ** Parameters: 80810175Seric ** m -- the mailer we are reading the reply from. 80957379Seric ** mci -- the mailer connection info structure. 81057379Seric ** e -- the current envelope. 81157379Seric ** timeout -- the timeout for reads. 81259285Seric ** pfunc -- processing function for second and subsequent 81359285Seric ** lines of response -- if null, no special 81459285Seric ** processing is done. 8154684Seric ** 8164684Seric ** Returns: 8174684Seric ** reply code it reads. 8184684Seric ** 8194684Seric ** Side Effects: 8204684Seric ** flushes the mail file. 8214684Seric */ 8224684Seric 82359285Seric reply(m, mci, e, timeout, pfunc) 82453751Seric MAILER *m; 82554967Seric MCI *mci; 82653751Seric ENVELOPE *e; 82759285Seric time_t timeout; 82859285Seric void (*pfunc)(); 8294684Seric { 83059014Seric register char *bufp; 83159014Seric register int r; 83259285Seric bool firstline = TRUE; 83358957Seric char junkbuf[MAXLINE]; 83458957Seric 83557379Seric if (mci->mci_out != NULL) 83657379Seric (void) fflush(mci->mci_out); 8374684Seric 8387677Seric if (tTd(18, 1)) 8394796Seric printf("reply\n"); 8404796Seric 8417356Seric /* 8427356Seric ** Read the input line, being careful not to hang. 8437356Seric */ 8447356Seric 84559014Seric for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 8464684Seric { 8477356Seric register char *p; 84853751Seric extern time_t curtime(); 8494684Seric 8507685Seric /* actually do the read */ 85153751Seric if (e->e_xfp != NULL) 85253751Seric (void) fflush(e->e_xfp); /* for debugging */ 8537356Seric 85410054Seric /* if we are in the process of closing just give the code */ 85553740Seric if (mci->mci_state == MCIS_CLOSED) 85610054Seric return (SMTPCLOSING); 85710054Seric 85858680Seric if (mci->mci_out != NULL) 85958680Seric fflush(mci->mci_out); 86058680Seric 86110054Seric /* get the line from the other side */ 86261093Seric p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 86353751Seric mci->mci_lastuse = curtime(); 86453751Seric 86510054Seric if (p == NULL) 86610131Seric { 86763753Seric bool oldholderrs; 86810148Seric extern char MsgBuf[]; /* err.c */ 86910148Seric 87021065Seric /* if the remote end closed early, fake an error */ 87121065Seric if (errno == 0) 87221065Seric # ifdef ECONNRESET 87321065Seric errno = ECONNRESET; 87456795Seric # else /* ECONNRESET */ 87521065Seric errno = EPIPE; 87656795Seric # endif /* ECONNRESET */ 87721065Seric 87857379Seric mci->mci_errno = errno; 87957642Seric mci->mci_exitstat = EX_TEMPFAIL; 88063753Seric oldholderrs = HoldErrs; 88163753Seric HoldErrs = TRUE; 88263753Seric usrerr("451 reply: read error from %s", mci->mci_host); 88363753Seric 88410420Seric /* if debugging, pause so we can see state */ 88510420Seric if (tTd(18, 100)) 88610420Seric pause(); 88754967Seric mci->mci_state = MCIS_ERROR; 88853751Seric smtpquit(m, mci, e); 88963753Seric #ifdef XDEBUG 89063753Seric { 89163753Seric char wbuf[MAXLINE]; 89264082Seric char *p = wbuf; 89364082Seric if (e->e_to != NULL) 89464082Seric { 89564082Seric sprintf(p, "%s... ", e->e_to); 89664082Seric p += strlen(p); 89764082Seric } 89864082Seric sprintf(p, "reply(%s) during %s", 89964082Seric mci->mci_host, SmtpPhase); 90063753Seric checkfd012(wbuf); 90163753Seric } 90263753Seric #endif 90363753Seric HoldErrs = oldholderrs; 90410054Seric return (-1); 90510131Seric } 90658957Seric fixcrlf(bufp, TRUE); 90710054Seric 90863753Seric /* EHLO failure is not a real error */ 90963753Seric if (e->e_xfp != NULL && (bufp[0] == '4' || 91063753Seric (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 91114900Seric { 91214900Seric /* serious error -- log the previous command */ 91364071Seric if (SmtpNeedIntro) 91464071Seric { 91564071Seric /* inform user who we are chatting with */ 91664071Seric fprintf(CurEnv->e_xfp, 91764071Seric "... while talking to %s:\n", 91864071Seric CurHostName); 91964071Seric SmtpNeedIntro = FALSE; 92064071Seric } 92159014Seric if (SmtpMsgBuffer[0] != '\0') 92259014Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 92359014Seric SmtpMsgBuffer[0] = '\0'; 92414900Seric 92514900Seric /* now log the message as from the other side */ 92658957Seric fprintf(e->e_xfp, "<<< %s\n", bufp); 92714900Seric } 92814900Seric 92914900Seric /* display the input for verbose mode */ 93058120Seric if (Verbose) 93159956Seric nmessage("050 %s", bufp); 9327356Seric 93359285Seric /* process the line */ 93467893Seric if (pfunc != NULL) 93567893Seric (*pfunc)(bufp, firstline, m, mci, e); 93659285Seric 93759285Seric firstline = FALSE; 93859285Seric 9397356Seric /* if continuation is required, we can go on */ 94059014Seric if (bufp[3] == '-') 9414684Seric continue; 9427356Seric 94359014Seric /* ignore improperly formated input */ 94459014Seric if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 94559014Seric continue; 94659014Seric 9477356Seric /* decode the reply code */ 94859014Seric r = atoi(bufp); 9497356Seric 9507356Seric /* extra semantics: 0xx codes are "informational" */ 95159014Seric if (r >= 100) 95259014Seric break; 95359014Seric } 9547356Seric 95559014Seric /* 95659014Seric ** Now look at SmtpReplyBuffer -- only care about the first 95759014Seric ** line of the response from here on out. 95859014Seric */ 95958061Seric 96059014Seric /* save temporary failure messages for posterity */ 96159014Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 96259014Seric (void) strcpy(SmtpError, SmtpReplyBuffer); 9639391Seric 96459014Seric /* reply code 421 is "Service Shutting Down" */ 96559014Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 96659014Seric { 96759014Seric /* send the quit protocol */ 96859014Seric mci->mci_state = MCIS_SSD; 96959014Seric smtpquit(m, mci, e); 9704684Seric } 97159014Seric 97259014Seric return (r); 9734684Seric } 9744796Seric /* 9754865Seric ** SMTPMESSAGE -- send message to server 9764796Seric ** 9774796Seric ** Parameters: 9784796Seric ** f -- format 97910175Seric ** m -- the mailer to control formatting. 9804796Seric ** a, b, c -- parameters 9814796Seric ** 9824796Seric ** Returns: 9834796Seric ** none. 9844796Seric ** 9854796Seric ** Side Effects: 98653740Seric ** writes message to mci->mci_out. 9874796Seric */ 9884796Seric 9894865Seric /*VARARGS1*/ 99057642Seric #ifdef __STDC__ 99157642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 99257642Seric #else 99357642Seric smtpmessage(f, m, mci, va_alist) 9944796Seric char *f; 99510175Seric MAILER *m; 99654967Seric MCI *mci; 99757642Seric va_dcl 99857642Seric #endif 9994796Seric { 100056852Seric VA_LOCAL_DECL 100156852Seric 100257135Seric VA_START(mci); 100356852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 100456852Seric VA_END; 100558680Seric 100658120Seric if (tTd(18, 1) || Verbose) 100758151Seric nmessage(">>> %s", SmtpMsgBuffer); 100863753Seric if (TrafficLogFile != NULL) 100963753Seric fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 101053740Seric if (mci->mci_out != NULL) 101158680Seric { 101253740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 101354967Seric m == NULL ? "\r\n" : m->m_eol); 101458680Seric } 101559149Seric else if (tTd(18, 1)) 101658725Seric { 101759149Seric printf("smtpmessage: NULL mci_out\n"); 101858725Seric } 10194796Seric } 10205182Seric 102156795Seric # endif /* SMTP */ 1022