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

/*
 *	Portable mini pop client.
 *
 *	Set up and provide the following for your machine
 */

extern void net_init(void);			/* Init comms */
extern int net_connect(const char *host);	/* Make a connection -1 = error, net has reported why */
extern int net_getline(char *buf,int len,long time);
				/* Get a line of text ending \r\n - return -1 for error, -2 timeout , -3 overlong input */
				/* result buffer has the \r\n stripped */
extern void net_sendline(const char *buf);	/* send a null terminated line of data */

#define IO_TIMEOUT		120L		/* 2 minutes - less for non amateur radio! */





static char linebuffer[515];		/* Standard isn't clear if its 512+\r\n or 512 */
static FILE *output;			/* Output mailbox */


static void whoops(const int error,const char *whatweupto)
{
	if(error==-1)
	{
		fprintf(stderr,"popit failed while %s: Network error.\n",whatweupto);
		exit(1);
	}
	if(error==-2)
	{
		fprintf(stderr,"popit failed while %s: No response after %ld seconds.\n",whatweupto,IO_TIMEOUT);
		exit(1);
	}
	if(error==-3)
	{
		fprintf(stderr,"popit was given a stupidly long line while %s by the remote POP3 server.\n",whatweupto);
		exit(1);
	}
}

static void check_all_is_hoopy(const char *whatweupto)
{
	int error=net_getline(linebuffer,515,IO_TIMEOUT);
	if(error<0)
		whoops(error,whatweupto);
	if(*linebuffer=='-')
	{
		const char *lp=linebuffer;
		while(*lp&&!isspace(*lp))
			lp++;
		fprintf(stderr,"popit was told '%s' while %s and gave up.\n",lp+1,whatweupto);
		exit(1);
	}
}

static void login_the_user(void)
{
	char buf[64];
	const char *name=getlogin();
	const char *pass;
	if(name==NULL)
	{
		fprintf(stderr,"It's kind of hard to ask for your mail if I don't know who you are!\n");
		exit(1);
	}
	sprintf(buf,"USER %s\r\n",name);
	net_sendline(buf);
	check_all_is_hoopy("sending the username");
	pass=getpass("POP3 Password: ");
	sprintf(buf,"PASS %-50s\r\n",pass);
	net_sendline(buf);
	check_all_is_hoopy("sending the password");
}


typedef struct _msgent Message;
struct _msgent
{
	Message *Next;
	char *Identity;
};

static Message *Messages=NULL;

static void get_msg_list(void)
{
	int error;
	char *lp,*tmp;
	Message *m,*last=NULL;
	net_sendline("LIST\r\n");
	check_all_is_hoopy("getting a message list");
	while(1)
	{
		error=net_getline(linebuffer,515,IO_TIMEOUT);
		if(error<0)
			whoops(error,"reading the list of messages");
		lp=linebuffer;
		if(*lp=='.')
		{
			if(lp[1]==0)
				break;
			lp++;
		}
		m=(Message *)malloc(sizeof(Message));
		if(m==NULL)
		{
			fprintf(stderr,"popit ran out of memory.\n");
			exit(1);
		}
		m->Next=NULL;
		if(last!=NULL)
			last->Next=m;
		else
			Messages=m;
		last=m;
		tmp=lp;
		while(*lp&&!isspace(*lp))
			lp++;
		*lp=0;
		m->Identity=strdup(tmp);
		if(m->Identity==NULL)
		{
			fprintf(stderr,"popit ran out of memory.\n");
			exit(1);
		}
	}
}


static void fetch_message(Message *m)
{
	char buf[128];
	int  error;
	char *lp;
	int  past_header=0;

/* Amateur radio wants to send a single packet of the request for those poor 1200 baud people ! */
	if(strlen(m->Identity)>100)
	{
		net_sendline("RETR ");
		net_sendline(m->Identity);
		net_sendline("\r\n");
	}
	else
	{
		sprintf(buf,"RETR %s\r\n",m->Identity);
		net_sendline(buf);
	}
	check_all_is_hoopy("fetching a message");
	
	while(1)
	{
		error=net_getline(linebuffer,515,IO_TIMEOUT);
		if(error<0)
			whoops(error,"fetching a message");
		lp=linebuffer;
		if(*lp=='.')
		{
			if(lp[1]==0)
				break;
			lp++;
		}
		if(past_header && strncmp(lp,"From",4)==0)
			fputc('>',output);	/* Quote from lines */
		if(*lp==0)
			past_header=1;
		fputs(lp,output);
		fputc('\n',output);
	}
	if(strlen(m->Identity)>100)
	{
		net_sendline("DELE ");
		net_sendline(m->Identity);
		net_sendline("\r\n");
	}
	else
	{
		sprintf(buf,"DELE %s\r\n",m->Identity);
		net_sendline(buf);
	}
	check_all_is_hoopy("deleting a message");
}


void main(int argc,const char *argv[])
{
	Message *m;
	if(argc!=3)
	{
		fprintf(stderr,"%s: Expected arguments <HostName> <OutputFile>\n",argv[0]);
		exit(1);
	}
	net_init();
	if(net_connect(argv[1])==-1)
	{
		fprintf(stderr,"%s: Unable to connect.\n");
		exit(1);
	}
	output=fopen(argv[2],"a");
	if(output==NULL)
	{
		perror(argv[2]);
		exit(1);
	}

	check_all_is_hoopy("waiting for the banner to fall");
	login_the_user();
	get_msg_list();

	m=Messages;

	while(m!=NULL)
	{
		fetch_message(m);
		m=m->Next;
	}

	fclose(output);

	net_sendline("QUIT\r\n");
	check_all_is_hoopy("logging out");
}
