#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

#include <vxWorks.h>
#include <sockLib.h>
#include <socket.h>
#include <inetLib.h>
#include <hostLib.h>
#include <remLib.h>

/*****************************************************************
*  Lyor Goldstein.             Email :  lyorg@scorpio.com
*  Scorpio Communications Ltd.
*  Software group
*****************************************************************/

/*---------------------------------------------------------------------------*/


#define SMTP_E_SYS_STAT    211 /* System status, or system help reply */
#define SMTP_E_HELP_MSG		214 /* Help message */
#define SMTP_E_DOMAIN_RDY  220 /* <domain> Service ready */
#define SMTP_E_DOMAIN_CLS  221 /* <domain> Service closing transmit channel */
#define SMTP_E_ACTION_OK   250 /* Requested mail action okay, completed */
#define SMTP_E_USR_NLOCAL  251 /* User not local; will forward to <path> */

#define is_bad_smtp_rcode(c) (((c) < 200) || ((c) >= 300))

#define SMTP_E_START_INP   354 /* Start mail input; end with <CRLF>.<CRLF> */
          
#define SMTP_E_SRVC_NA     421 /* <domain> Service not available */
#define SMTP_E_ACTION_NA   450 /* Requested mail action not taken */
#define SMTP_E_ACTION_ABRT 451 /* Requested action aborted */
#define SMTP_E_ACTION_MEM  452 /* insufficient system storage */

#define SMTP_E_SYNTAX      500 /* Syntax error, command unrecognized */
#define SMTP_E_PARAMS      501 /* Syntax error in parameters or arguments */
#define SMTP_E_CMD_NA      502 /* Command not implemented */
#define SMTP_E_BAD_CMD_SEQ 503 /* Bad sequence of commands */
#define SMTP_E_BAD_PARAM   504 /* Command parameter not implemented */
#define SMTP_E_MBOX_NA     550 /* mailbox unavailable */
#define SMTP_E_USR_TRY     551 /* User not local; please try <forward-path> */
#define SMTP_E_MEM_OVRFLW  552 /* exceeded storage allocation */
#define SMTP_E_MBOX_NAME   553 /* mailbox name not allowed */
#define SMTP_E_TRAN_FAILED 554 /* Transaction failed */

/*---------------------------------------------------------------------------*/


static int smtpOpen (const char hname[], int pnum)
{
	int s=(-1), t=1;
	struct sockaddr_in sin;
	struct sockaddr *saddrp=(struct sockaddr *) &sin;

	bzero((char *) &sin, (sizeof sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(pnum);

	if (isdigit((int) hname[0]))
	{
		if ((sin.sin_addr.s_addr=inet_addr(hname)) == ((u_long) ERROR))
			return (-1);
	}
	else
	{
		if ((sin.sin_addr.s_addr=hostGetByName(hname)) == ERROR)
			return (-1);
	}

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s < 0)
		return (-1);

	if((setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void *)&t,(sizeof t))) != OK)
	{
		close(s);
		return (-1);
	}

	if((setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(void *)&t,(sizeof t))) == -1)
	{
		close(s);
		return (-1);
	}

	if (connect(s, saddrp, (sizeof sin)) != OK)
	{
		close(s);
		return (-1);
	}

	return s;
}
/*---------------------------------------------------------------------------*/

static STATUS smtpClose (FILE *fp, int s, const char reason[], STATUS retval)
{
	if ((fp != NULL) && (fp != stdin))
		fclose(fp);
	if (s > 0)
		close(s);
	return retval;
}

/*---------------------------------------------------------------------------*/

static STATUS smtpGetResponse (int s, char l[], int *rcode)
{
	char *lp=l;

	do
	{
		if (read(s, lp, sizeof(char)) != sizeof(char))
			return ERROR;
		if (*lp == '\n')
			break;
		lp++;
	} while (*(lp-1) != '\n');

	*lp = '\0';
	if (!isdigit((int) l[0]))
		return ERROR;

	sscanf(l, "%d", rcode);
#ifdef VERBOSE
	fprintf(stderr, "RSP: %s\n", l);
#endif

	return OK;
}

/*---------------------------------------------------------------------------*/


static STATUS smtpSendCmdSync (int s, FILE *fp,
										 const char cmd[], char l[],
										 int expcode)
{
	int rcode;

	if (write(s, cmd, strlen(cmd)) <= 0)
		return smtpClose(fp, s, cmd, ERROR);

	/* skip all OK codes til ACTION-OK code found */
	do
	{
		if (smtpGetResponse(s, l, &rcode) != OK)
			return smtpClose(fp, s, l, ERROR);

		if (rcode == expcode)
			return OK;

		if (is_bad_smtp_rcode(rcode))
			return smtpClose(fp, s, l, ERROR);
	} while (rcode != SMTP_E_ACTION_OK);

	return OK;
}

/*---------------------------------------------------------------------------*/


static STATUS smtpAddRecepient (int s, const char rcpt[], char l[], int *rcode)
{
	sprintf(l, "RCPT TO: %s\n", rcpt);

	if (write(s, l, strlen(l)) <= 0)
		return smtpClose(NULL, s, "'RCPT TO:' failed", ERROR);

	if (smtpGetResponse(s, l, rcode) != OK)
		return smtpClose(NULL, s, "bad 'RCPT TO:' response", ERROR);

	return OK;
}

/*---------------------------------------------------------------------------*/

#define MAX_LL 127	/* max input line fro text file */
#define MAX_CMD 79	/* max command/response */

int smtpDataSockOpen (const char host[],
							 const char fromUsr[],
							 const char *tusrs[],
							 int			port)
{
	static const char data_cmd[]="DATA\n";

	int pnum=(port != 0) ? port : IPPORT_SMTP;
	static const char defhost[]="bootHost";
	const char *hname=(host != NULL) ? host : defhost;
	char usr[32], *stp, *enp;
	const char *fusr=fromUsr;
	int s=(-1), rcode, i, tnum;
	char l[MAX_CMD+1];

	if (fusr == NULL)
	{
		remCurIdGet(usr, NULL);
		fusr = usr;
	}

	sprintf(l, "MAIL FROM: %s\n", fusr);

	if ((s=smtpOpen(hname, pnum)) < 0)
		return smtpClose(NULL, s, "'smtpOpen' failed", ERROR);

	if (smtpSendCmdSync(s, NULL, l, l, (-1)) != OK)
		return ERROR;

	for (i=0, tnum=0; tusrs[i] != NULL; i++)
	{
		if (smtpAddRecepient(s,tusrs[i], l, &rcode) != OK)
			return smtpClose(NULL, s, "add recepient", ERROR);

		/* this is a special failure code - it suggests someone else to try - its
		 * suggested path is enclosed in '<...>'
		 */

		if ((rcode == SMTP_E_USR_TRY) && ((stp=strchr(l, '<')) != NULL))
		{
			stp++;

			if ((enp=strchr(stp, '>')) != NULL)
			{
				*enp = '\0';
				strncpy(usr, stp, (sizeof usr));
				usr[(sizeof usr)-1] = '\0';

				/* try the suggested user path */
				if (smtpAddRecepient(s, usr, l, &rcode) != OK)
					return smtpClose(NULL, s, "add recepient", ERROR);
			}
		}

		/* if we get a bad response we go to next recepient and don't count it */
		if ((rcode == SMTP_E_ACTION_OK) || (rcode == SMTP_E_USR_NLOCAL))
			tnum++;
	}

	/* make sure at least ONE recepient */
	if (tnum == 0)
		return smtpClose(NULL, s, "no recepients", ERROR);

	if (smtpSendCmdSync(s, NULL, data_cmd, l, SMTP_E_START_INP) != OK)
		return ERROR;

	return s;
}

/*---------------------------------------------------------------------------*/


STATUS smtpDataSockClose (int s, int add_crlf)
{
	char l[MAX_CMD+1];
	static const char quit_cmd[]="QUIT\n";

	if (add_crlf)
		sprintf(l, "\r\n.\r\n");
	else
		sprintf(l, ".\r\n");

	if (smtpSendCmdSync(s, NULL, l, l, (-1)) != OK)
		return smtpClose(NULL, s, "send '.'", ERROR);

	if (smtpSendCmdSync(s, NULL, quit_cmd, l, SMTP_E_DOMAIN_CLS) != OK)
		return smtpClose(NULL, s, "send 'QUIT' cmd", ERROR);

	return smtpClose(NULL, s, "OK", OK);
}

/*---------------------------------------------------------------------------*/


static STATUS smtpFcopy (int s, const char fname[], int *add_crlf)
{
	FILE *fp = (fname != NULL) ? fopen(fname, "r") : stdin;
	int is_stdin = (fname != NULL) ? 0 : 1;
	char l[MAX_LL+1];

	if (fp == NULL)
		return smtpClose(fp, s, "open file", ERROR);

	while (fgets(l, MAX_LL, fp) == l)
	{
		if (write(s, l, strlen(l)) <= 0)
			return smtpClose(fp, s, "Line send", ERROR);

		if (l[strlen(l)-1] == '\n')
			*add_crlf = 0;
		else
			*add_crlf = 1;
	}

	if (!is_stdin)
		fclose(fp);
	return OK;
}

/*---------------------------------------------------------------------------*/

static STATUS smtpTcopy (int s, const char txt[], int *add_crlf)
{
	if (write(s, txt, strlen(txt)) <= 0)
		return smtpClose(NULL, s, "Text send", ERROR);
	
	if (txt[strlen(txt)-1] == '\n')
		*add_crlf = 0;
	else
		*add_crlf = 1;
	return OK;
}

/*---------------------------------------------------------------------------*/

typedef enum enum_smtp_arg_case {
	SMTP_FILE_CASE,
	SMTP_TEXT_CASE,
	SMTP_BAD_CASE
} SMTP_ARG_CASE;

static STATUS smtpSend (const char host[],
								const char fromUsr[],
								const char *tusrs[],
								int		    port,
								SMTP_ARG_CASE acase,
								void		 *arg)
{
	int s=(-1), add_crlf=1;

	if ((s=smtpDataSockOpen(host, fromUsr, tusrs, port)) == ERROR)
		return smtpClose(NULL, s, "open data sock", ERROR);

	switch(acase)
	{
		case SMTP_FILE_CASE :
			if (smtpFcopy(s, (const char *) arg, &add_crlf) != OK)
				return ERROR;
			break;

		case SMTP_TEXT_CASE :
			if (smtpTcopy(s, (const char *) arg, &add_crlf) != OK)
				return ERROR;
			break;

		default :
			return smtpClose(NULL, s, "Unknown SMTP case", ERROR);
	}

	return smtpDataSockClose(s, add_crlf);
}

/*---------------------------------------------------------------------------*/

S
STATUS smtpFsend (const char host[],
						const char fromUsr[],
						const char *tusrs[],
						int		  port,
						const char fname[])
{
	return smtpSend(host, fromUsr, tusrs, port, SMTP_FILE_CASE, (void *) fname);
}

/*---------------------------------------------------------------------------*/

STATUS smtpSendText (const char host[],	/* name/address - default=bootHost*/
							const char fromUsr[], /* user - default=remCurIdGet() */
							const char toUsr[],
							int port,				/* port - default=IPPORT_SMTP */
							const char txt[])
{
	const char *tusrs[2];

	tusrs[0] = toUsr;
	tusrs[1] = NULL;
	return smtpSend(host, fromUsr, tusrs, port, SMTP_TEXT_CASE, (void *) txt);
}

/*---------------------------------------------------------------------------*/

STATUS smtpSendFile (const char host[],	/* name/address - default=bootHost*/
							const char fromUsr[], /* user - default=remCurIdGet() */
							const char toUsr[],
							int port,				/* port - default=IPPORT_SMTP */
							const char fname[]) /* default=stdin */
{
	const char *tusrs[2];

	tusrs[0] = toUsr;
	tusrs[1] = NULL;

	return smtpSend(host, fromUsr, tusrs, port, SMTP_FILE_CASE, (void *) fname);
}

/*---------------------------------------------------------------------------*/

/* uses the defaults for "smtpSendFile" */
STATUS smtpSendFQuick (const char toUsr[], const char fname[])
{
	return smtpSendFile(NULL, NULL, toUsr, 0, fname);
}

/* uses the defaults for "smtpSendText" */
STATUS smtpSendTQuick (const char toUsr[], const char txt[])
{
	return smtpSendText(NULL, NULL, toUsr, 0, txt);
}

