/*
 * Copyright (c) 1998-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

/*
 * sSMTP sendmail -- send messages via smtp to a mailhub for local delivery 
 *	or forwarding. This program is used in place of /usr/lib/sendmail,
 *      called by /bin/mail (et all).   sSMTP does a selected subset of 
 *      sendmail's standard tasks (including exactly one rewriting task), and
 *      explains if you ask it to do something it can't.  It then sends
 *      the mail to the mailhub via an smtp connection.  Believe it or not,
 *      this is nothing but a filter.  You can receive mail with inetd, an
 *	inverse filter and /bin/mail -d.
 *
 *  October 1998-2003 ArkanoiD
 *   - Heavy modifications for smapd operation
 *
 *  September 1997 Christoph Lameter
 *   - Fixed up to use more modern C (attempt to fix problems)
 *   - Fixed scores of bugs (I doubt it ever worked before)
 *   - Made it work under Debian/Linux
 *   - Add support for -t option. Limit header to 4K.
 *
 *  October 1997 Hugo Haas
 *   - Added the reverse aliases process for the From: field
 *   - Send the required headers at the beginning
 *   - Send only one recipient at a time
 *   - Changed the header parsing to avoid a bug due to mailx
 *
 *  November 1997 Hugo Haas
 *   - Changed the RCPT stuff which was wrong for arguments with "<>"
 *
 *  December 1997 Hugo Haas
 *   - Changed the MAIL FROM command to be RFC821 compliant (Debian Bug#15690)
 *   - Modified the recordReciepient function: no memory was allocated for
 *	the last recipient
 *   - Added the sending of the recorded recipients (Debian Bug#15690)
 *	The old way to do it was wrong. (Removed the argv=reciepients stuff)
 *
 *  January 1998 Hugo Haas
 *   - Changed the header parsing because it gobbled pseudo-header lines 
 *	(Debian Bug#17240)
 *   - Changed the RewriteDomain option
 *
 *  January 1998 Hugo Haas
 *   - Changed the configuration parsing (Debian Bug#17470)
 *   - Changed the logging: verbosity reduced
 *
 *  March 1998 Hugo Haas
 *   - No more adding "To: postmaster" (qmail and sendmail do this)
 *   - Improved "-f", "-F" and "-r" options
 *
 *  March 1998 Hugo Haas
 *   - Moved the configuration files to /etc/ssmtp
 *
 *  April 1998 Hugo Haas
 *   - Removed quote in the From: line
 *
 *  April 1998 Hugo Haas
 *   - Removed awful getDate() method; replaced by get_arpadate()
 *
 *  April 1998 Hugo Haas
 *   - Now ignoring -R keyword, -N dsn stuff
 *
 *  April 1998 Hugo Haas
 *   - Made 'Root' option work
 *   - Handled the case when the user has no name (do not send "(null)")
 */

#include <stdio.h>
#include <limits.h>
#include <pwd.h>       		/* For getpwent. */
#include <sys/types.h> 		/* For sockets. */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <syslog.h>    		/* For logging. */
#include <signal.h>    		/* For the timer and signals. */
#include <setjmp.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <sysexits.h>

#include "ssmtp.h"
#include "patchlevel.h"
#include "firewall.h"
#include "fwfunc.h"
#include "firewall2.h"
#include "newio.h"
#include "ci_milter.h"

static	char*	moduleId ATTR_UNUSED = "$Id: ssmtp.c,v 1.22 2007/09/22 14:35:25 arkenoi Exp $";

#define	QTEMPLATE	"/tmp/ssmtp.XXXXXXXXXX"

#ifndef MAILHUB
#define MAILHUB "mailhost" /* A surprisingly usefull default. */
#endif
#define PORTNUMBER 25 /* From the Assigned Numbers RFC. */

#if defined(linux) || defined (SOLARIS)
extern	void	initsetproctitle(int,char**);
#endif

char		*Version = VERSION;	/* The version of the program. */
char		*MailHub = MAILHUB;	/* The place to send the mail. */
char		HostName[MAXLINE];	/* Our name, unless overridden. */
char		fromname[1024] = "null";
struct 	passwd 	*Sender = NULL;   	/* The person sending the mail. */

int     	Verbose = NO;		/* Tell the user what's happening. */
int		Toption = NO;		/* Was a T option given? */
int		LogLevel =		/* Tell the log what's happening. */
#ifdef DEBUG
		1;
#else
		0;
#endif

static	int	hide_intranet = 0;
static	int	from_intranet = 0;
static	int	permit_routed = 0;
static	int	log_headers = 0;
static	int	dscp = 0;
static	int  	tfd;
static	char	**rcpts = NULL;

static	char	*baddir = NULL;

static	char	*mfrom;
static	char	riaddr[512] = "";

char 		*headers;	/* Pointer to beginning of headers */
char 		*headere;	/* End of header */

#define CHUNK 32000		/* Sizeof allocation */
#define ARPADATE_LENGTH 60

static	char	*qf = NULL;

/* Current date in RFC format. */
#include "arpadate.c"

char		DateString[ARPADATE_LENGTH];

char		*fromLine(void);

int		getOkFromSmtp(int,char *);
int		getFromSmtp(int,char *);

extern int milter_inspect_mail(char*,char*,char*,char*,char*,char*,char***);

void flush(void);

/*
 * die -- say something and exit with a non-zero return code.
 *	Save the message on stdin in dead.letter.
 */

void 
die(int ex,char *format,...) 
{
	va_list ap;
	(void) fprintf(stderr,"%s: ",proxy_name);
	va_start(ap,format);
	(void) vfprintf(stderr,format,ap);
	(void) vsyslog(LLEV,format,ap);
	va_end(ap);
	(void) putc('\n',stderr);
	flush(); /* Send message to dead.letter */
	if (qf)
		unlink(qf);
	exit(ex);
}

/*
 * putToSmtp -- a printf to an fd, which appends TCP/IP <CR/LF>.
 */

void 
putToSmtp(int fd, char *format, ...) 
{
	va_list args;
	char line[MAXLINE];
	int x;
	va_start(args, format);
	(void) vsnprintf(line,MAXLINE-3,format,args);
	if (LogLevel > 0) {
		syslog(LLEV,"Sent \"%s\" to smtp port.\n",line);
	}
	(void) strlcat(line,"\r\n",MAXLINE);
	x = strlen(line);
	if ((sowrite(fd,line,x)!=x))
		die(EX_IOERR,"error writing to relay");
	proxy_stats.outbytes += x;
	proxy_update_status();
	va_end(args);
}

void 
putToQf(int fd, char *format, ...) 
{
	va_list args;
	char line[MAXLINE];
	int x;
	va_start(args, format);
	(void) vsnprintf(line,MAXLINE-3,format,args);
	(void) strlcat(line,"\r\n",MAXLINE);
	x = strlen(line);
	if ((sowrite(fd,line,x)!=x))
		die(EX_IOERR,"error writing to temporary file");
	va_end(args);
}


void 
rrec(char *re) 
{
	char *p,*r,*ptr;

	if (!*re) 
		return;
	if ((p=index(re,'<'))) {
		ptr=p+1;
		if ((p=index(ptr,'>'))) {
			r=malloc(p-ptr+1);
			r[p-ptr]=0;
			memcpy(r,ptr,p-ptr);
			addlist(r,&rcpts);
			free(r);
		}
		else
			die (EX_USAGE,"Syntax error in recipient %s",p);
	}
	else
		addlist(re,&rcpts);
}

void 
recordReciepient(char *line) 
{
	char *p,*q,*r;

	q=p=line;
	do {
		while (*q==' ' || *q==',') q++;
		if (!*q)
			return;
		p=q;
		while (*p && *p!=',') p++;
		if (*p) {
			r=xmalloc(p-q+1);
			memcpy(r,q,p-q);
			r[p-q]=0;
			rrec(r);
			free(r);
			q=++p;
		}
	} while (*p);
	r=xstrdup(q);
	rrec(r);
	free(r);
}

/*
** Supporting libraries -- header insertion.
*/

static int hasFrom = NO,
	hasDate = NO;
/*
 * recordRequiredHeaders -- note which ones we've seen.
 */

void 
recordRequiredHeaders(char *line) 
{

	if (*line == ' ' || *line == '\t') {
		return;
	}
	else if (strncasecmp(line,"From:",5)==0) {
		hasFrom = YES;
	}
	else if (strncasecmp(line,"Date:",5)==0) {
		hasDate = YES;
	}
	if (Toption)
	{	/* Need to figure out reciepients from the e-mail */
		if (strncasecmp(line,"To:",3)==0) recordReciepient(line+4); else
		if (strncasecmp(line,"Bcc:",4)==0) recordReciepient(line+5); else
		if (strncasecmp(line,"CC:",3)==0) recordReciepient(line+4);
	}
}

/*
 * addRequiredHeaders -- add ones that have been missed.
 */

void 
addRequiredHeaders(int fd) {

	if (hasFrom == NO) {
		putToQf(fd,"From: %s", fromLine());
	}
	if (hasDate == NO) {
		putToQf(fd,"Date: %s",DateString);
	}
}

/*
 * addInitialHeaders -- prepend prerequisite timstamp
 *	and actual date lines.
 */

void 
addInitialHeaders(int fd) 
{ 

	putToQf(fd,"Received: by %s (ssmtp %s); %s",
		  HostName, Version, DateString);

}

/*
 * fromLine -- generate a from line in standard format. Used whenever
 *	we need one in standard format, "Real Name <id@site>" or "id@site"
 */

char*
fromLine(void) 
{
	static char buffer[MAXLINE];
	char	*phrase;
	if (strcmp(fromname,"null")) {
		return fromname;
	}
	if ((phrase = strtok(Sender->pw_gecos, ",;")) != NULL)
		(void) snprintf(buffer,MAXLINE-4,"%s <%s@%s>",
			phrase,
			Sender->pw_name,
			HostName);
	else
		(void) snprintf(buffer,MAXLINE-4,"%s@%s",
			Sender->pw_name,
			HostName);
	return buffer;
}

/*
 * standardize -- trim off '\n's, double leading dots.
 */

void 
standardize(char *p) 
{
	char *q;

	if (*p == '.' && *(p+1) == '\n') {
		/* Double it, in hopes smtp will single it. */
		*(p+1) = '.';
		return;
	}
	for (q=p; *q; q++)
		;
	*--q = '\0';
}

int is_intranet_relay(char *hdrline)
{
	char 	*foo,*t,*x;
	int  	i;
	Cfg	*cf;

	foo = strdup(hdrline);

	if (strncmp(foo,"Received: ",10))
		return(-1);

	if ((t = strchr(foo,'['))) { 
		x = strchr(t,']');
		if (!x) 
			die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap"); 
		else {
			*x = '\0';
			if (strlen(t) >16)
				die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap"); 
			strncpy(riaddr,t+1,strlen(t));
		} 
	} else
		die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap");

	cf = cfg_get("intranet-hosts",proxy_confp);
	while(cf != (Cfg *)0) {
		if(cf->argc < 1)
			goto skip;
		for(i = 0; i < cf->argc && cf->argv[i][0] != '-'; i++) {
			if(hostmatch(cf->argv[i],riaddr))  
				return(1);
		}
skip:
		cf = cfg_get("intranet-hosts",(Cfg*)0);
	}
	return(0);
}
			

void
get_header(void)
{
	int	i;
	char	foo[MAXLINE],bar[MAXLINE];
	int	firstline = 1;
	Cfg	*cf;

	headere=headers=xmalloc(CHUNK);

	while(fgets(bar,sizeof(bar),stdin) && (bar[0]!='\n')) {
		standardize(bar);
		recordRequiredHeaders(bar);
		if (firstline) {
			from_intranet = is_intranet_relay(bar);
			hide_intranet &= (from_intranet > 0) ? YES : NO; 
			if (hide_intranet) {
				strlcpy(foo,bar,MAXLINE);
				fgets(bar,sizeof(bar),stdin);
				if (!bar || (strlen(bar+1) > 128))
					die(EX_SOFTWARE,"invalid 'Received:' line, seems to be not from smap");
				syslog(LLEV,"intranet route removed for message smap %.128s",
				       bar+1);
				if (log_headers) {
					syslog(LLEV,"log-headers: %.512s",foo);
					syslog(LLEV,"log-headers: %.512s",bar);
				}
				snprintf(foo,MAXLINE-1,"Received: from undisclosed-intranet-sender %.128s",bar+1);
				strlcpy(bar,foo,MAXLINE);
				standardize(bar);
			}
		} else {
			if (hide_intranet) {
				if (!strncmp("Received: ",bar,10)) {
					if (log_headers)
						syslog(LLEV,"log-headers: %.512s",bar);
					while(fgets(bar,sizeof(bar),stdin) &&
					      ((bar[0]==' ')||(bar[0]=='\t'))) {
						if (bar[0] == '\n')
							return;
						if (log_headers)
							syslog(LLEV,"log-headers: %.512s",bar);
					}
					standardize(bar);
					recordRequiredHeaders(bar);
				}
			}
		}

		i = strlen(bar) + 1;
		if (headere+i-headers >CHUNK) 
			die (EX_SOFTWARE,"Header too large Max is %d characters",CHUNK);
		strlcpy(headere,bar,CHUNK-(headere-headers+i));
		headere+=i;
		if (*bar == '\0')
			break;
		firstline = 0;
	}
	permit_routed = 0;
	cf = cfg_get("permit-routed",proxy_confp);
	if (cf != (Cfg *)0) {
		if (cf->argc != 1) {
			die(EX_CONFIG,"fwtkcfgerr: permit-routed must have one parameter, line %d",cf->ln);
		}
		if (!strcmp(cf->argv[0],"any")) 
			permit_routed = 1;
		else if (!strcmp(cf->argv[0],"intranet")) 
			permit_routed = (from_intranet > 0) ? YES : NO;
	}
	return;
}

void
send_body(int fd)
{
	char	bar[MAXLINE];
	int 	barsize;
	FILE	*tf;

	if (!(tf = fopen(qf,"r")))
			die(EX_IOERR,"error reading temporary file");

	while(fgets(bar,sizeof(bar),tf)) {
		barsize = strlen(bar);
		if (sowrite(fd,bar,barsize)!=barsize) 
			die(EX_IOERR,"error writing to relay");
		proxy_stats.outbytes+=barsize;
		proxy_update_status();
	}
	fclose(tf);
	unlink(qf);
}

/*
** emergency exit functions.
*/

/*
 * flush -- save stdin to dead.letter, if you can.
 */

void 
flush(void) 
{
	char	line[MAXLINE];
	FILE	*fp;

	if (!baddir)
		return;

	if (isatty(fileno(stdin))) {
		if (LogLevel > 0) {
			syslog(LLEV,"stdin appears to be a terminal. Not saving to dead.letter.");
		}
		return;
	}

	if (*riaddr)
		return;

	(void) snprintf(line,MAXLINE-1,"%s/dead.letter",baddir);
	if ((fp= fopen(line,"a")) == NULL) {
		/* Perhaps the person doesn't have a homedir... */
		if (LogLevel > 0) {
				syslog(LLEV,"Can't open %s, failing horribly.",
					  line);
		}
		return;
	}
	(void) putc('\n',fp); /* Make sure we start on a new line, */
	(void) putc('\n',fp); /* with a blank line separating messages. */
	while (fgets(line,sizeof line,stdin)) {
		(void) fputs(line,fp);
	}
	if (fclose(fp) == ERR) {
		if (LogLevel > 0) {
			syslog(LLEV,"Can't close %s/dead.letter, possibly truncated.",
				  baddir);
		}
	}
}

/*
** Local/peculiar string manipulation.
*/


int 
getConfig(void) 
{
	Cfg	*cf;
	char	*dscpname;

	sotimeout(proxy_timeout = proxy_conf_timeout(proxy_confp));
	if (!(MailHub = proxy_conf_string(proxy_confp,"default-relay"))) {
		syslog(LLEV,"fwtkcfgerr: no default relay specified");
		exit(EX_CONFIG);
	}
	dscpname = proxy_conf_string(proxy_confp,"default-dscp");

	if (dscpname)
		dscp = proxy_conf_diffserv_codepoint(proxy_confp,dscpname);

	if ((cf = cfg_get("hostname",proxy_confp)) != (Cfg *)0) {
		if (cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: hostname must have one parameter, line %d",cf->ln);
			exit(EX_CONFIG);
		}
		strlcpy(HostName,cf->argv[0], MAXLINE);
	}

	if ((cf = cfg_get("hide-intranet",proxy_confp)) != (Cfg *)0) {
		if ((cf->argc == 1))
			log_headers |= !strcasecmp(cf->argv[0],"-log-headers");
		hide_intranet = 1;
	}

	return YES; 
}

/*
 * doOptions -- pull the options out of the command-line, process them 
 *      (and special-case calls to mailq, etc), and return the rest.
 */

void 
doOptions(int argc, char *argv[]) {
	int    i, 
		newArgC;

	newArgC = 0;

	if (strstr(argv[0],"mailq") != NULL) {
		/* Someone wants to know the queue state... */
		(void) printf("Mail queue is empty.\n");
		exit(0);
		}
	else if (strstr(argv[0],"newalias") != NULL) {
		/* Someone wanted to recompile aliases. */
		/* This is slightly more like to be a human... */
		die(EX_USAGE,"newalias is meaningless to sSMTP: it doesn't do aliases.");
	}
	
	for (i=1; i < argc; i++) {
		if (argv[i][0] != '-') {
			addlist(argv[i],&rcpts);
			newArgC++;
			continue;
		}
		switch(argv[i][1]) {
		case 'b':
			switch (argv[i][2]) {
			case 'a': /* ARPANET mode. */
				die(EX_USAGE,"-ba is not supported by sSMTP sendmail, nor is -t.");
			case 'd': /* Run as a daemon. */
				die(EX_USAGE,"-bd is not supported by sSMTP sendmail. Use rSMTP under inetd instead.");
			case 'i': /* Initialize aliases. */	      continue;
			case 'm': /*  Default addr processing. */     continue;
			case 'p': /* Print mailqueue. */
				die(0,"Mail queue is empty.");
			case 's': /* Read smtp from stdin. */
				die(EX_USAGE,"-bs is not supported by sSMTP sendmail."); 
			case 't': /* Test mode. */
				die(EX_USAGE,"-bt is meaningless to sSMTP sendmail. It doesn't route.");
			case 'v': /*  Verify names only. */
				die(EX_USAGE,"-bv is meaningless to sSMTP sendmail. It doesn't route.");
			case 'z': /* Create  freeze file. */
				die(EX_USAGE,"-bz is meaningless to sSMTP sendmail. It isn't programmable.");
			}
		case 'C': /* Configfile name. */		      continue;
		case 'd': /* Debug. */
			LogLevel = 1;
			Verbose = YES; /* Almost the same thing... */
			break;
		case 'E': /* insecure channel, don't trust userid. */ continue;
		case 'R': /* amount of the message to be returned */
			  /* Process queue for recipient. */
		case 'F': /* fullname of sender. */
		case 'f': /* Set from/sender address. */
		case 'r': /* Obsolete -f flag. */
			if (argv[i][2]) {
				strncpy(fromname,argv[i]+2,sizeof(fromname));
			} else {
				i++;
				if (i>argc)
					continue;
				if (argv[i][1]!='-') {
					strncpy(fromname,argv[i],sizeof(fromname));
				}
			}
			continue;
				/* Should I support these??? When? */
		case 'h': /* Set hopcount. */			      continue;
		case 'm': /* Ignore originator in adress list. */     continue;
		case 'M': /* USe specified message-id. */	      continue;
		case 'N': /* dsn options */			i++;  continue;
		case 'n': /* No aliasing. */			      continue;
		case 'o':
			switch (argv[i][2]) {
			case 'A': /* Alternate aliases file. */	      continue;
			case 'c': /* Delay connections. */	      continue;
			case 'D': /* Run newaliases if rqd. */	      continue;
			case 'd': /* Deliver now, in background or queue. */
				/* This may warrant a diagnostic for b or q. */
				continue;
			case 'e': /* Errors: mail, write or none. */  continue;
			case 'F': /* Set tempfile mode. */	      continue;
			case 'f': /* Save ``From ' lines. */	      continue;
			case 'g': /* Set group id. */		      continue;
			case 'H': /* Helpfile name. */		      continue;
			case 'i': /* DATA ends at EOF, not \n.\n */   continue;
			case 'L': /* Log level. */		      continue;
			case 'm': /* Send to me if in the list. */    continue;
			case 'o': /* Old headers, spaces between adresses. */
				die(EX_USAGE,"-oo (old header format) is not supported by sSMTP sendmail.");
			case 'Q': /* Queue dir. */		      continue;
			case 'r': /* Read timeout. */		      continue;
			case 's': /* Always init the queue. */	      continue;
			case 'S': /* Stats file. */		      continue;
			case 'T': /* Queue timeout. */		      continue;
			case 't': /* Set timezone. */		      continue;
			case 'u': /* Set uid. */		      continue;
			case 'v': /* Set verbose flag. */
				Verbose = YES;
				continue;
			}
			break;
		case 'q': /* Process the queue [at time] */
			die(0,"Mail queue is empty.");
		case 't': /* Read message's To/Cc/Bcc lines. */
				Toption = YES; continue;
		case 'v': /* Verbose (ditto -ov). */
			Verbose = YES;
			break;
		case 'V': /*  Say version and quit. */
		/* Similar as die, but no logging */
			fprintf(stderr,
				"sSMTP version %s (not sendmail at all)\n",
				Version);
		        flush(); /* Send message to dead.letter */
			exit(0);
			break;
		}
	}
	if (newArgC < 1 && ! Toption) {
		die(EX_USAGE,"no recipients supplied: no mail will be sent.");
	}
	if (newArgC >= 1 && Toption) die (EX_USAGE,"Recipientlist with -t option not supported.");
	return;
}

void
get_email(buffer,eaddress)
char	*buffer;
char	*eaddress;
{
	char	*t,*x;

	if ((t = strchr(buffer,'<'))) {
		x = strchr(t,'>');
		if(!x) {
			die(EX_DATAERR,"email address is malfored, no trailing bracket");
		} else {
			*x = '\0';
			strlcpy(eaddress,t+1,MAXLINE);
		}
	} else
		strlcpy(eaddress,buffer,MAXLINE);
	return;
}

void
get_relay(email,myrelay)
char	*email;
char	*myrelay;
{
	Cfg	*cf;
	char	*thost;
	char	foo[MAXLINE];
	char	z[MAXLINE];
	fwparm  options[] = { 	{ FWPARM_STRING,"-via",	       myrelay      },
		        	{ FWPARM_DSCP,  "-server-dscp",(char*)&dscp },
		        	{ FWPARM_DSCP,  "-dscp",       (char*)&dscp },
	 			{ 0,		0,	       0            }};

	strlcpy(foo,email,MAXLINE);
	get_email(foo,z);

	if (strtok(z,"@") != NULL)
		thost=strtok(NULL,"\0");
	else {
		strlcpy(myrelay,MailHub,MAXLINE);
		return;
	}
	if (!thost) {
		strlcpy(myrelay,MailHub,MAXLINE);
		return;
	}
	cf=cfg_get("relay", proxy_confp);
	while (cf != (Cfg *)0) {
		int i;
		if((cf->argc < 1) || (cf->argv[0][0] == '-')) {
			die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln);
		}
		for (i = 0; i < cf->argc && cf->argv[i][0] != '-'; ++i) {
			if (!(strcasecmp(cf->argv[i],thost))) {
				proxy_parse_options(cf,options);
				return;
			}
		}
		cf=cfg_get("relay",(Cfg *)0);
	}
	strlcpy(myrelay,MailHub,MAXLINE);
	return;
}


static void
prepare_message()
{
	char	*p;
	char	bar[MAXLINE];
	int 	barsize;
	Cfg	*cf;

	mfrom = xmalloc(MAXLINE);
	qf = xstrdup(QTEMPLATE);
	if ((tfd = mkstemp(qf)) == -1)
		die(EX_IOERR,"error creating temporary file");
	get_header();
	get_email(fromLine(),mfrom);
	addInitialHeaders(tfd);
	addRequiredHeaders(tfd);	
	for(p=headers;p<headere;p+=strlen(p)+1) {
		putToQf(tfd,"%s",p);
	}
	/* End of headers, start body. */
	putToQf(tfd,""); /* Seperate headers and body. */
	while(fgets(bar,sizeof(bar),stdin)) {
		standardize(bar);
		barsize = strlen(bar);
		bar[barsize++] = '\r';
		bar[barsize++] = '\n';
		if (sowrite(tfd,bar,barsize)!=barsize) 
			die(EX_IOERR,"error writing to temporary file");
	}
	close(tfd);

	if (Toption || (from_intranet < 0))
		return;

	cf=cfg_get("milter", proxy_confp);
	while (cf != (Cfg *)0) {
		int mfstate;
		proxy_update_operation("MILTER");
		if((cf->argc < 1) || (cf->argv[0][0] == '-')) {
			die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln);
		}
		if (((mfstate = milter_inspect_mail("ssmtp",riaddr,riaddr,
						    cf->argv[0],
					    	    qf,mfrom,&rcpts)) & MS_ER))
			die(EX_TEMPFAIL,"milter failed");
		if (mfstate & MS_RJ)
			die(EX_OK,"message rejected by milter");
		cf=cfg_get("milter",(Cfg *)0);
	}
	if (from_intranet) {
		cf=cfg_get("intranet-milter", proxy_confp);
		while (cf != (Cfg *)0) {
			int mfstate;
			proxy_update_operation("MILTER");
			if((cf->argc < 1) || (cf->argv[0][0] == '-')) {
				die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln);
			}
			if (((mfstate = milter_inspect_mail("ssmtp",riaddr,
							    riaddr, cf->argv[0],
					    	    	    qf,mfrom,&rcpts)) & 
								MS_ER))
				die(EX_TEMPFAIL,"milter failed");
			if (mfstate & MS_RJ)
				die(EX_OK,"message rejected by milter");
			cf=cfg_get("intranet-milter",(Cfg *)0);
		}
	} else {
		cf=cfg_get("extranet-milter", proxy_confp);
		while (cf != (Cfg *)0) {
			int mfstate;
			proxy_update_operation("MILTER");
			if((cf->argc < 1) || (cf->argv[0][0] == '-')) {
				die(EX_CONFIG,"fwtkcfgerr: missing parameter, line %d",cf->ln);
			}
			if (((mfstate = milter_inspect_mail("ssmtp",riaddr,
							    riaddr, cf->argv[0],
					    	    	    qf,mfrom,&rcpts)) & 
								MS_ER))
				die(EX_TEMPFAIL,"milter failed");
			if (mfstate & MS_RJ)
				die(EX_OK,"message rejected by milter");
			cf=cfg_get("extranet-milter",(Cfg *)0);
		}
	} 
}

/*
 * ssmtp -- send the message (exactly one) from stdin to the smtp
 *	port on the mailhub.
 */
int 
ssmtp()  
{

	char 	buffer[MAXLINE],
             	relay[MAXLINE],crelay[MAXLINE],*p,c[MAXLINE];
	int 	fd, i, newArgC =0;
	char 	**newrcpts = NULL;
	int	rc;

	get_relay(rcpts[0],relay);
	syslog(LLEV,"relay to %.128s",relay); 

	strncpy(proxy_stats.dst,relay,sizeof(proxy_stats.dst));
	proxy_update_operation("CONNECT");

	/* Now to the delivery of the message */
	if ((fd=conn_server(relay,PORTNUMBER,0,buffer)) < 0) {
		die(EX_IOERR,"can't open the smtp port (%d) on %s: %s.",
			PORTNUMBER,relay,buffer);
	}
	if (dscp)
		proxy_set_dscp(fd,dscp);
	if (getOkFromSmtp(fd,buffer) == NO) {
    		die(EX_IOERR,"didn't get initial OK message from smtp server.");
	}
	if (Verbose) {
		(void) fprintf(stderr,"Connected to smtp server %s\n",MailHub);
	}
	if (LogLevel > 0) {
		syslog(LLEV,"Connected to smtp server %s\n",MailHub);
	}

	/* Send "HELO", hostname. */
	proxy_update_operation("HELO");
	putToSmtp(fd,"HELO %s",HostName);
	if (getOkFromSmtp(fd,buffer) == NO) {
    		die(EX_NOHOST,"server didn't accept hostname %s, replied \"%s\".",
			HostName,buffer);
	}

	proxy_update_operation("MAIL FROM");
	putToSmtp(fd,"MAIL FROM:<%s>",mfrom);

	if (getOkFromSmtp(fd,buffer) == NO) {
    		die(1,"smtp server didn't accept MAIL From, replied \"%s\".",
			buffer);
	}
	if (Verbose) {
		(void) fprintf(stderr,"Server accepted MAIL FROM: %s line.\n",
			  fromLine());
	}
	if (LogLevel > 0) {
		syslog(LLEV,"Server accepted MAIL FROM: %s line.\n",
			  fromLine());
	}

	/* Send all the To: adresses. */
	/* Either we're using the -t option, or we're using the arguments */
	rc = EX_NOUSER;
	for (i=0; rcpts[i] != NULL; i++) {
		p=strtok(rcpts[i],",");
		while(p) {
			get_relay(p,crelay);
			if (!strcmp(relay,crelay)) {
				get_email(p,c);
				if (permit_routed || !index(c,'%')) {
					proxy_update_operation("RCPT TO");
					putToSmtp(fd,"RCPT TO:<%s>",c);
					if (getOkFromSmtp(fd,buffer) == NO) {
						syslog(LLEV,"smtp server didn't accept RCPT To: command, replied \"%s\".",
							buffer);
					} else
						rc = 0;
				} else
					syslog(LLEV,"routed delivery for %.256s denied",c);
			} else {
				syslog(LLEV,"relay=%s/%s for %s",relay,crelay,p);
				newArgC++;
				addlist(p,&newrcpts);
			}
			p=strtok(NULL,",");
		}
	}
	if (rc) {
		putToSmtp(fd,"QUIT");
		unlink(qf);
		die(rc,"no valid receipients for this message");
	}
	if (Verbose) {
		(void) fprintf(stderr,"Server accepted To: line(s).\n");
	}
	if (LogLevel > 0) {
		syslog(LLEV,"Server accepted To: line(s).\n");
	}

	/* Send DATA. */
	proxy_update_operation("DATA");
	putToSmtp(fd,"DATA");
	if (getFromSmtp(fd,buffer) != 3) {
		/* Oops, we were expecting "354 send your data". */
    		die(EX_PROTOCOL,"smtp server didn't accept DATA, replied \"%s\".",
			buffer);
	}
	if (Verbose) {
		(void) fprintf(stderr,"Message body transmission started.\n");
	}

	/* Send headers, with optional From: rewriting. */

	send_body(fd);
	putToSmtp(fd,".");

	proxy_update_operation("QUIT");
	if (getOkFromSmtp(fd,buffer) == NO) {
		die(EX_PROTOCOL,"smtp server wouldn't accept message, replied \"%s\".",
			buffer);
	}
	if (Verbose) {
		(void) fprintf(stderr,"Message body transmission complete.\n");
	}
	/* Close conection. */
	(void) signal(SIGALRM,SIG_IGN); 
		putToSmtp(fd,"QUIT");
	(void) getOkFromSmtp(fd,buffer);
	(void) close(fd);
	if (newArgC) {
		freelist(&rcpts);
		rcpts = newrcpts;
		ssmtp();
	}
	return rc;
}


/*
** Supporting libraries -- i/o.
*/

/*
 * getOkFromSmtp -- get a line and test the three-number string
 *      at the beginning.  If it starts with a 2, it's OK.
 */

int 
getOkFromSmtp(int fd, char *response) 
{ 

	return (getFromSmtp(fd,response) == 2)? YES: NO;
}


/*
 * getFromSmtp -- get a line and return the initial digit.  Deal with
 *	continuation lines by reading to the last (non-continuation) line.
 */

int 
getFromSmtp(int fd, char *response) 
{ 
	do {
		if (sogets(fd,response,MAXLINE) <= 0 ) {
			*response = '\0';
			return NO;
		}
	} while (response[3] == '-');
	if (LogLevel > 0) {
		syslog(LLEV,"Received \"%s\" from smtp port.\n",response);
	}
	return atoi(response) / 100;
}
	
/*
 * main -- make the program behave like sendmail, then call ssmtp.
 */

int 
main(int argc, char *argv[]) 
{
	/* Try to be bulletproof (:-)) */
	(void) signal(SIGHUP,SIG_IGN);
	(void) signal(SIGINT,SIG_IGN);
	(void) signal(SIGTTIN,SIG_IGN);
	(void) signal(SIGTTOU,SIG_IGN);

#ifndef LOG_DAEMON
	openlog("ssmtp",LOG_PID);
#else
	openlog("ssmtp",LOG_PID|LOG_NDELAY,LFAC);
#endif

#if defined(linux) || defined (SOLARIS)
	initsetproctitle(argc,argv);
#endif

	if((proxy_confp = cfg_read("ssmtp")) == (Cfg *)-1)
		exit(EX_CONFIG);

	proxy_conf_userid(proxy_confp);
	proxy_conf_groupid(proxy_confp);
	proxy_conf_chroot(proxy_confp);
	proxy_chroot_setugid();

	baddir = proxy_conf_string(proxy_confp,"baddir");

	strncpy(proxy_name,argv[0],sizeof(proxy_name) - 1);

	proxy_update_operation("INIT");
	if (gethostname(HostName,sizeof(HostName)) == ERR) {
		die(EX_OSERR,"fwtksyserr: can't find the name of this host, %s, exiting.",
			"(an impossible condition)");
	}
	if ((Sender= getpwuid(getuid())) == NULL) {
		die(EX_OSERR,"couldn't find password entry for sender (uid %d).",
			getuid());
	}

	strlcpy(DateString, arpadate(NULL), sizeof(DateString));
	doOptions(argc,argv);
	getConfig();
	if (Toption)
		rcpts = NULL;
	prepare_message();
	exit(ssmtp());
}
