#include <stdio.h>
#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include "date.h"
#include "multinet_root:[multinet.include]netdb.h"
#include "multinet_root:[multinet.include.sys]ioctl.h"
#include <ssdef.h>
#include <iodef.h>
#include <descrip.h>
#include <ctype.h>
#define DEL 127
#define POP3_PORT 110
#define boolean int
#include <varargs.h>

#define MAX_DATA 5120
#define ERR 	 (-3)
#define OK  	 0
#define syswork(x) ((x)&1)
#define sysfail(x) (!((x)&1))

extern	char	POP3_User_Name[32+1];
extern	int	POP3_No_Delete;
extern	int	POP3_Debug;

char	*get_password();
char	*enddat = ".\n";
char	*getenv();
int	net_chan;
int	debug=0;
int	rptr, buflen;

static int inetgt();
static int e_qio();
static int e_receive();
static int e_error();

/*
 *	Connect to remote host and allocate a buffer big enough for
 *	all the new mail we'll receive.
 */

char	*POP3_Connect_To_Server (msg_cnt, host)
int	*msg_cnt;
char	*host;
{
	static	char	*pass=NULL, *pop_user=NULL;
	static	char	  rbuf[512], buf[512];
		char	*cp, *mm_calloc();
		struct hostent	*hostent, *gethostbyname();
		struct sockaddr_in	*addrp;
		boolean connected = FALSE;
		boolean logged_in = FALSE;
		short	htons();
		int	tot_size = -1;

	/*
	 *	Error return unless everything is happy.
	 */
	*msg_cnt = -1;

	if ( strlen(POP3_User_Name) )
		pop_user = POP3_User_Name;

	if ( !pop_user ) {
		if ( !( cp = pop_user = getenv("USER")) ) {
			printf("?Couldn't get my user name");
			return NULL;
		}
		/* Convert Uppercase VMS to lowercase for Unix */
		while ( *cp ) {
			*cp  = _tolower(*cp);
			cp++;
		}
	}

	if ( !pass  &&	!(pass = get_password()) ) {
		printf("?Couldn't read password from terminal");
		return NULL;
	}

	if ( (net_chan = socket(AF_INET, SOCK_STREAM, 0)) == ERR) {
		printf( "?socket() failed - %s\n", vms_errno_string());
		return NULL;
	}
	if ( !(hostent = gethostbyname(host)) ) {
		printf( "?gethostbyname() failed - %s\n", vms_errno_string());
		return NULL;
	}
	addrp = (struct sockaddr_in *)hostent->h_addresses;
	while (addrp  &&  *(int *)addrp) {
		if ( addrp->sin_family != AF_INET)
			continue;
		addrp->sin_port = htons(POP3_PORT);
		if (POP3_Debug) printf("Attempting connection to %s\n",(cp = (char *)inet_ntoa(addrp->sin_addr)) ? cp : "\n**inet_ntoa() failed**");
		if ( connect(net_chan, addrp, sizeof(*addrp)) ) {
			printf("?connect() failed - %s\n\t", vms_errno_string());
			addrp++;
		} else {
			connected = TRUE;
			if (POP3_Debug) printf("Connection established\n");
			break;
		}
	}
	if ( !connected )
		return NULL;
	if ( !recv_reply(rbuf) ) {
		WARN("?Server greeting scared me off - '%s'\n", rbuf);
		*msg_cnt = 0;
		return NULL;
	}

	/*
	 *	Try to login as many times as the user wants
	 */
	do {
		if (POP3_Debug) printf("Attempting login validation for user '%s'", pop_user);
		send_command_w("USER %s", pop_user);
		send_command("PASS %s", pass);
		if ( !recv_reply(rbuf)) {
			printf("\n?Remote login failed - %s\n", rbuf);
			printf("\n Would you like to re-enter password and try again? ");
			gets(rbuf);
			if ( rbuf[0] != 'y' &&	rbuf[0] != 'Y') {
				*msg_cnt = 0;
				return NULL;
			}
			pass = get_password();
		} else
			logged_in = TRUE;
	} while ( !logged_in );

	/*
	 *	By here we are connected and logged in.  Let's find out how
	 *	many messages there are and allocate a big buffer to hold them.
	 */

	send_command("STAT");
	if ( !recv_reply(rbuf) ) {
		WARN("?Bad return from STAT command - '%s'\n", rbuf);
		*msg_cnt = 0;
		return NULL;
	}
	sscanf(rbuf, "%s %d %d", &buf, msg_cnt, &tot_size);
	if ( *msg_cnt == 0 ) {
		printf("\nNo new mail for user (%s) on %s\n", pop_user, host);
		return NULL;
	}
	else {
		if ( *msg_cnt < 0 ) {
			WARN("?Invalid number of messages specified by POP3 Server's STAT reply");
			*msg_cnt = 0;
			return NULL;
		}
		if ( tot_size < 1 ) {
			WARN("?Invalid mailbox size specified by POP3 Server's STAT reply");
			*msg_cnt = 0;
			return NULL;
		}
	}
	printf("\nUser %s validated at %s. %d new mail message%s\n", pop_user, host,
	       *msg_cnt, (*msg_cnt == 1) ? "." : "s.");
	return	mm_calloc(tot_size+512, 1);
}


POP3_Retrieve_Msgs (msg_cnt, big_buf)
int	msg_cnt;
char	*big_buf;
{
	int	time[2], i, status, len, temp, msg_size, save_size;
	char	*cp, tmp_buf[1024], *msg_buf, *big_buf_start=big_buf;
	char	rbuf[512], buffer[512], *msg_buf_start;
	char	date_buf[132] , *mm_calloc(), *strchr();
	char	Mail_Time[128];
	boolean done, date_found;
	int	tops_size, j;


	rptr = buflen = 0;
	bzero(date_buf,sizeof(date_buf));

	for ( i = 1; i <= msg_cnt; i++) {
		send_command("LIST %d", i);
		if ( !recv_reply(rbuf) ) {
			printf("?POP3 server reports error- '%s'\n", rbuf);
			exit();
		}
		sscanf(rbuf, "%s %d %d", buffer, &temp, &msg_size);
		if ( msg_size < 1 ) {
			DIE("?POP Server reports invalid message size %d for message # %d\n",i, msg_size);
		}
		if ( !(msg_buf_start = msg_buf = mm_calloc(msg_size+512, 1)) ) {
			DIE("?malloc(%d,1) - failed\n", msg_size+5);
		}
		send_command_w("RETR %d", i);
		if (POP3_Debug) printf("[Retrieving message %d]\n", i);
		save_size = msg_size;
		done = FALSE;
		date_found = FALSE;
		while (!done  &&  inetgt(buffer) != EOF)  {  /* fetch input lines until /n./n */
			if (strcmp(buffer, enddat) == 0)  {
				done = TRUE;	 /*  found the end */
				continue;
			} else	if (buffer[0] == '.')
				(void) strcpy(buffer, &buffer[1]);
			(void) strcpy(msg_buf, buffer);
			msg_buf += strlen(buffer);
			if ( !date_found
			&& _toupper(buffer[0]) == 'D'
			&& _toupper(buffer[1]) == 'A'
			&& _toupper(buffer[2]) == 'T'
			&& _toupper(buffer[3]) == 'E' ) {
				/* Move past 'Date:[whitspace]'  and copy date */
				j = 5;
				while ( isspace(buffer[j]) ) j++;
				strcpy(date_buf, &buffer[j]);
				date_buf[strlen(date_buf)-1] = '\0';
				date_found = TRUE;
			}

		}

		len = j = msg_buf - msg_buf_start;
		msg_buf = msg_buf_start;

		d_log('s', msg_buf_start);
		idtim_jsys(date_buf,0, time);
		odtim_jsys(time,
			   ODT_DAY_OF_WEEK |
			   ODT_DATE_WITH_SPACES |
			   ODT_DO_TIMEZONE |
			   ODT_NO_COLUMNATION,
			   date_buf);
		odtim_jsys(time,ODT_DO_TIMEZONE,Mail_Time);
		/*
		 *	Calculate the TOPS-20 size of the message text
		 */
		tops_size = 0;
		while(--j >= 0) {
			if (*msg_buf++ == '\n') tops_size++; /* Counts as 2 */
			tops_size++;
		}
		big_buf += sprintf(big_buf, "%s,%d;000000000000\n",Mail_Time, tops_size);
		memcpy(big_buf, msg_buf_start, len);
		big_buf += len;
		mm_free(msg_buf_start);
	}
	return ((int) big_buf - (int)big_buf_start);
}

send_command(va_alist)
va_dcl
{
	int i, numargs;
	register va_list ap;
	char *format;
#ifdef __ALPHA
	__int64 p[6];
#else
	int p[6];
#endif
	char	buf[512], *term = "\r\n";

	va_count(numargs);
	va_start(ap);
	format = va_arg(ap, char *);

	for (i=0; i<numargs-1 && i<6; i++) {
#ifdef __ALPHA
	    p[i] = va_arg(ap, __int64);
#else
	    p[i] = va_arg(ap, int);
#endif
	}
	va_end(ap);

	sprintf(buf, format, p[0], p[1], p[2], p[3], p[4], p[5]);
	strcat(buf, term);
	d_log('c', buf);
	if ( send(net_chan, buf, strlen(buf), 0)  !=  strlen(buf) ) {
		printf( "send() failed - %s\n", vms_errno_string());
		exit();
	}
}

send_command_w(va_alist)
va_dcl
{
	int i, numargs;
	register va_list ap;
	char *format;
#ifdef __ALPHA
	__int64 p[6];
#else
	int p[6];
#endif
	char	buf[512], *term = "\r\n";

	va_count(numargs);
	va_start(ap);
	format = va_arg(ap, char *);

	for (i=0; i<numargs-1 && i<6; i++) {
#ifdef __ALPHA
	    p[i] = va_arg(ap, __int64);
#else
	    p[i] = va_arg(ap, int);
#endif
	}
	va_end(ap);

	sprintf(buf, format, p[0], p[1], p[2], p[3], p[4], p[5]);
	strcat(buf, term);
	d_log('c', buf);
	if ( send(net_chan, buf, strlen(buf), 0)  !=  strlen(buf) ) {
		printf( "send() failed - %s\n", vms_errno_string());
		exit();
	}
	if ( !recv_reply(buf) ) {
		printf("Error reported from remote server - '%s'\n", buf);
		exit();
	}
}


WARN (va_alist)
va_dcl
{
	int i, numargs;
	register va_list ap;
	char *format;
#ifdef __ALPHA
	__int64 p[6];
#else
	int p[6];
#endif
	va_count(numargs);
	va_start(ap);
	format = va_arg(ap, char *);

	for (i=0; i<numargs-1 && i<6; i++) {
#ifdef __ALPHA
	    p[i] = va_arg(ap, __int64);
#else
	    p[i] = va_arg(ap, int);
#endif
	}
	va_end(ap);

	printf(format, p[0], p[1], p[2], p[3], p[4], p[5]);
}

DIE (va_alist)
va_dcl
{
	int i, numargs;
	register va_list ap;
	char *format;
#ifdef __ALPHA
	__int64 p[6];
#else
	int p[6];
#endif
	va_count(numargs);
	va_start(ap);
	format = va_arg(ap, char *);

	for (i=0; i<numargs-1 && i<6; i++) {
#ifdef __ALPHA
	    p[i] = va_arg(ap, __int64);
#else
	    p[i] = va_arg(ap, int);
#endif
	}
	va_end(ap);

	printf(format, p[0], p[1], p[2], p[3], p[4], p[5]);
	exit(0x10000000);
}


recv_reply (buf)
char	*buf;
{
	int	len;

	inetgt(buf);
	d_log('s', buf);
	if ( buf[0] ==	'+')
		return 1;
	if (buf[0] ==	'-')
		return NULL;
	return NULL;
}


d_log ( dir, str)
char	dir;	/* Direction 'S'erver or 'C'lient */
char	* str;
{
	register char	* cp;
	static	int	inited=0;


	if ( POP3_Debug ) {
		printf( "%c: ", dir);
		cp = str;
		while ( *cp ) {
			if (*cp >= ' '	&&   *cp <= '~')
				printf( "%c",  *cp);
			else if ( *cp == DEL )
				printf( "<DEL>");
			else if ( *cp == '\r')
				printf( "<CR>");
			else if ( *cp == '\n') {
				printf( "<LF>\n");
				if ( *(cp+1) )
					printf("%c: ", dir);
			} else if ( *cp >= 0  &&  *cp <=  31)
				printf( "^%c", *cp+'@');
			else
				printf( "(cant decode 0x%x char)", *cp);
			cp++;
		}
	}
}



static	char errbuf[256];
#define IN_SIZE 1000
#define TIME_LIMIT 30



static int inetgt(ubuf)
char *ubuf;
{
	char c='\0', lastc;
	int status=OK, i=0;
	static char inbuf[IN_SIZE];

	do {
	   if (rptr >= buflen )  {		  /*  read from network */
	      status = EOF;			   /*  assume error  */
	      buflen = e_receive(net_chan, inbuf, IN_SIZE, TIME_LIMIT, errbuf);
	      if (buflen <= 0) {
		 break;
	      }
	      status = OK;			   /*  reset status */
	      rptr = 0;
	   }
	   lastc = c;
	   c = inbuf[rptr++];
	   if (lastc == '\r' && c != '\n') ubuf[i++] = '\r';
	   if (c != '\r') ubuf[i++] = c;
	} while ((c != '\n') && (i <= MAX_DATA));
	ubuf[i] = '\0';
	return(status);
}



static unsigned short iosb[4];

static struct Qio  {
   char *qa;
   int qb, qc;
   char *qd;
   int qe, qf;
} qio={0,0,0,0,0,0};


static int e_receive(chan, buffer, size, sec, errbuf)
int  chan, size, sec;
char *buffer, *errbuf;
{
	qio.qa = buffer;
	qio.qb = size;
	if (e_qio(chan, IO$_READVBLK, &qio, sec, errbuf) != OK)
	   return (ERR);
	else
	   return (iosb[1]);
}


static int e_qio(chan, func, p, sec, errbuf)
struct Qio *p;
short chan;
int func, sec;
char *errbuf;
{
	int quad[2], retval, stat;
	long s;
	static int ioef = 0, mtef = 0, mask = 0;

	if (mask == 0)	{
	    (void) LIB$GET_EF(&ioef);	 /*  fetch an event flag for io */
	    (void) LIB$GET_EF(&mtef);	 /*  fetch an event flag for mark time */
	    mask = (1<< (ioef%32)) | (1<<(mtef%32));
	}

	if (sec > 0)  {			/*  convert seconds to system time */
	    int multip = -10000000;
	    int addend = 0;
	    stat = LIB$EMUL(&sec, &multip, &addend, quad);
	    stat = SYS$SETIMR(mtef, quad, 0, mtef);
	} else
	    stat = SYS$CLREF(mtef);

	s = SYS$QIO(ioef,chan,func,iosb,0,0,
	     p->qa, p->qb, p->qc, p->qd, p->qe, p->qf);

	retval = OK;
	if (syswork(s)) {	     /*  successfully queued */
	   stat = SYS$WFLOR(ioef, mask);
	   if (sec > 0)  {	 /* check for timeout or completion */
	      if (iosb[0] != 0)       /* cancel timer */
		 stat = SYS$CANTIM(mtef,0);
	      else  {
		 stat = SYS$CANCEL(chan);
		 iosb[0] = SS$_TIMEOUT;
		 retval = ERR;
	      }
	   }
	} else if (sec > 0)
	   stat = SYS$CANTIM(mtef,0);
	stat = e_error(s, (unsigned long) iosb[0], errbuf);
	if (retval != ERR)	    /* No timeout */
	   retval = stat;
	return (retval);
}

static int e_error(first, second, errbuf)
long first, second;
char *errbuf;
{
	long error;
	short errlen;
	struct dsc$descriptor  dsc = {256, 0, 0, errbuf};

	*errbuf = '\0';
	if (syswork(first) && syswork(second))
	   return (OK);
	if (sysfail(first))
	   error = first;
	else
	   error = second;

	(void) SYS$GETMSG(error,&errlen, &dsc, 15, 0 );
	errbuf[errlen] = '\0';
	return(ERR);
}


