/*

    File: smtp.c
    
    Copyright (C) 1999,2000,2003,2004 by Wolfgang Zekoll <wzk@quietsche-entchen.de>

    This source is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This source is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <errno.h>
#include <stdarg.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <signal.h>
#include <netdb.h>
#include <netinet/in.h>
#include <syslog.h>
#include <sys/time.h>

#include "smtp.h"
#include "acp.h"
#include "ip-lib.h"
#include "lib.h"


	/*
	 * I/O functions - taken from ftp.proxy
	 */

int getc_fd(smtp_t *x, int fd, int nooptime)
{
	int	c;
	bio_t	*bio;

	if (fd == 0)
		bio = &x->cbuf;
	else if (fd == x->sin)
		bio = &x->sbuf;
	else {
		syslog(LOG_NOTICE, "-PROXY: internal bio/fd error");
		exit (1);
		}

	if (bio->here >= bio->len) {
		int	rc, max, bytes;
		struct timeval tov;
		fd_set	available, fdset;

		bio->len = bio->here = 0;

		FD_ZERO(&fdset);
		FD_SET(fd, &fdset);
		max = fd;

		bytes = 0;
		while (1) {
			available = fdset;
			tov.tv_sec  = x->config->timeout;
			tov.tv_usec = 0;

			if (debug >= 2)
				fprintf (stderr, "select max= %d\n", max);

			rc = select(max + 1, &available, (fd_set *) NULL, (fd_set *) NULL, &tov);
			if (rc < 0) {
				syslog(LOG_NOTICE, "-PROXY: select() error: %s", strerror(errno));
				break;
				}
			else if (rc == 0) {
				syslog(LOG_NOTICE, "-ERR: connection timed out: client= %s, server= %s",
					x->client, x->servername);
				return (-1);
				}

			if (FD_ISSET(fd, &available)) {
				if ((bytes = read(fd, bio->buffer, sizeof(bio->buffer) - 2)) <= 0) {
					if (debug != 0) {
						if (bytes == 0)
							fprintf (stderr, "received zero bytes on fd %d\n", fd);
						else
							fprintf (stderr, "received %d bytes on fd %d, errno= %d, error= %s\n", bytes, fd, errno, strerror(errno));
						}

					return (-1);
					}

				break;
				}
			}

		bio->len  = bytes;
		bio->here = 0;
		}

	if (bio->here >= bio->len)
		return (-1);

	c = (unsigned char) bio->buffer[bio->here++];
	return (c);
}

char *readline_fd(smtp_t *x, int fd, char *line, int size, int nooptime)
{
	int	c, k;

	*line = 0;
	size = size - 2;

	c = getc_fd(x, fd, nooptime);
	if (c < 0)
		return (NULL);
	else if (c == 1) {
		strcpy(line, "\001");
		return (line);
		}

	k = 0;
	while (c > 0  &&  c != '\n'  &&  c != 0) {
		if (k < size)
			line[k++] = c;

		c = getc_fd(x, fd, nooptime);
		}

	line[k] = 0;
	noctrl(line);

	return (line);
}


char *cfgets(smtp_t *x, char *line, int size, int nooptime)
{
	char	*p;

	*line = 0;
	if ((p = readline_fd(x, 0, line, size, nooptime)) == NULL)
		return (NULL);
	else if (debug != 0)
		fprintf (stderr, "CLI >>>: %s\n", p);

	return (line);
}

int cfputs(smtp_t *x, char *line)
{
	if (debug)
		fprintf (stderr, ">>> CLI: %s\n", line);

	/*
	 * Something to do: handler "\r\n" the same way as for the
	 * server.
	 */

	write(1, line, strlen(line));
	write(1, "\r\n", 2);

	return (0);
}

char *sfgets(smtp_t *x, char *line, int size)
{
	char *p;

	*line = 0;
	if ((p = readline_fd(x, x->sin, line, size, 0)) == NULL)
		return (NULL);
	else if (debug != 0)
		fprintf (stderr, "SVR >>>: %s\n", p);

	return (line);
}

int sfputs(smtp_t *x, char *format, ...)
{
	char	buffer[4096];	/* Puffer auf 4k gesetzt, wegen langen Zeilen */
	va_list	ap;

	va_start(ap, format);
	vsnprintf (buffer, sizeof(buffer) - 10, format, ap);
	va_end(ap);

	if (debug)
		fprintf (stderr, ">>> SVR: %s\n", buffer);

	strcat(buffer, "\r\n");
	write(x->sout, buffer, strlen(buffer));

	return (0);
}

int sfputc(smtp_t *x, char *command, char *parameter, char *line, int size, char **here)
{
	int	rc;
	char	*p, buffer[300];

	if (command != NULL  &&  *command != 0) {
		if (parameter != NULL  &&  *parameter != 0)
			snprintf (buffer, sizeof(buffer) - 2, "%s %s", command, skip_ws(parameter));
		else
			copy_string(buffer, command, sizeof(buffer));

		sfputs(x, "%s", buffer);
		}
	
	if (sfgets(x, line, size) == NULL) {
		if (debug != 0)
			fprintf (stderr, "server disappered in sfputc(), pos #1\n");

		return (-1);
		}

	rc = strtol(line, &p, 10);
	p = skip_ws(p);
	if (*p == '-') {
		cfputs(x, line);
		while (1) {
			if (sfgets(x, line, size) == NULL) {
				if (debug != 2)
					fprintf (stderr, "server disappered in sfputc(), pos #2\n");

				return (-1);
				}

			p = noctrl(line);
			if (*p == ' ')
				/* weiter */ ;
			else if (atoi(line) == rc) {
				if (strlen(line) == 3)
					break;
				else if (strlen(line) > 3  &&  line[3] == ' ')
					break;
				}

			cfputs(x, line);
			}
		}

	p = skip_ws(p);
	if (here != NULL)
		*here = p;

	return (rc);
}



int reset_connection(smtp_t *x)
{
	x->state      = WAITING;

	x->nrcpt      = 0;
	x->rcpt.here  = 0;
/*	*x->rcpt.list = 0; */

	*x->sender    = 0;
	x->nrcpt      = 0;
	*x->jobid     = 0;
	*x->msgid     = 0;
	x->size       = 0;

	return (0);
}




char *get_emailadr(char *envelope, char *email, int size)
{
	char	*p, *r;

	*email = 0;

	/*
	 * Sonderfall: Es ist moeglich, dass die envelope Adresse `<>'
	 * ist.  In diesem liefern wir sie unveraendert zurueck.  Das
	 * beisst sich nicht mit Adressen der Form `<<>>', da geschachtelte
	 * spitze Klammern nicht unterstuetzt werden.
	 */

	if (strcmp(envelope, "<>") == 0) {
		strcpy(email, "<>");
		return (email);
		}


	p = envelope;
	if (*p != '<')
		return (email);
	
	p++;
	if ((r = strchr(p, '>')) == NULL)
		return (email);
	else if (r[1] != 0)
		return (email);

	get_quoted(&p, '>', email, size);
	return (email);
}

int check_emailadr(char *emailadr)
{
	char	*domain;

	if (strcmp(emailadr, "<>") == 0)
		return (1);
	else if (*emailadr == 0)
		return (0);
/*	else if (*emailadr == '@')
 *		return (0);
 */
	else if ((domain = strchr(emailadr, '@')) == NULL)
		return (0);
/*	else if (domain[1] == '0')
 *		return (0);
 */
	else if (strchr(&domain[1], '@') != NULL)
		return (0);
	else if (strchr(emailadr, '!') != NULL  /* ||  strchr(emailadr, '%') != NULL */)
		return (0);
	else if (strchr(emailadr, ':') != NULL  ||  strchr(emailadr, ',') != NULL)
		return (0);
	
	return (1);
}

int search_allowlist(char *emailadr, char *list)
{
	char	*p, *domain, pattern[200];

	if (strcmp(emailadr, "<>") == 0)
		return (1);

	domain = strchr(emailadr, '@');
	if (domain == NULL  ||  *domain == 0) {

		/*
		 * Das kann eigentlich nicht passieren, die E-Mail Adresse
		 * wurde schon auf genau ein @-Zeichen getestet.
		 */

		return (0);
		}
	else if (list == NULL  ||  *list == 0) {
		
		/*
		 * Kann eigentlich auch nicht vorkommen.
		 */

		return (0);
		}

	p = list;
	while ((p = skip_ws(p)), *get_quoted(&p, ',', pattern, sizeof(pattern)) != 0) {
		noctrl(pattern);
		if (*pattern == '@'  &&  strpcmp(domain, pattern) == 0)
			return (1);
		else if (strpcmp(emailadr, pattern) == 0)
			return (1);
		}
	
	return (0);
}


int addrcpt(smtp_t *x, char *rcpt)
{
	int	len;
	char	adr[200];

	/*
	 * Remove angle brackets from e-mail address ...
	 */

	copy_string(adr, &rcpt[1], sizeof(adr));
	len = strlen(adr);
	if (len > 0)
		adr[len-1] = 0;

	/*
	 * ... and add it to the list.
	 */

	len = strlen(rcpt = adr);
	if ((x->rcpt.here + len + 10) >= x->rcpt.max) {
		x->rcpt.max += 1024 + len;
		x->rcpt.list = reallocate(x->rcpt.list, x->rcpt.max);
		}

	if (x->rcpt.here > 0)
		x->rcpt.list[x->rcpt.here++] = ' ';

	strcpy(&x->rcpt.list[x->rcpt.here], rcpt);
	x->rcpt.here += len;

	return (0);
}

	/*
	 * spooldata() - liest entweder vom client (bei "fp == NULL")
	 * oder aus der angegebenen Datei.  Das Ausgabeziel der Daten
	 * ist in beiden Faellen der SMTP-Server.
	 */

int spooldata(smtp_t *x, FILE *fp)
{
	int	lineno, isheader;
	char	*p, line[2048];

	lineno   = 0;
	isheader = 1;
	while (1) {
		if (fp != NULL) {
			if (fgets(line, sizeof(line), fp) == NULL) {

				/*
				 * Provide the end-of-data line and jump.
				 */

				strcpy(line, ".");
				goto end;
				}
			}
		else {
			if (cfgets(x, line, sizeof(line), x->config->timeout) == NULL) {
				syslog(LOG_NOTICE, "-CLIENT: client terminated while sending data");
				return (-1);
				}
			}

		noctrl(line);


		/*
		 * Insert Received header line.
		 */

		if (lineno == 0) {
			if (x->config->droppath != 2) {
				char	buffer[1024];

				snprintf (buffer, sizeof(buffer) - 2, "Received: from %s (%s) by %s",
						x->client, x->ipnum, x->hostname);
				sfputs(x, "%s", buffer);
				}
			}


		/*
		 * If we are in the mail header we (1) try to read the
		 * mail's message-id and (2) remove the recevied-path
		 * from the header if removal is configured.
		 */

		if (isheader == 1) {
			if (*line == 0) {
				isheader = 0;

				if (*x->msgid == 0) {
					unsigned long now;

					now = time(NULL);
					snprintf (x->msgid, sizeof(x->msgid) - 2, "%lu.%d.%d", now, getpid(), x->mailcount);
					syslog(LOG_NOTICE, "no message id, assuming %s, client= %s", x->msgid, x->client);
					}
				}
			else {
				char	*p, word[80];

				p = line;
				get_word(&p, word, sizeof(word));
				strlwr(word);
				
				if (strcmp(word, "message-id:") == 0) {
					if (*x->msgid != 0) {
						syslog(LOG_NOTICE, "duplicate message id, client= %s", x->client);
						continue;
						}
					else {
						if ((p = strchr(p, '<')) != NULL) {
							p++;
							get_quoted(&p, '>', x->msgid, sizeof(x->msgid));
							}
						}
					}
				else if (strcmp(word, "received:") == 0) {
					if (x->config->droppath != 0)
						continue;
					}
				}
			}


		/*
		 * Send current line to SMTP server.  Notice: If we are
		 * reading from a file we have to provide an additional
		 * dot `.' if the line starts with a dot.
		 */

		p = line;
		if (fp != NULL) {
			if (*line == '.') {
				char	buffer[2050];

				snprintf (p = buffer, sizeof(buffer), ".%s", line);
				}
			}

end:
		if (sfputs(x, "%s", p) != 0) {
			syslog(LOG_NOTICE, "-SERVER: server terminated while receiving data");
			return (-1);
			}


		/*
		 * Did we reach the end of the mail?
		 */

		if (line[0] == '.'  &&  line[1] == 0)
			break;


		x->size += strlen(p) + 2;
		lineno++;
		}
		
	return (0);
}


int proxy_request(config_t *config)
{
	int	rc;
	char	*p, command[10], word[200], line[2048];
	smtp_t	*x;

	x = allocate(sizeof(smtp_t));

	get_client_info(0, x->ipnum, x->client);
	x->mailcount = 0;
	syslog(LOG_NOTICE, "connected to client: %s", x->ipnum);


	if (*config->clientdir != 0) {
		char	logfile[200];
		unsigned long now;
		struct stat sbuf;

		now = time(NULL);
		snprintf (logfile, sizeof(logfile) - 2, "%s/%s", config->clientdir, x->ipnum);
		if (stat(logfile, &sbuf) != 0  ||  (now - sbuf.st_mtime) >= config->accepttime) {
			printf ("421 service unavailable - authenticate with POP3 first\r\n");
			syslog(LOG_NOTICE, "-ERR: client not permitted: %s", x->ipnum);
			goto end;
			}
		}

	if (*config->server != 0) {
		unsigned int port;
		char	server[200];

		copy_string(server, config->server, sizeof(server));
		port = get_port(server, 25);
		
		if ((x->sin = openip(server, port)) < 0) {
			printf ("451 Service unavailable\r\n");
			syslog(LOG_NOTICE, "-ERR: can't connect to server: %s:%u, %s", server, port, strerror(errno));
			exit (1);
			}
			
		x->sout = x->sin;
		copy_string(x->servername, config->server, sizeof(x->servername));
		}
	else if (config->argv != NULL) {
		int	pid, pin[2], pout[2];

		if (pipe(pin) != 0  ||  pipe(pout) != 0) {
			printf ("451 Service unavailable\r\n");
			syslog(LOG_NOTICE, "-PROXY: can't pipe(): %m");
			exit (-1);
			}
		else if ((pid = fork()) < 0) {
			printf ("451 Service unavailable\r\n");
			syslog(LOG_NOTICE, "-PROXY: can't fork(): %m");
			exit (-1);
			}
		else if (pid == 0) {
			dup2(pin[1], 1);
			close (pin[0]);
			
			dup2(pout[0], 0);
			close(pout[1]);
			
			close (2);
			execvp(config->argv[0], config->argv);

			printf ("451 Service unavailable\r\n");
			syslog(LOG_NOTICE, "-PROXY: can't execute: %s: %m", config->argv[0]);
			exit (1);
			}
		else {	
			x->sin  = pin[0];
			close(pin[1]);
			
			x->sout = pout[1];
			close(pout[0]);

			copy_string(x->servername, config->argv[0], sizeof(x->servername));
			}
		}
	else {
		printf ("451 Service unavailable\r\n");
		syslog(LOG_NOTICE, "-PROXY: no server specified");
		exit (1);
		}


	syslog(LOG_NOTICE, "connected to server: %s", x->servername);


	/* Konfiguration uebernehmen
	 */

	x->config = config;


	/* Hostnamen holen
	 */

	gethostname(word, sizeof(word));
	getdomainname(line, sizeof(line));
	snprintf (x->hostname, sizeof(x->hostname) - 2, word, line);
	

	/* Greeting Message vom Sendmail Server lesen, und an
	 * Client schicken.
	 */

	sfgets(x, line, sizeof(line));
	while (line[3] != ' ') {
		if (sfgets(x, line, sizeof(line)) == NULL) {
			syslog(LOG_NOTICE, "-SERVER: lost server while reading server greeting");
			exit (1);
			}
		}
		
	cfputs(x, line);


	/* Wir stellen uns beim lokalen Sendmail Server vor. Die
	 * EHLO-replys werden 'verschluckt' und durch einen eigenen
	 * ersetzt.
	 */

	sfputs(x, "EHLO localhost");
	while (sfgets(x, line, sizeof(line)) != NULL) {
		if (line[3] == ' ')
			break;
		}

	rc = atol(line);
	if (rc != 250) {
		syslog(LOG_NOTICE, "-SERVER: server HELO: status is not 250: %s", line);
		cfputs(x, "421 service unavailable");

		exit (1);
		}
					



	/*
	 *  **  S M T P   M A I N L O O P
	 */

	x->state = WAITING;
	while (1) {
		rc = 0;		/* Server response code loeschen */


		/*
		 * Naechstes Kommando vom Client holen.
		 */

		if (cfgets(x, line, sizeof(line), 0) == NULL) {
			syslog(LOG_NOTICE, "-CLIENT: client closed connection");
			break;
			}


		/* Kommando isolieren
		 */

		p = noctrl(line);
		get_word(&p, command, sizeof(command));
		strupr(command);
		p = skip_ws(p);


		/* QUIT ist immer moeglich.
		 */

		if (strcmp(command, "QUIT") == 0) {
			rc = sfputc(x, "QUIT", "", line, sizeof(line), NULL);
			cfputs(x, line);

			x->state = SEND_QUIT;
			break;
			}


		/* HELP
		 */

		else if (strcmp(command, "HELP") == 0) {
			cfputs(x, "503 no help available");
			}


		/* NOOP 
		 */

		else if (strcmp(command, "NOOP") == 0) {
			rc = sfputc(x, "NOOP", "", line, sizeof(line), NULL);
			cfputs(x, line);
			}


		/* RSET
		 */

		else if (strcmp(command, "RSET") == 0) {
			rc = sfputc(x, "RSET", "", line, sizeof(line), NULL);
			cfputs(x, line);

			reset_connection(x);
			syslog(LOG_NOTICE, "RSET command, client= %s", x->client);
			}


		/* ETRN
		 */

		else if (strcmp(command, "ETRN") == 0) {
			if (x->config->etrn == 0) {
				cfputs(x, "500 unrecognized command");
				syslog(LOG_NOTICE, "-ERR: ETRN request rejected: client= %s", x->client);
				}
			else {
				if (*get_word(&p, word, sizeof(word)) == 0)
					cfputs(x, "500 ETRN needs parameter");
				else {
					rc = sfputc(x, "ETRN", word, line, sizeof(line), NULL);
					cfputs(x, line);

					if (rc != 250)
						syslog(LOG_NOTICE, "-ERR: ETRN rejected by server, client= %s", x->client);
					}
				}
			}


		/* HELO und EHLO sind auch immer verfuegbar, aber nur
		 * einmal.
		 */

		else if (strcmp(command, "HELO") == 0  ||  strcmp(command, "EHLO") == 0) {
			if (x->helloseen != 0)
				cfputs(x, "503 duplicate HELO/EHLO");
			else if (*get_word(&p, word, sizeof(word)) == 0) {
				snprintf (line, sizeof(line) - 2, "501 %s requires domain name", command);
				cfputs(x, line);
				}
			else {
				if (strcmp(command, "HELO") == 0) {
					snprintf (line, sizeof(line) - 2, "250 SMTP server v%s ready - %s [%s]", VERSION, x->client, x->ipnum);
					cfputs(x, line);
					}
				else {
					snprintf (line, sizeof(line) - 2, "250-SMTP server v%s ready - %s [%s]", VERSION, x->client, x->ipnum);
					cfputs(x, line);
					
					cfputs(x, "250-8BITMIME");

					if (x->config->etrn != 0)
						cfputs(x, "250-ETRN");

					cfputs(x, "250 HELP");
					}
					
				x->helloseen = 1;
				}
			}


		/* MAIL, SEND, SOML, SAML
		 *
		 * Laut RFC 821 kann das MAIL Kommando jederzeit abgesetzt
		 * werden, es macht dabei einen impliziten SMTP-Reset.  Der
		 * real existierende Sendmail will davon aber nichts wissen.
		 */

		else if (strcmp(command, "MAIL") == 0  || strcmp(command, "SEND") == 0  ||
		    strcmp(command, "SOML") == 0  || strcmp(command, "SAML") == 0) {
			
			get_quoted(&p, ':', word, sizeof(word));
			if (strcasecmp(word, "FROM") != 0)
				cfputs(x, "500 syntax error");
			else if (*x->sender != 0)
				cfputs(x, "503 sender already specified");
			else {
				int	allowed;
				char	sender[200], emailadr[200];
				
				p = skip_ws(p);
				get_word(&p, sender, sizeof(sender));
				strlwr(sender);


				/*
				 * Wir machen ein paar grundsaetzliche Tests mit
				 * der Absenderadresse:
				 *
				 *  - Ist die Adresse von spitzen Klammern
				 *    umgeben?
				 * ...
				 */

				allowed = 1;
				get_emailadr(sender, emailadr, sizeof(emailadr));
				if (*emailadr == 0)
					allowed = 0;

				/*
				 * ...
				 *  - Enthaelt die Adresse mindestens ein @-Zeichen?
				 *  - Enthaelt die Adresse genau ein @-Zeichen?
				 *  - Ist in der Adresse kein !- und kein %-Zeichen
				 *    enthalten.
				 * ...
				 */

				else if (check_emailadr(emailadr) == 0)
					allowed = 0;

				/*
				 * ...
				 *  - Schliesslich wird ggf. noch getestet,
				 *    ob die Absenderadresse auch auf der
				 *    allow-Liste steht.
				 *
				 * Mit den Empfaengeradressen werden die gleichen Tests
				 * durchgefuehrt.
				 */

				else if ((p = x->config->senderlist) == NULL  ||  *p == 0)
					allowed = 1;	/* kein Adresstest */
				else 
					allowed = search_allowlist(emailadr, x->config->senderlist);


				if (allowed == 0) {
					char	line[300];

					snprintf (line, sizeof(line) - 2, "550 not allowed: %s", sender);
					cfputs(x, line);
					syslog(LOG_NOTICE, "-FROM: sender not allowed: %s, client= %s", sender, x->client);
					}
				else {
					char	smtpcmd[20];

					/*
					 * Der Absender ist soweit ok.  Was wir jetzt ggf. machen muessen
					 * ist die Absenderdomain neu schreiben.
					 */

					copy_string(x->origsender, sender, sizeof(x->origsender));
					if (*x->config->senderdomain != 0) {
						char	*p, newadr[80];

						if ((p = strchr(sender, '@')) != NULL)
							*p = 0;

						p = sender;
						if (*p == '<')		/* Sollte eigentlich immer der Fall sein */
							p++;

						snprintf (newadr, sizeof(newadr) - 2, "<%s@%s>", p, x->config->senderdomain);
						copy_string(sender, newadr, sizeof(sender));
						}

					snprintf (smtpcmd, sizeof(smtpcmd) - 2, "%s FROM:", command);
					rc = sfputc(x, smtpcmd, sender, line, sizeof(line), NULL);
					cfputs(x, line);

					if (rc == 250) {
						copy_string(x->sender, sender, sizeof(sender));
						x->state = MAIL_SEEN;
						syslog(LOG_NOTICE, "sender: %s", x->sender);
						}
					else {
						syslog(LOG_NOTICE, "-FROM: sender rejected: %s, client= %s, resp= %s", sender, x->client, line);
						}
					}
				}
			}


		/* RCPT
		 */

		else if (strcmp(command, "RCPT") == 0) {
			get_quoted(&p, ':', word, sizeof(word));
			if (strcasecmp(word, "TO") != 0)
				cfputs(x, "500 syntax error");
			else if (x->state != MAIL_SEEN  &&  x->state != RCPT_SEEN)
				cfputs(x, "503 specify sender first");
			else {
				int	allowed;
				char	rcpt[200], emailadr[200];
				
				p = skip_ws(p);
				get_word(&p, rcpt, sizeof(rcpt));
				strlwr(rcpt);

				get_emailadr(rcpt, emailadr, sizeof(emailadr));
				if (*emailadr == 0)
					allowed = 0;
				else if (check_emailadr(emailadr) == 0)
					allowed = 0;
				else if ((p = x->config->rcptlist) == NULL  ||  *p == 0)
					allowed = 1;
				else 
					allowed = search_allowlist(emailadr, x->config->rcptlist);


				if (allowed == 0) {
					char	line[300];

					snprintf (line, sizeof(line) - 2, "550 no such user: %s", rcpt);
					cfputs(x, line);
					syslog(LOG_NOTICE, "-RCPT: recipient not allowed: %s, client= %s", rcpt, x->client);
					}
				else {
					rc = sfputc(x, "RCPT TO:", rcpt, line, sizeof(line), NULL);
					cfputs(x, line);

					if (rc == 250  ||  rc == 251) {
						addrcpt(x, rcpt);
						x->nrcpt++;
						x->state = RCPT_SEEN;

						syslog(LOG_NOTICE, "rcpt: %s", rcpt);
						}
					else {
						syslog(LOG_NOTICE, "-RCPT: recipient rejected: %s, client= %s, resp= %s", rcpt, x->client, line);
						}
					}
				}
			}


		/* DATA
		 */

		else if (strcmp(command, "DATA") == 0) {
			x->mailcount++;

			if (x->state != RCPT_SEEN)
				cfputs(x, "503 specify recipients first");
			else {
				FILE	*fp;

				if (*x->config->acp != 0) {
					run_acp(x, line, sizeof(line));

					rc = atoi(line);
					if (rc == 250  ||  rc == 354)
						/* ok for us, continue processing */ ;
					else if (rc >= 400  &&  rc < 600) {
						syslog(LOG_NOTICE, "-DATA: acp status: %s", line);
						cfputs(x, line);

						sfputc(x, "RSET", "", line, sizeof(line), NULL);
						reset_connection(x);
						if (rc == 421)
							break;

						continue;
						}
					else {
						syslog(LOG_NOTICE, "-PROXY: acp status error: %s", line);
						cfputs(x, "421 server error");
						break;
						}
					}

				if (*x->config->ccp == 0) {
					rc = sfputc(x, "DATA", "", line, sizeof(line), NULL);
					cfputs(x, line);
					}
				else {
					/*
					 * Ok, in case that we have a command control
					 * program (a ccp) we have interesting things
					 * to do.
					 *
					 * First we create a temporary spool file to
					 * store the mail data from the client ...
					 */

					gettmpfile(x->spoolfile, sizeof(x->spoolfile));
					cfputs(x, "354 send data");
					if ((rc = receivemail(x)) < 0)
						break;
					else if (rc != 0)
						copy_string(line, "421 server error", sizeof(line));
					else {

						/*
						 * ... and then we call the ccp and look at
						 * it's SMTP compatible return code.
						 */

						run_ccp(x, line, sizeof(line));
						}

					rc = atoi(line);
					if (rc == 250  ||  rc == 354)
						/* ok for us, let's pass the e-mail to the server */ ;
					else if (rc >= 400  &&  rc < 600) {

						/*
						 * Somethings wrong with the e-mail.  Log the event,
						 * pass the message to the client, reset the connection
						 * to the server and continue.
						 */

						syslog(LOG_NOTICE, "-DATA: ccp status: %s", line);
						cfputs(x, line);

						sfputc(x, "RSET", "", line, sizeof(line), NULL);
						reset_connection(x);
						if (rc == 421)
							break;

						/*
						 * ... and stop processing this e-mail
						 */

						continue;
						}
					else {
						/*
						 * Something is wrong with the ccp.  We terminate
						 * with a 421 response code.
						 */

						syslog(LOG_NOTICE, "-PROXY: ccp status error: %s", line);
						cfputs(x, "421 server error");
						break;
						}

					/*
					 * If we made it here we have to send the DATA
					 * command to the server.  Response checking is
					 * below.
					 */

					rc = sfputc(x, "DATA", "", line, sizeof(line), NULL);
					}

				if (rc == 354) {
					fp = NULL;

					if (*x->spoolfile != 0) {
						if ((fp = fopen(x->spoolfile, "r")) == NULL) {
							cfputs(x, "421 server error");
							syslog(LOG_NOTICE, "-PROXY: can't open spoolfile: %s, error= %s", x->spoolfile, strerror(errno));
							exit (1);
							}
						}

					if ((rc = spooldata(x, fp)) == 0) {
						sfgets(x, line, sizeof(line));
						cfputs(x, line);
						rc = atoi(line);

						if (rc == 250) {
							p = line;
							get_word(&p, word, sizeof(word));
							get_word(&p, x->jobid, sizeof(x->jobid));
							}
						}

					if (fp != NULL) {
						fclose (fp);
						unlink(x->spoolfile);
						}
					}

				syslog(LOG_NOTICE, "+MAIL: server= %s, client= %s, sender= %s, nrcpt= %d, size= %ld, message-id= <%s>, resp= %s",
							x->servername, x->client, x->sender, x->nrcpt, x->size,
							x->msgid, line);

				reset_connection(x);
				x->state = WAITING;
				}
			}


		/* 
		 * Other commands are not supported.
		 */

		else {
			cfputs(x, "500 unrecognized command");
			syslog(LOG_NOTICE, "-SMTP: received unknown command: %s", command);
			}


		/*
		 * Final status code checking - this assumes that the `rc'
		 * variable holds the current server status response code.
		 */

		if (rc == 421) {
			syslog(LOG_NOTICE, "-ERR: sendmail returned 421, state= %d, command= %s", x->state, command);
			break;
			}
		else if (rc == -1) {
			syslog(LOG_NOTICE, "terminating (sendmail terminated)");
			x->state = NO_SENDMAIL;

			break;
			}
		}

	if (x->state != SEND_QUIT  &&  x->state != NO_SENDMAIL) {
		sfputs(x, "QUIT", "");
		sfgets(x, line, sizeof(line));
		rc = atoi(line);
		}

end:
	syslog(LOG_NOTICE, "+OK: client %s disconnecting, %d mails", x->client, x->mailcount);
	return (0);
}



syntax highlighted by Code2HTML, v. 0.9.1