/* $Id: imap.cpp,v 1.42 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>
#include <connectionManager.h>
#include <compat.h>

extern class connectionManager ConMan;

#ifdef USE_GPASSWD
	#include <gpasswd.h>
	extern gPasswd Passwd;
#endif

#ifdef SASL
/* Set up for SASL authentication */
	#include "sasl.h"
extern struct sasl_client krb_sasl_client;
	#define gen_sasl_client krb_sasl_client
int sasl_done;
void *AuthState;


static int prot_req;
static int protlevel;

sasl_encodefunc_t *encodefunc;
sasl_decodefunc_t *decodefunc;

	#ifdef PROT_STDIO
/* Set up for protected stdio routines */
		#include "prot.h"

		#define MAX_HOST        128 /* host name length limit  */
		#define MAX_CONNECTIONS 4   /* connection number limit */
		#define CONN_BUF        128 /* connection buffer       */

struct _xf_conn {
	int sock;           /* socket file descriptor */
	char lbuf[CONN_BUF];    /* socket buffer          */
	char host[MAX_HOST];    /* host name (IP addr)    */
}
xf_conn;
	#endif
#endif

#define MAX_IMAP_STRING     524288  /* don't malloc more then this */
#define MAX_PLIST_NESTING   16  /* max nesting level of plist */
#define IMAP_NUM_FLD        "X-IMAP-Num"    /* field to store IMAP number */

#define IMAP_INBOX  "INBOX"
#define IMAP_TRASH  "trash"

/* commands */
#define ICOM_NOCOM  0x00    /* just wait for greeting */
#define ICOM_CAPABILITY 0x01    /* CAPABILITY */
#define ICOM_NOOP   0x02    /* NOOP */
#define ICOM_LOGOUT 0x03    /* LOGOUT */
#define ICOM_AUTHENTICATE   0x04    /* AUTHENTICATE */
#define ICOM_LOGIN  0x05    /* LOGIN */
#define ICOM_SELECT 0x06    /* SELECT */
#define ICOM_EXAMINE    0x07    /* EXAMINE */
#define ICOM_CREATE 0x08    /* CREATE */
#define ICOM_DELETE 0x09    /* DELETE */
#define ICOM_RENAME 0x0a    /* RENAME */
#define ICOM_SUBSCRIBE  0x0b    /* SUBSCRIBE */
#define ICOM_UNSUBSCRIBE    0x0c    /* UNSUBSCRIBE */
#define ICOM_LIST   0x0d    /* LIST */
#define ICOM_LSUB   0x0e    /* LSUB */
#define ICOM_STATUS 0x0f    /* STATUS */
#define ICOM_APPEND 0x10    /* APPEND */
#define ICOM_CHECK  0x11    /* CHECK */
#define ICOM_CLOSE  0x12    /* CLOSE */
#define ICOM_EXPUNGE    0x13    /* EXPUNGE */
#define ICOM_SEARCH 0x14    /* SEARCH */
#define ICOM_FETCH  0x15    /* FETCH */
#define ICOM_STORE  0x16    /* STORE */
#define ICOM_COPY   0x17    /* COPY */
#define ICOM_UIDSEARCH  0x18    /* UID SEARCH */
#define ICOM_UIDCOPY    0x19    /* UID COPY */
#define ICOM_UIDFETCH   0x1a    /* UID FETCH */
#define ICOM_UIDSTORE   0x1b    /* UID STORE */

#define ICOM_MAXCOMM    0x1c
/* Command flags */
#define ICOM_DONTLOG    0x0100  /* don't log output */
#define ICOM_SILENT 0x0200  /* don't display error messages */
#define ICOM_DONTWAIT   0x0400  /* don't wait for server response */
#define ICOM_ABORT  0x0800  /* abort command */
#define ICOM_LOGERR 0x1000  /* only add errors to log */

#define ICOM_MATCHALL   0xff

/* command status */
#define IMAP_FATAL  -2
#define IMAP_ERROR  -1
#define IMAP_OK     0
#define IMAP_NO     1
#define IMAP_BAD    2

/* capabilities */
#define ICAP_IMAP2  0x01    /* IMAP2 */
#define ICAP_IMAP4  0x02    /* IMAP4 */
#define ICAP_IMAP4REV1  0x04    /* IMAP4rev1 */
#define ICAP_STATUS 0x08    /* has STATUS command */
#define ICAP_SCAN   0x10    /* has SCAN command */
#define ICAP_AUTH       0x20    /* does authentication */
#define ICAP_ACL        0x40    /* does acls */
#define ICAP_QUOTA      0x80    /* does quotas */

/* auth types */
#define ICAUTH_KRB4     0x01    /* does Kerberos 4 authentication */
#define ICAUTH_SKEY     0x02    /* does skey authentication */
#define ICAUTH_GSSAPI   0x04    /* does GSSAPI authentication */
#define ICAUTH_SSL      0x08    /* does SSL authentication */

/* states */
#define ISTATE_NOAUTH   0x01    /* no authentication yet */
#define ISTATE_AUTH 0x02    /* successfull authentication */
#define ISTATE_SELECT   0x03    /* mailbox selected */
#define ISTATE_NOCON    0x04    /* not connected */

/* FETCH message parts */
#define IFETCH_BODYPART     0x00
#define IFETCH_BODYSTRUCTURE    0x01
#define IFETCH_BODY     0x02
#define IFETCH_ENVELOPE     0x03
#define IFETCH_FLAGS        0x04
#define IFETCH_INTERNALDATE 0x05
#define IFETCH_RFC822HEADER 0x06
#define IFETCH_RFC822SIZE   0x07
#define IFETCH_RFC822TEXT   0x08
#define IFETCH_RFC822       0x09
#define IFETCH_UID      0x0a
#define IFETCH_MAX      0x0b

char *mparts[] = {
	"BODY[",
	"BODYSTRUCTURE",
	"BODY",
	"ENVELOPE",
	"FLAGS",
	"INTERNALDATE",
	"RFC822.HEADER",
	"RFC822.SIZE",
	"RFC822.TEXT",
	"RFC822",
	"UID",
	NULL
};

extern cfgfile Config;
extern void set_imap_timer();
extern void imap_account(struct _imap_src * imap);

char *plist_getnext(struct _imap_src *imap, char *list, char **next);
char *plist_getnext_string(struct _imap_src *imap, char *list,
						   char **next);
void end_plist(struct _imap_src *imap);
int start_plist(struct _imap_src *imap);
char *get_imap_folder_domain(struct _imap_src *imap,
							 struct _mail_folder *folder);
char *get_imap_folder_domain_path(struct _imap_src *imap,
								  struct _mail_folder *folder);
char *get_imap_folder_short_name(struct _imap_src *imap,
								 struct _mail_folder *folder);
struct _mail_folder *imap_folder_switch(struct _imap_src *imap,
										struct _mail_folder *folder);
void expand_uid_range(struct _imap_src *imap, struct _mail_folder *folder,
					  struct _mail_msg *msg, int mask1, int mask2,
					  long *uid_top, long *uid_bottom, int status);
void imap_disconnect(struct _imap_src *imap);
int imap_connect(struct _imap_src *imap);
int imap_reconnect(struct _imap_src *imap);
int delete_imap_message(struct _mail_msg *msg);
int move_to_imap_folder(struct _mail_msg *msg,
						struct _mail_folder *folder);
long move_to_imap_folder_range(struct _imap_src *imap,
							   struct _mail_msg *msg,
							   struct _mail_folder *folder);
struct _mail_msg *copy_to_imap_folder(struct _mail_msg *msg,
									  struct _mail_folder *folder);
struct _mail_msg *copy_to_imap_folder_range(struct _imap_src *imap,
											struct _mail_msg *msg,
											struct _mail_folder *folder);
int update_imap_message(struct _mail_msg *msg);
void update_imap_message_range(struct _imap_src *imap,
							   struct _mail_msg *msg);
char *imap_string(struct _imap_src *imap, char *str);
long get_imap_folder_uid(struct _mail_folder *folder);
int imap_get_recent(struct _imap_src *imap);
int imap_fetchrfc822(struct _imap_src *imap, struct _mail_msg *msg,
					 char *arg);
int imap_dummy_open_folder(struct _mail_folder *folder, int flags);

typedef int (*imap_process) (struct _imap_src * imap, int command,
							 char *tag, char *resp, char *param);

int unk_process(struct _imap_src *imap, int command, char *tag, char *resp,
				char *param);
int ok_process(struct _imap_src *imap, int command, char *tag, char *resp,
			   char *param);
int no_process(struct _imap_src *imap, int command, char *tag, char *resp,
			   char *param);
int bad_process(struct _imap_src *imap, int command, char *tag, char *resp,
				char *param);
int bye_process(struct _imap_src *imap, int command, char *tag, char *resp,
				char *param);
int disc_process(struct _imap_src *imap, int command, char *tag,
				 char *resp, char *param);
int cap_process(struct _imap_src *imap, int command, char *tag, char *resp,
				char *param);
int list_process(struct _imap_src *imap, int command, char *tag,
				 char *resp, char *param);
int stat_process(struct _imap_src *imap, int command, char *tag,
				 char *resp, char *param);
int preauth_process(struct _imap_src *imap, int command, char *tag,
					char *resp, char *param);
int flags_process(struct _imap_src *imap, int command, char *tag,
				  char *resp, char *param);
int exists_process(struct _imap_src *imap, int command, char *tag,
				   char *resp, char *param);
int recent_process(struct _imap_src *imap, int command, char *tag,
				   char *resp, char *param);
int search_process(struct _imap_src *imap, int command, char *tag,
				   char *resp, char *param);
int fetch_process(struct _imap_src *imap, int command, char *tag,
				  char *resp, char *param);
int expunge_process(struct _imap_src *imap, int command, char *tag,
					char *resp, char *param);
int append_process(struct _imap_src *imap, int command, char *tag,
				   char *resp, char *param);

void process_respcode(struct _imap_src *imap, int command, char *param);

typedef struct _imapresp {
	int command;
	char tag[6];
	char resp[32];
	char *param;
	imap_process process;
}
imapresp;

struct _imapresp imap_responses[] = {
	{ICOM_MATCHALL, "*", "OK", "$all", ok_process},
	{ICOM_MATCHALL, "*", "NO", "$all", no_process},
	{ICOM_MATCHALL, "*", "BAD", "$all", bad_process},
	{ICOM_LOGOUT, "*", "BYE", "$all", bye_process},
	{ICOM_MATCHALL, "*", "BYE", "$all", disc_process},
	{ICOM_LIST, "*", "LIST", "$all", list_process},
	{ICOM_LSUB, "*", "LSUB", "$all", list_process},
	{ICOM_CAPABILITY, "*", "CAPABILITY", "$all", cap_process},
	{ICOM_STATUS, "*", "STATUS", "$all", stat_process},
	{ICOM_MATCHALL, "*", "PREAUTH", "$all", preauth_process},
	{ICOM_SELECT, "*", "FLAGS", "$all", flags_process},
	{ICOM_EXAMINE, "*", "FLAGS", "$all", flags_process},
	{ICOM_STATUS, "*", "FLAGS", "$all", flags_process},
	{ICOM_SEARCH, "*", "SEARCH", "$all", search_process},
	{ICOM_UIDSEARCH, "*", "SEARCH", "$all", search_process},
	{ICOM_FETCH, "*", "$num", "$FETCH (", fetch_process},
	{ICOM_UIDFETCH, "*", "$num", "$FETCH (", fetch_process},
	{ICOM_STORE, "*", "$num", "$FETCH (", fetch_process},
	{ICOM_UIDSTORE, "*", "$num", "$FETCH (", fetch_process},
	{ICOM_EXPUNGE, "*", "$num", "EXPUNGE", expunge_process},
	{ICOM_APPEND, "+", "$all", "$all", append_process},

	{ICOM_MATCHALL, "*", "CAPABILITY", "$all", cap_process},

	{ICOM_MATCHALL, "*", "$num", "EXISTS", exists_process},
	{ICOM_MATCHALL, "*", "$num", "RECENT", recent_process},
	{ICOM_MATCHALL, "*", "$num", "$FETCH (", fetch_process},
	{ICOM_MATCHALL, "*", "$num", "EXPUNGE", expunge_process},
	{ICOM_MATCHALL, "$all", "OK", "$all", ok_process},
	{ICOM_MATCHALL, "$all", "NO", "$all", no_process},
	{ICOM_MATCHALL, "$all", "BAD", "$all", bad_process},
	{ICOM_MATCHALL, "$all", "$all", "$all", unk_process},   /* unknown */
	{ICOM_NOCOM, "$all", "$all", "$all", unk_process}
};

char *imap_commands[] = {
	NULL,
	"CAPABILITY",
	"NOOP",
	"LOGOUT",
	"AUTHENTICATE",
	"LOGIN",
	"SELECT",
	"EXAMINE",
	"CREATE",
	"DELETE",
	"RENAME",
	"SUBSCRIBE",
	"UNSUBSCRIBE",
	"LIST",
	"LSUB",
	"STATUS",
	"APPEND",
	"CHECK",
	"CLOSE",
	"EXPUNGE",
	"SEARCH",
	"FETCH",
	"STORE",
	"COPY",
	"UID SEARCH",
	"UID COPY",
	"UID FETCH",
	"UID STORE",
	NULL
};

#if defined(SASL) && defined(PROT_STDIO)
struct _xf_conn *get_conn(int sock);

char *imap_getline(char *s, int n, FILE * iop) {
	struct _xf_conn *conn;
	char tbuf[128], *p, *p1, *lbuf;
	int len, res, allocbuf, len1;

	if((conn = get_conn(fileno(iop))) == NULL)
		return NULL;
	lbuf = conn->lbuf;

	if(n < 0) {
		n = abs(n);
		allocbuf = 1;
	} else
		allocbuf = 0;

	if((len = strlen(lbuf)) > 0) {
		if((p = strchr(lbuf, '\n')) != NULL) {
			p1 = p + 1;
			*p = '\0';
			p--;
			if(*p == '\r')
				*p = '\0';
			if(allocbuf)
				s = (char *) malloc(strlen(lbuf) + 1);
			strcpy(s, lbuf);
			strcpy(tbuf, p1);
			strcpy(lbuf, tbuf);

			return s;
		}
		if(allocbuf) {
			s = (char *) malloc(strlen(lbuf) + 1);
			allocbuf = strlen(lbuf) + 1;
		}

		strcpy(s, lbuf);
		p = s + len;
	} else {
		if(allocbuf) {
			s = (char *) malloc(1);
			allocbuf = 1;
		}
		s[0] = '\0';
		p = s;
		len = 0;
	}

	if((res = imap_check_io_forms(imap->pin, 0)) < 0) {
		lbuf[0] = '\0';
		if(allocbuf)
			free(s);
		return((res == -2) && allocbuf) ? strdup("") : NULL;
	}

	while(len < n) {
		len1 = (n - len) > 127 ? 127 : (n - len);
		if(allocbuf) {
			allocbuf += (len1 + 1);
			s = (char *) realloc(s, allocbuf);
			p = s + strlen(s);
		}

		if((res = prot_read(imap->pin, p, len1)) == -1) {
			if((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
				if((res = imap_check_io_forms(imap->pin, 0)) < 0) {
					lbuf[0] = '\0';
					if(allocbuf)
						free(s);
					return((res == -2) && allocbuf) ? strdup("") : NULL;
				}

				continue;
			}
			display_msg(MSG_WARN, "recv: getline", "connection error");
			if(allocbuf)
				free(s);
			lbuf[0] = '\0';
			return NULL;
		}

		if(res == 0)
			break;

		p[res] = '\0';
		if((p1 = strchr(p, '\n')) != NULL) {
			*p1 = '\0';
			strcpy(lbuf, p1 + 1);
			len += (p1 - p);
			p1--;
			if(*p1 == '\r')
				*p1 = '\0';

			break;
		}
		p += res;
		len += res;
	}

	s[len] = '\0';

	if(len >= n) {
		display_msg(MSG_LOG, "recv: getline",
					"string is too long, splitting");
		return s;
	}

	if(len == 0) {
		display_msg(MSG_WARN, "recv: getline",
					"connection closed by foreign host");
		if(allocbuf)
			free(s);
		lbuf[0] = '\0';
		return NULL;
	}

	return s;
}

int imap_putline(char *s, FILE * iop)
char *s;
FILE *iop;
{
	struct _xf_conn *conn;
	char sbuf[512], *lbuf;
	int res;

	if((conn = get_conn(fileno(iop))) == NULL)
		return -1;
	lbuf = conn->lbuf;

	if(strlen(s) >= 510) {
		display_msg(MSG_WARN, "send", "line too long");
		return -1;
	}

	snprintf(sbuf, sizeof(sbuf), "%s\r\n", s);
	chwrite:
	if((res = imap_check_io_forms(imap->pout, 1)) < 0)
		return res;

	if(prot_write(imap->pout, sbuf, strlen(sbuf)) == -1) {
		if((errno == EAGAIN) || (errno == EWOULDBLOCK))
			goto chwrite;

		display_msg(MSG_WARN, "send", "connection lost");
		lbuf[0] = '\0';
		return -1;
	}

	prot_flush(imap->pout);

	return 0;
}

int imap_getdata(char *s, long n, FILE * iop, FILE * ofile) {
	struct _xf_conn *conn;
	char buf[128], *p, *p1, *lbuf;
	long rdone, res;

	if((conn = get_conn(fileno(iop))) == NULL)
		return -1;
	lbuf = conn->lbuf;

	if(n == 0)
		return 0;

	rdone = 0;

	if(ofile)
		p = buf;
	else
		p = s;

	if((res = (long) strlen(lbuf)) > 0) {
		if(res >= n) {
			if(ofile) {
				if(fwrite(lbuf, (size_t) n, 1, ofile) == EOF) {
					display_msg(MSG_WARN, "getdata", "Write failed");
					return -1;
				}
			} else {
				strncpy(s, lbuf, (int) n);
				s[n] = '\0';
			}

			strcpy(buf, lbuf + n);
			strcpy(lbuf, buf);
			return 0;
		}

		if(ofile) {
			if(fputs(lbuf, ofile) == EOF) {
				display_msg(MSG_WARN, "recv", "Write failed!");
				return -1;
			}
		} else {
			strcpy(s, lbuf);
			p += res;
		}

		lbuf[0] = '\0';
		rdone = res;
	}
	if((res = imap_check_io_forms(imap->pin, 0)) < 0) {
		lbuf[0] = '\0';
		return res;
	}

	while(rdone < n) {
		if((res = imap_check_io_forms(imap->pin, 0)) < 0) {
			lbuf[0] = '\0';
			return res;
		}

		if(
		  (res =
		   prot_read(imap->pin, p,
					 (int) (n - rdone) >
					 127 ? 127 : (int) (n - rdone))) == -1) {
			if((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
				if((res = imap_check_io_forms(imap->pin, 0)) < 0) {
					lbuf[0] = '\0';
					return res;
				}

				continue;
			}
			display_msg(MSG_WARN, "recv: getdata", "connection error");
			lbuf[0] = '\0';
			return -1;
		}
		if(res == 0) {
			display_msg(MSG_WARN, "recv: getdata",
						"connection closed by foreign host");
			lbuf[0] = '\0';
			return -1;
		}

		p[res] = '\0';
		rdone += res;

		p1 = p;
		while((p1 = strchr(p1, '\r')) != NULL)
			memmove(p1, p1 + 1, strlen(p1));

		if(ofile) {
			if(fputs(p, ofile) == EOF) {
				display_msg(MSG_WARN, "recv: getdata", "Write failed!");
				return -1;
			}
		} else
			p += strlen(p);
	}

	return 0;
}

int imap_putdata(char *s, int n, FILE * iop, FILE * ifile) {
	struct _xf_conn *conn;
	char sbuf[512], lastchar, *lbuf;
	int sent, chunk, res;

	if((conn = get_conn(fileno(iop))) == NULL)
		return -1;
	lbuf = conn->lbuf;

	if(s) {
		chwrite1:
		if((res = imap_check_io_forms(imap->pout, 1)) < 0) {
			lbuf[0] = '\0';
			return res;
		}

		if(prot_write(imap->pout, s, n) == -1) {
			if((errno == EAGAIN) || (errno == EWOULDBLOCK))
				goto chwrite1;

			display_msg(MSG_WARN, "send", "connection lost");
			lbuf[0] = '\0';
			return -1;
		}

		prot_flush(imap->pout);
	} else {
		sent = 0;
		lastchar = '\0';

		while(sent < n) {
			if(fgets(sbuf, 511, ifile) == NULL) {
				if(ferror(ifile))
					return -1;
				if(feof(ifile))
					break;
			}

			chunk = strlen(sbuf);
			if(chunk && (sbuf[chunk - 1] == '\n')) {
				if(chunk > 1)
					lastchar = sbuf[chunk - 2];

				if(lastchar != '\r') {
					sbuf[chunk - 1] = '\r';
					chunk++;
					sbuf[chunk - 1] = '\n';
					sbuf[chunk] = '\0';
				}

				lastchar = '\n';
			} else
				lastchar = chunk ? sbuf[chunk - 1] : '\0';

			if((res = imap_check_io_forms(imap->pout, 1)) < 0) {
				lbuf[0] = '\0';
				return res;
			}

			if(prot_write(imap->pout, sbuf, chunk) == -1) {
				if((errno == EAGAIN) || (errno == EWOULDBLOCK))
					continue;

				display_msg(MSG_WARN, "send", "connection lost");
				lbuf[0] = '\0';
				return -1;
			}

			sent += chunk;
		}
		prot_flush(imap->pout);
	}

	chwrite2:
	if((res = imap_check_io_forms(imap->pout, 1)) < 0) {
		lbuf[0] = '\0';
		return res;
	}

	if(prot_write(imap->pout, "\r\n", 2) == -1) {
		if((errno == EAGAIN) || (errno == EWOULDBLOCK))
			goto chwrite2;

		display_msg(MSG_WARN, "send", "connection lost");
		lbuf[0] = '\0';
		return -1;
	}

	prot_flush(imap->pout);

	return 0;
}
#endif

void
set_imap_msgnum(struct _imap_src *imap, struct _mail_msg *msg, long num) {
	char sbuf[16];

	sprintf(sbuf, "%ld", num);
	msg->status |= MNOREFRESH;
	replace_field(msg, IMAP_NUM_FLD, sbuf);
	msg->status &= ~MNOREFRESH;

	return;
}

long get_imap_msgnum(struct _imap_src *imap, struct _mail_msg *msg) {
	struct _head_field *hf;
	long msgnum;

	msg->status |= MNOREFRESH;
	if((hf = find_field(msg, IMAP_NUM_FLD)) == NULL) {
		msg->status &= ~MNOREFRESH;
		return -1;
	}
	msg->status &= ~MNOREFRESH;

	msgnum = strtol(hf->f_line, NULL, 10);
	if (msgnum == LONG_MIN || msgnum == LONG_MAX)
	    return -1;
	return msgnum;
}

struct _mail_msg *find_imap_msgnum(struct _imap_src *imap,
				   struct _mail_folder *folder, long num) {
	struct _mail_msg *msg;

	msg = folder->messages;
	while(msg) {
		if(num == get_imap_msgnum(imap, msg))
			return msg;
		msg = msg->next;
	}

	return NULL;
}

void process_respcode(struct _imap_src *imap, int command, char *param) {
	char *p, *q, rcode[127];
	int len;

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

	if(*param != '[')
		return;

	param++;
	if((p = strchr(param, ']')) == NULL)
		return;

	*p++ = '\0';
	len = strlen(param);
	if((len < 2) || (len >= 126))
		return;

	if((q = strchr(param, ' ')) != NULL)
		*q++ = '\0';

	strcpy(rcode, param);
	while(*p == ' ')
		p++;
	if(strlen(p) > 64)
		p[64] = '\0';

	if(!strcasecmp(rcode, "ALERT"))
		display_msg(MSG_WARN, "IMAP ALERT", "%s", p);
	else if(!strcasecmp(rcode, "PARSE"))
		display_msg(MSG_LOG, "IMAP PARSE ERROR", "%.64s", p);
	else if(!strcasecmp(rcode, "READ-ONLY")) {
		if(imap->ifold && (command != ICOM_EXAMINE))
			imap->ifold->status |= FRONLY;
	} else if(!strcasecmp(rcode, "READ-WRITE")) {
		if(imap->ifold)
			imap->ifold->status &= ~FRONLY;
	} else if(!strcasecmp(rcode, "TRYCREATE"))
		display_msg(MSG_WARN, "IMAP - Try to create destination mailbox",
					"%s", p);
	else if(!strcasecmp(rcode, "NEWNAME"))
		display_msg(MSG_WARN, "The mailbox has been renamed", "%s", p);
	else
		if(!strcasecmp(rcode, "UIDVALIDITY") ||
		   !strcasecmp(rcode, "UID-VALIDITY")) {
		if(q == NULL)
			display_msg(MSG_WARN, "IMAP",
						"Missing parameter in UIDVALIDITY response");
		else {
			if(imap->ifold) {
				imap->ifold->uid = strtol(q, &p, 10);
				if(*p) {
					imap->ifold->uid = -1;
					display_msg(MSG_WARN, "IMAP",
								"Invalid UIDVALIDITY value in OK response");
				}
			}
		}
	}

	return;
}

int
ok_process(struct _imap_src *imap, int command, char *tag, char *resp,
		   char *param) {
	process_respcode(imap, command, param);
	return IMAP_OK;
}

int
no_process(struct _imap_src *imap, int command, char *tag, char *resp,
		   char *param) {
	process_respcode(imap, command, param);
	return IMAP_OK;
}

int
bad_process(struct _imap_src *imap, int command, char *tag, char *resp,
			char *param) {
	process_respcode(imap, command, param);
	return IMAP_OK;
}

int
bye_process(struct _imap_src *imap, int command, char *tag, char *resp,
			char *param) {
	return IMAP_OK;
}

int
disc_process(struct _imap_src *imap, int command, char *tag, char *resp,
			 char *param) {
	display_msg(MSG_WARN, "IMAP", "Server closed connection");
	imap->istate = ISTATE_NOCON;
	imap_close(imap, 0);
	return IMAP_FATAL;
}

int
append_process(struct _imap_src *imap, int command, char *tag, char *resp,
			   char *param) {
	FILE *afd;
	char *mfile;
	int res;

	if(!imap->imsg)
		return IMAP_ERROR;

	if((mfile = imap->imsg->get_file(imap->imsg)) == NULL)
		return IMAP_ERROR;

	if((afd = fopen(mfile, "r")) == NULL)
		return IMAP_ERROR;

#if defined(SASL) && defined(PROT_STDIO)
	if(
	  (res =
	   imap_putdata(NULL, imap->imsg->msg_len * 2, imap->pout, afd)) < 0)
#else
	if((res = putdata(NULL, imap->imsg->msg_len * 2, imap->imap_out, afd))
	   < 0)
#endif
	{
		fclose(afd);
		if(res == -2)
			imap_reconnect(imap);
		else if(res == -1)
			imap_close(imap, 0);
		return IMAP_ERROR;
	}

	fclose(afd);

	return IMAP_OK;
}

int
expunge_process(struct _imap_src *imap, int command, char *tag, char *resp,
				char *param) {
	char *p;
	struct _mail_msg *msg, *msgprev, *msgnext;
	long expnum, msgnum;

	if(!imap->ifold)
		return IMAP_OK;

	imap->ifold->status &= ~FEXPNG;
	expnum = strtol(resp, &p, 10);
	if(*p || expnum == LONG_MIN || expnum == LONG_MAX) {
		display_msg(MSG_WARN, "IMAP", "Invalid EXPUNGE response");
		return IMAP_ERROR;
	}

	msg = imap->ifold->messages;
	msgprev = NULL;
	while(msg) {
		msgnext = msg->next;
		if((msgnum = get_imap_msgnum(imap, msg)) <= 0) {
			msg = msg->next;
			continue;
		}

		if(expnum == msgnum) {
			if(msg->status & LOCKED)
				msg->status |=
				(DELETED | DELPERM | IMAPDELETED | MNOTEXISTS);
			else {
				if((msg->status & UNREAD) && (imap->ifold->unread_num))
					imap->ifold->unread_num--;
				if(msgprev)
					msgprev->next = msg->next;
				else
					imap->ifold->messages = msg->next;
				discard_message(msg);
			}
			imap->ifold->status |= FRESCAN;
		} else {
			if(msgnum > expnum)
				set_imap_msgnum(imap, msg, msgnum - 1);
			msgprev = msg;
		}

		msg = msgnext;
	}
	return IMAP_OK;
}

int
cap_process(struct _imap_src *imap, int command, char *tag, char *resp,
			char *param) {
	char *p, *q;

	imap->icap = 0;

	if((p = strtok(param, " ")) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid CAPABILITY response");
		return IMAP_ERROR;
	}

	do {
		/* Yay. Let's deploy code code not based on any standard, and
		 * then whine later about the "big client base" that uses it now.
		 * Sigh. Support both. */
		if(!strncasecmp(p, "AUTH=", 5) || !strncasecmp(p, "AUTH-", 5)) {
			if((q = strchr(p, '=')) != NULL)
				q++;
			else
				q = strchr(p, '-') + 1;
			if(!strcasecmp(q, "KERBEROS_V4"))
				imap->icauth |= ICAUTH_KRB4;
			else if(!strcasecmp(q, "GSSAPI"))
				imap->icauth |= ICAUTH_GSSAPI;
			else if(!strcasecmp(q, "SKEY"))
				imap->icauth |= ICAUTH_SKEY;
			else if(!strcasecmp(q, "SSL"))
				imap->icauth |= ICAUTH_SSL;

			imap->icap |= ICAP_AUTH;
		} else if(!strcasecmp(p, "QUOTA"))
			imap->icap |= ICAP_QUOTA;
		else if(!strcasecmp(p, "ACL"))
			imap->icap |= ICAP_ACL;
		else if(!strcasecmp(p, "IMAP4"))
			imap->icap |= ICAP_IMAP4;
		else if(!strcasecmp(p, "IMAP4rev1")) {
			imap->icap |= ICAP_IMAP4;
			imap->icap |= ICAP_IMAP4REV1;
			imap->icap |= ICAP_STATUS;
		} else if(!strcasecmp(p, "IMAP2"))
			imap->icap |= ICAP_IMAP2;
		else if(!strcasecmp(p, "STATUS"))
			imap->icap |= ICAP_STATUS;
		else if(!strcasecmp(p, "SCAN"))
			imap->icap |= ICAP_SCAN;
	} while((p = strtok(NULL, " ")) != NULL);

	if(!(imap->icap & ICAP_IMAP4) && !(imap->icap & ICAP_IMAP2)) {
		display_msg(MSG_WARN, "IMAP",
					"Unsupported IMAP server version\ncan not proceed");
		return IMAP_ERROR;
	}

	if(imap->icap & ICAP_IMAP2) {
		display_msg(MSG_WARN, "IMAP", "IMAP2 is not supported");
		return IMAP_ERROR;
	}

	return IMAP_OK;
}

int
flags_process(struct _imap_src *imap, int command, char *tag, char *resp,
			  char *param) {
	return IMAP_OK;
}

int
exists_process(struct _imap_src *imap, int command, char *tag, char *resp,
			   char *param) {
	char *p;
	unsigned long i;

	if(!imap->ifold)
		return IMAP_OK;

	i = strtoul(resp, &p, 10);
	if(*p || i == ULONG_MAX) {
		display_msg(MSG_WARN, "IMAP", "Invalid EXISTS response");
		return IMAP_ERROR;
	}

	if(i != imap->ifold->num_msg)
		imap->ifold->status |= FRESCAN;
	imap->ifold->num_msg = i;

	if(imap->ifold->unread_num > imap->ifold->num_msg)
		imap->ifold->unread_num = imap->ifold->num_msg;

	return IMAP_OK;
}

int
recent_process(struct _imap_src *imap, int command, char *tag, char *resp,
			   char *param) {
	struct _mail_folder *pfold;
	char *p;
	unsigned long r;

	if(!imap->ifold)
		return IMAP_OK;

	r = strtoul(resp, &p, 10);
	if(*p || r == ULONG_MAX) {
		display_msg(MSG_WARN, "IMAP", "Invalid RECENT response");
		return IMAP_ERROR;
	}
	if(r > 0) {
		imap->ifold->status |= (FRECNT | FRESCAN);
		for(pfold = imap->ifold->pfold; pfold; pfold = pfold->pfold)
			pfold->status |= FMRKTMP;
	} else
		imap->ifold->status &= ~FRECNT;

	return IMAP_OK;
}

long is_literal(struct _imap_src *imap, char *str) {
	char *s;
	unsigned long lvalue;

	if(*str != '{')
		return -1;
	str++;

	lvalue = strtoul(str, &s, 10);
	if(*s != '}')
		return -1;

	return (long)lvalue;
}

void skip_literal(struct _imap_src *imap, u_long len) {
	FILE *nul;
	int res;

	if((nul = fopen("/dev/null", "w")) != NULL) {
#if defined(SASL) && defined(PROT_STDIO)
		if((res = imap_getdata(NULL, len, imap->imap_in, nul)) < 0)
#else
		if((res = getdata(NULL, len, imap->imap_in, nul)) < 0)
#endif
		{
			if(res == -2)
				imap_reconnect(imap);
			else if(res == -1)
				imap_close(imap, 0);
		}
		fclose(nul);
	}

	return;
}

char *get_imap_string(struct _imap_src *imap, char *string, FILE * file) {
	char *res;
	long llen;
	int rres;

	if(!string)
		return NULL;

	if((llen = is_literal(imap, string)) != -1) {
		if(imap->response)
			free(imap->response);
		imap->response = NULL;
		imap->plist = NULL;
		res = NULL;

		if(file) {
			/* if(llen >= MAX_MSG_LEN) {
				display_msg(MSG_WARN, "IMAP",
							"Server response too long, skipping");
				skip_literal(imap, MAX_MSG_LEN);
				return "";
			} else */
#if defined(SASL) && defined(PROT_STDIO)
			if((rres = imap_getdata(NULL, llen, imap->pin, file)) < 0)
#else
			if((rres = getdata(NULL, llen, imap->imap_in, file)) < 0)
#endif
			{
				if(rres == -2)
					imap_reconnect(imap);
				else if(rres == -1) {
					display_msg(MSG_WARN, "IMAP",
								"Can not receive string");
					imap_close(imap, 0);
				}
				return "";
			} else
				res = "OK";
		} else {
			if(llen >= MAX_IMAP_STRING) {
				display_msg(MSG_WARN, "IMAP", "Server response too long, skipping");
				skip_literal(imap, MAX_IMAP_STRING);
				return NULL;
			}

			if((res = (char *) malloc(llen + 1)) == NULL)
				display_msg(MSG_FATAL, "IMAP", "Malloc failed");
			else
#if defined(SASL) && defined(PROT_STDIO)
				if((rres = imap_getdata(res, llen, imap->pin, NULL)) < 0)
#else
				if((rres = getdata(res, llen, imap->imap_in, NULL)) < 0)
#endif
			{
				free(res);
				if(rres == -2)
					imap_reconnect(imap);
				else if(rres == -1) {
					display_msg(MSG_WARN, "IMAP",
								"Can not receive string");
					imap_close(imap, 0);
				}
				return NULL;
			}
		}

#if defined(SASL) && defined(PROT_STDIO)
		if((imap->response = imap_getline(NULL, -65535, imap->pin)) ==
		   NULL)
#else
		if((imap->response = getline(NULL, -65535, imap->imap_in)) ==
		   NULL)
#endif
		{
			display_msg(MSG_WARN, "IMAP", "Incomplete FETCH response");
			imap_close(imap, 0);
			return NULL;
		}

		if(!*imap->response) {
			imap_reconnect(imap);
			return NULL;
		}

		imap->plist = imap->response;

		return res;
	}

	while((*string == '"') || (*string == '\'') || (*string == ' '))
		string++;

	if((llen = strlen(string)) == 0)
		return strdup("");

	while((string[llen - 1] == '"') ||
		  (string[llen - 1] == '\'') || (string[llen - 1] == ' '))
		llen--;

	if((llen == 0) || !strcasecmp(string, "NIL"))
		return strdup("");

	if((res = (char *) malloc(llen + 1)) == NULL) {
		display_msg(MSG_FATAL, "IMAP", "Malloc failed");
		return "";
	}

	memcpy(res, string, llen);
	res[llen] = '\0';

	return res;
}

char *imap_string(struct _imap_src *imap, char *str) {
	static char imapstr[255];

	if(!str)
		return "NIL";

	if(!strpbrk(str, "%*(){ \\\""))
		return str;

	if((strlen(str) + 2) >= sizeof(imapstr))
		return str;

	snprintf(imapstr, sizeof(imapstr), "\"%s\"", str);

	return imapstr;
}

char *get_imap_datetime_str(struct _imap_src *imap, time_t itime) {
	static char idatetime[64];
	char buf[64];
	int off;

	off = get_date_offt();

#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "C");
#endif
	strftime(buf, 59, "%d-%b-%Y %T %%c%%04d", localtime(&itime));
#ifdef HAVE_SETLOCALE
	setlocale(LC_TIME, "");
#endif

	snprintf(idatetime, sizeof(idatetime), buf, off > 0 ? '+' : '-',
			 abs((off / 60 * 100) + (off % 60)));

	return idatetime;
}

time_t get_imap_date(struct _imap_src * imap, char *str) {
	int year, mon, day, hour, min, sec;
	int i, offt;
	struct tm tmm;
	char month[5];

	if(!str || (strlen(str) < 24))
		return 0L;

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

	sscanf(str, "%2d-%3s-%4d %2d:%2d:%2d %d", &day, month, &year, &hour,
		   &min, &sec, &offt);

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

	if((mon == -1) || (year == -1) || (hour == -1))
		return 0L;

	if(offt)
		offt = ((offt / 100) * 60 + (offt % 100)) * 60;

	if(year > 1900)
		year -= 1900;

	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;

#ifdef  HAVE_TM_ZONE
	tmm.tm_gmtoff = offt;
	tmm.tm_zone = NULL;
#endif
	return mktime(&tmm);
}

int
imap_fetchbody(struct _imap_src *imap, struct _mail_msg *msg, char *arg) {
	char *str, *next;
	char typestr[64], subtypestr[64], buf[255];

	if(!*arg)
		return 0;

	if(start_plist(imap) == -1)
		return -1;

	if((str = plist_getnext_string(imap, imap->plist, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Unknown body MIME type");
		return -1;
	}

	strncpy(typestr, (str[0] == '(') ? "MULTIPART" : str, 63);
	typestr[63] = '\0';
	free(str);

	subtypestr[0] = '\0';

	while((str = plist_getnext_string(imap, NULL, &next)) != NULL) {
		if((*subtypestr == '\0') && (*str != '(')) {
			strncpy(subtypestr, str, 63);
			subtypestr[63] = '\0';
		}
		free(str);
	}

	snprintf(buf, sizeof(buf), "%s/%s", typestr, subtypestr);
	add_field(msg, MIME_C_TYPE, buf);

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

	return 0;
}

int
imap_fetchbodystruct(struct _imap_src *imap, struct _mail_msg *msg,
					 char *arg) {
	return imap_fetchbody(imap, msg, arg);
}

int
imap_fetchbodypart(struct _imap_src *imap, struct _mail_msg *msg,
				   char *part, char *arg) {
	char *p = strchr(part, '[');

	if((p == NULL) || (p[1] == ']'))
		return imap_fetchrfc822(imap, msg, arg);

	display_msg(MSG_WARN, "FETCH",
				"Fetching of separate MIME parts is not supported");
	if((p = get_imap_string(imap, arg, NULL)) != NULL)
		free(p);

	return 0;
}

struct _mail_addr *imap_fetchaddr(struct _imap_src *imap, char *str) {
	struct _mail_addr *addr;
	char *next, *p;

	if(start_plist(imap) == -1)
		return NULL;

	if((p = plist_getnext_string(imap, imap->plist, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid address");
		end_plist(imap);
		return NULL;
	}

	if((addr = (struct _mail_addr *) malloc(sizeof(struct _mail_addr))) ==
	   NULL) {
		display_msg(MSG_FATAL, "IMAP", "Malloc failed");
		end_plist(imap);
		return NULL;
	}

	addr->num = 0;
	addr->comment = NULL;
	addr->pgpid = NULL;
	addr->next_addr = NULL;
	addr->name = *p ? strdup(p) : NULL;
	addr->addr = NULL;

	free(p);

	if((p = plist_getnext_string(imap, NULL, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid address");
		discard_address(addr);
		end_plist(imap);
		return NULL;
	}

	free(p);

	if((p = plist_getnext_string(imap, NULL, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid address");
		discard_address(addr);
		end_plist(imap);
		return NULL;
	}

	if(*p)
		addr->addr = strdup(p);

	free(p);

	if((p = plist_getnext_string(imap, NULL, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid address");
		discard_address(addr);
		end_plist(imap);
		return NULL;
	}

	if(*p) {
		if(addr->addr) {
			if(
			  (next =
			   (char *) malloc(strlen(addr->addr) + strlen(p) + 3)) ==
			  NULL) {
				display_msg(MSG_FATAL, "IMAP", "Malloc failed");
				discard_address(addr);
				free(p);
				end_plist(imap);
				return NULL;
			}

			sprintf(next, "%s@%s", addr->addr, p);
			free(addr->addr);
			addr->addr = next;
		} else
			addr->addr = strdup(p);
	}

	free(p);

	end_plist(imap);

	return addr;
}

struct _mail_addr *imap_fetchaddrlist(struct _imap_src *imap, char *str) {
	struct _mail_addr *addr, *addr1, *addr2;

	if(start_plist(imap) == -1)
		return NULL;

	addr = NULL;
	addr1 = NULL;

	while((addr2 = imap_fetchaddr(imap, imap->plist)) != NULL) {
		if(addr2->addr) {
			if(addr)
				addr1->next_addr = addr2;
			else
				addr = addr2;
			addr1 = addr2;
			addr->num++;
		} else
			discard_address(addr2);
	}

	return addr;
}

int
imap_fetchenvelope(struct _imap_src *imap, struct _mail_msg *msg,
				   char *arg) {
	char *str, *next;
	struct _mail_addr *taddr;

	if(!*arg)
		return 0;

	if(start_plist(imap) == -1)
		return -1;

	if((str = plist_getnext_string(imap, imap->plist, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid envelope date");
		end_plist(imap);
		return -1;
	}

	msg->header->snt_time = *str ? get_date(str) : 0L;
	if(msg->header->rcv_time == 0L)
		msg->header->rcv_time = msg->header->snt_time;
	replace_field(msg, "Date", str);

	free(str);

	if((str = plist_getnext_string(imap, NULL, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid envelope subject");
		end_plist(imap);
		return -1;
	}

	if(msg->header->Subject)
		free(msg->header->Subject);
	msg->header->Subject = *str ? strdup(str) : NULL;

	free(str);

	msg->header->From = imap_fetchaddrlist(imap, imap->plist);
	msg->header->Sender = imap_fetchaddrlist(imap, imap->plist);
	/* unused, but need to remove */
	taddr = imap_fetchaddrlist(imap, imap->plist);
	if (taddr != NULL)
	    discard_address(taddr);
	msg->header->To = imap_fetchaddrlist(imap, imap->plist);
	msg->header->Cc = imap_fetchaddrlist(imap, imap->plist);
	msg->header->Bcc = imap_fetchaddrlist(imap, imap->plist);

	if((str = plist_getnext_string(imap, NULL, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid envelope in-reply-to");
		end_plist(imap);
		return -1;
	}

	if(*str)
		replace_field(msg, "In-Reply-To", str);

	free(str);

	if((str = plist_getnext_string(imap, NULL, &next)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid envelope message-id");
		end_plist(imap);
		return -1;
	}

	if(*str)
		replace_field(msg, "Message-ID", str);

	free(str);

	replace_field(msg, SOURCE_FIELD, imap->name);

	end_plist(imap);

	return 0;
}

int
imap_fetchflags(struct _imap_src *imap, struct _mail_msg *msg, char *arg) {
	char *p;
	int wasunread;

	if(*arg != '(') {
		display_msg(MSG_WARN, "IMAP", "Invalid FLAGS list");
		return -1;
	}

	arg++;
	if((p = strchr(arg, ')')) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid FLAGS list");
		return -1;
	}
	*p = '\0';

	wasunread = (msg->flags & UNREAD) ? 1 : 0;

	msg->flags |= UNREAD;
	msg->header->flags |= UNREAD;

	if((p = strtok(arg, " ")) == NULL)
		return 0;

	msg->flags &= ~ANSWERED;
	msg->header->flags &= ~ANSWERED;

	msg->flags &= ~MARKED;
	msg->header->flags &= ~MARKED;

	msg->status &= ~(DELETED | DELPERM | IMAPDELETED);
	msg->status &= ~RECENT;

	do {
		if(!strcasecmp(p, "\\Seen")) {
			if(wasunread && msg->folder && msg->folder->unread_num)
				msg->folder->unread_num--;

			msg->flags &= ~UNREAD;
			msg->header->flags &= ~UNREAD;
		} else if(!strcasecmp(p, "\\Answered")) {
			msg->flags |= ANSWERED;
			msg->header->flags |= ANSWERED;
		} else if(!strcasecmp(p, "\\Flagged")) {
			msg->flags |= MARKED;
			msg->header->flags |= MARKED;
		} else if(!strcasecmp(p, "\\Deleted")) {
			msg->status |= (IMAPDELETED | DELETED | DELPERM);
			msg->flags &= ~UNREAD;
			msg->header->flags &= ~UNREAD;
		} else if(!strcasecmp(p, "\\Draft")) {
		} else if(!strcasecmp(p, "\\Recent"))
			msg->status |= RECENT;
		else if (!strcasecmp(p, "\\NonJunk"))
			/* ignore */;
		else
			display_msg(MSG_LOG, "IMAP", "Unknown flag %s", p);

	} while((p = strtok(NULL, " ")) != NULL);

	return 0;
}

int
imap_fetchidate(struct _imap_src *imap, struct _mail_msg *msg, char *arg) {
	msg->header->rcv_time = *arg ? get_imap_date(imap, arg) : 0L;
	replace_field(msg, "X-RDate", get_arpa_date(msg->header->rcv_time));

	return 0;
}

int
imap_fetchrfc822(struct _imap_src *imap, struct _mail_msg *msg, char *arg) {
	struct _mail_msg *msg1;
	char buf[255];
	FILE *mfd;
	int oflags;

	if(msg->num == -1) {
		if((msg->num = get_new_name(imap->fimap)) == -1) {
			display_msg(MSG_WARN, "IMAP", "No space in %s",
						imap->fimap->fold_path);
			return -1;
		}
	}

	snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path, msg->num);
	if((mfd = fopen(buf, "w")) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Can not open %s",
					imap->fimap->fold_path);
		msg->num = -1;
		return -1;
	}

	if(strcmp(get_imap_string(imap, arg, mfd), "OK")) {
		display_msg(MSG_WARN, "IMAP",
					"Failed to fetch message from server");
		msg->num = -1;
		fclose(mfd);
		unlink(buf);
		return -1;
	}
	fclose(mfd);

	if((msg1 = get_message(msg->num, imap->fimap)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Can not parse message");
		msg->num = -1;
		unlink(buf);
		return -1;
	}

	oflags = msg->header->flags;
	msg->msg_len = msg1->msg_len;
	discard_message_header(msg);
	msg->header = msg1->header;
	msg1->header = NULL;
	discard_message(msg1);
	msg->header->flags = oflags;
	msg->flags &= ~H_ONLY;
	msg->status &= ~H_SHORT;
	discard_mime(msg->mime);
	msg->mime = NULL;

	return 0;
}

int
imap_fetchrfc822hdr(struct _imap_src *imap, struct _mail_msg *msg,
					char *arg) {
	struct _mail_msg *msg1;
	char buf[255];
	FILE *mfd;
	long oflags = -1;

	if(msg->num == -1) {
		if((msg->num = get_new_name(imap->fimap)) == -1) {
			display_msg(MSG_WARN, "IMAP", "No space in %s",
						imap->fimap->fold_path);
			return -1;
		}
		msg->flags |= H_ONLY;
	}

	if(msg->flags & H_ONLY)
		snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path,
				 msg->num);
	else {
		if((oflags = get_new_name(imap->fimap)) == -1) {
			display_msg(MSG_WARN, "IMAP", "No space in %s",
						imap->fimap->fold_path);
			return -1;
		}
		snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path,
				 oflags);
	}

	if((mfd = fopen(buf, "w")) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Can not open %s",
					imap->fimap->fold_path);
		return -1;
	}

	if(strcmp(get_imap_string(imap, arg, mfd), "OK")) {
		display_msg(MSG_WARN, "IMAP",
					"Failed to fetch message header from server");
		fclose(mfd);
		unlink(buf);
		return -1;
	}

	if(!(msg->flags & H_ONLY)) {
		msg->print_body(msg, mfd);
		snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path,
				 msg->num);
		unlink(buf);
		msg->num = oflags;
	}

	fclose(mfd);

	if((msg1 = get_message(msg->num, imap->fimap)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Can not parse message header");
		msg->num = -1;
		unlink(buf);
		return -1;
	}

	oflags = msg->header->flags;
	discard_message_header(msg);
	msg->header = msg1->header;
	if(msg->flags & H_ONLY) {
		sprintf(buf, "%d", (int) msg->msg_len);
		replace_field(msg, MIME_C_LENGTH, buf);
	}
	msg1->header = NULL;
	msg->header->flags = oflags;
	msg->msg_len = msg1->msg_len;
	msg->status &= ~H_SHORT;
	discard_message(msg1);

	return 0;
}

int
imap_fetchrfc822size(struct _imap_src *imap, struct _mail_msg *msg,
					 char *arg) {
	char *p;

	msg->msg_len = strtoul(arg, &p, 10);
	if(*p || msg->msg_len == ULONG_MAX) {
		display_msg(MSG_WARN, "IMAP", "Invalid message size");
		msg->msg_len = 0;
		return -1;
	}

	replace_field(msg, MIME_C_LENGTH, arg);

	return 0;
}

int
imap_fetchrfc822text(struct _imap_src *imap, struct _mail_msg *msg,
					 char *arg) {
	struct _mail_msg *msg1;
	char buf[255];
	FILE *mfd;
	int oflags;

	if(msg->num == -1) {
		if((msg->num = get_new_name(imap->fimap)) == -1) {
			display_msg(MSG_WARN, "IMAP", "No space in %s",
						imap->fimap->fold_path);
			return -1;
		}
	}

	if((mfd = fopen(buf, "w")) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Can not open %s",
					imap->fimap->fold_path);
		return -1;
	}

	print_message_header(msg, mfd);

	if(strcmp(get_imap_string(imap, arg, mfd), "OK")) {
		display_msg(MSG_WARN, "IMAP",
					"Failed to fetch message header from server");
		fclose(mfd);
		return -1;
	}

	fclose(mfd);

	if((msg1 = get_message(msg->num, imap->fimap)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Can not parse message header");
		return -1;
	}

	oflags = msg->header->flags;
	discard_message_header(msg);
	msg->header = msg1->header;
	msg1->header = NULL;
	discard_message(msg1);
	msg->header->flags = oflags;
	msg->msg_len = msg1->msg_len;
	msg->flags &= ~H_ONLY;

	return 0;
}

int imap_fetchuid(struct _imap_src *imap, struct _mail_msg *msg, char *arg) {
	char *p;

	msg->uid = strtol(arg, &p, 10);
	if(*p || msg->uid == LONG_MIN || msg->uid == LONG_MAX) {
		display_msg(MSG_WARN, "IMAP", "Invalid UID");
		msg->uid = -1;
		return -1;
	}

	return 0;
}

int
fetch_process(struct _imap_src *imap, int command, char *tag, char *resp,
			  char *param) {
	struct _mail_msg *msg, *msg1, *msg2;
	char *p, *next, *value;
	int mpart, res = 0;
	long msgnum;

	if(imap->ifold == NULL) {
		display_msg(MSG_WARN, "IMAP", "No folder to fetch into");
		return IMAP_ERROR;
	}

	msgnum = strtol(resp, &p, 10);
	if(*p || msgnum == LONG_MIN || msgnum == LONG_MAX) {
		display_msg(MSG_WARN, "IMAP",
					"Invalid message number in FETCH response");
		return IMAP_ERROR;
	}

	if(!imap->imsg) {
		imap->imsg = find_imap_msgnum(imap, imap->ifold, msgnum);
		if(imap->imsg && (imap->imsg->status & LOCKED))
			imap->imsg = NULL;
	}

	display_msg(MSG_STAT, NULL, "Fetching %lu", msgnum);

	if((p = strchr(param, '(')) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Invalid FETCH response");
		imap->imsg = NULL;
		return IMAP_ERROR;
	}

	p++;

	if((p = plist_getnext(imap, p, &next)) == NULL) {
		imap->imsg = NULL;
		return IMAP_OK;
	}

	if(imap->imsg) {
		msg = imap->imsg;
		msg->status |= MREFRESH;
	} else {
		if((msg = alloc_message()) == NULL) {
			display_msg(MSG_FATAL, "IMAP", "Malloc failed");
			imap->imsg = NULL;
			return IMAP_FATAL;
		}

		if((msg->header = (msg_header *) malloc(sizeof(msg_header))) ==
		   NULL) {
			display_msg(MSG_FATAL, "IMAP", "Malloc failed");
			imap->imsg = NULL;
			return IMAP_FATAL;
		}

		msg->flags = H_ONLY | UNREAD;
		msg->status |= H_SHORT;
		msg->folder = imap->ifold;
		msg->num = -1;
		msg->uid = -1;

		msg->header->other_fields = NULL;
		msg->header->flags = H_ONLY | UNREAD;
		msg->header->From = NULL;
		msg->header->To = NULL;
		msg->header->Cc = NULL;
		msg->header->Bcc = NULL;
		msg->header->News = NULL;
		msg->header->Fcc = NULL;
		msg->header->Sender = NULL;
		msg->header->snt_time = 0L;
		msg->header->rcv_time = 0L;
		msg->header->Subject = NULL;

		imap_message(imap, msg);
	}

	do {
		mpart = 0;
		while(mparts[mpart] != NULL) {
			if(!strncasecmp(mparts[mpart], p, strlen(mparts[mpart])))
				break;
			mpart++;
		}

		if((mpart != IFETCH_ENVELOPE) && (mpart != IFETCH_BODY) &&
		   (mpart != IFETCH_BODYPART) && (mpart != IFETCH_BODYSTRUCTURE)) {
			if((p = plist_getnext(imap, NULL, &next)) == NULL) {
				display_msg(MSG_WARN, "IMAP", "Invalid FETCH response");
				if(!imap->imsg)
					discard_message(msg);
				if(imap->response)
					free(imap->response);
				imap->response = NULL;
				imap->imsg = NULL;
				return IMAP_ERROR;
			}
		}

		value = NULL;

		switch(mpart) {
			case IFETCH_RFC822:
			case IFETCH_RFC822TEXT:
			case IFETCH_RFC822HEADER:
			case IFETCH_ENVELOPE:
			case IFETCH_BODY:
			case IFETCH_BODYPART:
			case IFETCH_BODYSTRUCTURE:
				break;

			default:
				if((value = get_imap_string(imap, p, NULL)) == NULL) {
					display_msg(MSG_WARN, "IMAP", "Can not get value of %s",
								mparts[mpart] ? mparts[mpart] : "UNKNOWN");
					continue;
				}
				break;
		}

		if(mparts[mpart] == NULL) {
			display_msg(MSG_WARN, "IMAP",
						"Unknown message part name in FETCH response");
			continue;
		}

		switch(mpart) {
			case IFETCH_BODY:
				res = imap_fetchbody(imap, msg, imap->plist);
				break;

			case IFETCH_BODYPART:
				res = imap_fetchbodypart(imap, msg, p, imap->plist);
				break;

			case IFETCH_BODYSTRUCTURE:
				res = imap_fetchbodystruct(imap, msg, imap->plist);
				break;

			case IFETCH_ENVELOPE:
				res = imap_fetchenvelope(imap, msg, imap->plist);
				break;

			case IFETCH_FLAGS:
				res = imap_fetchflags(imap, msg, value);
				break;

			case IFETCH_INTERNALDATE:
				res = imap_fetchidate(imap, msg, value);
				break;

			case IFETCH_RFC822:
				res = imap_fetchrfc822(imap, msg, p);
				break;

			case IFETCH_RFC822HEADER:
				res = imap_fetchrfc822hdr(imap, msg, p);
				break;

			case IFETCH_RFC822SIZE:
				res = imap_fetchrfc822size(imap, msg, value);
				break;

			case IFETCH_RFC822TEXT:
				res = imap_fetchrfc822text(imap, msg, p);
				break;

			case IFETCH_UID:
				res = imap_fetchuid(imap, msg, value);
				break;
		}

		if(value)
			free(value);
		value = NULL;

		if(imap->elem)
			free(imap->elem);
		imap->elem = NULL;

		if(res < 0)
			display_msg(MSG_WARN, "IMAP", "Failed to fetch message part");

	} while((p = plist_getnext(imap, NULL, &next)) != NULL);

	set_imap_msgnum(imap, msg, msgnum);
	if(imap->response)
		free(imap->response);
	imap->response = NULL;

	if(imap->elem)
		free(imap->elem);
	imap->elem = NULL;

	if(msg->uid == -1) {
		if(!imap->imsg)
			discard_message(msg);
		imap->imsg = NULL;
		return IMAP_OK;
	}

	if(imap->imsg == NULL) {
		msg1 = msg2 = imap->ifold->messages;
		while(msg1) {
			if(msg1->uid == msg->uid)
				break;
			msg2 = msg1;
			msg1 = msg1->next;
		}

		if(msg1) {
			if(!(msg1->status & LOCKED)) {
				msg->num = msg1->num;
				msg->next = msg1->next;
				if(imap->ifold->messages == msg1)
					imap->ifold->messages = msg;
				else
					msg2->next = msg;

				discard_message(msg1);
			}
		} else {
			msg->next = imap->ifold->messages;
			imap->ifold->messages = msg;
			imap->ifold->status &= ~SORTED;
			imap->ifold->num_msg++;
			if(msg->flags & UNREAD)
				imap->ifold->unread_num++;
		}
	}

	cache_msg(msg);
	imap->imsg = NULL;
	return IMAP_OK;
}

int
search_process(struct _imap_src *imap, int command, char *tag, char *resp,
			   char *param) {
	long scount;
	char *p;

	if(imap->search_res)
		free(imap->search_res);
	imap->search_res = NULL;

	if(!param || (strlen(param) < 1))
		return IMAP_OK;

	scount = 1;
	p = param;
	while((p = strchr(p, ' ')) != NULL) {
		scount++;
		while(*p == ' ')
			p++;
	}

	if(
	  (imap->search_res =
	   (long *) malloc((scount + 2) * sizeof(long))) == NULL) {
		display_msg(MSG_WARN, "IMAP", "malloc failed");
		return IMAP_FATAL;
	}

	imap->search_res[0] = scount;

	p = param;
	scount = 1;
	do {
		while(*p == ' ')
			p++;
		imap->search_res[scount] = strtol(p, NULL, 10);
		scount++;
	} while((p = strchr(p, ' ')) != NULL);

	return IMAP_OK;
}

int
preauth_process(struct _imap_src *imap, int command, char *tag, char *resp,
				char *param) {
	if((imap->istate == ISTATE_NOAUTH) || (imap->istate == ISTATE_NOCON))
		imap->istate = ISTATE_AUTH;

	return IMAP_OK;
}

int
stat_process(struct _imap_src *imap, int command, char *tag, char *resp,
			 char *param) {
	char fname[MAX_IMAP_FOLD_NAME_LEN], *p, *p1;
	struct _mail_folder *folder;
	unsigned long len;

	if(!param)
		return IMAP_ERROR;

	if(((p = strrchr(param, ')')) == NULL) || (p == param)) {
		display_msg(MSG_WARN, "IMAP", "Invalid STATUS response");
		return IMAP_ERROR;
	}
	*p = '\0';

	if(((p = strrchr(param, '(')) == NULL) || (p == param)) {
		display_msg(MSG_WARN, "IMAP", "Invalid STATUS response");
		return IMAP_ERROR;
	}

	*p++ = '\0';
	if(strlen(param) >= MAX_IMAP_FOLD_NAME_LEN) {
		display_msg(MSG_WARN, "IMAP", "Folder name too long");
		return IMAP_ERROR;
	}
	strcpy(fname, rem_tr_spacequotes(param));

	if((folder = find_imap_folder(imap, fname)) == NULL) {
		display_msg(MSG_WARN, "IMAP",
					"Unknown folder name in STATUS response");
		return IMAP_OK;
	}

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

	do {
		if(!strcasecmp(p, "MESSAGES")) {
			if((p = strtok(NULL, " ")) == NULL) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid MESSAGES value in STATUS response");
				return IMAP_ERROR;
			}
			folder->num_msg = strtoul(p, &p1, 10);
			if(*p1 || folder->num_msg == ULONG_MAX) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid MESSAGES value in STATUS response");
				return IMAP_ERROR;
			}
		} else if(!strcasecmp(p, "UNSEEN")) {
			if((p = strtok(NULL, " ")) == NULL) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid UNSEEN value in STATUS response");
				return IMAP_ERROR;
			}
			folder->unread_num = strtoul(p, &p1, 10);
			if(*p1 || folder->unread_num == ULONG_MAX) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid UNSEEN value in STATUS response");
				return IMAP_ERROR;
			}
		} else if(!strcasecmp(p, "RECENT")) {
			if((p = strtok(NULL, " ")) == NULL) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid RECENT value in STATUS response");
				return IMAP_ERROR;
			}
			len = strtoul(p, &p1, 10);
			if(*p1 || len == ULONG_MAX) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid RECENT value in STATUS response");
				return IMAP_ERROR;
			}
			if(len > 0)
				folder->status |= FRECNT;
			else
				folder->status &= ~FRECNT;
		} else if(!strcasecmp(p, "UIDNEXT") || !strcasecmp(p, "UID-NEXT")) {
			if((p = strtok(NULL, " ")) == NULL) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid UIDNEXT value in STATUS response");
				return IMAP_ERROR;
			}
			imap->uid = strtol(p, &p1, 10);
			if(*p1 || imap->uid == LONG_MIN || imap->uid == LONG_MAX) {
				imap->uid = -1;
				display_msg(MSG_WARN, "IMAP",
							"Invalid UIDNEXT value in STATUS response");
				return IMAP_ERROR;
			}
		} else
			if(!strcasecmp(p, "UIDVALIDITY") ||
			   !strcasecmp(p, "UID-VALIDITY")) {
			if((p = strtok(NULL, " ")) == NULL) {
				display_msg(MSG_WARN, "IMAP",
							"Invalid UIDVALIDITY value in STATUS response");
				return IMAP_ERROR;
			}
			folder->uid = strtol(p, &p1, 10);
			if(*p1 || folder->uid == LONG_MIN || folder->uid == LONG_MAX) {
				folder->uid = -1;
				display_msg(MSG_WARN, "IMAP",
							"Invalid UIDVALIDITY value in STATUS response");
				return IMAP_ERROR;
			}
		} else {
			display_msg(MSG_WARN, "IMAP", "Unknown STATUS parameter");
			return IMAP_ERROR;
		}

	} while((p = strtok(NULL, " ")) != NULL);


	return IMAP_OK;
}

int
list_process(struct _imap_src *imap, int command, char *tag, char *resp,
			 char *param) {
	struct _mail_folder *fld;
	char *p, *foldname;
	char flags[127], hdelim[6], fname[MAX_IMAP_FOLD_NAME_LEN], delimchar;
	int len, fflags;

	fflags = 0;

	if(*param != '(') {
		display_msg(MSG_WARN, "IMAP", "Missing flags in LIST response");
		return IMAP_ERROR;
	}

	param++;
	if((p = strchr(param, ')')) == NULL) {
		display_msg(MSG_WARN, "IMAP",
					"Unterminated flag list in LIST response");
		return IMAP_ERROR;
	}

	len = p - param;
	if(len > 126) {
		display_msg(MSG_WARN, "IMAP",
					"Flag list too long in LIST response");
		return IMAP_ERROR;
	}

	strncpy(flags, param, len);
	flags[len] = '\0';
	param = p;

	if((p = strtok(flags, " ")) != NULL)
		do {
			if(!strcasecmp(p, "\\Noinferiors"))
				fflags |= NOINFR;
			else if(!strcasecmp(p, "\\Noselect"))
				fflags |= FDUMMY;
			else if(!strcasecmp(p, "\\Marked"))
				fflags |= FMRKTMP;
		} while((p = strtok(NULL, " ")) != NULL);

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

	if((p = strchr(param, ' ')) == NULL) {
		display_msg(MSG_WARN, "IMAP",
					"Missing folder name in LIST response");
		return IMAP_ERROR;
	}

	*p = '\0';
	strncpy(hdelim, param, 3);
	hdelim[3] = '\0';
	*p++ = ' ';
	while(*p == ' ')
		p++;
	if(strlen(p) >= MAX_IMAP_FOLD_NAME_LEN) {
		display_msg(MSG_WARN, "IMAP", "Folder name too long");
		return IMAP_ERROR;
	}

	strcpy(fname, p);
	foldname = rem_tr_spacequotes(fname);

	p = hdelim;

	if(*p == '"')
		p++;

	if(!strcasecmp(p, "NIL"))
		delimchar = '\0';
	else
		delimchar = *p;

	p = foldname;
	while(*p) {
		if(!isgraph(*p) && (*p != ' '))
			return IMAP_OK;
		p++;
	}

	if((fld = find_imap_folder(imap, foldname)) == NULL) {

		if(strlen(foldname) >= MAX_IMAP_FOLD_NAME_LEN)
			return IMAP_OK;

		display_msg(MSG_STAT, NULL, "Processing: %-.64s", foldname);
		if((fld = alloc_folder()) == NULL)
			return IMAP_FATAL;

		strcpy(fld->fold_path, foldname);
		if(fflags & FDUMMY) {
			dummy_folder(fld);
			fld->open = imap_dummy_open_folder;
			fld->spec = imap;
			fld->hdelim = '\0';
			fld->status |= FREMOTE;
			fld->type = F_IMAP;
		} else
			imap_folder(imap, fld);

		fld->status |= fflags;
		fld->status |= imap->lflags;
		fld->hdelim = delimchar;
		fld->sname = strdup(get_imap_folder_short_name(imap, fld));

		append_folder(fld, 0);

	} else {
		fld->status |= fflags;
		fld->status |= imap->lflags;
		fld->hdelim = delimchar;
		fld->sname = strdup(get_imap_folder_short_name(imap, fld));
	}

	return IMAP_OK;
}

int
unk_process(struct _imap_src *imap, int command, char *tag, char *resp,
			char *param) {
	display_msg(MSG_WARN, "IMAP", "unknown response [%s] from server",
				resp);
	return IMAP_ERROR;
}

int token_comp(struct _imap_src *imap, char *token, char *string) {
	char *s;

	if(!token || !string)
		return 0;

	if(!strcmp(token, "$all"))
		return 1;

	if(!strcmp(token, "$num")) {
		strtoul(string, &s, 10);
		return *s ? 0 : 1;
	}

	if(*token == '$') {
		token++;
		return !strncasecmp(token, string, strlen(token));
	}

	return !strcasecmp(token, string);
}

#if STDC_HEADERS || defined(HAVE_STDARG_H)
int imap_command(struct _imap_src *imap, int command, char *fmt, ...)
#else
int imap_command(struct _imap_src *imap, int command, char *fmt, va_list)
va_dcl
#endif
{
	static int ntag = 0;
	static int rcommand = 0;
	va_list ap;
	char buf[255], tag[6], *p;
	char rtag[16], rresp[16], rparam[64], rmsg[64];
	int rnum, pstatus, comflags, res, lines;
	int showerr = MSG_WARN;
#if defined(SASL) && defined(PROT_STDIO)
	int len;
	char *outbuf;
	char mybuf[4096];
#endif

#if STDC_HEADERS || defined(HAVE_STDARG_H)
	va_start(ap, fmt);
#else
	va_start(ap);
#endif

	if(rcommand) {
		display_msg(MSG_WARN, "IMAP", "command still in progress");
		return IMAP_ERROR;
	}

	if(!imap_isconnected(imap))
		return IMAP_ERROR;

	imap->lastcom = time(NULL);
	set_imap_timer();

	comflags = command & 0xff00;
	command &= 0x00ff;

	if(comflags & ICOM_LOGERR)
		showerr = MSG_LOG;

	if(command == ICOM_NOCOM) {
		strcpy(tag, "*");
		goto rwait;
	}

	if(imap->istate == ISTATE_NOCON)
		return IMAP_ERROR;

	if((command < 0) || (command >= ICOM_MAXCOMM)) {
		display_msg(showerr, "IMAP", "unknown command");
		return IMAP_ERROR;
	}
	ntag++;
	if(ntag > 999)
		ntag = 0;
	sprintf(tag, "A%03d", ntag);
	snprintf(buf, sizeof(buf), "%s %s", tag, imap_commands[command]);

	p = NULL;
	if(fmt) {
		p = buf + strlen(buf);
		*p = ' ';
		p++;
		vsnprintf(p, 200, fmt, ap);
	}

	if(imap->flags & ISRC_LOGSESSION) {
		if(command != ICOM_LOGIN)
			display_msg(MSG_LOG, "imap", "%s-> %s %s", imap->name,
						imap_commands[command], p ? p : "");
		else {
			va_start(ap, fmt);
			p = va_arg(ap, char *);
			display_msg(MSG_LOG, "imap", "%s-> LOGIN %s ******",
						imap->name, p);
			va_end(ap);
		}
	}
#if defined(SASL) && defined(PROT_STDIO)
	if((res = imap_putline(buf, imap->pout)) < 0)
#else
	if((res = putline(buf, imap->imap_out)) < 0)
#endif
	{
		if(res == -2)
			imap_reconnect(imap);
		else if(res == -1)
			imap_close(imap, 0);
		return IMAP_ERROR;
	}

	rwait:
	if(imap->response)
		free(imap->response);
	imap->response = NULL;
	pstatus = IMAP_OK;

	if(comflags & ICOM_DONTWAIT)
		return IMAP_OK;

#if defined(SASL) && defined(PROT_STDIO)
	if((imap->response = imap_getline(NULL, -65535, imap->pin)) == NULL)
#else
	if((imap->response = getline(NULL, -65535, imap->imap_in)) == NULL)
#endif
	{
		imap_close(imap, 0);
		return IMAP_ERROR;
	}
#if defined(SASL) && defined(PROT_STDIO)
	if((command == ICOM_AUTHENTICATE)) {
		if(imap->response[0] == 'A' && isdigit(imap->response[1])) {
			if(sasl_done == SASL_DONE) {
				char *user;
				int maxplain;

				sasl_done = 0;
				gen_sasl_client.query_state(AuthState, &user, &protlevel,
											&encodefunc, &decodefunc,
											&maxplain);

				if(encodefunc || decodefunc) {
					prot_setfunc(imap->pin, decodefunc, AuthState, 0);
					prot_setfunc(imap->pout, encodefunc, AuthState,
								 maxplain);
					prot_setflushonread(imap->pin, pout);
					prot_settimeout(imap->pin, 20);
				}
			}
			goto auth;
		}

		len = from64(imap->response + 2, imap->response + 2);
		sasl_done =
		gen_sasl_client.auth(AuthState, len, imap->response + 2, &len,
							 &outbuf);
		to64(&mybuf, outbuf, len);

		if(sasl_done == SASL_FAIL && AuthState)
			gen_sasl_client.free_state(AuthState);

		mybuf[strlen(mybuf) - 2] = '\0';
	#if defined(SASL) && defined(PROT_STDIO)
		if((res = imap_putline(mybuf, pout)) < 0)
	#else
		if((res = putline(mybuf, imap->imap_out)) < 0)
	#endif
		{
			if(res == -2)
				imap_reconnect(imap);
			else if(res == -1)
				imap_close(imap, 0);
			return IMAP_ERROR;
		}
		goto rwait;
	}
	auth:
#endif

	lines = 0;
	rcommand = command;
	do {

		lines++;
		if(!*imap->response) {
			free(imap->response);
			imap->response = NULL;
			rcommand = 0;
			imap_reconnect(imap);
			return IMAP_ERROR;
		}

		rparam[0] = '\0';
		if(sscanf(imap->response, "%15s %15s %63s", rtag, rresp, rparam) <
		   2) {
			if(!(comflags & ICOM_SILENT))
				display_msg(showerr, "IMAP",
							"Invalid reponse from server");
			free(imap->response);
			imap->response = NULL;
			rcommand = 0;
			imap_reconnect(imap);
			return IMAP_ERROR;
		}

		if(comflags & ICOM_ABORT) {
			free(imap->response);
			imap->response = NULL;
			if((*rtag == 'A') && isdigit(rtag[1])) {
				rcommand = 0;
				return IMAP_ERROR;
			}

			continue;
		}

		if((p = strchr(imap->response, ' ')) != NULL) {
			p++;
			if(imap->flags & ISRC_LOGSESSION) {
				if(!strcmp(tag, rtag) ||
				   (!(comflags & ICOM_DONTLOG) && (lines < 15)))
					display_msg(MSG_LOG, "imap", "%s<- %.64s",
								imap->name, p);
			}
			if((p = strchr(p, ' ')) != NULL) {
				p++;
				strncpy(rmsg, p, 63);
				rmsg[63] = '\0';
			}
		}

		rnum = 0;
		while(imap_responses[rnum].command != ICOM_NOCOM) {
			if((imap_responses[rnum].command == ICOM_MATCHALL) ||
			   (imap_responses[rnum].command == command)) {
				if(token_comp(imap, imap_responses[rnum].tag, rtag) &&
				   token_comp(imap, imap_responses[rnum].resp, rresp) &&
				   token_comp(imap, imap_responses[rnum].param, p)) {
					if(imap_responses[rnum].process == unk_process)
						comflags |=
						(ICOM_ABORT | ICOM_SILENT | ICOM_DONTLOG);
					if(pstatus == IMAP_OK)
						pstatus =
						imap_responses[rnum].process(imap, command,
													 rtag, rresp, p);
					else
						imap_responses[rnum].process(imap, command, rtag,
													 rresp, p);

					if(pstatus == IMAP_FATAL) {
						free(imap->response);
						imap->response = NULL;
						rcommand = 0;
						return pstatus;
					}
					break;
				}
			}
			rnum++;
		}

		if(!strcmp(tag, rtag))
			break;

		if(imap->response)
			free(imap->response);
		imap->response = NULL;
	}
#if defined(SASL) && defined(PROT_STDIO)
	while((imap->response = imap_getline(NULL, -65535, imap->pin)) !=
		  NULL);
#else
	while((imap->response = getline(NULL, -65535, imap->imap_in)) !=
		  NULL);
#endif

	rcommand = 0;
	if(!imap->response) {
		imap_close(imap, 0);
		return IMAP_ERROR;
	} else {
		free(imap->response);
		imap->response = NULL;
	}

	if(pstatus != IMAP_OK)
		return pstatus;

	if(!strcasecmp(rresp, "OK"))
		return IMAP_OK;

	if(!strcasecmp(rresp, "NO")) {
		if(!(comflags & ICOM_SILENT))
			display_msg(showerr, "IMAP", "%s", rmsg);
		return IMAP_NO;
	}

	if(!strcasecmp(rresp, "BAD")) {
		if(!(comflags & ICOM_SILENT))
			display_msg(showerr, "IMAP error", "%s", rmsg);
		return IMAP_BAD;
	}

	if(command == ICOM_NOCOM)
		return IMAP_OK;

	if(!(comflags & ICOM_SILENT))
		display_msg(showerr, "Invalid response from server", "%s", rmsg);

	return IMAP_ERROR;
}

int imap_isconnected(struct _imap_src *imap) {
	if(imap != NULL)
		return imap->imapsock > 0 ? 1 : 0;
	else
		return 0;
}

void imap_timer_cb() {
	int itimeset = Config.getInt("imaptime", 600);
	time_t tnow = time(NULL);
	int wastimer = 0;
	struct _imap_src *imap;
	list<struct _retrieve_src>::iterator p;

	for(p = retrieve_srcs.begin(); p != retrieve_srcs.end(); p++) {
		if(p->type != RSRC_IMAP)
			continue;
		imap = (struct _imap_src *) p->spec;
		if(!imap_isconnected(imap))
			continue;

		if((tnow - imap->lastcom) >= itimeset)
			imap_command(imap, ICOM_NOOP, NULL);
		if(imap_get_recent(imap) == 1) {
			new_mail_notify();
			update_title();
		}

		wastimer = 1;
	}

	if(wastimer) {
		refresh_msgs();
		refresh_folders();
	}

#ifdef XFMAIL
	set_imap_timer();
#endif

	return;
}

int imap_connect(struct _imap_src *imap) {
	if(imap->imapsock != -1)
		return 0;

	if(
	  (imap->imapsock =
	   ConMan.host_connect(imap->hostname, imap->service, NULL)) == -1) {
		display_msg(MSG_WARN, "IMAP connect",
					"Failed to connect to IMAP server");
		return -1;
	}

	if((imap->imap_in = fdopen(imap->imapsock, "r+")) == NULL) {
		display_msg(MSG_WARN, "IMAP connect", "fdopen failed");
		imap_close(imap, 0);
		return -1;
	}

	imap->imap_out = imap->imap_in;

#if defined(SASL) && defined(PROT_STDIO)
	imap->pin = prot_new(imap->imapsock, 0);
	imap->pout = prot_new(imap->imapsock, 1);
#endif

	imap->istate = ISTATE_NOAUTH;

	if(imap_command(imap, ICOM_NOCOM, NULL) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP connect",
					"no greeting from IMAP server");
		imap_close(imap, 0);
		return -1;
	}

	return 0;
}

int imap_login(struct _imap_src *imap) {
	int res;
#ifdef SASL
	struct sockaddr_in addr, laddr;
	int addrlen;
#endif

	if(!imap_isconnected(imap))
		return -1;

	if((imap->icap & ICAP_AUTH) && (imap->icauth & ICAUTH_KRB4)) {
#ifdef SASL
		addrlen = sizeof(laddr);
		getsockname(imap->imapsock, (struct sockaddr *) &laddr, &addrlen);
		addrlen = sizeof(addr);
		getpeername(imap->imapsock, (struct sockaddr *) &addr, &addrlen);

		encodefunc = 0;
		decodefunc = 0;
		prot_req = SASL_PROT_NONE;
		prot_req |= SASL_PROT_ANY;

		res = gen_sasl_client.start(gen_sasl_client.rock, "imap",
									imap->hostname,
									imap->username,
									prot_req, 4096,
									(struct sockaddr *) &laddr,
									(struct sockaddr *) &addr, &AuthState);
		if(!res) {
			imap_command(imap, ICOM_AUTHENTICATE, "KERBEROS_V4");
			return 0;
		}
#endif /* SASL */
	}

	if(supress_errors != 1) {
		if((strlen(imap->password) < 1) && !(imap->flags & ISRC_STOREPWD))
			imap_account(imap);
	}

	if((res = imap_command(imap, ICOM_LOGIN, "%s %s", imap->username,
			imap_string(imap, imap->password))) != IMAP_OK) {
		if(res != IMAP_NO) {
			display_msg(MSG_WARN, "IMAP login", "login failed");
			imap_close(imap, 0);
			return -1;
		}

		imap_account(imap);

		if((res = imap_command(imap, ICOM_LOGIN, "%s %s", imap->username,
				imap_string(imap, imap->password))) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP login", "login failed");
			imap_close(imap, 0);
			return -1;
		}
	}

	return 0;
}

int imap_list(struct _imap_src *imap) {
	char llist[255], *p, *p1, *p2;
	int lcom;

	strcpy(llist, imap->list);
	p = llist;
	do {
		while(*p == ' ')
			p++;
		if((p1 = strchr(p, ' ')) != NULL) {
			*p1 = '\0';
			p1++;
		}

		if(strlen(p) < 1)
			p = "\"\"";

		lcom = ICOM_LIST;
		imap->lflags = 0;
		if(isalpha(*p) && ((p2 = strchr(p, ':')) != NULL)) {
			*p2++ = '\0';
			p = strtok(p, ",");
			do {
				if(p == NULL)
					break;
				if(!strcasecmp(p, "LSUB")) {
					lcom = ICOM_LSUB;
					imap->lflags |= FSUBS;
				} else if(!strcasecmp(p, "LIST"))
					lcom = ICOM_LIST;
				else if(!strcasecmp(p, "NOSCAN"))
					imap->lflags |= FNSCAN;
			} while((p = strtok(NULL, ",")) != NULL);
			p = p2;
		}

		if(imap_command(imap, lcom, "%s \"*\"", p) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Failed to obtain folder list");
			return -1;
		}
		imap->lflags = 0;

		p = p1;
	} while(p);

	return 0;
}

int imap_init(struct _imap_src *imap) {
	int i, res;
	char buf[255], *p;
	struct _mail_folder *dfold;
	bool trysubtrash = false;	/* If the INBOX exists and opening a root
								 * TRASH mailbox fails, attempt to open a
								 * sub trash folder: INBOX.trash. Set true
								 * below if INBOX exists.
								 */

	if(imap->imapsock != -1) {
		display_msg(MSG_WARN, "IMAP", "Already connected");
		return -1;
	}

	imap->ifold = NULL;
	imap->svfold = NULL;
	imap->imsg = NULL;
	imap->iinbox = NULL;
	imap->itrash = NULL;
	imap->istate = ISTATE_NOCON;
	imap->conninprogress = 0;
	imap->lastcom = time(NULL);
	if(imap->search_res)
		free(imap->search_res);
	imap->search_res = NULL;
	discard_imap_folders(imap);

	if(imap_connect(imap) == -1)
		return -1;

	imap->conninprogress = 1;
	if(imap_command(imap, ICOM_CAPABILITY, NULL) != IMAP_OK) {
		imap_close(imap, 0);
		return -1;
	}

	if(imap->istate != ISTATE_AUTH) {
		if(imap_login(imap) == -1) {
			imap->conninprogress = 0;
			return -1;
		}
	}

	imap->istate = ISTATE_AUTH;

	if(imap_list(imap) == -1) {
		imap_close(imap, 0);
		return -1;
	}

	if((imap->iinbox = find_imap_folder(imap, IMAP_INBOX)) == NULL) {
		if((res = imap_command(imap, ICOM_LIST, "\"\" %s", IMAP_INBOX)) !=
		   IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Failed to find INBOX folder");
			imap_close(imap, 0);
			return -1;
		}
	}

	if((imap->iinbox = find_imap_folder(imap, IMAP_INBOX)) == NULL) {
		if(imap_command(imap, ICOM_CREATE, "%s", IMAP_INBOX) == IMAP_OK) {
			if(
			  (res =
			   imap_command(imap, ICOM_LIST, "\"\" %s",
							IMAP_INBOX)) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP",
							"Failed to find INBOX folder");
				imap_close(imap, 0);
				return -1;
			}
		} else
			display_msg(MSG_WARN, "IMAP", "Failed to create INBOX folder");
	}

	if((imap->iinbox = find_imap_folder(imap, IMAP_INBOX)) == NULL)
		display_msg(MSG_WARN, "IMAP", "Warning: No INBOX on server");

	if(imap->flags & ISRC_TRASH) {
		if (imap->iinbox != NULL && imap->iinbox->hdelim != '\0') {
			snprintf(buf, sizeof(buf), "%s%c%s", IMAP_INBOX,
				imap->iinbox->hdelim, IMAP_TRASH);
			trysubtrash = true;
		}
		imap->itrash = find_imap_folder(imap, IMAP_TRASH);
		if (imap->itrash == NULL && trysubtrash)
			imap->itrash = find_imap_folder(imap, buf);
		if (imap->itrash == NULL) {
			res = imap_command(imap, ICOM_LIST, "\"\" %s", IMAP_TRASH);
			if (res != IMAP_OK && trysubtrash)
				res = imap_command(imap, ICOM_LIST, "\"\" %s", buf);
			if (res != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP",
					"Failed to find TRASH folder");
				imap_close(imap, 0);
				return -1;
			}
		}

		if (imap->itrash == NULL) {
			imap->itrash = find_imap_folder(imap, IMAP_TRASH);
			if (imap->itrash == NULL && trysubtrash)
				imap->itrash = find_imap_folder(imap, buf);
			/* Attempt to create trash folder if we still can't find it */
			if (imap->itrash == NULL) {
				imap->itrash = create_imap_folder(imap, NULL, IMAP_TRASH);
				if (imap->itrash == NULL && trysubtrash)
					/* Sub trash is a child of the INBOX */
					imap->itrash = create_imap_folder(imap, imap->iinbox, IMAP_TRASH);
				if (imap->itrash == NULL)
					display_msg(MSG_WARN, "IMAP",
						"Warning: Failed to create trash folder");
			} 
		}
		if (imap->itrash != NULL)
			imap->itrash->status |= NOTRASH;
	}

	folder_sort &= ~FLD_SORTED;

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

			if(mailbox[i]->status & (FDUMMY | FNSCAN))
				continue;

			display_msg(MSG_STAT, NULL, "Checking %-.64s",
						mailbox[i]->fold_path);
			imap_command(imap, ICOM_STATUS | ICOM_LOGERR,
						 "%s (MESSAGES UNSEEN UIDVALIDITY)",
						 imap_string(imap, mailbox[i]->fold_path));
		}
	} else {
		for(i = 0; i < (int)mailbox.size(); i++) {
			if(!(mailbox[i]->type & F_IMAP) || (mailbox[i]->spec != imap))
				continue;

			if(mailbox[i]->status & (FDUMMY | FNSCAN))
				continue;

			imap->ifold = mailbox[i];
			display_msg(MSG_STAT, NULL, "Checking %-.64s",
						mailbox[i]->fold_path);
			imap_command(imap, ICOM_EXAMINE | ICOM_LOGERR, "%s",
						 imap_string(imap, mailbox[i]->fold_path));
		}

		if(imap->iinbox)
			imap->iinbox->rescan(imap->iinbox);
	}

	imap->ifold = NULL;
	snprintf(buf, sizeof(buf), "%s-%s", FIMAP, imap->name);
	if(!imap->fimap)
		imap->fimap = get_mh_folder_by_name(buf);

	if(!imap->fimap) {
		if((imap->fimap = create_mh_folder(NULL, buf)) == NULL) {
			display_msg(MSG_WARN, "IMAP INIT", "Can not open folder %s",
						buf);
			imap_close(imap, 0);
			return -1;
		}

		if(!(imap->flags & ISRC_CACHE))
			imap->fimap->empty(imap->fimap);
		if(imap->fimap->open(imap->fimap, 0) == -1) {
			display_msg(MSG_WARN, "IMAP INIT", "Can not open folder %s",
						buf);
			imap_close(imap, 0);
			return -1;
		}

	}

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

		if((mailbox[i]->hdelim != '\0') &&
		   ((p = strrchr(mailbox[i]->fold_path, mailbox[i]->hdelim)) !=
			NULL)) {
			*p = '\0';
			strcpy(buf, mailbox[i]->fold_path);
			*p = mailbox[i]->hdelim;

			if(find_imap_folder(imap, buf))
				continue;

			if((dfold = alloc_folder()) == NULL)
				break;

			strcpy(dfold->fold_path, buf);
			dummy_folder(dfold);
			dfold->open = imap_dummy_open_folder;
			dfold->hdelim = mailbox[i]->hdelim;
			dfold->status |= (FREMOTE | FDUMMY);
			dfold->type = F_IMAP;
			dfold->sname = strdup(get_imap_folder_short_name(imap, dfold));
			dfold->spec = imap;
			dfold->descr = strdup("Open to search for new folders");
			if(mailbox[i]->status & FSUBS)
				dfold->status |= FSUBS;

			if(append_folder(dfold, 0) == -1) {
				display_msg(MSG_WARN, "IMAP INIT",
							"Failed to append folder");
				imap_close(imap, 0);
				return -1;
			}

			i = 0;
		}
	}

	if((dfold = alloc_folder()) == NULL) {
		display_msg(MSG_WARN, "IMAP INIT", "Can not allocate top folder");
		imap_close(imap, 0);
		return -1;
	}

	dummy_folder(dfold);
	dfold->open = imap_dummy_open_folder;
	strcpy(dfold->fold_path, imap->name);
	dfold->hdelim = '\0';
	dfold->status |= (FREMOTE | FDUMMY | FTOP);
	dfold->type = F_IMAP;
	dfold->sname = strdup(imap->name);
	dfold->spec = imap;
	dfold->descr = strdup("Open to search for new folders");
	if(append_folder(dfold, 0) == -1) {
		display_msg(MSG_WARN, "IMAP INIT", "Failed to append top folder");
		imap_close(imap, 0);
		return -1;
	}

	load_folders_conf(imap->name);
	imap->conninprogress = 0;
	return 0;
}

void imap_disconnect(struct _imap_src *imap) {
	if(imap->imapsock == -1)
		return;

#ifdef SASL
	if(AuthState)
		gen_sasl_client.free_state(AuthState);

	#ifdef PROT_STDIO
	prot_free(imap->pin);
	prot_free(imap->pout);
	#endif
#endif

	ConMan.del_cinfo(imap->imapsock);
	imap->imapsock = -1;

	if(imap->imap_in) {
		fclose(imap->imap_in);
		imap->imap_in = NULL;
		imap->imap_out = NULL;
	}

	return;
}

int imap_reconnect(struct _imap_src *imap) {
	if(imap->conninprogress) {
		imap_close(imap, 0);
		return -1;
	}

	imap_disconnect(imap);

	if(imap->response)
		free(imap->response);
	imap->response = NULL;
	imap->plist = NULL;
	imap->elem = NULL;
	if(imap->search_res)
		free(imap->search_res);
	imap->search_res = NULL;

	if(imap->istate == ISTATE_NOCON)
		return 0;

	if(imap_connect(imap) != 0) {
		display_msg(MSG_WARN, "IMAP reconnect", "Failed to reconnect");
		return -1;
	}


	imap->conninprogress = 1;
	if(imap->istate != ISTATE_AUTH) {
		if(imap_login(imap) != 0) {
			display_msg(MSG_WARN, "IMAP reconnect", "Failed to log in");
			imap->conninprogress = 0;
			return -1;
		}
	}

	if(imap->ifold) {
		if(imap_command
		   (imap, ICOM_SELECT, "%s",
			imap_string(imap, imap->ifold->fold_path)) != IMAP_OK) {
			imap->ifold = NULL;
			imap->svfold = NULL;
			imap->conninprogress = 0;
			return -1;
		}
	}

	imap->conninprogress = 0;
	return 0;
}

void imap_close(struct _imap_src *imap, int bygui) {
	if(imap->imapsock == -1)
		return;

	if(bygui) {
		if(imap->ifold)
			imap->ifold->close(imap->ifold);

		if(imap->istate != ISTATE_NOCON)
			imap_command(imap, ICOM_LOGOUT, NULL);

		save_folders_conf(imap->name, F_IMAP);
	}

	imap_disconnect(imap);

	imap->icap = 0;
	imap->istate = 0;
	imap->ifold = NULL;
	imap->svfold = NULL;
	imap->imsg = NULL;
	imap->iinbox = NULL;
	imap->itrash = NULL;
	imap->conninprogress = 0;
	imap->lastcom = time(NULL);
	if(imap->search_res)
		free(imap->search_res);
	imap->search_res = NULL;
	discard_imap_folders(imap);

	if(!bygui) {
		current_folder = inbox;
		redraw_folders();
		update_title();
		set_menu_imap();
	}

	set_imap_timer();
	return;
}

struct _mail_folder *imap_folder_switch(struct _imap_src *imap,
										struct _mail_folder *folder) {
	struct _mail_folder *ofold;

	if(folder == NULL) {
		if(imap->ifold)
			return imap->ifold;

		if(!(imap->flags & ISRC_NOEXPUNGE))
			imap_command(imap, ICOM_CLOSE, NULL);
		return NULL;
	}

	if(folder == imap->ifold)
		return imap->ifold;

	if(!(imap->flags & ISRC_NOEXPUNGE)) {
		if(imap->ifold && (imap->ifold->status & FEXPNG)) {
			if(!(imap->ifold->status & FRONLY))
				imap_command(imap, ICOM_EXPUNGE, NULL);
			imap->ifold->status &= ~FEXPNG;
		}
	}

	ofold = imap->ifold;
	imap->ifold = folder;
	if(imap_command
	   (imap, ICOM_SELECT, "%s",
		imap_string(imap, folder->fold_path)) != IMAP_OK) {
		imap->ifold = ofold;
		return NULL;
	}

	return ofold ? ofold : folder;
}

char *get_imap_folder_name(struct _mail_folder *folder) {
	return folder->fold_path;
}

int imap_folder_expunge(struct _mail_folder *folder) {
	struct _mail_folder *ofold;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

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

	if((ofold = imap_folder_switch(imap, folder)) == NULL)
		return -1;

	if(imap->ifold && !(imap->ifold->status & FRONLY)) {
		if(imap_command(imap, ICOM_EXPUNGE, NULL) != 0) {
			imap_folder_switch(imap, ofold);
			return -1;
		}
		imap->ifold->status &= ~FEXPNG;
	}

	imap_folder_switch(imap, ofold);
	return 0;
}

char *get_imap_folder_short_name(struct _imap_src *imap,
								 struct _mail_folder *folder) {
	char *p;

	if(folder->hdelim == '\0')
		return folder->fold_path;

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

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

	return p;
}

char *get_imap_folder_path(struct _imap_src *imap,
						   struct _mail_folder *folder) {
	static char fpath[MAX_IMAP_FOLD_NAME_LEN];
	char *p;

	if(folder->hdelim == '\0')
		return NULL;

	strcpy(fpath, folder->fold_path);

	if((p = strrchr(fpath, folder->hdelim)) == NULL)
		return NULL;

	*p = '\0';

	return fpath;
}

struct _mail_folder *get_imap_trash(struct _imap_src *imap,
									struct _mail_msg *msg) {
	char buf[255], *p;
	struct _mail_folder *trfold;

	if(!msg)
		return NULL;

	if((msg->folder->status & NOTRASH) ||
	   (msg->status & DELPERM) ||
	   (msg->folder->flags & FNTRASH) || !(imap->flags & ISRC_TRASH))
		return NULL;

	if((p = get_imap_folder_domain(imap, msg->folder)) != NULL)
		snprintf(buf, sizeof(buf), "%s%c%s", p, msg->folder->hdelim,
				 IMAP_TRASH);
	else
		return imap->itrash;

	if((trfold = find_imap_folder(imap, buf)) != NULL) {
		trfold->status |= NOTRASH;
		if(trfold == msg->folder)
			return NULL;
		return trfold;
	}

	if(imap_command(imap, ICOM_LIST, "\"\" %s", buf) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Failed to list %s folder", buf);
		return NULL;
	}

	if((trfold = find_imap_folder(imap, buf)) == NULL) {
		if((trfold = create_imap_folder(imap, NULL, buf)) == NULL) {
			if(!display_msg
			   (MSG_QUEST | MSG_DEFNO, "IMAP",
				"Failed to create %s folder\ncontinue without saving?",
				buf)) return NULL;
			msg->folder->status |= NOTRASH;
		} else
			redraw_fld_win();
	}
	trfold->status |= NOTRASH;

	if(trfold == msg->folder)
		return NULL;

	return trfold;
}

long
delete_imap_message_range(struct _imap_src *imap, struct _mail_msg *msg) {
	int mask;
	long top, bottom, i, deleted;
	struct _mail_folder *trfold, *ofold;
	struct _mail_msg *msg1;

	if(!imap_isconnected(imap))
		return -1;

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

	if(msg->folder && msg->folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP", "READ-ONLY folder");
		msg->status &= ~DELETED;
		msg->status &= ~DELETED;
		return -1;
	}

	if(msg->status & LOCKED) {
		msg->status &= ~DELETED;
		msg->status &= ~IMAPDELETED;
		return -1;
	}

	msg->folder->status |= FEXPNG;
	if(msg->status & IMAPDELETED)
		return (long)delete_imap_message(msg);

	mask = DELETED;
	if(msg->status & DELPERM)
		mask |= DELPERM;

	expand_uid_range(imap, msg->folder, msg, mask, IMAPDELETED, &bottom,
		&top, 1);
	if(top == bottom)
		return (long)delete_imap_message(msg);

	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(msg->folder, i)) != NULL)
			msg1->status &= ~DELETED;
	}

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return -1;

	imap->imsg = NULL;

	if((trfold = get_imap_trash(imap, msg)) != NULL) {
		if(imap_command
		   (imap, ICOM_UIDCOPY, "%ld:%ld %s", bottom, top,
			imap_string(imap, trfold->fold_path)) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Can not copy messages to %s",
						trfold->fold_path);
			imap_folder_switch(imap, ofold);
			return -1;
		}
	}

	if(imap_command
	   (imap, ICOM_UIDSTORE, "%ld:%ld FLAGS.SILENT (\\Deleted)", bottom,
		top) != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return -1;
	}

	imap_folder_switch(imap, ofold);

	deleted = 0;
	ofold = msg->folder;
	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(ofold, i)) == NULL)
			continue;

		msg1->status |= (DELETED | DELPERM | IMAPDELETED);
		if(((folder_sort & 0x0f) == BY_MSGNUM) ||
		   (((folder_sort & 0x0f) == BY_UNREAD) &&
			(msg1->flags & UNREAD))) folder_sort &= ~FLD_SORTED;

		if(trfold) {
			trfold->num_msg++;
			if(msg1->flags & UNREAD)
				trfold->unread_num++;
		}

		msg_cache_del(msg1);
		deleted++;
	}

	return deleted;
}

int delete_imap_message(struct _mail_msg *msg) {
	struct _mail_folder *trfold, *ofold;
	struct _imap_src *imap = (struct _imap_src *) msg->folder->spec;

	if(!imap_isconnected(imap))
		return -1;

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

	msg->status &= ~DELETED;

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

	if(msg->folder && msg->folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP", "READ-ONLY folder");
		return -1;
	}

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

	imap->imsg = msg;

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL) {
		imap->imsg = NULL;
		return -1;
	}

	msg->folder->status |= FRESCAN;
	msg->folder->status |= FEXPNG;
	if(!(msg->status & IMAPDELETED)) {
		if((trfold = get_imap_trash(imap, msg)) != NULL) {
			if(imap_command
			   (imap, ICOM_UIDCOPY, "%ld %s", msg->uid,
				imap_string(imap, trfold->fold_path)) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Can not copy message to %s",
							trfold->fold_path);
				imap->imsg = NULL;
				imap_folder_switch(imap, ofold);
				return -1;
			}

			trfold->num_msg++;

			if(msg->flags & UNREAD)
				trfold->unread_num++;
			trfold->status |= FRESCAN;
		}
	}

	if(msg->status & IMAPDELETED) {
		if(imap_command
		   (imap, ICOM_UIDSTORE, "%ld -FLAGS.SILENT (\\Deleted)", msg->uid)
		   != IMAP_OK) {
			imap->imsg = NULL;
			imap_folder_switch(imap, ofold);
			return -1;
		}
		msg->status &= ~(DELETED | DELPERM | IMAPDELETED);
	} else {
		if(imap_command
		   (imap, ICOM_UIDSTORE, "%ld +FLAGS.SILENT (\\Deleted)", msg->uid)
		   != IMAP_OK) {
			imap->imsg = NULL;
			imap_folder_switch(imap, ofold);
			return -1;
		}
		msg->status |= (DELETED | DELPERM | IMAPDELETED);
	}

	imap_folder_switch(imap, ofold);

	imap->imsg = NULL;

	if(((folder_sort & 0x0f) == BY_MSGNUM) ||
	   (((folder_sort & 0x0f) == BY_UNREAD) && (msg->flags & UNREAD)))
		folder_sort &= ~FLD_SORTED;

	msg_cache_del(msg);

	return 0;
}

int get_imap_message_header(struct _mail_msg *msg) {
	struct _imap_src *imap;
	struct _mail_folder *ofold;
	int oflags = msg->flags;
	int ohflags = msg->header->flags;
	int ostatus = msg->status;

	imap = (struct _imap_src *) msg->folder->spec;
	if(!imap_isconnected(imap))
		return -1;

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

	if((msg->num != -1) ||
	   (msg->uid == -1) ||
	   !(msg->status & H_SHORT) ||
	   (msg->status & MNOREFRESH) || !(msg->flags & H_ONLY))
		return 0;

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return -1;

	imap->imsg = msg;
	if(imap_command(imap, ICOM_UIDFETCH, "%ld (RFC822.HEADER)", msg->uid)
	   != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		imap->imsg = NULL;
		return -1;
	}

	imap->imsg = NULL;

	if(oflags & UNREAD)
		msg->flags |= UNREAD;

	if(ohflags & UNREAD)
		msg->header->flags |= UNREAD;

	if(ostatus & RECENT)
		msg->status |= RECENT;

	imap_folder_switch(imap, ofold);

	return 0;
}

unsigned long get_imap_message_validity(struct _mail_msg *msg) {
	/*
	struct _imap_src *imap = (struct _imap_src *)msg->folder->spec;
	unsigned long validity = msg->folder->uid;

	 validity = (validity << 16) | msg->uid;

	 return validity;
	*/

	return 0;
}

char *get_imap_msg_file(struct _mail_msg *msg) {
	struct _imap_src *imap = (struct _imap_src *) msg->folder->spec;
	static char buf[255];
	struct _mail_folder *ofold;
	struct stat sb;
	int oflags = msg->flags;
	int ohflags = msg->header->flags;
	int ostatus = msg->status;

	if(!imap_isconnected(imap))
		return NULL;

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

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

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

		msg->num = -1;
	}

	if((msg->num == -1) || (msg->flags & H_ONLY)) {
		if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
			return NULL;

		imap->imsg = msg;
		if(imap_command (imap, ICOM_UIDFETCH, "%ld (BODY.PEEK[])", msg->uid) != IMAP_OK) {
			imap_folder_switch(imap, ofold);
			imap->imsg = NULL;
			display_msg(MSG_STAT, NULL, "");
			return NULL;
		}

		imap->imsg = NULL;
		display_msg(MSG_STAT, NULL, "");

		imap_folder_switch(imap, ofold);
		if(oflags & UNREAD)
			msg->flags |= UNREAD;

		if(ohflags & UNREAD)
			msg->header->flags |= UNREAD;

		if(ostatus & RECENT)
			msg->status |= RECENT;

	}

	snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path, msg->num);

	return buf;
}

struct _mail_msg *get_imap_msg_by_uid(struct _mail_folder *folder,
									  long uid) {
	struct _mail_msg *msg;
	struct _mail_folder *ofold;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

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

	if(!imap_isconnected(imap))
		return NULL;

	if(uid == -1)
		return NULL;

	if((ofold = imap_folder_switch(imap, folder)) == NULL)
		return NULL;

	imap->imsg = NULL;
	if(imap_command
	   (imap, ICOM_UIDFETCH,
		"%ld (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
		uid) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Failed to retrieve message");
		imap_folder_switch(imap, ofold);
		return NULL;
	}

	imap_folder_switch(imap, ofold);
	folder->status &= ~SORTED;

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

	return NULL;
}

char *get_imap_minus_flags(struct _imap_src *imap, struct _mail_msg *msg) {
	static char flags[64];
	int num;

	num = 0;
	flags[0] = '\0';
	if((msg->flags & UNREAD) && !(msg->header->flags & UNREAD)) {
		strcat(flags, "\\Seen");
		num++;
	}

	if(!(msg->flags & ANSWERED) && (msg->header->flags & ANSWERED)) {
		strcat(flags, "\\Answered");
		num++;
	}

	if(!(msg->flags & MARKED) && (msg->header->flags & MARKED)) {
		strcat(flags, "\\Flagged");
		num++;
	}

	if(!(msg->status & DELETED) && (msg->status & IMAPDELETED)) {
		strcat(flags, "\\Deleted");
		num++;
	}

	return num ? flags : NULL;
}

char *get_imap_plus_flags(struct _imap_src *imap, struct _mail_msg *msg) {
	static char flags[64];
	int num;

	num = 0;
	flags[0] = '\0';
	if((msg->flags & UNREAD) && (msg->header->flags & UNREAD)) {
		strcat(flags, "\\Seen");
		num++;
	}

	if((msg->flags & ANSWERED) && !(msg->header->flags & ANSWERED)) {
		strcat(flags, "\\Answered");
		num++;
	}

	if((msg->flags & MARKED) && !(msg->header->flags & MARKED)) {
		strcat(flags, "\\Flagged");
		num++;
	}

	if((msg->status & DELETED) && !(msg->status & IMAPDELETED)) {
		strcat(flags, "\\Deleted");
		num++;
	}

	return num ? flags : NULL;
}

char *get_imap_flags(struct _imap_src *imap, struct _mail_msg *msg) {
	static char flags[64];
	int num;

	num = 0;
	flags[0] = '\0';
	if(!(msg->flags & UNREAD)) {
		strcat(flags, "\\Seen");
		num++;
	}

	if(msg->flags & ANSWERED) {
		strcat(flags, num ? " \\Answered" : "\\Answered");
		num++;
	}

	if(msg->flags & MARKED) {
		strcat(flags, num ? " \\Flagged" : "\\Flagged");
		num++;
	}

	if(msg->status & DELETED) {
		strcat(flags, num ? " \\Deleted" : "\\Deleted");
		num++;
	}

	return num ? flags : NULL;
}

void
update_imap_message_range(struct _imap_src *imap, struct _mail_msg *msg) {
	struct _mail_folder *ofold;
	struct _mail_msg *msg1;
	char *flags;
	int checkmask, plusmask, minusmask;
	long bottom, top, i;

	if(!imap_isconnected(imap))
		return;

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

	if((msg->status & DELETED) || (msg->status & IMAPDELETED)) {
		update_imap_message(msg);
		return;
	}

	checkmask = (UNREAD | ANSWERED | MARKED);
	if((msg->flags & checkmask) == (msg->header->flags & checkmask))
		return;

	plusmask =
	(msg->flags & checkmask) & ~(msg->header->flags & checkmask);
	minusmask =
	~(msg->flags & checkmask) & (msg->header->flags & checkmask);
	expand_uid_range(imap, msg->folder, msg, plusmask, minusmask, &bottom,
					 &top, 0);

	if(bottom == top) {
		update_imap_message(msg);
		return;
	}

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return;

	imap->imsg = NULL;

	if((flags = get_imap_flags(imap, msg)) != NULL) {
		if(imap_command(imap, ICOM_UIDSTORE, "%d:%d FLAGS.SILENT (%s)",
						bottom, top, flags) == IMAP_OK)
			msg->header->flags = msg->flags;
	} else {
		flags = get_imap_minus_flags(imap, msg);
		if(imap_command(imap, ICOM_UIDSTORE, "%d:%d -FLAGS.SILENT (%s)",
						bottom, top, flags ? flags : "\\Seen") == IMAP_OK)
			msg->header->flags = msg->flags;
	}

	imap_folder_switch(imap, ofold);

	ofold = msg->folder;
	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(ofold, i)) == NULL)
			continue;
		msg1->header->flags = msg1->flags;
		msg_cache_del(msg1);
	}

	return;
}

int update_imap_message(struct _mail_msg *msg) {
	struct _mail_folder *ofold;
	char *flags;
	struct _imap_src *imap = (struct _imap_src *) msg->folder->spec;
	int delflag = (msg->status & (DELETED | IMAPDELETED));

	if(!imap_isconnected(imap))
		return -1;

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

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

	if(((msg->flags & UNREAD) == (msg->header->flags & UNREAD)) &&
	   ((msg->flags & ANSWERED) == (msg->header->flags & ANSWERED)) &&
	   ((msg->flags & MARKED) == (msg->header->flags & MARKED)) &&
	   ((delflag == 0) || (delflag == (DELETED | IMAPDELETED))))
		return 0;

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return -1;

	msg_cache_del(msg);
	imap->imsg = msg;
	if((flags = get_imap_flags(imap, msg)) != NULL) {
		if(imap_command(imap, ICOM_UIDSTORE, "%d FLAGS.SILENT (%s)",
						msg->uid, flags) != IMAP_OK) {
			imap->imsg = NULL;
			imap_folder_switch(imap, ofold);
			return -1;
		}
	} else {
		flags = get_imap_minus_flags(imap, msg);
		if(imap_command(imap, ICOM_UIDSTORE, "%d -FLAGS.SILENT (%s)",
						msg->uid, flags ? flags : "\\Seen") != IMAP_OK) {
			imap->imsg = NULL;
			imap_folder_switch(imap, ofold);
			return -1;
		}
	}

	if(msg->status & DELETED)
		msg->status |= IMAPDELETED;
	else
		msg->status &= ~IMAPDELETED;

	msg->header->flags = msg->flags;
	imap->imsg = NULL;
	imap_folder_switch(imap, ofold);

	return 0;
}

int refresh_imap_message(struct _mail_msg *msg) {
	char buf[255];
	struct _mail_folder *ofold;
	struct _imap_src *imap = (struct _imap_src *) msg->folder->spec;

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

	if(!imap_isconnected(imap))
		return -1;

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

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return -1;

	msg_cache_del(msg);
	if(msg->msg_body)
		msg->free_text(msg);
	discard_mime(msg->mime);
	msg->mime = NULL;
	if(msg->num > 0) {
		snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path,
				 msg->num);
		unlink(buf);
		msg->num = -1;
	}

	imap->imsg = msg;
	if(imap_command
	   (imap, ICOM_UIDFETCH,
		"%d (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
		msg->uid) != IMAP_OK) {
		imap->imsg = NULL;
		imap_folder_switch(imap, ofold);
		return -1;
	}

	imap->imsg = NULL;
	imap_folder_switch(imap, ofold);
	cache_msg(msg);

	return 0;
}

void imap_message(struct _imap_src *imap, struct _mail_msg *msg) {
	msg->mdelete = delete_imap_message;
	msg->print = print_message;
	msg->print_body = print_message_body;
	msg->get_text = get_message_text;
	msg->get_header = get_imap_message_header;
	msg->free_text = free_message_text;
	msg->get_file = get_imap_msg_file;
	msg->update = update_imap_message;
	msg->validity = get_imap_message_validity;
	msg->refresh = refresh_imap_message;
	msg->type = M_IMAP;
}

int open_imap_folder(struct _mail_folder *folder, int flags) {
	struct _mail_folder *pfold;
	struct _mail_msg *msg, *msg1, *msg_lock;
	char buf[255];
	int i, k;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return -1;

	if(!folder)
		return -1;

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

	if(imap->istate == ISTATE_SELECT)
		imap->svfold = imap->ifold;
	else
		imap->svfold = NULL;

	if(!(imap->flags & ISRC_NOEXPUNGE)) {
		if(imap->ifold && (imap->ifold->status & FEXPNG)) {
			if(!(imap->ifold->status & FRONLY))
				imap_command(imap, ICOM_EXPUNGE, NULL);
			imap->ifold->status &= ~FEXPNG;
		}
	}

	imap->ifold = folder;
	imap->imsg = NULL;
	if(imap_command
	   (imap, ICOM_SELECT, "%s",
		imap_string(imap, folder->fold_path)) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Can not SELECT folder");
		imap->ifold = NULL;
		imap->svfold = NULL;
		return -1;
	}

	imap->istate = ISTATE_SELECT;

	i = folder->num_msg;
	folder->num_msg = 0;
	folder->unread_num = 0;
	if((folder->flags & CACHED) &&
	   !(flags & FOPEN_NOCACHE) && !exists_cache(folder)) {
		flags |= FOPEN_NOCACHE;
		flags |= FOPEN_CRCACHE;
	}

	if(i) {
		if((folder->flags & CACHED) && !(flags & FOPEN_NOCACHE)) {
			if(imap_command(imap, ICOM_UIDSEARCH,
							const_cast <
							char *>((flags & FOPEN_UNREAD) ? "UNSEEN" :
									"ALL")) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Failed to find messages");
				imap->ifold = NULL;
				goto open_fail;
			}

			if(imap->search_res && (imap->search_res[0] > 0)) {
				for(k = 1; k <= imap->search_res[0]; k++) {
					if((msg = msg_cache(folder, imap->search_res[k])) ==
					   NULL) {
						if(imap_command
						   (imap, ICOM_UIDFETCH,
							"%d (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
							imap->search_res[k]) != IMAP_OK) {
							display_msg(MSG_WARN, "IMAP",
										"Failed to fetch UID %d",
										imap->search_res[k]);
							imap->ifold = NULL;
							free(imap->search_res);
							imap->search_res = NULL;
							goto open_fail;
						}
					} else {
						msg->next = folder->messages;
						folder->messages = msg;
						folder->status &= ~SORTED;
						folder->num_msg++;
						if(msg->flags & UNREAD)
							folder->unread_num++;
					}
				}
				free(imap->search_res);
				imap->search_res = NULL;
			}

		} else if(flags & FOPEN_UNREAD) {
			if(imap_command(imap, ICOM_SEARCH, "UNSEEN") != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP",
							"Failed to find unseen messages");
				imap->ifold = NULL;
				goto open_fail;
			}

			if(imap->search_res && (imap->search_res[0] > 0)) {
				for(k = 1; k <= imap->search_res[0]; k++) {
					if(imap_command
					   (imap, ICOM_FETCH,
						"%d (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
						imap->search_res[k]) != IMAP_OK) {
						display_msg(MSG_WARN, "IMAP", "Failed to fetch %d",
									imap->search_res[k]);
						imap->ifold = NULL;
						free(imap->search_res);
						imap->search_res = NULL;
						goto open_fail;
					}
				}
				free(imap->search_res);
				imap->search_res = NULL;
			}
		} else {
			if(imap_command
			   (imap, ICOM_FETCH,
				"1:%d (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
				i) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Failed to scan folder");
				imap->ifold = NULL;
				goto open_fail;
			}
		}
	} else
		delete_cache(folder);

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

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

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

	sort_folder(folder);
	expire_msgs(folder);
	display_msg(MSG_STAT, NULL, "");

	return 0;

	open_fail:
	if(!imap_isconnected(imap))
		return -1;

	folder->status &= ~OPENED;
	folder->status &= ~SORTED;
	folder->status &= ~SEARCH;
	folder->status &= ~FUNREAD;
	folder->status &= ~FEXPNG;

	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", imap->fimap->fold_path,
						 msg->num);
				unlink(buf);
				msg->num = -1;
				cache_msg(msg);
			}

			discard_message(msg);
		}

		msg = msg1;
	}
	folder->messages = msg_lock;
	return -1;
}

int imap_get_recent(struct _imap_src *imap) {
	int res = 0;
	long i, onum;

	if(!imap->ifold || !(imap->ifold->status & FRECNT))
		return 0;

	if(imap_command(imap, ICOM_UIDSEARCH, "RECENT") != IMAP_OK)
		return -1;

	if(!imap->search_res) {
		imap->ifold->status &= ~FRECNT;
		return 0;
	}

	onum = imap->ifold->num_msg;
	for(i = 1; i <= imap->search_res[0]; i++) {
		if(!get_msg_by_uid(imap->ifold, imap->search_res[i])) {
			msg_cache_deluid(imap->ifold, imap->search_res[i]);
			imap->imsg = NULL;
			if(imap_command
			   (imap, ICOM_UIDFETCH,
				"%ld (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
				imap->search_res[i]) != IMAP_OK) {
				free(imap->search_res);
				imap->search_res = NULL;
				imap->ifold->num_msg = onum;
				return -1;
			}
			res = 1;
		}
	}

	imap->ifold->num_msg = onum;
	free(imap->search_res);
	imap->search_res = NULL;
	imap->ifold->status |= FRESCAN;
	imap->ifold->status &= ~FRECNT;

	return res;
}

int refresh_imap_folder(struct _mail_folder *folder) {
	struct _mail_folder *ofold;
	long i;
	unsigned long oldnum = folder->num_msg, oldunum = folder->unread_num;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return -1;

	if(imap->icap & ICAP_STATUS) {
		if(imap_command
		   (imap, ICOM_STATUS, "%s (MESSAGES UNSEEN RECENT)",
			imap_string(imap, folder->fold_path)) != IMAP_OK)
			return -1;
		if((folder->num_msg == oldnum) &&
		   (folder->unread_num == oldunum) && !(folder->status & FRECNT))
			return 0;
		if((ofold = imap_folder_switch(imap, folder)) == NULL)
			return -1;

		goto frefresh;
	}

	if(imap->ifold == folder) {
		if(!(imap->flags & ISRC_NOEXPUNGE)) {
			if((imap->ifold->status & FEXPNG) &&
			   !(imap->ifold->status & FRONLY))
				imap_command(imap, ICOM_EXPUNGE, NULL);
			imap->ifold->status &= ~FEXPNG;
			imap_command(imap, ICOM_CLOSE, NULL);
		}

		if(imap_command
		   (imap, ICOM_SELECT, "%s",
			imap_string(imap, folder->fold_path)) != IMAP_OK)
			return -1;
		ofold = imap->ifold = folder;
	} else {
		if((ofold = imap_folder_switch(imap, folder)) == NULL)
			return -1;
	}

	if(!(folder->status & FRECNT) &&
	   (oldnum == folder->num_msg) && (oldunum == folder->unread_num)) {
		imap_folder_switch(imap, ofold);
		return 0;
	}

	frefresh:
	folder->status |= FRESCAN;
	if(!(folder->status & OPENED)) {
		imap_folder_switch(imap, ofold);
		return 1;
	}

	if(imap_command(imap, ICOM_UIDSEARCH,
					const_cast <
					char *>((folder->status & FUNREAD) ? "UNSEEN" :
							"ALL")) != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return -1;
	}

	if(!imap->search_res) {
		imap_folder_switch(imap, ofold);
		return 1;
	}

	oldnum = folder->num_msg;
	oldunum = folder->unread_num;

	for(i = 1; i <= imap->search_res[0]; i++) {
		if(!get_msg_by_uid(folder, imap->search_res[i])) {
			msg_cache_deluid(folder, imap->search_res[i]);
			imap->imsg = NULL;
			if(imap_command
			   (imap, ICOM_UIDFETCH,
				"%ld (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)",
				imap->search_res[i]) != IMAP_OK) {
				free(imap->search_res);
				imap->search_res = NULL;
				imap_folder_switch(imap, ofold);
				return -1;
			}
		}
	}

	free(imap->search_res);
	imap->search_res = NULL;
	folder->num_msg = oldnum;
	folder->unread_num = oldunum;
	folder->status &= ~FRECNT;
	folder->status &= ~SORTED;
	imap_folder_switch(imap, ofold);
	return 1;
}

int rescan_imap_folder(struct _mail_folder *folder) {
	struct _mail_folder *ofold;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return -1;

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

	if(imap->icap & ICAP_STATUS) {
		if(imap->ifold && !(imap->flags & ISRC_NOEXPUNGE))
			imap_command(imap, ICOM_CLOSE, NULL);

		if(imap_command
		   (imap, ICOM_STATUS, "%s (MESSAGES UNSEEN UIDVALIDITY)",
			imap_string(imap, folder->fold_path)) != IMAP_OK) {
			if(imap->ifold)
				imap_command(imap, ICOM_SELECT, "%s",
							 imap_string(imap, imap->ifold->fold_path));
			return -1;
		}
		if(imap->ifold)
			imap_command(imap, ICOM_SELECT, "%s",
						 imap_string(imap, imap->ifold->fold_path));
		return 0;
	}

	if((ofold = imap_folder_switch(imap, folder)) == NULL)
		return -1;

	if(imap_command(imap, ICOM_SEARCH, "UNSEEN") != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return -1;
	}

	imap_folder_switch(imap, ofold);

	if(imap->search_res) {
		folder->unread_num = imap->search_res[0];
		free(imap->search_res);
		imap->search_res = NULL;
	}

	return 0;
}

void close_imap_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg, *msg1, *msg_lock;
	int deleted = 0;
	char buf[255];
	struct _mail_folder *pfold;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return;

	if(!folder)
		return;

	if(!(folder->status & FRONLY))
		folder->update(folder);

	for(msg = folder->messages; msg; msg = msg->next) {
		if(!(msg->status & LOCKED) &&
		   !(msg->status & MNOTEXISTS) &&
		   ((msg->status & DELETED) || (msg->status & DELPERM))) {
			deleted = 1;
			break;
		}
	}

	if(!(imap->flags & ISRC_NOEXPUNGE)) {
		if((deleted || (folder->status & FEXPNG)) &&
		   !(folder->status & FRONLY))
			imap_command(imap, ICOM_EXPUNGE, NULL);
		folder->status &= ~FEXPNG;
	}
	msg = folder->messages;
	msg_lock = NULL;
	while(msg) {
		msg->update(msg);
		msg1 = msg->next;
		if(msg->status & LOCKED) {
			msg->next = msg_lock;
			msg_lock = msg;
		} else {
			if(!(folder->flags & CACHED) || !(imap->flags & ISRC_CACHE)) {
				if(msg->num > 0) {
					snprintf(buf, sizeof(buf), "%s/%ld",
							 imap->fimap->fold_path, msg->num);
					unlink(buf);
					msg->num = -1;
					cache_msg(msg);
				}
			}

			discard_message(msg);
		}

		msg = msg1;
	}
	folder->messages = msg_lock;

	imap->ifold = imap->svfold;
	if(imap->svfold) {
		if(imap_command
		   (imap, ICOM_SELECT, "%s",
			imap_string(imap, imap->svfold->fold_path)) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Can not RE-SELECT folder");
			imap->ifold = NULL;
		} else
			imap->istate = ISTATE_SELECT;
		imap->svfold = NULL;
	} else {
		if((folder->status & OPENED) &&
		   (imap->istate == ISTATE_SELECT) &&
		   !(imap->flags & ISRC_NOEXPUNGE)) {
			if(imap_command(imap, ICOM_CLOSE, NULL) != IMAP_OK)
				display_msg(MSG_WARN, "IMAP", "Can not CLOSE folder");
		}
	}

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

	close_cache(folder);
	return;
}

void empty_imap_folder(struct _mail_folder *folder) {
	char buf[255];
	struct _mail_folder *ofold;
	struct _mail_msg *msg, *msg_nxt;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return;

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP", "Can not empty read-only folder");
		return;
	}

	msg = folder->messages;
	while(msg) {
		if(msg->status & LOCKED) {
			display_msg(MSG_WARN, "IMAP",
						"Can not empty folder with opened messages");
			return;
		}
		msg = msg->next;
	}

	msg = folder->messages;
	while(msg) {
		msg_nxt = msg->next;
		if(msg->num > 0) {
			snprintf(buf, sizeof(buf), "%s/%ld", imap->fimap->fold_path,
					 msg->num);
			unlink(buf);
		}
		discard_message(msg);
		msg = msg_nxt;
	}

	folder->messages = NULL;
	folder->unread_num = 0;

	if((ofold = imap_folder_switch(imap, folder)) == NULL)
		return;

	if(imap->ifold == ofold) {
		if(imap_command
		   (imap, ICOM_SELECT, "%s",
			imap_string(imap, folder->fold_path)) != IMAP_OK) {
			imap_folder_switch(imap, ofold);
			return;
		}
	}

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP", "Can not empty read only folder");
		return;
	}

	if(folder->num_msg == 0) {
		imap_folder_switch(imap, ofold);
		return;
	}

	if(imap_command
	   (imap, ICOM_STORE, "1:%d +FLAGS.SILENT (\\Deleted)",
		folder->num_msg) != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return;
	}

	imap_command(imap, ICOM_EXPUNGE, NULL);

	folder->num_msg = 0;

	imap_folder_switch(imap, ofold);
	delete_cache(folder);

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

	return;
}

int delete_imap_folder(struct _mail_folder *folder) {
	struct _mail_msg *msg;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return -1;

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP", "Can not delete read only folder");
		return -1;
	}

	if((folder == imap->iinbox) || (folder == imap->itrash) ||
	   (folder->status & NOTRASH)) {
		display_msg(MSG_WARN, "IMAP", "Can not delete this folder");
		return -1;
	}

	msg = folder->messages;
	while(msg) {
		if(msg->status & LOCKED) {
			display_msg(MSG_WARN, "IMAP",
						"Close all messages in this folder and try again");
			return -1;
		}
		msg = msg->next;
	}

	if(folder == imap->ifold) {
		imap->ifold = NULL;
		if(!(imap->flags & ISRC_NOEXPUNGE))
			imap_command(imap, ICOM_CLOSE, NULL);
	}

	switch(imap_command(imap, ICOM_DELETE, "%s",
						imap_string(imap, folder->fold_path))) {
		case IMAP_NO:
			display_msg(MSG_WARN, "IMAP",
						"Folder was probably already deleted\nremoving it anyway");
			break;

		case IMAP_OK:
			break;

		default:
			return -1;
			break;
	}

	delete_cache(folder);
	return remove_folder(folder);
}

int rename_imap_folder(struct _mail_folder *folder, char *name) {
	char *path;
	char fname[MAX_IMAP_FOLD_NAME_LEN];
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return -1;

	if(!folder || !name)
		return -1;

	if(folder->status & SYSTEM) {
		display_msg(MSG_WARN, "rename", "Can not rename system folder");
		return -1;
	}


	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP", "Can not rename read only folder");
		return -1;
	}

	if((strlen(name) < 1) || (strlen(name) > MAX_IMAP_FOLD_NAME_LEN)) {
		display_msg(MSG_WARN, "IMAP", "Invalid name length");
		return -1;
	}

	if((folder->hdelim != '\0') && (strchr(name, folder->hdelim) != NULL)) {
		display_msg(MSG_WARN, "IMAP", "Illegal character in folder name");
		return -1;
	}

	path = get_imap_folder_path(imap, folder);
	if((folder->hdelim != '\0') && path) {
		if(strlen(path) + strlen(name) >= MAX_IMAP_FOLD_NAME_LEN) {
			display_msg(MSG_WARN, "IMAP", "Name too long");
			return -1;
		}
		snprintf(fname, sizeof(fname), "%s%c%s", path, folder->hdelim,
				 name);
	} else
		strcpy(fname, name);

	if(find_imap_folder(imap, name)) {
		display_msg(MSG_WARN, "IMAP", "IMAP folder '%s' already exits",
					name);
		return -1;
	}

	if((folder == imap->iinbox) || (folder == imap->itrash)) {
		display_msg(MSG_WARN, "IMAP", "Can not rename this folder");
		return -1;
	}


	if(imap_command
	   (imap, ICOM_RENAME, "%s %s", imap_string(imap, folder->fold_path),
		fname) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Rename failed");
		return -1;
	}

	strcpy(folder->fold_path, fname);

	path = folder->sname;
	folder->sname = strdup(get_imap_folder_short_name(imap, folder));
	rename_cache(folder, path);

	if(path)
		free(path);

	update_cfold_path(folder);
	folder_sort &= ~FLD_SORTED;

	return 0;
}

void update_imap_folder(struct _mail_folder *folder) {
	struct _mail_folder *ofold, *fld;
	struct _mail_msg *msg, *msg_next;
	long deleted;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!imap_isconnected(imap))
		return;

	if(!folder->messages)
		return;

	msg = folder->messages;
	msg_next = msg->next;
	deleted = 0;

	if((ofold = imap_folder_switch(imap, folder)) == NULL)
		return;

	while(msg) {
		msg_next = msg->next;
		if(msg->uid < 0) {
			msg = msg_next;
			continue;
		}

		if(msg->status & MNOTEXISTS) {
			msg = msg_next;
			continue;
		}

		if((msg->status & LOCKED) && !(msg->status & COPIED)) {
			msg->status &= ~DELETED;
			msg->status &= ~MOVED;
			msg->flags &= ~NOT_SENT;
			msg = msg->next;
			continue;
		}

		if(msg->status & DELETED) {
			if(msg->status & IMAPDELETED) {
				msg = msg->next;
				continue;
			}
			if(folder->status & FRONLY)
				continue;
			display_msg(MSG_STAT, NULL, "Deleting %ld", msg->uid);
			if(delete_imap_message_range(imap, msg) >= 0)
				deleted++;
			msg = folder->messages;
			continue;
		} else if(msg->status & MOVED) {
			msg->status &= ~MOVED;
			if(folder->status & FRONLY)
				continue;
			fld = msg->folder;
			msg->folder = folder;
			display_msg(MSG_STAT, NULL, "Moving %ld", msg->uid);
			update_imap_message_range(imap, msg);
			if((fld->type & F_IMAP) && (fld->spec == imap)) {
				if(move_to_imap_folder_range(imap, msg, fld) >= 0)
					deleted++;
			} else {
				if(fld->move(msg, fld) == 0)
					deleted++;
			}
			msg = folder->messages;
			continue;
		} else if(msg->status & COPIED) {
			fld = msg->folder;
			msg->folder = folder;
			msg->status &= ~COPIED;
			display_msg(MSG_STAT, NULL, "Copying %ld", msg->uid);
			update_imap_message_range(imap, msg);
			if((fld->type & F_IMAP) && (fld->spec == imap))
				copy_to_imap_folder_range(imap, msg, fld);
			else
				fld->copy(msg, fld);
		} else if(msg->flags & NOT_SENT) {
			msg->flags &= ~NOT_SENT;
			display_msg(MSG_STAT, NULL, "Sending %ld", msg->uid);
			msg->update(msg);
			send_message(msg);
		} else
			update_imap_message_range(imap, msg);

		msg = msg_next;
	}

	imap_folder_switch(imap, ofold);

	return;
}

struct _mail_msg *copy_to_imap_folder_range(struct _imap_src *imap,
											struct _mail_msg *msg,
											struct _mail_folder *folder) {
	long i, top, bottom, copied;
	struct _mail_folder *ofold;
	struct _mail_msg *msg1;

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

	msg->status &= ~COPIED;
	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP",
					"Can not copy messages to read only folder");
		return NULL;
	}

	if(msg->status & LOCKED)
		return NULL;
	folder->status |= FRESCAN;

	if(!(msg->type & M_IMAP) || !msg->folder ||
	   (msg->folder->spec != folder->spec))
		return copy_to_imap_folder(msg, folder);

	ofold = msg->folder;
	msg->folder = folder;
	msg->status |= COPIED;
	expand_uid_range(imap, ofold, msg, COPIED, 0, &bottom, &top, 1);
	msg->status &= ~COPIED;
	msg->folder = ofold;

	if(top == bottom)
		return copy_to_imap_folder(msg, folder);

	copied = 0;
	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(msg->folder, i)) != NULL) {
			msg1->folder = msg->folder;
			copied++;
			msg1->status &= ~COPIED;
			msg1->update(msg1);
		}
	}

	if(folder->status & OPENED)
		return copy_to_imap_folder(msg, folder);

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return NULL;

	if(imap_command
	   (imap, ICOM_UIDCOPY, "%ld:%ld %s", bottom, top,
		imap_string(imap, folder->fold_path)) != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return NULL;
	}

	imap_folder_switch(imap, ofold);

	ofold = msg->folder;
	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(ofold, i)) != NULL) {
			folder->num_msg++;
			if(msg1->flags & UNREAD)
				folder->unread_num++;
		}
	}

	folder->status &= ~SORTED;

	return NULL;
}

struct _mail_msg *copy_to_imap_folder(struct _mail_msg *msg,
									  struct _mail_folder *folder) {
	struct _mail_folder *ofold;
	struct _mail_msg *msg1;
	long i;
	char *flags;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

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

	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP",
					"Can not copy messages to read only folder");
		return NULL;
	}

	msg->status &= ~COPIED;

	imap->uid = -1;
	if(((folder->status & OPENED) || (msg->status & LOCKED))
	   && (imap->icap & ICAP_STATUS)) {
		if(imap_command
		   (imap, ICOM_STATUS, "%s (UIDNEXT)",
			imap_string(imap, folder->fold_path)) != IMAP_OK)
			imap->uid = -1;
	}

	msg->update(msg);
	if((msg->type & M_IMAP) && msg->folder &&
	   (msg->folder->spec == folder->spec)) {
		if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
			return NULL;

		if(imap_command
		   (imap, ICOM_UIDCOPY, "%ld %s", msg->uid,
			imap_string(imap, folder->fold_path)) != IMAP_OK) {
			imap_folder_switch(imap, ofold);
			return NULL;
		}

		imap_folder_switch(imap, ofold);
	} else {
		imap->imsg = msg;

		flags = get_imap_flags(imap, msg);
		if(imap->icap & ICAP_IMAP4REV1) {
			if(imap_command(imap, ICOM_APPEND, "%s (%s) \"%s\" {%d}",
							imap_string(imap, folder->fold_path),
							flags ? flags : "",
							get_imap_datetime_str(imap,
												  msg->header->rcv_time),
							calc_msg_len(msg)) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Append failed");
				imap->imsg = NULL;
				return NULL;
			}
		} else {
			if(imap_command
			   (imap, ICOM_APPEND, "%s {%d}",
				imap_string(imap, folder->fold_path),
				calc_msg_len(msg)) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Append failed");
				imap->imsg = NULL;
				return NULL;
			}
		}

		imap->imsg = NULL;
	}

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

	if((folder->status & OPENED) || (msg->status & LOCKED)) {
		if(imap->uid == -1) {
			if((ofold = imap_folder_switch(imap, folder)) == NULL)
				return NULL;

			imap_command(imap, ICOM_UIDSEARCH, "ALL");
			if(imap->search_res) {
				for(i = 1; i <= imap->search_res[0]; i++) {
					if(!get_msg_by_uid(folder, imap->search_res[i])) {
						imap->uid = imap->search_res[i];
						break;
					}
				}
				free(imap->search_res);
				imap->search_res = NULL;
			}
			imap_folder_switch(imap, ofold);
		}

		msg1 = copy_msg(msg);
		imap_message(imap, msg1);
		msg1->uid = imap->uid;
		msg1->flags |= H_ONLY;
		msg1->status &= ~LOCKED;
		msg1->next = folder->messages;
		msg1->folder = folder;
		msg1->num = -1;
		folder->messages = msg1;

		return msg1;
	}

	if(((folder_sort & 0x0f) == BY_MSGNUM) ||
	   (((folder_sort & 0x0f) == BY_UNREAD) && (msg->flags & UNREAD)))
		folder_sort &= ~FLD_SORTED;

	return msg;
}

long
move_to_imap_folder_range(struct _imap_src *imap, struct _mail_msg *msg,
						  struct _mail_folder *folder) {
	long i, top, bottom, moved;
	struct _mail_folder *ofold, *pfold;
	struct _mail_msg *msg1;

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

	msg->status &= ~MOVED;
	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP",
					"Can not move messages to read only folder");
		return -1;
	}

	if(msg->folder && msg->folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP",
					"Can not move messages from read only folder");
		return -1;
	}

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

	if((msg->folder) && (msg->folder == folder))
		return 0;

	if(!(msg->type & M_IMAP) || !msg->folder ||
	   (msg->folder->spec != folder->spec))
		return (long)move_to_imap_folder(msg, folder);

	ofold = msg->folder;
	msg->folder = folder;
	msg->status |= MOVED;
	expand_uid_range(imap, ofold, msg, MOVED, 0, &bottom, &top, 1);
	msg->status &= ~MOVED;
	msg->folder = ofold;

	if(top == bottom)
		return (long)move_to_imap_folder(msg, folder);

	moved = 0;
	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(msg->folder, i)) != NULL) {
			msg_cache_del(msg1);
			msg1->folder = msg->folder;
			moved++;
			msg1->status &= ~MOVED;
			msg1->update(msg1);
		}
	}

	if(folder->status & OPENED)
		return move_to_imap_folder(msg, folder);

	if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
		return -1;

	if(imap_command
	   (imap, ICOM_UIDCOPY, "%ld:%ld %s", bottom, top,
		imap_string(imap, folder->fold_path)) != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return -1;
	}

	if(imap_command
	   (imap, ICOM_UIDSTORE, "%ld:%ld FLAGS.SILENT (\\Deleted)", bottom,
		top) != IMAP_OK) {
		imap_folder_switch(imap, ofold);
		return -1;
	}

	imap_folder_switch(imap, ofold);

	ofold = msg->folder;
	ofold->status |= FEXPNG;

	for(i = bottom; i <= top; i++) {
		if((msg1 = get_msg_by_uid(ofold, i)) != NULL) {
			folder->num_msg++;
			if(msg1->flags & UNREAD)
				folder->unread_num++;
			if(msg1->status & RECENT) {
				msg1->status &= ~RECENT;
				folder->status |= FRECNT;
				for(pfold = folder->pfold; pfold; pfold = pfold->pfold)
					pfold->status |= FMRKTMP;
			}
			msg1->status |= (DELETED | DELPERM | IMAPDELETED);
		}
	}

	folder->status &= ~SORTED;

	return moved;
}

int move_to_imap_folder(struct _mail_msg *msg, struct _mail_folder *folder) {
	struct _mail_folder *ofold, *pfold;
	struct _mail_msg *msg1;
	long i;
	char *flags;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

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

	folder->status |= FRESCAN;
	if(folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP",
					"Can not move messages to read only folder");
		return -1;
	}

	if(msg->folder && msg->folder->status & FRONLY) {
		display_msg(MSG_WARN, "IMAP",
					"Can not move messages from read only folder");
		return -1;
	}

	msg->status &= ~MOVED;

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

	if(msg->folder) {
		if(msg->folder == folder)
			return 0;
		msg->folder->status |= FRESCAN;
	}

	imap->uid = -1;
	if((folder->status & OPENED) && (imap->icap & ICAP_STATUS)) {
		if(imap_command
		   (imap, ICOM_STATUS, "%s (UIDNEXT)",
			imap_string(imap, folder->fold_path)) != IMAP_OK)
			imap->uid = -1;
		else
			msg_cache_deluid(folder, imap->uid);
	}

	msg->update(msg);
	msg_cache_del(msg);
	if((msg->type & M_IMAP) && msg->folder &&
	   (msg->folder->spec == folder->spec)) {
		if((ofold = imap_folder_switch(imap, msg->folder)) == NULL)
			return -1;

		if(imap_command
		   (imap, ICOM_UIDCOPY, "%ld %s", msg->uid,
			imap_string(imap, folder->fold_path)) != IMAP_OK) {
			imap_folder_switch(imap, ofold);
			return -1;
		}

		imap_folder_switch(imap, ofold);
	} else {
		imap->imsg = msg;

		flags = get_imap_flags(imap, msg);
		if(imap->icap & ICAP_IMAP4REV1) {
			if(imap_command(imap, ICOM_APPEND, "%s (%s) \"%s\" {%d}",
							imap_string(imap, folder->fold_path),
							flags ? flags : "",
							get_imap_datetime_str(imap,
												  msg->header->rcv_time),
							calc_msg_len(msg)) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Append failed");
				imap->imsg = NULL;
				return -1;
			}
		} else {
			if(imap_command
			   (imap, ICOM_APPEND, "%s {%d}",
				imap_string(imap, folder->fold_path),
				calc_msg_len(msg)) != IMAP_OK) {
				display_msg(MSG_WARN, "IMAP", "Append failed");
				imap->imsg = NULL;
				return -1;
			}
		}

		imap->imsg = NULL;
	}

	msg->flags &= ~M_TEMP;

	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) {
		if(imap->uid == -1) {
			if((ofold = imap_folder_switch(imap, folder)) == NULL)
				return -1;

			imap_command(imap, ICOM_UIDSEARCH, "ALL");
			if(imap->search_res) {
				for(i = 1; i <= imap->search_res[0]; i++) {
					if(!get_msg_by_uid(folder, imap->search_res[i])) {
						imap->uid = imap->search_res[i];
						break;
					}
				}
				free(imap->search_res);
				imap->search_res = NULL;
			}
			imap_folder_switch(imap, ofold);
		}

		msg1 = copy_msg(msg);
		imap_message(imap, msg1);
		msg1->uid = imap->uid;
		msg1->flags |= H_ONLY;
		msg1->next = folder->messages;
		msg1->folder = folder;
		msg1->num = -1;
		folder->messages = msg1;
	}

	msg->status |= (DELETED | DELPERM);
	msg->mdelete(msg);

	folder->status &= ~SORTED;

	if(((folder_sort & 0x0f) == BY_MSGNUM) ||
	   (((folder_sort & 0x0f) == BY_UNREAD) && (msg->flags & UNREAD)))
		folder_sort &= ~FLD_SORTED;

	return 0;
}

char *get_search_string(char *where) {
	static char str[75];

	if(!strcasecmp(where, "Header"))
		return "TEXT";

	if(!strcasecmp(where, "Body"))
		return "BODY";

	if(!strcasecmp(where, "Message"))
		return "TEXT";

	if(!strcasecmp(where, "From"))
		return "FROM";

	if(!strcasecmp(where, "To"))
		return "TO";

	if(!strcasecmp(where, "Cc"))
		return "CC";

	if(!strcasecmp(where, "Bcc"))
		return "BCC";

	if(!strcasecmp(where, "Sender"))
		return "SENDER";

	if(!strcasecmp(where, "Subject"))
		return "SUBJECT";

	snprintf(str, sizeof(str), "HEADER \"%s\"", where);

	return str;
}

int
imap_find_text(struct _mail_folder *folder, char *text, char *where,
			   int flags, fld_search_cb callback) {
	struct _mail_folder *ofold;
	struct _mail_msg *msg;
	long found, i;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

	if(!text || !folder || !(folder->type & F_IMAP))
		return -1;

	if(strlen(where) > 63) {
		display_msg(MSG_WARN, "IMAP SEARCH", "Message part too long");
		return -1;
	}

	if(strlen(text) > 127) {
		display_msg(MSG_WARN, "IMAP SEARCH", "Expression too long");
		return -1;
	}

	if(!(folder->status & OPENED) && (folder->status & FRONLY)) {
		display_msg(MSG_WARN, "IMAP SEARCH",
					"Folder is read only\ncan not store search results");
		return -1;
	}

	if((ofold = imap_folder_switch(imap, folder)) == NULL)
		return -1;

	if(imap->search_res)
		free(imap->search_res);
	imap->search_res = NULL;

	if(imap_command(imap, ICOM_UIDSEARCH, "%s \"%s\"",
					get_search_string(where), text) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP SEARCH", "Search failed");
		imap_folder_switch(imap, ofold);
		return -1;
	}

	if(!imap->search_res)
		return 0;

	if(imap->search_res[0] == 0) {
		free(imap->search_res);
		imap->search_res = NULL;
		return 0;
	}

	found = imap->search_res[0];
	if(folder->status & OPENED) {
		for(i = 1; i <= found; i++) {
			if(callback)
				callback(folder, imap->search_res[i]);
			if((msg = get_msg_by_uid(folder, imap->search_res[i])) !=
			   NULL) msg->status |= MARKTMP;
			else
				imap_command(imap, ICOM_UIDSTORE,
							 "%ld +FLAGS.SILENT (\\Flagged)",
							 imap->search_res[i]);
		}
	} else {
		for(i = 1; i <= found; i++) {
			if(callback)
				callback(folder, imap->search_res[i]);
			imap_command(imap, ICOM_UIDSTORE,
						 "%ld +FLAGS.SILENT (\\Flagged)",
						 imap->search_res[i]);
		}
	}

	imap_folder_switch(imap, ofold);

	free(imap->search_res);
	imap->search_res = NULL;

	return found;
}

void imap_folder(struct _imap_src *imap, struct _mail_folder *folder) {
	folder->name = get_imap_folder_name;
	folder->open = open_imap_folder;
	folder->rescan = rescan_imap_folder;
	folder->close = close_imap_folder;
	folder->empty = empty_imap_folder;
	folder->fdelete = delete_imap_folder;
	folder->rename = rename_imap_folder;
	folder->update = update_imap_folder;
	folder->move = move_to_imap_folder;
	folder->copy = copy_to_imap_folder;
	folder->search = imap_find_text;
	folder->getuid = get_imap_folder_uid;
	folder->getmsg = get_imap_msg_by_uid;
	folder->refresh = refresh_imap_folder;
	folder->expunge = imap_folder_expunge;
	folder->hdelim = '\0';
	folder->type = F_IMAP;
	folder->status |= FREMOTE;
	folder->spec = imap;
}

char *get_imap_folder_domain_path(struct _imap_src *imap,
								  struct _mail_folder *folder) {
	char *p;

	if((*folder->fold_path != '#') || (folder->hdelim == '\0'))
		return folder->fold_path;

	if((p = strchr(folder->fold_path, folder->hdelim)) != NULL) {
		p++;
		return p;
	}

	return folder->fold_path;
}

char *get_imap_folder_domain(struct _imap_src *imap,
							 struct _mail_folder *folder) {
	static char fdomain[16];
	char *p;

	if(*folder->fold_path != '#')
		return NULL;

	strncpy(fdomain, folder->fold_path, 15);
	fdomain[15] = '\0';

	if((folder->hdelim != '\0') &&
	   ((p = strchr(fdomain, folder->hdelim)) != NULL))
		*p = '\0';

	return fdomain;
}

struct _mail_folder *find_imap_folder(struct _imap_src *imap, char *name) {
	int i;

	if(!name || !*name || (strlen(name) > MAX_FOLD_NAME_LEN))
		return NULL;

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(!(mailbox[i]->type & F_IMAP))
			continue;
		if(imap && ((struct _imap_src *) mailbox[i]->spec != imap))
			continue;
		if(!strcmp(mailbox[i]->fold_path, name))
			return mailbox[i];
	}

	return NULL;
}

struct _mail_folder *find_imap_folder_by_name(struct _imap_src *imap,
											  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]->type & F_IMAP))
			continue;
		if(imap && ((struct _imap_src *) mailbox[i]->spec != imap))
			continue;
		if(!strcmp(mailbox[i]->name(mailbox[i]), name))
			return mailbox[i];
	}

	return NULL;
}

void discard_imap_folders(struct _imap_src *imap) {
	int i;
	struct _mail_msg *msg, *msg1;
	char buf[255];

	for(i = 0; i < (int)mailbox.size(); i++) {
		if(!(mailbox[i]->type & F_IMAP) || (mailbox[i]->spec != imap))
			continue;
		mailbox[i]->close(mailbox[i]);
		if(!(imap->flags & ISRC_CACHE))
			delete_cache(mailbox[i]);
		msg = mailbox[i]->messages;
		while(msg) {
			msg1 = msg->next;
			if(msg->status & LOCKED) {
				msg->folder = imap->fimap;
				msg->next = imap->fimap->messages;
				imap->fimap->messages = msg;
				local_message(msg);
			} else {
				if(!(mailbox[i]->flags & CACHED) ||
				   !(imap->flags & ISRC_CACHE)) {
					if(msg->num > 0) {
						snprintf(buf, sizeof(buf), "%s/%ld",
								 imap->fimap->fold_path, msg->num);
						unlink(buf);
						msg->num = -1;
						cache_msg(msg);
					}
				}
				discard_message(msg);
			}

			msg = msg1;
		}
		mailbox[i]->messages = NULL;

		remove_folder(mailbox[i]);
		i--;
	}

	folder_sort &= ~FLD_SORTED;
	imap->iinbox = NULL;
	imap->itrash = NULL;

	return;
}

struct _mail_folder *create_imap_folder(struct _imap_src *imap,
										struct _mail_folder *pfolder,
										char *name) {
	struct _mail_folder *folder, *ofold;
	char fname[MAX_IMAP_FOLD_NAME_LEN], *p;
	int len;

	if(!name)
		return NULL;

	if(!imap_isconnected(imap)) {
		display_msg(MSG_WARN, "IMAP", "Not connected");
		return NULL;
	}

	if(!imap && pfolder)
		imap = (struct _imap_src *) pfolder->spec;

	if(pfolder && (pfolder->hdelim != '\0') &&
	   strchr(name, pfolder->hdelim)) {
		display_msg(MSG_WARN, "Create IMAP folder",
					"Folder name can not contain %c", pfolder->hdelim);
		return NULL;
	}

	if(pfolder && (pfolder->status & NOINFR)) {
		display_msg(MSG_WARN, "Create IMAP folder",
					"This folder can not have subfolders");
		return NULL;
	}

	len = 2 + strlen(name) + (pfolder ? strlen(pfolder->fold_path) : 0);
	if(len > MAX_IMAP_FOLD_NAME_LEN) {
		display_msg(MSG_WARN, "IMAP", "folder name too long");
		return NULL;
	}

	if(pfolder)
		snprintf(fname, sizeof(fname), "%s%c%s", pfolder->fold_path,
				 pfolder->hdelim, name);
	else
		strcpy(fname, name);

	if(find_imap_folder(imap, fname) != NULL) {
		display_msg(MSG_WARN, "IMAP", "Folder already exists");
		return NULL;
	}

	if(imap_command(imap, ICOM_CREATE, "%s", fname) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Create failed");
		return NULL;
	}

	if(imap_command(imap, ICOM_LIST, "\"\" %s", fname) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Failed to list new folder");
		return NULL;
	}

	if((folder = find_imap_folder(imap, fname)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Folder was not created");
		return NULL;
	}

	while((p = strrchr(fname, folder->hdelim)) != NULL) {
		*p = '\0';
		if(find_imap_folder(imap, fname) == NULL)
			imap_command(imap, ICOM_LIST, "\"\" %s", fname);
	}

	if((ofold = imap_folder_switch(imap, folder)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Folder can not be selected");
		return NULL;
	}

	if(folder->status & FRONLY)
		display_msg(MSG_WARN, "IMAP", "New folder is read-only");

	imap_folder_switch(imap, ofold);

	folder_sort &= ~FLD_SORTED;

	return folder;
}

void imap_inbox(struct _imap_src *imap) {
	char buf[96];

	if(imap->iinbox)
		return;

	if(!imap->fimap) {
		snprintf(buf, sizeof(buf), "%s-%s", FIMAP, imap->name);
		if((imap->fimap = create_mh_folder(NULL, buf)) == NULL) {
			display_msg(MSG_WARN, "IMAP INIT", "Can not open folder %s",
						buf);
			imap_close(imap, 0);
			return;
		}

		imap->fimap->empty(imap->fimap);
		if(imap->fimap->open(imap->fimap, 0) == -1) {
			display_msg(MSG_WARN, "IMAP INIT", "Can not open folder %s",
						buf);
			imap_close(imap, 0);
			return;
		}

	}

	if(imap_command(imap, ICOM_LIST, "\"\" %s", IMAP_INBOX) != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Failed to find INBOX folder");
		imap_disconnect(imap);
		return;
	}

	imap->iinbox = find_imap_folder(imap, IMAP_INBOX);

	return;
}

long imap_inc(struct _retrieve_src *source, long *notify) {
	long i, num, connected;
	struct _mail_folder *ofold;
	struct _mail_msg *msg;
	struct _imap_src *imap;

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

	imap = (struct _imap_src *) source->spec;
	if(!(imap->flags & ISRC_RETRIEVE))
		return 0;

	connected = 0;
	if(!imap_isconnected(imap)) {
		if(imap_connect(imap) != 0)
			return -1;
		if(imap->istate != ISTATE_AUTH) {
			if(imap_login(imap) != 0)
				return -1;
		}
		imap_inbox(imap);
		connected = 1;
	}

	if(!imap->iinbox) {
		display_msg(MSG_WARN, "IMAP", "No INBOX on server");
		if(connected) {
			discard_imap_folders(imap);
			imap_command(imap, ICOM_LOGOUT, NULL);
			imap_disconnect(imap);
		}
		return -1;
	}

	if(imap->ifold == imap->iinbox) {
		if(imap_command(imap, ICOM_SELECT, "%s", imap->iinbox->fold_path)
		   != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Folder can not be selected");
			if(connected) {
				discard_imap_folders(imap);
				imap_command(imap, ICOM_LOGOUT, NULL);
				imap_disconnect(imap);
			}
			return -1;
		}
		ofold = imap->iinbox;
	} else if((ofold = imap_folder_switch(imap, imap->iinbox)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Folder can not be selected");
		if(connected) {
			discard_imap_folders(imap);
			imap_command(imap, ICOM_LOGOUT, NULL);
			imap_disconnect(imap);
		}
		return -1;
	}

	if(imap_command(imap, ICOM_UIDSEARCH, "NOT SEEN") != IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Search failed");
		imap_folder_switch(imap, ofold);
		if(connected) {
			discard_imap_folders(imap);
			imap_command(imap, ICOM_LOGOUT, NULL);
			imap_disconnect(imap);
		}
		return -1;
	}

	if(!imap->search_res || (imap->search_res[0] == 0)) {
		if(imap->search_res)
			free(imap->search_res);
		imap->search_res = NULL;
		imap_folder_switch(imap, ofold);
		if(connected) {
			discard_imap_folders(imap);
			imap_command(imap, ICOM_LOGOUT, NULL);
			imap_disconnect(imap);
		}
		return 0;
	}

	num = imap->search_res[0];
	for(i = 1; i <= num; i++) {
		/* if (imap_command(imap, ICOM_UIDFETCH, "%ld (INTERNALDATE RFC822.SIZE ENVELOPE FLAGS BODY UID)", imap->search_res[i]) != IMAP_OK)    { */
		if(imap_command
		   (imap, ICOM_UIDFETCH, "%ld (RFC822.HEADER UID)",
			imap->search_res[i]) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Failed to fetch message");
			if(imap->search_res)
				free(imap->search_res);
			imap->search_res = NULL;
			imap_folder_switch(imap, ofold);
			if(connected) {
				discard_imap_folders(imap);
				imap_command(imap, ICOM_LOGOUT, NULL);
				imap_disconnect(imap);
			}
			return -1;
		}

		if((msg = get_msg_by_uid(imap->iinbox, imap->search_res[i])) ==
		   NULL) {
			display_msg(MSG_WARN, "IMAP", "Failed to fetch message");
			if(imap->search_res)
				free(imap->search_res);
			imap->search_res = NULL;
			imap_folder_switch(imap, ofold);
			if(connected) {
				discard_imap_folders(imap);
				imap_command(imap, ICOM_LOGOUT, NULL);
				imap_disconnect(imap);
			}
			return -1;
		}
#ifdef WITH_FACES
		update_faces(msg);
#endif

		set_flags_by_status(msg);
		convert_fields(msg);
		msg->status |= (CHANGED | RECENT);

		switch(apply_rule(msg, 0)) {
			case 0:
				(*notify)++;
				break;

			case -1:
				if(imap->search_res)
					free(imap->search_res);
				imap->search_res = NULL;
				imap_folder_switch(imap, ofold);
				if(connected) {
					discard_imap_folders(imap);
					imap_command(imap, ICOM_LOGOUT, NULL);
					imap_disconnect(imap);
				}
				return -1;
				break;
		}
		imap_command(imap, ICOM_UIDSTORE, "%ld +FLAGS.SILENT (\\Seen)",
					 imap->search_res[i]);

	}

	imap_command(imap, ICOM_EXPUNGE, NULL);

	if(imap->search_res)
		free(imap->search_res);
	imap->search_res = NULL;

	imap_folder_switch(imap, ofold);
	if(connected) {
		discard_imap_folders(imap);
		imap_command(imap, ICOM_LOGOUT, NULL);
		imap_disconnect(imap);
	}

	return num;
}

int check_imap_folder(struct _mail_folder *folder) {
	struct _mail_folder *ofold;
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

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

	if((ofold = imap_folder_switch(imap, folder)) == NULL) {
		display_msg(MSG_WARN, "IMAP", "Folder can not be selected");
		return -1;
	}

	if(imap_command(imap, ICOM_UIDFETCH, "%ld:*", get_max_uid(folder)) !=
	   IMAP_OK) {
		display_msg(MSG_WARN, "IMAP", "Can not check folder");
		imap_folder_switch(imap, ofold);
		return -1;
	}

	imap_folder_switch(imap, ofold);

	return 0;
}

char *skip_plist(struct _imap_src *imap, char *ptr) {
	char *p, *p1, c;
	static int nlevel = 0;
	const char *delim;

	nlevel++;

	if(ptr == NULL) {
		nlevel--;
		return NULL;
	}

	if(nlevel > MAX_PLIST_NESTING) {
		display_msg(MSG_WARN, "IMAP-PARSE", "Nesting level too high");
		nlevel--;
		return NULL;
	}

	if (imap->flags & ISRC_SQUOTES)
		delim = "\"'()";
	else
		delim = "\"()";

	p = ptr;
	while(*p != '\0') {
		p1 = p + strlen(p);
		if((p = strpbrk(p, delim)) == NULL) {
			nlevel--;
			return p1;
		}

		c = *p;
		switch(c) {
			case '"':
			case '\'':
				p++;
				if((p = strchr(p, c)) == NULL) {
					display_msg(MSG_WARN, "IMAP-PARSE", "Unterminated string");
					nlevel--;
					return p1;
				}
				p++;
				break;

			case '(':
				p++;
				if((p = skip_plist(imap, p)) == NULL) {
					nlevel--;
					return p;
				}
				if((nlevel > 1) && (*p == ')') && *p)
					p++;
				break;

			case ')':
				nlevel--;
				return p;
				break;
		}
	}

	nlevel--;
	return p;
}

char *plist_getnext(struct _imap_src *imap, char *list, char **next) {
	char *p1, c, *ln;
	int len, res;
	long llen;

	if(list)
		imap->plist = list;
	else if(!imap->plist || !*imap->plist) {
		*next = NULL;
		imap->plist = NULL;
		return NULL;
	}

	if(imap->elem)
		free(imap->elem);
	imap->elem = NULL;

	while(*imap->plist == ' ')
		imap->plist++;

	while(*imap->plist != '\0') {
		c = *imap->plist;
		switch(c) {
			case '"':
			case '\'':
				imap->plist++;
				/* Find end of string. Watch out for escaped quotes within */
				for ( p1 = imap->plist; *p1 != c; p1++ ) {
					if ( *p1 == '\0' ) {
						p1 = NULL;
						break;
					}
					
					if ( *p1 == '\\' )
						p1++;
				}
				
				if(p1 == NULL) {
					display_msg(MSG_WARN, "IMAP - PARSE",
								"Unterminated string");
					return NULL;
				}

				len = p1 - imap->plist;
				if((imap->elem = (char *) malloc(len + 1)) == NULL) {
					display_msg(MSG_FATAL, "IMAP - PARSE", "Malloc failed");
					return NULL;
				}

				strncpy(imap->elem, imap->plist, len);
				imap->elem[len] = '\0';
				p1++;
				imap->plist = p1;
				while(*imap->plist == ' ')
					imap->plist++;
				*next = imap->plist;

				return imap->elem;
				break;

			case '(':
				if((p1 = skip_plist(imap, imap->plist)) == NULL) {
					display_msg(MSG_WARN, "IMAP - PARSE", "Unterminated list");
					imap->plist = NULL;
					*next = NULL;
					return NULL;
				}

				if(*p1)
					p1++;
				else {
					p1--;
					if(*p1 == '}') {
						p1--;
						while((p1 != imap->plist) && isdigit(*p1))
							p1--;
						if((llen = is_literal(imap, p1)) != -1) {
							if(llen >= MAX_IMAP_STRING) {
								display_msg(MSG_WARN, "IMAP",
											"unexpected literal data too big");
								return NULL;
							}
							len = imap->plist - imap->response;
							imap->response =
							(char *) realloc(imap->response,
											 strlen(imap->response) +
											 llen + 2 - strlen(p1));
							if(imap->response == NULL) {
								display_msg(MSG_FATAL, "IMAP",
											"Realloc failed");
								return NULL;
							}
							p1 = strrchr(imap->response, '{');
#if defined(SASL) && defined(PROT_STDIO)
							if((res = imap_getdata(p1, llen, imap->pin, NULL))
							   < 0)
#else
							if((res = getdata(p1, llen, imap->imap_in, NULL))
							   < 0)
#endif
							{
								if(res == -1) {
									display_msg(MSG_WARN, "IMAP",
												"Can not receive string");
									imap_close(imap, 0);
								} else if(res == -2)
									imap_reconnect(imap);
								return NULL;
							}
							strcat(imap->response, " ");

#if defined(SASL) && defined(PROT_STDIO)
							if((ln = imap_getline(NULL, -65535, imap->pin)) ==
							   NULL)
#else
							if((ln = getline(NULL, -65535, imap->imap_in)) ==
							   NULL)
#endif
							{
								display_msg(MSG_WARN, "IMAP",
											"Incomplete response");
								imap_close(imap, 0);
								return NULL;
							}

							if(!*ln) {
								imap_reconnect(imap);
								return NULL;
							}

							imap->response =
							(char *) realloc(imap->response,
											 strlen(imap->response) +
											 strlen(ln) + 1);
							if(imap->response == NULL) {
								display_msg(MSG_FATAL, "IMAP",
											"Realloc failed");
								return NULL;
							}

							strcat(imap->response, ln);
							free(ln);
							imap->plist = imap->response + len;

							continue;
						}
					}
				}

				len = p1 - imap->plist;
				if((imap->elem = (char *) malloc(len + 1)) == NULL) {
					display_msg(MSG_FATAL, "IMAP - PARSE", "Malloc failed");
					return NULL;
				}

				strncpy(imap->elem, imap->plist, len);
				imap->elem[len] = '\0';
				imap->plist = p1;
				while(*imap->plist == ' ')
					imap->plist++;
				*next = imap->plist;

				return imap->elem;
				break;

			case ')':
				imap->plist++;
				while(*imap->plist == ' ')
					imap->plist++;
				*next = imap->plist;
				return NULL;
				break;

			default:
				if((p1 = strpbrk(imap->plist, ") ")) == NULL) {
					imap->elem = strdup(imap->plist);
					imap->plist = NULL;
					*next = NULL;
					return imap->elem;
				}

				len = p1 - imap->plist;
				if((imap->elem = (char *) malloc(len + 1)) == NULL) {
					display_msg(MSG_FATAL, "IMAP - PARSE", "Malloc failed");
					return NULL;
				}

				strncpy(imap->elem, imap->plist, len);
				imap->elem[len] = '\0';
				imap->plist = p1;
				while(*imap->plist == ' ')
					imap->plist++;
				*next = imap->plist;

				return imap->elem;
				break;
		}
		imap->plist++;
	}

	imap->plist = NULL;
	*next = NULL;
	return NULL;
}

int start_plist(struct _imap_src *imap) {
	if(!imap->plist)
		return -1;

	while(*imap->plist == ' ')
		imap->plist++;

	if(*imap->plist == ')') {
		imap->plist++;
		return -1;
	}

	if(!strncasecmp(imap->plist, "NIL", 3)) {
		imap->plist += 3;
		return -1;
	}

	if(*imap->plist != '(') {
		display_msg(MSG_WARN, "IMAP", "Missing parenthized list");
		return -1;
	} else
		imap->plist++;

	return 0;
}

void end_plist(struct _imap_src *imap) {
	if(!imap->plist)
		return;

	while(*imap->plist && (*imap->plist != ')'))
		imap->plist++;

	if(*imap->plist != ')')
		display_msg(MSG_WARN, "IMAP", "Unterminated parenthized list");
	else
		imap->plist++;
}

char *plist_getnext_string(struct _imap_src *imap, char *plist,
						   char **next) {
	char *p, *str;

	if((p = plist_getnext(imap, plist, next)) == NULL)
		return NULL;

	if((str = get_imap_string(imap, p, NULL)) == NULL) {
		free(imap->elem);
		imap->elem = NULL;
		return NULL;
	}

	free(imap->elem);
	imap->elem = NULL;

	return str;
}

void
expand_uid_range(struct _imap_src *imap, struct _mail_folder *folder,
				 struct _mail_msg *msg, int mask1, int mask2,
				 long *uid_bottom, long *uid_top, int status) {
	struct _mail_msg *msg1;
	long bottom, top;
	int check = 3;

	if(status) {
		if((mask1 & MOVED) || (mask1 & COPIED))
			check = 1;
		else if((mask1 & DELETED) || (mask1 & DELPERM))
			check = 2;
	} else
		check = 3;

	bottom = msg->uid;
	top = msg->uid;

	msg1 = msg;
	while(((msg1 = get_smaller_uid(folder, msg1->uid)) != NULL) &&
		  !(msg1->status & LOCKED) && !(msg1->status & MNOTEXISTS)) {
		if((check == 1) && (msg->folder != msg1->folder))
			break;
		else if((check == 2) && (!(msg1->status & mask1) ||
					(msg1->status & mask2)))
			break;
		else if(check == 3) {
			if(((msg1->flags & mask1) != mask1) ||
			   ((msg1->header->flags & mask1) != 0))
				break;
			if(((msg1->flags & mask2) != 0) ||
			   ((msg1->header->flags & mask2) != mask2))
				break;
		}
		bottom = msg1->uid;
	}

	msg1 = msg;
	while(((msg1 = get_larger_uid(folder, msg1->uid)) != NULL) &&
		  !(msg1->status & LOCKED) && !(msg1->status & MNOTEXISTS)) {
		if((check == 1) && (msg->folder != msg1->folder))
			break;
		else if((check == 2) && (!(msg1->status & mask1) ||
					(msg1->status & mask2)))
			break;
		else if(check == 3) {
			if(((msg1->flags & mask1) != mask1) ||
			   ((msg1->header->flags & mask1) != 0))
				break;
			if(((msg1->flags & mask2) != 0) ||
			   ((msg1->header->flags & mask2) != mask2))
				break;
		}
		top = msg1->uid;
	}

	*uid_bottom = bottom;
	*uid_top = top;

	return;
}

long get_imap_folder_uid(struct _mail_folder *folder) {
	struct _imap_src *imap = (struct _imap_src *) folder->spec;

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

	if(folder->uid != -1)
		return folder->uid;

	if(imap->icap & ICAP_STATUS) {
		if(imap_command
		   (imap, ICOM_STATUS, "%s (UIDVALIDITY)",
			imap_string(imap, folder->fold_path)) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "STATUS failed");
			return -1;
		}
	} else {
		if(imap_command
		   (imap, ICOM_EXAMINE, "%s",
			imap_string(imap, folder->fold_path)) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "EXAMINE failed");
			return -1;
		}
	}

	if(folder->uid == -1) {
		display_msg(MSG_WARN, "IMAP", "Can not obtain folder's UID value");
		return -1;
	}

	return folder->uid;
}

#if defined(SASL) && defined(PROT_STDIO)
int imap_check_io_forms(struct protstream *s, int type) {
	#if defined(__linux__) || defined(__EMX__)
	fd_set read_fds, write_fds, except_fds;
	#else
	struct fd_set read_fds, write_fds, except_fds;
	#endif
	int fd, xconn = 0;

	if(!s->fd || s->fd < 0)
		return -1;

	fd = s->fd;

	if((type == 0) && (s->cnt != 0))
		return 0;

	xconn = ConnectionNumber(fl_display);

	fwait:
	FD_ZERO(&read_fds);
	FD_ZERO(&write_fds);

	switch(type) {
		case 0:
			FD_SET(fd, &read_fds);
			break;

		case 1:
			FD_SET(fd, &write_fds);
			break;

		case 2:
			FD_SET(fd, &read_fds);
			FD_SET(fd, &write_fds);
			break;
	}

	FD_ZERO(&except_fds);
	FD_SET(fd, &except_fds);

	FD_SET(xconn, &read_fds);

	#ifdef  HPUX9
	if(select
	   (fd > xconn ? fd + 1 : xconn + 1, (int *) &read_fds,
		(int *) &write_fds, (int *) &except_fds, NULL) < 0)
	#else
	if(select
	   (fd > xconn ? fd + 1 : xconn + 1, &read_fds, &write_fds,
		&except_fds, NULL) < 0)
	#endif
	{
		if(errno == EINTR)
			return 0;

		return -1;
	}


	if(FD_ISSET(fd, &except_fds))
		return -1;

	if(FD_ISSET(fd, &read_fds) || FD_ISSET(fd, &write_fds))
		return 0;

	if(FD_ISSET(xconn, &read_fds)) {
		if(abortpressed())
			return -2;
	}

	goto fwait;

	return 0;
}
#endif

void init_imap_source(struct _retrieve_src *source) {
	struct _imap_src *imap;

	if(source->spec == NULL) {
		source->spec =
		(struct _imap_src *) malloc(sizeof(struct _imap_src));
		imap = (struct _imap_src *) source->spec;
		strcpy(imap->name, source->name);
		strcpy(imap->hostname, "127.0.0.1");
		strcpy(imap->service, "143");
		strcpy(imap->username, user_n);
		*imap->password = '\0';
		strcpy(imap->list, "\"\"");
		imap->flags = ISRC_TRASH;
	} else {
		imap = (struct _imap_src *) source->spec;
		if(imap->imapsock > 0)
			close(imap->imapsock);
		if(imap->imap_in)
			fclose(imap->imap_in);
		if(imap->search_res)
			free(imap->search_res);
		if(imap->response)
			free(imap->response);
		if(imap->elem)
			free(imap->elem);
	}

	imap->imapsock = -1;
	imap->imap_in = NULL;
	imap->imap_out = NULL;
	imap->response = NULL;
	imap->plist = NULL;
	imap->elem = NULL;
	imap->icap = 0;
	imap->icauth = 0;
	imap->istate = 0;
	imap->lflags = 0;
	imap->ifold = NULL;
	imap->iinbox = NULL;
	imap->itrash = NULL;
	imap->svfold = NULL;
	imap->fimap = NULL;
	imap->imsg = NULL;
	imap->search_res = NULL;
	imap->uid = -1;
	imap->conninprogress = 0;
	imap->lastcom = time(NULL);

	return;
}

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

	return;
}

int load_imap_source(struct _retrieve_src *source, FILE * fd) {
	struct _imap_src *imap;
	char buf[255], *p, *p1;

	imap = (struct _imap_src *) source->spec;
	if(!fgets(buf, 255, fd))
		return -1;
	strip_newline(buf);
	if(sscanf(buf, "%s %15s", imap->hostname, imap->service) != 2)
		return -1;

	if(!fgets(buf, 255, fd))
		return -1;
	strip_newline(buf);

	p1 = buf;
	if((p = get_quoted_str(&p1)) == NULL)
		return -1;
	strncpy(imap->username, p, IMAP_MAX_USERNAME_LEN);
	imap->username[IMAP_MAX_USERNAME_LEN] = '\0';

	*imap->password = '\0';
	if((p = get_quoted_str(&p1)) != NULL) {

#ifdef USE_GPASSWD
		if(Config.getInt("use_gpasswd",0) != 0) {
			int ecode = CE_BASE64;
			char * passwd = NULL;
			/* now encrypted passwords are also base_64 encoded so they are
			   text strings */
			base64_decode(NULL, &ecode);	/* initialize decoder */
			passwd  = base64_decode(p, &ecode);
			if (passwd) {
				//cerr << "Decrypting Pass: " << p << endl;
				p = passwd;
				strncpy(imap->password, Passwd.decrypt(p).c_str(), POP_MAX_PASSWD_LEN);
				//cerr << "Decrypted Pass: " << imap->password << endl;
			} else 
				*imap->password = '\0';
		} else {
#endif
			strncpy(imap->password, p, IMAP_MAX_PASSWD_LEN);
			imap->password[IMAP_MAX_PASSWD_LEN] = '\0';

#ifdef USE_GPASSWD
		}
#endif
	}

	if(!fgets(buf, 255, fd))
		return -1;
	strip_newline(buf);
	if(sscanf(buf, "%d", &imap->flags) != 1)
		return -1;

	if(!fgets(imap->list, 127, fd))
		return -1;
	strip_newline(imap->list);

	return 0;
}

int save_imap_source(struct _retrieve_src *source, FILE * fd) {
	struct _imap_src *imap;
#ifdef USE_GPASSWD
	char passwd[4 * ((IMAP_MAX_PASSWD_LEN + 2) / 3) + 1];
	int len, len64 ,len1 = 0, len2 = 0 ;
	char *init = NULL, *body = NULL, *end = NULL;
#else
	char passwd[IMAP_MAX_PASSWD_LEN+1];
#endif

	imap = (struct _imap_src *) source->spec;
	fprintf(fd, "%s %s\n", imap->hostname, imap->service);

	if(strchr(imap->username, ' '))
		fprintf(fd, "\"%s\"", imap->username);
	else
		fprintf(fd, "%s", imap->username);

	if(imap->flags & ISRC_STOREPWD) {
#ifdef USE_GPASSWD
		if(Config.getInt("use_gpasswd",0) != 0) {
			//cerr << "Encrypting Pass: " << imap->password << endl;
			strcpy(passwd, Passwd.encrypt(imap->password).c_str());
			//cerr << "Encrypted Pass: " << passwd << endl;
			/* md5 encryption to a text string */
			len = strlen(passwd);
			len64 = 4 * ((len + 2) / 3) ;
			init = base64_encode(NULL, len64 + 12);
			if (init) 
				body = base64_encode(passwd, len);
			*passwd = '\0';
			if(body) {
				len1 = strlen(body);
				end = base64_encode(NULL, len);	/* terminate encoding */
			}
			if(end) {
				len2 = strlen(end);
				if( len1 + len2 < sizeof(passwd) ) {
					strncpy(passwd, body, len1);
					strncpy(passwd + len1, end, len2);
					*(passwd + len1 + len2) = '\0';
				}
			} 
			//cerr << "Encrypted Pass: " << passwd << endl;
		} else {
#endif
			strcpy(passwd, imap->password);

#ifdef USE_GPASSWD
		}
#endif
		fprintf(fd," %s\n",passwd);
	} else
		fprintf(fd, "\n");
	
	fprintf(fd, "%d\n", imap->flags);
	fprintf(fd, "%s\n", imap->list);
	return 0;
}

void imap_source(struct _retrieve_src *source) {
	init_imap_source(source);
	source->type = RSRC_IMAP;
	source->retrieve = imap_inc;
	source->free = free_imap_source;
	source->init = init_imap_source;
	source->load = load_imap_source;
	source->save = save_imap_source;

	return;
}

void save_imap_folders() {
	struct _imap_src *imap;
	list<struct _retrieve_src>::iterator p;

	for(p = retrieve_srcs.begin(); p != retrieve_srcs.end(); p++) {
		if(p->type != RSRC_IMAP)
			continue;
		imap = (struct _imap_src *) p->spec;
		if(imap_isconnected(imap))
			save_folders_conf(imap->name, F_IMAP);
	}

	return;
}

void imap_close_all(int bygui) {
	struct _imap_src *imap;
	list<struct _retrieve_src>::iterator p;

	for(p = retrieve_srcs.begin(); p != retrieve_srcs.end(); p++) {
		if(p->type != RSRC_IMAP)
			continue;
		imap = (struct _imap_src *) p->spec;
		if(imap_isconnected(imap))
			imap_close(imap, bygui);
	}

	return;
}

int imap_connected() {
	int connections = 0;
	struct _imap_src *imap;
	list<struct _retrieve_src>::iterator p;

	for(p = retrieve_srcs.begin(); p != retrieve_srcs.end(); p++) {
		if(p->type != RSRC_IMAP)
			continue;
		imap = (struct _imap_src *) p->spec;
		if(imap_isconnected(imap))
			connections++;
	}

	return connections;
}

struct _imap_src *get_imap_connection(char *name) {
	struct _imap_src *imap;
	list<struct _retrieve_src>::iterator p;

	for(p = retrieve_srcs.begin(); p != retrieve_srcs.end(); p++) {
		if(p->type != RSRC_IMAP)
			continue;

		if(name && strcasecmp(p->name, name))
			continue;

		imap = (struct _imap_src *) p->spec;
		if(imap_isconnected(imap))
			return imap;
	}

	return NULL;
}

int imap_dummy_open_folder(struct _mail_folder *folder, int flags) {
	struct _imap_src *imap;
	int ofnum = (int)mailbox.size();
	int lcom;

	if((folder->type != F_IMAP) ||
	   !(folder->status & FDUMMY) ||
	   (folder->status & NOINFR) || !folder->spec) return -1;

	imap = (struct _imap_src *) folder->spec;
	if(!imap_isconnected(imap))
		return -1;

	if(folder->status & FTOP) {
		if(imap_list(imap) == -1)
			return -1;
	} else {
		if(folder->hdelim == '\0')
			return -1;

		lcom = (folder->status & FSUBS) ? ICOM_LSUB : ICOM_LIST;
		if(imap_command(imap, lcom, "\"%s%c\" \"*\"",
						folder->fold_path, folder->hdelim) != IMAP_OK) {
			display_msg(MSG_WARN, "IMAP", "Failed to obtain folder list");
			return -1;
		}
	}

	if(ofnum != (int)mailbox.size()) {
		sort_folders();
		return 1;
	}

	return 0;
}
