* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)usersmtp.c 5.21 (Berkeley) %G% (with SMTP)";
static char sccsid
[] = "@(#)usersmtp.c 5.21 (Berkeley) %G% (without SMTP)";
** USERSMTP -- run SMTP protocol from the user end.
** This protocol is described in RFC821.
#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
#define SMTPCLOSING 421 /* "Service Shutting Down" */
char SmtpMsgBuffer
[MAXLINE
]; /* buffer for commands */
char SmtpReplyBuffer
[MAXLINE
]; /* buffer for replies */
char SmtpError
[MAXLINE
] = ""; /* save failure error messages */
int SmtpPid
; /* pid of mailer */
** SMTPINIT -- initialize SMTP.
** Opens the connection and sends the initial protocol.
** m -- mailer to create connection to.
** pvp -- pointer to parameter vector to pass to
** appropriate exit status -- EX_OK on success.
** If not EX_OK, it should close the connection.
** creates connection and sends initial protocol.
static int greettimeout();
extern MCONINFO
*openmailer();
** Open the connection to the mailer.
setproctitle("%s %s: %s", e
->e_id
, pvp
[1], "user open");
mci
= openmailer(m
, pvp
, (ADDRESS
*) NULL
, TRUE
);
if (mci
->mci_state
!= MCIS_OPENING
&& mci
->mci_state
!= MCIS_CLOSED
)
mci
->mci_phase
= "user open";
mci
->mci_state
= MCIS_OPENING
;
printf("smtpinit: cannot open %s: stat %d errno %d\n",
pvp
[0], ExitStat
, errno
);
extern char *errstring();
extern char *statstring();
p
= statstring(ExitStat
);
p
, pvp
[1], m
->m_name
, p
);
"421 %s.%s... Deferred: %s\n",
pvp
[1], m
->m_name
, errstring(errno
));
mci
->mci_exitstat
= ExitStat
;
** Get the greeting message.
** This should appear spontaneously. Give it five minutes to
if (setjmp(CtxGreeting
) != 0)
gte
= setevent((time_t) 300, greettimeout
, 0);
mci
->mci_phase
= "greeting wait";
setproctitle("%s %s: %s", e
->e_id
, CurHostName
, mci
->mci_phase
);
if (r
< 0 || REPLYTYPE(r
) != 2)
** Send the HELO command.
** My mother taught me to always introduce myself.
smtpmessage("HELO %s", m
, mci
, MyHostName
);
mci
->mci_phase
= "HELO wait";
setproctitle("%s %s: %s", e
->e_id
, CurHostName
, mci
->mci_phase
);
else if (REPLYTYPE(r
) == 5)
else if (REPLYTYPE(r
) != 2)
** If this is expected to be another sendmail, send some internal
if (bitnset(M_INTERNAL
, m
->m_flags
))
/* tell it to be verbose */
smtpmessage("VERB", m
, mci
);
/* tell it we will be sending one transaction only */
smtpmessage("ONEX", m
, mci
);
mci
->mci_state
= MCIS_OPEN
;
mci
->mci_exitstat
= EX_TEMPFAIL
;
mci
->mci_state
= MCIS_TEMPFAIL
;
mci
->mci_exitstat
= EX_UNAVAILABLE
;
mci
->mci_state
= MCIS_ERROR
;
** Send the HOPS command.
** This is non-standard and may give an "unknown command".
** It can give a "bad hop count" error if the hop
** Send the MAIL command.
** Designates the sender.
mci
->mci_state
= MCIS_ACTIVE
;
expand("\001<", buf
, &buf
[sizeof buf
- 1], e
);
if (e
->e_from
.q_mailer
== LocalMailer
||
!bitnset(M_FROMPATH
, m
->m_flags
))
smtpmessage("MAIL From:<%s>", m
, mci
, buf
);
smtpmessage("MAIL From:<@%s%c%s>", m
, mci
, MyHostName
,
buf
[0] == '@' ? ',' : ':', buf
);
mci
->mci_phase
= "MAIL wait";
setproctitle("%s %s: %s", e
->e_id
, CurHostName
, mci
->mci_phase
);
if (r
< 0 || REPLYTYPE(r
) == 4)
mci
->mci_exitstat
= EX_TEMPFAIL
;
mci
->mci_state
= MCIS_TEMPFAIL
;
mci
->mci_exitstat
= EX_OK
;
/* signal service unavailable */
mci
->mci_exitstat
= EX_UNAVAILABLE
;
mci
->mci_state
= MCIS_ERROR
;
/* protocol error -- close up */
mci
->mci_exitstat
= EX_PROTOCOL
;
mci
->mci_state
= MCIS_ERROR
;
/* timeout reading the greeting message */
** SMTPRCPT -- designate recipient.
** to -- address of recipient.
** m -- the mailer we are sending to.
** exit status corresponding to recipient status.
** Sends the mail via SMTP.
extern char *remotename();
smtpmessage("RCPT To:<%s>", m
, mci
, to
->q_user
);
mci
->mci_phase
= "RCPT wait";
setproctitle("%s %s: %s", e
->e_id
, CurHostName
, mci
->mci_phase
);
if (r
< 0 || REPLYTYPE(r
) == 4)
else if (REPLYTYPE(r
) == 2)
else if (r
== 550 || r
== 551 || r
== 553)
else if (r
== 552 || r
== 554)
** SMTPDATA -- send the data and clean up the transaction.
** m -- mailer being sent to.
** e -- the envelope for this message.
** exit status corresponding to DATA command.
** First send the command and check that it is ok.
** Follow it up with a dot to terminate.
** Finally get the results of the transaction.
/* send the command and check ok to proceed */
smtpmessage("DATA", m
, mci
);
mci
->mci_phase
= "DATA wait";
setproctitle("%s %s: %s", e
->e_id
, CurHostName
, mci
->mci_phase
);
if (r
< 0 || REPLYTYPE(r
) == 4)
/* now output the actual message */
(*e
->e_puthdr
)(mci
->mci_out
, m
, e
);
putline("\n", mci
->mci_out
, m
);
(*e
->e_putbody
)(mci
->mci_out
, m
, e
);
/* terminate the message */
fprintf(mci
->mci_out
, ".%s", m
->m_eol
);
if (Verbose
&& !HoldErrs
)
nmessage(Arpa_Info
, ">>> .");
/* check for the results of the transaction */
mci
->mci_phase
= "result wait";
setproctitle("%s %s: %s", e
->e_id
, CurHostName
, mci
->mci_phase
);
mci
->mci_state
= MCIS_OPEN
;
else if (r
== 552 || r
== 554)
** SMTPQUIT -- close the SMTP connection.
** m -- a pointer to the mailer.
** sends the final protocol and closes the connection.
/* if the connection is already closed, don't bother */
if (mci
->mci_state
== MCIS_CLOSED
)
/* send the quit message if not a forced quit */
if (mci
->mci_state
!= MCIS_ERROR
)
smtpmessage("QUIT", m
, mci
);
if (mci
->mci_state
== MCIS_CLOSED
)
/* now actually close the connection and pick up the zombie */
i
= endmailer(mci
, m
->m_argv
[0]);
syserr("smtpquit %s: stat %d", m
->m_argv
[0], i
);
** REPLY -- read arpanet reply
** m -- the mailer we are reading the reply from.
** flushes the mail file.
(void) fflush(mci
->mci_out
);
** Read the input line, being careful not to hang.
/* actually do the read */
(void) fflush(e
->e_xfp
); /* for debugging */
/* if we are in the process of closing just give the code */
if (mci
->mci_state
== MCIS_CLOSED
)
/* get the line from the other side */
p
= sfgets(SmtpReplyBuffer
, sizeof SmtpReplyBuffer
, mci
->mci_in
);
mci
->mci_lastuse
= curtime();
extern char MsgBuf
[]; /* err.c */
extern char Arpa_TSyserr
[]; /* conf.c */
/* if the remote end closed early, fake an error */
message(Arpa_TSyserr
, "reply: read error");
/* if debugging, pause so we can see state */
syslog(LOG_INFO
, "%s", &MsgBuf
[4]);
mci
->mci_state
= MCIS_CLOSED
;
fixcrlf(SmtpReplyBuffer
, TRUE
);
if (e
->e_xfp
!= NULL
&& index("45", SmtpReplyBuffer
[0]) != NULL
)
/* serious error -- log the previous command */
if (SmtpMsgBuffer
[0] != '\0')
fprintf(e
->e_xfp
, ">>> %s\n", SmtpMsgBuffer
);
/* now log the message as from the other side */
fprintf(e
->e_xfp
, "<<< %s\n", SmtpReplyBuffer
);
/* display the input for verbose mode */
if (Verbose
&& !HoldErrs
)
nmessage(Arpa_Info
, "%s", SmtpReplyBuffer
);
/* if continuation is required, we can go on */
if (SmtpReplyBuffer
[3] == '-' || !isdigit(SmtpReplyBuffer
[0]))
/* decode the reply code */
r
= atoi(SmtpReplyBuffer
);
/* extra semantics: 0xx codes are "informational" */
/* reply code 421 is "Service Shutting Down" */
if (r
== SMTPCLOSING
&& mci
->mci_state
!= MCIS_SSD
)
/* send the quit protocol */
mci
->mci_state
= MCIS_SSD
;
/* save temporary failure messages for posterity */
if (SmtpReplyBuffer
[0] == '4' && SmtpError
[0] == '\0')
(void) strcpy(SmtpError
, &SmtpReplyBuffer
[4]);
** SMTPMESSAGE -- send message to server
** m -- the mailer to control formatting.
** writes message to mci->mci_out.
smtpmessage(f
, m
, mci
, a
, b
, c
)
(void) sprintf(SmtpMsgBuffer
, f
, a
, b
, c
);
if (tTd(18, 1) || (Verbose
&& !HoldErrs
))
nmessage(Arpa_Info
, ">>> %s", SmtpMsgBuffer
);
if (mci
->mci_out
!= NULL
)
fprintf(mci
->mci_out
, "%s%s", SmtpMsgBuffer
,
m
== 0 ? "\r\n" : m
->m_eol
);