usersmtp.c

来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 625 行

C
625
字号
/************************************************************************ *									* *			Copyright (c) 1987, 1988 by			* *		Digital Equipment Corporation, Maynard, MA		* *			All rights reserved.				* *									* *   This software is furnished under a license and may be used and	* *   copied  only  in accordance with the terms of such license and	* *   with the  inclusion  of  the  above  copyright  notice.   This	* *   software  or  any  other copies thereof may not be provided or	* *   otherwise made available to any other person.  No title to and	* *   ownership of the software is hereby transferred.			* *									* *   This software is  derived  from  software  received  from  the	* *   University    of   California,   Berkeley,   and   from   Bell	* *   Laboratories.  Use, duplication, or disclosure is  subject  to	* *   restrictions  under  license  agreements  with  University  of	* *   California and with AT&T.						* *									* *   The information in this software is subject to change  without	* *   notice  and should not be construed as a commitment by Digital	* *   Equipment Corporation.						* *									* *   Digital assumes no responsibility for the use  or  reliability	* *   of its software on equipment which is not supplied by Digital.	* *									* ************************************************************************//* * EDIT HISTORY * * 001	01-April-1988	Jeff Michaud *	For the "HEAD" command, dump out the message body in addition to the *	message headers.  This makes the "HEAD" command basically look like *	a "DATA" command. */# include <ctype.h># include <sysexits.h># include <errno.h># include "sendmail.h"# ifndef SMTP# ifndef lintstatic char	SccsId[] = "@(#)usersmtp.c	4.1	(ULTRIX) (no SMTP)	7/2/90";# endif not lint# else SMTP# ifndef lintstatic char	SccsId[] = "@(#)usersmtp.c	4.1	(ULTRIX) (with SMTP)	7/2/90";# endif not lint/***  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 */FILE	*SmtpOut;			/* output file */FILE	*SmtpIn;			/* input file */int	SmtpPid;			/* pid of mailer */#ifdef DNUMODSint	SmtpMultiStatus;		/* mailer will give reply for each					   recipient after "DATA" done	   */#endif DNUMODS/* following represents the state of the SMTP connection */int	SmtpState;			/* connection state, see below */#define SMTP_CLOSED	0		/* connection is closed */#define SMTP_OPEN	1		/* connection is open for business */#define SMTP_SSD	2		/* service shutting down *//***  SMTPINIT -- initialize SMTP.****	Opens the connection and sends the initial protocol.****	Parameters:**		m -- mailer to create connection to.**		pvp -- pointer to parameter vector to pass to**			the mailer.****	Returns:**		appropriate exit status -- EX_OK on success.**		If not EX_OK, it should close the connection.****	Side Effects:**		creates connection and sends initial protocol.*/jmp_buf	CtxGreeting;#ifndef DNUMODSsmtpinit(m, pvp)#else DNUMODSsmtpinit(m, pvp, e)#endif DNUMODS	struct mailer *m;	char **pvp;#ifdef DNUMODS	ENVELOPE *e;#endif DNUMODS{	register int r;	EVENT *gte;	char buf[MAXNAME];	extern greettimeout();	/*	**  Open the connection to the mailer.	*/#ifdef DEBUG	if (SmtpState == SMTP_OPEN)		syserr("smtpinit: already open");#endif DEBUG	SmtpIn = SmtpOut = NULL;	SmtpState = SMTP_CLOSED;	SmtpError[0] = '\0';	SmtpPhase = "user open";	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);	if (SmtpPid < 0)	{# ifdef DEBUG		if (tTd(18, 1))			printf("smtpinit: cannot open %s: stat %d errno %d\n",			   pvp[0], ExitStat, errno);# endif DEBUG		if (CurEnv->e_xfp != NULL)		{			register char *p;			extern char *errstring();			extern char *statstring();			if (errno == 0)			{				p = statstring(ExitStat);				fprintf(CurEnv->e_xfp,					"%.3s %s.%s... %s\n",					p, pvp[1], m->m_name, p);			}			else			{				fprintf(CurEnv->e_xfp,					"421 %s.%s... Deferred: %s\n",					pvp[1], m->m_name, errstring(errno));			}		}		return (ExitStat);	}	SmtpState = SMTP_OPEN;	/*	**  Get the greeting message.	**	This should appear spontaneously.  Give it five minutes to	**	happen.	*/	if (setjmp(CtxGreeting) != 0)		goto tempfail;	gte = setevent((time_t) 300, greettimeout, 0);	SmtpPhase = "greeting wait";	r = reply(m);	clrevent(gte);	if (REPLYTYPE(r) == 5) {		SmtpState = SMTP_CLOSED;		goto unavailable;	}	if (r < 0 || REPLYTYPE(r) != 2)		goto tempfail;	/*	**  Send the HELO command.	**	My mother taught me to always introduce myself.	*/	smtpmessage("HELO %s", m, MyHostName);	SmtpPhase = "HELO wait";	r = reply(m);	if (r < 0)		goto tempfail;	else if (REPLYTYPE(r) == 5)		goto unavailable;	else if (REPLYTYPE(r) != 2)		goto tempfail;	/*	**  If this is expected to be another sendmail, send some internal	**  commands.	*/	if (bitnset(M_INTERNAL, m->m_flags))	{		/* tell it to be verbose */		smtpmessage("VERB", m);		r = reply(m);		if (r < 0)			goto tempfail;		/* tell it we will be sending one transaction only */		smtpmessage("ONEX", m);		r = reply(m);		if (r < 0)			goto tempfail;	}#ifdef DNUMODS	/*	**  Ask mailer if it will be returning multi-status for DATA command	*/	if( bitnset(M_XDEVSTATUS, m->m_flags) )	{		smtpmessage("MULT", m);		r = reply(m);		if( r < 0 )			goto tempfail;		SmtpMultiStatus = (r == 250);	}	else		SmtpMultiStatus = FALSE;	/*	**  If this mailer wants the envelope header early, see if	**  he will accept it.	*/	if( bitnset(M_EARLYHEAD, m->m_flags) )	{		/* Ask it if it wants early header now */		smtpmessage("HEAD", m);		r = reply(m);		if( r < 0 )			goto tempfail;		if( REPLYTYPE(r) == 2  ||  REPLYTYPE(r) == 3 )		{		    /* Send the header and message body */		    (*e->e_puthdr)(SmtpOut, m, CurEnv);		    putline("\n", SmtpOut, m);		    (*e->e_putbody)(SmtpOut, m, CurEnv);		    /* terminate the message */		    smtpmessage(".", m);		    /* Finish protocol for this */		    r = reply(m);		    if( r < 0 )			    goto tempfail;		}	}#endif DNUMODS	/*	**  Send the MAIL command.	**	Designates the sender.	*/	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);	if (CurEnv->e_from.q_mailer == LocalMailer ||	    !bitnset(M_FROMPATH, m->m_flags))	{		smtpmessage("MAIL From:<%s>", m, buf);	}	else	{		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,			buf[0] == '@' ? ',' : ':', buf);	}	SmtpPhase = "MAIL wait";	r = reply(m);	if (r < 0 || REPLYTYPE(r) == 4)		goto tempfail;	else if (r == 250)		return (EX_OK);#ifdef DNUMODS	else if (r == 559)		return EX_NOHOST;#endif DNUMODS	else if (r == 552)		goto unavailable;	/* protocol error -- close up */	smtpquit(m);	return (EX_PROTOCOL);	/* signal a temporary failure */  tempfail:	smtpquit(m);	return (EX_TEMPFAIL);	/* signal service unavailable */  unavailable:	smtpquit(m);	return (EX_UNAVAILABLE);}staticgreettimeout(){	/* timeout reading the greeting message */	longjmp(CtxGreeting, 1);}/***  SMTPRCPT -- designate recipient.****	Parameters:**		to -- address of recipient.**		m -- the mailer we are sending to.****	Returns:**		exit status corresponding to recipient status.****	Side Effects:**		Sends the mail via SMTP.*/smtprcpt(to, m)	ADDRESS *to;	register MAILER *m;{	register int r;	extern char *remotename();	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));	SmtpPhase = "RCPT wait";	r = reply(m);	if (r < 0 || REPLYTYPE(r) == 4)		return (EX_TEMPFAIL);	else if (REPLYTYPE(r) == 2)		return (EX_OK);	else if (r == 550 || r == 551 || r == 553)		return (EX_NOUSER);	else if (r == 552 || r == 554)		return (EX_UNAVAILABLE);	return (EX_PROTOCOL);}/***  SMTPDATA -- send the data and clean up the transaction.****	Parameters:**		m -- mailer being sent to.**		e -- the envelope for this message.****	Returns:**		exit status corresponding to DATA command.****	Side Effects:**		none.*/smtpdata(m, e)	struct mailer *m;	register ENVELOPE *e;{	register int r;	/*	**  Send the data.	**	First send the command and check that it is ok.	**	Then send the data.	**	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);	SmtpPhase = "DATA wait";	r = reply(m);	if (r < 0 || REPLYTYPE(r) == 4)		return (EX_TEMPFAIL);	else if (r == 554)		return (EX_UNAVAILABLE);	else if (r != 354)		return (EX_PROTOCOL);	/* now output the actual message */	(*e->e_puthdr)(SmtpOut, m, CurEnv);	putline("\n", SmtpOut, m);	(*e->e_putbody)(SmtpOut, m, CurEnv);	/* terminate the message */	fprintf(SmtpOut, ".%s", m->m_eol);	if (Verbose && !HoldErrs)		nmessage(Arpa_Info, ">>> .");#ifdef DNUMODS	return EX_OK;#else DNUMODS	/* check for the results of the transaction */	SmtpPhase = "result wait";	r = reply(m);	if (r < 0 || REPLYTYPE(r) == 4)		return (EX_TEMPFAIL);	else if (r == 250)		return (EX_OK);	else if (r == 552 || r == 554)		return (EX_UNAVAILABLE);	return (EX_PROTOCOL);#endif DNUMODS}#ifdef DNUMODSintsmtpdeliverstat(m)	struct mailer *m;{register int r;	/* check for the results of the transaction */	SmtpPhase = "result wait";	r = reply(m);	if (r < 0 || REPLYTYPE(r) == 4)		return (EX_TEMPFAIL);	else if (r == 250)		return (EX_OK);	else if (r == 552 || r == 554)		return (EX_UNAVAILABLE);	else if (r == 550 || r == 551 || r == 553)		return (EX_NOUSER);	return (EX_PROTOCOL);}#endif DNUMODS/***  SMTPQUIT -- close the SMTP connection.****	Parameters:**		m -- a pointer to the mailer.****	Returns:**		none.****	Side Effects:**		sends the final protocol and closes the connection.*/smtpquit(m)	register MAILER *m;{	int i;	/* if the connection is already closed, don't bother */	if (SmtpIn == NULL)		return;	/* send the quit message if not a forced quit */	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)	{		smtpmessage("QUIT", m);		(void) reply(m);		if (SmtpState == SMTP_CLOSED)			return;	}	/* now actually close the connection */	(void) fclose(SmtpIn);	(void) fclose(SmtpOut);	SmtpIn = SmtpOut = NULL;	SmtpState = SMTP_CLOSED;	/* and pick up the zombie */	i = endmailer(SmtpPid, m->m_argv[0]);	if (i != EX_OK)		syserr("smtpquit %s: stat %d", m->m_argv[0], i);}/***  REPLY -- read arpanet reply****	Parameters:**		m -- the mailer we are reading the reply from.****	Returns:**		reply code it reads.****	Side Effects:**		flushes the mail file.*/reply(m)	MAILER *m;{	(void) fflush(SmtpOut);	if (tTd(18, 1))		printf("reply\n");	/*	**  Read the input line, being careful not to hang.	*/	for (;;)	{		register int r;		register char *p;		/* actually do the read */		if (CurEnv->e_xfp != NULL)			(void) fflush(CurEnv->e_xfp);	/* for debugging */		/* if we are in the process of closing just give the code */		if (SmtpState == SMTP_CLOSED)			return (SMTPCLOSING);		/* get the line from the other side */		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);		if (p == NULL)		{			extern char MsgBuf[];		/* err.c */			extern char Arpa_TSyserr[];	/* conf.c */			/* if the remote end closed early, fake an error */			if (errno == 0)# ifdef ECONNRESET				errno = ECONNRESET;# else ECONNRESET				errno = EPIPE;# endif ECONNRESET			message(Arpa_TSyserr, "reply: read error");# ifdef DEBUG			/* if debugging, pause so we can see state */			if (tTd(18, 100))				pause();# endif DEBUG# ifdef LOG			syslog(LOG_ERR, "%s", &MsgBuf[4]);# endif LOG			SmtpState = SMTP_CLOSED;			smtpquit(m);			return (-1);		}		fixcrlf(SmtpReplyBuffer, TRUE);		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)		{			/* serious error -- log the previous command */			if (SmtpMsgBuffer[0] != '\0')				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);			SmtpMsgBuffer[0] = '\0';			/* now log the message as from the other side */			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);		}		/* display the input for verbose mode */		if (Verbose && !HoldErrs)			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);#ifdef DNUMODS		/* save temporary failure messages for posterity */		if (SmtpReplyBuffer[0] == '4' && strlen(SmtpReplyBuffer) > 4)			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);#endif DNUMODS		/* if continuation is required, we can go on */		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))			continue;		/* decode the reply code */		r = atoi(SmtpReplyBuffer);		/* extra semantics: 0xx codes are "informational" */		if (r < 100)			continue;		/* reply code 421 is "Service Shutting Down" */		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)		{			/* send the quit protocol */			SmtpState = SMTP_SSD;			smtpquit(m);		}#ifndef DNUMODS		/* save temporary failure messages for posterity */		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);#endif DNUMODS		return (r);	}}/***  SMTPMESSAGE -- send message to server****	Parameters:**		f -- format**		m -- the mailer to control formatting.**		a, b, c -- parameters****	Returns:**		none.****	Side Effects:**		writes message to SmtpOut.*//*VARARGS1*/smtpmessage(f, m, a, b, c)	char *f;	MAILER *m;{	(void) sprintf(SmtpMsgBuffer, f, a, b, c);	if (tTd(18, 1) || (Verbose && !HoldErrs))		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);	if (SmtpOut != NULL)		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);}# endif SMTP

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?