/****************************************************************/
/**                                                            **/
/**   Direct Communication Chat and Conferencing System        **/
/**   Created May 1993 out of complete boredom.                **/
/**   (c) 1993 Takoyaki Software Ltd.                          **/
/**                                                            **/
/**   Will only run in conjunction with Desqview/X.            **/
/**                                                            **/
/**   File:    CHATMAIL.C                                      **/
/**   Purpose: Controls the mail to/from users                 **/
/**   Usage:   Should be linked with CHATHOST.O                **/
/**                                                            **/
/****************************************************************/

#include	<stdio.h>
#include	<stdlib.h>
#include	<stdarg.h>
#include	<string.h>
#include	<time.h>
#include	<ctype.h>

#include	"chatvars.h"

extern struct user *currentuser,*userlist;
extern struct emote *currentemote,*emotes;
extern struct staticuser *currentstatic,*staticlist,*findstatic(char *);
extern struct mail *maillist;
extern struct item *additem(int);
extern char mailbox[];
extern int dealwithit(char *inbuff, char *input);
extern int dealwithmodem(char *inbuff, char *input);
extern int sysmsgflag;
extern struct user *finduser(char *username);
extern struct user *findusername(char *username);
extern void sendmsg(char *,char *,...);

unsigned long findid=1;

char *days[7] =
	{
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday"
	};

char *months[12] =
	{
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December"
	};


char *ExpandPercents(char *s)
	{
	static char buf[4096];
	char *b=buf;
	int n;

	while (*s)
		{
		if (*s == '%')
			{
			*b++ = '%';
			if (b-buf>=sizeof(buf)-1) break;
			}
		*b++ = *s++;
		if (b-buf>=sizeof(buf)-1) break;
		}

	*b++ = '\0';

	return buf;
	}


/***********************************/
/* find mail with the specified ID */
/***********************************/

struct mail *findmail(unsigned long id)
	{
	struct mail *m=maillist;

	while (m)
		{

		if (m->id == id) break;

		m = m->next;
		}

	return m;
	}

/***************************************/
/* validate mail with the specified ID */
/***************************************/

validatemail(unsigned long id)
	{
	struct mail *n,*m;
	unsigned long i;
	struct user *u;

	if (n = findmail(id))
		{
		if (u = findusername(n->whoto))
			{
			if ((i = u->staticuser->mail))
				{
				do {
					if (n == (m = findmail(i))) return 1;
					} while (i = m->nextmail);
				}
			}
		}
	return 0;
	}

	


/***********************************/
/* find mail with the specified ID */
/***********************************/

struct mail *findunsentmail()
	{
	struct mail *m=maillist;

	while (m)
		{

		if ((m->flags&M_UNSENT) && !(m->itemflags&I_INVALID)) break;

		m = m->next;
		}

	return m;
	}

/******************************************/
/* show mail list to the current user     */
/******************************************/

showmaillist(struct user *u,char *inout)
	{
	unsigned long i;
	struct mail *m;
	int n=0,n2;
	struct staticuser *su = u->staticuser;
	char *mail = u->mailbox;
	char buf[31];
	char buf2[16];

	newsection();

	if (strcmpl(inout,"i")==0)
		{
		if ((i = su->mail))
			{
			do {
				m = findmail(i);
				n = strlen(m->subject);
				n2 = strlen(m->whofrom);
				strncpy(buf,m->subject,20);
				strncpy(buf2,m->whofrom,12);
				buf[20] = '\0';
				buf2[12] = '\0';
				sysmsg(mail,"%4d: [%s%s] from %s%s %s%s"
					,m->id
					,buf
					,(n > 20)?"...":""
					,buf2
					,(n2 >12)?"...":""
					,(m->flags&M_MARKED)?"(MARKED)":""
					,((m->flags&M_UNREAD)?"(UNREAD)":""));
				} while (i = m->nextmail);
			}
		else
			{
			sysmsg(mail,"You have no mail.\n");
			}
		}
	else
		{
		m = maillist;
		while (m)
			{
			if (strcmpl(m->whofrom,currentuser->username)==0)
				{
				n = strlen(m->subject);
				n2 = strlen(m->whoto);
				strncpy(buf,m->subject,20);
				strncpy(buf2,m->whoto,12);
				buf[20] = '\0';
				buf2[12] = '\0';
				sysmsg(mail,"%4d: [%s%s] to %s%s. %s"
					,m->id
					,buf
					,(n > 20)?"...":""
					,buf2
					,(n2 >12)?"...":""
					,(m->flags&M_EXTERNAL)?((m->flags&M_UNSENT)?"[UNSENT]":""):((m->flags&M_UNREAD)?"(UNREAD)":"") );
				}
			m = m->next;
			}
		}

	}

/******************************************/
/* purge any mail who's time has expired  */


void PurgeMail()
	{
	struct mail *m=maillist,*n;
	unsigned long now;

	time(&now);

	while (m)
		{
		n = m->next;

		if (!((m->flags&M_UNREAD) || (m->flags&M_MARKED) || (m->itemflags&I_INVALID)))
			{
/* purge any mail over 14 days old */
			if (difftime(now,m->time) > MAILPURGE*24*60*60)
				deletemail(m);
			}
		m = n;
		}

	}

/******************************************/
/* find out how much unread mail there is */
/******************************************/

int countunread(struct staticuser *su)
	{
	unsigned long i;
	struct mail *m;
	int n=0;
	unsigned long now;

	if ((i = su->mail))
		{
		do {
			m = findmail(i);
			if (!m)
				{
				su->mail = 0;		/* cut the list short */
				printf("Mail lists are corrupt for %s at %d, fixing...\n",su->username,i);
				break;
				}
			if (m->flags&M_UNREAD) n++;
			} while (i = m->nextmail);
		}

	PurgeMail();

	return n;
	}

/************************************/
/* get next unread for current user */
/************************************/

struct mail *getnextunread()
	{
	unsigned long i;
	struct mail *m=NULL;

	if ((i = currentuser->staticuser->mail))
		{
		do {
			m = findmail(i);
			if (m->flags&M_UNREAD) return m;
			} while (i = m->nextmail);
		}

	return NULL;
	}



/*****************************************/
/* Show mail message to the current user */
/*****************************************/

showmsg(struct mail *m)
	{
	struct tm *t;

	newsection();

	t = localtime(&m->time);

	sendmsg(mailbox,
					"[01mTo:      %s\n"
					"[01mFrom:    %s\n"
					"[01mTime:    %d:%d Date: %s %d %s %d\n"
					"[01mSubject: %s\n"
					,m->whoto,m->whofrom
					,(t->tm_hour==23)?0:t->tm_hour+1,t->tm_min,days[t->tm_wday],t->tm_mday,months[t->tm_mon],1900+t->tm_year
					,m->subject
						);

	newsection();

	if (m->body)
		{
		if (strlen(m->body)<8192)
			{
			sendmsg(mailbox,m->body);
			}
		else
			{
			sendmsg(mailbox,"Message too large to read.");
			}
		}

	currentuser->mailid = m->id;

	newsection();
	}

/*********************************************/
/* Show next unread mail to the current user */
/*********************************************/

shownextunread()
	{
	struct mail *m;

	if (!(m = getnextunread()))
		{
		sysmsg(mailbox,"No more unread messages.\n");
		return;
		}

	showmsg(m);

	m->flags &= ~M_UNREAD;

	}


unsigned long findmailid()
	{
	while (findmail(findid))
		findid++;
	return findid;
	}


/***************************************************/
/* delete mail from the system                     */
/***************************************************/

deletemail(struct mail *m)
	{
	struct mail *p,*n;
	struct staticuser *su;

	if (validatemail(m->id))
		{
		if (m->prevmail) findmail(m->prevmail)->nextmail = m->nextmail;
		else if (su = findstatic(m->whoto))
			su->mail = m->nextmail;
		if (m->nextmail) findmail(m->nextmail)->prevmail = m->prevmail;
		}

	if (m->whoto) free(m->whoto);
	if (m->subject) free(m->subject);
	if (m->body) free(m->body);
	if (m->whofrom) free(m->whofrom);
	
	remmail(m);
	free(m);

	}

/***************************************************/
/* link the mail in to the specified users IN list */
/***************************************************/

linkmailin(char *user, struct mail *m)
	{
	struct staticuser *su;
	unsigned long i;
	struct mail *ml;

	if (!(su = findstatic(user))) return 0;

/* generate magic id number for mail message */

	m->id = findmailid();

/* link into mailnext/mailprev list for the user */

	if (!(i = su->mail))
		su->mail = m->id;
	else
		{
		do {
			ml = findmail(i);
			} while (i = ml->nextmail);

		ml->nextmail = m->id;
		m->prevmail = ml->id;
		}

/* mark as unread */

	m->flags |= M_UNREAD;

	return 1;
	}


/*************************************************/
/* add a line to the body of the current message */
/*************************************************/
void addmsgline(struct user *currentuser, char *input)
	{
	if (!currentuser->mail->body) currentuser->mail->body = strdup("");
	currentuser->mail->body = realloc(currentuser->mail->body,strlen(currentuser->mail->body)+strlen(input)+4);
	strcat(currentuser->mail->body,"\\n");
	strcat(currentuser->mail->body,input);
	sysmsgflag |= MAILMSG;
	if (*input) sendmsg(currentuser->mailbox,input);
	else sendmsg(currentuser->mailbox," ");
	}


/******************************/
/* deal with the user's input */
/******************************/

m_mail(char *inbuff, char *input)
	{
	struct mail *m;
	struct staticuser *su;
	struct user *u;
	int n;
	char linebuf[1024],*s;

	strcpy(input,ExpandPercents(input));

	switch(currentuser->modestate)
		{

/* deal with adding a new message to someone */
		case 0:
			sysmsg(mailbox,"Send mail to whom?");
			currentuser->modestate++;
			break;

		case 1:
			if (strnicmp(input,"bbs/",4) == 0 || strchr(input,'@'))
				{
				if (!strchr(input,'@'))
					{
					if (input[4]=='\0')
						{
						sysmsg(mailbox,"Error: Invalid user specified.\n");
						currentuser->mode = dealwithit;
						currentuser->modestate = 0;
						break;
						}
					}
				if (!(currentuser->mail = addmail()))
					{
					sysmsg(mailbox,"Rather Serious Error: Host out of memory.\n");
					currentuser->mode = dealwithit;
					currentuser->modestate = 0;
					break;
					}

				currentuser->mail->whoto = strdup(input);
				currentuser->mail->whofrom = strdup(currentuser->username);
				currentuser->mail->itemflags |= I_INVALID;
				currentuser->mail->flags |= M_UNSENT|M_EXTERNAL;
				}
			else
				{
				if (!(su = findstatic(input)))
					{
					sysmsg(mailbox,"Error: User doesn't exist.\n");
					currentuser->mode = dealwithit;
					currentuser->modestate = 0;
					break;
					}

				if (!(currentuser->mail = addmail()))
					{
					sysmsg(mailbox,"Rather Serious Error: Host out of memory.\n");
					currentuser->mode = dealwithit;
					currentuser->modestate = 0;
					break;
					}

				currentuser->mail->whoto = strdup(su->username);
				currentuser->mail->whofrom = strdup(currentuser->username);
				currentuser->mail->itemflags |= I_INVALID;
				}

			sysmsg(mailbox,"Enter subject matter: (50 chars)\n");

			currentuser->modestate++;
			break;

		case 2:
			if (strlen(input)>50)
				{
				sysmsg(mailbox,"Subject truncated to 50 chars in length.\n");
				input[50] = '\0';
				}

			currentuser->mail->subject = strdup(input);
			currentuser->quote=NULL;

			sysmsg(mailbox,"Enter message to %s:\n(/S to save or /Q to quit on a blank line)\n",currentuser->mail->whoto);

			currentuser->modestate++;
			break;

		case 3:
			if (strcmpl(input,"/") && strncmpl(input,"/\" ",3) && strcmpl(input,"/D") && strcmpl(input,"/L")
				&& strcmpl(input,"/S") && strcmpl(input,"/Q") && strcmpl(input,"."))
				{
				addmsgline(currentuser, input);
				break;
				}

			if (strncmpl(input,"/\" ",3)==0)
				{
				if (input[3]=='/')
					{
					sysmsg(mailbox,"Cannot run commands while editing!");
					break;
					}
				dealwithit(input+3,input+3);
				break;
				}

			if (strcmpl(input,"/")==0)
				{
				if (currentuser->quote==NULL)
					{
					sysmsg(mailbox,"No message to quote from.");
					break;
					}
				if (findmail(currentuser->mailid))
					{
					if (strlen(currentuser->quote)==0)
						{
						sysmsg(mailbox,"No more lines to quote.");
						break;
						}

					s = linebuf+2;

					strcpy(linebuf,"> ");

					n=0;
					while (1)
						{
						if (currentuser->quote[n]=='\0' || (currentuser->quote[n]=='\\' && toupper(currentuser->quote[n+1])=='N'))
							{
							currentuser->quote += n;
							if (*currentuser->quote) currentuser->quote+=2;
							break;
							}
						*s++ = currentuser->quote[n++]; *s = '\0';
						}

					addmsgline(currentuser, linebuf);

					}
				break;
				}

			if (strcmpl(input,"/<")==0)
				{
				
				break;
				}

			if (strcmpl(input,"/D")==0)
				{
				n = strlen(currentuser->mail->body)-2;
				if (n<1)
					{
					sysmsg(mailbox,"No more lines to delete.");
					break;
					}

				for (;n>0;n--)
					{
					if (currentuser->mail->body[n]=='\\' && toupper(currentuser->mail->body[n+1])=='N')
						{
						currentuser->mail->body[n]='\0';
						n = -1;
						break;
						}
					}
				if (n!=-1) *currentuser->mail->body='\0';
				}

			if (strcmpl(input,"/L")==0 || strcmpl(input,"/D")==0)
				{
				newsection();				
				sysmsg(mailbox,"Current mail is to %s\nSubject: %s\n---",currentuser->mail->whoto,currentuser->mail->subject);
				sysmsgflag |=MAILMSG;
				if (currentuser->mail->body) sendmsg(mailbox,currentuser->mail->body);
				break;
				}

			if (strcmpl(input,"/Q")==0)
				{
				sysmsg(mailbox,"Sending of mail aborted.\n");
				deletemail(currentuser->mail);
				currentuser->mail = NULL;
				currentuser->mode = dealwithit;
				currentuser->modestate = 0;
				break;
				}

			if (!(currentuser->mail->flags&M_EXTERNAL))
				{
				if (!linkmailin(currentuser->mail->whoto,currentuser->mail))
					{
					sysmsg(mailbox,"Mail NOT sent due to undefined error.\n");
					}
				else
					{
					sysmsg(mailbox,"Mail sent successfully to %s.\n",currentuser->mail->whoto);
					currentuser->mail->itemflags &= ~I_INVALID;
					time(&currentuser->mail->time);
					if ((u = findusername(currentuser->mail->whoto)))
						{
						u->BELL = 1;
						sysmsg(u->mailbox,"You have mail! (%s)\n",currentuser->mail->whofrom);
						}
					}
				}
			else
				{
				sysmsg(mailbox,"Mail polled to %s.\n",currentuser->mail->whoto);
				currentuser->mail->itemflags &= ~I_INVALID;
				currentuser->mail->id = findmailid();
				time(&currentuser->mail->time);
				}

			currentuser->mode = dealwithit;
			currentuser->modestate = 0;
			break;

/* if the user is replying to a message */
		case 20:
			if (!currentuser->mailid)
				{
				sysmsg(mailbox,"At first read the message you wish to reply to.");
				currentuser->mode = dealwithit;
				currentuser->modestate = 0;
				break;
				}

			if (!(m = findmail(currentuser->mailid)))
				{
				sysmsg(mailbox,"The mail you wish to reply to no longer exists.");
				currentuser->mode = dealwithit;
				currentuser->modestate = 0;
				break;
				}

			currentuser->quote=m->body;

			currentuser->mail = addmail();
			currentuser->mail->whoto = strdup(m->whofrom);
			currentuser->mail->whofrom = strdup(currentuser->username);
			currentuser->mail->subject = malloc(strlen(m->subject)+4+1);
			if (strnicmp(m->subject,"Re: ",4) == 0) strcpy(currentuser->mail->subject,m->subject);
			else
				{
				strcpy(currentuser->mail->subject,"Re: ");
				strcat(currentuser->mail->subject,m->subject);
				}
			currentuser->mail->itemflags |= I_INVALID;
			if (m->flags&M_EXTERNAL) currentuser->mail->flags |= M_UNSENT|M_EXTERNAL;

			newsection();
			sysmsg(mailbox,"Replying to message %d from %s.\n"
				"Subject: %s\n"
				"Enter message to %s:\n(/S to save or /Q to quit on a blank line)\n"
					,m->id,m->whofrom,currentuser->mail->subject,m->whofrom);

			currentuser->modestate = 3;
			break;

		default:
			break;
		}
	}


/*******************************/
/* deal with the modem's input */
/*******************************/

m_modemmail(char *inbuff, char *input)
	{
	struct staticuser *su;
	char *s,*p;
	struct user *u;
	struct mail *m;
	int n = 0;
	static int changed;

	switch(currentuser->modestate)
		{

/* deal with adding a new message to someone */
		case 0:
			if (strcmpl(input,"quit") == 0)
				{
				sendmsg(mailbox,"Leaving mailbox.");
				currentuser->mode = dealwithmodem;
				currentuser->modestate = 0;
				if (changed) saveall();
				changed = 0;
				}
			if (strcmpl(input,"any new?") == 0)
				{
				if (findunsentmail())
					{
					sendmsg(mailbox,"Yes");
					currentuser->modestate = 10;
					changed = 1;
					}
				else sendmsg(mailbox,"No");
				break;
				}
			if (strcmpl(input,"new") == 0)
				{
				currentuser->modestate++;
				sendmsg(mailbox,"To>");
				changed = 1;
				}
			else sendmsg(mailbox,"Mail>");
			break;

		case 1:
			su = findstatic(input);

			if (!su)
				{
				sendmsg(mailbox,"Unknown User!");
				currentuser->modestate = 0;
				break;
				}
			if (!(currentuser->mail = addmail()))
				{
				currentuser->mode = dealwithmodem;
				currentuser->modestate = 0;
				break;
				}

			currentuser->mail->whoto = strdup(su->username);
			currentuser->mail->itemflags |= I_INVALID;

			sendmsg(mailbox,"Subject>");

			currentuser->modestate++;
			break;

		case 2:
			if (strlen(input)>50)
				input[50] = '\0';

			currentuser->mail->subject = strdup(input);

			sendmsg(mailbox,"From>");

			currentuser->modestate++;
			break;

		case 3:
			s = malloc(strlen(input)+4+1);
			if (!strchr(input,'@')) strcpy(s,"bbs/");
			else *s='\0';
			strcat(s,input);
			
			currentuser->mail->whofrom = s;
			sendmsg(mailbox,"Date>");
			currentuser->mail->flags |= M_EXTERNAL;
			currentuser->modestate++;
			break;

		case 4:
			time(&currentuser->mail->time);
			sendmsg(mailbox,"Body>");
			currentuser->modestate++;
			break;

		case 5:
			if (strcmp(input,"ÈQUIT") == 0)
				{
				if (linkmailin(currentuser->mail->whoto,currentuser->mail))
					{
					currentuser->mail->itemflags &= ~I_INVALID;
					if ((u = findusername(currentuser->mail->whoto)))
						{
						u->BELL = 1;
						sysmsg(u->mailbox,"You have mail! (%s)\n",currentuser->mail->whofrom);
						}
					sendmsg(mailbox,"Finished.");
					}
				currentuser->modestate = 0;
				break;
				}

			if (!currentuser->mail->body) currentuser->mail->body = strdup("");

			currentuser->mail->body = realloc(currentuser->mail->body,strlen(currentuser->mail->body)+strlen(input)+4);

			strcat(currentuser->mail->body,"\\n");
			strcat(currentuser->mail->body,ExpandPercents(input));

			sendmsg(mailbox,"Body>");
			break;
	
		case 10:
			if (strcmpl(input,"Ok, give us it.") == 0)
				{
				m = findunsentmail();
				currentuser->mail = m;
				sendmsg(mailbox,"Sending.");

				sendmsg(mailbox,m->whofrom);

				sendmsg(mailbox,(strchr(m->whoto,'@'))?m->whoto:strchr(m->whoto,'/')+1);

				sendmsg(mailbox,m->subject);

				s = m->body;
				if (s==NULL) s = "--- NO BODY TO MESSAGE ---";
				while (p = strstr(s,"\\n"))
					{
					*p = '\0';
					sendmsg(mailbox,s);
					*p = '\\';
					s = p+2;
					}
				sendmsg(mailbox,s);
				sendmsg(mailbox,"ÈQUIT");

				currentuser->modestate++;
				}
			break;
		case 11:
			if (strcmpl(input,"Received ok.")==0)
				{
				sendmsg(mailbox,"Mail>");
				currentuser->modestate = 0;
				currentuser->mail->flags &= ~M_UNSENT;
				if (findusername(currentuser->mail->whofrom))
					sysmsg(findusername(currentuser->mail->whofrom)->mailbox,"Polled mail sent to [%s].",currentuser->mail->whoto);
				currentuser->mail = NULL;
				}
			break;

		default:
			break;
		}
	}
