/* $Id: fmail.cpp,v 1.68 2004/01/02 03:54:01 fesnel Exp $ */
/*******************************************************************************
 *   This program is part of a library used by the Archimedes email client     * 
 *                                                                             *
 *   Copyright : (C) 1995-1998 Gennady B. Sorokopud (gena@NetVision.net.il)    *
 *               (C) 1995 Ugen. J. S. Antsilevich (ugen@latte.worldbank.org)   *
 *               (C) 1998-2004 by the Archimedes Project                       *
 *                   http://sourceforge.net/projects/archimedes                *
 *                                                                             *
 *             --------------------------------------------                    *
 *                                                                             *
 *   This program is free software; you can redistribute it and/or modify      *
 *   it under the terms of the GNU Library General Public License as published *
 *   by the Free Software Foundation; either version 2 of the License, or      *
 *   (at your option) any later version.                                       *
 *                                                                             *
 *   This program is distributed in the hope that it will be useful,           *
 *   but WITHOUT ANY WARRANTY, without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU Library General Public License for more details.                      *
 *                                                                             *
 *   You should have received a copy of the GNU Library General Public License *
 *   along with this program; if not, write to the Free Software               *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA.  *
 *                                                                             *
 ******************************************************************************/


#include <glib.h>

#include <fmail.h>
#include <umail.h>
#include <cfgfile.h>

#include <algorithm>

#include "addrbook_util.h"

#ifdef WITH_THREADS
	#include <pthread.h>
pthread_mutex_t send_mutex;
#endif

extern cfgfile Config;

char    lockname[MAXPATHLEN];
int     locked = 0;

vector<struct _mail_folder *> mailbox;
vector<struct _mail_folder *> hidden_mailbox;

/* system folders */
struct _mail_folder *trash, *inbox, *outbox, *sentm, *draft, *mftemplate, *ftemp = NULL, *fmbox = NULL;

char mailbox_path[255];
u_int sort_type, folder_sort;
u_int offline;

/* headers to remove before sending */
char *stripfields[]= { STATUS_FIELD, "X-RDate", "X-SDate", "Fcc", FWD_ORGMSG,
	REPLY_ORGMSG, BOUNCE_ORGMSG, "Status", REALENGTH, FROMLINE, SOURCE_FIELD,
	NULL};

/* headers to scan if short header only */
char *shorthfields[] = { STATUS_FIELD, MIME_C_TYPE, MIME_C_LENGTH, "From", "To", "Subject", "Date", "X-RDate", "X-SDate", "Message-ID", "In-Reply-To", "References", "Status", "Newsgroups", SOURCE_FIELD, NULL};

/* headers with special meaning */
char *specfields[] = { "Received", MIME_C_ENCR, MIME_C_DESCR, MIME_C_ID, MIME_C_DISP, MIME_VERSION, "X-Mailer", "X-Newsreader", "Path", "Xref", NULL};

typedef struct _tzdef { /* timezone definition */
	char tzname[4];     /* timezone name */
	int gmtofft;        /* timezone offset from GMT in hours */
} tzdef;

char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
	"Sep", "Oct", "Nov", "Dec"};

char *days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

/* known timezones */
struct _tzdef timezones[] = {
	{ "UT",     0},
	{ "GMT",    0},
	{ "EST",    -5},
	{ "EDT",    -4},
	{ "CST",    -6},
	{ "CDT",    -5},
	{ "MST",    -7},
	{ "MDT",    -6},
	{ "PST",    -8},
	{ "PDT",    -7},
	{ "",       0}
};

struct _flag msgflags[] = {
	{ SIGNED,      SIGNED,         "Signed"},
	{ UNREAD,      UNREAD,         "Unread"},
	{ MARKED,      MARKED,         "Marked"},
	{ URGENT1|URGENT2, URGENT1|URGENT2,    "VeryHigh"},
	{ URGENT1|URGENT2, URGENT2,        "High"},
	{ URGENT1|URGENT2, 0,          "Normal"},
	{ URGENT1|URGENT2, URGENT1,        "Low"},
	{ ANSWERED,        ANSWERED,       "Answered"},
	{ FORWARDED,       FORWARDED,      "Forwarded"},
	{ PGP_SIGNED,      PGP_SIGNED,     "PgpSigned"},
	{ PGP_ENCRYPTED,   PGP_ENCRYPTED,      "PgpEncrypted"},
	{ M_DONTEXPIRE,    M_DONTEXPIRE,       "NoExpire"},
	{ 0, 0, ""}
};

#ifdef MMAP_MSG     /* mmap message to speed up parsing */

char *mmsg = NULL;      /* mmapped message */
long mmpos = 0;         /* position in msg */
long mmlen = 0;         /* length of mmapped block */
long mmmax = 0;         /* max length of mmaped block */
long mmofft = 0;        /* offset from beginning of the message */
int  mmapfd = -1;       /* mmaped file descriptor */

long
mmtell(FILE *stream)          /* for compatibility with ftell */ {
	return mmsg ? (mmpos + mmofft) : (stream ? ftell(stream) : -1);
}

int
mmseek(FILE *stream , long offset, int whence)  /* for compatibility with fseek */ {
	long tpos;

	if(mmsg == NULL)
		return stream ? fseek(stream, offset, whence) : -1;

	switch(whence) {
		case SEEK_SET:
			offset -= mmofft;
			if((offset > mmlen) || (offset < 0))
				return -1;
			else {
				mmpos = offset;
				return 0;
			}
			break;

		case SEEK_CUR:
			tpos = mmpos + offset;
			if((tpos > mmlen) || (tpos < 0))
				return -1;
			else {
				mmpos = tpos;
				return 0;
			}
			break;

		case SEEK_END:
			tpos = mmlen + offset;
			if((tpos > mmlen) || (tpos < 0))
				return -1;
			else {
				mmpos = tpos;
				return 0;
			}
			break;

		default:
			return -1;
			break;
	}

	return -1;
}

char *
mmgets(char *str, register size_t size, register FILE *stream)   /* for compatibility with fgets */ {
	register size_t len;
	register char *t;

	if(mmsg == NULL)
		return stream ? fgets(str, size, stream) : NULL;

	if(mmpos >= mmmax)
		return NULL;

	if(size == 0)
		return NULL;

	chsize:
	if((mmlen - mmpos) > size)
		len = size;
	else
		len = mmlen - mmpos;

	if((t = (char*)memchr((void *)(mmsg + mmpos), '\n', len)) != NULL) {
		len = ++t - (mmsg + mmpos);
		(void)memcpy((void *)str, (void *)(mmsg + mmpos), len);
		str[len] = '\0';
		mmpos += len;
		return str;
	} else {
		if(mmmax > mmlen) {
			munmap(mmsg, mmlen);
			mmlen += MMAP_INCR;
			if(mmlen > mmmax)
				mmlen = mmmax;
			if((mmsg = (char *)mmap(NULL, mmlen , PROT_READ, MAP_PRIVATE, mmapfd, (off_t)0)) == (char *)MAP_FAILED) {
				display_msg(MSG_FATAL, "mmgets", "MMAP failed");
				return NULL;
			}
			goto chsize;
		}
		(void)memcpy((void *)str, (void *)(mmsg + mmpos), len);
		str[len] = '\0';
		mmpos += len;
		return str;
	}

}

#endif

int
strip_newline(char *string) {
	int len;

	len = strlen(string);
	len--;

	if((len >= 0) && (string[len] == '\n')) {
		string[len] = '\0';
		len--;
		if((len >= 0) && (string[len] == '\r')) {
			string[len] = '\0';
			return 2;
		}

		return 1;
	}

	return 0;
}

int
get_tz_offt(char *tzone) {
	int i = 0;

	while(*timezones[i].tzname) {
		if(!strcmp(tzone, timezones[i].tzname))
			return timezones[i].gmtofft * 3600;
		i++;
	}

	return -1;
}

int
get_date_offt() {
	static int off = -1;
	register struct tm *lt;
	time_t t;
	struct tm gmt;

	if(off != -1)
		return off;

	(void) time(&t);
	gmt = *gmtime(&t);
	lt = localtime(&t);

	off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;

	if(lt->tm_year < gmt.tm_year)
		off -= 24 * 60;
	else if(lt->tm_year > gmt.tm_year)
		off += 24 * 60;
	else if(lt->tm_yday < gmt.tm_yday)
		off -= 24 * 60;
	else if(lt->tm_yday > gmt.tm_yday)
		off += 24 * 60;

	if(off >= 24*60)
		off = 23*60+59;

	return off;
}

char *
get_arpa_date(time_t t) {
	static char arpadate[60];
	char buf[50];
	int off;

	off = get_date_offt();

#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "C");
#endif
	strftime(buf, 49, "%a, %d %h %Y %T %%c%%04d (%Z)", localtime(&t));
	snprintf(arpadate, sizeof(arpadate), buf, off > 0 ? '+' : '-', abs((off/60*100) + (off % 60)));
#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "");
#endif

	return arpadate;
}

int
get_month(char *str) {
	int i;

	for(i = 0;i < 12;i++) {
		if(!strncasecmp(str, months[i], 3))
			return i;
	}

	return -1;
}

int
get_day(const char *str) {
	int i;

	for(i = 0;i < 7;i++) {
		if(!strncasecmp(str, days[i], 3))
			return i;
	}

	return -1;
}

time_t
get_date(const char *str) {
	int year, mon, day, hour, min, sec, wday;
	int offt;
	struct tm tmm;
	char month[5], hbuf[6], tzone[6];
	const char *p;
	char *p2;
	time_t rtime;

	if(strlen(str) < 16)
		return 0L;

	mon = year = hour = min = sec = -1;
	month[0] = '\0';
	tzone[0] = '\0';
	offt = 0;
	rtime = 0;

	while((*str == ' ') || (*str == 0x09))
		str++;

	if((wday = get_day(str)) != -1) {
		if((p = strchr(str, ',')) != NULL)
			p++;
		else
			if((p = strchr(str, ' ')) != NULL)
			p++;
		else
			p = str + 3;
	} else
		p = str;

	while(*p == ' ')
		p++;

	sscanf(p, "%d%3s%d%d:%d:%d%5s", &day, month, &year, &hour, &min, &sec, tzone);
	if(year>=0&&year<100)
	{
	if(year>=70)year+=1900;
	else year+=2000;
	}
	mon = get_month(month);

	if((mon == -1) || (year == -1) || (hour == -1)) {
		sscanf(p, "%3s%d%d:%d:%d%d", month, &day, &hour, &min, &sec, &year);
	if(year>=0&&year<100)
	{
		if(year>=70)year+=1900;
		else year+=2000;
	}
		mon = get_month(month);
		if((mon == -1) || (year == -1) || (hour == -1))
			return 0L;
	}

	if(isalpha(*tzone)) {
		if((offt = get_tz_offt(tzone)) == -1)
			offt = 0;
	} else {
		if((offt = atoi(tzone)) != 0)
			offt = ((offt / 100) * 60 + (offt % 100)) * 60;
	}

	if(year > 1900)
		year -= 1900;

	if((hour > 23) || (min < 0)) {
		sprintf(hbuf, "%04d", hour);
		p2 = hbuf + 2;
		min = atoi(p2);
		*p2 = '\0';
		hour = atoi(hbuf);
		sec = 0L;
	}

	if(sec < 0)
		sec = 0L;

	tmm.tm_sec = sec;
	tmm.tm_min = min;
	tmm.tm_hour = hour;
	tmm.tm_mday = day;
	tmm.tm_mon = mon;
	tmm.tm_year = year;

	tmm.tm_yday = 0;
	tmm.tm_wday = 0;
	tmm.tm_isdst = -1;

	rtime = mktime(&tmm);
	rtime = rtime - offt + get_date_offt() * 60;
	return rtime;
}

int
parse_offt(const char *str) {
	char month[5], tzone[6];
	const char *p;
	int i, offt;

	month[0] = '\0';
	tzone[0] = '\0';

	while((*str == ' ') || (*str == 0x09))
		str++;

	if(get_day(str) != -1) {
		if((p = strchr(str, ',')) != NULL)
			p++;
		else
			if((p = strchr(str, ' ')) != NULL)
			p++;
		else
			p = str + 3;
	} else
		p = str;

	while(*p == ' ')
		p++;

	if(sscanf(p, "%d%3s%d%d:%d:%d%5s", &i, month, &i, &i, &i, &i, tzone) < 7)
		return -1;

	if(isalpha(*tzone)) {
		if((offt = get_tz_offt(tzone)) == -1)
			return -1;
	} else {
		if((*tzone != '-') && (*tzone != '+') &&
		   !isdigit(*tzone))
			return -1;
		if((offt = atoi(tzone)) != 0)
			offt = ((offt / 100) * 60 + (offt % 100)) * 60;
	}

	return offt;
}

struct _news_addr *
get_news_addr(char *str) {
	struct _news_addr *naddr, *naddr1, *naddr2;
	char *p;

	if(!str)
		return NULL;

	if((p = strtok(str, ",")) == NULL)
		return NULL;

	naddr = NULL;
	do {
		p = rem_tr_space(p);
		if(!*p)
			continue;
		if((naddr1 = (struct _news_addr *) malloc (sizeof(struct _news_addr))) == NULL) {
			display_msg(MSG_FATAL, "get_news_addr", "malloc() failed");
			return NULL;
		}

		if(!strncmp(p, "#news.", 6))
			p += 6;
		naddr1->name = strdup(p);
		naddr1->descr = NULL;
		naddr1->next = NULL;

		if(naddr == NULL)
			naddr = naddr1;
		else {
			naddr2 = naddr;
			while(naddr2->next)
				naddr2 = naddr2->next;

			naddr2->next = naddr1;
		}
	} while((p = strtok(NULL, ",")) != NULL);

	return naddr;
}

struct _mail_addr *
get_address(const char *str, int flags) {
	mail_addr *ma , *ma1, *ma2;
	char addr[256];
	char name[256];
	char comment[256];
	char *cur, *p, *p2, c;
	const char *brkstr, *p1;
	int len, curlen;
#define ABUF_LEN    200

	if(!str)
		return NULL;

	ma1 = NULL;
	ma2 = NULL;
	addr[0] = name[0] = comment[0] = '\0';
	curlen = 0;

	cur = addr;
	brkstr = (flags & ADDR_IGNORE_COMMAS) ? "<(\'\"" : "<(,\'\"";


	while(curlen < ABUF_LEN) {
		if((p = strpbrk(str, brkstr)) != NULL) {
			c = *p;
			*p = '\0';
			len = ABUF_LEN - curlen;
			if(len < 0)
				len = 0;
			strncpy(cur, str, len);
			cur[len] = '\0';
			len = strlen(cur);
			cur = cur + len;
			curlen += len;
			*p = c;
			str = p;
		} else {
			len = ABUF_LEN - curlen;
			if(len < 0)
				len = 0;
			strncpy(cur, str, len);
			cur[len] = '\0';
			c = '\0';
		}

		switch(c) {
			case '(':
				str++;
				if((p = strchr(str, ')')) == NULL) {
					*cur = c;
					cur++;
					*cur = '\0';
					break;
				}

				for(p1 = str, p2 = p; p1 < p2; p1++) {
					if(*p1 == '(') {
						if((p = strchr(++p, ')')) == NULL)
							break;
					}
				}

				if(p == NULL) {
					*cur = c;
					cur++;
					*cur = '\0';
					break;
				}

				if(comment[0] == '\0') {
					c = *p;
					*p = '\0';
					snprintf(comment, sizeof(comment), "%s", str);
					*p = c;
				}

				str = p;
				str++;
				if(addr[0] != '\0') {
					cur = name + strlen(name);
					curlen = strlen(name);
				} else {
					cur = addr + strlen(addr);
					curlen = strlen(addr);
				}
				break;

			case '<':
				str++;
				if((p = strchr(str, '>')) == NULL) {
					*cur = c;
					cur++;
					*cur = '\0';
					break;
				}

				if(addr[0] != '\0') {
					if(name[0] == '\0')
						snprintf(name, sizeof(name), "%s", addr);
					addr[0] = '\0';
				}

				c = *p;
				*p = '\0';
				snprintf(addr, sizeof(addr), "%s", str);
				*p = c;

				p++;
				str = p;
				cur = name + strlen(name);
				curlen = strlen(name);
				break;

			case '\'':
			case '"':
				str++;
				*cur++ = c;
				curlen++;

				if(strchr(str, c) == NULL)
					break;

				p = const_cast<char *>(str);
				while(*p && (*p != c) && (curlen < ABUF_LEN)) {
					*cur++ = *p;
					curlen++;
					if(*p == '\\') {
						p++;
						*cur++ = *p;
						curlen++;
					}
					p++;
				}

				if(*p == c) {
					*cur++ = c;
					curlen++;
					*cur = '\0';
					str = ++p;
				} else
					str = p;
				break;

			case ',':
			case '\0':
				if(c == ',')
					str++;

				p = rem_tr_space(addr);
				/*
					 if (p[0] == '\0') {
						if (c == '\0')
							return ma1;
						else
							break;
								}
				*/

				ma = (struct _mail_addr *)malloc(sizeof(struct _mail_addr));
				ma->next_addr = NULL;
				ma->pgpid = NULL;
				ma->num = 0;

				ma->addr = strdup(p);

				p = rem_tr_spacequotes(name);

				if(p[0] != '\0')
					ma->name = strdup(p);
				else
					ma->name = NULL;

				p = rem_tr_space(comment);

				if(comment[0] != '\0')
					ma->comment = strdup(p);
				else
					ma->comment = NULL;


				if(ma1)
					ma2->next_addr = ma;
				else
					ma1 = ma;

				ma2 = ma;
				ma1->num++;

				if((c == '\0') || (flags & ADDR_GET_FIRST) ||
				   (ma1->num > MAX_ADDR_IN_FIELD))
					return ma1;

				addr[0] = name[0] = comment[0] = '\0';

				cur = addr;
				curlen = 0;

				break;

		}

	}

	return NULL;

}

int
graph_str(char *str) {
	char *p = str;
	int changed = 0;

	if(!str)
		return 0;

	while(*p) {
		if((*p < 32) && (*p != '\n') && (*p != 0x09)) {
			p[0] = '_';
			changed = 1;
		}
		p++;
	}

	return changed;
}

char *
rem_tr_space(char *str) {
	char *p;

	if(!str)
		return "";

	while((*str == ' ') ||
		  (*str == 0x09))
		str++;

	if(*str == '\0')
		return "";

	p = str + strlen(str) - 1;
	while((p != str) && ((*p == ' ') || (*p == 0x09))) {
		*p = '\0';
		p--;
	}

	if(*str == '\0')
		return "";

	return str;
}

char *
rem_tr_spacequotes(char *str) {
	char *p;

	if(!str)
		return "";

	while((*str == ' ') ||
		  (*str == 0x09) ||
		  (*str == '\'') ||
		  (*str == '"'))
		str++;

	if(*str == '\0')
		return "";

	p = str + strlen(str) - 1;
	while((p != str) &&
		  ((*p == ' ') || (*p == 0x09) || (*p == '\'') || (*p == '"'))) {
		*p = '\0';
		p--;
	}

	if(*str == '\0')
		return "";

	return str;
}

void
replace_field_noload(struct _mail_msg *msg, char *name, char *str) {
	struct _head_field *hf;

	if((hf = find_field_noload(msg, name)) == NULL) {
		add_field(msg, name, str);
		return;
	}

	free(hf->f_line);
	hf->f_line = strdup(str);

	if(hf->f_line == NULL) {
		display_msg(MSG_FATAL, "replace field", "strdup failed");
		return;
	}

	return;
}

void
replace_field(struct _mail_msg *msg, char *name, char *str) {
	struct _head_field *hf;

	if(!msg || !str || !name)
		return;

	if(!msg->header)
		return;

	if((hf = find_field(msg, name)) == NULL) {
		add_field(msg, name, str);
		return;
	}

	free(hf->f_line);
	hf->f_line = strdup(str);

	if(hf->f_line == NULL) {
		display_msg(MSG_FATAL, "malloc", "strdup failed");
		return;
	}

	return;
}

struct _head_field *
copy_field(struct _head_field *fld) {
	struct _head_field *hf;

	if(!fld)
		return NULL;

	hf = (struct _head_field *)malloc(sizeof(struct _head_field));
	hf->f_line = fld->f_line ? strdup(fld->f_line) : NULL;
	strcpy(hf->f_name, fld->f_name);
	hf->num_fields = 0;
	hf->next_head_field = NULL;

	return hf;
}

struct _head_field *
copy_field_chain(struct _head_field *fld) {
	struct _head_field *hf, *hf1;

	if(!fld)
		return NULL;

	hf = NULL;
	while(fld) {
		hf1 = copy_field(fld);
		hf1->next_head_field = hf;
		hf = hf1;

		fld = fld->next_head_field;
	}

	return hf;
}

void
add_field(struct _mail_msg *msg, char *name, char *str) {
	struct _head_field *hf;

	if(!msg || !str || !name)
		return;

	if(!msg->header)
		return;

	if((strlen(name) < 1) || (strlen(name) >= MAX_FIELD_NAME_LEN))
		return;

	hf = (struct _head_field *)malloc(sizeof(struct _head_field));
	hf->f_line = strdup(str);
	strcpy(hf->f_name, name);
	hf->num_fields = 0;
	hf->next_head_field = msg->header->other_fields;
	msg->header->other_fields = hf;

	if(msg->header->other_fields)
		msg->header->other_fields->num_fields++;

	return;

}

void
delete_field(struct _mail_msg *msg, struct _head_field *field) {
	struct _head_field *hf;

	if(!msg || !field)
		return;

	if(!msg->header)
		return;

	hf = msg->header->other_fields;
	if(hf == field)
		msg->header->other_fields = field->next_head_field;
	else {
		while(hf && (hf->next_head_field != field))
			hf = hf->next_head_field;

		if(!hf)
			return;

		hf->next_head_field = field->next_head_field;
	}

	if(field->f_line)
		free(field->f_line);

	free(field);

	return;

}

void
delete_all_fields(struct _mail_msg *msg, char *name) {
	struct _head_field *hf;

	while((hf = find_field_noload(msg, name)) != NULL)
		delete_field(msg, hf);

	return;
}

struct _head_field *
find_last_field(struct _mail_msg *msg, char *str) {
	struct _head_field *hf = msg->header->other_fields, *fld = NULL;

	while(hf) {
		if(!strcasecmp(hf->f_name, str))
			fld = hf;
		hf = hf->next_head_field;
	}

	return fld;
}

struct _head_field *
find_field_noload(struct _mail_msg *msg, char *str) {
	struct _head_field *hf = msg->header->other_fields;

	while(hf) {
		if(!strcasecmp(hf->f_name, str))
			return hf;
		hf = hf->next_head_field;
	}

	return NULL;
}

struct _head_field *
find_field(struct _mail_msg *msg,char *str) {
	struct _head_field *hf;
	int i;
	char *p;

	if(!msg || !msg->header) {
		return NULL;
	}

	if(!str || (strlen(str) < 1) || (strlen(str) > MAX_FIELD_NAME_LEN)) {
		return NULL;
	}

	if(msg->status & H_SHORT) {
		i = 0;
		while((p = shorthfields[i++]) != NULL)
			if(!strcasecmp(str, p))
				break;

		if(p == NULL)
			if(msg->get_header(msg) != 0)
				return NULL;
	}

	hf = msg->header->other_fields;
	while(hf) {
		if(!strcasecmp(hf->f_name, str))
			return hf;
		hf = hf->next_head_field;
	}

	return NULL;
}

void
free_field_content(struct _mail_msg *msg, char *p, int fr) {
	switch(fr) {
		case 1:
			/* msg->free_text(msg); */
			break;

		case 2:
			if(p)
				free(p);
			/* msg->free_text(msg); */
			break;
	}

	return;
}

char *
get_field_content(struct _mail_msg *msg, char *str, int *fr) {
	static char fld[MAX_FIELD_LEN];
	char *mbuf;
	struct _head_field *hf;
	int i;

	if(!fr || !msg || !str || !*str) {
		return NULL;
	}


	*fr = 0;

	if(!strncasecmp(str, "Flags", 5)) {
		i = 0;
		fld[0] = '\0';
		while(msgflags[i].mask != 0) {
			if((msg->flags & msgflags[i].mask) == msgflags[i].result) {
				if(*fld) strcat(fld, ",");
				strcat(fld, msgflags[i].name);
			}
			i++;
		}

		return fld;
	}

	if(!strncasecmp(str, "Message", 7) && (msg->get_header(msg) == 0) &&
	   (msg->get_text(msg, NULL) == 0)) {
		*fr = 1;
		return msg->msg_body;
	}

	if(!strncasecmp(str, "Body", 4) && (msg->get_header(msg) == 0) &&
	   (msg->get_text(msg, NULL) == 0)) {
		*fr = 1;
		return(msg->msg_body + msg->header->header_len);
	}

	if(!strncasecmp(str, "Header", 6)) {
		msg->get_header(msg);
		msg->get_text(msg, NULL);
		*fr = 1;

		if((mbuf = (char*)malloc(msg->header->header_len + 1)) == NULL) {
			display_msg(MSG_WARN, "malloc", "malloc failed");
			return NULL;
		}

		*fr = 2;

		memcpy(mbuf, msg->msg_body, msg->header->header_len);
		mbuf[msg->header->header_len] = '\0';
		return mbuf;
	}

	if(!strncasecmp(str, "Subject", 7)) {
		snprintf(fld, sizeof(fld), "%s", (msg->header->Subject != NULL) ?
				 msg->header->Subject : "nosubject");
		return fld;
	}

	//Search for an odd ball field in the header
	if(!(hf = find_field(msg, str))) {
		return NULL;
	}

	if(!hf->f_line)
		return NULL;

	snprintf(fld, sizeof(fld), "%s", hf->f_line);
	return fld;
}

struct _head_field *
get_field(const char *str) {
	head_field *nhf;
	char *p;
	int len;


	if(!str) {
		return NULL;
	}

	if((nhf = (struct _head_field *)malloc(sizeof(struct _head_field))) == NULL) {
		display_msg(MSG_FATAL, "field parse", "malloc failed");
		return NULL;
	}
	nhf->next_head_field = NULL;
	nhf->num_fields = 0;

	if((p = strchr(str, ':')) == NULL) {
		free(nhf);
		return NULL;
	}

	*p = '\0';
	len = p - str;

	p++;
	while((*p == ' ') || (*p == 0x09))
		p++;

	if((len >= MAX_FIELD_NAME_LEN) || (len < 1)) {
		free(nhf);
		return NULL;
	}

	snprintf(nhf->f_name, sizeof(nhf->f_name), "%s", str);

	len = strlen(p);

	if(len > MAX_FIELD_LEN)
		len = MAX_FIELD_LEN;

	while((len > 0) && ((p[len - 1] == ' ') ||
						(p[len - 1] == 0x09)))
		len--;

	p[len] = '\0';

	nhf->f_line = strdup(p);

	return nhf;
}

struct _head_field *
get_folded_field(FILE *ffd) {
	char buf[MAX_FIELD_LEN + 1], *p, *fline;
	struct _head_field *fld;
	off_t pos;

	pos = ftell(ffd);
	if(!fgets(buf, MAX_FIELD_LEN, ffd))
		return NULL;

	if((fld = get_field(buf)) == NULL) {
		fseek(ffd, pos, SEEK_SET);
		return NULL;
	}

	ffnsp:
	pos = ftell(ffd);
	if(!fgets(buf, MAX_FIELD_LEN, ffd))
		return fld;
	strip_newline(buf);

	if((buf[0] == ' ') || (buf[0] == 0x09)) {
		if(strlen(fld->f_line) >= MAX_SPLIT_FIELD_LEN)
			goto ffnsp;

		p = buf;
		while((p[1] == ' ') || (p[1] == 0x09))
			p++;

		*p = ' ';
		strip_newline(p);
		if(!(fline = (char*) realloc(fld->f_line, (strlen(p) + strlen(fld->f_line) + 1)))) {
			display_msg(MSG_FATAL, "realloc", "Can not allocate memory!");
			return fld;
		} else
			fld->f_line = fline;

		strcat(fld->f_line, p);
		goto ffnsp;
	} else
		fseek(ffd, pos, SEEK_SET);

	return fld;
}

struct _msg_header * get_msg_header(FILE *mfd, int flags, int *eoh) {
	struct _mail_addr *addr;
	struct _news_addr *naddr;
	char buf[MAX_FIELD_LEN + 1], buf1[127];
	head_field *cur_fld, *end_fld, *stat_fld = NULL;
	msg_header *mh;
	char *p, *fline;
	long pos;
	u_int fldnum;
	int i, wasxfmstat = 0;

	if(!(mh = (msg_header *)malloc(sizeof(msg_header)))) {
		display_msg(MSG_FATAL, "malloc", "Can not allocate memory!");
		exit(1);
	}

	fldnum = 0;
	mh->other_fields = NULL;
	mh->flags = flags;
	mh->From = mh->To = mh->Sender = mh->Cc = mh->Bcc = NULL;
	mh->News = NULL;
	mh->Fcc = NULL;
	end_fld = mh->other_fields;
	mh->snt_time = 0L;
	mh->rcv_time = 0L;
	mh->Subject = NULL;
	*eoh = 0;

	while(msg_gets(buf, MAX_FIELD_LEN, mfd)) {
		buf[MAX_FIELD_LEN - 1] = '\0';

		if((strlen(buf) > 0) && !strip_newline(buf)) {
			while(msg_gets(buf1, sizeof(buf1) - 1, mfd)) {
				if(strip_newline(buf1))
					break;
			}
		}

		if(strlen(buf) < 1) {
			*eoh = 1;
			break;
		}

		if(fldnum > MAX_FIELDS_IN_HDR)
			break;

		if(!strncasecmp(buf, "From ", 5))
			continue;

		if((cur_fld = get_field(buf))) {
			nsp:    pos = msg_tell(mfd);
			if(msg_gets(buf, MAX_FIELD_LEN, mfd)) {
				strip_newline(buf);
				if((buf[0] == ' ') || (buf[0] == 0x09)) {
					if(strlen(cur_fld->f_line) >= MAX_SPLIT_FIELD_LEN)
						goto nsp;

					p = buf;
					while((p[1] == ' ') || (p[1] == 0x09))
						p++;

					*p = ' ';
					strip_newline(p);
					if(!(fline = (char*) realloc(cur_fld->f_line, strlen(p) +
							 strlen(cur_fld->f_line) + 1))) {
						display_msg(MSG_FATAL, "realloc", "Can not allocate memory!");
						exit(1);
					} else
						cur_fld->f_line = fline;

					strcat(cur_fld->f_line, p);
					goto nsp;
				} else
					msg_seek(mfd, pos, SEEK_SET);
			} else
				msg_seek(mfd, pos, SEEK_SET);

			fldnum++;

			i = 0;
			if(flags & M_HSHORT) {
				while((p = shorthfields[i++]) != NULL)
					if(!strcasecmp(cur_fld->f_name, p))
						break;

				if(p == NULL)
					goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, STATUS_FIELD, 9)) {
				sscanf(cur_fld->f_line, "%4x", &mh->flags);
				wasxfmstat = 1;
				goto dsc_fld;
			}

			/* we really should save a rfc 1522 decoded subject, otherwise
		   many strcmp are messed up! SHG */
			if(!mh->Subject && !strncasecmp(cur_fld->f_name, "Subject", 7) &&
			   *cur_fld->f_line) {
				int ftype;
				ftype=-1;
				mh->Subject = strdup(rfc1522_decode(cur_fld->f_line,&ftype));
				goto dsc_fld;
			}

			if(!mh->From && !strncasecmp(cur_fld->f_name, "From", 4)) {
				mh->From = get_address(cur_fld->f_line, ADDR_IGNORE_COMMAS);

                if(mh->From != NULL) {
				    discard_address(mh->From->next_addr);
				    mh->From->next_addr = NULL;
                }
				goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, "To", 2)) {
				if(mh->To == NULL)
					mh->To = get_address(cur_fld->f_line, (flags & M_HSHORT) ? ADDR_GET_FIRST : 0);
				else {
					addr = mh->To;
					while(addr->next_addr)
						addr = addr->next_addr;
					addr->next_addr = get_address(cur_fld->f_line, (flags & M_HSHORT) ? ADDR_GET_FIRST : 0);
				}
				goto dsc_fld;
			}

			if(!mh->Sender && !strncasecmp(cur_fld->f_name, "Sender", 6)) {
				mh->Sender = get_address(cur_fld->f_line, 0);
				goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, "Cc", 2)) {
				if(!mh->Cc)
					mh->Cc = get_address(cur_fld->f_line, 0);
				else {
					addr = mh->Cc;
					while(addr->next_addr)
						addr = addr->next_addr;
					addr->next_addr = get_address(cur_fld->f_line, 0);
				}
				goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, "Bcc", 3)) {
				if(!mh->Bcc)
					mh->Bcc = get_address(cur_fld->f_line, 0);
				else {
					addr = mh->Bcc;
					while(addr->next_addr)
						addr = addr->next_addr;
					addr->next_addr = get_address(cur_fld->f_line, 0);
				}
				goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, "Newsgroups", 10)) {
				if(!mh->News)
					mh->News = get_news_addr(cur_fld->f_line);
				else {
					naddr = mh->News;
					while(naddr->next)
						naddr = naddr->next;
					naddr->next = get_news_addr(cur_fld->f_line);
				}
				goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, "Fcc", 3)) {
				parse_fcc_list(mh, cur_fld->f_line);
				goto dsc_fld;
			}

			if(!strncasecmp(cur_fld->f_name, "Date", 4)) {
				if(mh->snt_time == 0)
					mh->snt_time = get_date(cur_fld->f_line);
			} else
				if(!strncmp(cur_fld->f_name, "X-RDate", 7))
				mh->rcv_time = get_date(cur_fld->f_line);
			else
				if(!strncmp(cur_fld->f_name, "X-SDate", 7))
				mh->snt_time = get_date(cur_fld->f_line);
			else
				if(!strncmp(cur_fld->f_name, "Status", 6))
				stat_fld = cur_fld;

			if(mh->other_fields == NULL)
				mh->other_fields = cur_fld;
			else
				end_fld->next_head_field = cur_fld;

			end_fld = cur_fld;
			continue;

			dsc_fld:   
		free(cur_fld->f_line);
			free(cur_fld);

		}

	}

	if(!wasxfmstat && stat_fld) {
		p = stat_fld->f_line;
		while(*p != '\0') {
			switch(*p) {
				case 'R':
					mh->flags &= ~UNREAD;
					break;

				case 'O':
					if(p == stat_fld->f_line)
						mh->flags |= UNREAD;
					break;

				case 'U':
					mh->flags |= UNREAD;
					break;
			}
			p++;
		}
	}

	if(!wasxfmstat && !stat_fld)
		mh->flags |= UNREAD;


	if(!mh->rcv_time && mh->snt_time)
		mh->rcv_time = mh->snt_time;
	else if(mh->rcv_time && !mh->snt_time)
	   	mh->snt_time = mh->rcv_time;

	mh->header_len = msg_tell(mfd);

	return mh;
}

class compare_mail_folders {
public:
int operator()(const struct _mail_folder *fld1, const struct _mail_folder *fld2) {
	int iCompareResults = 0, iFolderType;

/* Fix the predicate, return true only if fld1 < fld2 */
#define FIXPRED(a) ((a) < 0 ? 1 : 0)

	if(!fld1 || !fld2 || (fld1 == fld2))
		return 0;

	switch(find_ancestors(&fld1, &fld2)) {
		case -1:
			return FIXPRED(0);
			break;

		case 1:
			return FIXPRED(-1);
			break;

		case 2:
			return FIXPRED(1);
			break;
	}

	iFolderType = (fld2->status & SYSTEM) - (fld1->status & SYSTEM);
	if (iFolderType != 0)
		return FIXPRED(iFolderType);

	iFolderType = fld1->type - fld2->type;
	if((folder_sort & 0x0f) == NO_SORT || iFolderType != 0)
		return FIXPRED(iFolderType);

	switch(folder_sort & 0x0f) {
		case BY_NAME:
			iCompareResults = strcasecmp(fld1->sname , fld2->sname);
			break;

		case BY_PRIOR:
			iCompareResults = fld1->flags - fld2->flags;
			break;

		case BY_MSGNUM:
			iCompareResults = fld1->num_msg - fld2->num_msg;
			break;

		case BY_UNREAD:
			iCompareResults = fld1->unread_num - fld2->unread_num;
			break;
	}

	if(iCompareResults == 0) {
		if((fld1->type == F_IMAP) &&
		   (fld1->spec != fld2->spec)) {
			struct _imap_src *imap1, *imap2;

			imap1 = (struct _imap_src *)fld1->spec;
			imap2 = (struct _imap_src *)fld2->spec;
			iCompareResults = strcmp(imap1->name, imap2->name);
		} else if((folder_sort & 0x0f) == BY_NAME) {
			    iCompareResults = (fld1 > fld2) ? 1 : -1;
        } else {
			iCompareResults = strcmp(fld1->sname , fld2->sname);
			if(iCompareResults == 0) {
				iCompareResults = (fld1 > fld2) ? 1 : -1;
            }
		}
	}

	return(folder_sort & MSG_ASCEND) ? FIXPRED(iCompareResults) : FIXPRED((iCompareResults * -1));
#undef FIXPRED
}
};

void sort_folders() {

	sort(mailbox.begin(), mailbox.end(), compare_mail_folders());

	folder_sort |= FLD_SORTED;
	return;
}

/* Moved this to keep gcc happy SHG */
char *
remove_lead_trail_blanks(char *name) {
	int len;
	int iReLength;
    string sRePrefix;

	sRePrefix = Config.get("reprefix", "Re:");
    iReLength = sRePrefix.size();

	if(!strncasecmp(name, sRePrefix.c_str(), iReLength))
		name += iReLength;
	else if (!strncasecmp(name, "Re:", 3))
	name += 3;

	while(*name == ' ')
		name++;

	len = strlen(name);
	while(*(name+len-1) == ' ')
		*(name+(--len)) = '\0';

	return name;
}

int
compare_msgs(struct _mail_msg **msg1, struct _mail_msg **msg2) {
	int cmpres = 0, srt_type;
	struct _mail_msg *m1 = *msg1, *m2 = *msg2;

	if(!*msg1 || !*msg2 || (*msg1 == *msg2))
		return 0;

	if(!(*msg1)->header || !(*msg2)->header)
		return 0;

	if(m1->folder && (m1->folder->sort != -1))
		srt_type = m1->folder->sort;
	else
		srt_type = sort_type;

	if(srt_type & MSG_THREAD) {
		struct _mail_msg **msgs1=NULL, **msgs2=NULL;

		if(m1->refs) {
			msgs1 = (struct _mail_msg **)malloc(m1->refs * sizeof(struct _mail_msg *));
			while(m1->ref) {
				msgs1[m1->refs - 1] = m1;
				m1 = m1->ref;
				/* msg1 > msg2 because of a reference */
				if(m1 == *msg2) {
					free(msgs1);
					return 1;
				}
			}
		}

		if(m2->refs) {
			msgs2 = (struct _mail_msg **)malloc(m2->refs * sizeof(struct _mail_msg *));
			while(m2->ref) {
				msgs2[m2->refs - 1] = m2;
				m2 = m2->ref;
				/* here msg1 < msg2 */
				if(m2 == *msg1) {
					free(msgs2);
					if (msgs1)
						free(msgs1);
					return -1;
				}
			}
		}

		if(m1 == m2) { /* the thread starters are identical */
			int i = 0;

			/* => find first referenced message, where they differ */
			while(msgs1[i] == msgs2[i])
				i++;
			m1 = msgs1[i];
			m2 = msgs2[i];
		}

		if(msgs1)
			free(msgs1);
		if(msgs2)
			free(msgs2);
	}

	switch(srt_type & 0x0f) {
		case NO_SORT:
			return 0;
			break;

		case BY_SNT_TIME:
			cmpres = m1->header->snt_time - m2->header->snt_time;
			break;

		case BY_RCV_TIME:
			cmpres = m1->header->rcv_time - m2->header->rcv_time;
			break;

		case BY_SUBJECT:
			if(!m1->header->Subject) {
				cmpres = -1;
				break;
			} else
				if(!m2->header->Subject) {
				cmpres = 1;
				break;
			}

			/* maybe we should ignore RE: when comparing SHG */
		{
			char *s1, *s2, *subj1, *subj2;

			subj1 = strdup(m1->header->Subject);
			subj2 = strdup(m2->header->Subject);

			s1 = remove_lead_trail_blanks(subj1);
			s2 = remove_lead_trail_blanks(subj2);

			cmpres = strcmp(s1,s2);

			free(subj1);
			free(subj2);
		}

			break;

		case BY_AUTHOR:
			if(!m1->header->From) {
				cmpres = -1;
				break;
			} else
				if(!m2->header->From) {
				cmpres = 1;
				break;
			}

			cmpres = strcmp(get_short_addr_line(m1->header->From), get_short_addr_line(m2->header->From) );
			break;

		case BY_RECIPIENT:
			if(!m1->header->To) {
				cmpres = -1;
				break;
			} else
				if(!m2->header->To) {
				cmpres = 1;
				break;
			}

			cmpres = strcmp(get_short_addr_line(m1->header->To), get_short_addr_line(m2->header->To) );
			break;

		case BY_FLAGS:
			cmpres = get_msg_priority(m1) - get_msg_priority(m2);
			break;

		case BY_SIZE:
			cmpres = m1->msg_len - m2->msg_len;
			break;

		case BY_UID:
			cmpres = m1->uid - m2->uid;
			break;

		case BY_MSG_UID:
			cmpres = m1->real_uid - m2->real_uid;
			break;

		default:
			return 0;
			break;
	}

	return(srt_type & MSG_ASCEND) ? cmpres : (cmpres * -1);
}

unsigned long
my_hash(const char *name) {
	unsigned long key = 0;
	unsigned char c;

	while((c = *name++) && (c != '>')) {
		key += c;
		key ^= (key << 24) | (key >> 8 );
	}

	return key;
}

void
make_entry(ht_t *ht, unsigned long key, int htsize,
		   char *s, struct _mail_msg *msg) {
	unsigned long nkey = key;

	while(ht[nkey].id) { /* solve key collision */
		nkey++;
		if(nkey >= htsize)
			nkey = 0;
	}

	if(nkey != key) {
		while(ht[key].next < htsize)
			key = ht[key].next;
		ht[key].next = nkey;
		key = nkey;
	}

	ht[key].id = s;
	ht[key].msg = msg;

	return;
}

struct _mail_msg *
find_entry(struct _ht *ht, unsigned long key,
		   int htsize, char *s) {
	int cnt = strchr(s,'>') - s + 1;

	while((key < htsize) && ht[key].id)
		if(strncmp(ht[key].id, s, cnt)) /* we have a key collision */
			key = ht[key].next;
		else                          /* found the key */
			break;

	return key < htsize ? ht[key].msg : NULL;
}

void
sort_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg, **marr;
	int total, i, srt_type;

	if(!folder)
		return;

	if(!folder->messages) {
		folder->status |= SORTED;
		return;
	}

	if(folder->sort != -1)
		srt_type = folder->sort;
	else
		srt_type = sort_type;

	if((srt_type & 0x0f) == NO_SORT)
		return;

	for(total = 0,msg = folder->messages; msg; msg = msg->next, total++) ;

	if((marr = (struct _mail_msg **)malloc(sizeof(struct _mail_msg *) * total)) == NULL) {
		display_msg(MSG_WARN, "sort", "Failed to allocate memory");
		return;
	}

	for(total = 0,msg = folder->messages; msg; msg = msg->next, total++) {
		marr[total] = msg;
		msg->status &= ~S_THREAD;
	}

	if((srt_type & MSG_THREAD) && (total > 1)) {
		/* should be enough for the hash table */
		int htsize = total*2;
		struct _ht *ht;

		if((ht = (struct _ht *)malloc(htsize * sizeof(struct _ht))) == NULL) {
			display_msg(MSG_FATAL, "sort", "Malloc failed");
			return;
		}

		for(total = 0; total < htsize; total++) {
			ht[total].id = NULL;
			ht[total].msg = NULL;
			ht[total].next = htsize;
		}

		for(total = 0,msg = folder->messages; msg; msg = msg->next, total++) {
			struct _head_field *m_id = find_field(msg,"Message-ID");
			char *s;

			if(m_id &&
			   ((s = strchr(m_id->f_line,'<')) != NULL)) {
				unsigned long key = my_hash(s) % htsize;
				make_entry(ht, key, htsize, s, msg);
			}
			msg->ref = NULL;
			msg->refs = 0;

		}

		for(total = 0,msg = folder->messages; msg; msg = msg->next, total++) {
			struct _head_field *m_id;
			struct _mail_msg *ref;
			unsigned long key;
			char *s;

			ref=NULL;
			m_id = find_field(msg,"In-Reply-To");
			if(m_id && (s=strrchr(m_id->f_line,'<'))) {
				key = my_hash(s) % htsize;
				ref = find_entry(ht, key, htsize, s);
			}
			if(!ref) {
				m_id=find_field(msg,"References");
				if(m_id && (s=strrchr(m_id->f_line,'<'))) {
					key = my_hash(s) % htsize;
					ref = find_entry(ht, key, htsize, s);
				}
			}

			if(ref == msg)
				ref = NULL;

			/* we have a "References" or a "In-Reply-To" field */
			if(ref) {
				/* maybe we shouldn't choke when the subject changes
		   inside a thread. Trust the in-reply-to/references. SHG */
		   
/*
				char *s1, *s2, *subj1, *subj2;

		if(msg->header->Subject)
					subj1 = strdup(msg->header->Subject);
				else
					subj1 = strdup("");

				if(ref->header->Subject)
					subj2 = strdup(ref->header->Subject);
				else
					subj2 = strdup("");

				s1 = remove_lead_trail_blanks(subj1);
				s2 = remove_lead_trail_blanks(subj2);

				if(!strcmp(s1,s2)) {
*/
					msg->ref=ref;
					msg->status |= S_THREAD;
/*
				}

				free(subj1);
				free(subj2);
*/
			}
		}

		free(ht);
		for(total = 0,msg = folder->messages; msg; msg = msg->next, total++) {
			struct _mail_msg *m = msg;
			int i = 0;

			while(m->ref) {
				m = m->ref;
				i++;
			}
			msg->refs=i;
		}

	}

	qsort(marr, total, sizeof(struct _mail_msg *),
		  (int(*)(const void*, const void*))compare_msgs);

	folder->messages = marr[0];

	total--;

	for(i = 0; i < total; i++)
		marr[i]->next = marr[i + 1];

	marr[total]->next = NULL;

	free(marr);

	folder->status |= SORTED;
	return;
}

struct _mail_msg *
alloc_message() {
	struct _mail_msg *message;

	message = (struct _mail_msg *)malloc(sizeof(struct _mail_msg));
	if(!message) {
		display_msg(MSG_FATAL, "malloc", "Can not allocate memory");
		return NULL;
	}

	message->msg_len = 0;
	message->folder = NULL;
	message->num = -1;
	message->data = MSG_DAT_EMPTY;
	message->pdata = NULL;
	message->ref = NULL;
	message->refs = 0;
	message->flags = 0;
	message->status = 0;
	message->type = 0;
	message->uid = -1;
	message->real_uid = -1;
	message->msg_body = NULL;
	message->msg_body_len = 0;
	message->next = NULL;
	message->mime = NULL;
	message->header = NULL;

	return message;
}

struct _mail_msg *
create_message(struct _mail_folder *folder) {
	long new_num;
	int i;
#ifdef WITH_FACES
	char *face;
#endif
	struct _mail_msg *message;
	struct _mail_addr *addr;
	msg_header *mh;
	struct _head_field *hf;
	FILE *mfd;
	char buf[255];

	if(!folder)
		folder = ftemp;

	if((new_num = get_new_name(folder)) == -1) {
		display_msg(MSG_WARN, "Can not choose file name for a new message in", "%s", folder->name(folder));
		return NULL;
	}

	if((message = alloc_message()) == NULL) {
		display_msg(MSG_FATAL, "message alloc", "Can not allocate memory");
		return NULL;
	}

	local_message(message);
	folder->status |= FRESCAN;

	if(!(mh = (msg_header *)malloc(sizeof(msg_header)))) {
		display_msg(MSG_FATAL, "malloc", "Can not allocate memory!");
		return NULL;
	}

	mh->other_fields = NULL;
	mh->To = mh->Sender = mh->Cc = mh->Bcc = NULL;
	mh->News = NULL;
	mh->Fcc = NULL;

	//See if current folder has a specific From:
	if(current_folder && current_folder->From) {
		mh->From = copy_address(current_folder->From);
	} else {
		mh->From = get_address(Config.get("from", "").c_str(), ADDR_GET_FIRST);
	}
	mh->From->next_addr = NULL;

	//See if the current folder has a default To:
	if (current_folder != NULL && current_folder->To != NULL)
		mh->To = copy_address(current_folder->To);

	if(Config.getInt("smtpbcc", 0))
			mh->Bcc = copy_address(mh->From);

	mh->rcv_time = mh->snt_time = 0L;
	mh->Subject = NULL;
	mh->flags = 0;

	message->header = mh;
	message->status |= MSGNEW;
	message->msg_body = NULL;
	message->msg_body_len = 0;
	message->mime = NULL;
	message->next = folder->messages;
	message->num = new_num;
	message->uid = new_num;
	message->real_uid = new_num;
	message->folder = folder;
	folder->num_msg++;
	folder->messages = message;

	if(Config.exist("organization"))
			add_field(message, "Organization",  const_cast<char *>(Config.get("organization", "").c_str()));
	if(Config.exist("replyto"))
			add_field(message, "Reply-To",  const_cast<char *>(Config.get("replyto", "").c_str()));
#ifdef WITH_FACES
	if((face = xbm_to_face()) != NULL)
		add_field(message, FACEFIELD, face);
#endif

	message->header->snt_time = message->header->rcv_time =  time(NULL);
	add_field(message, "Date", get_arpa_date(time(NULL)));

	for(i = 1; i <= MAX_EXTRA_HEADERS; i++) {
		sprintf(buf, "ExtraHeader%d", i);
		if(Config.exist(buf)) {
			strcpy(buf,  Config.get(buf, "").c_str());
			if(((hf = get_field(buf)) == NULL) || (hf->f_line == NULL)) {
				Config.remove(buf);
				if(hf)                    
					free(hf);
				continue;
			}

			if(!strncasecmp(hf->f_name, "Subject", 7)) {
				if((hf->f_name[7] == '\0') ||
				   (isdigit(hf->f_name[7]) && (hf->f_name[8] == '\0'))) {
					if(hf->f_name[7] == '\0')
						message->header->Subject = strdup(hf->f_line);
					free(hf->f_line);
					free(hf);
					continue;
				}
			}

			if(is_spechdr(hf)) {
				display_msg(MSG_WARN, "extra header", "\"%s\" can not be used", hf->f_name);
				free(hf->f_line);
				free(hf);
				Config.remove(buf);
				continue;
			}

			if(!strcasecmp(hf->f_name, "Cc"))
				message->header->Cc = get_address(hf->f_line, 0);
			else
				if(!strcasecmp(hf->f_name, "Bcc")) {
				addr = message->header->Bcc;
				while(addr && addr->next_addr)
					addr = addr->next_addr;
				if(addr)
					addr->next_addr = get_address(hf->f_line, 0);
				else
					message->header->Bcc = get_address(hf->f_line, 0);
			} else
				if(!strcasecmp(hf->f_name, "Sender"))
				message->header->Sender = get_address(hf->f_line, 0);
			else {
				snprintf(buf, sizeof(buf), "%s", hf->f_line);
				expand_str(message, buf);
				if(strchr(buf, '\n') == NULL)
					add_field(message, hf->f_name, buf);
			}
			free(hf->f_line);
			free(hf);
		}
	}

	if(Config.getInt("smtpsave", 1))
			add_fcc_list(message->header, sentm);
	add_field(message, MIME_C_ENCR, supp_encodings[def_encoding].encoding_name);

	snprintf(buf, sizeof(buf), "%s/%s; charset=%s", mailcap[DEFAULT_MAILCAP].type_text, mailcap[DEFAULT_MAILCAP].subtype_text, supp_charsets[def_charset].charset_name);
	add_field(message, MIME_C_TYPE, buf);

	sprintf(buf, "%02d", MIME_VERS_SUPP);
	buf[2] = buf[1];
	buf[1] = '.';
	buf[3] = '\0';
	add_field(message, MIME_VERSION, buf);

	if((mfd = fopen(message->get_file(message), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", message->get_file(message));
		unlink(message->get_file(message));
		discard_message(message);
		return NULL;
	}

#ifndef __EMX__
	if(fchmod(fileno(mfd), 00600) == -1)
		display_msg(MSG_WARN, "Can not change mode of", "%s", message->get_file(message));
#endif

	print_message_header(message, mfd);

	fflush(mfd);
	if((message->header->header_len = ftell(mfd)) == -1) {
		display_msg(MSG_WARN, "Can not determine file length", "%s", message->get_file(message));
		unlink(message->get_file(message));
		discard_message(message);
		return NULL;
	}

	add_signature(message, mfd, 0);
	fflush(mfd);

	if((message->msg_len = ftell(mfd)) == -1) {
		display_msg(MSG_WARN, "Can not determine file length", "%s", message->get_file(message));
		unlink(message->get_file(message));
		discard_message(message);
		return NULL;
	}

	fclose(mfd);

	touch_message(message);
	cache_msg(message);

	return message;
}

struct _mail_msg *get_message(long num, struct _mail_folder *folder) {
	int msg, status;
	char str[255];
	struct _mail_msg *message;
	struct stat sb;
	FILE *fmsg = NULL;
	int eoh;

	if(!folder)
		folder = ftemp;

	status = (folder->status & FSHORTH) ? M_HSHORT : 0;
	snprintf(str, sizeof(str), "%s/%ld", folder->fold_path, num);

	if((msg = open(str, O_RDONLY)) < 0) {
		display_msg(MSG_WARN, "get_message", "Can not open %s", str);
		return NULL;
	}

#ifndef MMAP_MSG
	if((fmsg = fdopen(msg, "r")) == NULL) {
		display_msg(MSG_WARN, "get_message", "fdopen failed");
		close(msg);
		return NULL;
	}
#endif

	if(fstat(msg, &sb)) {
		display_msg(MSG_WARN, "get_message", "Can not stat %s", str);
		close(msg);
#ifndef MMAP_MSG
		fclose(fmsg);
#endif
		return NULL;
	}

	if(!(sb.st_mode & S_IFREG)) {
		display_msg(MSG_WARN, "get_message", "Not a file: %s", str);
		close(msg);
#ifndef MMAP_MSG
		fclose(fmsg);
#endif
		return NULL;
	}

	if(sb.st_size == 0) {
		close(msg);
#ifndef MMAP_MSG
		fclose(fmsg);
#endif
		return NULL;
	}

	if((message = alloc_message()) == NULL) {
		display_msg(MSG_FATAL, "alloc message", "Can not allocate memory");
		close(msg);
#ifndef MMAP_MSG
		fclose(fmsg);
#endif
		return NULL;
	}

	message->status = (folder->status & FSHORTH) ? H_SHORT : 0;
	local_message(message);

#ifdef MMAP_MSG
	mmlen = ((size_t)sb.st_size < MAP_HEADER_LEN) ? (size_t)sb.st_size : MAP_HEADER_LEN;
	if((mmsg = (char *)mmap(NULL, mmlen , PROT_READ, MAP_PRIVATE, msg, (off_t)0)) == (char *)MAP_FAILED) {
		display_msg(MSG_WARN, "mmap failed on", "%s", str);
		close(msg);
		free(message);
		return NULL;
	}

	mmpos = 0;
	mmofft = 0;
	mmmax = (size_t)sb.st_size;
	mmapfd = msg;
#endif

	message->msg_len = sb.st_size;
	message->folder = folder;
	message->num = num;
	message->uid = num;
	message->real_uid = num;
	message->header = get_msg_header(fmsg, status, &eoh);
	message->flags |= message->header->flags;
	message->flags &= 0xffff;
	message->header->flags &= 0xffff;


#ifdef MMAP_MSG
	munmap(mmsg, mmlen);
	mmsg = NULL;
	mmlen = 0;
	mmpos = 0;
	mmmax = 0;
	mmofft = 0;
	mmapfd = -1;
#else
	fclose(fmsg);
#endif

	close(msg);

	if((sb.st_mtime >= sb.st_atime) && !(message->flags & UNREAD))
		touch_message(message);
	else
		if((sb.st_mtime < sb.st_atime) && (message->flags & UNREAD))
		touch_message(message);

	return message;
}

struct _mail_msg *
get_fwd_msg(struct _mail_msg *msg, char *fwdtext) {
	struct _mail_msg *fwd_msg;
	struct _head_field *fld, *hf;
	struct _mime_msg *mtext;
	FILE *mfd, *tfd;
	int i, fwdmime;
    string sFwdPrefix;
	char *p;
	char rbuf[256], fbuf[PATH_MAX], subjbuf[MAX_FIELD_LEN];
	
	if(!msg)
		return NULL;

	if(!msg->header)
		return NULL;

	fwd_msg = create_message(outbox);
	if(!fwd_msg)
		return NULL;

	if(!fwd_msg->header)
		return NULL;

	if(msg->get_file(msg) == NULL) {
		fwd_msg->mdelete(fwd_msg);
		return NULL;
	}

	sFwdPrefix = Config.get("fwdprefix", "Fwd:");

	// create_message() will copy the default from address into
	// the message.
	//
	// The default to address of the folder is not used even
	// if it exists. The reasoning being that if you are forwarding
	// a message from a specific folder (usually a folder storing all
	// mail from a particular email list) than you are probably
	// intending to forward it to someone other than the folder's
	// address. Therefore, discard the To address if it exists.
	//
	if (fwd_msg->header->To != NULL) {
		discard_address(fwd_msg->header->To);
		fwd_msg->header->To = NULL;
	}

	msg->get_header(msg);
	add_field(fwd_msg, FWD_ORGMSG, get_msg_url(msg));

	if(msg->header->Subject &&
	   (!strncasecmp(msg->header->Subject , sFwdPrefix.c_str(), sFwdPrefix.size()) ||
		!strncasecmp(msg->header->Subject , "Fwd:", 4) ||
		!strncasecmp(msg->header->Subject , "FW:", 3)))
		fwd_msg->header->Subject = strdup(msg->header->Subject);
	else {
		snprintf(subjbuf, sizeof(subjbuf), "%s %s", sFwdPrefix.c_str(), msg->header->Subject ? msg->header->Subject : "");
		fwd_msg->header->Subject =  strdup(subjbuf);
	}

	if(msg->header->To)
		add_field(fwd_msg, "Resent-To", get_full_addr_line(msg->header->To));

	if(msg->header->From)
		add_field(fwd_msg, "Resent-From", get_full_addr_line(msg->header->From));

	if((fld = find_field(msg, "Message-Id")))
		add_field(fwd_msg, "Resent-Message-Id", fld->f_line);

	if((fld = find_field(msg, "Date")))
		add_field(fwd_msg, "Resent-Date", fld->f_line);

	/* Preserving Reply-To seems to be confusing */
	/*
	 if ((fld = find_field(msg, "Reply-To")))
	   replace_field(fwd_msg, "Reply-To", fld->f_line);
	 else {
	   if (msg->header->From)
		replace_field(fwd_msg, "Reply-To", msg->header->From->addr);
		}
	*/

	fwdmime =  Config.getInt("fwdinclude", 2);
	if(fwdtext && (fwdmime == 4))
			fwdmime = 2;

	if(fwdmime == 4) {
		if(attach_file(fwd_msg, msg->get_file(msg), &mailcap[MESSAGE_MAILCAP], DEFAULT_ENCODING , ATT_NODISPINFO|ATT_NOFILEINFO) == NULL) {
			display_msg(MSG_WARN, "forward", "Failed to attach message");
			fwd_msg->mdelete(fwd_msg);
			return NULL;
		}
		return fwd_msg;
	}

	fwd_msg->flags &= ~SIGNED;

	if((mfd = fopen(fwd_msg->get_file(fwd_msg), "w")) == NULL) {
		display_msg(MSG_WARN, "forward", "Can not open file %s", fwd_msg->get_file(fwd_msg));
		return fwd_msg;
	}

	print_message_header(fwd_msg, mfd);
	fflush(mfd);
	fwd_msg->header->header_len = ftell(mfd);
	if(fwdtext) {
		if(fwrite(fwdtext, strlen(fwdtext), 1, mfd) < 1) {
			display_msg(MSG_WARN, "forward", "Can not write selected message text");
			return fwd_msg;
		}
	} else {
		if(msg->print_body(msg, mfd) != 0) {
			display_msg(MSG_WARN, "forward", "Can not write message text");
			return fwd_msg;
		}
	}

	if(fflush(mfd) == EOF) {
		display_msg(MSG_WARN, "forward", "Failed to write message");
		return fwd_msg;
	}

	fwd_msg->msg_len = ftell(mfd);
	fclose(mfd);

	if((hf = find_field(msg, MIME_C_TYPE)) != NULL)
		replace_field(fwd_msg, MIME_C_TYPE, hf->f_line);

	if((hf = find_field(msg, MIME_VERSION)) != NULL)
		replace_field(fwd_msg, MIME_VERSION, hf->f_line);

	if((hf = find_field(msg, MIME_C_ENCR)) != NULL)
		replace_field(fwd_msg, MIME_C_ENCR, hf->f_line);

	if((hf = find_field(msg, MIME_C_DESCR)) != NULL)
		replace_field(fwd_msg, MIME_C_DESCR, hf->f_line);

	if((hf = find_field(msg, MIME_C_ID)) != NULL)
		replace_field(fwd_msg, MIME_C_ID, hf->f_line);

	if((hf = find_field(msg, MIME_C_LENGTH)) != NULL)
		replace_field(fwd_msg, MIME_C_LENGTH, hf->f_line);

	if((mtext = get_text_part(fwd_msg)) == NULL)
		return fwd_msg;

	if((mfd = fopen(fwd_msg->get_file(fwd_msg), "r")) == NULL) {
		display_msg(MSG_WARN, "forward", "Can not open %s", fwd_msg->get_file(fwd_msg));
		return fwd_msg;
	}

	fseek(mfd, mtext->m_start, SEEK_SET);
	if(mtext->boundary) {
		while(fgets(rbuf, sizeof(rbuf), mfd)) {
			if(strlen(rbuf) <= 1)
				break;
		}
	}

	strcpy(fbuf, get_temp_file("fwd"));

	mtext->src_info = strdup(fbuf);

	if((tfd = fopen(fbuf, "w")) == NULL) {
		display_msg(MSG_WARN, "forward", "Can not open %s", fbuf);
		fclose(mfd);
		return NULL;
	}

	fputc('\n', tfd);
	strcpy(rbuf,  Config.get("fwdstr", "-----Fwd: %i-----%n%n").c_str());
	expand_str(msg, rbuf);
	fputs(rbuf, tfd);

	switch(fwdmime) {
		default:
			break;

		case 2:
			if((hf = find_field(msg, "Date")) == NULL)
				hf = find_field(msg, "X-SDate");
			fprintf(tfd, "Date: %s\n", (hf && hf->f_line) ? hf->f_line : get_arpa_date(msg->header->snt_time));

			print_addr(msg->header->Sender, "Sender", tfd, -2);
			print_addr(msg->header->From, "From", tfd, -2);
			print_addr(msg->header->To, "To", tfd, -2);
			if(msg->header->News)
				print_news_addr(msg->header->News, "Newsgroups", tfd);
			if(msg->header->Subject)
				fprintf(tfd, "Subject: %s\n", msg->header->Subject);

			print_addr(msg->header->Cc, "Cc", tfd, -2);
			print_addr(msg->header->Bcc, "Bcc", tfd, -2);

			fprintf(tfd, "\n");
			break;

		case 3:
			print_message_header(msg, tfd);
			break;
	}

	while((ftell(mfd) < mtext->m_end) && fgets(rbuf, sizeof(rbuf), mfd)) {
		if((p = (*mtext->encoding->ce_dec)(rbuf, &i)) == NULL)
			continue;

		fputs(p, tfd);
	}

	fputs("\n--------------End of forwarded message-------------------------\n", tfd);
	add_signature(fwd_msg, tfd, 0);
	fclose(tfd);
	fclose(mfd);

	update_mime(fwd_msg);

	return fwd_msg;
}

struct _mail_msg *
get_vac_msg(struct _mail_msg *msg, char *vfile) {
	struct _mail_msg *vac_msg;
	struct _mail_addr *addr;
	struct _head_field *mid;
	FILE *vfd, *mfd;
	char buf[255], *vsubj, *p;
    string sRePrefix, sVacationSubject;

	if(!msg)
		return NULL;

	msg->get_header(msg);
	if(!msg->header)
		return NULL;

	if((vac_msg = create_message(outbox)) == NULL)
		return NULL;

	if((vfd = fopen(vfile, "r")) == NULL) {
		display_msg(MSG_WARN, "vacation", "Can not open %s", vfile);
		vac_msg->status |= (DELETED|DELPERM);
		vac_msg->mdelete(vac_msg);
		return NULL;
	}

	sRePrefix = Config.get("reprefix", "Re:");
	sVacationSubject = Config.get("vacsubject", "I'm on vacation");

	if(fgets(buf, 255, vfd) && !strncmp(buf, "Subject: ", 9)) {
		vsubj = buf + 9;
		while(*vsubj == ' ')
			vsubj++;
		expand_str(msg, vsubj);
		if((p = strchr(vsubj, '\n')) != NULL)
			*p = '\0';
	} else {
		fseek(vfd, 0, SEEK_SET);
		if(msg->header->Subject &&
	   (!strncasecmp(msg->header->Subject, sRePrefix.c_str(), sRePrefix.size()) ||
		!strncasecmp(msg->header->Subject, "Re:", 3)))
			snprintf(buf, sizeof(buf), "%s (%s)", sVacationSubject.c_str(), msg->header->Subject ? msg->header->Subject : "");
		else
			snprintf(buf, sizeof(buf), "%s (%s %s)", sVacationSubject.c_str(), sRePrefix.c_str(), msg->header->Subject ? msg->header->Subject : "");
		vsubj = buf;
	}

	vac_msg->header->Subject = strdup(vsubj);

	if((mid = find_field(msg, "Reply-To")) &&
	   (addr = get_address(mid->f_line, 0)))
		vac_msg->header->To = addr;
	else
		vac_msg->header->To = copy_address(msg->header->From);

	discard_address(vac_msg->header->Bcc);
	vac_msg->header->Bcc = NULL;

	if((mfd = fopen(vac_msg->get_file(vac_msg), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", vac_msg->get_file(vac_msg));
		return vac_msg;
	}

	print_message_header(vac_msg, mfd);
	fflush(mfd);
	vac_msg->header->header_len = ftell(mfd);

	while(fgets(buf, 255, vfd))
		fputs(buf, mfd);

	fflush(mfd);
	vac_msg->msg_len = ftell(mfd);

	fclose(mfd);
	fclose(vfd);

	return vac_msg;
}

void add_signature(struct _mail_msg *msg, FILE *file, int forceattach) {
	FILE *sf;
	char *s_file, buf[255], buf1[255];
	char date[16];
	time_t tm;
	int sl = 0;
	int i, n, sattach;
	struct _proc_info pinfo;
    string sFortune;

	if(file == NULL)
		return;

	if(!forceattach) {
		sattach =  Config.getInt("sattach", 2);
		if((msg->flags & SIGNED) || (sattach != 2)) 
	 			return;
	}

	if((s_file = get_sign_file(msg)) == NULL)
		return;

	if((sf = fopen(s_file, "r")) == NULL) {
		display_msg(MSG_WARN, "Can not open signature file for reading", "%s", s_file);
		return;
	}

	tm = time(NULL);

	fseek(sf, 0L, SEEK_SET);

	fputc('\n', file);
	if(Config.getInt("signprefix", 0))
			fputs("-- \n", file);
#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "C");
#endif
	while(fgets(buf, 255, sf)) {
		if(!strchr(buf, '$'))
			fputs(buf, file);
		else {
			for(i = 0; i < strlen(buf);i++) {
				if((buf[i] == '$') && (buf[i+1] != '\0')) {
					switch(buf[i+1]) {
						case 't':
							strftime(date, 9, "%H:%M:%S", localtime(&tm));
							fputs(date, file);
							break;

						case 'd':
							strftime(date, 16, "%d-%b-%Y", localtime(&tm));
							fputs(date, file);
							break;

						case 'm':
							fputs(get_full_addr_line(msg->header->From), file);
							break;

						case 'f':
							sFortune =  Config.get("fortune", "/usr/games/fortune -s");
							init_pinfo(&pinfo);
							pinfo.fd_out[0] = 0;
							if(exec_child(const_cast<char *>(sFortune.c_str()),&pinfo) == -1) {
								if(pinfo.fd_out[0] > 0)
									close(pinfo.fd_out[0]);
								break;
							}
							while((n = read(pinfo.fd_out[0],buf1,254)) > 0) {
								buf1[n] = '\0';
								fputs(buf1, file);
							}

							close(pinfo.fd_out[0]);
							break;

						case '$':
							fputc('$', file);
							break;

						default:
							fputc(buf[i], file);
							fputc(buf[i+1], file);
							break;
					}
					i++;
				} else
					fputc(buf[i], file);
			}
		}

		sl++;

		if(sl > 24)
			break;
	}

#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "");
#endif

	fclose(sf);
	msg->flags |= SIGNED;
	return;
}

void
add_signature_to_msg(struct _mail_msg *msg, char *tfile) {
	FILE *mfd;

	if(!tfile)
		return;

	if((mfd = fopen(tfile, "a+")) == NULL)
		return;

	add_signature(msg, mfd, 1);
	fclose(mfd);

	return;
}

char *
get_reply_text(struct _mail_msg *msg) {
	struct _mime_msg *mtext;
	char m_tmp_file[255];
	int pgp;

	pgp = 0;

	if(msg->get_file(msg) == NULL)
		return NULL;

	if((mtext = get_any_text_part(msg)) == NULL)
		return NULL;

	if(mtext->flags & PGP_PART)
		pgp = 1;

	strcpy(m_tmp_file, get_temp_file("reply"));
	if(save_part(msg, mtext, m_tmp_file, 0) != 0) {
		display_msg(MSG_WARN, "reply", "Can not extract original text");
		return NULL;
	}

	if(!pgp && is_pgp(m_tmp_file) == 1)
		pgp = 1;

	if(pgp)
		pgp_decode_file(m_tmp_file);

	return strdup(m_tmp_file);
}

void format_reply_text(struct _mail_msg *msg, FILE * ifd, FILE * ofd, int include) {
	int was_cr, nlen, wraplen, continous_line = 0;
	char prefix[17], buf[255], *p, *p1, *p2, c, *pai;

	if(include == 1)
		return;

	if((include != 2) && (include != 3))
		return;

	fputc('\n', ofd);

	if(include == 3)
		fputs("-------------Original message follows----------------------\n", ofd);

	strncpy(prefix,  Config.get("prefix", ">").c_str(), 16);

	if(include == 2) {
		strcpy(buf,  Config.get("replystr", "On %d %f wrote:%n").c_str());
	    expand_str(msg, buf);
	    fputs(buf, ofd);
	}

	wraplen = abs( Config.getInt("editwrap", 80));
	was_cr = 1;
	nlen = 0;

	while(fgets(buf, 255, ifd)) {
		if(*buf == '\0')  /* if the buffer ends then jump out of this loop */
			continue;

		p = buf;
		nextrln:
		if((include == 2) && was_cr) {  /* if we are on a new line and in the
						   correct mode */
			fputs(prefix, ofd);      /* then put down the > */
			nlen = strlen(prefix);   /* and work out the insertion lenth */
			/* the following indent characters should possibly be user configurable */
			if(NEAT_SPACED_INDENTS &&
			   (strchr(INDENT_CHARS, *p) == NULL)) {
				/* we are now going to add a neatening space and
				   lengthen the line accordingly */
				fputs(" ", ofd);
				nlen++;
			}
		}

		/* this bit finds the end of any indenting - else a space in the
		   indenting cause a wrap and look really nasty */
		pai=p;  /* p after indent */
		while(strchr(INDENT_CHARS, *pai) != NULL) {
			pai++;
			while((*pai == ' ') || (*pai == '\t'))
				pai++;
		}

		/* check if we will wrap on when we add the buf */
		if(((nlen + strlen(p)) > wraplen) && REFORMAT_ENABLE
		   && ((strrchr(pai, ' ') || REFORMAT_CONTINOUS_LINES))) {
			p2 = p + (wraplen - nlen);  /* pointer to the line limit */
			c = *p2;     /* temporary storage for the character we blotted */
			*p2 = '\0';  /* temporarily terminate the buf to find the wrap point */

			/* find the last space in the line */
			if((p1 = strrchr(pai, ' ')) != NULL) {
				*p2 = c;       /* restore the string back to normal */
				*p1++ = '\0';  /* put in the wrap */
				fputs(p, ofd);
				/*  dump it all to the output file */
				continous_line=0;
			} else {  /* we have a string with no spaces */
				*p2 = c;  /* restore the string back to normal */
				fwrite(p, (wraplen - nlen), 1, ofd);  /* fill the line */
				p1 = p + (wraplen - nlen);  /* get the position of the wrap */
				continous_line=1;
			}

			fputc('\n', ofd);  /* put in the newline */
			nlen = 0;          /* clear the lenth of the line so far */
			was_cr = 1;        /* flag the newline */
			p2 = p;        /* save the pointer for prefix calculation */
			p = p1;            /* set our pointer to the beginning of the section
					   that hasn't been writtern yet */
			while((*p == ' ') || (*p == '\t'))  /* clear out any white space */
				p++;
			/* check if we got to the end of the string */
			if((*p == '\0') || (*p == '\n') || (*p == '\r'))
				continue;  /* if so jump out */

			/* XXX: if the indent scanning surpassed the wrap point then
			 * we will endlessly loop trying to wrap the line. Just continue
			 * if this is the case.
			 */
			if (pai > p)
				continue;

			while(pai != p2)         /* make the wrapped part start with the */
				*(--p) = *(--pai); /* same prefix as the main line */
			goto nextrln;/* if we didn't get to the end then go back round again */
		}  /* close break test */
		else {  /* we don't wrap */
			fputs(p, ofd);  /* dump the buffer in the file */
			/* set the set the lenth to the total for the line writtern */
			nlen += strlen(p);
		}

		if(p[strlen(p) - 1] == '\n') { /* check if a newline was written */
			was_cr = 1;         /* flag it if it was */
			nlen = 0;
		} /* set the buffer length back to zero */
		else
			was_cr = 0;             /* flag the lack of a newline */
	}       /* close while */

	if(include == 3)
		fputs("-----------------------------------------------------------\n", ofd);

	return;
}

void
insert_orig(char *tfile, struct _mail_msg *msg, int type, u_long offt) {
	FILE *fd, *mfd, *tfd;
	char buf[255], tmp[255];
	char *rtext, *p, c;
	u_long tofft;

	if(!tfile || !msg)
		return;

	snprintf(tmp, sizeof(tmp), "%s_ins", tfile);

	if((fd = fopen(tmp, "w")) == NULL) {
		display_msg(MSG_WARN, "Can not open message file", "%s", tmp);
		return;
	}

	if((tfd = fopen(tfile, "r")) == NULL) {
		display_msg(MSG_WARN, "Can not open file", "%s", tfile);
		fclose(fd);
		return;
	}

	if((rtext = get_reply_text(msg)) == NULL) {
		fclose(fd);
		fclose(tfd);
		unlink(tmp);
		return;
	}

	tofft = 0;
	while(fgets(buf, (offt - tofft + 1) > 255 ? 255 : (offt - tofft + 1), tfd)) {
		tofft = ftell(tfd);
		if(tofft >= offt) {
			p = buf + (strlen(buf) - (tofft - offt));
			c = *p;
			*p = '\0';
			fputs(buf, fd);
			if((c != '\n') && (c != '\0'))
				fputc('\n', fd);
			break;
		}
		fputs(buf, fd);
	}


	if((mfd = fopen(rtext, "r")) == NULL) {
		display_msg(MSG_WARN, "reply", "Can not open %s", rtext);
		fclose(fd);
		fclose(tfd);
		unlink(rtext);
		free(rtext);
		unlink(tmp);
		return;
	}

	format_reply_text(msg, mfd, fd, type ? 3 : 2);

	while(fgets(buf, 255, tfd))
		fputs(buf, fd);

	fclose(fd);
	fclose(mfd);
	fclose(tfd);
	unlink(rtext);
	free(rtext);

#ifdef __EMX__ /* Under OS/2 the file will not be deleted during rename() */
	if(access(tfile, 0)==0) {
		if(unlink(tfile)!=0) {
			display_msg(MSG_WARN, "unlink", "delete %s before moving", tfile);
		}
	}
#endif
	if(rename(tmp, tfile) == -1) {
		display_msg(MSG_WARN, "Can not rename", "%s to %s", tmp, tfile);
		unlink(tmp);
	}

	return;
}

void
add_file_to_msg(char *tfile, char *file, u_long offt, int separators) {
	FILE *fd, *mfd, *tfd;
	char buf[255], tmp[255], *p, *ln;
	unsigned char c;
	struct stat sb;
	int swarn, eos, len;
	u_long tofft;

#define INS_WARN_NOTEXT 0x01
#define INS_WARN_BIGSTR 0x02

	if(!tfile || !file)
		return;

	snprintf(tmp, sizeof(tmp), "%s_ins", tfile);
	swarn = 0;

	if(stat(file, &sb) == -1) {
		display_msg(MSG_WARN, "Can not access file", "%s", file);
		return;
	}

	if((sb.st_size > MAX_INSFILE_SIZE) &&
	   !display_msg(MSG_QUEST|MSG_DEFNO, "File is too big , use MIME to attach big files", "Are you still wanting to insert it?"))
		return;

	if((fd = fopen(file, "r")) == NULL) {
		display_msg(MSG_WARN, "Can not open file", "%s", file);
		return;
	}

	if((mfd = fopen(tmp, "w")) == NULL) {
		display_msg(MSG_WARN, "Can not open message file", "%s", tmp);
		fclose(fd);
		return;
	}

	if((tfd = fopen(tfile, "r")) == NULL) {
		display_msg(MSG_WARN, "Can not open file", "%s", tfile);
		fclose(fd);
		fclose(mfd);
		return;
	}

	tofft = 0;
	while(fgets(buf, (offt - tofft + 1) > 255 ? 255 : (offt - tofft + 1), tfd)) {
		tofft = ftell(tfd);
		if(tofft >= offt) {
			buf[strlen(buf) - (tofft - offt)] = '\0';
			fputs(buf, mfd);
			fputc('\n', mfd);
			break;
		}
		fputs(buf, mfd);
	}

	if(separators) {
		p = strrchr(file, '/');
		sprintf(buf, "-------------- begin: %s --------------\n", p ? p + 1 : file);
		fputs(buf, mfd);
	}

	eos = 0;
	while(fgets(buf, MAX_INSFILE_LINE, fd)) {
		for(p = buf; *p; p++) {
			c = *p;
			if((c < 32) && (c != '\n') &&
			   (c != '\r') && (c != '\t')) {
				if(!(swarn & INS_WARN_NOTEXT)) {
					display_msg(MSG_WARN, "insert file", "This is not a text file\nand it could be inserted incorrectly");
					swarn |= INS_WARN_NOTEXT;
				}
				*p = '_';
			}
		}

		ln = buf;
		npart:
		if((p = strchr(ln, '\n')) == NULL)
			len = strlen(ln);
		else {
			if((p != ln) && (p[-1] == '\r'))
				p--;
			len = p - ln;
		}

		if((len + eos) > MAX_INSFILE_LINE) {
			if(!(swarn & INS_WARN_BIGSTR)) {
				display_msg(MSG_WARN, "insert file", "Some lines in this file are too long\nand will be splitted");
				swarn |= INS_WARN_BIGSTR;
			}
			len = MAX_INSFILE_LINE - eos;
			fwrite(ln, len, 1, mfd);
			fputc('\n', mfd);
			ln += len;
			eos = 0;
			goto npart;
		} else {
			if((p = strrchr(ln , '\n')) != NULL)
				eos += (strlen(ln) - (p - ln) - 1);
			else
				eos += len;
			fputs(ln, mfd);
		}

	}
	fclose(fd);

	if(separators) {
		p = strrchr(file, '/');
		sprintf(buf, "--------------- end: %s ---------------\n", p ? p + 1 : file);
		fputs(buf, mfd);
	}

	fseek(tfd, offt, SEEK_SET);
	while(fgets(buf, 255, tfd))
		fputs(buf, mfd);

	fclose(tfd);
	fclose(mfd);

#ifdef __EMX__ /* Under OS/2 the file will not be deleted during rename() */
	if(access(tfile, 0)==0) {
		if(unlink(tfile)!=0) {
			display_msg(MSG_WARN, "unlink", "delete %s before moving", tfile);
		}
	}
#endif
	if(rename(tmp, tfile) == -1) {
		display_msg(MSG_WARN, "Can not rename", "%s to %s", tmp, tfile);
		unlink(tmp);
	}

	return;
}

struct _head_field *
need_read_confirm(struct _mail_msg *msg) {
	struct _head_field *hf;

	hf = find_field(msg, XFRECEIPT);
	if(!hf)
		hf = find_field(msg, "X-Chameleon-Return-To");
	if(!hf)
		hf = find_field(msg, "X-Confirm-Reading-To");
	if(!hf)
		hf = find_field(msg, "Disposition-Notification-To");

	if(!hf || !hf->f_line)
		return NULL;

	return hf;
}

void
delete_read_confirm(struct _mail_msg *msg) {
	delete_all_fields(msg, XFRECEIPT);
	delete_all_fields(msg, "X-Chameleon-Return-To");
	delete_all_fields(msg, "X-Confirm-Reading-To");
	delete_all_fields(msg, "Disposition-Notification-To");
	msg->status |= CHANGED;
	return;
}

void
read_confirm(struct _mail_msg *msg) {
	struct _mail_msg *rep_msg;
	struct _head_field *hf;
	FILE *mfd;
	char buf[255];

	if((hf = need_read_confirm(msg)) == NULL)
		return;

	rep_msg = create_message(outbox);
	if(!rep_msg)
		return;

	rep_msg->header->To = get_address(hf->f_line, ADDR_IGNORE_COMMAS);
	if(!rep_msg->header->To)
		return;
	discard_address(rep_msg->header->Bcc);
	rep_msg->header->Bcc = NULL;

	snprintf(buf, sizeof(buf), "Receipt: %s", msg->header->Subject ? msg->header->Subject : "");
	rep_msg->header->Subject =  strdup(buf);

	if(unlink(rep_msg->get_file(rep_msg)) == -1) {
		display_msg(MSG_WARN, "Cannot delete old copy of message", "%s", rep_msg->get_file(rep_msg));
		return;
	}

	if((mfd = fopen(rep_msg->get_file(rep_msg), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", rep_msg->get_file(rep_msg));
		return;
	}

	print_message_header(rep_msg, mfd);

	fflush(mfd);
	rep_msg->header->header_len = ftell(mfd);

	fputs("Message opening confirmation:\n", mfd);
	if(msg->header->To) {
		fprintf(mfd, "\nThe message you sent to: %s\n", get_full_addr_line(msg->header->To));
		fputs("has been received and opened.\n", mfd);
	}

	fputc('\n', mfd);
	fputs("----------Original message header follows----------------\n", mfd);
	print_message_header(msg, mfd);
	fputs("---------------------------------------------------------\n", mfd);
	fputc('\n', mfd);

	fflush(mfd);
	rep_msg->msg_len = ftell(mfd);
	fclose(mfd);

	//if(send_message(rep_msg) == -1)
	//display_msg(MSG_WARN, "Failed to send confirmation message", "%-.64s", msg->header->Subject ? msg->header->Subject : "* No Subject *");
	send_message(rep_msg);

	return;
}

int
incl_in_reply(struct _mail_msg *msg, struct _mail_addr *addr) {
	int replyex;
	char replyexand[255], *p;
	struct _mail_addr *addr1;

	if(!msg || !addr)
		return 0;

	replyex =  Config.getInt("replyex", 1);
	if(replyex && msg->header->From && !strcasecmp(msg->header->From->addr, addr->addr))
		return 0;

	if(replyex && msg->folder && msg->folder->From &&
	   !strcasecmp(msg->folder->From->addr, addr->addr))
		return 0;

	if(replyex && !strcmp(user_n, addr->addr))
		return 0;

	snprintf(replyexand, sizeof(replyexand), "%s", Config.get("replyexand", "").c_str());
	if(strlen(replyexand)) {
		p = strtok(replyexand, ";:, ");
		if(p && !strcasecmp(p, addr->addr))
			return 0;

		while(p && ((p = strtok(NULL, ";:, ")) != NULL)) {
			if(!strcasecmp(p, addr->addr))
				return 0;
		}
	}

	addr1 = msg->header->To;
	while(addr1) {
		if(!strcasecmp(addr1->addr, addr->addr))
			return 0;
		addr1 = addr1->next_addr;
	}

	addr1 = msg->header->Cc;
	while(addr1) {
		if(!strcasecmp(addr1->addr, addr->addr))
			return 0;
		addr1 = addr1->next_addr;
	}

	addr1 = msg->header->Bcc;
	while(addr1) {
		if(!strcasecmp(addr1->addr, addr->addr))
			return 0;
		addr1 = addr1->next_addr;
	}

	return 1;
}

struct _mail_msg *
get_reply_msg(struct _mail_msg *msg, char *rtextbuf, int to_all) {
	struct _mail_msg *rep_msg;
	struct _mime_msg *mtext, *mtext1;
	struct _mail_addr *addr, *addr1;
	struct _head_field *mid, *hf;
	struct _mail_folder *fld;
	FILE *mfd, *tfd;
	int include, inclmime, inclrecp;
	char *rtext;
    string sRePrefix;
	char headbuf[MAX_FIELD_LEN], fbuf[PATH_MAX];

	if(!msg)
		return NULL;

	if(!msg->header)
		return NULL;

	include =  Config.getInt("include", 2);
	inclmime =  Config.getInt("inclmime", 3);
	inclrecp =  Config.getInt("inclrecp", 3);
	rep_msg = create_message(outbox);
	mfd = tfd = NULL;
	if(!rep_msg)
		return NULL;

	if(!rep_msg->header)
		return NULL;

	if((msg->data == MSG_DAT_ENCAP) && msg->pdata) {
		fld = ((struct _mail_msg *)msg->pdata)->folder;
		if(fld && fld->From) {
			discard_address(rep_msg->header->From);
			rep_msg->header->From = copy_address(fld->From);
			rep_msg->header->From->next_addr = NULL;
		}
	} else
		if(msg->folder && msg->folder->From) {
		discard_address(rep_msg->header->From);
		rep_msg->header->From = copy_address(msg->folder->From);
		rep_msg->header->From->next_addr = NULL;
	}

	rep_msg->data = MSG_DAT_ORIG;
	rep_msg->pdata = (void *)msg;
	rep_msg->flags &= ~SIGNED;
	add_field(rep_msg, REPLY_ORGMSG, get_msg_url(msg));
	if(msg->folder && (Config.getInt("replykeep", 0) || (msg->folder->flags & FRKEEP))) {
		if(rep_msg->header->Fcc)
			free(rep_msg->header->Fcc);
		rep_msg->header->Fcc = NULL;
		add_fcc_list(rep_msg->header, msg->folder);
	}

	if(include > 1) {
		if(msg->get_file(msg) == NULL)
			return NULL;
	}
	msg->get_header(msg);

	msg->status |= LOCKED;

	sRePrefix = Config.get("reprefix", "Re:");

	if(msg->header->Subject &&
	   (!strncasecmp(msg->header->Subject, sRePrefix.c_str(), sRePrefix.size()) ||
	!strncasecmp(msg->header->Subject, "Re:", 3)))
		rep_msg->header->Subject =  strdup(msg->header->Subject);
	else {
		snprintf(headbuf, sizeof(headbuf), "%s %s",sRePrefix.c_str(), msg->header->Subject ? msg->header->Subject : "");
		rep_msg->header->Subject = strdup(headbuf);
	}

	if((mid = find_field(msg, "Message-ID")))
		add_field(rep_msg, "In-Reply-To", mid->f_line);

	addr = NULL;

	if(msg->header->News &&
	   display_msg(MSG_QUEST, "Reply", "Post followup?")) {
		rep_msg->header->News = copy_news_address_chain(msg->header->News);
		goto incl_repl;
	}

	if(msg->header->From &&
	   Config.getInt("replysave", 0)) {
		add_each_addr(msg->header->From, "default");
		save_addressbook("default");
	}

	if((mid = find_field(msg, "Reply-To")) &&
	   (addr = get_address(mid->f_line, 0)) &&
	   strcmp(msg->header->From ? msg->header->From->addr : "", addr->addr) &&
	   display_msg(MSG_QUEST, "Do you want to reply to Reply-To address", "(%s) instead of From?", mid->f_line)) {
		rep_msg->header->To = addr;
		addr = NULL;
		/*   if (msg->header->From)
			rep_msg->header->Cc = copy_address(msg->header->From); */
	} else
		if(msg->header->From)
		rep_msg->header->To = copy_address(msg->header->From);
	else
		if(msg->header->Sender)
		rep_msg->header->To = copy_address(msg->header->Sender);
	else
		rep_msg->header->To = NULL;

	discard_address(addr);

	switch(to_all) {
		case 0:
			goto incl_repl;
			break;

		case 2:
			goto incl_all;
			break;
	}

	if(inclrecp == 1)
		goto incl_repl;

	if(inclrecp == 3) {
		if(msg->header->To) {
			if(incl_in_reply(rep_msg, msg->header->To))
				inclrecp = 2;
			else
				if(msg->header->To->next_addr)
				inclrecp = 2;
		}

		if(msg->header->Cc)
			inclrecp = 2;

		if(msg->header->Bcc)
			inclrecp = 2;

		if(msg->header->News)
			inclrecp = 2;

		if(inclrecp != 2)
			goto incl_repl;

		if(!display_msg(MSG_QUEST|MSG_DEFNO, "Reply", "Include all recipients?"))
			goto incl_repl;
	}

	incl_all:
	addr = msg->header->To;
	while(addr) {
		if(!incl_in_reply(rep_msg, addr)) {
			addr = addr->next_addr;
			continue;
		}

		addr1 = copy_address(addr);
		if(!addr1) {
			addr = addr->next_addr;
			continue;
		}

		if(rep_msg->header->Cc)
			addr1->num = rep_msg->header->Cc->num + 1;

		addr1->next_addr = rep_msg->header->Cc;
		rep_msg->header->Cc = addr1;
		addr = addr->next_addr;
	}

	addr = msg->header->Cc;
	while(addr) {
		if(!incl_in_reply(rep_msg, addr)) {
			addr = addr->next_addr;
			continue;
		}

		addr1 = copy_address(addr);
		if(!addr1) {
			addr = addr->next_addr;
			continue;
		}

		if(rep_msg->header->Cc)
			addr1->num = rep_msg->header->Cc->num + 1;

		addr1->next_addr = rep_msg->header->Cc;
		rep_msg->header->Cc = addr1;
		addr = addr->next_addr;
	}

	rep_msg->header->News = copy_news_address_chain(msg->header->News);

	incl_repl:
	if((mfd = fopen(rep_msg->get_file(rep_msg), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", rep_msg->get_file(rep_msg));
		return rep_msg;
	}

	print_message_header(rep_msg, mfd);
	fflush(mfd);
	rep_msg->header->header_len = ftell(mfd);

	if(inclmime == 3) {
		if(is_mime_msg(msg))
			if(display_msg(MSG_QUEST|MSG_DEFNO, "Reply", "Include MIME attachments in reply?"))
				inclmime = 2;
	}

	if(inclmime == 2) {
		if(msg->print_body(msg, mfd) != 0) {
			display_msg(MSG_WARN, "update", "Can not process file %s", rep_msg->get_file(rep_msg));
			return rep_msg;
		}
		if(fflush(mfd) == EOF) {
			display_msg(MSG_WARN, "update", "Can not process file %s", rep_msg->get_file(rep_msg));
			return rep_msg;
		}
	} else {
		rep_msg->msg_len = ftell(mfd);
		fclose(mfd);

		rep_msg->mime = create_mime();
		rep_msg->mime->flags = TEXT_PART;

		goto hreply;
	}

	rep_msg->msg_len = ftell(mfd);

	fclose(mfd);

	if((hf = find_field(msg, MIME_C_TYPE)) != NULL)
		replace_field(rep_msg, MIME_C_TYPE, hf->f_line);

	if((hf = find_field(msg, MIME_VERSION)) != NULL)
		replace_field(rep_msg, MIME_VERSION, hf->f_line);

	if((hf = find_field(msg, MIME_C_DESCR)) != NULL)
		replace_field(rep_msg, MIME_C_DESCR, hf->f_line);

	if((hf = find_field(msg, MIME_C_ENCR)) != NULL)
		replace_field(rep_msg, MIME_C_ENCR, hf->f_line);

	hreply:
	rtext = NULL;
	mfd = NULL;
	if((mtext = get_any_text_part(rep_msg)) == NULL) {
		mtext = create_mime();
		mtext->flags = TEXT_PART;
		mtext->mime_next = rep_msg->mime;
		rep_msg->mime = mtext;
	}

	if(Config.getInt("replykeepchset", 0)) {
		if((mtext1 = get_any_text_part(msg)) != NULL) {
			mtext->encoding = mtext1->encoding;
			mtext->charset = mtext1->charset;
		}
	} else {
		mtext->encoding = &supp_encodings[def_encoding];
		mtext->charset = &supp_charsets[def_charset];
	}

	if(rtextbuf) {
		strcpy(fbuf, get_temp_file("rtext"));
		rtext = strdup(fbuf);
		strip_newline(rtextbuf);

		if((mfd = fopen(rtext, "a+")) == NULL) {
			display_msg(MSG_WARN, "reply", "Can not open %s", rtext);
			free(rtext);
			return rep_msg;
		}

		if(fputs(rtextbuf, mfd) == EOF) {
			display_msg(MSG_WARN, "reply", "Can not write into %s", rtext);
			unlink(rtext);
			free(rtext);
			return rep_msg;
		}

		fputc('\n', mfd);

		fflush(mfd);
		fseek(mfd, 0, SEEK_SET);
		include = 2;
	} else
		if((include == 2) || (include == 3)) {
		if((rtext = get_reply_text(msg)) == NULL)
			return rep_msg;

		if((mfd = fopen(rtext, "r")) == NULL) {
			display_msg(MSG_WARN, "reply", "Can not open %s", rtext);
			unlink(rtext);
			free(rtext);
			return rep_msg;
		}
	}

	strcpy(fbuf, get_temp_file("rep"));
	mtext->src_info = strdup(fbuf);
	mtext->mailcap = &mailcap[DEFAULT_MAILCAP];
	mtext->flags = TEXT_PART|FILE_TEMP;

	if((tfd = fopen(fbuf, "w")) == NULL) {
		display_msg(MSG_WARN, "reply", "Can not open %s", fbuf);
		if(mfd)
			fclose(mfd);
		if(rtext) {
			unlink(rtext);
			free(rtext);
		}
		return NULL;
	}

	format_reply_text(msg, mfd, tfd, include);
	add_signature(rep_msg, tfd, 0);
	fclose(tfd);

	if(mfd)
		fclose(mfd);

	update_mime(rep_msg);

	if(rtext) {
		unlink(rtext);
		free(rtext);
	}

	return rep_msg;
}

long
get_msg_index(struct _mail_folder *folder, struct _mail_msg *msg) {
	long i = 0;
	struct _mail_msg *msg1 = folder ? folder->messages : NULL;

	while(msg1) {
		if(msg == msg1)
			return i;
		msg1 = msg1->next;
		i++;
	}

	return -1;
}

long
get_max_uid(struct _mail_folder *folder) {
	struct _mail_msg *msg = folder ? folder->messages : NULL;
	long uid;

	uid = 1;
	while(msg) {
		if(msg->uid > uid)
			uid = msg->uid;
		msg = msg->next;
	}

	return uid;
}

struct _mail_msg *
get_larger_uid(struct _mail_folder *folder, long muid) {
	struct _mail_msg *umsg, *msg;
	long uid;

	if(!folder)
		return NULL;

	uid = 0;
	umsg = NULL;

	msg = folder->messages;
	while(msg) {
		if((msg->uid > muid) &&
		   ((msg->uid < uid) || (uid == 0))) {
			uid = msg->uid;
			umsg = msg;
		}
		msg = msg->next;
	}

	return umsg;
}

struct _mail_msg *
get_smaller_uid(struct _mail_folder *folder, long muid) {
	long uid;
	struct _mail_msg *umsg, *msg;

	if(!folder)
		return NULL;

	uid = 0;
	umsg = NULL;

	msg = folder->messages;
	while(msg) {
		if((msg->uid < muid) &&
		   (msg->uid > uid)) {
			uid = msg->uid;
			umsg = msg;
		}
		msg = msg->next;
	}

	return umsg;
}

struct _mail_msg *
get_msg_by_uid(struct _mail_folder *folder, long uid) {
	struct _mail_msg *msg = folder ? folder->messages : NULL;

	while(msg) {
		if(msg->uid == uid)
			return msg;
		msg = msg->next;
	}

	return NULL;
}

struct _mail_msg *
get_mh_msg_by_uid(struct _mail_folder *folder, long uid) {
	struct _mail_msg *msg;

	msg = folder->messages;
	while(msg) {
		if(msg->uid == uid)
			return msg;
		msg = msg->next;
	}

	if((msg = get_message(uid, folder)) == NULL)
		return NULL;

	msg->next = folder->messages;
	folder->messages = msg;
	folder->status &= ~SORTED;

	return msg;
}

struct _mail_msg *
get_msg_by_index(struct _mail_folder *folder, long index) {
	long i = 0;
	struct _mail_msg *msg1 = folder ? folder->messages : NULL;

	while(msg1) {
		if(i == index)
			return msg1;
		msg1 = msg1->next;
		i++;
	}

	return NULL;
}

int
get_folder_index(struct _mail_folder *folder) {
	int i, ind;

	if(!folder || !mailbox.size())
		return 0;

	for(i = 0, ind = 0; i< (int)mailbox.size();i++) {
		if(mailbox[i]->status & FSKIP)
			continue;
		if(mailbox[i] == folder)
			return ind;
		ind++;
	}

	return 0;
}

int
get_folder_index_noskip(struct _mail_folder *folder) {
	int i;

	if(!folder || !mailbox.size())
		return 0;

	for(i = 0; i < (int)mailbox.size();i++) {
		if(mailbox[i] == folder)
			return i;
	}

	return 0;
}

struct _mail_folder *
get_folder_by_index(int index) {
	int i, ind;

	for(i = 0, ind = 0; i < (int)mailbox.size();i++) {
		if(mailbox[i]->status & FSKIP)
			continue;
		if(ind == index)
			return mailbox[i];
		ind++;
	}

	return mailbox[0];
}

struct _mail_folder *
get_folder_by_index_noskip(int index) {
	int i, ind;

	for(i = 0, ind = 0; i < (int)mailbox.size();i++) {
		if(ind == index)
			return mailbox[i];
		ind++;
	}

	return mailbox[0];
}

char *
get_folder_unique_name(struct _mail_folder *folder) {
	static char fname[255];
	char *prefix, buf[64];

	if(folder->type & F_IMAP) {
		snprintf(buf, sizeof(buf), "#[%s]/",((struct _imap_src *)folder->spec)->name);
		prefix = buf;
	} else
		if(folder->type & F_NEWS)
		prefix = "#news/";
	else
		if(folder->type & F_MBOX)
		prefix = "#mbox/";
	else
		prefix = "";

	snprintf(fname, sizeof(fname), "%-.*s%s%s", folder->level, "        ", prefix, folder->sname);

	return fname;
}

char *
get_folder_full_name(struct _mail_folder *folder) {
	static char fname[255];

	if(!folder)
		return "noname";

	if(folder->type & F_IMAP)
		snprintf(fname, sizeof(fname), "#[%s]/%s",
				 ((struct _imap_src *)folder->spec)->name, folder->name(folder));
	else
		if(folder->type & F_NEWS)
		snprintf(fname, sizeof(fname), "#news/%s", folder->name(folder));
	else
		if(folder->type & F_MBOX)
		snprintf(fname, sizeof(fname), "#mbox/%s", folder->fold_path);
	else
		return folder->name(folder);

	return fname;
}

char *
get_folder_name(struct _mail_folder *folder) {
	struct _mail_folder *pfold = folder->pfold;
	int len;
	char *p;

	while(pfold && pfold->pfold)
		pfold = pfold->pfold;

	if(pfold) {
		len = strlen(pfold->fold_path);
		if(!strncmp(folder->fold_path, pfold->fold_path, len)) {
			p = folder->fold_path + len;
			if(*p-- != pfold->hdelim)
				return folder->fold_path;

			while((*p != pfold->hdelim) &&
				  (p != folder->fold_path))
				p--;

			if(*p != pfold->hdelim)
				return folder->fold_path;

			return ++p;
		}
	}

	if((p = strrchr(folder->fold_path, '/')) == NULL)
		return folder->fold_path;

	p++;
	if(*p == '\0')
		return folder->fold_path;

	return p;
}

char *
get_folder_short_name(struct _mail_folder *folder) {
	char *p;
	int i;

	if(!folder)
		return NULL;

	if((p = strrchr(folder->fold_path, '/')) == NULL)
		return folder->fold_path;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if((mailbox[i]->status & FREMOTE)
		   || (folder == mailbox[i]))
			continue;

		if(mailbox[i]->sname &&
		   !strcmp(mailbox[i]->sname, p)) {
			if(strlen(folder->fold_path) <= MAX_FOLDER_ALIAS_NAME)
				return folder->fold_path;

			p = folder->fold_path;
			p += strlen(p) - MAX_FOLDER_ALIAS_NAME;
			return p;
		}
	}

	p++;
	if(*p == '\0')
		return folder->fold_path;

	return p;
}

void
close_all_folders() {
	int i;
	struct _mail_msg *msg, *msg1;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(mailbox[i])
			mailbox[i]->close(mailbox[i]);

		msg = mailbox[i]->messages;
		while(msg) {
			msg1 = msg->next;
			discard_message(msg);
			msg = msg1;
		}
		mailbox[i]->messages = NULL;
	}

	for(i = 0; i < (int)hidden_mailbox.size(); i++) {
		if(hidden_mailbox[i])
			hidden_mailbox[i]->close(hidden_mailbox[i]);
		msg = hidden_mailbox[i]->messages;
		while(msg) {
			msg1 = msg->next;
			discard_message(msg);
			msg = msg1;
		}
		hidden_mailbox[i]->messages = NULL;
	}
}

void
discard_all_folders() {
	int i;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(mailbox[i]) {
			discard_folder(mailbox[i]);
			mailbox[i] = NULL;
		}
	}

	for(i = 0; i < (int)hidden_mailbox.size(); i++) {
		if(hidden_mailbox[i]) {
			discard_folder(hidden_mailbox[i]);
			hidden_mailbox[i] = NULL;
		}
	}
}

char *
get_msg_file(struct _mail_msg *msg) {
	static char mfile[255];

	if(msg->num < 0)
		return NULL;

	snprintf(mfile, sizeof(mfile), "%s/%ld",
	    msg->folder ? msg->folder->fold_path : ftemp->fold_path, msg->num);

	return mfile;
}

void
local_message(struct _mail_msg *msg) {
	msg->mdelete = delete_message;
	msg->print = print_message;
	msg->print_body = print_message_body;
	msg->get_text = get_message_text;
	msg->get_header = get_message_header;
	msg->free_text = free_message_text;
	msg->get_file = get_msg_file;
	msg->update = update_message;
	msg->validity = get_message_validity;
	msg->refresh = refresh_message;
	msg->type = M_MH;
}

void local_folder(struct _mail_folder *folder) {
	folder->name = get_folder_name;
	folder->open = open_folder;
	folder->rescan = rescan_folder;
	folder->close = close_folder;
	folder->empty = empty_folder;
	folder->fdelete = delete_folder;
	folder->rename = rename_folder;
	folder->update = update_folder;
	folder->move = move_to_folder;
	folder->copy = copy_to_folder;
	folder->search = find_text;
	folder->getuid = get_mh_folder_uid;
	folder->getmsg = get_mh_msg_by_uid;
	folder->refresh = refresh_folder;
	folder->expunge = expunge_folder;
	folder->hdelim = '/';
	folder->type = F_MH;
}

int
traverse_mh_tree(struct _mail_folder *fld) {
	struct _mail_folder *fld1;
	DIR *dirp;
	struct dirent *dp;
	struct stat sb;
	char *p, buf[255], buf1[255];
	FILE *mfd;
	int k, i = 0;

	if((fld->status & NOINFR) ||
	   (fld->type != F_MH))
		return 0;

	if(!(dirp = opendir(fld->fold_path))) {
		display_msg(MSG_WARN, "scan tree", "Can not read from\n%s", fld->fold_path);
		return -1;
	}
	while((dp = readdir(dirp)) != NULL) {
		if((NAMLEN(dp) > MAX_MH_FOLD_NAME_LEN) || (NAMLEN(dp) < 1))
			continue;

		if(*dp->d_name == '.')
			continue;

		for(k = 0; k < NAMLEN(dp); k++) {
			if(!isgraph(dp->d_name[k]))
				goto snext;
		}

		snprintf(buf, sizeof(buf), "%s/%s", fld->fold_path, dp->d_name);
		if(get_mh_folder_by_path(buf))
			continue;

		if(stat(buf, &sb) == -1)
			continue;

		if(!(sb.st_mode & S_IFDIR)) {
			if(S_ISREG(sb.st_mode)) {
				if((*dp->d_name == '#') ||
				   (*dp->d_name == ','))
					k = 1;
				else
					k = 0;
				for(; k < NAMLEN(dp); k++) {
					if(!isdigit(dp->d_name[k]))
						break;
				}
				if(k == NAMLEN(dp))
					continue;

				if(sb.st_size != 0) {
					if((mfd = fopen(buf, "r")) == NULL)
						continue;
					if(fgets(buf1, 255, mfd) == NULL) {
						fclose(mfd);
						continue;
					}
					if(!is_from(buf1, NULL, 0)) {
						fclose(mfd);
						continue;
					}
					fclose(mfd);
				} else {
					if(((p = strstr(dp->d_name, ".lock")) != NULL) &&
					   (strlen(p) == 5))
						continue;
				}

				create_mbox_folder(NULL, buf);
			}
			continue;
		}

		if((fld1 = create_mh_folder(fld, dp->d_name)) == NULL)
			continue;
		fld1->status |= FSHORTH;

		if(i++ >= MAX_SUBFOLDERS)
			break;

		traverse_mh_tree(fld1);

		snext:
			;;
	}

	closedir(dirp);
	return 0;
}

int
open_all_folders(char *path, int noscan) {
	char maildir[255];
	char buf[255], buf1[255], *p;
	DIR *dirp;
	struct dirent *dp;
	struct stat sb;
	int i;
	struct _mail_folder *fld;
	FILE *mfd;

	trash = NULL;
	inbox = NULL;
	outbox = NULL;
	sentm = NULL;
	draft = NULL;

	if(!path || (strlen(path) < 1))
		snprintf(maildir, sizeof(maildir), "%s/Mail", homedir);
	else
		snprintf(maildir, sizeof(maildir), "%s", path);

	if(!(dirp = opendir(maildir))) {
		if(mkdir(maildir, 00700) == -1) {
			display_msg(MSG_WARN, "init", "mkdir failed");
			return -1;
		} else
			display_msg(MSG_MSG, "init", "Created %s", maildir);

		if(!(dirp = opendir(maildir))) {
			display_msg(MSG_WARN, "init", "Can not read from %s", maildir);
			return -1;
		}
	}

	snprintf(mailbox_path, sizeof(mailbox_path), "%s", maildir);

	if((inbox = create_mh_folder(NULL, INBOX)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", INBOX);
		return -1;
	}

	if((outbox = create_mh_folder(NULL, OUTBOX)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", OUTBOX);
		return -1;
	}

	if((trash = create_mh_folder(NULL, TRASH)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", TRASH);
		return -1;
	}

	if((sentm = create_mh_folder(NULL, SENTMAIL)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", SENTMAIL);
		return -1;
	}

	if((draft = create_mh_folder(NULL, DRAFT)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", DRAFT);
		return -1;
	}

	if((mftemplate = create_mh_folder(NULL, TEMPLATE)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", TEMPLATE);
		return -1;
	}

	if((ftemp = create_mh_folder(NULL, FTEMP)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", FTEMP);
		return -1;
	}

	if((fmbox = create_mh_folder(NULL, FMBOX)) == NULL) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", FIMAP);
		return -1;
	}

	if(ftemp->open(ftemp, 0) == -1) {
		display_msg(MSG_WARN, "INIT", "Can not open folder %s", FTEMP);
		return -1;
	}

	while(!noscan && ((dp = readdir(dirp)) != NULL)) {
		if((NAMLEN(dp) > MAX_MH_FOLD_NAME_LEN) || (NAMLEN(dp) < 1))
			continue;

		if(dp->d_name[0] == '.')
			continue;

		for(i = 0; i < NAMLEN(dp); i++) {
			if(!isgraph(dp->d_name[i]))
				goto next;
		}

		snprintf(buf, sizeof(buf), "%s/%s", maildir, dp->d_name);

		if(get_mh_folder_by_path(buf))
			continue;

		if(stat(buf, &sb) == -1)
			continue;

		if(!(sb.st_mode & S_IFDIR)) {
			if(S_ISREG(sb.st_mode)) {
				if(sb.st_size != 0) {
					if((mfd = fopen(buf, "r")) == NULL)
						continue;
					if(fgets(buf1, 255, mfd) == NULL) {
						fclose(mfd);
						continue;
					}
					if(!is_from(buf1, NULL, 0)) {
						fclose(mfd);
						continue;
					}
					fclose(mfd);
				} else {
					if(((p = strstr(dp->d_name, ".lock")) != NULL) &&
					   (strlen(p) == 5))
						continue;
				}

				create_mbox_folder(NULL, buf);
			}
			continue;
		}

		if((fld = create_mh_folder(NULL, dp->d_name)) == NULL)
			continue;

		fld->status |= FSHORTH;
		traverse_mh_tree(fld);

		next:
			;;
	}

	closedir(dirp);
	inbox->status  |= (SYSTEM|NOINFR|FSHORTH);
	trash->status  |= (SYSTEM|NOINFR|NOTRASH);
	outbox->status |= (SYSTEM|NOINFR);
	sentm->status  |= (SYSTEM|NOINFR);
	draft->status  |= (SYSTEM|NOINFR);
	ftemp->status  |= (SYSTEM|NOINFR);
	mftemplate->status  |= (SYSTEM|NOINFR);

	outbox->flags |= FSHRECP;
	sentm->flags  |= FSHRECP;
	draft->flags  |= FSHRECP;

	sort_folders();

	return 0;
}

struct _mail_folder *
get_folder_by_name(char *name) {
	char *p, pref[48];
	struct _retrieve_src *source;

	if(!name)
		return NULL;

	if((strlen(name) < 1) || (strlen(name) > MAX_FOLD_NAME_LEN))
		return NULL;

	if((*name == '#') &&
	   ((p = strchr(name, '/')) != NULL)) {
		*p = '\0';
		strncpy(pref, name, 47);
		*p = '/';
		p++;
		pref[47] = '\0';
		if(!strcmp(pref, "#mh"))
			return get_mh_folder_by_name(p);
		else
			if(!strcmp(pref, "#imap"))
			return find_imap_folder(NULL, p);
		else
			if(!strcmp(pref, "#mbox"))
			return get_mbox_folder_by_path(p);
		else
			if(!strncmp(pref, "#[", 2) &&
			   (pref[strlen(pref) - 1] == ']')) {
			pref[strlen(pref) - 1] = '\0';
			if((source = get_src_by_name(pref + 2)) == NULL)
				return get_mh_folder_by_path(name);
			switch(source->type) {
				case RSRC_IMAP:
					return find_imap_folder((struct _imap_src *)source->spec, p);
					break;

				default:
					return get_mh_folder_by_path(name);
					break;
			}
		} else
			return get_mh_folder_by_path(name);
	}

	return get_mh_folder_by_name(name);
}

struct _mail_folder *
get_mh_folder_by_path(char *path) {
	int i;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(!mailbox[i] || (mailbox[i]->status & FREMOTE))
			continue;

		if(!strcmp(mailbox[i]->fold_path, path))
			return mailbox[i];
	}

	for(i = 0; i < (int)hidden_mailbox.size(); i++) {
		if(!hidden_mailbox[i] || (hidden_mailbox[i]->status & FREMOTE))
			continue;

		if(!strcmp(hidden_mailbox[i]->fold_path, path))
			return hidden_mailbox[i];
	}

	return NULL;
}

struct _mail_folder *
get_mh_folder_by_name(char *name) {
	int i;

	if(!name)
		return NULL;

	if((strlen(name) < 1) || (strlen(name) > MAX_FOLD_NAME_LEN))
		return NULL;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(!mailbox[i] || (mailbox[i]->status & FREMOTE))
			continue;

		if(!strcmp(mailbox[i]->name(mailbox[i]), name))
			return mailbox[i];
	}

	for(i = 0; i < (int)hidden_mailbox.size(); i++) {
		if(!hidden_mailbox[i] || (hidden_mailbox[i]->status & FREMOTE))
			continue;

		if(!strcmp(hidden_mailbox[i]->name(hidden_mailbox[i]), name))
			return hidden_mailbox[i];
	}

	return NULL;
}

int
expunge_folder(struct _mail_folder *folder) {
	folder->update(folder);

	return 0;
}

int
refresh_folder(struct _mail_folder *folder) {
	DIR *dirp;
	struct dirent *dp;
	struct stat sb;
	struct _mail_msg *msg;
	struct _mail_folder *pfold;
	char *p, buf[255];
	int fchanged = 0;
	long msg_num, uid = folder->uid;
	unsigned long num = 0, unum = 0;

	if(uid == folder->getuid(folder))
		return 0;

	if(!(dirp = opendir(folder->fold_path))) {
		display_msg(MSG_WARN, "refresh folder", "Can not read from\n%s", folder->fold_path);
		return -1;
	}

	while((dp = readdir(dirp)) != NULL) {
		msg_num = strtol(dp->d_name, &p, 10);
		if(*p || msg_num == LONG_MIN || msg_num == LONG_MAX)
			continue;

		snprintf(buf, sizeof(buf), "%s/%ld", folder->fold_path, msg_num);
		if(stat(buf, &sb) == -1)
			continue;

		if(!(sb.st_mode & S_IFREG))
			continue;

		num++;
		if(sb.st_mtime >= sb.st_atime)
			unum++;

		if(!(folder->status & OPENED))
			continue;

		if((msg = get_msg_by_uid(folder, msg_num)) != NULL) {
			if(msg->flags & UNREAD) {
				if(sb.st_mtime < sb.st_atime)
					unum++;
			} else {
				if(sb.st_mtime >= sb.st_atime)
					unum--;
			}
			continue;
		}

		if((folder->status & FUNREAD) && (sb.st_mtime < sb.st_atime))
			continue;

		if((msg = get_message(msg_num, folder)) == NULL)
			continue;

		msg_cache_deluid(folder, msg_num);
		if(msg->flags & UNREAD) {
			if(sb.st_mtime < sb.st_atime)
				unum++;
		} else {
			if(sb.st_mtime >= sb.st_atime)
				unum--;
		}
		msg->folder = folder;
		msg->next = folder->messages;
		msg->status |= RECENT;
		folder->messages = msg;
		fchanged = 1;
		folder->status |= FRESCAN|FRECNT;
		folder->status &= ~SORTED;
	}
	closedir(dirp);
	if((num != folder->num_msg) ||
	   (unum != folder->unread_num)) {
		fchanged = 1;
		folder->unread_num = unum;
		folder->num_msg = num;
		folder->status |= FRESCAN|FRECNT;
	}

	if(folder->status & FRECNT) {
		for(pfold = folder->pfold; pfold; pfold = pfold->pfold)
			pfold->status &= ~FMRKTMP;
	}

	return(fchanged) ? 1 : 0;
}

int
rescan_folder(struct _mail_folder *folder) {
	DIR *dirp;
	struct dirent *dp;
	struct stat sb;
	long msg_num;
	char *p, buf[255];

	if(!folder)
		return -1;

	if(!(dirp = opendir(folder->fold_path))) {
		display_msg(MSG_WARN, "rescan folder", "Can not read from\n%s", folder->fold_path);
		return -1;
	}

	folder->num_msg = 0;
	folder->unread_num = 0;

	while((dp = readdir(dirp)) != NULL) {
		msg_num = strtol(dp->d_name, &p, 10);
		if(*p || msg_num == LONG_MIN || msg_num == LONG_MAX)
			continue;

		snprintf(buf, sizeof(buf), "%s/%ld", folder->fold_path, msg_num);
		if(stat(buf, &sb) == -1)
			continue;

		if(!(sb.st_mode & S_IFREG))
			continue;

		if(sb.st_size == 0) {
			unlink(buf);
			continue;
		}

		if(sb.st_mtime >= sb.st_atime)
			folder->unread_num++;

		folder->num_msg++;
	}
	closedir(dirp);

	return 0;
}

int
find_text(struct _mail_folder *folder, char *text, char *where, int flags, fld_search_cb callback) {
	struct _mail_msg *msg;
	struct _xf_rule rule;
	int opened;
	int found;
	unsigned long num;
	char fldname[16];

#ifdef HAVE_REGCOMP
	static regex_t rx;
	int regflags =  REG_EXTENDED;

	if(!text) {
		return -1;
	}

	if(flags & S_CASE)
		regflags |= REG_ICASE;

	if(regcomp(&rx, text, regflags)) {
		display_msg(MSG_WARN, "search", "Invalid regular expression %s", text);
		regfree(&rx);
		return -1;
	}

	rule.rx = rx;
#endif

	if(!text) {
		return -1;
	}

	if(where && (strlen(where) >= MAX_FIELD_NAME_LEN)) {
		return -1;
	}

	init_rule(&rule);

	if(folder == NULL) {
#ifdef HS_REGEX
		regfree(&rx);
#endif
		return -1;
	}

	opened = 0;
	found = 0;
	num = 0;

	if(!(folder->status & OPENED)) {
		if(folder->open(folder, 0) == -1) {
#ifdef HS_REGEX
			regfree(&rx);
#endif
			return -1;
		}

		opened = 1;
	}

	if(folder->messages == NULL) {
		folder->close(folder);
#ifdef HS_REGEX
		regfree(&rx);
#endif
		return 0;
	}

	strncpy(fldname, folder->name(folder), 15);
	fldname[15] = '\0';

	strcpy(rule.fmatch, where ? where : "Header");
	strncpy(rule.tmatch, text, 254);
	rule.tmatch[254] = '\0';

	msg = folder->messages;
	while(msg) {
		if(abortpressed()) {
			found = -1;
			break;
		}

		num++;
		display_msg(MSG_STAT, NULL, "Searching in %s: %d%%", fldname, num*100/folder->num_msg);

		if(match_rule(msg, &rule)) {
			found++;
			msg->status |= MARKTMP;
			if(callback)
				(*callback) (folder, msg->uid);
		} else
			msg->status &= ~MARKTMP;
		msg->free_text(msg);

		msg = msg->next;
	}

	if(opened)
		folder->close(folder);
	else {
		if(found > 0)
			folder->update(folder);
	}

#ifdef HS_REGEX
	regfree(&rx);
#endif

	return found;
}

struct _mail_folder *
alloc_folder() {
	struct _mail_folder *fld;

	if((fld = (struct _mail_folder *)malloc(sizeof(mail_folder))) == NULL) {
		display_msg(MSG_FATAL, "create folder", "Can not allocate memory");
		return NULL;
	}

	fld->sname = NULL;
	fld->descr = NULL;
	*fld->fold_path = '\0';
	fld->hdelim = '\0';
	fld->color = -1;
	fld->sort = -1;
	fld->expire = -1;
	fld->From = NULL;
	fld->To = NULL;
	fld->uid = -1;
	fld->type = 0;
	fld->flags = 0;
	fld->status = 0;
	fld->num_msg = 0;
	fld->unread_num = 0;
	fld->level = 0;
	fld->cache = NULL;
	fld->spec = NULL;
	fld->messages = NULL;
	fld->pfold = NULL;
	fld->subfold = NULL;

	return fld;
}

int append_folder(struct _mail_folder *folder, int hidden) {
	if(!hidden) {
		mailbox.push_back(folder);
		append_folder_tree(folder);
	} else {
		folder->status |= FHIDDN;
		hidden_mailbox.push_back(folder);
	}

	return 0;
}

int
remove_folder(struct _mail_folder *folder) {
	int i;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(mailbox[i] == folder) {
			remove_subfold(folder);
			discard_folder(folder);
			mailbox.erase(mailbox.begin() + i);
			return 0;
		}
	}
	return -1;
}

struct _mail_folder *
create_mh_folder(struct _mail_folder *folder, char *name) {
	struct _mail_folder *fld;
	struct stat sb;
	int hidden, crdir;
	char path[255], buf[255], *p;

	if(!name)
		return NULL;

	crdir = 0;
	hidden = 0;

	if((strlen(name) < 1) || (strlen(name) > MAX_MH_FOLD_NAME_LEN)) {
		display_msg(MSG_WARN, "create folder", "Invalid folder name %s", name);
		return NULL;
	}

	if(strrchr(name, '/')) {
		display_msg(MSG_WARN, "create folder", "folder name can not contain /");
		return NULL;
	}

	if(name[0] == '.')
		hidden = 1;

	p = name;
	while(*p != '\0') {
		if(!isgraph(*p)) {
			display_msg(MSG_WARN, "create folder", "Invalid character in folder name");
			return NULL;
		}

		p++;
	}

	snprintf(path, sizeof(path), "%s/%s", folder ? folder->fold_path : mailbox_path, name);

	if(get_mh_folder_by_path(path) != NULL) {
		display_msg(MSG_WARN, "create folder", "Folder already exists\n%s", path);
		return NULL;
	}

	if(folder && display_msg(MSG_QUEST|MSG_DEFNO, "MH folders can have both MH (default) and MBOX subfolders", "Do you want to create MBOX subfolder?"))
		return create_mbox_folder(NULL, path);

	if(stat(path, &sb) != -1) {
		if(!(sb.st_mode & S_IFDIR)) {
			crdir = 1;
			if(display_msg(MSG_QUEST, "create folder", "%s is not a directory, save it?", path)) {
				snprintf(buf, sizeof(buf), "%s.bak", path);
#ifdef __EMX__ /* Under OS/2 the file will not be deleted during rename() */
				if(access(buf, 0)==0) {
					if(unlink(buf)!=0) {
						display_msg(MSG_WARN, "unlink", "delete %s before moving", buf);
					}
				}
#endif
				if(rename(path, buf) == -1) {
					display_msg(MSG_WARN, "open folder", "Cannot rename %s", path);
					return NULL;
				} else
					display_msg(MSG_WARN, path, "was saved to %s", buf);
			} else
				return NULL;

		}
	} else {
		if(errno != ENOENT) {
			display_msg(MSG_WARN, "create folder", "Error accessing\n%s", path);
			return NULL;
		}
		crdir = 1;
	}

	if(crdir) {
		if(mkdir(path, 00700) == -1) {
			display_msg(MSG_WARN, "create folder", "Error creating\n%s", path);
			return NULL;
		}
	}

	if((fld = alloc_folder()) == NULL)
		return NULL;

	snprintf(fld->fold_path, sizeof(fld->fold_path), "%s", path);
	local_folder(fld);

	if(!crdir && (fld->rescan(fld) != 0)) {
		display_msg(MSG_WARN, "create folder", "Can not rescan folder\n%s", fld->fold_path);
		free(fld);
		return NULL;
	}

	if(append_folder(fld, hidden) == -1) {
		free(fld);
		return NULL;
	}

	fld->sname = strdup(get_folder_short_name(fld));

	folder_sort &= ~FLD_SORTED;

	return fld;
}

int
open_folder(struct _mail_folder *folder, int flags) {
	DIR *dirp;
	struct dirent *dp;
	struct _mail_msg *msg, *msg1, *msglock;
	struct _mail_folder *pfold;
	struct stat sb;
	char *p, buf[255];
	long msg_num;
	unsigned long prev_msg, prev_unread;

	if(!folder)
		return -1;

	prev_msg = folder->num_msg;
	prev_unread = folder->unread_num;
	if((folder->status & OPENED) || (folder->messages))
		folder->close(folder);

	if(!(dirp = opendir(folder->fold_path))) {
		display_msg(MSG_WARN, "open folder", "Can not read\n%s", folder->fold_path);
		return -1;
	}

	folder->num_msg = 0;
	folder->unread_num = 0;

	msg = NULL;
	msglock = folder->messages;
	if((folder->flags & CACHED) &&
	   !(flags & FOPEN_NOCACHE) &&
	   !exists_cache(folder)) {
		flags |= FOPEN_CRCACHE;
		flags |= FOPEN_NOCACHE;
	}

	while((dp = readdir(dirp)) != NULL) {
		msg_num = strtol(dp->d_name, &p, 10);
		if(*p || msg_num == LONG_MIN || msg_num == LONG_MAX)
			continue;

		snprintf(buf, sizeof(buf), "%s/%ld", folder->fold_path, msg_num);
		if(stat(buf, &sb) != 0)
			continue;

		if(!(sb.st_mode & S_IFREG))
			continue;

		if(flags & FOPEN_UNREAD) {
			if(sb.st_size == 0) {
				unlink(buf);
				continue;
			}

			if(sb.st_mtime < sb.st_atime) {
				folder->num_msg++;
				continue;
			}
		}

		if((folder->flags & CACHED) &&
		   !(flags & FOPEN_NOCACHE)) {
			if((msg = msg_cache(folder, msg_num)) == NULL) {
				if((msg = get_message(msg_num, folder)) == NULL) {
					if(sb.st_size == 0)
						unlink(buf);
					continue;
				}
				cache_msg(msg);
			}
		} else {
			if((msg = get_message(msg_num, folder)) == NULL) {
				if(sb.st_size == 0)
					unlink(buf);
				continue;
			}
			if(flags & FOPEN_CRCACHE)
				cache_msg(msg);
		}

		msg1 = msglock;
		while(msg1) {
			if(msg1->status & LOCKED) {
				if(msg1->num == msg_num) {
					discard_message(msg);
					if(msg1->flags & UNREAD)
						folder->unread_num++;
					folder->num_msg++;
					goto next_msg;
				}
			}
			msg1 = msg1->next;
		}

		if(msg->flags & UNREAD)
			folder->unread_num++;
		msg->folder = folder;
		msg->next = folder->messages;
		folder->messages = msg;
		folder->num_msg++;
		if((folder->num_msg % 50) == 0)
			display_msg(MSG_STAT, NULL, "Rescanning %s/%ld",
						folder->fold_path, msg_num);
		if(abortpressed()) {
			closedir(dirp);
			folder->close(folder);
			folder->num_msg = prev_msg;
			folder->unread_num = prev_unread;
			return -1;
		}

		next_msg:
		;;
	}

	closedir(dirp);
	folder->status |= OPENED;
	folder->status &= ~SEARCH;
	if(folder->status & FRECNT) {
		folder->status &= ~FRECNT;
		for(pfold = folder->pfold; pfold; pfold = pfold->pfold)
			pfold->status &= ~FMRKTMP;
	}

	if(flags & FOPEN_UNREAD)
		folder->status |= FUNREAD;
	else
		folder->status &= ~FUNREAD;

	if(((prev_msg != folder->num_msg) ||
		(prev_unread != folder->unread_num)) &&
	   (((folder_sort & 0x0f) == BY_MSGNUM) ||
		((folder_sort & 0x0f) == BY_UNREAD)))
		folder_sort &= ~FLD_SORTED;

	expire_msgs(folder);
	sort_folder(folder);

	return 0;
}

unsigned long
get_message_validity(struct _mail_msg *msg) {
	struct stat sb;

	if(msg->folder && (msg->folder->flags & FNVALD))
		return 0;

	if(lstat(msg->get_file(msg), &sb) == -1)
		return 0;

	return(unsigned long)sb.st_mtime;
}

int
refresh_message(struct _mail_msg *msg) {
	struct _mail_msg *msg1, *msg2;
	int oflags = msg->flags, ostats = msg->status, mime = 0;

	if(!msg->folder)
		return -1;

	msg_cache_del(msg);
	if(msg->msg_body)
		msg->free_text(msg);
	if(msg->mime) {
		discard_mime(msg->mime);
		msg->mime = NULL;
		mime = 1;
	}
	msg2 = msg->next;

	if((msg1 = get_message(msg->num, msg->folder)) == NULL)
		return -1;

	discard_message_header(msg);
	memcpy(msg, msg1, sizeof(struct _mail_msg));
	msg->next = msg2;
	msg->status |= ostats;
	if(!(oflags & H_ONLY))
		msg->get_header(msg);
	if(mime)
		mime_scan(msg);
	msg1->header = NULL;
	discard_message(msg1);
	cache_msg(msg);

	return 0;
}

int
get_message_header(struct _mail_msg *msg) {
	FILE *mfd = NULL;
	int eoh;
	struct stat sb;

	if(!(msg->status & H_SHORT))
		return 0;

	msg->status &= ~CHANGED;
	msg->update(msg);

	if(!(mfd = fopen(msg->get_file(msg), "r"))) {
		display_msg(MSG_WARN, "Can not open message file", "%s", msg->get_file(msg));
		return -1;
	}

	if(fstat(fileno(mfd), &sb) != 0) {
		display_msg(MSG_WARN, "Can not stat message file", "%s", msg->get_file(msg));
		return -1;
	}


#ifdef MMAP_MSG
	mmlen = ((size_t)sb.st_size < MAP_HEADER_LEN) ? (size_t)sb.st_size : MAP_HEADER_LEN;
	if((mmsg = (char *)mmap(NULL, mmlen , PROT_READ, MAP_PRIVATE, fileno(mfd), (off_t)0)) == (char *)MAP_FAILED) {
		display_msg(MSG_WARN, "mmap failed on", "%s",msg->get_file(msg));
		fclose(mfd);
		return -1;
	}

	mmpos = 0;
	mmofft = 0;
	mmmax = (size_t)sb.st_size;
	mmapfd = fileno(mfd);
#endif

	discard_message_header(msg);
	msg->header = get_msg_header(mfd, 0, &eoh);
	msg->flags |= msg->header->flags;
	msg->status &= ~H_SHORT;
	msg->flags &= 0xffff;
	msg->header->flags &= 0xffff;

#ifdef MMAP_MSG
	munmap(mmsg, mmlen);
	mmsg = NULL;
	mmlen = 0;
	mmpos = 0;
	mmmax = 0;
	mmofft = 0;
	mmapfd = -1;
#endif

	fclose(mfd);

	return 0;
}

int
xfmail_getpagesize() {
#ifdef  HAVE_GETPAGESIZE
	return getpagesize();
#else
	#ifdef  HAVE_SYSCONF
		#if defined(sun) && defined(SVR4)
	return sysconf (_SC_PAGESIZE);
		#else
	return sysconf (_SC_PAGE_SIZE);
		#endif
	#else
	return 4096;
	#endif
#endif
}

int
get_message_text(struct _mail_msg *msg, struct _mime_msg *mime) {
	int mfd, mimenum = 0;
	long mlen, mofft, mpage;
	struct _mime_msg *mm;

	if(msg->msg_len == 0)
		return 0;

	if(mime) {
		for(mm = msg->mime; mm != mime; mm = mm->mime_next, mimenum++) {
		}
		if(mm != mime)
			return -1;
	}

#ifdef  MMAP_MSG
	if(mmsg != NULL)
		return -1;
#endif

	if((mfd = open(msg->get_file(msg), O_RDONLY)) == -1)
		return -1;
	msg->get_header(msg);

	mpage = xfmail_getpagesize();
	if(mime) {
		if(msg->mime == NULL) {
			mime_scan(msg);
			mime = msg->mime;
			while(mimenum--)
				mime = mime->mime_next;
			if(mime == NULL)
				return -1;
		}

		if(mime->m_start == mime->m_end)
			return 0;

		if((mime->m_start > mime->m_end) ||
		   (mime->m_start > msg->msg_len))
			return -1;

		mofft = mime->m_start - (mime->m_start % mpage);
		mlen = mime->m_end - mofft;
	} else {
		mofft = 0;
		mlen = msg->msg_len;
	}

	if((msg->msg_body = (char *)mmap(NULL, (size_t)mlen, PROT_READ|PROT_WRITE, MAP_PRIVATE, mfd, (off_t)mofft)) == (char *)MAP_FAILED) {
		display_msg(MSG_WARN, "get_message_text", "mmap failed");
		close(mfd);
		return -1;
	}

#ifdef HAVE_MADVISE
	#ifdef  MADV_SEQUENTIAL
	madvise(msg->msg_body, mlen, MADV_SEQUENTIAL);
	#endif
#endif

#ifdef  MMAP_MSG
	mmsg = msg->msg_body;
	mmpos = 0;
	mmofft = mofft;
	mmlen = mlen;
	mmmax = mlen;
	mmapfd = -1;
#endif

	close(mfd);
	msg->msg_body_len = mlen;
	return 0;
}

void
free_message_text(struct _mail_msg *msg) {
	if(msg->msg_body == NULL)
		return;

	munmap(msg->msg_body, msg->msg_body_len);
	msg->msg_body = NULL;
	msg->msg_body_len = 0;

#ifdef  MMAP_MSG
	mmsg = NULL;
	mmpos = 0;
	mmofft = 0;
	mmlen = 0;
	mmmax = 0;
	mmapfd = -1;
#endif

	return;
}

struct _news_addr *
copy_news_address(struct _news_addr *addr) {
	struct _news_addr *ma;

	if(!addr)
		return NULL;

	ma = (struct _news_addr *)malloc(sizeof(struct _news_addr));
	if(!ma) {
		display_msg(MSG_FATAL, "malloc", "malloc failed in copy news address");
		return NULL;
	}

	if(addr->name)
		ma->name = strdup(addr->name);
	else
		ma->name = NULL;

	if(addr->descr)
		ma->descr = strdup(addr->descr);
	else
		ma->descr = NULL;

	ma->next = addr->next;
	return ma;
}

struct _mail_addr *
copy_address(struct _mail_addr *addr) {
	struct _mail_addr *ma;

	if(!addr)
		return NULL;

	ma = (struct _mail_addr *)malloc(sizeof(struct _mail_addr));
	if(!ma) {
		display_msg(MSG_FATAL, "malloc", "malloc failed in copy address");
		return NULL;
	}

	if(addr->addr)
		ma->addr = strdup(addr->addr);
	else
		ma->addr = NULL;

	if(addr->name)
		ma->name = strdup(addr->name);
	else
		ma->name = NULL;

	if(addr->comment)
		ma->comment = strdup(addr->comment);
	else
		ma->comment = NULL;

	if(addr->pgpid)
		ma->pgpid = strdup(addr->pgpid);
	else
		ma->pgpid = NULL;

	ma->next_addr = addr->next_addr;
	ma->num = addr->num;

	return ma;
}

struct _news_addr *
copy_news_address_chain(struct _news_addr *addr) {
	struct _news_addr *ma, *ma1;

	ma = NULL;

	while(addr) {
		ma1 = copy_news_address(addr);
		ma1->next = ma;
		ma = ma1;

		addr = addr->next;
	}

	return ma;
}

struct _mail_addr *
copy_address_chain(struct _mail_addr *addr) {
	struct _mail_addr *ma, *ma1;

	ma = NULL;

	while(addr) {
		ma1 = copy_address(addr);
		ma1->next_addr = ma;
		ma = ma1;

		addr = addr->next_addr;
	}

	return ma;
}

struct _msg_header *
copy_message_header(struct _msg_header *header) {
	struct _msg_header *nheader;

	if((nheader = (struct _msg_header *)malloc(sizeof(struct _msg_header)))
	   == NULL) {
		display_msg(MSG_FATAL, "copy", "Malloc failed");
		return NULL;
	}

	memcpy(nheader, header, sizeof(struct _msg_header));
	nheader->Subject = header->Subject ? strdup(header->Subject) : NULL;
	nheader->From = copy_address_chain(header->From);
	nheader->To = copy_address_chain(header->To);
	nheader->Cc = copy_address_chain(header->Cc);
	nheader->Bcc = copy_address_chain(header->Bcc);
	nheader->Sender = copy_address_chain(header->Sender);
	nheader->News = copy_news_address_chain(header->News);
	nheader->other_fields = copy_field_chain(header->other_fields);

	return nheader;
}

struct _mail_msg *
copy_msg(struct _mail_msg *msg) {
	struct _mail_msg *nmsg;

	if((nmsg = (struct _mail_msg *)malloc(sizeof(struct _mail_msg))) == NULL) {
		display_msg(MSG_FATAL, "copy", "Malloc failed");
		return NULL;
	}

	memcpy(nmsg, msg, sizeof(struct _mail_msg));
	nmsg->header = copy_message_header(msg->header);
	nmsg->msg_body = NULL;
	nmsg->msg_body_len = 0;
	nmsg->next = NULL;
	nmsg->ref = NULL;
	nmsg->data = MSG_DAT_EMPTY;
	nmsg->pdata = NULL;
	nmsg->mime = NULL;

	return nmsg;
}

struct _mail_msg *
copy_to_folder(struct _mail_msg *msg, struct _mail_folder *folder) {
	struct _mail_msg *nmsg;
	long cnum;
	char cpath[255], *mfile;
	struct stat sb;
	FILE *nfd;

	if(!msg || !folder)
		return NULL;

	msg->status &= ~COPIED;
	folder->status |= FRESCAN;

	if((cnum = get_new_name(folder)) == -1) {
		display_msg(MSG_WARN, "copy", "Can not create new message in %s", folder->name(folder));
		return NULL;
	}

	snprintf(cpath, sizeof(cpath), "%s/%ld", folder->fold_path, cnum);

	if((mfile = msg->get_file(msg)) == NULL) {
		display_msg(MSG_WARN, "copy", "Can not get message");
		return NULL;
	}

	if(stat(mfile, &sb)) {
		display_msg(MSG_WARN, "copy", "Can not access\n%s", mfile);
		return NULL;
	}

	if(msg->status & CHANGED) {
		if((nfd = fopen(cpath, "w")) == NULL) {
			display_msg(MSG_WARN, "copy", "Can not open\n%s", cpath);
			return NULL;
		}

		if(msg->print(msg, nfd, 0) != 0) {
			display_msg(MSG_WARN, "copy", "Can not write to\n%s", cpath);
			fclose(nfd);
			return NULL;
		}

		if(fclose(nfd) == EOF) {
			display_msg(MSG_WARN, "copy", "Can not write to\n%s", cpath);
			fclose(nfd);
			return NULL;
		}

		msg->header->flags = msg->flags;
	} else {
		if(msg->update(msg) != 0) {
			display_msg(MSG_WARN, "copy", "Can not update message");
			return NULL;
		}

		if(fastcopy(mfile, cpath, &sb)) {
			display_msg(MSG_WARN, "copy", "Can not copy\nfrom %s to\n%s", mfile, cpath);
			return NULL;
		}
	}

	folder->num_msg++;
	if(msg->flags & UNREAD)
		folder->unread_num++;

	if((folder->status & OPENED) || (msg->status & LOCKED)) {
		if(!(nmsg = get_message(cnum, folder)))
			return NULL;

		cache_msg(nmsg);
		nmsg->flags = msg->flags;
		nmsg->status = msg->status;
		nmsg->status &= ~LOCKED;
		nmsg->folder = folder;
		nmsg->next = folder->messages;
		folder->messages = nmsg;
		folder->status &= ~SORTED;
		return nmsg;
	}

	return msg;
}

void
discard_news_address(struct _news_addr *addr) {
	struct _news_addr *addr1;

	while(addr) {
		addr1 = addr->next;
		if(addr->name)
			free(addr->name);
		if(addr->descr)
			free(addr->descr);
		free(addr);
		addr = addr1;
	}

	return;
}

void
discard_address(struct _mail_addr *addr) {
	struct _mail_addr *addr1;
	while(addr) {
		addr1 = addr->next_addr;
		if(addr->addr)
			free(addr->addr);

		if(addr->name)
			free(addr->name);

		if(addr->comment)
			free(addr->comment);

		if(addr->pgpid)
			free(addr->pgpid);

		free(addr);
		addr = addr1;
	}
	return;
}


void
discard_message_header(struct _mail_msg *msg) {
	head_field *hf, *hf1;

	if(!msg->header)
		return;

	discard_address(msg->header->From);
	discard_address(msg->header->To);
	discard_address(msg->header->Sender);
	discard_address(msg->header->Cc);
	discard_address(msg->header->Bcc);
	discard_news_address(msg->header->News);

	hf = msg->header->other_fields;
	while(hf) {
		hf1 = hf->next_head_field;
		if(hf->f_line)
			free(hf->f_line);
		free(hf);
		hf = hf1;
	}

	if(msg->header->Subject)
		free(msg->header->Subject);
	if(msg->header->Fcc)
		free(msg->header->Fcc);

	free(msg->header);
	msg->header = NULL;

	return;
}

void
discard_message(struct _mail_msg *msg) {
	if(!msg)
		return;

	if(msg->msg_body)
		msg->free_text(msg);

	discard_message_header(msg);

	if(msg->mime)
		discard_mime(msg->mime);

	free(msg);

	return;
}

char *
dir_path(char *path) {
	static char dir[255];
	char *p;

	dir[0] = '.';
	dir[1] = '\0';

	if(!path)
		return dir;

	if(strrchr(path, '/') == NULL)
		return dir;

	snprintf(dir, sizeof(dir), "%s", path);
	p = strrchr(dir, '/');
	p[0] = '\0';
	return dir;

}

char *
name_path(const char *path) {
	static char name[MAXPATHLEN];
	char *p;

	name[0] = '.';
	name[1] = '\0';

	if(!path)
		return name;

	snprintf(name, sizeof(name), "%s", path);

	if((p = strrchr(path, '/')) == NULL)
		return name;

	p++;
	snprintf(name, sizeof(name), "%s", p);
	return name;
}

int
rename_folder(struct _mail_folder *folder, char *name) {
	char new_path[255], *p;
	struct stat sb;

	if(!folder)
		return -1;

	if(folder->status & SYSTEM) {
		display_msg(MSG_WARN, "rename", "%s is a system folder , you can not rename it", folder->sname);
		return -1;
	}

	if((strlen(name) < 1) || (strlen(name) > MAX_MH_FOLD_NAME_LEN)) {
		display_msg(MSG_WARN, "rename folder", "Invalid folder name %s", name);
		return -1;
	}

	if(strrchr(name, '/')) {
		display_msg(MSG_WARN, "rename folder", "folder name can not contain /");
		return -1;
	}

	p = name;
	while(*p != '\0') {
		if(!isgraph(*p)) {
			display_msg(MSG_WARN, "rename folder", "Invalid character in folder name");
			return -1;
		}

		p++;
	}

	snprintf(new_path, sizeof(new_path), "%s/%s", dir_path(folder->fold_path), name);
	if(get_mh_folder_by_path(new_path)) {
		display_msg(MSG_WARN, "rename folder", "MH folder with name %s already exists", name);
		return -1;
	}


	if(stat(new_path, &sb) == 0) {
		if(!(sb.st_mode & S_IFDIR)) {
			if(!display_msg(MSG_QUEST|MSG_DEFNO, "rename folder", "file %s exists, delete?", new_path))
				return -1;
			if(unlink(new_path) == -1) {
				display_msg(MSG_WARN, "rename folder", "Can not delete %s", new_path);
				return -1;
			}
		} else {
			if(rmdir(new_path) == -1) {
				display_msg(MSG_WARN, "rename folder", "Can not delete directory %s", new_path);
				return -1;
			}
		}
	}
#ifdef __EMX__ /* Under OS/2 the file will not be deleted during rename() */
	if(access(new_path, 0)==0) {
		if(unlink(new_path)!=0) {
			display_msg(MSG_WARN, "unlink", "delete %s before moving", new_path);
		}
	}
#endif
	if(rename(folder->fold_path, new_path) == -1) {
		display_msg(MSG_WARN, "rename folder", "Failed to rename directory");
		return -1;
	}

	snprintf(folder->fold_path, sizeof(folder->fold_path), "%s", new_path);

	p = folder->sname;
	folder->sname = strdup(get_folder_short_name(folder));
	rename_cache(folder, p);

	if(p)
		free(p);

	update_cfold_path(folder);

	folder_sort &= ~FLD_SORTED;
	return 0;

}

int
increase_level(struct _mail_folder *fold) {
	int i;

	if(fold->level >= MAX_NESTLEVEL) {
		display_msg(MSG_WARN, "folder tree", "nesting level too high");
		return -1;
	}

	fold->level++;

	if(!fold->subfold)
		return 0;

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(fold->subfold[i]) {
			if(increase_level(fold->subfold[i]) == -1)
				return -1;
		}
	}

	return 0;
}

int
reduce_level(struct _mail_folder *fold) {
	int i;

	if(fold->level)
		fold->level--;

	if(!fold->subfold)
		return 0;

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(fold->subfold[i])
			reduce_level(fold->subfold[i]);
	}

	return 0;
}

int
remove_subfold(struct _mail_folder *fold) {
	struct _mail_folder *pfold;
	int i, freepfold = 1;

	if(fold->status & FRECNT) {
		for(pfold = fold->pfold; pfold; pfold = pfold->pfold)
			pfold->status &= ~FMRKTMP;
	}

	if(fold->subfold) {
		for(i = 0; i < MAX_SUBFOLDERS; i++) {
			if(fold->subfold[i]) {
				fold->subfold[i]->pfold = fold->pfold;
				reduce_level(fold->subfold[i]);
			}
		}

		free(fold->subfold);
		fold->subfold = NULL;
	}

	if(fold->pfold) {
		for(i = 0; i < MAX_SUBFOLDERS; i++) {
			if(fold->pfold->subfold[i] == fold)
				fold->pfold->subfold[i] = NULL;
			else
				if(fold->pfold->subfold[i])
				freepfold = 0;
		}

		if(freepfold) {
			free(fold->pfold->subfold);
			fold->pfold->subfold = NULL;
		}
	}

	fold->pfold = NULL;

	return 0;
}

int
find_subfold_ind(struct _mail_folder *fold) {
	int i;

	if(!fold->pfold)
		return -1;

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(fold->pfold->subfold[i] == fold)
			return i;
	}

	display_msg(MSG_WARN, "find_subfold_ind", "Error in folders tree");
	return -1;
}

int
add_subfold(struct _mail_folder *fold, struct _mail_folder *sfold) {
	int i, k = -1;

	if((sfold->pfold == fold) ||
	   (fold == sfold))
		return 0;

	if(sfold->pfold) {
		if((i = find_subfold_ind(sfold)) == -1)
			return -1;
		sfold->pfold->subfold[i] = NULL;
	}

	if(fold->subfold == NULL) {
		fold->subfold = (struct _mail_folder **)malloc(MAX_SUBFOLDERS * sizeof(struct _mail_folder *));
		for(i = 0; i < MAX_SUBFOLDERS; i++)
			fold->subfold[i] = NULL;
		fold->subfold[0] = sfold;
		sfold->pfold = fold;
		sfold->level = fold->level;
		if(increase_level(sfold) == -1) {
			free(fold->subfold);
			fold->subfold = NULL;
			sfold->level = 0;
			sfold->pfold = NULL;
			return -1;
		}
		if(fold->flags & FEXPND) {
			sfold->status &= ~FSKIP;
			sfold->flags |= FEXPND;
		} else {
			sfold->status |= FSKIP;
			sfold->flags &= ~FEXPND;
		}
		return 0;
	}

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(fold->subfold[i] == sfold)
			return 0;
		if((k == -1) && (fold->subfold[i] == NULL))
			k = i;
	}

	if(k == -1) {
		display_msg(MSG_WARN, "add_subfolder", "%-.64 has too many subfolders", fold->fold_path);
		return -1;
	}

	fold->subfold[k] = sfold;
	sfold->pfold = fold;
	sfold->level = fold->level;
	if(increase_level(sfold) == -1) {
		fold->subfold[k] = NULL;
		sfold->level = 0;
		sfold->pfold = NULL;
		return -1;
	}
	if(fold->flags & FEXPND)
		sfold->status &= ~FSKIP;
	else
		sfold->status |= FSKIP;

	return 0;
}

int is_parent(struct _mail_folder *fold, struct _mail_folder *sfold) {
	int flen, sflen;

	if(!sfold || !fold || (fold == sfold))
		return -1;

	if(sfold->type != fold->type) {
		if((fold->type != F_MH) ||
		   (sfold->type != F_MBOX))
			return -1;
	} else
		if((fold->type == F_IMAP) &&
		   (fold->spec != sfold->spec))
		return -1;

	sflen = strlen(sfold->fold_path);
	flen = strlen(fold->fold_path);

	if((fold->status & FTOP) && sflen)
		return 0;

	if(flen >= (sflen - 1))
		return -1;
	if(sfold->fold_path[flen] != fold->hdelim)
		return -1;
	if(strncmp(fold->fold_path, sfold->fold_path, flen))
		return -1;

	return 0;
}

int
create_folder_tree() {
	int i, k, s;
	struct _mail_folder *fold, *sfold, *pfold;

	for(i = 0; i < (int)mailbox.size(); i++) {
		fold = mailbox[i];
		fold->pfold = NULL;
		fold->level = 0;
		if(fold->subfold) {
			free(fold->subfold);
			fold->subfold = NULL;
		}
	}

	for(i = 0; i < (int)mailbox.size(); i++) {
		fold = mailbox[i];
		if((fold->status & NOINFR) ||
		   (fold->hdelim == '\0'))
			continue;

		for(k = 0; k < (int)mailbox.size(); k++) {
			sfold = mailbox[k];
			if(fold == sfold)
				continue;
			if(is_parent(fold, sfold) == -1)
				continue;

			while(is_parent(fold, sfold->pfold) != -1)
				sfold = sfold->pfold;

			if(sfold->pfold == NULL) {
				fold->level = 0;
				add_subfold(fold, sfold);
				continue;
			}

			if((s = find_subfold_ind(sfold)) == -1)
				continue;

			pfold = sfold->pfold;
			fold->level = sfold->level;
			add_subfold(fold, sfold);
			pfold->subfold[s] = NULL;
			add_subfold(pfold, fold);
		}
	}

	sort_folders();
	return 0;
}

int append_folder_tree(struct _mail_folder *fold) {
	int i, k, exsubfold;
	struct _mail_folder *sfold;

	remove_subfold(fold);

	for(i = 0; i < (int)mailbox.size(); i++) {
		sfold = mailbox[i];
		if(sfold == fold)
			continue;

		if((sfold->pfold == fold) ||
		   (fold->pfold == sfold))
			continue;

		if(is_parent(fold, sfold) != -1) {
			while(is_parent(fold, sfold->pfold) != -1)
				sfold = sfold->pfold;

			if(sfold->pfold)
				add_subfold(sfold->pfold, fold);
			add_subfold(fold, sfold);
		} else
			if(is_parent(sfold, fold) != -1) {
			exsubfold = 1;
			while(exsubfold && sfold->subfold) {
				exsubfold = 0;
				for(k = 0; k < MAX_SUBFOLDERS; k++) {
					if(is_parent(sfold->subfold[k], fold) != -1) {
						sfold = sfold->subfold[k];
						exsubfold = 1;
						break;
					}
				}
			}

			add_subfold(sfold, fold);
		}
	}

	return 0;
}

int
is_tree_parent(const struct _mail_folder *fold, const struct _mail_folder *sfold) {
	while(sfold->pfold) {
		if(sfold->pfold == fold)
			return 0;
		sfold = sfold->pfold;
	}

	return -1;
}

const struct _mail_folder *
get_ancestor(const struct _mail_folder *folder) {
	struct _mail_folder *pfold;
	if(folder->pfold == NULL)
		return NULL;

	for(pfold = folder->pfold; pfold->pfold; pfold = pfold->pfold) {
	}

	return pfold;
}

int
find_ancestors(const struct _mail_folder **fold1, const struct _mail_folder **fold2) {
	if((*fold1)->pfold == (*fold2)->pfold)
		return 0;

	if(is_tree_parent(*fold1, *fold2) != -1)
		return 1;

	if(is_tree_parent(*fold2, *fold1) != -1)
		return 2;

	while((*fold1)->pfold != NULL) {
		if(is_tree_parent((*fold1)->pfold, *fold2) != -1)
			break;
		*fold1 = (*fold1)->pfold;
	}

	while((*fold2)->pfold != NULL) {
		if(is_tree_parent((*fold2)->pfold, *fold1) != -1)
			break;
		*fold2 = (*fold2)->pfold;
	}

	return 0;
}

void expand_tree(struct _mail_folder *fold, int all) {
	int i;

	if(all)
		fold->flags |= FEXPND;

	if(fold->subfold == NULL)
		return;

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(fold->subfold[i]) {
			if(fold->flags & FEXPND)
				fold->subfold[i]->status &= ~FSKIP;
			expand_tree(fold->subfold[i], all);
		}
	}

	return;
}

void
collapse_tree(struct _mail_folder *fold, int all) {
	int i;

	if(all)
		fold->flags &= ~FEXPND;

	if(fold->subfold == NULL)
		return;

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(fold->subfold[i]) {
			fold->subfold[i]->status |= FSKIP;
			collapse_tree(fold->subfold[i], all);
		}
	}

	return;
}

int
expand_collapse_tree(struct _mail_folder *fold, int all) {

	if(!fold->subfold)
		return -1;

	if(fold->flags & FEXPND) {
		fold->flags &= ~FEXPND;
		collapse_tree(fold, all);
	} else {
		fold->flags |= FEXPND;
		expand_tree(fold, all);
	}

	return 0;
}

void
discard_spec(struct _mail_folder *folder) {

	if(folder->type & F_MBOX)
		free_mbox_spec(folder);
	else
		if(folder->type & F_IMAP) {
	} else
		free(folder->spec);

	folder->spec = NULL;
}

void
discard_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg, *msg1;

	if(!folder)
		return;

	if(folder->messages)
		folder->close(folder);

	msg = folder->messages;
	while(msg) {
		msg1 = msg->next;
		discard_message(msg);
		msg = msg1;
	}

	if(folder->subfold)
		free(folder->subfold);

	if(folder->cache)
		close_cache(folder);

	if(folder->spec && folder->type == M_MBOX)
		discard_spec(folder);

	if(folder->sname)
		free(folder->sname);

	if(folder->descr)
		free(folder->descr);

	if(folder->From)
		discard_address(folder->From);

	if(folder->To)
		discard_address(folder->To);

	free(folder);

	return;
}

int
delete_folder(struct _mail_folder *folder) {
	DIR *dirp;
	struct dirent *dp;
	char buf[255];
	struct stat sb;
	int i;

	if(!folder)
		return -1;

	if(folder->status & SYSTEM) {
		display_msg(MSG_WARN, "delete", "%s is a system folder , you can not delete it", folder->name(folder));
		return -1;
	}

	if(folder->subfold) {
		for(i = 0; i < MAX_SUBFOLDERS; i++) {
			if(folder->subfold[i]) {
				display_msg(MSG_WARN, "delete", "%s has subfolder(s), can not delete", folder->name(folder));
				return -1;
			}
		}
	}

	folder->empty(folder);
	if(folder->num_msg || folder->unread_num) {
		display_msg(MSG_WARN, "delete", "Can not delete all messages in folder %s", folder->name(folder));
		return -1;
	}

	if(stat(folder->fold_path, &sb) != -1) {
		if(!(sb.st_mode & S_IFDIR))
			return -1;
	}

	if((dirp = opendir(folder->fold_path)) != NULL) {
		while((dp = readdir(dirp)) != NULL) {
			if(!strcmp(dp->d_name, "."))
				continue;

			if(!strcmp(dp->d_name, ".."))
				continue;

			snprintf(buf, sizeof(buf), "%s/%s", folder->fold_path, dp->d_name);
			if(unlink(buf) == -1) {
				closedir(dirp);
				display_msg(MSG_WARN, "delete", "Can not remove %s from folder directory", dp->d_name);
				return -1;
			}
		}

		closedir(dirp);
	}

	if(rmdir(folder->fold_path) == -1)
		display_msg(MSG_WARN, "delete", "Can not remove folder directory");
	delete_cache(folder);

	folder_sort &= ~FLD_SORTED;

	return remove_folder(folder);
}

void
empty_folder(struct _mail_folder *folder) {
	DIR *dirp;
	struct dirent *dp;
	char buf[255];
	int oflags;

	if(!folder)
		return;

	oflags = folder->status;

	if((folder->status & OPENED) || (folder->messages))
		folder->close(folder);

	if(folder->messages)
		goto reop;

	if(!(dirp = opendir(folder->fold_path))) {
		folder->num_msg = folder->unread_num = 0;
		return;
	}

	while((dp = readdir(dirp)) != NULL) {

		if(abortpressed()) {
			closedir(dirp);
			return;
		}

		if(!isdigit(dp->d_name[0]))
			continue;

		snprintf(buf, sizeof(buf), "%s/%s", folder->fold_path, dp->d_name);
		if(unlink(buf) == -1) {
			closedir(dirp);
			display_msg(MSG_WARN, "Failed to delete", "%s", buf);
			return;
		}

		display_msg(MSG_STAT, NULL, "Deleting %s", dp->d_name);
	}

	closedir(dirp);
	folder->num_msg = folder->unread_num = 0;
	folder->status &= ~FRECNT;
	folder->status &= ~SEARCH;
	folder->status &= ~FMRKTMP;

	reop: if(oflags & OPENED)
		folder->open(folder, 0);

	folder_sort &= ~FLD_SORTED;
	delete_cache(folder);
	return;
}

void
close_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg, *msg1, *msg_lock;
	struct _mail_folder *pfold;

	if(!folder)
		return;

	folder->status &= ~OPENED;
	folder->status &= ~SORTED;
	folder->status &= ~SEARCH;
	folder->status &= ~FUNREAD;
	if(folder->status & FRECNT) {
		folder->status &= ~FRECNT;
		for(pfold = folder->pfold; pfold; pfold = pfold->pfold)
			pfold->status &= ~FMRKTMP;
	}

	close_cache(folder);

	if(!folder->messages)
		return;

	folder->update(folder);

	msg = folder->messages;
	msg_lock = NULL;
	while(msg) {
		msg1 = msg->next;
		if(msg->status & LOCKED) {
			msg->next = msg_lock;
			msg_lock = msg;
		} else
			discard_message(msg);

		msg = msg1;
	}

	folder->messages = msg_lock;

	return;
}

/* this is ugly :-( */
/* why? wim */

long
get_new_name(struct _mail_folder *folder) {
	static char buf[255];
	struct stat sb;
	long i, hi = 0;
	DIR *dirp;
	struct dirent *de;
	char *cp, *dep;

	if(!folder)
		folder = ftemp;

	if((dirp = opendir (folder->fold_path)) == NULL)
		return -1;
	while((de = readdir (dirp)) != NULL) {
		dep = de->d_name;
		if(*dep == ',')
			dep++;

		i = strtol(dep, &cp, 10);
		if (*cp || i == LONG_MIN || i == LONG_MAX)
			continue;
		if(i > hi)
				hi = i;
	}
	closedir (dirp);

	for(i = hi+1; i < LONG_MAX; i++) {
		snprintf(buf, sizeof(buf), "%s/%ld", folder->fold_path, i);
		if(stat(buf, &sb) == -1) {
			msg_cache_deluid(folder, i);
			return i;
		}
	}
	// that sure was ugly...
	return -1;
}

void
update_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg, *msg_nxt;
	struct _mail_folder *fld;
	int k;

	if(!folder)
		return;

	if(!folder->messages)
		return;

	msg = folder->messages;
	msg_nxt = folder->messages->next;
	k = 0;
	while(msg) {

		if((msg->status & LOCKED) &&
		   !(msg->status & COPIED)) {
			msg->status &= ~DELETED;
			msg->status &= ~MOVED;
			msg->flags &= ~NOT_SENT;
			msg = msg->next;
			continue;
		}

		msg_nxt = msg->next;
		k++;

		if((msg->status & DELETED) ||
		   (msg->status & MOVED) ||
		   (msg->status & COPIED) ||
		   (msg->flags & NOT_SENT)) {
			if(abortpressed())
				return;
		}

		if(msg->status & DELETED) {
			if((k%2) == 0)
				display_msg(MSG_STAT, NULL, "Deleting %d", k);
			msg->mdelete(msg);
		} else
			if(msg->status & MOVED) {
			fld = msg->folder;
			msg->folder = folder;
			msg->status &= ~MOVED;
			if(msg->folder != fld) {
				if((k%2) == 0)
					display_msg(MSG_STAT, NULL, "Moving %d", k);
				if(msg->update(msg) != 0) {
					display_msg(MSG_WARN, "update folder", "Failed to update message");
					return;
				}

				if(fld->move(msg, fld) != 0) {
					display_msg(MSG_WARN, "update folder", "Failed to move message");
					return;
				}
			}
		} else
			if(msg->status & COPIED) {
			fld = msg->folder;
			msg->folder = folder;
			msg->status &= ~COPIED;
			if((k%2) == 0)
				display_msg(MSG_STAT, NULL, "Copying %d", k);
			if(msg->update(msg) != 0) {
				display_msg(MSG_WARN, "update folder", "Failed to update message");
				return;
			}
			fld->copy(msg, fld);
		} else
			if(msg->flags & NOT_SENT) {
			msg->flags &= ~NOT_SENT;
			if(!(msg->flags & M_SFAILED)) {
				display_msg(MSG_STAT, NULL, "Sending %d", k);
				if(msg->update(msg) != 0) {
					display_msg(MSG_WARN, "update folder", "Failed to update message");
					return;
				}
				send_message(msg);
			}
		} else
			msg->update(msg);

		msg = msg_nxt;
	}

	msg = folder->messages;
	while(msg) {
		msg_nxt = msg->next;
		if(msg->flags & M_SFAILED) {
		msg->flags^=M_SFAILED;
		msg->flags|=NOT_SENT;
			msg->status |= CHANGED;
		msg->update(msg);
	}
		msg = msg_nxt;
	}

	display_msg(MSG_STAT, NULL, "");
	return;

}

struct _mail_addr *
get_addr_by_name(struct _mail_msg *msg, char *aname) {
	if(!aname || !msg)
		return NULL;

	if(!strcasecmp(aname, "From"))
		return msg->header->From;

	if(!strcasecmp(aname, "To")) {
		msg->get_header(msg);
		return msg->header->To;
	}

	if(!strcasecmp(aname, "Cc")) {
		msg->get_header(msg);
		return msg->header->Cc;
	}

	if(!strcasecmp(aname, "Bcc")) {
		msg->get_header(msg);
		return msg->header->Bcc;
	}

	if(!strcasecmp(aname, "Sender")) {
		msg->get_header(msg);
		return msg->header->Sender;
	}

	return NULL;
}

char *
get_charset_addr_line(char *prefix, mail_addr *addr, int *charset) {
	static char addr_line[512];
	char addrbuf[255], namebuf[80];

	strcpy(addr_line, "unknown");

	if(!addr || !addr->addr)
		return addr_line;

	snprintf(addrbuf, sizeof(addrbuf), "%s", rfc1522_decode(addr->addr, charset));

	if(addr->name && addr->comment) {
		snprintf(namebuf, sizeof(namebuf), "%s", rfc1522_decode(addr->name, charset));
		snprintf(addr_line, sizeof(addr_line), "%s%s <%s> (%s)", prefix ? prefix : "" , namebuf, addrbuf, rfc1522_decode(addr->comment, charset));
	} else
		if(!addr->name && !addr->comment)
		snprintf(addr_line, sizeof(addr_line), "%s%s", prefix ? prefix : "" , addrbuf);
	else
		if(addr->comment)
		snprintf(addr_line, sizeof(addr_line), "%s(%s) <%s>", prefix ? prefix : "" , rfc1522_decode(addr->comment, charset), addrbuf);
	else
		snprintf(addr_line, sizeof(addr_line), "%s%s <%s>", prefix ? prefix : "" , rfc1522_decode(addr->name, charset), addrbuf);

	return addr_line;
}

char *
get_full_addr_line(mail_addr *addr) {
	static char addr_line[255];

	strcpy(addr_line, "<UNKNOWN>");

	if(!addr)
		return addr_line;

	if(!addr->addr)
		return addr_line;

	if(!addr->name && !addr->comment)
		snprintf(addr_line, sizeof(addr_line), "%s", addr->addr);
	else
		if(addr->name && addr->comment)
		snprintf(addr_line, sizeof(addr_line), "%s <%s> (%s)", addr->name, addr->addr, addr->comment);
	else
		if(addr->name)
		snprintf(addr_line, sizeof(addr_line), "%s <%s>", addr->name, addr->addr);
	else
		snprintf(addr_line, sizeof(addr_line), "(%s) <%s>", addr->comment, addr->addr);

	return addr_line;
}

char *
get_short_addr_line(mail_addr *addr) {
	int iFolderType = -1;

	if(!addr)
		return "unknown";

	if(addr->name)
		return rfc1522_decode(addr->name, &iFolderType);

	if(addr->comment)
		return rfc1522_decode(addr->comment, &iFolderType);

	return addr->addr;
}

char *
get_addr_line(mail_addr *addr) {
	static char addr_line[255];

	strcpy(addr_line, "unknown");

	if(!addr)
		return addr_line;

	if(addr->name && *addr->name)
		snprintf(addr_line, sizeof(addr_line), "%s", addr->name);
	else
		snprintf(addr_line, sizeof(addr_line), "%s", addr->addr);

	return addr_line;
}

char *
get_addr_addr(mail_addr *addr) {
	static char addr_line[255];

	snprintf(addr_line, sizeof(addr_line), "%s", addr ? addr->addr : "unknown");
	return addr_line;
}

void print_news_addr(struct _news_addr *addr, char *str, FILE* file) {
	int i = 0, alen = 0;

	if(!file)
		return;

	if(str) {
		fprintf(file, "%s: ", str);
		alen += (strlen(str) + 2);
	}

	while(addr) {
		if(i && (alen + strlen(addr->name) > 78)) {
			fputs(",\n ", file);
			alen = 1;
		} else
			if(i) {
			fputs(",", file);
			alen += 1;
		}

		fprintf(file, "%s", addr->name);
		alen += strlen(addr->name);

		i++;
		addr = addr->next;
	}

	if(alen)
		fputc('\n', file);

	return;
}

void
print_addr(mail_addr *addr,char *str, FILE* file, int charset) {
	u_long st, st1;
	u_int alen;
	int quote;
	char *p, anam[255];

	if(!addr)
		return;

	if(file == NULL)
		return;

	st = ftell(file);

	if((charset >= -1) && addr->name) {
		snprintf(anam, sizeof(anam), "%s", rfc1522_encode(addr->name, charset, -1));
		p = anam;
	} else
		p = addr->name;

	if(p && strpbrk(p, ".,;\'\"()<>") &&
	   (*p != '\'') && (*p != '\"'))
		quote = 1;
	else
		quote = 0;

	if(addr->name && addr->comment)
		fprintf(file, "%s: %s%s%s <%s> (%s)", str, quote ? "\"" : "", p, quote ? "\"" : "", addr->addr, (charset >= -1) ? rfc1522_encode(addr->comment, charset, -1) : addr->comment);
	else
		if(addr->name)
		fprintf(file, "%s: %s%s%s <%s>", str, quote ? "\"" : "", p, quote ? "\"" : "" , addr->addr);
	else
		if(addr->comment)
		fprintf(file, "%s: (%s) <%s>", str, (charset >= -1) ? rfc1522_encode(addr->comment, charset, -1) : addr->comment , addr->addr);
	else
		fprintf(file, "%s: %s", str, addr->addr);

	addr =  addr->next_addr;

	if(!addr) {
		fputc('\n', file);
		return;
	}

	while(addr) {
		if((charset >= -1) && addr->name) {
			snprintf(anam, sizeof(anam), "%s", rfc1522_encode(addr->name, charset, -1));
			p = anam;
		} else
			p = addr->name;

		alen = strlen(addr->addr);

		if(p && strpbrk(p, ".,;\'\"()<>") &&
		   (*p != '\'') && (*p != '\"')) {
			quote = 1;
			alen += 2;
		} else
			quote = 0;

		if(addr->comment)
			alen += (strlen(addr->comment) + 4);

		if(p)
			alen += (strlen(p) + 4);

		st1 = ftell(file);
		if((st1 - st + alen + 2) >= 80) {
			st = st1;
			fputs(",\n ", file);
		} else
			fputs(", ", file);

		if(addr->name && addr->comment)
			fprintf(file, "%s%s%s <%s> (%s)", quote ? "\"" : "", p , quote ? "\"" : "", addr->addr, (charset >= -1) ? rfc1522_encode(addr->comment, charset, -1) : addr->comment);
		else
			if(addr->name)
			fprintf(file, "%s%s%s <%s>", quote ? "\"" : "", p, quote ? "\"" : "", addr->addr);
		else
			if(addr->comment)
			fprintf(file, "(%s) <%s>", (charset >= -1) ? rfc1522_encode(addr->comment, charset, -1) : addr->comment , addr->addr);
		else
			fprintf(file, "%s", addr->addr);

		addr =  addr->next_addr;
	}

	fputc('\n', file);
	return;
}

void chld_handler() {
	int i;
	wait(&i);
}


void send_message_finalizer(struct _mail_msg *msg, int code) {
	switch(code) {
		case -1:
		case -2:
			msg->flags |= M_SFAILED;
			msg->status |= CHANGED;
			outbox->move(msg, outbox);
		return;
		case -3:
			msg->status |= CHANGED;
			msg->update(msg);
	}

	if(process_fcc_list(msg) == -1) {
		msg->flags |= M_SFAILED;
		msg->status |= CHANGED;
		msg->update(msg);
	}

	msg->status |= (MOUTGOING|CHANGED);
	apply_rule(msg, 0);

#ifdef WITH_THREADS
	//Only smtp sends use threads so far..
	if(Config.getInt("smtpsend", 0) == 1)
			pthread_mutex_unlock(&send_mutex);
#endif
}

int
send_message(struct _mail_msg *msg) {
	int sstat;
	struct _pop_src *popsrc;
	char buf[255];
	char id[32];
	time_t lt = time(NULL);

#ifdef WITH_THREADS
	pthread_t send_thread;
#endif

	if(!msg)
		return -1;

	if(!msg->header)
		return -1;

	if(!msg->header->To &&
	   !msg->header->News) {
		display_msg(MSG_WARN, "send", "Must specify at least one recipient");
		return -1;
	}

	if(!msg->header->From) {
		display_msg(MSG_WARN, "send", "Can not send mail from Ghost!");
		return -1;
	}

	if(msg->status & C_DELIVERY) {
		replace_field(msg, "Return-Receipt-To", msg->header->From->addr);
		msg->status &= ~C_DELIVERY;
	}

	if(msg->status & C_READ) {
		replace_field(msg, XFRECEIPT, msg->header->From->addr);
		replace_field(msg, "X-Chameleon-Return-To", msg->header->From->addr);
		replace_field(msg, "X-Confirm-Reading-To", msg->header->From->addr);
		replace_field(msg, "Disposition-Notification-To", msg->header->From->addr);
		msg->status &= ~C_READ;
	}

	if(offline) {
		msg->status |= CHANGED;
		if(outbox->move(msg, outbox) == -1)
			return -1;
		return 0;
	}

	if(msg->get_file(msg) == NULL)
		return -1;

	delete_all_fields(msg, "Sender");
	discard_address(msg->header->Sender);
	if(Config.getInt("setsender", 1))
		msg->header->Sender = get_address(sender_name, ADDR_GET_FIRST);
	else
		msg->header->Sender = NULL;

	msg->header->snt_time = msg->header->rcv_time =  time(NULL);
	replace_field(msg, "Date", get_arpa_date(time(NULL)));

	set_priority_by_flags(msg);
	snprintf(buf, sizeof(buf), "XFMail %s%s on %s", VERSION, DEBUGVERSION, XF_OS);
	replace_field(msg, "X-Mailer", buf);

	if(!find_field(msg, "Message-ID")) {
		strftime(id, 31, "%Y%m%d%H%M%S", localtime(&lt));
		snprintf(buf, sizeof(buf), "<XFMail.%s.%s>", id, msg->header->From->addr);
		add_field(msg, "Message-ID", buf);
	}

	sprintf(buf, "%d", (int)(msg->msg_len - msg->header->header_len));
	replace_field(msg, MIME_C_LENGTH, buf);

	if(msg->header->News &&
	   !(msg->flags & M_SFAILED)) {
		if((sstat = nntp_send_message(msg)) == -1) {
			msg->flags |= M_SFAILED;
			msg->update(msg);
			return -1;
		}
	}

	if(msg->header->To == NULL) {
		send_message_finalizer(msg, -3);
		return -1;
	}

	/* Save back changed headers so that Message-ID etc is available
	   for reference when threating. SHG */
	msg->status |= CHANGED;
	msg->update(msg);

	switch(Config.getInt("smtpsend", 0)) {
		case 0:          
			sstat = sendmail_send_message(msg);
			send_message_finalizer(msg,sstat);
			break;

		case 1:
#ifdef WITH_THREADS
			pthread_create(&send_thread, NULL, smtp_send_message, (void*)msg);
			pthread_detach(send_thread);
            cerr << "SMTP background send started on thread " << send_thread << endl; 
#else
			smtp_send_message((void*)msg);
#endif
			break;

		case 2:
			if((popsrc = get_popsrc_by_name(const_cast<char *>(Config.get("smtppopsrc", "").c_str()))) == NULL) {
				display_msg(MSG_WARN, "send", "POP account is not defined or\ndefined incorrectly");
				sstat = -1;
				break;
			}
			sstat = pop_send_message(popsrc, msg);
			send_message_finalizer(msg,sstat);
			break;

		default:
			sstat = sendmail_send_message(msg);
			send_message_finalizer(msg,sstat);
			break;
	}

	return 0;
}

void
sendmail_exit(struct _proc_info *pinfo) {
	if(pinfo && pinfo->u_data) {
		unlink((char *)pinfo->u_data);
		free(pinfo->u_data);
	}
}

int
sendmail_send_message(struct _mail_msg *msg) {
	struct _proc_info pinfo;
	char mailbuf[2048];
	FILE *m_tmp_fd;
	int m_tmp, res;
	char m_tmp_file[255], options[127];
	struct _mail_addr *addr;

	strcpy(m_tmp_file, get_temp_file("send"));

	if((m_tmp_fd = fopen(m_tmp_file, "w")) == NULL) {
		display_msg(MSG_WARN, "send", "Can not open file %s", m_tmp_file);
		return -1;
	}
	msg->print(msg, m_tmp_fd, 1);
	fclose(m_tmp_fd);

	if((m_tmp = open(m_tmp_file, O_RDONLY)) < 0) {
		display_msg(MSG_WARN, "send", "Can not open file %s", m_tmp_file);
		return -1;
	}

	init_pinfo(&pinfo);
	pinfo.wait =  Config.getInt("smtpbg", 0) ? WAIT_ASYNC : WAIT_BG;
	pinfo.u_data = strdup(m_tmp_file);
	pinfo.ul_data = 0L;
	pinfo.handle = sendmail_exit;
	pinfo.ifd = m_tmp;

	snprintf(options, sizeof(options), "%s", Config.get("sendmailopt", "-i").c_str());
	if(find_field(msg, "Return-Receipt-To") && Config.getInt("smtpdsn", 0)) {
		delete_all_fields(msg, "X-DSN-Envid");
		delete_all_fields(msg, "Return-Receipt-To");
		strcat(options, " ");
		strcat(options,  Config.get("sendmaildsn", "-R hdrs -N failure,delay,success").c_str());
	}
	snprintf(mailbuf, sizeof(mailbuf), "%s %s",  Config.get("sendmail", _PATH_SENDMAIL).c_str(), options);
	addr = msg->header->To;
	while(addr) {
		if((strlen(addr->addr) + strlen(mailbuf) + 2) >= sizeof(mailbuf)) {
			display_msg(MSG_WARN, "send", "address list too long");
			close(m_tmp);
			return -1;
		}
		strcat(mailbuf, " ");
		strcat(mailbuf, addr->addr);
		addr = addr->next_addr;
	}

	addr = msg->header->Cc;
	while(addr) {
		if((strlen(addr->addr) + strlen(mailbuf) + 2) >= sizeof(mailbuf)) {
			display_msg(MSG_WARN, "send", "address list too long");
			close(m_tmp);
			return -1;
		}
		strcat(mailbuf, " ");
		strcat(mailbuf, addr->addr);
		addr = addr->next_addr;
	}

	addr = msg->header->Bcc;
	while(addr) {
		if((strlen(addr->addr) + strlen(mailbuf) + 2) >= sizeof(mailbuf)) {
			display_msg(MSG_WARN, "send", "address list too long");
			close(m_tmp);
			return -1;
		}
		strcat(mailbuf, " ");
		strcat(mailbuf, addr->addr);
		addr = addr->next_addr;
	}

	res = exec_child(mailbuf, &pinfo);
	if(res == -1) {
		sendmail_exit(&pinfo);
		return -2;
	}

	return 0;
}

void
update_message_length(struct _mail_msg *msg) {
	FILE *mfd;
	char buf[255];

	if(!msg)
		return;

	if((mfd = fopen(msg->get_file(msg), "r")) == NULL)
		return;

	if(msg->header) {
		while(fgets(buf, 255, mfd)) {
			strip_newline(buf);
			if(strlen(buf) < 1) {
				msg->header->header_len = ftell(mfd);
				break;
			}
		}
	}

	if(fseek(mfd, 0L, SEEK_END) != -1)
		msg->msg_len = ftell(mfd);

	fclose(mfd);
	return;
}

int
set_message_file(struct _mail_msg *msg, char *file) {
	int ohflags;
	FILE *mfd, *fd;
	char buf[255];
	struct _mail_msg *msg1;
	int closef = 0;

	if(!msg || !file)
		return -1;

	if(!strcmp(file, "-"))
		fd = stdin;
	else {
		if((fd = fopen(file, "r")) == NULL) {
			display_msg(MSG_WARN, "Can not open file", "%s", file);
			return -1;
		}
		closef = 1;
	}

	if(unlink(msg->get_file(msg)) == -1) {
		display_msg(MSG_WARN, "Cannot delete old copy of message", "%s", msg->get_file(msg));
		if(closef)
			fclose(fd);
		return -1;
	}

	if((mfd = fopen(msg->get_file(msg), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", msg->get_file(msg));
		if(closef)
			fclose(fd);
		return -1;
	}

	while(fgets(buf, 255, fd))
		fputs(buf, mfd);

	if(closef)
		fclose(fd);
	fclose(mfd);

	ohflags = msg->header->flags;
	if((msg1 = get_message(msg->num, get_file_folder(msg))) == NULL) {
		display_msg(MSG_WARN, "update file", "The message is invalid");
		return -1;
	}
	msg1->get_header(msg1);

	msg_cache_del(msg);

	if(msg->msg_body)
		msg->free_text(msg);
	discard_message_header(msg);
	if(msg->mime)
		discard_mime(msg->mime);
	msg->mime = NULL;
	discard_message_header(msg);

	msg->header = msg1->header;
	msg->header->flags = ohflags;
	msg->msg_len = msg1->msg_len;

	msg1->header = NULL;
	discard_message(msg1);

	return 0;
}

int
set_message_text(struct _mail_msg *msg, char *file) {
	FILE *mfd, *fd;
	char buf[255];
	int closef = 0;

	if(!msg || !file)
		return -1;

	if(!strcmp(file, "-"))
		fd = stdin;
	else {
		if((fd = fopen(file, "r")) == NULL) {
			display_msg(MSG_WARN, "Can not open file", "%s", file);
			return -1;
		}
		closef = 1;
	}

	if(unlink(msg->get_file(msg)) == -1) {
		display_msg(MSG_WARN, "Cannot delete old copy of message", "%s", msg->get_file(msg));
		if(closef)
			fclose(fd);
		return -1;
	}

	if((mfd = fopen(msg->get_file(msg), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", msg->get_file(msg));
		if(closef)
			fclose(fd);
		return -1;
	}

	print_message_header(msg, mfd);

	msg->header->header_len = ftell(mfd);
	while(fgets(buf, 255, fd))
		fputs(buf, mfd);

	msg->msg_len = ftell(mfd);
	msg->status |= MMODIFIED;
	if(closef)
		fclose(fd);
	fclose(mfd);

	return 0;
}

int
update_message_text(struct _mail_msg *msg, char *text) {
	FILE *mfd;
	int i;

	if(!msg)
		return -1;

	if(!text)
		return -1;

	if(unlink(msg->get_file(msg)) == -1) {
		display_msg(MSG_WARN, "Cannot delete old copy of message", "%s", msg->get_file(msg));
		return -1;
	}

	if((mfd = fopen(msg->get_file(msg), "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", msg->get_file(msg));
		return -1;
	}

	print_message_header(msg, mfd);
	i = 0;

	fflush(mfd);
	msg->header->header_len = ftell(mfd);

	while(text[i] != '\0') {
		fputc(text[i], mfd);
		i++;
	}

	fflush(mfd);
	msg->msg_len = ftell(mfd);
	msg->status |= MMODIFIED;

	fclose(mfd);
	return 0;
}

void
update_message_status(struct _mail_msg *msg) {
	char buf[255];
	int nl, oflags;
	u_long offset;
	FILE *mfd;

	if(!msg || !msg->header)
		return;

	if(msg->flags == msg->header->flags)
		return;

	if((mfd = fopen(msg->get_file(msg), "r+")) == NULL)
		return;

	offset = 0L;
	while(fgets(buf, 255, mfd)) {
		nl = strip_newline(buf);
		if(strlen(buf) < 1)
			break;
		if(!strncmp(buf, STATUS_FIELD, strlen(STATUS_FIELD))) {
			offset += strlen(STATUS_FIELD);
			if(fseek(mfd, offset, SEEK_SET) == -1) {
				fclose(mfd);
				return;
			}
			switch(nl) {
				case 0:
					fprintf(mfd, ": %04X", msg->flags & 0xFFFF);
					break;

				case 1:
					fprintf(mfd, ": %04X\n", msg->flags & 0xFFFF);
					break;

				case 2:
					fprintf(mfd, ": %04X\r\n", msg->flags & 0xFFFF);
					break;
			}
			fclose(mfd);
			if(!(msg->flags & UNREAD))
				touch_message(msg);
			msg->header->flags = msg->flags;
			cache_msg(msg);
			msg->status |= MMODIFIED;
			return;
		}
		offset = ftell(mfd);
	}

	fclose(mfd);
	oflags = msg->flags;
	msg->header->flags = msg->flags;
	msg->get_header(msg);
	msg->flags = oflags;
	msg->status |= CHANGED;
	update_message(msg);

	return;
}

void
print_header_field(head_field *fld, FILE *file, int encode) {
	char *p, *p1, c;
	int nlen;

	fputs(fld->f_name, file);
	fputs(": ", file);
	p = encode ? rfc1522_encode(fld->f_line, -1, -1) : fld->f_line;
	nlen = FLD_BREAK - (strlen(fld->f_name) + 2);

	while(strlen(p) > nlen) {
		c = p[nlen];
		p[nlen] = '\0';
		if((p1 = strstr(p, "; ")) ||
		   (p1 = strstr(p, ", ")) ||
		   (p1 = strrchr(p, ' '))) {
			p[nlen] = c;
			if(*p1 != ' ')
				p1++;

			nlen = (p1 - p);
			if(nlen <= 0)
				nlen = 1;

			if((nlen < (FLD_BREAK / 8)) ||
			   ((strlen(p) - nlen) < (FLD_BREAK / 8))) {
				fwrite(p, nlen, 1, file);
				p += nlen;
				nlen = FLD_BREAK;
				continue;
			}
			fwrite(p, nlen, 1, file);
			p1++;
			p = p1;
			fputc('\n', file);
			fputc(' ', file);
			nlen = FLD_BREAK - 1;
		} else {
			p[nlen] = c;
			fwrite(p, nlen, 1, file);
			p += nlen;
			nlen = FLD_BREAK;
		}
	}

	fputs(p, file);
	fputc('\n', file);

	return;
}

void
print_message_header(struct _mail_msg *msg, FILE *file) {
	head_field *hf;
	int hasdate = 0;

	if(!msg)
		return;

	msg->get_header(msg);

	if(msg->header) {
		hf = msg->header->other_fields;
		while(hf) {
			print_header_field(hf, file, 0);
			if(!strcasecmp(hf->f_name, "Date"))
				hasdate = 1;
			hf = hf->next_head_field;
		}

		print_fcc_list(msg, file);
		if(!hasdate)
			fprintf(file,"Date: %s\n",get_arpa_date(msg->header->snt_time));
		fprintf(file, "%s: %04X\n", STATUS_FIELD, msg->flags & 0xFFFF);
		print_addr(msg->header->Sender, "Sender", file, -2);
		print_addr(msg->header->From, "From", file, -2);
		print_addr(msg->header->To, "To", file, -2);
		if(msg->header->News)
			print_news_addr(msg->header->News, "Newsgroups", file);
		if(msg->header->Subject)
			fprintf(file, "Subject: %s\n", msg->header->Subject);

		print_addr(msg->header->Cc, "Cc", file, -2);
		print_addr(msg->header->Bcc, "Bcc", file, -2);

	}
	fputc('\n', file);

	return;

}

void
lpr_exit(struct _proc_info *pinfo) {
	if(pinfo && pinfo->u_data) {
		unlink((char *)pinfo->u_data);
		free(pinfo->u_data);
	}
}

char *
get_print_command(char *file) {
	static char printcmd[255];
	char printdef[255];
    string sPrint;
    string sPrinter;

    sPrinter = Config.get("printer", "lp");

#ifdef  _PRINT_LP
	snprintf(printdef, sizeof(printdef), "%s -d$p $f", _PATH_LPR);
#else
	snprintf(printdef, sizeof(printdef), "%s -P$p $f", _PATH_LPR);
#endif

	if(file == NULL) {
		snprintf(printcmd, sizeof(printcmd), "%s", printdef);
		return printcmd;
	}

	sPrint =  Config.get("print", printdef);

    findreplace(sPrint, "$$", "$");
    findreplace(sPrint, "$p", sPrinter);
    findreplace(sPrint, "$f", file);

    snprintf(printcmd, sizeof(printcmd), "%s", sPrint.c_str());
    cout << "PrintCmd: " << printcmd << endl;

	return printcmd;
}

void
lpr_message(struct _mail_msg *msg) {
	struct _proc_info pinfo;
	char m_tmp_file[255];
	int pflags;

	if(!msg)
		return;

	if(Config.getInt("printheader", 0) == 1) {
		pflags = SAVEPART_HEADER|SAVEPART_FHDR;
	} else {
		pflags = SAVEPART_HEADER;
	}

	strcpy(m_tmp_file, get_temp_file("lpr"));
	if(save_part(msg, get_text_part(msg), m_tmp_file, pflags) == -1) {
		display_msg(MSG_WARN, "lpr", "Can not print message!");
		unlink(m_tmp_file);
		return;
	}

	init_pinfo(&pinfo);
	pinfo.wait = WAIT_BG;
	pinfo.u_data = strdup(m_tmp_file);
	pinfo.ul_data = 0L;
	pinfo.handle = lpr_exit;

	if(exec_child(get_print_command(m_tmp_file), &pinfo) == -1)
		lpr_exit(&pinfo);

	return;
}

int
strip_when_send(head_field *fld) {
	int i = 0;
	char *p;

	while((p = stripfields[i++]) != NULL)
		if(!strcasecmp(fld->f_name, p) || !strncasecmp(fld->f_name, "XF-", 3))
			return 1;

	if(!strcmp(fld->f_name, "Message-ID") &&
	   ! Config.getInt("setmsgid", 1))
			return 1;
	return 0;
}

int
print_message(struct _mail_msg *msg, FILE *file, int send) {
	head_field *hf;
	int charset , i, hasdate = 0;
	struct _mime_msg *mime;

	if(!msg)
		return -1;

	msg->get_file(msg);
	if(!send || ! Config.getInt("encheader", 1))
			charset = -2;
	else {
		charset = -1;
		if((mime = get_text_part(msg)) != NULL) {
			i = 0;
			while(supp_charsets[i].charset_code != CHAR_UNKNOWN) {
				if(mime->charset->charset_code == supp_charsets[i].charset_code) {
					charset = i;
					break;
				}
				i++;
			}
		}
	}

	if(msg->header) {
		hf = msg->header->other_fields;
		while(hf) {
			if(!(send && strip_when_send(hf)))
				print_header_field(hf, file, send);
			if(!strcasecmp(hf->f_name, "Date"))
				hasdate = 1;
			hf = hf->next_head_field;
		}
		if(!send) {
			print_fcc_list(msg, file);
			fprintf(file, "%s: %04X\n", STATUS_FIELD, msg->flags & 0xFFFF);
		}
		if(!hasdate)
			fprintf(file,"Date: %s\n",get_arpa_date(msg->header->snt_time));

		print_addr(msg->header->Sender, "Sender", file, charset);
		print_addr(msg->header->From, "From", file, charset);
		print_addr(msg->header->To, "To", file, charset);
		if(msg->header->News)
			print_news_addr(msg->header->News, "Newsgroups", file);
		if(msg->header->Subject)
			fprintf(file, "Subject: %s\n", (charset >= -1) ? rfc1522_encode(msg->header->Subject, charset, -1) : msg->header->Subject);

		print_addr(msg->header->Cc, "Cc", file, charset);
		print_addr(msg->header->Bcc, "Bcc", file, charset);
	} else
		return -1;

	fprintf(file, "\n");
	if(fflush(file) == EOF) {
		if(errno == ENOSPC)
			display_msg(MSG_WARN, "write message", "DISK FULL!");
		else
			display_msg(MSG_WARN, "write message", "Failed to write");
		return -1;
	}

	if(msg->print_body(msg, file) != 0)
		return -1;

	return 0;
}

int
print_message_body(struct _mail_msg *msg, FILE *file) {
	long len;
	char *ptr, *p;
	int body;

	if(!msg)
		return -1;

	if(file == NULL)
		return -1;

	body = 0;

	if((msg->msg_body == NULL) ||
	   (msg->msg_body_len < msg->msg_len)) {
		msg->free_text(msg);
		if(msg->get_text(msg, NULL) == -1) {
			display_msg(MSG_WARN, "print", "Failed to access message");
			return -1;
		}
		body = 1;
	}

	if((msg->msg_len - msg->msg_body_len) > 1)
		return -1;

	len = msg->msg_len - msg->header->header_len;
	ptr = msg->msg_body + msg->header->header_len;

	while((len > 0) &&
		  ((p = (char*) memchr(ptr, '\n', len)) != NULL)) {
		p++;

		/*
			if (!strncmp(ptr, "From ", 5))
				fputc('>', file);
		*/

		if(fwrite(ptr, (p - ptr), 1, file) != 1) {
			display_msg(MSG_WARN, "print", "Failed to write message");
			return -1;
		}

		len -= (p - ptr);
		ptr = p;
	}

	if(len > 0) {
		fwrite(ptr, len, 1, file);
		fputc('\n', file);
	}

	if(fflush(file) == EOF) {
		if(errno == ENOSPC)
			display_msg(MSG_WARN, "write message", "DISK FULL!");
		else
			display_msg(MSG_WARN, "write message", "Failed to write");
		return -1;
	}

	if(body)
		msg->free_text(msg);

	return 0;
}

char *
get_temp_file(char *base) {
	static char buf[255];
	struct timeval tp;

	gettimeofday(&tp, NULL);
	snprintf(buf, sizeof(buf), "%s/xf%s%04lu%04lu.%d",tmpdir, base ? base : "mail",
			 tp.tv_sec%1000, tp.tv_usec%1000 , (int)getpid());
	return buf;
}

void
touch_message(struct _mail_msg *msg) {
	struct timeval tv[2];
	char *mfile;

	if((mfile = msg->get_file(msg)) == NULL)
		return;

	if(msg->flags & UNREAD) {
		if(gettimeofday(&tv[1], NULL) != 0)
			return;
		tv[0].tv_sec = tv[1].tv_sec - 1;
		tv[0].tv_usec = tv[1].tv_usec;
	} else {
		if(gettimeofday(&tv[0], NULL) != 0)
			return;
		tv[1].tv_sec = tv[0].tv_sec - 1;
		tv[1].tv_usec = tv[0].tv_usec;
	}

	utimes(mfile, tv);

	return;
}

int
update_message(struct _mail_msg *msg) {
	FILE *mfd;
	char buf[255];
	long h_len;

	if(!msg)
		return -1;

	if(msg->flags & M_TEMP)
		return 0;

	if(!(msg->status & CHANGED)) {
		update_message_status(msg);
		return 0;
	}

	msg_cache_del(msg);
	msg->status &= ~CHANGED;
	msg->status |= MMODIFIED;
	msg->header->flags = msg->flags;

	if(msg->msg_body)
		msg->free_text(msg);

	strcpy(buf, get_temp_file("upd"));

	if((mfd = fopen(buf, "w")) == NULL) {
		display_msg(MSG_WARN, "update", "Can not open file %s", buf);
		return -1;
	}

	print_message_header(msg, mfd);
	if(fflush(mfd) == EOF) {
		display_msg(MSG_WARN, "update", "Can not update message");
		fclose(mfd);
		return -1;
	}

	h_len = ftell(mfd);

	if(msg->print_body(msg, mfd) != 0) {
		fclose(mfd);
		return -1;
	}

	if(fflush(mfd) == EOF) {
		display_msg(MSG_WARN, "update", "Can not update message");
		fclose(mfd);
		return -1;
	}

	msg->msg_len = ftell(mfd);
	msg->header->header_len = h_len;

	if(fclose(mfd) == EOF) {
		display_msg(MSG_WARN, "update", "Can not update message");
		return -1;
	}

	if(do_move(buf, msg->get_file(msg)) != 0) {
		display_msg(MSG_WARN, "update", "Can not update message");
		return -1;
	}

	unlink(buf);
	touch_message(msg);
	cache_msg(msg);

	return 0;
}

int
unlink_message(struct _mail_msg *msg) {
	struct _mail_msg *msg1;

	if(!msg || !msg->folder || !msg->folder->messages)
		return 0;

	msg1 = msg->folder->messages;
	if(msg == msg->folder->messages)
		msg->folder->messages = msg->folder->messages->next;
	else {
		while(msg1) {
			if(msg1->next == msg) {
				msg1->next = msg->next;
				break;
			}
			msg1 = msg1->next;
		}
	}

	if(msg1) {
		if((msg->flags & UNREAD) && msg->folder->unread_num)
			msg->folder->unread_num--;

		if(msg->folder->num_msg)
			msg->folder->num_msg--;

		return 1;
	}

	return 0;
}

int
delete_message(struct _mail_msg *msg) {
	if(!msg)
		return -1;

	msg->status &= ~DELETED;

	if(msg->status & LOCKED)
		return -1;

	if((msg->flags & H_ONLY) &&
	   get_msg_popsrc(msg)) {
		if(display_msg(MSG_QUEST, NULL, "Delete message from server?"))
			pop_delmsg_by_uidl(get_msg_popsrc(msg), msg);
	}

	msg->folder->status |= FRESCAN;
	if(!(msg->folder->status & NOTRASH) &&
	   !(msg->folder->flags & FNTRASH) &&
	   !(msg->status & DELPERM))
		return trash->move(msg, trash);

	if(unlink(msg->get_file(msg)) == -1) {
		display_msg(MSG_WARN, "delete", "Can not remove file %s", msg->get_file(msg));
		return -1;
	}

	if(((folder_sort & 0x0f) == BY_MSGNUM) ||
	   ( ( (folder_sort & 0x0f) == BY_UNREAD) &&
		 (msg->flags & UNREAD) ))
		folder_sort &= ~FLD_SORTED;

	msg_cache_del(msg);
	unlink_message(msg);
	discard_message(msg);

	return 0;
}

int move_to_folder(struct _mail_msg *msg, struct _mail_folder *folder) {
	struct _mail_folder *pfold;
	struct _mail_msg *msg1;
	long newnum;
	char new_path[255];
	FILE *nfd;

	if(!msg || !folder || !(folder->type & F_MH))
		return -1;

	msg->status &= ~MOVED;

	if((msg->status & LOCKED) ||
	   (folder->status & FRONLY))
		return -1;

	if(msg->folder) {
		if(msg->folder->status & FRONLY)
			return -1;
		msg_cache_del(msg);
		if((msg->folder == folder) && !(msg->status & CHANGED)) {
			if(msg->update(msg) != 0)
				return -1;
			touch_message(msg);
			return 0;
		}
		msg->folder->status |= FRESCAN;
	}
	folder->status |= FRESCAN;

	if((newnum = get_new_name(folder)) == -1) {
		display_msg(MSG_WARN, "move","Folder %s is full", folder->name(folder));
		return -1;
	}

	msg->flags &= ~M_TEMP;

	snprintf(new_path, sizeof(new_path), "%s/%ld", folder->fold_path, newnum);
	if((msg->status & CHANGED) ||
	   (msg->folder && !(msg->folder->type & F_MH))) {
		if((nfd = fopen(new_path, "w")) == NULL) {
			display_msg(MSG_WARN, "move", "Can not open %s", new_path);
			return -1;
		}
		if(msg->print(msg, nfd, 0) != 0) {
			display_msg(MSG_WARN, "move", "Can not write message");
			return -1;
		}

		if(fclose(nfd) == EOF) {
			display_msg(MSG_WARN, "move", "Can not write to message file\n%s", new_path);
			return -1;
		}

		if(msg->folder->type & F_MH) {
			unlink(msg->get_file(msg));
			msg->header->flags = msg->flags;
		} else {
			if(msg->update(msg) != 0) {
				display_msg(MSG_WARN, "move", "Can not update message");
				return -1;
			}
		}
	} else {
		if(msg->update(msg) != 0) {
			display_msg(MSG_WARN, "move", "Can not update message");
			return -1;
		}

		if(do_move(msg->get_file(msg), new_path) != 0) {
			display_msg(MSG_WARN, "move", "Can not move message");
			return -1;
		}
	}

	if(msg->folder && (msg->folder->type & F_MH))
		unlink_message(msg);
	else {
		if(msg->folder) {
			msg1 = copy_msg(msg);
			local_message(msg1);
			msg->status |= (DELETED|DELPERM);
			msg->mdelete(msg);
			msg = msg1;
			msg->flags &= ~H_ONLY;
		}
	}

	msg->folder = folder;
	msg->num = newnum;
	msg->uid = newnum;
	msg->real_uid = newnum;

	touch_message(msg);

	folder->num_msg++;
	if(msg->flags & UNREAD)
		folder->unread_num++;
	if(msg->status & RECENT) {
		msg->status &= ~RECENT;
		folder->status |= FRECNT;
		for(pfold = folder->pfold; pfold; pfold = pfold->pfold)
			pfold->status |= FMRKTMP;
	}

	if(folder->status & OPENED) {
		msg->next = folder->messages;
		folder->messages = msg;
		msg->status &= ~CHANGED;
		update_message_length(msg);
		discard_mime(msg->mime);
		msg->mime = NULL;
		if(msg->msg_body)
			msg->free_text(msg);
	} else
		discard_message(msg);

	folder->status &= ~SORTED;

	if(((folder_sort & 0x0f) == BY_MSGNUM) ||
	   ( ( (folder_sort & 0x0f) == BY_UNREAD) &&
		 (msg->flags & UNREAD) ))
		folder_sort &= ~FLD_SORTED;

	return 0;

}

void
convert_fields(struct _mail_msg *msg) {
	struct _head_field *fld;
	time_t stime;
	char *p;

	set_priority_by_headers(msg);

	if((fld = find_field(msg, "Apparently-To")) != NULL) {
		msg->header->To = get_address(fld->f_line, 0);
		delete_field(msg, fld);
	}

	if((fld = find_field(msg, MIME_C_TYPE)) != NULL) {
		if((p = get_fld_param(fld, "protocol")) != NULL) {
			if(!strcasecmp(p, "application/pgp-signature"))
				msg->flags |= PGP_SIGNED;
			else
				if(!strcasecmp(p, "application/pgp-encrypted"))
				msg->flags |= PGP_ENCRYPTED;
		} else {
			if(strcasestr(fld->f_line, "application/pgp", 1))
				msg->flags |= PGP_ENCRYPTED;
		}
	}

	fld = find_field(msg, "Date");
	if((msg->header->snt_time == 0) ||
	   (fld && (parse_offt(fld->f_line) == -1))) {
		if(((fld = find_last_field(msg, "Received")) != NULL) &&
		   ((p = strrchr(fld->f_line, ';')) != NULL)) {
			p++;
			if(((stime = get_date(p)) != 0) &&
			   (parse_offt(p) != -1)) {
				msg->header->snt_time = stime;
				replace_field(msg, "X-SDate", get_arpa_date(stime));
			}
		}
	}

	return;
}

void
expand_str(struct _mail_msg *msg, char *str) {
	char buf[255];
	char *p, *p1;
	struct _head_field *fld;

	if(!msg || !str)
		return;

	if((strlen(str) < 1) || (strlen(str) > 200))
		return;

#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "C");
#endif

	buf[0] = '\0';
	p = str;
	p1 = buf;
	while(*p != '\0') {

		switch(*p) {
			case '%':
				p++;
				if((*p == '\0') || (*p == '%')) {
					*p1 = *p;
					p++;
					p1++;
					*p1 = '\0';
					break;
				}

				switch(*p) {
					case 'f':
						if(msg->header->From)
							strcpy(p1, get_short_addr_line(msg->header->From));
						else
							if(msg->header->Sender)
							strcpy(p1, get_short_addr_line(msg->header->Sender));
						else
							strcpy(p1, "unknown");
						break;

					case 'i':
						if((fld = find_field(msg, "Message-ID")))
							strncpy(p1, fld->f_line, 64);
						else
							strncpy(p1, msg->header->Subject ? msg->header->Subject : "", 64);
						p1[64] = '\0';
						break;

					case 's':
						strncpy(p1, msg->header->Subject ? msg->header->Subject : "* No Subject *", 64);
						p1[64] = '\0';
						break;

					case 't':
						strftime(p1, 48, "%T", gmtime(&msg->header->snt_time));
						break;

					case 'd':
						strftime(p1, 48, "%d-%b-%Y", gmtime(&msg->header->snt_time));
						break;

					case 'n':
						strcpy(p1, "\n");
						break;

					default:
						sprintf(p1, "%%%c", *p);
						break;
				}

				p1 += strlen(p1);
				p++;
				break;

			default:
				*p1 = *p;
				p1++;
				*p1 = '\0';

				p++;
				break;
		}
	}

#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "");
#endif
	strcpy(str, buf);

	return;
}

void
set_flags_by_status(struct _mail_msg *msg) {
	struct _head_field *fld;
	char *p;

	if(!msg)
		return;

	msg->flags |= UNREAD;

	if((fld = find_field(msg, "Status")) != NULL) {
		p = fld->f_line;
		while(*p != '\0') {
			switch(*p) {
			case 'R':
				msg->flags &= ~UNREAD;
				break;

			case 'O':
				if(p == fld->f_line)
					msg->flags |= UNREAD;
				break;

			case 'U':
				msg->flags |= UNREAD;
				break;
			}

			p++;
		}
		delete_field(msg, fld);
	}

	if((fld = find_field(msg, "X-Status")) != NULL) {
		p = fld->f_line;
		while(*p != '\0') {
			switch(*p) {
			case 'A':
				msg->flags |= ANSWERED;
				break;

			case 'F':
				msg->flags |= MARKED;
				break;
			}
			p++;
		}
		delete_field(msg, fld);
	}
}

void
set_status_by_flags(struct _mail_msg *msg) {
	char *p, nstat[4];
	struct _head_field *fld;

	nstat[0] = '\0';
	p = nstat;
	if(msg->flags & UNREAD) {
		*p++ = 'O';
		*p = '\0';
	} else {
		*p++ = 'R';
		*p++ = 'O';
		*p = '\0';
	}
	replace_field(msg, "Status", nstat);

	nstat[0] = '\0';
	p = nstat;
	if(msg->flags & ANSWERED) {
		*p++ = 'A';
		*p = '\0';
	}

	if(msg->flags & MARKED) {
		*p++ = 'F';
		*p = '\0';
	}
	if(*nstat)
		replace_field(msg, "X-Status", nstat);
	else {
		if((fld = find_field(msg, "X-Status")) != NULL)
			delete_field(msg, fld);
	}

	return;
}

int
ishebrew(u_int c) {
	return((c >= 224) && (c <= 250));
}

char *
hebrew_conv(char *str) {
	char hbuf[256];
	u_int direct;
	char *p, *s;
	u_int c;

	if(!str || (strlen(str) >= sizeof(hbuf)))
		return str;

	hbuf[0] = '\0';
	p = hbuf;
	s = str;
	direct = 0;

	while(*s != '\0') {

		c = *s & 0xff;

		if(ishebrew(c))
			direct = 1;
		else
			if(isalnum(c) ||
			   (c < 32)) {
			if(direct)
				p += strlen(p);
			direct = 0;
		}

		if(direct) {
			memcpy((p + 1), p, strlen(p) + 1);
			*p = *s;
		} else {
			*p++ = *s;
			*p = '\0';
		}

		s++;
	}
	strcpy(str, hbuf);

	return str;
}

long
calc_msg_len(struct _mail_msg *msg) {
	FILE *mfd;
	char buf[512], lastchar;
	long len, blen;

	if((mfd = fopen(msg->get_file(msg), "r")) == NULL)
		return -1;

	len = 0;
	lastchar = '\0';

	while(fgets(buf, 511, mfd)) {
		blen = strlen(buf);
		len += blen;
		if(blen && (buf[blen - 1] == '\n')) {
			if(blen > 1)
				lastchar = buf[blen - 2];

			if(lastchar != '\r')
				len++;

			lastchar = '\n';
		} else
			lastchar = blen ? buf[blen - 1] : '\0';
	}

	fclose(mfd);

	return len;
}

int
count_recipients(struct _mail_msg *msg) {
	int i = 0;
	struct _mail_addr *addr;

	if(msg == NULL) {
	//Error
		return 0;
	}

	addr = msg->header->To;
	while(addr) {
		addr = addr->next_addr;
		i++;
	}

	addr = msg->header->Cc;
	while(addr) {
		addr = addr->next_addr;
		i++;
	}

	addr = msg->header->Bcc;
	while(addr) {
		addr = addr->next_addr;
		i++;
	}

	return i;
}

struct _mail_msg *
get_unread_msg() {
	int i, opened;
	struct _mail_folder *fld;
	struct _mail_msg *msg;

	for(i = 0; i< (int)mailbox.size();i++) {
		if((mailbox[i]->unread_num > 0) &&
		   !(mailbox[i]->status & NOTRASH)) {

			fld = mailbox[i];
			opened = 0;
			if(!(fld->status & OPENED)) {
				if(fld->open(fld, FOPEN_UNREAD) == -1)
					return NULL;
				opened = 1;
			}

			msg = fld->messages;
			while(msg) {
				if((msg->flags & UNREAD) &&
				   !(msg->status & DELETED) &&
				   !(msg->status & MOVED) &&
				   !(msg->status & MNOTEXISTS))
					return msg;
				msg = msg->next;
			}

			if(opened)
				fld->close(fld);
		}
	}

	return NULL;
}

unsigned long
get_total_unread() {
	int i;
	unsigned long res;

	res = 0;
	for(i = 0; i< (int)mailbox.size();i++) {
		if(!(mailbox[i]->status & NOTRASH) &&
		   !(mailbox[i]->flags & FNCOUNT))
			res += mailbox[i]->unread_num;
	}

	return res;
}

long
get_mh_folder_uid(struct _mail_folder *fld) {
	struct stat sb;

	if(stat(fld->fold_path, &sb) != 0)
		return -1;

	fld->uid = sb.st_mtime;

	return fld->uid;
}

int load_folders_conf(char *ffile) {
	char cfile[255], buf[255], fname[MAX_FOLD_NAME_LEN];
	char *p, *p1;
	int ftype, fflags, fmsgnum, funreadnum, finvalid = 0, i;
	FILE *cfd;
	struct _mail_folder *fld = NULL;
	struct _retrieve_src *source = ffile ? get_src_by_name(ffile) : NULL;
	long fuid;

	if(ffile)
		snprintf(cfile, sizeof(cfile), "%s/.xfmfolders-%s", configdir, ffile);
	else
		snprintf(cfile, sizeof(cfile), "%s/.xfmfolders", configdir);

	if((cfd = fopen(cfile, "r+")) == NULL)
		return 0;

	if(!fgets(buf, 255, cfd))
		return 0;

	if(*buf == '|') {
		p = buf + 1;
		if(*p != '^') {
			finvalid = 1;
			display_msg(readonly ? MSG_LOG : MSG_WARN, "folder config", "file %s is invalid\nignoring message number(s)", cfile);
		}
	} else {
		display_msg(MSG_WARN, "folder config", "file %s is invalid\nAll folder options will be lost!", cfile);
		fclose(cfd);
		return 0;
	}

	while(fgets(buf, 255, cfd)) {
		if(*buf == '#')
			continue;

		if(*buf == '@') {
			fld = NULL;
			p = buf + 1;
			while((*p == ' ') || (*p == 0x09))
				p++;
			if(sscanf(p, "%254s %d %d %d %d %ld", fname, &ftype, &fflags, &fmsgnum, &funreadnum, &fuid) != 6)
				continue;
			switch(ftype) {
				case F_MH:
					if((fld = get_mh_folder_by_name(fname)) == NULL) {
						if((fld = get_mh_folder_by_path(fname)) == NULL)
							continue;
					}

					fld->flags |= fflags;

					if(fuid < fld->getuid(fld))
						display_msg(MSG_LOG, "folder config", "folder %s has been changed", fld->sname);
					else {
						if((finvalid == 0) &&
						   !(fld->status & OPENED)) {
							if(fmsgnum >= 0)
								fld->num_msg = fmsgnum;
							if(funreadnum >= 0)
								fld->unread_num = funreadnum;
						}
					}

					if(fld->num_msg >  Config.getInt("mincache", 512))
							fld->flags |= CACHED;
					break;

				case F_IMAP:
					if((fld = find_imap_folder_by_name(source ? (struct _imap_src *)source->spec : NULL, fname)) == NULL)
						continue;

					fld->flags |= fflags;
					break;

				case F_MBOX:
					if(*fname != '/') {
						snprintf(buf, sizeof(buf), "%s/%s", mailbox_path, fname);
						p = buf;
					} else
						p = fname;
					if((fld = get_mh_folder_by_path(p)) == NULL) {
						if((fld = create_mbox_folder(NULL, p)) == NULL)
							continue;
					}

					fld->flags |= fflags;

					if(fuid < fld->getuid(fld))
						display_msg(MSG_LOG, "folder config", "folder %s has been changed", fld->sname);
					else {
						if((finvalid == 0) &&
						   !(fld->status & OPENED)) {
							if(fmsgnum >= 0)
								fld->num_msg = fmsgnum;
							if(funreadnum >= 0)
								fld->unread_num = funreadnum;
						}
					}

					if(fld->num_msg >  Config.getInt("mincache", 512))
							fld->flags |= CACHED;
					break;

				default:
					display_msg(MSG_WARN, "Unknown folder type in", "%s", cfile);
					continue;
					break;
			}
			if((fld && fld->flags & FEXPND) &&
			   !(fld->status & FSKIP) &&
			   (fld->subfold)) {
				for(i = 0; i < MAX_SUBFOLDERS; i++) {
					if(fld->subfold[i])
						fld->subfold[i]->status &= ~FSKIP;
				}
			}
		} else
			if((*buf == ' ') || (*buf == 0x09)) {
			if(fld == NULL)
				continue;

			strip_newline(buf);
			p = buf;
			while((*p == ' ') || (*p == 0x09))
				p++;

			if((p1 = strchr(p, ' ')) == NULL) {
				display_msg(MSG_WARN, "Invalid parameter name in", "%s", cfile);
				continue;
			}
			*p1++ = '\0';
			while((*p1 == ' ') || (*p1 == 0x09))
				p1++;

			if(!strcmp(p, "Color:"))
				fld->color = atoi(p1);
			else
				if(!strcmp(p, "Sort:"))
				fld->sort = atoi(p1);
			else
				if(!strcmp(p, "Descr:") &&
				   !(fld->status & FDUMMY)) {
				if(fld->descr)
					free(fld->descr);
				fld->descr = strdup(p1);
			} else
				if(!strcmp(p, "Expire:") &&
				   !(fld->status & FDUMMY))
				fld->expire = atoi(p1);
			else
				if(!strcmp(p, "Alias:") &&
				   !(fld->status & FDUMMY)) {
				if((fld->sname == NULL) || strcmp(fld->sname, p1))
					fld->status |= FALIAS;
				if(fld->sname)
					free(fld->sname);
				if(strlen(p1) > MAX_FOLDER_ALIAS_NAME)
					p1[MAX_FOLDER_ALIAS_NAME] = '\0';
				fld->sname =  strdup(p1);
			} else if(!strcmp(p, "From:")) {
				if(fld->From)
					discard_address(fld->From);
				fld->From = get_address(p1, ADDR_GET_FIRST);
			} else if (strcmp(p, "To:") == 0) {
				if (fld->To)
					discard_address(fld->To);
				fld->To = get_address(p1, ADDR_GET_FIRST);
			}

		} else
			display_msg(MSG_WARN, "Invalid line in", "%s", cfile);
	}

	if(!readonly) {
		fseek(cfd, 0, SEEK_SET);
		fputs("|#\n", cfd);
	}
	fclose(cfd);

	inbox->status  |= (SYSTEM|NOINFR|FSHORTH);
	trash->status  |= (SYSTEM|NOINFR|NOTRASH);
	outbox->status |= (SYSTEM|NOINFR);
	sentm->status  |= (SYSTEM|NOINFR);
	draft->status  |= (SYSTEM|NOINFR);
	ftemp->status  |= (SYSTEM|NOINFR);
	mftemplate->status  |= (SYSTEM|NOINFR);

	outbox->flags |= FSHRECP;
	sentm->flags  |= FSHRECP;
	draft->flags  |= FSHRECP;

	return 0;
}

int
save_folders_conf(char *ffile, int ftype) {
	char tfile[255], cfile[255];
	FILE *cfd;
	int i, mboxlen = strlen(mailbox_path);
	struct _mail_folder *fld = NULL;
	char *p;

	if(readonly)
		return 0;

	if(ffile)
		snprintf(cfile, sizeof(cfile), "%s/.xfmfolders-%s", configdir, ffile);
	else
		snprintf(cfile, sizeof(cfile), "%s/.xfmfolders", configdir);

	snprintf(tfile, sizeof(tfile), "%s/.xfmfolders_tmp", configdir);

	if((cfd = fopen(tfile, "w")) == NULL) {
		display_msg(MSG_WARN, "save", "Can not open %s", tfile);
		return -1;
	}

	fputs("|^\n", cfd);

	for(i = 0; i < (int)mailbox.size(); i++) {
		fld = mailbox[i];
		if((ftype > 0) && !(fld->type & ftype))
			continue;

		if(!strncmp(fld->fold_path, mailbox_path, mboxlen) &&
		   (fld->hdelim != '\0') &&
		   (strchr(fld->fold_path + mboxlen + 1, fld->hdelim) == NULL))
			p = fld->name(fld);
		else
			p = fld->fold_path;

		fprintf(cfd, "@ %s %d %d %lu %lu %ld\n", p, fld->type, fld->flags,
				fld->num_msg, fld->unread_num, (fld->type & F_IMAP) ?  -1 : fld->getuid(fld));
		if(fld->descr)
			fprintf(cfd, " Descr: %s\n", fld->descr);
		if(fld->color != -1)
			fprintf(cfd, " Color: %d\n", fld->color);
		if(fld->sort != -1)
			fprintf(cfd, " Sort: %d\n", fld->sort);
		if(fld->expire > 0)
			fprintf(cfd, " Expire: %d\n", fld->expire);
		if(fld->sname && (fld->status & FALIAS))
			fprintf(cfd, " Alias: %s\n", fld->sname);
		if(fld->From)
			fprintf(cfd, " From: %s\n", get_full_addr_line(fld->From));
		if(fld->To)
			fprintf(cfd, " To: %s\n", get_full_addr_line(fld->To));
	}

	fclose(cfd);

	if(rename(tfile, cfile)) {
		display_msg(MSG_WARN, "Can not rename", "%s to\n%s", tfile, cfile);
		unlink(tfile);
		return -1;
	}

	return 0;
}

char *
get_msg_url(struct _mail_msg *msg) {
	static char msgurl[255];

	if(!msg || !msg->folder)
		return NULL;

	snprintf(msgurl, sizeof(msgurl), "%s %ld %lu", get_folder_full_name(msg->folder), msg->uid,
			 msg->validity(msg));

	return msgurl;
}

struct _mail_msg *
get_msg_by_url(char *url) {
	char fname[255];
	long uid;
	unsigned long validity = 0;
	struct _mail_folder *fld;
	struct _mail_msg *msg;

	if(sscanf(url, "%s %ld %lu", fname, &uid, &validity) < 2)
		return NULL;

	if((fld = get_folder_by_name(fname)) == NULL)
		return NULL;

	if((msg = fld->getmsg(fld, uid)) == NULL)
		return NULL;

	if(validity > 0) {
		if(msg->validity(msg) > validity)
			return NULL;
	}

	return msg;
}

int
expire_msgs(struct _mail_folder *folder) {
	struct _mail_msg *msg;
	int expired = 0;
	time_t tnow = time(NULL), tdelta;

	if(!folder || !(folder->status & OPENED) || (folder->expire <= 0))
		return 0;

	msg = folder->messages;
	while(msg) {
		tdelta = tnow - msg->header->rcv_time;
		if(!(msg->flags & M_DONTEXPIRE) &&
		   (tdelta > (folder->expire * 86400))) {
			msg->status |= DELETED;
			expired++;
		}
		msg = msg->next;
	}

	folder->update(folder);

	return expired;
}

void
set_msg_date(struct _mail_msg *msg, time_t rdate, time_t sdate) {
	if(rdate > 0) {
		msg->header->rcv_time = rdate;
		replace_field(msg, "X-RDate", get_arpa_date(rdate));
	}

	if(sdate > 0) {
		msg->header->snt_time = rdate;
		replace_field(msg, "Date", get_arpa_date(sdate));
		delete_all_fields(msg, "X-SDate");
	}
}

char *
strip_percent(char *buf) {
	char newbuf[255], *p, *p1;
	int elen;

	if((p = strchr(buf, '%')) == NULL)
		return buf;

	elen = sizeof(newbuf) - strlen(buf);
	if(elen <= 0)
		return buf;

	newbuf[0] = '\0';
	p1 = buf;
	do {
		if(--elen <= 0)
			return buf;
		*p = '\0';
		strcat(newbuf, p1);
		strcat(newbuf, "%%");
		p1 =  ++p;
	} while((p = strchr(p, '%')) != NULL);
	strcat(newbuf, p1);

	strcpy(buf, newbuf);
	return buf;
}

int
is_spechdr(struct _head_field *hf) {
	int k = 0;
	char *p;

	while((p = stripfields[k++]) != NULL) {
		if(!strcasecmp(hf->f_name, p))
			return 1;
	}

	k = 0;
	while((p = shorthfields[k++]) != NULL) {
		if(!strcasecmp(hf->f_name, p))
			return 1;
	}

	k = 0;
	while((p = specfields[k++]) != NULL) {
		if(!strcasecmp(hf->f_name, p))
			return 1;
	}

	return 0;
}

char *
get_msg_priority_name(struct _mail_msg *msg) {
	switch(msg->flags & (URGENT1|URGENT2)) {
		case 0:       /* Normal (3) */
			return "Normal";
			break;

		case URGENT1:     /* Low (5) */
			return "Low";
			break;

		case URGENT2:     /* High (2) */
			return "High";
			break;

		case URGENT1|URGENT2: /* Very high (1) */
			return "Very high";
			break;
	}
	return NULL;
}

int
get_msg_priority(struct _mail_msg *msg) {
	switch(msg->flags & (URGENT1|URGENT2)) {
		case 0:       /* Normal (3) */
			return 3;
			break;

		case URGENT1:     /* Low (5) */
			return 5;
			break;

		case URGENT2:     /* High (2) */
			return 2;
			break;

		case URGENT1|URGENT2: /* Very high (1) */
			return 1;
			break;
	}

	return 0;
}

void
set_priority_by_headers(struct _mail_msg *msg) {
	struct _head_field *hf;
	int pset = 0;

	msg->flags &= ~URGENT1;
	msg->flags &= ~URGENT2;

	if((hf = find_field(msg, "X-Priority")) != NULL) {
		switch(atoi(hf->f_line)) {
			case 1:
				msg->flags |= URGENT1;
				msg->flags |= URGENT2;
				pset = 1;
				break;

			case 2:
				msg->flags |= URGENT2;
				pset = 1;
				break;

			case 3:
				pset = 1;
				break;

			case 4:
				msg->flags |= URGENT1;
				pset = 1;
				break;

			case 5:
				msg->flags |= URGENT1;
				pset = 1;
				break;

			default:
				break;
		}
		delete_field(msg, hf);
	}

	if((hf = find_field(msg, "Priority")) != NULL) {
		if(!pset &&
		   !strncasecmp(hf->f_line, "urgent", 6))
			msg->flags |= (URGENT1|URGENT2);
		delete_field(msg, hf);
	}

	return;
}

void
set_priority_by_flags(struct _mail_msg *msg) {
	struct _head_field *hf;

	switch(msg->flags & (URGENT1|URGENT2)) {
		case 0:       /* Normal (3) */
			if((hf = find_field(msg, "Priority")) != NULL)
				delete_field(msg, hf);
			replace_field(msg, "X-Priority", "3 (Normal)");
			break;

		case URGENT1:     /* Low (5) */
			if((hf = find_field(msg, "Priority")) != NULL)
				delete_field(msg, hf);
			replace_field(msg, "X-Priority", "5 (Low)");
			break;

		case URGENT2:     /* High (2) */
			replace_field(msg, "Priority", "urgent");
			replace_field(msg, "X-Priority", "2 (High)");
			break;

		case URGENT1|URGENT2: /* Very high (1) */
			replace_field(msg, "Priority", "urgent");
			replace_field(msg, "X-Priority", "1 (High)");
			break;
	}

	return;
}

char *
dummy_get_folder_name(struct _mail_folder *folder) {
	return folder->fold_path;
}

int
dummy_open_folder(struct _mail_folder *folder, int flags) {
	return 0;
}

int
dummy_rescan_folder(struct _mail_folder *folder) {
	return 0;
}

void
dummy_close_folder(struct _mail_folder *folder) {
	return;
}

void
dummy_empty_folder(struct _mail_folder *folder) {
	return;
}

int
dummy_delete_folder(struct _mail_folder *folder) {
	return 0;
}

void
dummy_update_folder(struct _mail_folder *folder) {
	return;
}

int
dummy_rename_folder(struct _mail_folder *folder, char *name) {
	return 0;
}

int
dummy_move_to_folder(struct _mail_msg *msg, struct _mail_folder *folder) {
	return 0;
}

struct _mail_msg *
dummy_copy_to_folder(struct _mail_msg *msg, struct _mail_folder *folder) {
	return NULL;
}

long
dummy_get_folder_uid(struct _mail_folder *folder) {
	return 0;
}

struct _mail_msg *
dummy_get_msg_by_uid(struct _mail_folder *folder, long uid) {
	return NULL;
}

int
dummy_refresh_folder(struct _mail_folder *folder) {
	return 0;
}

int
dummy_find_text(struct _mail_folder *folder, char *text, char *where, int flags, fld_search_cb callback) {
	return 0;
}

void
dummy_folder(struct _mail_folder *folder) {
	folder->name = dummy_get_folder_name;
	folder->open = dummy_open_folder;
	folder->rescan = dummy_rescan_folder;
	folder->close = dummy_close_folder;
	folder->empty = dummy_empty_folder;
	folder->fdelete = dummy_delete_folder;
	folder->rename = dummy_rename_folder;
	folder->update = dummy_update_folder;
	folder->move = dummy_move_to_folder;
	folder->copy = dummy_copy_to_folder;
	folder->search = dummy_find_text;
	folder->getuid = dummy_get_folder_uid;
	folder->getmsg = dummy_get_msg_by_uid;
	folder->refresh = dummy_refresh_folder;
}

struct _mail_folder *
get_file_folder(struct _mail_msg *msg) {
	struct _imap_src *imap;

	switch(msg->type) {
		case M_MH:
			return msg->folder ? msg->folder : ftemp;
			break;

		case M_MBOX:
			return fmbox;
			break;

		case M_IMAP:
			if(msg->folder == NULL)
				return NULL;

			imap = (struct _imap_src *)msg->folder->spec;
			return imap->fimap;
			break;
	}

	return NULL;
}

int
addr_in_list(struct _mail_addr * list, struct _mail_addr *addr) {
	struct _mail_addr *ad;

	ad = list;
	while(ad) {
		if(!strcasecmp(ad->addr, addr->addr))
			return 1;
		ad = ad->next_addr;
	}

	return 0;
}

void
mark_to_us(struct _mail_folder *folder) {
	struct _mail_addr *from, *reply, *addr;
	struct _mail_msg *msg;

	from = get_address(Config.get("from", "").c_str(), 0);
	reply = get_address(Config.get("replyexand", "").c_str(), 0);
	msg = folder->messages;
	while(msg) {
		msg->status &= ~MTOUS;
		msg->status &= ~MFROMUS;

		addr = msg->header->From;
		if(addr) {
			if(addr_in_list(from, addr) ||
			   addr_in_list(reply, addr) ||
			   addr_is_us(msg, addr))
				msg->status |= MFROMUS;
		}

		addr = msg->header->To;
		while(addr) {
			if(addr_in_list(from, addr) ||
			   addr_in_list(reply, addr) ||
			   addr_is_us(msg, addr)) {
				msg->status |= MTOUS;
				break;
			}
			addr = addr->next_addr;
		}

		addr = msg->header->Cc;
		while(addr) {
			if(addr_in_list(from, addr) ||
			   addr_in_list(reply, addr) ||
			   addr_is_us(msg, addr)) {
				msg->status |= MTOUS;
				break;
			}
			addr = addr->next_addr;
		}

		msg = msg->next;
	}

	discard_address(from);
	discard_address(reply);

	return;
}

int
addr_is_us(struct _mail_msg *msg, struct _mail_addr *addr) {

	if(msg->folder && msg->folder->From &&
	   !strcasecmp(msg->folder->From->addr, addr->addr))
		return 1;

	if(!strcasecmp(addr->addr, user_n))
		return 1;

	return 0;
}

void
remove_nonexistent(struct _mail_folder *folder) {
	struct _mail_msg *msg = folder->messages;

	while(msg) {
		if((msg->status & MNOTEXISTS) &&
		   !(msg->status & LOCKED)) {
			if(msg->num > 0)
				unlink(msg->get_file(msg));
			unlink_message(msg);
			discard_message(msg);
		}
		msg = msg->next;
	}

	folder->status |= FRESCAN;
	return;
}

int
pipe_msg(struct _mail_msg *msg, char *command) {
	struct _proc_info pinfo;

	if(!msg || !command || !*command)
		return -1;

	init_pinfo(&pinfo);
	if((pinfo.ifd = open(msg->get_file(msg), O_RDONLY)) == -1) {
		display_msg(MSG_WARN, "PIPE", "Can not access message file");
		return -1;
	}

	if(exec_child(command, &pinfo) < 0) {
		display_msg(MSG_WARN, "PIPE", "Command failed");
		close(pinfo.ifd);
		return -1;
	}

	return 0;
}

void
update_cfold_path(struct _mail_folder *folder) {
	int i;
	char *p, new_path[255];
	struct _mail_folder *subfold;

	if((folder->status & NOINFR) ||
	   (folder->hdelim == '\0') ||
	   (folder->subfold == NULL))
		return;

	for(i = 0; i < MAX_SUBFOLDERS; i++) {
		if(folder->subfold[i] == NULL)
			continue;
		subfold = folder->subfold[i];
		if(subfold->hdelim == '\0')
			continue;

		if((p = strrchr(subfold->fold_path, subfold->hdelim)) == NULL)
			continue;
		snprintf(new_path, sizeof(new_path), "%s%s", folder->fold_path, p);
		snprintf(subfold->fold_path, sizeof(subfold->fold_path), "%s", new_path);
		update_cfold_path(subfold);
	}

	return;
}

char *
get_quoted_str(char **str) {
	char *p, *p1;

	if(!str || !*str)
		return NULL;

	p = *str;
	while((*p == ' ') || (*p == '\t'))
		p++;

	if(*p == '\0')
		return NULL;

	if(((*p == '"') || (*p == '\'')) &&
	   ((p1 = strchr(p + 1, *p)) != NULL)) {
		*p1++ = '\0';
		*str = p1;
		return p + 1;
	}

	if(((p1 = strchr(p, ' ')) != NULL) ||
	   ((p1 = strchr(p, '\t')) != NULL))
		*p1++ = '\0';

	*str = p1;

	return p;
}

int
add_fcc_list(struct _msg_header *msgh, struct _mail_folder *folder) {
	char *fname, *p;
	int fcclen;

	fname = get_folder_full_name(folder);
	if(!fname || !*fname)
		return -1;

	if(msgh->Fcc == NULL) {
		if((msgh->Fcc = (char *)malloc(strlen(fname) + 2)) == NULL) {
			display_msg(MSG_FATAL, "add_fcc_list", "malloc failed");
			return -1;
		}
		strcpy(msgh->Fcc, fname);
		msgh->Fcc[strlen(fname) + 1] = '\0';

		return 0;
	}

	fcclen = 0;
	p = msgh->Fcc;
	while((p[0] != '\0') || (p[1] != '\0')) {
		p++;
		fcclen++;
	}
	fcclen += 2;

	if(fcclen + strlen(fname) + 1 >= MAX_FCC_LEN) {
		display_msg(MSG_WARN, "add_fcc_list", "Fcc list too long, can not add");
		return -1;
	}

	msgh->Fcc = (char *) realloc(msgh->Fcc, fcclen + strlen(fname) + 1);
	if(msgh->Fcc == NULL) {
		display_msg(MSG_FATAL, "add_fcc_list", "realloc failed");
		return -1;
	}

	p = msgh->Fcc + fcclen - 1;
	strcpy(p, fname);
	p[strlen(fname) + 1] = '\0';

	return 0;
}

int
del_fcc_list(struct _mail_msg *msg, char *fptr) {
	char *p, *nfcc;
	int i, fcclen, fptrlen = strlen(fptr);

	if(!msg->header->Fcc)
		return -1;

	fcclen = 0;
	p = msg->header->Fcc;
	while((p[0] != '\0') || (p[1] != '\0')) {
		p++;
		fcclen++;
	}

	if(fcclen == fptrlen) {
		free(msg->header->Fcc);
		msg->header->Fcc = NULL;
		return 0;
	}

	fcclen += 2;

	nfcc = (char *)malloc(fcclen - fptrlen - 1);
	if(nfcc == NULL) {
		display_msg(MSG_WARN, "del_fcc_list", "malloc failed");
		return -1;
	}

	i = fptr - msg->header->Fcc;
	memcpy(nfcc, msg->header->Fcc, i);
	p = nfcc + i;
	if(fptr[fptrlen + 1] == '\0')
		*p = '\0';
	else {
		i = fcclen - i - fptrlen - 1;
		memcpy(p, fptr + fptrlen + 1, i);
	}

	free(msg->header->Fcc);
	msg->header->Fcc = nfcc;
	return 0;
}

char *
scan_fcc_list(struct _mail_msg *msg, char *fptr) {
	char *nextfcc;

	if((fptr == NULL) || (msg->header->Fcc == NULL))
		return msg->header->Fcc;

	nextfcc = fptr + strlen(fptr) + 1;

	return *nextfcc ? nextfcc : NULL;
}

void
parse_fcc_list(struct _msg_header *msgh, char *fcclist) {
	char *p = strtok(fcclist, ",");
	struct _mail_folder *folder;

	if(p == NULL)
		return;

	do {
		if((folder = get_folder_by_name(p)) != NULL)
			add_fcc_list(msgh, folder);
	} while((p = strtok(NULL, ",")) != NULL);

	return;
}

void
print_fcc_list(struct _mail_msg *msg, FILE *mfd) {
	char *fcc = NULL;
	int fccnum = 0;

	if(msg->header->Fcc == NULL)
		return;

	fputs("Fcc: ", mfd);
	while((fcc = scan_fcc_list(msg, fcc)) != NULL) {
		if(fccnum++ != 0)
			fputc(',', mfd);
		fputs(fcc, mfd);
	}
	fputc('\n', mfd);

	return;
}

int
process_fcc_list(struct _mail_msg *msg) {
	struct _mail_folder *folder;

	while(msg->header->Fcc) {
		if((folder = get_folder_by_name(msg->header->Fcc)) == NULL) {
			display_msg(MSG_WARN, "FCC", "There is no such folder: %s",
						msg->header->Fcc);
			return -1;
		}

		if(folder->copy(msg, folder) == NULL)
			return -1;

		del_fcc_list(msg, msg->header->Fcc);
	}

	return 0;
}


void findreplace(string & haystack, string needle, string newtext) {
    int iPosition = 0;

    do {
        iPosition = haystack.find(needle, iPosition);

        if(iPosition != string::npos) {
            haystack.replace(iPosition,needle.size(), newtext);
            iPosition++;
        }
    } while(iPosition != string::npos);

    return;
}


#ifdef  HAVE_REGCOMP
struct _url_spec *
highlight_urls(char *str) {
	#define SHOWURL_EXPR_MATCH  2
	#define HTTP_REG        "http://[[:alnum:]\\-]+(\\.[[:alnum:]\\-]+)*(:[[:digit:]]+)?(()|/[[:alnum:]~&%#=/_\?\\.\\-]*)"
	#define FTP_REG         "ftp://[[:alnum:]\\-]+(\\.[[:alnum:]\\-]+)*(:[[:digit:]]+)?(()|/[[:alnum:]~&%#=/_\?\\.\\-]*)"
	#define MAIL_REG        "[[:alnum:]][[:alnum:]_%\\.\\-]*@[[:alnum:]\\-]+(\\.[[:alnum:]\\-]+)*"
	#define URLSTART_CHARS      "'\"([< "
	#define URLEND_CHARS        "'\")]> "

	static int regcompiled = 0;
	static regex_t http_reg, ftp_reg, mail_reg;
	regmatch_t matches[SHOWURL_EXPR_MATCH + 1];
	int i, res = 0, lnofft = 0, slen;
	char sch, ech;
	struct _url_spec *url, *urls = NULL;

	if(!regcompiled) {
		if(regcomp(&http_reg, HTTP_REG, REG_EXTENDED|REG_ICASE) != 0)
			return NULL;
		if(regcomp(&ftp_reg, FTP_REG, REG_EXTENDED|REG_ICASE) != 0)
			return NULL;
		if(regcomp(&mail_reg, MAIL_REG, REG_EXTENDED|REG_ICASE) != 0)
			return NULL;
		regcompiled = 1;
	}

	if(str == NULL)
		return NULL;
	slen = strlen(str);

	for(i = 0; i < 3; i++) {
		switch(i) {
			case 0:
				res = regexec(&http_reg, str + lnofft, SHOWURL_EXPR_MATCH, matches, 0);
				break;

			case 1:
				res = regexec(&ftp_reg, str + lnofft, SHOWURL_EXPR_MATCH, matches, 0);
				break;

			case 2:
				res = regexec(&mail_reg, str + lnofft, SHOWURL_EXPR_MATCH, matches, 0);
				break;
		}

		if((res != 0) || (matches[0].rm_so == -1) ||
		   (matches[0].rm_eo == -1)) {
			lnofft = 0;
			continue;
		}

		matches[0].rm_so += lnofft;
		matches[0].rm_eo += lnofft;
		sch = matches[0].rm_so ? str[matches[0].rm_so - 1] : '\0';
		ech = (matches[0].rm_eo < slen) ? str[matches[0].rm_eo] : '\0';

		if(sch && !strchr(URLSTART_CHARS, sch))
			goto nexturl;
		if(ech && !strchr(URLEND_CHARS, ech))
			goto nexturl;

		if(sch != ech) {
			if((sch == '\0') && (ech != ' '))
				goto nexturl;
			if((ech == '\0') && (sch != ' '))
				goto nexturl;
			if((sch == ' ') && (ech != '\0'))
				goto nexturl;
			if((sch == '(') && (ech != ')'))
				goto nexturl;
			if((sch == '[') && (ech != ']'))
				goto nexturl;
			if((sch == '<') && (ech != '>'))
				goto nexturl;
			if((sch == '\'') || (sch == '"'))
				goto nexturl;
		}

		if((url = (struct _url_spec *)malloc(sizeof(struct _url_spec))) == NULL) {
			display_msg(MSG_FATAL, "highlight_urls", "malloc failed");
			return NULL;
		}

		ech = (matches[0].rm_eo < slen) ? str[matches[0].rm_eo - 1] : str[slen - 1];
		if(ech == '.')
			matches[0].rm_eo--;

		url->url_s = matches[0].rm_so;
		url->url_e = matches[0].rm_eo;
		url->next = urls;
		urls = url;

		nexturl:
		if(ech != '\0') {
			i--;
			lnofft = matches[0].rm_eo;
		}
	}

	return urls;
}
#endif
