/* --------------------
	vmail -- load.c

	Routines to find folders, find all mail items in folders.  Empty
	folders are ignored.

	Copyright (C) J. Zobel, University of Melbourne, October 1987.
-------------------- */

#include "defs.h"
#include <ctype.h>


/* --------------------
	Find all folders in mail directory, set up linked list.
-------------------- */
void
find_folders()
{
	struct direct *dp;
	struct stat statbuf;
	DIR		*dirp;
	int		i, n = 0, scmp();
	char	str[LEN], **fds, **ftmp;
	folder	tmp, last = (folder) NULL;

	stat(mail_dir, &statbuf);
		/* guess number of folders - 10 is an error bound */
	ftmp = fds = (char **) malloc(sizeof(char *) * (statbuf.st_nlink+10));
	dirp = opendir(mail_dir);
	for(dp=readdir(dirp) ; dp != (struct direct *)NULL ; dp=readdir(dirp)) {
		if(strcmp(dp->d_name, ".") == 0)
			continue;
		if(strcmp(dp->d_name, "..") == 0)
			continue;
		sprintf(str, "%s/%s", mail_dir, dp->d_name);
		stat(str, &statbuf);
		if(! (statbuf.st_mode & S_IFDIR))
			continue;
		if(! (statbuf.st_mode & S_IREAD) || ! (statbuf.st_mode & S_IWRITE)
				|| ! (statbuf.st_mode & S_IEXEC)) {
			printf("Folder %s unreadable.\n", dp->d_name);
			continue;
		}
		*ftmp = NEWSTR(strlen(dp->d_name)+1);
		strcpy(*ftmp, dp->d_name);
		n++, ftmp++;
	}
	closedir(dirp);
	if(n == 0) {
		printf("No folders.\n");
		exit(1);
	}
	qsort(fds, n, sizeof(char *), scmp);
	for(i=0, ftmp=fds ; i < n ; ftmp++, i++) {
		tmp = NEW(mail_folder);
		tmp->name = *ftmp;
		tmp->last = tmp->mail = (item) NULL;
		tmp->pages = tmp->pagenum = 1;
		tmp->valid = false;
		tmp->prev = last;
		tmp->next = (folder) NULL;
		if(last != (folder) NULL)
			last->next = tmp;
		else
			folders = tmp;
		last = tmp;
	}
}


int
scmp(p, q)
	char	**p, **q;
{
	return(strcmp(*p, *q));
}


/* --------------------
	Find all mail items in given folder, throw away folder record if no
	mail items.  Returns next folder in linked list, adds new folder
	records to linked list if original record overflows.
-------------------- */
folder
find_mail(flr, load_time)
	folder	flr;
	int		load_time;
{
	struct direct *dp, *readdir();
	DIR		*dirp, *opendir();
	int		i, n = 0, items[MAXITEMS], *itmp = items, dcmp();
	char	str[LEN];
	item	tmp, last = (item) NULL;
	folder	new_folder();

	sprintf(str, "%s/%s", mail_dir, flr->name);
	dirp = opendir(str);
	if(dirp == (DIR *) NULL) {
		if(load_time)
			printf("%s: strange folder.\n", str);
	} else {
		for(dp=readdir(dirp) ; dp!=(struct direct *) NULL ; dp=readdir(dirp)) {
					/* get numbers of all mail items */
			if(n >= MAXITEMS) {
				no_control();
				printf("\nMore than %d items in folder.\n",MAXITEMS-1);
				exit(1);
			}
				/* ignore anything that is not string of digits */
			if(! digits(dp->d_name))
				continue;
					/* assume any file that is string of digits is a mail
					   item and not a directory */
			*itmp = atoi(dp->d_name);
			n++, itmp++;
		}
		closedir(dirp);
	}
	if(n == 0) {		/* Empty folder - delete from list */
		flr->valid = EMPTY;
		if(load_time)
			printf("\t%s: empty\n", flr->name);
		if(flr->prev != (folder) NULL)
			flr->prev->next = flr->next;
		else
			folders = flr->next;
		if(flr->next != (folder) NULL)
			flr->next->prev = flr->prev;
		return(flr->next);
	} else
		qsort(items, n, sizeof(int), dcmp);
	if(load_time)		/* show status of folder */
		if(n == 1)
			printf("\t%s: %d\n", flr->name, *items);
		else
			printf("\t%s: %d-%d (%d items)\n",flr->name,*items,*(items+n-1),n);
	for(i=0, itmp=items ; i < n ; itmp++, i++) {
			/* add item to list of items in folder */
		tmp = NEW(mail_item);
		get_title(flr, tmp, *itmp);
		if(i % lines == 0 && last != (item) NULL) {
				/* add new record for folder */
			flr->last = last;
			flr = new_folder(flr);
			last = (item) NULL;
		}
		tmp->next = (item) NULL;
		tmp->prev = last;
		if(last != (item) NULL)
			last->next = tmp;
		else
			flr->mail = tmp;
		last = tmp;
	}
	flr->last = last;
	return(flr->next);
}


int
dcmp(a, b)
	int		*a, *b;
{
	return(*a - *b);
}


/* --------------------
	Return true if string consists only of digits.
-------------------- */
int
digits(str)
	char	*str;
{
	for( ; *str != '\0' ; str++)
		if(! isdigit(*str))
			return(false);
	return(true);
}


/* --------------------
	Make header line from mail item.
-------------------- */
void
get_title(flr, mail, num)
	folder	flr;
	item	mail;
	int		num;
{
	FILE	*fp, *fopen();
	char	date[10], str[LEN], subj[42], from[20], to[16], fill[42];
	char	repl = ' ', *first(), *s, *fgets();
	int		i;

	subj[0] = from[0] = to[0] = fill[0] = '\0';
	sprintf(str, "%s/%s/%d", mail_dir, flr->name, num);
	fp = fopen(str, "r");
	while((s=fgets(str, LEN, fp)) != (char *) NULL && *str != '\n') {
			/* while not eof and reading header */
		str[strlen(str)-1] = '\0';
			/* should also check date */
		if(! lstrncmp("date:", str, 5))
			get_date(str+5, date);
		else if(! lstrncmp("subject:", str, 8)) {
			s = first(str+8);
			strncpy(subj, s, 41);
			subj[41] = '\0';
		} else if(! lstrncmp("replied:", str, 8))
			repl = '-';
		else if(! lstrncmp("from:", str, 5)) {
			s = first(str+5);
			if(! isuser(s)) {
				strncpy(from, s, 19);
				from[19] = '\0';
			}
		} else if(! lstrncmp("to:", str, 3)) {
			s = first(str+3);
			strncpy(to, s, 15);
			to[15] = '\0';
		} else if(! lstrncmp("apparently-to:", str, 14)) {
			s = first(str+14);
			strncpy(to, s, 15);
			to[15] = '\0';
		}
	}
	if(s != (char *) NULL) {
		for(i=0 ; i < 42 && fgets(str, LEN, fp) != (char *) NULL ;) {
			str[strlen(str)-1] = ' ';
			strncpy(fill+i, str, 42-i);
			i += strlen(str);
		}
		fill[41] = '\0';
	}
	fclose(fp);
	if(subj[0] == '\0')
		strcpy(subj, "(none)");
	flatten(subj);
	flatten(fill);
	if(from[0] == '\0')
		sprintf(str, "  %8s %cTo: %-15s  %s << %s",date,repl,to,subj,fill);
	else
		sprintf(str, "  %8s %c%-19s  %s << %s",date,repl,from,subj,fill);
	str[cols-6] = '\0';
	mail->title = NEWSTR(strlen(str)+1);
	mail->number = num;
	strcpy(mail->title, str);
}


#ifdef USDATE
#define DAY1	date[3]
#define DAY2	date[4]
#define MTH1	date[0]
#define MTH2	date[1]
#else
#define DAY1	date[0]
#define DAY2	date[1]
#define MTH1	date[3]
#define MTH2	date[4]
#endif
#define YR1		date[6]
#define YR2		date[7]

/* --------------------
	Get date from first argument; assumes format is one of
Date: Fri, 3 Apr 87 ...						(a)
Date: Wed, 17 Jun 87 ...					(b)
Date: 07 Sep 87 ...							(c)
Date: Tue Sep 29 12:27:01 EST 1987			(d)
	Dates are put into Imperial form (dd-mm-yy), or US form (mm-dd-yy)
	if -DUSDATE is set.

	This routine should be more flexible re: recognizing date formats.
-------------------- */
void
get_date(str, date)
	char	*str, *date;
{
	int		i;
	bool	get_month();
	char	*next_token();

	date[2] = date[5] = '-';
	date[8] = '\0';
	for(; *str == ' ' || *str == '\t' ; str++)
		;
	if(*str == '\0')
		goto unknown;
	if(! isdigit(*str))		/* day of month not first field */
		str = next_token(str);		/* skip day of week */
	if(str == (char *) NULL + 1)
		goto unknown;

	if(isdigit(*str)) {		/* one of (a), (b) or (c) formats */
		if(isdigit(*(str+1)))	/* two-number date */
#ifdef USDATE
			DAY1 = *str, DAY2 = *(++str);
		else
			DAY1 = '0', DAY2 = *str;
#else
			DAY1 = (*str == '0') ? ' ' : *str, DAY2 = *(++str);
		else
			DAY1 = ' ', DAY2 = *str;
#endif
		str = next_token(str);	/* go to month */
		if(str == (char *) NULL + 1)
			goto unknown;
		if(! get_month(str, date))
			goto unknown;
		str = next_token(str);	/* go to year */
		if(str == (char *) NULL + 1)
			goto unknown;
		if(! isdigit(*str) || ! isdigit(*(str+1)))
			goto unknown;
		YR1 = *str, YR2 = *(str+1);
	} else {	/* format (d) */
		if(! get_month(str, date))
			goto unknown;
		str = next_token(str);	/* go to day */
		if(str == (char *) NULL + 1)
			goto unknown;
		if(! isdigit(*str) || ! isdigit(*(str+1)))
			goto unknown;
#ifdef USDATE
		DAY1 = *str, DAY2 = *(++str);
#else
		DAY1 = (*str == '0') ? ' ' : *str, DAY2 = *(++str);
#endif
		str += strlen(str) - 2;		/* go to year */
		if(! isdigit(*str) || ! isdigit(*(str+1)))
			goto unknown;
		YR1 = *str, YR2 = *(str+1);
	}
	return;		/* date is ok */

unknown:			/* erase date */
	for(i=0 ; i < 8 ; i++)
		date[i] = ' ';
}


static char *month[] = {
	"jan", "feb", "mar", "apr", "may", "jun",
	"jul", "aug", "sep", "oct", "nov", "dec"
};

/* --------------------
	Put the month from the first part of str in date as a number.
-------------------- */
bool
get_month(str, date)
	char	*str, *date;
{
	int		i;

	for(i=0 ; i < 12 && lstrncmp(month[i], str, 3) != 0 ; i++)
		;
	if(i == 12)		/* string doesn't match month */
		return(false);
	else {	
		if(i < 9)	/* single digit month */
#ifdef USDATE
			MTH1 = ' ', MTH2 = i + '0' + 1;
#else
			MTH1 = '0', MTH2 = i + '0' + 1;
#endif
		else
			MTH1 = '1', MTH2 = i + '0' - 9;
		return(true);
	}
}


/* --------------------
	Replace tabs and newlines in string by spaces.
-------------------- */
void
flatten(str)
	char	*str;
{
	char	prev, *s = str;

	for(prev='\0' ; (*s = *str) != '\0' ; prev = *str, str++) {
		if(*str == '\t')
			*s = ' ';
		if(prev != ' ' || *s != ' ')
			s++;
	}
}


/* --------------------
	Return pointer to first non-white character in string.
-------------------- */
char *
first(str)
	char	*str;
{
	for(; *str == ' ' || *str == '\t' || *str == '\n' ; str++)
		;
	return(str);
}


/* --------------------
	Check given string for occurence of user's name.
-------------------- */
int
isuser(str)
	char	*str;
{
	int		len = strlen(user);

	for(; *str != '\0' ; str++)
			/* assume all chars in all unames alphanumeric */
		if(*str == *user && strncmp(str, user, len) == 0 &&
				(*(str+len) < 'a' || *(str+len) > 'z') &&
				(*(str+len) < 'A' || *(str+len) > 'Z') &&
				(*(str+len) < '0' || *(str+len) > '9'))
			return(true);
	return(false);
}


/* --------------------
	Make a new folder record.
-------------------- */
folder
new_folder(flr)
	folder	flr;
{
	folder	f;

	f = NEW(mail_folder);
	f->prev = flr;
	f->next = flr->next;
	flr->next = f;
	if(f->next != (folder) NULL)
		f->next->prev = f;
	f->pagenum = f->pages = flr->pages + 1;
	f->name = flr->name;
	f->valid = true;
	f->mail = f->last = (item) NULL;
	flr = f;
	for(f=f->prev; f != (folder) NULL && f->name == flr->name ; f=f->prev)
		f->pages = flr->pages;
	return(flr);
}


/* --------------------
	Find next free slot in directory for mail item (that is, find next
	unused number) -- structures in vmail may not be up to date if user
	has initiated a "send" process.
-------------------- */
int
next_vacant(flr)
	folder	flr;
{
	struct direct *dp, *readdir();
	DIR		*dirp, *opendir();
	char	str[LEN];
	int		i, n = 0;

	sprintf(str, "%s/%s", mail_dir, flr->name);
	dirp = opendir(str);
	for(dp=readdir(dirp) ; dp != (struct direct *) NULL ; dp=readdir(dirp)) {
			/* ignore anything that is not string of digits */
		if(! digits(dp->d_name))
			continue;
		if((i = atoi(dp->d_name)) > n)
			n = i;
	}
	closedir(dirp);
	return(n+1);
}
