/* $Id: mbox.cpp,v 1.25 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 <fmail.h>
#include <umail.h>
#include <cfgfile.h>

char *get_mbox_folder_name(struct _mail_folder *folder);
int open_mbox_folder(struct _mail_folder *folder, int flags);
int rescan_mbox_folder(struct _mail_folder *folder);
void close_mbox_folder(struct _mail_folder *folder);
void empty_mbox_folder(struct _mail_folder *folder);
int delete_mbox_folder(struct _mail_folder *folder);
void update_mbox_folder(struct _mail_folder *folder);
int rename_mbox_folder(struct _mail_folder *folder, char *name);
int move_to_mbox_folder(struct _mail_msg *msg,
						struct _mail_folder *folder);
struct _mail_msg *copy_to_mbox_folder(struct _mail_msg *msg,
									  struct _mail_folder *folder);
int mbox_find_text(struct _mail_folder *folder, char *text, char *where,
				   int flags, fld_search_cb callback);
long get_mbox_folder_uid(struct _mail_folder *folder);
struct _mail_msg *get_mbox_msg_by_uid(struct _mail_folder *folder,
									  long uid);
int refresh_mbox_folder(struct _mail_folder *folder);
int expunge_mbox_folder(struct _mail_folder *folder);

int delete_mbox_message(struct _mail_msg *msg);
int print_mbox_message(struct _mail_msg *msg, FILE * fd, int send);
int print_mbox_message_body(struct _mail_msg *msg, FILE * fd);
int get_mbox_message_text(struct _mail_msg *msg, struct _mime_msg *mime);
void free_mbox_message_text(struct _mail_msg *msg);
char *get_mbox_msg_file(struct _mail_msg *msg);
int update_mbox_message(struct _mail_msg *msg);
int get_mbox_message_header(struct _mail_msg *msg);
unsigned long get_mbox_message_validity(struct _mail_msg *msg);
int refresh_mbox_message(struct _mail_msg *msg);

void get_from(struct _mail_msg *msg, char *buf, FILE * file);
FILE *get_mbox_folder_fd(struct _mail_folder *folder, char *mode);
void update_clen(struct _mail_msg *msg);
int mbox_rewrite(struct _mail_folder *folder);
void free_mbox_messages(struct _mail_folder *folder);
int lockfolder(struct _mail_folder *folder);
int unlockfolder(struct _mail_folder *folder);
int relock_fd(struct _mail_folder *folder);
int create_mbox_file(struct _mail_folder *folder);

#define MAXLOCKATTEMPT  5   /* max attempts to aquire a lock */
int locking = -1;       /* locking method */

#define READMBOXMODE    "r"
#define WRITEMBOXMODE   "r+"
#define APPENDMBOXMODE  "a+"

extern cfgfile Config;

static char *mboxmsg = NULL;    /* mmapped message  */
static size_t mboxmsglen = 0;   /* mmapped part length  */

void mbox_folder(struct _mail_folder *folder) {
	folder->name = get_mbox_folder_name;
	folder->open = open_mbox_folder;
	folder->rescan = rescan_mbox_folder;
	folder->close = close_mbox_folder;
	folder->empty = empty_mbox_folder;
	folder->fdelete = delete_mbox_folder;
	folder->rename = rename_mbox_folder;
	folder->update = update_mbox_folder;
	folder->move = move_to_mbox_folder;
	folder->copy = copy_to_mbox_folder;
	folder->search = find_text;
	folder->getuid = get_mbox_folder_uid;
	folder->getmsg = get_mbox_msg_by_uid;
	folder->refresh = refresh_mbox_folder;
	folder->expunge = expunge_mbox_folder;
	folder->hdelim = '/';
	folder->type = F_MBOX;
}

void mbox_message(struct _mail_msg *msg) {
	msg->mdelete = delete_mbox_message;
	msg->print = print_mbox_message;
	msg->print_body = print_mbox_message_body;
	msg->get_text = get_mbox_message_text;
	msg->get_header = get_mbox_message_header;
	msg->free_text = free_mbox_message_text;
	msg->get_file = get_mbox_msg_file;
	msg->update = update_mbox_message;
	msg->validity = get_mbox_message_validity;
	msg->refresh = refresh_mbox_message;
	msg->type = M_MBOX;
}

void free_mbox_spec(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;

	if(spec) {
		if(spec->ffd) {
			unlockfolder(folder);
			fclose(spec->ffd);
		}
		free(spec);
	}

	folder->spec = NULL;
}

void init_mbox_spec(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;

	if(!spec) {
		spec = (struct _mbox_spec *) malloc(sizeof(struct _mbox_spec));
		spec->ffd = NULL;
		spec->fsize = 0;
		spec->mode[0] = '\0';
		folder->spec = (void *) spec;
		return;
	}

	if(spec->ffd) {
		unlockfolder(folder);
		fclose(spec->ffd);
	}

	spec->ffd = NULL;
	spec->mode[0] = '\0';

	return;
}

int mbox_changed(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct stat sb;

	if(stat(folder->fold_path, &sb) != 0)
		return 1;

	if((sb.st_size != spec->fsize) || (folder->uid != sb.st_mtime))
		return 1;

	return 0;
}

int relock_fd(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;

	if(!spec->ffd || !(folder->status & FLOCKED))
		return 0;

	if(locking == -1)
		locking =
		Config.getInt("spoolock", MBOX_DOT_LOCK | MBOX_KERNEL_LOCK);
	if(locking & MBOX_KERNEL_LOCK) {
#if !defined(HAVE_FLOCK) && defined(HAVE_LOCKF)
		if(lockf(fileno(spec->ffd), F_LOCK, 0) == -1) {
#else
		if(flock(fileno(spec->ffd), LOCK_EX | LOCK_NB) == -1) {
#endif
			if(!is_iconized())
				display_msg(MSG_WARN, "lock", "Can not re-lock folder\n");
			unlockfolder(folder);
			return -1;
		}
	}

	return 0;
}

int lockfolder(struct _mail_folder *folder) {
	FILE *ffd = NULL;
	char buf[255];
	int fd, statfailed = 0, lattempts = 0, uerr = 0;
	struct stat st;
	time_t now;

	if(folder->status & FLOCKED)
		return 0;

	if(folder->status & FRONLY) {
		folder->status |= FLOCKED;
		return 0;
	}

	if(locking == -1)
		locking =
		Config.getInt("spoolock", MBOX_DOT_LOCK | MBOX_KERNEL_LOCK);
	if(locking & MBOX_KERNEL_LOCK) {
		if((ffd = get_mbox_folder_fd(folder, WRITEMBOXMODE)) == NULL)
			return -1;
#if !defined(HAVE_FLOCK) && defined(HAVE_LOCKF)
		if((ffd = get_mbox_folder_fd(folder, WRITEMBOXMODE)) == NULL)
			return -1;

		if(fseek(ffd, 0, SEEK_SET) == -1) {
			display_msg(MSG_WARN, "LOCK", "Can not seek");
			return -1;
		}

		for(;; sleep(1)) {
			if(lattempts++ > MAXLOCKATTEMPT) {
				display_msg(MSG_WARN, "LOCK",
							"Can not lock folder (lockf() failed)\n%s",
							folder->fold_path);
				break;
			}
			if(lockf(fileno(ffd), F_LOCK, 0) == -1) {
				if(errno != EAGAIN) {
					init_mbox_spec(folder);
					if((ffd = get_mbox_folder_fd(folder, WRITEMBOXMODE))
					   == NULL)
						return -1;

					if(fseek(ffd, 0, SEEK_SET) == -1) {
						display_msg(MSG_WARN, "LOCK", "Can not seek");
						return -1;
					}
				}

			} else
				break;
		}
#else
		if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL)
			return -1;

		if(flock(fileno(ffd), LOCK_EX | LOCK_NB) == -1) {
			display_msg(MSG_WARN, "LOCK",
				"Can not lock folder (flock(%i) failed)\n%s",
				fileno(ffd), folder->fold_path);
			return -1;
		}
#endif
	}

	if(locking & MBOX_DOT_LOCK) {
		lattempts = 0;
		snprintf(buf, sizeof(buf), "%s.lock", folder->fold_path);
		if(stat(buf, &st) < 0)
			statfailed++;
		for(;; sleep(1)) {
			if(lattempts++ > MAXLOCKATTEMPT) {
				display_msg(MSG_WARN, "LOCK", "Can not lock\n%s", buf);
				uerr = 1;
				break;
			}

			fd = open(buf, O_WRONLY | O_EXCL | O_CREAT, 00644);
			if(fd >= 0) {
				close(fd);
				break;
			}

			if(errno == EACCES) {
				if(!(locking & MBOX_KERNEL_LOCK)) {
					display_msg(MSG_WARN, "LOCK",
								"Can not create dotlock");
					uerr = 1;
				}

				if(statfailed == 0)
					uerr = 1;
				break;
			}

			if(stat(buf, &st) < 0) {
				if(statfailed++ > MAXLOCKATTEMPT) {
					display_msg(MSG_WARN, "LOCK", "Can not stat\n%s", buf);
					uerr = 1;
					break;
				}
				continue;
			}
			statfailed = 0;
			time(&now);
			if(now < st.st_ctime + 300)
				continue;
			unlink(buf);
		}
	}

	if(!uerr) {
		folder->status |= FLOCKED;
#ifdef  LOCK_DEBUG
		fprintf(stderr, "Folder %s locked\n", folder->sname);
#endif
		return 0;
	}

	if(locking & MBOX_KERNEL_LOCK) {
#ifdef HAVE_FLOCK
		if(flock(fileno(ffd), LOCK_UN) == -1) {
			display_msg(MSG_WARN, "UNLOCK", "flock failed");
			return -1;
		}
#else
#ifdef HAVE_LOCKF
		if(fseek(ffd, 0, SEEK_SET) == -1) {
			display_msg(MSG_WARN, "UNLOCK", "Can not seek");
			return -1;
		}

		if(!(folder->status & FRONLY)) {
			if(lockf(fileno(ffd), F_ULOCK, 0) == -1) {
				display_msg(MSG_WARN, "UNLOCK", "lockf failed");
				return -1;
			}
		}
#endif
#endif
	}

	return -1;
}

int unlockfolder(struct _mail_folder *folder) {
	FILE *ffd;
	char buf[255];
	struct stat sb;

	if(!(folder->status & FLOCKED))
		return 0;

	folder->status &= ~FLOCKED;
	if(folder->status & FRONLY)
		return 0;

	if(locking == -1)
		locking =
		Config.getInt("spoolock", MBOX_DOT_LOCK | MBOX_KERNEL_LOCK);
	if(locking & MBOX_DOT_LOCK) {
		snprintf(buf, sizeof(buf), "%s.lock", folder->fold_path);
		if(stat(buf, &sb) != -1) {
			if(unlink(buf) == -1) {
				display_msg(MSG_WARN, "UNLOCK",
							"Can not remove lockfile %s", buf);
				return -1;
			}
		}
	}

	if(locking & MBOX_KERNEL_LOCK) {
#if !defined(HAVE_FLOCK) && defined(HAVE_LOCKF)
		if((ffd = get_mbox_folder_fd(folder, WRITEMBOXMODE)) == NULL)
			return -1;
#else
		if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL)
			return -1;
#endif

#ifdef HAVE_FLOCK
		if(flock(fileno(ffd), LOCK_UN) == -1) {
			display_msg(MSG_WARN, "UNLOCK", "flock failed");
			return -1;
		}
#else
#ifdef HAVE_LOCKF
		if(fseek(ffd, 0, SEEK_SET) == -1) {
			display_msg(MSG_WARN, "UNLOCK", "Can not seek");
			return -1;
		}

		if(!(folder->status & FRONLY)) {
			if(lockf(fileno(ffd), F_ULOCK, 0) == -1) {
				display_msg(MSG_WARN, "UNLOCK", "lockf failed");
				return -1;
			}
		}
#endif
#endif
	}

#ifdef  LOCK_DEBUG
	fprintf(stderr, "Folder %s unlocked\n", folder->sname);
#endif
	return 0;
}

char *get_mbox_folder_name(struct _mail_folder *folder) {
	char *p;

	if(!folder)
		return NULL;

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

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

	return p;
}

void update_mbox_fsize(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct stat sb;

	if((spec->ffd == NULL) &&
	   (get_mbox_folder_fd(folder, READMBOXMODE) == NULL)) return;

	if(spec->ffd && (fstat(fileno(spec->ffd), &sb) != -1))
		spec->fsize = sb.st_size;

	return;
}

int reopen_folder_fd(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;

	if(!spec->ffd || (*spec->mode == '\0'))
		return 0;

	fclose(spec->ffd);
	if((spec->ffd = fopen(folder->fold_path, spec->mode)) == NULL) {
		display_msg(MSG_WARN, "reopen folder file", "Can not open %s",
					folder->fold_path);
		return -1;
	}

	return relock_fd(folder);
}

//bug
FILE *get_mbox_folder_fd(struct _mail_folder * folder, char *mode) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct stat sb;
	int res;

	if(spec->ffd) {
		if(!strcmp(spec->mode, mode) || !strcmp(mode, READMBOXMODE))
			return spec->ffd;
		fclose(spec->ffd);
	}

	if((folder->status & FRONLY) && strcmp(mode, READMBOXMODE))
		mode = READMBOXMODE;

	if((spec->ffd = fopen(folder->fold_path, mode)) == NULL) {
		if(errno == ENOENT) {
			if(create_mbox_file(folder) == -1)
				return NULL;
			if((spec->ffd = fopen(folder->fold_path, mode)) == NULL)
				return NULL;
		} else if((errno == EACCES) && !strcmp(mode, WRITEMBOXMODE)) {
			mode = READMBOXMODE;
			if((spec->ffd = fopen(folder->fold_path, mode)) == NULL) {
				display_msg(MSG_WARN, "open folder file",
							"Can not open %s (even tried read-only)",
							folder->fold_path);
				return NULL;
			}
			folder->status |= FRONLY;
		} else {
			display_msg(MSG_WARN, "open folder file", "Can not open %s",
						folder->fold_path);
			return NULL;
		}
	}

	if(relock_fd(folder)) {
		fclose(spec->ffd);
		spec->ffd = NULL;
		return NULL;
	}

	if(fstat(fileno(spec->ffd), &sb) == -1) {
		display_msg(MSG_WARN, "open folder file", "Can not open %s",
					folder->fold_path);
		fclose(spec->ffd);
		spec->ffd = NULL;
		return NULL;
	}

	if((res = fcntl(fileno(spec->ffd), F_GETFL)) == -1) {
		display_msg(MSG_WARN, "open folder", "fcntl F_GETFL failed");
		fclose(spec->ffd);
		spec->ffd = NULL;
		return NULL;
	}

	if(fcntl(fileno(spec->ffd), F_SETFL, res | O_NONBLOCK) == -1) {
		display_msg(MSG_WARN, "open folder",
					"fcntl F_SETFL, O_NONBLOCK failed");
		fclose(spec->ffd);
		spec->ffd = NULL;
		return NULL;
	}

	snprintf(spec->mode, sizeof(spec->mode), "%s", mode);
	if((sb.st_mode & S_IWUSR) != S_IWUSR)
		folder->status |= FRONLY;

	return spec->ffd;
}

int skip_hdr(FILE * ffd) {
	char buf[255];

	while(fgets(buf, sizeof(buf), ffd)) {
		if(!*buf || (*buf == '\r') || (*buf == '\n'))
			return 0;
	}

	return -1;
}

int skip_hdr_flags(FILE * ffd, size_t * clen) {
	head_field *hf;
	int fl = 0;
	char buf[255];

	while(fgets(buf, sizeof(buf), ffd)) {
		if(!*buf || (*buf == '\r') || (*buf == '\n'))
			return fl;

		if(!strncmp(buf, STATUS_FIELD, STATUS_FIELD_LEN) &&
		   ((hf = get_field(buf)) != NULL))
			sscanf(hf->f_line, "%4x", &fl);
		else
			if(!strncmp(buf, "Status: ", 8) &&
			   ((hf = get_field(buf)) != NULL))
			fl = (*hf->f_line == 'R') ? 0 : UNREAD;
		else
			if(!strncmp(buf, "Content-Length: ", 16) &&
			   ((hf = get_field(buf)) != NULL) && clen)
			*clen = atoi(hf->f_line);
	}
	return -1;
}

int skip_msg(FILE * ffd) {
	off_t mofft1;
	int slen;
	char buf[255];

	mofft1 = ftell(ffd);
	slen = 1;
	while(fgets(buf, sizeof(buf), ffd)) {
		if(is_from(buf, NULL, 0)) {
			fseek(ffd, mofft1, SEEK_SET);
			return 1;
		}

		if((*buf == '\r') || (*buf == '\n')) {
			mofft1 = ftell(ffd);
			slen = strlen(buf);
			if(!fgets(buf, sizeof(buf), ffd))
				break;
			if(is_from(buf, NULL, 0)) {
				fseek(ffd, mofft1, SEEK_SET);
				return slen;
			}
			slen = 1;
		}
		mofft1 = ftell(ffd);
	}

	return ferror(ffd) ? -1 : 1;
}

struct _mail_msg *get_mbox_message(long uid, struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct _mail_msg *msg, *tmpmsg;
	struct _msg_header *hd;
	struct _head_field *hf;
	char buf[255];
	FILE *ffd;
	time_t rt;
	int eoh, flags;
	size_t clength = 0;
	off_t shuid, from_uid;

	if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL)
		return NULL;

	if(uid == spec->fsize)
		return NULL;

	if(fseek(ffd, uid, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "get message",
					"Can not access message (%ld)", uid);
		return NULL;
	}

	do {
		from_uid = ftell(ffd);
		if(!fgets(buf, sizeof(buf), ffd)) {
			display_msg(MSG_WARN, "get message", "Error reading message (%ld)",
				uid);
			return NULL;
		}
	} while (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n');

	if((rt = is_from(buf, NULL, 0)) == 0) {
		display_msg(MSG_WARN, "get message",
					"Corrupt message/folder (%ld - no From line)", uid);
		return NULL;
	}

	shuid = ftell(ffd);
	flags =
	(folder->status & FSHORTH) ? (M_HSHORT | M_TESTFLAG) : M_TESTFLAG;
	if((hd = get_msg_header(ffd, flags, &eoh)) == NULL) {
		display_msg(MSG_WARN, "get message",
					"Message is corrupt\n(Can not parse message header)");
		return NULL;
	}

	hd->header_len = ftell(ffd) - shuid;
	if(hd->rcv_time == 0)
		hd->rcv_time = rt;
	if(hd->snt_time == 0)
		hd->snt_time = rt;

	if((msg = alloc_message()) == NULL) {
		display_msg(MSG_FATAL, "get message", "malloc failed");
		return NULL;
	}

	mbox_message(msg);
	msg->folder = folder;
	msg->num = -1;
	msg->uid = from_uid;
	msg->real_uid = 1;
	msg->flags |= hd->flags;
	msg->header = hd;

	//Speed problem here..
	for(tmpmsg = msg->folder->messages; tmpmsg; tmpmsg = tmpmsg->next)
		msg->real_uid++;

	if(msg->flags & M_TESTFLAG) {
		set_flags_by_status(msg);
		msg->header->flags = msg->flags;
	}

	msg->status |= (folder->status & FSHORTH) ? H_SHORT : 0;
	msg->flags &= 0xFFFF;
	msg->header->flags &= 0xFFFF;

	if(find_field_noload(msg, "Date") == NULL) {
		add_field(msg, "Date", get_arpa_date(msg->header->snt_time));
		folder->status |= FREWRTE;
	}

	strip_newline(buf);
	replace_field_noload(msg, FROMLINE, buf);

	hf = find_field_noload(msg, MIME_C_LENGTH);
	if(hf) {
		clength = (size_t) atoi(hf->f_line);
		if(clength > (spec->fsize - from_uid))
			clength = 0;
	}

	if(clength > 0) {
		clength += (ftell(ffd) + 1);
		if(clength >= spec->fsize) {
			msg->msg_len = (unsigned long) (spec->fsize - shuid - 1);
			fseek(ffd, 0, SEEK_END);
			return msg;
		}

		if((clength < spec->fsize) &&
		   (fseek(ffd, clength, SEEK_SET) != -1) &&
		   (fgets(buf, sizeof(buf), ffd) != NULL)) {
			if(!strncmp(buf, "From ", 5)) {
				msg->msg_len = (unsigned long) (clength - shuid - 1);
				fseek(ffd, clength, SEEK_SET);
				return msg;
			} else
				folder->status |= FREWRTE;
		}

		fseek(ffd, shuid + hd->header_len, SEEK_SET);
	} else
		folder->status |= FREWRTE;

	if((eoh = skip_msg(ffd)) < 0) {
		display_msg(MSG_WARN, "get message",
					"Can not find end-of-message (read error)");
		discard_message(msg);
		return NULL;
	}

	msg->msg_len = ftell(ffd) - shuid - eoh;

	return msg;
}

int open_mbox_folder(struct _mail_folder *folder, int flags) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct _mail_msg *msg, *msg1, *msglock;
	struct _mail_folder *pfold;
	int fl, locked = 1, oerr = 0;
	unsigned long prev_msg, prev_unread;
	off_t mofft;
	size_t clength;
	FILE *ffd;
	char buf[255];

	if(!folder)
		return -1;

	prev_msg = folder->num_msg;
	prev_unread = folder->unread_num;
	if(!(flags & FOPEN_NOCLOSE)) {
		if((folder->status & OPENED) || (folder->messages))
			folder->close(folder);
	}
	folder->status &= ~FREWRTE;

	reopen_folder_fd(folder);
	if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL)
		return -1;

	if(!(folder->status & FLOCKED)) {
		if(lockfolder(folder) == -1) {
			display_msg(MSG_WARN, "open folder", "Can not lock folder\n%s",
						folder->fold_path);
			return -1;
		}
		locked = 1;
		/* Re-retrieve the file descriptor */
		ffd = get_mbox_folder_fd(folder, spec->mode);
	}

	fseek(ffd, 0, SEEK_SET);
	update_mbox_fsize(folder);

	folder->num_msg = 0;
	folder->unread_num = 0;
	if(spec->fsize == 0)
		goto endopen;

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

	do {
		if((mofft = ftell(ffd)) < 0) {
			display_msg(MSG_WARN, "open folder",
						"Can not read from %s (ftell failed)",
						folder->fold_path);
			if(locked)
				unlockfolder(folder);
			init_mbox_spec(folder);
			folder->status |= FRESCAN;
			return -1;
		}

		if((folder->flags & CACHED) && !(flags & FOPEN_NOCACHE)) {
			if((msg = msg_cache(folder, mofft)) != NULL) {
				if(flags & FOPEN_UNREAD) {
					if(!(msg->flags & UNREAD)) {
						folder->num_msg++;
						discard_message(msg);
						goto next_msg;
					}
				}

				if(fgets(buf, sizeof(buf), ffd) == NULL) {
					fseek(ffd, mofft, SEEK_SET);
					goto onocache;
				}
				if(fseek(ffd, msg->msg_len + 1, SEEK_CUR) == -1) {
					discard_message(msg);
					break;
				}
				goto lnk_msg;
			}
		}

		onocache:
		if(flags & FOPEN_UNREAD) {
			clength = 0;
			if((fl = skip_hdr_flags(ffd, &clength)) == -1)
				break;
			if(!(fl & UNREAD)) {
				if(clength == 0) {
					if(skip_msg(ffd) == -1)
						break;
				} else
					fseek(ffd, clength + 1, SEEK_CUR);
				folder->num_msg++;
				goto next_msg;
			}
			fseek(ffd, mofft, SEEK_SET);
		}

		if((msg = get_mbox_message((long) mofft, folder)) == NULL) {
			if(locked)
				unlockfolder(folder);
			init_mbox_spec(folder);
			folder->status |= FRESCAN;
			return -1;
		}

		if(folder->flags & CACHED)
			cache_msg(msg);

		lnk_msg:
		msg1 = msglock;
		while(msg1) {
			if((flags & FOPEN_NOCLOSE) ||
			   (folder->status & FNOCLSE) || (msg1->status & LOCKED)) {
				if((msg1->uid == mofft) &&
				   (msg1->msg_len == msg->msg_len)) {
					msg1->status &= ~MNOTEXISTS;
					discard_message(msg);
					if(msg1->flags & UNREAD)
						folder->unread_num++;
					folder->num_msg++;
					goto next_msg;
				}
			}
			msg1 = msg1->next;
		}

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

		next_msg:
		if((folder->num_msg % 10) == 1)
			display_msg(MSG_STAT, NULL, "Rescanning %s : %d%%",
						folder->fold_path, mofft * 100 / spec->fsize);

		if(abortpressed()) {
			display_msg(MSG_WARN, "open folder",
						"Rescan aborted, not all messages will be shown");
			folder->num_msg = prev_msg;
			folder->unread_num = prev_unread;
			oerr = 1;
			break;
		}
	} while(!feof(ffd) && !ferror(ffd) && (ftell(ffd) < spec->fsize));

	msg = msglock;
	while(msg) {
		msg1 = msg->next;
		if((msg->status & MNOTEXISTS) && !(msg->status & LOCKED))
			unlink_message(msg);
		msg = msg1;
	}

	if(ferror(ffd)) {
		display_msg(MSG_WARN, "open folder", "Error reading from %s",
					folder->fold_path);
		if(locked)
			unlockfolder(folder);
		init_mbox_spec(folder);
		folder->status |= FRESCAN;
		return -1;
	}

	endopen:
	if(!oerr)
		folder->status |= OPENED;

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

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

	if((prev_msg != folder->num_msg) ||
	   (prev_unread != folder->unread_num)) folder->status |= FRESCAN;

	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;

	sort_folder(folder);
	expire_msgs(folder);
	if(locked)
		unlockfolder(folder);

	return 0;
}

int rescan_mbox_folder(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	FILE *ffd;
	char buf[255];
	int fl, locked = 0;
	size_t clength;

	if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL)
		return -1;

	update_mbox_fsize(folder);
	folder->num_msg = 0;
	folder->unread_num = 0;
	if(spec->fsize == 0) {
		init_mbox_spec(folder);
		return 0;
	}
	fseek(ffd, 0, SEEK_SET);

	if(!(folder->status & FLOCKED)) {
		if(lockfolder(folder) == -1) {
			display_msg(MSG_WARN, "rescan folder",
						"Can not lock folder\n%s", folder->fold_path);
			init_mbox_spec(folder);
			return -1;
		}
		locked = 1;
	}

	while(fgets(buf, sizeof(buf), ffd)) {
		if(!is_from(buf, NULL, 0)) {
			display_msg(MSG_WARN, "rescan folder", "Folder is corrupt");
			if(locked)
				unlockfolder(folder);
			init_mbox_spec(folder);
			return -1;
		}

		clength = 0;
		if((fl = skip_hdr_flags(ffd, &clength)) == -1)
			break;
		folder->num_msg++;
		if(fl & UNREAD)
			folder->unread_num++;

		if(clength == 0) {
			if(skip_msg(ffd) == -1)
				break;
		} else
			fseek(ffd, clength + 1, SEEK_CUR);
	}

	if(locked)
		unlockfolder(folder);
	if(ferror(ffd)) {
		init_mbox_spec(folder);
		return -1;
	}

	init_mbox_spec(folder);
	return 0;
}

void free_mbox_messages(struct _mail_folder *folder) {
	struct _mail_msg *msg, *msg1, *msg_lock;
	char buf[255];

	msg = folder->messages;
	msg_lock = NULL;
	while(msg) {
		msg1 = msg->next;
		if(msg->status & LOCKED) {
			msg->next = msg_lock;
			msg_lock = msg;
		} else {
			if(msg->num > 0) {
				snprintf(buf, sizeof(buf), "%s/%ld", fmbox->fold_path,
						 msg->num);
				unlink(buf);
			}
			discard_message(msg);
		}

		msg = msg1;
	}

	folder->messages = msg_lock;

	return;
}

void close_mbox_folder(struct _mail_folder *folder) {
	struct _mail_folder *pfold;

	if(!folder)
		return;

	folder->update(folder);
	if(mbox_rewrite(folder) == -1) {
		unlockfolder(folder);
		return;
	}

	if(!(folder->status & FNOCLSE) || (folder->status & FUNREAD))
		folder->status &= ~OPENED;
	folder->status &= ~SORTED;
	folder->status &= ~SEARCH;
	folder->status &= ~FUNREAD;
	folder->status &= ~FREWRTE;
	if(folder->status & FRECNT) {
		folder->status &= ~FRECNT;
		for(pfold = folder->pfold; pfold; pfold = pfold->pfold)
			pfold->status &= ~FMRKTMP;
	}

	init_mbox_spec(folder);
	close_cache(folder);

	if(!folder->messages) {
		unlockfolder(folder);
		return;
	}

	if(!(folder->status & FNOCLSE))
		free_mbox_messages(folder);

	unlockfolder(folder);
	return;
}

void empty_mbox_folder(struct _mail_folder *folder) {
	int oflags;

	if(!folder)
		return;

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "empty",
					"%s is a read-only folder , you can not delete it",
					folder->name(folder));
		return;
	}

	oflags = folder->status;

	if((folder->status & OPENED) || (folder->messages))
		folder->close(folder);

	if(folder->messages)
		goto reop;

	if(truncate(folder->fold_path, 0) == -1)
		display_msg(MSG_WARN, "empty folder", "Can not truncate %s",
					folder->fold_path);

	init_mbox_spec(folder);
	folder->num_msg = folder->unread_num = 0;

	reop:
	if(oflags & OPENED)
		folder->open(folder, 0);

	folder->status &= ~FRECNT;
	folder->status &= ~SEARCH;
	folder->status &= ~FMRKTMP;

	folder_sort &= ~FLD_SORTED;
	delete_cache(folder);
	return;
}

int delete_mbox_folder(struct _mail_folder *folder) {
	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->status & FRONLY) {
		display_msg(MSG_WARN, "delete",
					"%s is a read-only folder , you can not delete it",
					folder->name(folder));
		return -1;
	}

	if(display_msg(MSG_QUEST | MSG_DEFNO, "delete",
				   "Delete folder file from disk?")) {
		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(unlink(folder->fold_path) == -1) {
			display_msg(MSG_WARN, "delete", "Can not remove %-.64s",
						folder->fold_path);
			return -1;
		}
	} else
		folder->close(folder);

	delete_cache(folder);

	folder_sort &= ~FLD_SORTED;

	return remove_folder(folder);
}

void update_mbox_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg, *msg_nxt;
	struct _mail_folder *fld;
	int k, ronlyw = 0;

	if(!folder)
		return;

	if(!folder->messages)
		return;

	if(mbox_changed(folder))
		refresh_mbox_folder(folder);

	msg = folder->messages;
	msg_nxt = folder->messages->next;
	k = 0;
	while(msg) {
		if(msg->status & MNOTEXISTS) {
			msg = msg->next;
			continue;
		}

		if(folder->status & FRONLY) {
			if((msg->status & MOVED) ||
			   (msg->status & DELETED) || (msg->status & DELPERM)) {
				if(!ronlyw) {
					display_msg(MSG_WARN, "update", "Folder is read-only");
					ronlyw = 1;
				}
				msg->status &= ~MOVED;
				msg->status &= ~DELETED;
				msg->status &= ~DELPERM;
				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)
			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;
	}

	display_msg(MSG_STAT, NULL, "");
	return;
}

int rename_mbox_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(folder->status & FRONLY) {
		display_msg(MSG_WARN, "rename",
					"%s is a read-only 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++;
	}

	if(get_mh_folder_by_name(name)) {
		display_msg(MSG_WARN, "rename folder",
					"folder with name %s already exists", name);
		return -1;
	}

	snprintf(new_path, sizeof(new_path), "%s/%s",
			 dir_path(folder->fold_path), name);
	if(stat(new_path, &sb) == 0) {
		if(!(sb.st_mode & S_IFREG)) {
			display_msg(MSG_WARN, "rename folder",
						"%s already exits, but it's not a file");
			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 file");
		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 move_to_mbox_folder(struct _mail_msg *msg, struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct _mail_folder *pfold, *oldfold = msg->folder;
	struct _mail_msg *msg1;
	struct stat sb;
	int locked = 0;
	off_t hoff, shuid;
	FILE *ffd;
	char buf[255];

	if(!msg || !folder || !(folder->type & F_MBOX))
		return -1;

	msg->status &= ~MOVED;
	if(folder->status & FRONLY)
		return -1;

	if(msg->status & LOCKED)
		return -1;

	if(oldfold) {
		if(oldfold->status & FRONLY)
			return -1;
		msg_cache_del(msg);
		if(oldfold == folder) {
			if(locked)
				unlockfolder(folder);
			return 0;
		}
		oldfold->status |= FRESCAN;
	}

	folder->status |= FRESCAN;
	refresh_mbox_folder(folder);

	if((ffd = get_mbox_folder_fd(folder, APPENDMBOXMODE)) == NULL)
		return -1;

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "move", "Read-only folder");
		return -1;
	}

	if(!(folder->status & FLOCKED)) {
		if(lockfolder(folder) == -1)
			return -1;
		locked = 1;
	}

	if(fstat(fileno(ffd), &sb) == -1) {
		display_msg(MSG_WARN, "move", "Can not access folder");
		if(locked)
			unlockfolder(folder);
		return -1;
	}

	if(fseek(ffd, sb.st_size, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "move", "Can not access folder");
		if(locked)
			unlockfolder(folder);
		return -1;
	}

	msg->get_header(msg);
	get_from(msg, buf, ffd);
	shuid = ftell(ffd);
	update_clen(msg);
	set_status_by_flags(msg);
	delete_all_fields(msg, FROMLINE);
	print_message_header(msg, ffd);
	strip_newline(buf);
	add_field(msg, FROMLINE, buf);
	hoff = ftell(ffd);
	if(msg->print_body(msg, ffd) == -1) {
		display_msg(MSG_WARN, "move", "Can not write message");
		if(locked)
			unlockfolder(folder);
		return -1;
	}
	fputc('\n', ffd);

	if(fflush(ffd) == EOF) {
		if(errno == ENOSPC)
			display_msg(MSG_WARN, "write message", "DISK FULL!");
		else
			display_msg(MSG_WARN, "write message", "Failed to write");
		if(locked)
			unlockfolder(folder);
		return -1;
	}

	spec->fsize = ftell(ffd);

	if(oldfold) {
		msg1 = copy_msg(msg);
		mbox_message(msg1);
		msg->folder = oldfold;
		msg->status |= (DELETED | DELPERM);
		msg->mdelete(msg);
		msg = msg1;
	}

	msg->folder = folder;
	msg->num = -1;
	msg->uid = sb.st_size;
	msg->msg_len = spec->fsize - shuid - 1;
	msg->header->header_len = hoff - shuid;

	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;

	if(locked)
		unlockfolder(folder);
	return 0;
}

struct _mail_msg *copy_to_mbox_folder(struct _mail_msg *msg,
									  struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct _mail_msg *nmsg;
	int locked = 0;
	FILE *ffd;
	struct stat sb;

	if(!msg || !folder || !(folder->type & F_MBOX))
		return NULL;

	msg->status &= ~COPIED;
	if(folder->status & FRONLY)
		return NULL;

	folder->status |= FRESCAN;

	refresh_mbox_folder(folder);

	if((ffd = get_mbox_folder_fd(folder, APPENDMBOXMODE)) == NULL)
		return NULL;

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "move", "Read-only folder");
		return NULL;
	}

	if(!(folder->status & FLOCKED)) {
		if(lockfolder(folder) == -1)
			return NULL;
		locked = 1;
	}

	if(fstat(fileno(ffd), &sb) == -1) {
		display_msg(MSG_WARN, "copy", "Can not access folder");
		if(locked)
			unlockfolder(folder);
		return NULL;
	}

	if(fseek(ffd, sb.st_size, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "copy", "Can not access folder");
		if(locked)
			unlockfolder(folder);
		return NULL;
	}

	msg->get_file(msg);
	msg->get_header(msg);
	update_clen(msg);
	set_status_by_flags(msg);
	get_from(msg, NULL, ffd);
	if(msg->print(msg, ffd, 0) == -1) {
		display_msg(MSG_WARN, "copy", "Can not write message");
		if(locked)
			unlockfolder(folder);
		return NULL;
	}
	fputc('\n', ffd);

	if(fflush(ffd) == EOF) {
		if(errno == ENOSPC)
			display_msg(MSG_WARN, "write message", "DISK FULL!");
		else
			display_msg(MSG_WARN, "write message", "Failed to write");
		if(locked)
			unlockfolder(folder);
		return NULL;
	}

	spec->fsize = ftell(ffd);

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

	if((folder->status & OPENED) || (msg->status & LOCKED)) {
		if(!(nmsg = get_mbox_message((long) sb.st_size, folder))) {
			if(locked)
				unlockfolder(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;
		if(locked)
			unlockfolder(folder);
		return nmsg;
	}

	if(locked)
		unlockfolder(folder);
	return msg;
}

int
mbox_find_text(struct _mail_folder *folder, char *text, char *here,
			   int flags, fld_search_cb callback) {
	return -1;
}

long get_mbox_folder_uid(struct _mail_folder *folder) {
	struct stat sb;

	if(stat(folder->fold_path, &sb) != 0)
		return -1;

	folder->uid = sb.st_mtime;

	return folder->uid;
}

struct _mail_msg *get_mbox_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_mbox_message(uid, folder)) == NULL)
		return NULL;

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

	return msg;
}

int create_mbox_file(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct _mail_msg *msg;
	int ffd;
	struct stat sb;

	if(stat(folder->fold_path, &sb) == -1) {
		if((ffd = open(folder->fold_path, O_CREAT | O_RDWR, 00600)) == -1) {
			display_msg(MSG_WARN, "create", "Failed to create %-.127s",
						folder->fold_path);
			return -1;
		}
		close(ffd);
		if(stat(folder->fold_path, &sb) == -1)
			return -1;
	}

	init_mbox_spec(folder);
	folder->uid = sb.st_mtime;
	if(sb.st_size == 0) {
		msg = folder->messages;
		while(msg) {
			msg->status |= MNOTEXISTS;
			msg = msg->next;
		}
		spec->fsize = 0;
		folder->num_msg = 0;
		folder->unread_num = 0;
		folder->status |= FRESCAN;
	}

	return 0;
}

int expunge_mbox_folder(struct _mail_folder *folder) {
	return mbox_rewrite(folder);
}

int refresh_mbox_folder(struct _mail_folder *folder) {
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	struct _mail_folder *pfold;
	struct _mail_msg *msg;
	struct stat sb;
	size_t fsize = spec->fsize;
	FILE *ffd = NULL;
	char buf[255];
	long maxuid, nextofft;
	int locked = 0;
	int openflags = FOPEN_NOCACHE | FOPEN_NOCLOSE;

	if(!(folder->status & FLOCKED)) {
		if(lockfolder(folder) == -1) {
			display_msg(MSG_WARN, "refresh folder",
						"Can not lock folder\n%s", folder->fold_path);
			return -1;
		}
		locked = 1;
	}

	if((folder->flags & OPENED) && (folder->flags & FUNREAD))
		openflags |= FOPEN_UNREAD;

	if(stat(folder->fold_path, &sb) == -1) {
		if(locked)
			unlockfolder(folder);
		return create_mbox_file(folder);
	}

	if((folder->uid == sb.st_mtime) && (fsize == sb.st_size)) {
		if(locked)
			unlockfolder(folder);
		return 0;
	}
	folder->uid = sb.st_mtime;

#ifdef  LOCK_DEBUG
	fprintf(stderr, "Refreshing folder %s ...\n", folder->sname);
#endif

	if(sb.st_size == 0) {
		msg = folder->messages;
		while(msg) {
			msg->status |= MNOTEXISTS;
			msg = msg->next;
		}
		spec->fsize = 0;
		folder->num_msg = 0;
		folder->unread_num = 0;
		folder->status |= FRESCAN;
		if(locked)
			unlockfolder(folder);
		return 0;
	}

	reopen_folder_fd(folder);
	if(spec->ffd && (lseek(fileno(spec->ffd), 0, SEEK_SET) != 0))
		init_mbox_spec(folder);
	if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL) {
		if(locked)
			unlockfolder(folder);
		return -1;
	}

	if(fseek(ffd, fsize, SEEK_SET) == -1) {
		init_mbox_spec(folder);
		if((ffd = get_mbox_folder_fd(folder, READMBOXMODE)) == NULL) {
			if(locked)
				unlockfolder(folder);
			spec->fsize = 0;
			return -1;
		}
		display_msg(MSG_LOG, "refresh",
					"Folder %s changed size (seek failed), rescanning",
					folder->sname);
		folder->status |= FRESCAN;
		spec->fsize = sb.st_size;
		if(locked)
			unlockfolder(folder);
		if(open_mbox_folder(folder, openflags) != 0)
			return -1;
		return 1;
	}

	if(fsize == sb.st_size) {
		if(!folder->messages)
			return 0;
		maxuid = 0;
		msg = folder->messages;
		while(msg) {
			if(msg->uid > maxuid)
				maxuid = msg->uid;
			msg = msg->next;
		}
		if(maxuid == 0) {
			if(locked)
				unlockfolder(folder);
			return 0;
		}
		if((fseek(ffd, maxuid, SEEK_SET) == -1) ||
		   !fgets(buf, sizeof(buf), ffd) || !is_from(buf, NULL, 0)) {
			folder->status |= FRESCAN;
			spec->fsize = sb.st_size;
			display_msg(MSG_LOG, "refresh",
						"Folder %s has been modified (the size is still the same), rescanning",
						folder->sname);
			if(locked)
				unlockfolder(folder);
			if(open_mbox_folder(folder, openflags) != 0)
				return -1;
			return 1;
		}

		if(locked)
			unlockfolder(folder);
		return 0;
	} else if(fsize > sb.st_size) {
		display_msg(MSG_LOG, "refresh",
					"Folder %s changed size (shortened), rescanning",
					folder->sname);
		folder->status |= FRESCAN;
		spec->fsize = sb.st_size;
		if(locked)
			unlockfolder(folder);
		if(open_mbox_folder(folder, openflags) != 0)
			return -1;
		return 1;
	} else if(!fgets(buf, sizeof(buf), ffd) || !is_from(buf, NULL, 0)) {
		folder->status |= FRESCAN;
		spec->fsize = sb.st_size;
		display_msg(MSG_LOG, "refresh",
					"Folder %s changed size (increased), rescanning",
					folder->sname);
		if(locked)
			unlockfolder(folder);
		if(open_mbox_folder(folder, openflags) != 0)
			return -1;
		return 1;
	}

	folder->status |= FRESCAN;
	fstat(fileno(ffd), &sb);
	spec->fsize = sb.st_size;
	fseek(ffd, fsize, SEEK_SET);
	nextofft = fsize;

	while((nextofft < sb.st_size) && (sb.st_size > 0) &&
		  (msg = get_mbox_message(nextofft, folder)) != NULL) {
		nextofft = (long) ftell(ffd);
		msg_cache_deluid(folder, msg->uid);
		folder->num_msg++;
		if(msg->flags & UNREAD)
			folder->unread_num++;
		msg->status |= RECENT;
		folder->status |= FRECNT;

		if(((folder->status & FUNREAD) &&
			!(msg->flags & UNREAD)) ||
		   get_msg_by_uid(folder, msg->uid) || !(folder->status & OPENED)) {
			discard_message(msg);
			continue;
		}

		msg->folder = folder;
		msg->next = folder->messages;
		msg->status |= RECENT;
		folder->messages = msg;
		folder->status &= ~SORTED;
		folder->status |= FRECNT;
	}

	if((nextofft > sb.st_size) && (sb.st_size > 0)) {
		display_msg(MSG_LOG, "refresh",
					"Folder %s changed unexpectedly, rescanning",
					folder->sname);
		folder->status |= FRESCAN;
		spec->fsize = sb.st_size;
		if(locked)
			unlockfolder(folder);
		if(open_mbox_folder(folder, openflags) != 0)
			return -1;
		return 1;
	}

	if(locked)
		unlockfolder(folder);

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

	if(ferror(ffd)) {
		display_msg(MSG_WARN, "refresh folder",
					"Error reading from folder");
		init_mbox_spec(folder);
		return -1;
	}

	return 1;
}

int delete_mbox_message(struct _mail_msg *msg) {
	if(!msg || (msg->status & LOCKED))
		return -1;

	if((msg->status & MNOTEXISTS) || (msg->folder->status & FRONLY)) {
		msg->status &= ~DELPERM;
		msg->status &= ~DELETED;
		return 0;
	}

	msg->folder->status |= FRESCAN;
	msg->status |= DELETED;

	return 0;
}

int print_mbox_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;

	if(msg->num != -1)
		return print_message(msg, file, send);

	if(msg->get_header(msg) == -1)
		return -1;

	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)) &&
			   strcasecmp(hf->f_name, REALENGTH) &&
			   strcasecmp(hf->f_name, FROMLINE))
				print_header_field(hf, file, send);
			if(!strcasecmp(hf->f_name, "Date"))
				hasdate = 1;
			hf = hf->next_head_field;
		}

		if(!hasdate)
			fprintf(file, "Date: %s\n",
					get_arpa_date(msg->header->snt_time));
		if(!send)
			fprintf(file, "%s: %04X\n", STATUS_FIELD, msg->flags & 0xFFFF);

		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_mbox_message_body(struct _mail_msg *msg, FILE * file) {
	FILE *ffd;
	char buf[255];
	off_t shuid = msg->uid;

	if(!msg || !file || (msg->uid == -1))
		return -1;

	if(msg->status & MNOTEXISTS)
		return -1;

	if(msg->num != -1)
		return print_message_body(msg, file);

	if((ffd = get_mbox_folder_fd(msg->folder, READMBOXMODE)) == NULL)
		return -1;

	if(fseek(ffd, msg->uid, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "print message body",
					"Can not access message (%ld)", msg->uid);
		return -1;
	}

	if(!fgets(buf, sizeof(buf), ffd)) {
		display_msg(MSG_WARN, "print message body",
					"Invalid message (%ld)", msg->uid);
		return -1;
	}

	if(is_from(buf, NULL, 0) == 0) {
		display_msg(MSG_WARN, "print message body",
					"Invalid from line in message (%ld)", msg->uid);
		return -1;
	}

	shuid = ftell(ffd);
	if(fseek(ffd, msg->header->header_len, SEEK_CUR) == -1) {
		display_msg(MSG_WARN, "print message body",
					"Can not access message body");
		return -1;
	}

	buf[0] = '\0';
	while((ftell(ffd) < (shuid + msg->msg_len)) && fgets(buf, sizeof(buf), ffd)) {
		/*
		   if (!strncmp(buf, "From ", 5))
			fputc('>', file);
		*/
		fputs(buf, file);
	}

	if(!strchr(buf, '\n'))
		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;
	}

	return 0;
}

int get_mbox_message_text(struct _mail_msg *msg, struct _mime_msg *mime) {
	struct _mbox_spec *spec = (struct _mbox_spec *) msg->folder->spec;
	FILE *ffd;
	long mlen, mofft, mdiff, mpage;
	int lofft, mimen;
	void *ptr;
	struct _mime_msg *mime1;

	if(msg->uid == -1)
		return -1;

	if(msg->num != -1)
		return get_message_text(msg, mime);

	if(mime) {
		mimen = 0;
		mime1 = msg->mime;
		while(mime1 && mime1 != mime)
			mime1 = mime->mime_next;
		if(mime1 == NULL)
			return -1;
		if(msg->get_file(msg) == NULL)
			return -1;
		mime = msg->mime;
		while(mime && mimen--)
			mime = mime->mime_next;
		return get_message_text(msg, mime);
	}

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

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

	mpage = xfmail_getpagesize();
	lofft = (msg->uid % mpage);
	mofft = msg->uid - lofft;
	mlen = msg->msg_len;
	//mlen = ((maxlen > (lofft + msg->msg_len)) || (maxlen <= 0)) ? (lofft + msg->msg_len) : maxlen + lofft;
	if((mlen + mofft) > spec->fsize)
		mlen = spec->fsize - mofft;

	if(mbox_changed(msg->folder))
		refresh_mbox_folder(msg->folder);

	if(msg->status & MNOTEXISTS)
		return -1;

	if((ffd = get_mbox_folder_fd(msg->folder, READMBOXMODE)) == NULL)
		return -1;

	mdiff = spec->fsize - (mofft + mlen);
	if(mdiff < 0) {
		display_msg(MSG_WARN, "get_message_text",
					"message is no longer in the mailbox");
		msg->status |= MNOTEXISTS;
		init_mbox_spec(msg->folder);
		return -1;
	}

	if(mdiff > mpage) {
		mlen += mpage;
		mdiff -= mpage;
	} else {
		mlen += mdiff;
		mdiff = 0;
	}

	if(mlen && ((mlen % mpage) == 0)) {
		if(mdiff)
			mlen++;
		else
			mlen--;
	}

	if(locking == -1)
		locking =
		Config.getInt("spoolock", MBOX_DOT_LOCK | MBOX_KERNEL_LOCK);
	if(locking & MBOX_KERNEL_LOCK) {
#if !defined(HAVE_FLOCK) && defined(HAVE_LOCKF)
		if(fseek(ffd, 0, SEEK_SET) == -1) {
			display_msg(MSG_WARN, "get_message_text", "Can not seek");
			return -1;
		}

		if(msg->folder->status & FLOCKED)
			lockf(fileno(ffd), F_ULOCK, 0);
#endif
	}

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

	if(msg->msg_body == NULL) {
		display_msg(MSG_WARN, "get_message_text", "mmap failed");
		init_mbox_spec(msg->folder);
		return -1;
	}

	mboxmsg = msg->msg_body;
	mboxmsglen = mlen;

#if defined(HAVE_MADVISE) && defined(MADV_SEQUENTIAL)
	madvise(msg->msg_body, mlen, MADV_SEQUENTIAL);
#endif

	msg->msg_body_len = msg->msg_len;
	msg->msg_body += lofft;
	if((ptr = memchr(msg->msg_body, '\n', msg->msg_body_len)) != NULL) {
        msg->msg_body_len -= (char*)ptr - msg->msg_body + 1;
		msg->msg_body = (char *) ptr;
		msg->msg_body++;
	}
	msg->msg_body[msg->msg_body_len] = '\0';

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

	return 0;
}

void free_mbox_message_text(struct _mail_msg *msg) {
#if !defined(HAVE_FLOCK) && defined(HAVE_LOCKF)
	FILE *ffd;
#endif

	if((msg->msg_body == NULL) || (mboxmsg == NULL))
		return;

	if(msg->num != -1) {
		free_message_text(msg);
		return;
	}

	msg->msg_body[msg->msg_body_len] = '\n';
	munmap(mboxmsg, mboxmsglen);
	msg->msg_body = NULL;
	msg->msg_body_len = 0;

	if(locking == -1)
		locking =
		Config.getInt("spoolock", MBOX_DOT_LOCK | MBOX_KERNEL_LOCK);
	if(locking & MBOX_KERNEL_LOCK) {
#if !defined(HAVE_FLOCK) && defined(HAVE_LOCKF)
		if(msg->folder->status & FLOCKED) {
			if((ffd = get_mbox_folder_fd(msg->folder, READMBOXMODE)) !=
			   NULL) {
				fseek(ffd, 0, SEEK_SET);
				lockf(fileno(ffd), F_LOCK, 0);
			}
		}
#endif
	}
#ifdef  MMAP_MSG
	mmsg = NULL;
	mmpos = 0;
	mmofft = 0;
	mmlen = 0;
	mmmax = 0;
	mmapfd = -1;
#endif

	return;
}

char *get_mbox_msg_file(struct _mail_msg *msg) {
	static char buf[255];
	struct stat sb;
	FILE *fd;
	long mnum;
	char *ptr, *ptr1;
	size_t len;

	if((msg->uid == -1) || (msg->status & MNOTEXISTS))
		return "does not exists";

	if((msg->type != M_MBOX) ||
	   (msg->folder && (msg->folder->type != F_MBOX))) return NULL;

	if(msg->num != -1) {
		snprintf(buf, sizeof(buf), "%s/%ld", fmbox->fold_path, msg->num);
		if(lstat(buf, &sb) == 0)
			return buf;
		msg->num = -1;
	}

	if((mnum = get_new_name(fmbox)) == -1) {
		display_msg(MSG_WARN, "MBOX", "No space in %s", fmbox->fold_path);
		return NULL;
	}

	snprintf(buf, sizeof(buf), "%s/%ld", fmbox->fold_path, mnum);
	if((fd = fopen(buf, "w")) == NULL) {
		display_msg(MSG_WARN, "MBOX", "Can not create\n%s", buf);
		return NULL;
	}

	msg->free_text(msg);
	if(msg->get_text(msg, NULL) == -1) {
		display_msg(MSG_WARN, "MBOX", "Can not access\n%s", buf);
		init_mbox_spec(msg->folder);
		fclose(fd);
		unlink(buf);
		return NULL;
	}

	if(fwrite(msg->msg_body, msg->msg_body_len, 1, fd) != 1) {
		display_msg(MSG_WARN, "MBOX", "Can not write to\n%s", buf);
		fclose(fd);
		msg->free_text(msg);
		unlink(buf);
		return NULL;
	}

	fclose(fd);

	ptr = msg->msg_body;
	msg->header->header_len = len = msg->msg_body_len;
	while((len > 0) && (ptr1 = (char *) memchr(ptr, '\n', len)) != NULL) {
		len -= ((size_t) (ptr1 - ptr));
		if(len <= 0)
			break;

		ptr1++;
		if(*ptr1 == '\r')
			ptr1++;

		if((*ptr1 == '\n') || (*ptr1 == '\0')) {
			msg->header->header_len = (ptr1 - msg->msg_body + 1);
			break;
		}

		ptr = ptr1;
	}

	msg->num = mnum;
	msg->msg_len = msg->msg_body_len;
	msg->free_text(msg);
	if(msg->mime) {
		discard_mime(msg->mime);
		msg->mime = NULL;
		mime_scan(msg);
	}
	return buf;
}

int update_mbox_message(struct _mail_msg *msg) {
	char buf[255];
	int nl, locked = 0;
	unsigned long offset;
	FILE *ffd;

	if(msg->status & MNOTEXISTS)
		return -1;

	if(msg->num != -1) {
		if((msg->status & CHANGED) || (msg->status & MMODIFIED))
			msg->folder->status |= FREWRTE;
		return update_message(msg);
	}

	if(msg->uid < 0)
		return -1;

	if(msg->flags == msg->header->flags)
		return 0;

	if(msg->folder->status & FRONLY) {
		msg->flags = msg->header->flags;
		return 0;
	}

	if((msg->flags & UNREAD) != (msg->header->flags & UNREAD)) {
		msg->folder->status |= FREWRTE;
		return 0;
	}

	if(!(msg->folder->status & FLOCKED)) {
		if(lockfolder(msg->folder) == -1)
			return -1;
		locked = 1;
	}

	if((ffd = get_mbox_folder_fd(msg->folder, WRITEMBOXMODE)) == NULL) {
		if(locked)
			unlockfolder(msg->folder);
		return -1;
	}

	if(msg->folder->status & FRONLY) {
		msg->flags = msg->header->flags;
		if(locked)
			unlockfolder(msg->folder);
		display_msg(MSG_WARN, "update message", "Read-only folder");
		return 0;
	}

	if(fseek(ffd, msg->uid, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "update message",
					"Can not access message (%ld)", msg->uid);
		if(locked)
			unlockfolder(msg->folder);
		return -1;
	}

	if(!fgets(buf, sizeof(buf), ffd)) {
		display_msg(MSG_WARN, "update message",
					"Error reading message (%ld)", msg->uid);
		if(locked)
			unlockfolder(msg->folder);
		return -1;
	}

	offset = ftell(ffd);
	while(fgets(buf, sizeof(buf), ffd)) {
		nl = strip_newline(buf);
		if(strlen(buf) < 1)
			break;
		if(!strncmp(buf, STATUS_FIELD, strlen(STATUS_FIELD))) {
			offset += strlen(STATUS_FIELD);
			if(fseek(ffd, offset, SEEK_SET) == -1) {
				if(locked)
					unlockfolder(msg->folder);
				return -1;
			}
			switch(nl) {
				case 0:
					fprintf(ffd, ": %04X", msg->flags & 0xFFFF);
					break;

				case 1:
					fprintf(ffd, ": %04X\n", msg->flags & 0xFFFF);
					break;

				case 2:
					fprintf(ffd, ": %04X\r\n", msg->flags & 0xFFFF);
					break;
			}
			msg->header->flags = msg->flags;
			cache_msg(msg);
			if(locked)
				unlockfolder(msg->folder);
			return 0;
		}
		offset = ftell(ffd);

	}

	msg->folder->status |= FREWRTE;
	if(locked)
		unlockfolder(msg->folder);
	return 0;
}

int get_mbox_message_header(struct _mail_msg *msg) {
	FILE *ffd;
	char buf[255], fromline[255];
	int eoh;
	time_t rt;
	off_t shuid = msg->uid;
	struct _head_field *hf;

	if(!msg || (msg->uid == -1))
		return -1;

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

	if(msg->num != -1) {
		msg->status &= ~CHANGED;
		msg->update(msg);
		return get_message_header(msg);
	}

	if(mbox_changed(msg->folder))
		refresh_mbox_folder(msg->folder);

	if(msg->status & MNOTEXISTS)
		return -1;

	if((ffd = get_mbox_folder_fd(msg->folder, READMBOXMODE)) == NULL)
		return -1;

	if(fseek(ffd, msg->uid, SEEK_SET) == -1) {
		display_msg(MSG_WARN, "get header", "Can not access message (%ld)",
					msg->uid);
		init_mbox_spec(msg->folder);
		return -1;
	}

	if(!fgets(buf, sizeof(buf), ffd)) {
		display_msg(MSG_WARN, "get header", "Error reading message (%ld)",
					msg->uid);
		init_mbox_spec(msg->folder);
		return -1;
	}

	if((rt = is_from(buf, NULL, 0)) == 0) {
		display_msg(MSG_WARN, "get header",
					"Corrupt message/folder (%ld - no From line)\nMessage deleted?",
					msg->uid);
		init_mbox_spec(msg->folder);
		msg->status |= MNOTEXISTS;
		return -1;
	}

	shuid = ftell(ffd);

	*fromline = '\0';
	*buf = '\0';
	if((hf = find_field_noload(msg, FROMLINE)) != NULL)
		strcpy(fromline, hf->f_line);

	if((hf = find_field_noload(msg, REALENGTH)) != NULL)
		strcpy(buf, hf->f_line);

	discard_message_header(msg);
	if((msg->header = get_msg_header(ffd, 0, &eoh)) == NULL) {
		display_msg(MSG_WARN, "get header",
					"Message is corrupt\n(Can not parse message header)");
		init_mbox_spec(msg->folder);
		return -1;
	}

	msg->header->header_len = ftell(ffd) - shuid;
	if(msg->header->rcv_time == 0)
		msg->header->rcv_time = rt;
	if(msg->header->snt_time == 0)
		msg->header->snt_time = rt;
	msg->flags |= msg->header->flags;
	msg->status &= ~H_SHORT;
	if(*buf)
		replace_field_noload(msg, REALENGTH, buf);
	if(*fromline)
		replace_field_noload(msg, FROMLINE, fromline);

	return 0;
}

unsigned long get_mbox_message_validity(struct _mail_msg *msg) {
	return 0;
}

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

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

	msg_cache_del(msg);
	if(msg->status & MNOTEXISTS)
		return -1;

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

	if((msg1 = get_mbox_message(msg->uid, msg->folder)) == NULL)
		return -1;

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

	return 0;
}

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

	if(!path)
		return NULL;

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

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

struct _mail_folder *create_mbox_folder(struct _mail_folder *pfolder,
										char *name) {
	struct _mail_folder *fld;
	struct stat sb;
	int ffd;
	char *p, path[255];
	struct _mbox_spec *spec;

	if(!name)
		return NULL;

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

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

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

		p++;
	}

	if(*name == '/')
		strcpy(path, name);
	else
		snprintf(path, sizeof(path), "%s/%s",
				 pfolder ? pfolder->fold_path : mailbox_path, name);

	if((fld = get_mh_folder_by_path(path)) != NULL)
		return fld;

	if(stat(path, &sb) != -1) {
		if(!(sb.st_mode & S_IFREG)) {
			display_msg(MSG_WARN, "create folder",
						"%s exists, but it's not a file", path);
			return NULL;
		}
	} else {
		if(errno != ENOENT) {
			display_msg(MSG_WARN, "create folder", "Error accessing\n%s",
						path);
			return NULL;
		}

		if((ffd = open(path, O_CREAT | O_RDWR, 00600)) == -1) {
			display_msg(MSG_WARN, "create folder", "Can not create\n%s",
						path);
			return NULL;
		}

		if(stat(path, &sb) == -1) {
			display_msg(MSG_WARN, "create folder", "Can not access\n%s",
						path);
			return NULL;
		}
	}

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

	fld->status = (FSHORTH | NOINFR);
	snprintf(fld->fold_path, sizeof(fld->fold_path), "%s", path);
	mbox_folder(fld);
	append_folder(fld, 0);

	fld->sname = strdup(get_folder_short_name(fld));
	fld->uid = sb.st_mtime;
	spec = (struct _mbox_spec *) malloc(sizeof(struct _mbox_spec));
	spec->ffd = NULL;
	spec->fsize = sb.st_size;
	spec->mode[0] = '\0';
	fld->spec = (void *) spec;
	if((sb.st_mode & S_IWUSR) != S_IWUSR)
		fld->status |= FRONLY;

	folder_sort &= ~FLD_SORTED;

	return fld;
}

char *skip_word(char *str) {
	while(*str && (*str != ' '))
		str++;

	while(*str && (*str == ' '))
		str++;

	return str;
}

time_t is_from(char *str, char *retpath, int retpathlen) {
	struct tm tm;
	char *p, *p1;
	int len;

	if(strncmp("From ", str, 5))
		return 0;

	str = skip_word(str);
	if(!*str)
		return 0;

	if(get_day(str) == -1) {
		p = str;
		while(*p) {
			switch(*p) {
				case ' ':
					break;

				case '"':
				case '\'':
					if((p1 = strchr(p + 1, *p)) != NULL)
						p = p1;
					break;
			}
			if(*p == ' ')
				break;
			p++;
		}
		if(*p != ' ')
			return 0;

		if(retpath && retpathlen) {
			len = p - str;
			if(len > retpathlen)
				len = retpathlen;
			strncpy(retpath, str, len);
			retpath[len] = '\0';
		}

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

		if(get_day(str) == -1)
			return 0;
	}

	str = skip_word(str);
	if(!*str)
		return 0;

	if(get_day(str) != -1) {
		str = skip_word(str);
		if(!*str)
			return 0;
	}

	if((tm.tm_mon = get_month(str)) == -1)
		return 0;

	str = skip_word(str);
	if(!*str)
		return 0;

	if(sscanf(str, "%d", &tm.tm_mday) != 1)
		return 0;

	str = skip_word(str);
	if(!*str)
		return 0;

	if(sscanf(str, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) {
		if(sscanf(str, "%d:%d", &tm.tm_hour, &tm.tm_min) != 2)
			return 0;
		else
			tm.tm_sec = 0;
	}

	str = skip_word(str);
	if(!*str)
		return 0;

	if(isalpha(*str) || (*str == '+') || (*str == '-') || (*str == '?')) {
		str = skip_word(str);
		if(!*str)
			return 0;

		if(isalpha(*str)) {
			str = skip_word(str);
			if(!*str)
				return 0;
		}
	}

	if(sscanf(str, "%d", &tm.tm_year) != 1)
		return 0;

	if(tm.tm_year > 1900)
		tm.tm_year -= 1900;

	if(tm.tm_year == 70)   /* hack */
		tm.tm_sec = 1;

	tm.tm_isdst = 0;
	tm.tm_yday = 0;
	tm.tm_wday = 0;

#ifdef  HAVE_TM_ZONE
	tm.tm_zone = NULL;
	tm.tm_gmtoff = 0;
#endif

	return mktime(&tm);
}

void get_from(struct _mail_msg *msg, char *buf, FILE * file) {
	char abuf[255], *p;
	struct _head_field *hf;

	if((hf = find_field_noload(msg, FROMLINE)) != NULL) {
		if(file)
			fprintf(file, "%s\n", hf->f_line);

		if(buf)
			sprintf(buf, "%s\n", hf->f_line);

		return;
	}

	snprintf(abuf, sizeof(abuf), "%s",
		msg->header->From ? msg->header->From->addr : "unknown");
	while((p = strchr(abuf, ' ')) != NULL)
		*p = '_';

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

	if(file)
		fprintf(file, "From %s  %s", abuf, ctime(&msg->header->rcv_time));

	if(buf)
		sprintf(buf, "From %s  %s", abuf, ctime(&msg->header->rcv_time));

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

	return;
}

int need_rewrite(struct _mail_folder *folder) {
	struct _mail_msg *msg;

	if(folder->status & FRONLY)
		return 0;

	if(folder->status & FREWRTE)
		return 1;

	msg = folder->messages;
	while(msg) {
		if(msg->status & MNOTEXISTS) {
			msg = msg->next;
			continue;
		}
		if((msg->status & CHANGED) ||
		   (msg->status & MMODIFIED) ||
		   (msg->status & MOVED) ||
		   (msg->status & DELETED) || (msg->status & DELPERM)) {
			folder->status |= FREWRTE;
			return 1;
		}
		msg = msg->next;
	}

	return 0;
}

int mbox_rewrite(struct _mail_folder *folder) {
	int osort = folder->sort;
	struct _mbox_spec *spec = (struct _mbox_spec *) folder->spec;
	FILE *ffd, *nfd;
	struct _mail_msg *msg, *msgprev;
	char tmpfile[255], buf[255];
	size_t l, m_len, h_len;
	off_t mofft, shuid;
	int wnum, onum, oflags, locked = 0;
	struct timeval tval[2];
	struct stat sb;

	if(!need_rewrite(folder))
		return 0;

	if(!(folder->status & OPENED) || (folder->status & FUNREAD))
		folder->open(folder, FOPEN_NOCLOSE | FOPEN_NOCACHE);
	else
		folder->refresh(folder);

	if((ffd = get_mbox_folder_fd(folder, WRITEMBOXMODE)) == NULL)
		return -1;

	if(folder->status & FRONLY) {
		folder->status &= ~FREWRTE;
		return 0;
	}

	if((folder->status & FNOMOD) &&
	   !display_msg(MSG_QUEST, "save changes", "Rewrite %s mailbox?",
					folder->sname)) {
		folder->status &= ~FREWRTE;
		return 0;
	}

	if(!(folder->status & FLOCKED)) {
		if(lockfolder(folder) == -1)
			return -1;
		locked = 1;
	}

	if(stat(folder->fold_path, &sb) == -1) {
		display_msg(MSG_WARN, "rewrite", "can not stat %s",
					folder->fold_path);
		return -1;
	}

	folder->sort = BY_UID | MSG_ASCEND;
	sort_folder(folder);
	folder->sort = osort;
	folder->status &= ~SORTED;

	strcpy(tmpfile, get_temp_file("mbox"));
	if((nfd = fopen(tmpfile, "w")) == NULL) {
		display_msg(MSG_WARN, "write", "can not open\n%s", tmpfile);
		if(locked)
			unlockfolder(folder);
		return -1;
	}

	wnum = 1;
	msgprev = msg = folder->messages;
	while(msg) {
		msg->folder = folder;
		msg_cache_deluid(folder, msg->uid);
		if((msg->status & MNOTEXISTS) && (msg->status & LOCKED)) {
			msgprev = msg;
			msg = msg->next;
			continue;
		}

		if(!(msg->status & LOCKED) &&
		   ((msg->status & MOVED) ||
			(msg->status & DELETED) ||
			(msg->status & MNOTEXISTS) || (msg->status & DELPERM))) {

			if((msg->status & DELETED) &&
			   !(msg->folder->status & NOTRASH) &&
			   !(msg->folder->flags & FNTRASH) &&
			   !(msg->status & DELPERM)) {
				display_msg(MSG_STAT, NULL, "Moving %ld to trash",
							msg->uid);
				if(trash->move(msg, trash) == -1) {
					msg->status &= ~DELETED;
					if(locked)
						unlockfolder(folder);
					fclose(nfd);
					unlink(tmpfile);
					return -1;
				}
			} else if(msg->num > 0) {
				snprintf(buf, sizeof(buf), "%s/%ld", fmbox->fold_path,
						 msg->num);
				unlink(buf);
			}

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

			if(folder->num_msg)
				folder->num_msg--;

			if(msg == folder->messages) {
				folder->messages = msg->next;
				discard_message(msg);
				msgprev = msg = folder->messages;
			} else {
				msgprev->next = msg->next;
				discard_message(msg);
				msg = msgprev->next;
			}
			continue;
		}

		oflags = msg->flags;
		msg->get_header(msg);
		msg->flags = oflags;
		update_clen(msg);
		set_status_by_flags(msg);
		mofft = ftell(nfd);
		get_from(msg, buf, nfd);
		shuid = ftell(nfd);
		delete_all_fields(msg, FROMLINE);
		print_message_header(msg, nfd);
		strip_newline(buf);
		add_field(msg, FROMLINE, buf);
		if(fflush(nfd) == EOF) {
			display_msg(MSG_WARN, "rewrite", "Can not write message");
			if(locked)
				unlockfolder(folder);
			fclose(nfd);
			unlink(tmpfile);
			return -1;
		}
		h_len = ftell(nfd);

		if((wnum++ % 10) == 1)
			display_msg(MSG_STAT, NULL, "Writing %s : %d%%",
						folder->fold_path,
						(mofft * 100) / (spec->fsize + 1));

		oflags = msg->status;
		onum = msg->num;

		if(msg->print_body(msg, nfd) == -1) {
			if(locked)
				unlockfolder(folder);
			fclose(nfd);
			unlink(tmpfile);
			msg->status = oflags;
			msg->num = onum;
			return -1;
		}

		msg->status = oflags;
		msg->num = onum;

		if(fflush(nfd) == EOF) {
			display_msg(MSG_WARN, "rewrite", "Can not write message");
			if(locked)
				unlockfolder(folder);
			fclose(nfd);
			unlink(tmpfile);
			return -1;
		}
		m_len = ftell(nfd);
		fputc('\n', nfd);

		msg->uid = mofft;
		msg->msg_len = m_len - shuid;
		msg->header->header_len = h_len - shuid;
		if(msg->num > 0) {
			snprintf(buf, sizeof(buf), "%s/%ld", fmbox->fold_path,
					 msg->num);
			unlink(buf);
		}
		msg->num = -1;
		msg->status &= ~CHANGED;
		msg->status &= ~MMODIFIED;
		msg->header->flags = msg->flags;

		msgprev = msg;
		msg = msg->next;
	}

	if(fclose(nfd) == EOF) {
		display_msg(MSG_WARN, "write", "Write to %s failed", tmpfile);
		if(locked)
			unlockfolder(folder);
		unlink(tmpfile);
		return -1;
	}

	fseek(ffd, 0, SEEK_SET);
	if((nfd = fopen(tmpfile, "r")) == NULL) {
		display_msg(MSG_WARN, "write", "can not open %s for reading",
					tmpfile);
		if(locked)
			unlockfolder(folder);
		unlink(tmpfile);
		return -1;
	}

	while((l = fread(buf, 1, sizeof(buf), nfd)) > 0)
		fwrite(buf, 1, l, ffd);
	fclose(nfd);

	spec->fsize = ftell(ffd);
	if(ftruncate(fileno(ffd), ftell(ffd)) == -1)
		display_msg(MSG_WARN, "write", "can not truncate mailbox");
	reopen_folder_fd(folder);

	tval[0].tv_sec = sb.st_atime;
	tval[1].tv_sec = sb.st_mtime;
	tval[0].tv_usec = tval[1].tv_usec = 0;
	if(utimes(folder->fold_path, tval) != 0)
		display_msg(MSG_LOG, "rewrite",
					"failed to set modification time on %s",
					folder->fold_path);

	unlink(tmpfile);
	if(locked)
		unlockfolder(folder);
	sort_folder(folder);
	folder->status &= ~FREWRTE;

	return 0;
}

void update_clen(struct _mail_msg *msg) {
	char buf[10];
	size_t clen = msg->msg_len - msg->header->header_len;

	delete_all_fields(msg, MIME_C_LENGTH);
	if(clen <= 0) {
		display_msg(MSG_LOG, "FOLDER", "Invalid %s, ignoring",
					MIME_C_LENGTH);
		return;
	}

	sprintf(buf, "%d", clen);
	replace_field(msg, MIME_C_LENGTH, buf);

	return;
}

long mbox_inc_mail(struct _retrieve_src *source, long *notify) {
	struct _mail_folder *ifold;
	struct _mail_msg *msg;
	struct _spool_src *spool;
	long num_msgs = 0, num_diff = 0;
	int locked = 0;

	if(source->flags & RSRC_DISABLED)
		return 0;

	spool = (struct _spool_src *) source->spec;

	if((ifold = get_mbox_folder_by_path(spool->path)) == NULL) {
		if(
		  (ifold =
		   create_mbox_folder((struct _mail_folder *) NULL,
							  spool->path)) == NULL) {
			display_msg(MSG_WARN, "spool", "Can not access %s",
						spool->path);
			return -1;
		}
	}
	ifold->status |= (FRESCAN | SYSTEM | NOINFR | FNOCLSE);
	ifold->status &= ~FSHORTH;
	if(!(spool->flags & SSRC_REWRITE))
		ifold->status |= FNOMOD;

	if(get_mbox_folder_fd(ifold, WRITEMBOXMODE) == NULL) {
		display_msg(MSG_WARN, "spool",
					"Can not access %s in read-write mode", spool->path);
		return -1;
	}

	if(!(ifold->status & FLOCKED)) {
		if(lockfolder(ifold) == -1) {
			display_msg(MSG_WARN, "spool",
						"%s is locked, please try again later",
						spool->path);
			return -1;
		}
		locked = 1;
	}

	if(!(ifold->status & OPENED) || (ifold->status & FUNREAD)) {
		if(ifold->open(ifold, FOPEN_NOCLOSE | FOPEN_NOCACHE) == -1) {
			if(locked)
				unlockfolder(ifold);
			return -1;
		}
	} else {
		if(ifold->refresh(ifold) == -1) {
			if(locked)
				unlockfolder(ifold);
			return -1;
		}
	}

	msg = ifold->messages;
	while(msg) {
		if((msg->status & LOCKED) || (msg->status & MNOTEXISTS)) {
			msg = msg->next;
			continue;
		}


		if((msg->flags & UNREAD) ||
		   (spool->flags & SSRC_TRUNCATE) || (msg->status & RECENT)) {
			set_flags_by_status(msg);
			convert_fields(msg);
			msg->status |= (CHANGED | RECENT);
			if(source->flags & RSRC_MARKREAD)
				msg->flags &= ~UNREAD;
			replace_field(msg, "X-RDate", get_arpa_date(time(NULL)));
			replace_field(msg, SOURCE_FIELD, source->name);
			msg->header->rcv_time = time(NULL);

#ifdef WITH_FACES
			update_faces(msg);
#endif

			switch(apply_rule(msg, 0)) {
				case 0:
					if(!(source->flags & RSRC_NONOTIFY))
						(*notify)++;
					break;

				case -1:
					if(locked)
						unlockfolder(ifold);
					return -1;
					break;
			}

			if(spool->flags & SSRC_TRUNCATE)
				msg->status |= (DELETED | DELPERM);
			else {
				msg->folder = ifold;
				msg->status &= ~DELETED;
				msg->status &= ~DELPERM;
				msg->status &= ~MOVED;
				msg->status &= ~RECENT;
				if(msg->flags & UNREAD) {
					msg->flags &= ~UNREAD;
					if(ifold->unread_num)
						ifold->unread_num--;
				}
			}
			num_msgs++;
			num_diff++;
			ifold->status |= FRESCAN;
			display_msg(MSG_STAT, NULL, "Retrieving %d (%-.64s)", num_msgs,
						msg->header->Subject ? msg->header->
						Subject : "* No Subject *");
		} else {
			if((msg->status & CHANGED) ||
			   (msg->status & MMODIFIED) ||
			   (msg->status & MOVED) ||
			   (msg->status & DELETED) ||
			   (msg->status & DELPERM) ||
			   (msg->flags != msg->header->flags)) num_diff++;
		}
		msg = msg->next;
	}

	if((spool->flags & SSRC_TRUNCATE) ||
	   (!(ifold->status & FNOMOD) &&
		((num_diff * 100 / (ifold->num_msg + 1)) > 10))) {
		if(mbox_rewrite(ifold) == -1) {
			if(locked)
				unlockfolder(ifold);
			return -1;
		}
	}

	ifold->status &= ~FRECNT;
	display_msg(MSG_STAT, NULL, "");
	if(locked)
		unlockfolder(ifold);

	return num_msgs;
}

void free_spool_source(struct _retrieve_src *source) {
	if(source->spec) {
		free(source->spec);
		source->spec = NULL;
	}
	return;
}

void init_spool_source(struct _retrieve_src *source) {
	struct _spool_src *spool;

	if(source->spec == NULL) {
		source->spec =
		(struct _spool_src *) malloc(sizeof(struct _spool_src));
		spool = (struct _spool_src *) source->spec;
		snprintf(spool->path, sizeof(spool->path), "%s/%s", _PATH_MAILDIR,
				 user_n);
		spool->flags = SSRC_TRUNCATE | SSRC_REWRITE;
	}

	return;
}

int load_spool_source(struct _retrieve_src *source, FILE * fd) {
	struct _spool_src *spool;
	char buf[MAXPATHLEN + 4];
	struct _mail_folder *ifold;

	spool = (struct _spool_src *) source->spec;
	if(!fgets(buf, MAXPATHLEN + 3, fd))
		return -1;
	if(sscanf(buf, "%d %s", &spool->flags, spool->path) != 2)
		return -1;

	if((ifold = get_mbox_folder_by_path(spool->path)) == NULL) {
		if(
		  (ifold =
		   create_mbox_folder((struct _mail_folder *) NULL,
							  spool->path)) == NULL) {
			display_msg(MSG_WARN, "spool", "Can not access %s",
						spool->path);
			return -1;
		}
	}
	ifold->status |= (FRESCAN | SYSTEM | NOINFR | FNOCLSE);
	ifold->status &= ~FSHORTH;
	if(!(spool->flags & SSRC_REWRITE))
		ifold->status |= FNOMOD;
	else
		ifold->status &= ~FNOMOD;

	if(ifold->sname) {
		snprintf(buf, sizeof(buf), "%s (spool)",
				 get_folder_short_name(ifold));
		free(ifold->sname);
	} else
		sprintf(buf, "spool");
	ifold->sname = strdup(buf);
	if(!ifold->descr)
		ifold->descr = strdup("spool mailbox");
	ifold->open(ifold, FOPEN_NOCACHE);

	return 0;
}

int save_spool_source(struct _retrieve_src *source, FILE * fd) {
	struct _spool_src *spool;

	spool = (struct _spool_src *) source->spec;
	fprintf(fd, "%d %s\n", spool->flags, spool->path);

	return 0;
}

void spool_source(struct _retrieve_src *source) {
	init_spool_source(source);
	source->type = RSRC_SPOOL;
	source->retrieve = mbox_inc_mail;
	source->free = free_spool_source;
	source->init = init_spool_source;
	source->load = load_spool_source;
	source->save = save_spool_source;

	return;
}
