#include "pop.h"

/* Stuff for "From " Header */
/* Courtesy Michael B. Smith */


struct mytm	{
				int year;
				int month;
				int day;
				int dow;
				int hour;
				int min;
				int sec;
			};

/* DaysInMonth */
static const int	dim [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static const char	*months [] = {	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		       						"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static const char	*days [] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

static void DateStampToMyTm (struct DateStamp *date, struct mytm *da)
{
	int days, years, leap  = 0, month = 0;

	days = date->ds_Days + 731;		/*  1976	*/
	years = days / (365*3+366);		/*  #quad yrs	*/
	days -= years * (365*3+366);
	years = 1976 + 4 * years;

	if (days <= 365)
	{
		leap = 1;
	}
	else
	{
		days -= 366;
		++years;
		years += days / 365;
		days %= 365;
	}

	month = 0;
	while (1)
	{
		if (month == 1)
		{
			if (days < (28 + leap)) break;
			days -= 28 + leap;
		}
		else
		{
			if (days < dim [month]) break;
			days -= dim [month];
		}
		++month;
	}

	da->hour   = date->ds_Minute / 60;
	da->min    = date->ds_Minute % 60;
	da->sec    = date->ds_Tick / 50;

	da->day    = days + 1;
	da->month  = month + 1;
	da->dow    = date->ds_Days % 7;    /*  0 = sunday   */
	da->year   = years;

	return;
}

char * datestr (char *buf)
{
	/*
	**  datestr
	**
	**	Build ARPA date-time as specified in RFC-822:
	**
	**	    Wed, 13 Oct 93 18:52:04 EST
	**	    123456789012345678901234567890
	**
	**	buf must be 24 characters, plus space for TimeZone and NUL.
	**
	**	As TimeZone may be "(+23:30)" or similar, 33 chars should
	**	do it.
	*/
	struct mytm da;
	struct DateStamp ds;

	DateStampToMyTm (DateStamp (&ds), &da);

	DoFmt (buf, "%s, %ld %s %ld %02ld:%02ld:%02ld",
				days [da.dow], da.day,
				months [da.month - 1], da.year % 100,
				da.hour, da.min, da.sec);

	return buf;
}

char * time_stamp (char *buf)
{
	/*
	**  time_stamp
	**
	**	Put a time/date stamp into buf (for logging)
	**
	**	13 Nov 93 13:14:45
	*/
	struct DateStamp ds;
	struct mytm da;

	DateStampToMyTm (DateStamp (&ds), &da);

	DoFmt (buf, "%ld %s %ld %02ld:%02ld:%02ld",
				da.day,
				months [da.month - 1], da.year % 100,
				da.hour, da.min, da.sec);

	return buf;
}

/*  End stuff for "From " header */


#define ASYNCBUFSIZE 8192

BOOL needfrom ( char * , struct AsyncFile *);

/* Variables global to this file */

struct Library *SockBase ;
int havemail;

/* Functions */

int dopop(void)
{
	int s;
	int count=1;
	int okay=1;
	struct hostent *hp;
	struct sockaddr_in sa;

	DoFmt(title,"Connecting to %s",pophost);
	settitle(title);

	if((SockBase = OpenLibrary( SOCKLIBNAME, SOCKLIBVERSION )) == NULL)
	{
		doreq("Error opening "SOCKLIBNAME"\n",bum);
		return(1);
	}
#ifdef AMITCP
	SetErrnoPtr(&errno, sizeof errno);
#else
	setup_sockets( MAXSOCKS, &errno );
#endif

	if((hp=gethostbyname(pophost))==NULL)
	{
#ifndef AMITCP
		cleanup_sockets();
#endif
		CloseLibrary( SockBase ) ;
		doreq("Connection Refused.",bum);
		return(1);
	}

	bzero(&sa, sizeof(sa));
	bcopy(hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
	sa.sin_family = hp->h_addrtype;
	sa.sin_port = htons((u_short)port);
	if ((s=socket(hp->h_addrtype,SOCK_STREAM,0)) < 0)
	{
#ifndef AMITCP
		cleanup_sockets();
#endif
		CloseLibrary( SockBase ) ;
		doreq("Something bad\nhas happened.",bum);
		return(1);
	}

	if (connect(s,(struct sockaddr *) &sa,sizeof(sa))< 0)
	{
		doreq("No POP3 daemon\nrunning on this\nmachine or port.",bum);
		s_close(s);
#ifndef AMITCP
		cleanup_sockets();
#endif
		CloseLibrary( SockBase ) ;
		return(1);
	}

/* Put actual code here */

	recv(s,buf,BUFSIZE-1,0);
	settitle("Got Connection");

	if (senduser(s))
	{
#ifndef AMITCP
		cleanup_sockets();
#endif
		CloseLibrary( SockBase ) ;
		return(1);
	}

	havemail=sendstat(s);

	while ((okay) && ((havemail > count) || (havemail == count)))
	{
		DoFmt(title,"Retrieving %lu",count);
		settitle(title);

		okay=retrieve(s,count);

		if (delmail && okay)
		{
			DoFmt(title,"Deleting %lu",count);
			settitle(title);

			okay=delmessage(s,count);
		}
		++count;
	}

	if(sendquit(s))
	{
#ifndef AMITCP
		cleanup_sockets();
#endif
		CloseLibrary( SockBase ) ;
		return(1);
	}

	if (Project0Wnd)
	{
		SetWindowTitles(Project0Wnd,Project0Wdt, (UBYTE *) ~0);
	}

	if (havemail && notify) /* removed sendstat() after Nofify */
	{
		doreq("You have new mail.","Cool");
	}

/* End actual code */

    s_close(s);
#ifndef AMITCP
	cleanup_sockets();
#endif
	CloseLibrary( SockBase ) ;
	return(0);
}

int senduser(int s)
{
	int t;

	settitle("Sending Username");

	DoFmt(buf,"USER %s\r\n",username);

	if ( !trans(s,buf) ) return(1);

	t=sscanf(buf,"%s",temp);
	if ( valcheck(t,temp) ) return(1);

/* Password */

	if (password[1])
	{
		settitle("Sending Password");

		DoFmt(buf,"PASS %s\r\n",password);

		if ( !trans(s,buf) ) return(1);

		t=sscanf(buf,"%s",temp);

		if ( valcheck(t,temp) ) return(1);
	}

	return(0);
}

int sendquit(int s)
{
	int t;

	settitle("Sending QUIT");

	DoFmt(buf,"QUIT\r\n");

	if ( !trans(s,buf) ) return(1);

	t=sscanf(buf,"%s",temp);

	if ( valcheck(t,temp) ) return(1);

	settitle("Quit Acknowledged");

	return(0);
}

int sendstat( int s )
{
	int t;
	int count=0;

	DoFmt(buf,"STAT\r\n");

	if ( !trans(s,buf) ) return(0);

	t=sscanf(buf,"%s %lu",temp,&count);

	if ( valcheck(t,temp) ) return(0);

	return(count);
}

/* function retrieve does the work of retrieving a message, removing
   the byte stuffing and writing it to the mailfile.
   Rewritten by David Duggan, Sandia National Labs */

int retrieve(int s, int count)
{
    int foo;			/* temp integer for numb bytes read */
    int state;			/* flag saying what state we are in */
    int x1;			/* index for tmpbuf */

    char *tmpbuf;		/* ptr to place to put unstuffed msg */
    char *buf3;			/* ptr to front of mail message */
    char *buf4;			/* ptr to char being examined in mail message */

    struct AsyncFile *ofp;

	if ( ! (tmpbuf = AllocVec(BUFSIZE,MEMF_CLEAR)))
	  {
	    doreq("Not enough memory for buffer.\n",bum);
	    return(0);
	  }

	if (( count == 1 ) && ( !appfile ))
	  ofp = OpenAsync(maildir, MODE_WRITE,ASYNCBUFSIZE);
	else
	  ofp = OpenAsync(maildir, MODE_APPEND,ASYNCBUFSIZE);

	if ( ofp == NULL )
	  { doreq("Unable to lock mailbox.\n",bum); FreeVec(tmpbuf); return(0); }

	DoFmt(buf,"RETR %lu\r\n",count);

	if ( !trans(s,buf) )	/* send data, get answer */
	  { CloseAsync(ofp); FreeVec(tmpbuf); return(0); }

	if ( valcheck(1,buf) )	/* check for good response */
	  { CloseAsync(ofp); FreeVec(tmpbuf); return(0); }

	/* Here we have a good first line for this multi-line response.
	   Now we should process the rest of the buffer */

	buf3 = strstr(buf,"\r\n");
	if (buf3 != NULL) buf3 += 2;/* move ptr past first line */
	needfrom(buf3,ofp);	/* add "from" if needed */
	buf4 = buf3 = strip(buf3); /* remove CRs */

	state = 0;		/* start clean every time */
	while ( state < 3 )
	  {

	    x1 = 0;			/* reset index */
	    while ( *buf4 )		/* until we hit the end of buffer */
	      {
		switch (state)
		  {
		    case 0:
		      if ( *buf4 == '\n' )
		        state++;	/* go to next state */
		      tmpbuf[x1++] = *buf4;/* move character */
		      break;

		    case 1:
		      if ( *buf4 == '.' )
		        state++;	/* next state, deleting period */
		      else
			{	/* reset state if not "\n"  */
		          if (*buf4 != '\n') state = 0;
			  tmpbuf[x1++] = *buf4;/* move character */
			}
		      break;

		    case 2:
		      if ( *buf4 == '\n' )
		        state++;	/* go to final state */
		      else
			{
		          state = 0;	/* reset the state */
			  tmpbuf[x1++] = *buf4;
			}
		      break;
		  }
		buf4++;		/* move to next byte */
	      }

	    tmpbuf[x1] = '\0';		/* terminate it */

	    if ( state < 3 )	/* we need more in the buffer */
	      {
		if ( WriteAsync(ofp,tmpbuf,strlen(tmpbuf) ) == -1)
		  {
		    doreq("Error writing to mailbox.\n",bum);
		    CloseAsync(ofp);
		    FreeVec(tmpbuf);
		    return(0);	/* return error */
		  }

		/* get next buffer full */
		foo = recv(s,buf,(BUFSIZE - 1),0);

		if ( foo > 0 )
		  {
		    buf[foo] = '\0'; /* terminate it for grins */
		    buf4 = buf3 = strip(buf);/* remove CRs */
		  }
		else
		  { /* should never happen! */
		    doreq("recv ewouldblock!\n",bum);
		    state = 4;/* should never happen */
		  }
	      }	/* if (state < 3) */
	  }	/* while (state < 3) */

	/* write out last buffer, leaving off last ".\n" */
	if ( WriteAsync(ofp,tmpbuf,strlen(tmpbuf)) == -1 )
	  {
	    doreq("Error writing to mailbox.\n",bum);
	    CloseAsync(ofp);
	    FreeVec(tmpbuf);
	    return(0);	/* return error */
	  }

	CloseAsync(ofp);
	FreeVec(tmpbuf);
	return(1);

} /* end of retrieve */

int delmessage( int s, int count )
{
	DoFmt(buf,"DELE %lu\r\n",count);
	trans(s,buf);

	if ( valcheck(1,buf) ) return(0);

	return(1);
}

int valcheck(int t, char *localtemp)
{
	if (t == 0)
	{
		doreq("Got a bogus return string.\n",bum);
		return(1);
	}

	if ( !strstr(localtemp,"+OK") )
	{
		doreq("Didn't get +OK",bum);

		if (localtemp[0] != '\0')
		{
			doreq(localtemp,bum);
		}
		return(1);
	}
	return(0);
}

int trans( int s, char *mybuf )
{
	int foo;

	send(s,mybuf,strlen(mybuf),0);

	bzero(buf,BUFSIZE);
	foo=recv(s,buf,BUFSIZE-1,0);
	if ( foo < 1 )
	{
		return(0);
	}

	buf[foo]='\0';

	return (1);
}

void settitle( char *newtitle )
{
	if (winop)
	{
		SetWindowTitles(Project0Wnd,newtitle, (UBYTE *) ~0);
	}
}

/* Scott, this is a slight modification of your strip.  It has the exact same
   functionality as yours, except it uses dynamic memory instead of the stack
   for the temporary buffer.  If you want to leave your routine here,
   everything should work just the same.  David */

/* function strip will strip out all the carraige returns
   from the buffer and return the original pointer */

char * strip( char *mybuf )
  {
    char tmpbuf[BUFSIZE];
    ULONG x1, x2, len;

    len = strlen(mybuf);

    /* strip out the CRs */
    for (x1 = x2 = 0 ; x1 < len; x1++)
      {
	tmpbuf[x2] = mybuf[x1];
	if ( tmpbuf[x2] != '\r' ) x2++;
      }

    tmpbuf[x2] = '\0';		/* terminate it */
    strcpy(mybuf,tmpbuf);	/* copy back the string */

    return(mybuf);		/* return their buf ptr */

  } /* end of strip */

int lastblock (char *segment)
{
	char *found;
	int len = strlen(segment);

	if ( (found=strstr(segment,"\r\n.\r\n")))
	{
		/*buf[found-segment]='\n';*/
		buf[found-segment]='\0'; /* used to be found-segment+2 */
		return(0);
	}

	if ( (len==2) && (found=strstr(segment,"\r\n")) )
	{
		buf[found-segment]='\0'; /* Added recently */
		return(0);
	}

	if ( len==1 ) /* Kludge of the century */
	{
		return(0);
	}

	if ( (len <= 4) && (found=strstr(segment,".\r\n")) )
	{
		/*buf[found-segment]='\n';*/
		buf[found-segment]='\0';
		return(0);
	}

	return(1);
}

BOOL needfrom(char *mybuf,struct AsyncFile *ofp)
{
	if ( !strstr(mybuf,"From ") )
	{
		char *newtemp;

		if (newtemp=AllocVec(BIGSTRING,MEMF_CLEAR))
		{
			int tempsize=0;

			DoFmt(newtemp,"From %s@%s ", username,pophost);
			tempsize=strlen(newtemp);
			DoFmt((newtemp+tempsize),"%s\n",datestr((newtemp+tempsize)));
			WriteAsync(ofp,newtemp,strlen(newtemp));
			FreeVec(newtemp);
		}

		return(TRUE);
	}
	return(FALSE);
}
