


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <io.h>
#include <dos.h>
#include <unistd.h>
#include <pwd.h>

#include "sckstdio.h"



	char *mailcmds[] =
		{
		"HELO",
		"MAIL",
		"RCPT",
		"DATA",
		"SEND",
		"SOML",
		"SAML",
		"RSET",
		"VRFY",
		"EXPN",
		"HELP",
		"NOOP",
		"QUIT",
		"TURN",
		NULL
		};

#define MAIL_HELO 1
#define MAIL_MAIL 2
#define MAIL_RCPT 3
#define MAIL_DATA 4
#define MAIL_SEND 5
#define MAIL_SOML 6
#define MAIL_SAML 7
#define MAIL_RSET 8
#define MAIL_VRFY 9
#define MAIL_EXPN 10
#define MAIL_HELP 11
#define MAIL_NOOP 12
#define MAIL_QUIT 13
#define MAIL_TURN 14
#define MAIL_UNKNOWN 0

char remotehost[64];
char hostname[64];
char reversepath[256];
char forwardpath[256];
char *recipients[100];
char mailfrom[512];

char *mail;

int numrecpts=0;
int exitplease;

int uniquefileid=0;


/*******************************************************************/
FILE *fopenuniquename(char *fn)
	{
	FILE *fp;
	char *e = strchr(fn,'.')+1;

	while(1)
		{
		sprintf(e,"%d",uniquefileid);
		if (++uniquefileid>999) uniquefileid = 0;
		if (access(fn,F_OK)) if (fp = fopen(fn,"w")) break;
		}

	return fp;
	}

/*******************************************************************/

void AddRecipient(char *s)
	{
	recipients[numrecpts++] = strdup(s);
	}

void ResetRecipients()
	{
	for(;numrecpts>0;numrecpts--)
		{
		free(recipients[numrecpts-1]);
		}
	}

void mputc(char **mem,char c)
	{
	int len = (*mem)?strlen(*mem):0;
	*mem = realloc(*mem,len+2);

	(*mem)[len+1] = '\0';
	(*mem)[len] = c;

	}

void mputs(char **mem,char *s)
	{
	int n,len=(*mem)?strlen(*mem):0;

	*mem = realloc(*mem,len+strlen(s)+1);
	strcpy(*mem+strlen(*mem),s);
	}

void mprintf(char **mem, char *s, ...)
	{
	va_list ap;
	char buf[1024];

	va_start(ap,s);
	vsprintf(buf,s,ap);

	va_end(ap);

	mputs(mem,buf);
	}

char *mopen()
	{
	char *mem;

	mem = malloc(1);
	mem[0] = '\0';
	return mem;
	}

char *mclose(char **mem)
	{
	if (*mem) free(*mem);
	*mem = NULL;
	}

char *mtofile(char *mem,char *fn,char *mode)
	{
	FILE *fp;

	if (fp = fopen(fn,mode))
		{
		fwrite(mem,strlen(mem),1,fp);
		fclose(fp);
		}
	}

int GetReturnCode(FILE *sin)
	{
	char buf[512];

	SOCKfgets(buf,sizeof(buf)-1,sin);

	return strtol(buf,NULL,10);
	}

void replycode(FILE *sout,int err, char *s)
	{
	FILE *fp;

	fp = fopen("mailserv.log","a");
	fprintf(fp,"Replying %d.\n",err);
	fclose(fp);

	switch(err)
		{

		case 220:
			SOCKfprintf(sout,"220 %s Simple Mail Transfer Service Ready\n",hostname);
			break;

		case 221:
			SOCKfprintf(sout,"221 %s Simple Mail Transfer Service Closing\n",hostname);
			break;

		case 250:
			SOCKfprintf(sout,"250 OK\n",sout);
			break;
		
		case 251:
			SOCKfprintf(sout,"251 User not local; will forward to <%s>\n",s);
			break;

		case 354:
			SOCKfprintf(sout,"354 Start mail input; end with <CRLF>.<CRLF>\n");
			break;

		case 421:
			SOCKfprintf(sout,"421 Service not available, closing transmission channel\n");
			break;

		case 450:
			SOCKfprintf(sout,"450 Requested mail action not taken: mailbox unavailable\n");
			break;

		case 451:
			SOCKfprintf(sout,"451 Requested action aborted: local error in processing\n");
			break;

		case 452:
			SOCKfprintf(sout,"452 Requested action not taken: insufficient system storage\n");
			break;

		case 500:
			SOCKfprintf(sout,"500 Syntax error, command unrecognised\n");
			break;

		case 501:
			SOCKfprintf(sout,"501 Syntax error in parameters or arguments\n");
			break;

		case 502:
			SOCKfprintf(sout,"502 Command not implemented\n");
			break;

		case 503:
			SOCKfprintf(sout,"503 Bad sequence of commands\n");
			break;

		case 504:
			SOCKfprintf(sout,"504 Command parameter not implemented\n");
			break;

		case 550:
			SOCKfprintf(sout,"550 Requested action not taken: mailbox unavailable\n");
			break;

		case 551:
			SOCKfprintf(sout,"551 User not local; please try <%s>\n",s);
			break;

		case 552:
			SOCKfprintf(sout,"552 Requested mail action aborted: exceeded storage allocation\n");
			break;

		case 553:
			SOCKfprintf(sout,"553 Requested action not taken: mailbox name not allowed\n");
			break;
		case 554:
			SOCKfprintf(sout,"554 Transaction failed\n");
			break;

		default:
			break;
		}

	SOCKfflush(sout);
	}

char *GetString(char *msg)
	{
	char *s,*p;
	static char buf[512];

	if (s = strchr(msg,'<'))
		{
		if (p = strchr(msg,'>'))
			{
			strncpy(buf,s+1,p-s-1);
			buf[p-s-1] = '\0';
			return buf;
			}
		}
	return NULL;
	}


char *SOCKfgetline(char *buf,int len,FILE *sin,FILE *sout)
	{
	struct timeval t;
	int s = sin->_file;
	int readbit,writebit,excludebit;
	FILE *fp;
	char *p;
	char *b,c;

	SOCKfflush(sout);

	b = buf;
	while (len-->1)
		{
		writebit = 0;
		readbit = 1<<s;
		excludebit = 0;

		t.tv_sec = 120;
		t.tv_usec = 0;

		if (sin->_cnt<=0)
			if (select(s+1,(struct fd_set *)&readbit,(struct fd_set *)&writebit,(struct fd_set *)&excludebit,&t)!=1) return NULL;
		c = SOCKgetc(sin);
		if (c==EOF) return NULL;
		if (c=='\n')
			{
			*b = '\0';
			break;
			}
		*b++ = c;
		}

	return buf;
	}

void ProcessCmd(int cmd,char *parm,FILE *sin, FILE *sout)
	{
	char *s,*p;
	char fname[15]="inmail.xxx";
	int reply=250;
	char linebuf[1024];
	int n;
	FILE *fp;

	switch(cmd)
		{
		case MAIL_HELO:
			strcpy(remotehost,parm);
			SOCKfprintf(sout,"250 %s\n",hostname);
			SOCKfflush(sout);
			reply = 0;
			break;

		case MAIL_MAIL:
			if (strnicmp(parm,"TO:",3)==0)
				{
				if (s = GetString(parm))
					{
					AddRecipient(s);
					}
				else reply = 550;
				}
			if (strnicmp(parm,"FROM:",5)==0)
				{
				if (s = GetString(parm))
					{
					strcpy(mailfrom,s);
					mclose(&mail);
					mail = mopen();
					ResetRecipients();
					}
				else reply=553;
				}
			break;

		case MAIL_RCPT:
			if (strnicmp(parm,"TO:",3)==0)
				{
				if (s = GetString(parm))
					{
					AddRecipient(s);
					}
				else reply = 550;
				}
			break;

		case MAIL_DATA:
			printf("Receiving Data\n");
			replycode(sout,354,NULL);
			do {
				if (!SOCKfgetline(linebuf,sizeof(linebuf)-1,sin,sout))
					{
					mclose(&mail);
					ResetRecipients();
					exitplease = 1;
					return;
					}

				for (n=strlen(linebuf)-1;n>=0;n--)
					if (linebuf[n]=='\r' || linebuf[n] == '\n') linebuf[n] = '\0';
					else break;

				mprintf(&mail,"%s\n",linebuf);
				} while (strcmp(linebuf,"."));

/* Now write the data out */

			for (n=0;n<numrecpts;n++)
				{
				if (fp = fopenuniquename(fname))
					{
					fprintf(fp,"To: %s\n",recipients[n]);
					fprintf(fp,"From: %s\n",mailfrom);
					fprintf(fp,"Data:\n");
					fwrite(mail,strlen(mail),1,fp);
					fclose(fp);
					}
				else reply=421;
				}

			mclose(&mail);
			break;

		case MAIL_RSET:
			mclose(&mail);
			mail = mopen();
			ResetRecipients();

			break;

		case MAIL_NOOP:
			break;

		case MAIL_QUIT:
			reply=221;
			exitplease = 1;
			break;

#if 0
		case MAIL_SEND:
			break;
		case MAIL_SOML:
			break;
		case MAIL_SAML:
			break;
		case MAIL_VRFY:
			break;
		case MAIL_EXPN:
			break;
		case MAIL_HELP:
			break;
		case MAIL_TURN:
			break;

#endif
		default:
			reply = 500;
			exitplease = 1;
			break;
		}
	if (reply) replycode(sout,reply,NULL);
	}

void ProcessMail(FILE *sin, FILE *sout)
	{
	char buf[512];
	FILE *fp;
	int buflen = sizeof(buf)-1;
	int n;

	mail = NULL;
	exitplease=0;

	gethostname(hostname,sizeof(hostname)-1);

	replycode(sout,220,NULL);

	while(!exitplease)
		{
		if (!SOCKfgetline(buf,buflen,sin,sout)) break;

		for (n=0; mailcmds[n]; n++)
			{
			if (strnicmp(buf,mailcmds[n],strlen(mailcmds[n])) == 0)
				{
				ProcessCmd(n+1,buf+strlen(mailcmds[n])+1,sin,sout);
				break;
				}
			}
		if (!mailcmds[n])
			{
			replycode(sout,550,NULL);
			exitplease = 1;
			}
		}

	}



main(int argc, char *argv[])
	{
	int s,bytes;
	FILE *fp;
	FILE *sin,*sout;
	int bit;
	int iobit,writebit,readbit,exceptbit=0;
	struct timeval t;
	long		nonBlocking = 1l;
	struct sockaddr_in s_incoming;
	int len;
	struct passwd *pw;

	so_init();
	if (!(pw = getpwuid(getuid()))) printf("Error!\n");
	pw = getpwnam("Dylan");

	printf("%d %d\n",getuid(),pw->pw_uid);

	exit(1);

/* Start the Daemon looping */

	while (1)
		{

/* Check for pending messages */

		if ((s = so_daemon(argv,1))>=0)
			{

			sethostent();

/* get the remote host's name and number */

#if 0
			len = sizeof(s_incoming);
			getpeername(s,(struct sockaddr *)(&s_incoming),&len);

/* Set the port for writing */
			iobit = writebit = bit = 1L<<s;
			readbit = 0;
			t.tv_sec = 50;
			t.tv_usec = 0;
			iobit = select(s+1,(struct fd_set *)&readbit,(struct fd_set *)&writebit,(struct fd_set *)&exceptbit,&t);	/* Get read/write status */

#endif

			sin = SOCKfdopen(s,"r");
			sout = SOCKfdopen(s,"w");

			ProcessMail(sin,sout);

			SOCKfclose(sin);
			SOCKfclose(sout);

			endhostent();
			so_close(s);
			}
		else break;
		}


	}
