/* asis.c - asynchronous streaming IMAP synchronizer
 *
 * (C) 2001 Harald Welte <laforge@gnumonks.org>
 *
 * $Id: asis.c,v 1.7 2001/03/29 22:08:25 laforge Exp $
 *
 * Based on ideas and code of isync-0.4 by Michael Elkins <me@mutt.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU 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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#include <pwd.h>

#include "isync.h"
#include "imap.h"

#if 0
#define DEBUGP(format, args...) do { fprintf(stderr, format, ## args); fflush(stderr);} while(0);
#else
#define DEBUGP(format, args...)
#endif

const char *Flags[] = {
	"\\Seen",
	"\\Answered",
	"\\Deleted",
	"\\Flagged",
	"\\Recent",
	"\\Draft"
};

static config_t global;
static config_t *box = NULL;
static SSL *ssl;

static unsigned int MaildirCount = 0;

static unsigned int bytes_sent = 0, bytes_received = 0;

#define exit_error(x, args...) 		\
	do { fprintf(stderr, x, ## args); 	\
	     exit(-1); } while (0);

char *next_arg(char **s)
{
	char *ret;

	if (!s)
		return 0;
	if (!*s)
		return 0;
	while (isspace((unsigned char) **s))
		(*s)++;
	if (!**s) {
		*s = 0;
		return 0;
	}
	ret = *s;
	while (**s && !isspace((unsigned char) **s))
		(*s)++;
	if (**s)
		*(*s)++ = 0;
	if (!**s)
		*s = 0;
	return ret;
}



/* write a buffer stripping all \r bytes */
static int write_strip(int fd, char *buf, size_t len)
{
	size_t start = 0;
	size_t end = 0;

	while (start < len) {
		while (end < len && buf[end] != '\r')
			end++;
		write(fd, buf + start, end - start);
		end++;
		start = end;
	}
	return 0;
}

static config_t *find_box(const char *s)
{
	config_t *p = box;

	for (; p; p = p->next)
		if (!strcmp(s, p->path)
		    || (p->alias && !strcmp(s, p->alias)))
			return p;
	return 0;
}

/***********************************************************************
 * SSL specific routines
 ***********************************************************************/

#define MAX_DEPTH 1

SSL_CTX *SSLContext = 0;

static int init_ssl(config_t *conf)
{
	if (!conf->cert_file) {
		puts("Error, CertificateFile not defined");
		return -1;
	}

	SSL_library_init();
	SSL_load_error_strings();
	SSLContext = SSL_CTX_new(SSLv23_client_method());
	if (access(conf->cert_file, F_OK)) {
		if (errno != ENOENT) {
			perror("access");
			return -1;
		}
		printf("*** CertificateFile doesn't exist!\n");
	} else
	    if (!SSL_CTX_load_verify_locations
		(SSLContext, conf->cert_file, NULL)) {
		printf("Error, SSL_CTX_load_verify_locations: %s\n",
		       ERR_error_string(ERR_get_error(), 0));
		return -1;
	}
	/*
	if (!conf->use_sslv2)
		SSL_CTX_set_options(SSLContext, SSL_OP_NO_SSLv2);
	if (!conf->use_sslv3)
		SSL_CTX_set_options(SSLContext, SSL_OP_NO_SSLv3);
	if (!conf->use_tlsv1)
		SSL_CTX_set_options(SSLContext, SSL_OP_NO_TLSv1);
	*/

	/* we check the result of the verification after SSL_connect() */
	SSL_CTX_set_verify(SSLContext, SSL_VERIFY_NONE, 0);
	return 0;
}

/***********************************************************************
 * IMAP handling routines, interfacing with imap.c
 ***********************************************************************/
static int cap_callback(buf_t * buf, imap_cmd_t * cmd)
{
	char *arg;
	char *p;

	/* FIXME */
	*(buf_cur(buf)) = '\0';

	p = buf->buf;

	while ((arg = next_arg(&p)))
		if (strcmp("NAMESPACE", arg))
			continue;

	while ((arg = next_arg(&p))) {
		if (!strcmp("NAMESPACE", arg))
			cmd->imap->have_namespace = 1;
		else if (!strcmp("AUTH=CRAM-MD5", arg))
			cmd->imap->have_cram = 1;
		else if (!strcmp("STARTTLS", arg))
			cmd->imap->have_starttls = 1;
	}

	return 0;
}

static int expunge_callback(buf_t * buf, imap_cmd_t * cmd)
{
	/* FIXME */
	return 0;
}

static int uid_callback(buf_t * buf, imap_cmd_t * cmd)
{
	char *line;
	char *eol = NULL, *bol;

	list_t *list;

	bol = buf->buf;
	while ((eol = strchr(bol, '\r'))) {
		/* FIXME: strchr return */
		*eol = '\0';

		line = strchr(bol, '(');
		if (!line)
			continue;
		DEBUGP("CMD: '%s'\n", line);
		list = parse_list(line, 0);

		if (parse_fetch(cmd->imap, list)) {
			DEBUGP("error in parse_fetch!\n");
			free_list(list);
			return -1;
		}
		free_list(list);
		bol = (eol + 1);
	}

	sync_mailbox((mailbox_t *) cmd->private.param, cmd->imap, 0,
		     9999999);
	return 0;
}

/* proecess a namespace answer */
static int namespace_callback(buf_t * buf, imap_cmd_t * cmd)
{
	char *arg;
	/* FIXME */
	arg = next_arg(&buf->buf);

	if (strcmp("NAMESPACE", arg)) {
		printf("Invalid response to NAMESPACE\n");
		return -1;
	}
}

/* query the server about available namespaces */
int imap_get_namespace(imap_t * imap)
{
	imap_cmd_t *ns_cmd;

	ns_cmd = imap_cmd_alloc();
	if (!ns_cmd) {
		/* FIXME */
		return -1;
	}

	strcpy(ns_cmd->command, "NAMESPACE");
	ns_cmd->callback = namespace_callback;

	imap_enqueue_command(imap, ns_cmd);

	return 0;
}

/* fetch all UID's for one folder */
int imap_fetch_uids(imap_t *imap, mailbox_t *mail)
{
	imap_cmd_t *fetch_cmd;

	fetch_cmd = imap_cmd_alloc();
	/* FIXME */

	sprintf(fetch_cmd->command, "UID FETCH %d:* (FLAGS RFC822.SIZE)",
		imap->minuid);
	fetch_cmd->callback = uid_callback;
	fetch_cmd->private.param = (void *) mail;

	imap_enqueue_command(imap, fetch_cmd);
}

/* called for server response of SELECT */
static int select_callback(buf_t *buf, imap_cmd_t *cmd)
{
	char *arg, *oldarg = NULL;
	char *p;

	*(buf_cur(buf)) = '\0';
	p = buf->buf;

	/* invalidate uidvalidity */
	cmd->imap->uidvalidity = 0;

	while ((arg = next_arg(&p))) {
		if (!strcmp("[UIDVALIDITY", arg)) {
			arg = next_arg(&p);
			if (!arg) {
				printf("Error during parse of UIDVAILDITY\n");
				return -1;
			}
			cmd->imap->uidvalidity = atol(arg);
		} else if (!strcmp("EXISTS", arg)) {
			cmd->imap->count = atoi(oldarg);
		} else if (!strcmp("RECENT", arg)) {
			cmd->imap->recent = atoi(oldarg);
		}
		oldarg = arg;
	}

	printf("%d messages, %d recent\n", cmd->imap->count, cmd->imap->recent);

	return 0;
}

int imap_select_folder(imap_t *imap, config_t *box)
{
	imap_cmd_t *select_cmd;
	char *ns_prefix;

	select_cmd = imap_cmd_alloc();

	if (box->use_namespace && is_list(imap->ns_personal) &&
	    is_list(imap->ns_personal->child) &&
	    is_atom(imap->ns_personal->child->child)) {
		if (strcmp(box->box, "INBOX")) {
			ns_prefix = imap->ns_personal->child->child->val;

		} else
			ns_prefix = "";
	}

			/* FIXME */
			ns_prefix = "INBOX.";

	sprintf(select_cmd->command, "SELECT %s%s", ns_prefix, box->box);
	select_cmd->callback = select_callback;

	imap_enqueue_command(imap, select_cmd);
}

static int setflag_callback(buf_t * buf, imap_cmd_t * cmd)
{
	/* FIXME */
	return 0;
}

/* change flags. mode 0: add, mode 1: delete */
int imap_change_flags(imap_t * imap, unsigned int uid, unsigned int flags,
		      int mode)
{
	imap_cmd_t *set_cmd;

	char buf[256];
	int i;


	buf[0] = 0;
	for (i = 0; i < D_MAX; i++) {
		if (flags & (1 << i))
			snprintf(buf + strlen(buf),
				 sizeof(buf) - strlen(buf), "%s%s",
				 (buf[0] != 0) ? " " : "", Flags[i]);
	}

	set_cmd = malloc(sizeof(*set_cmd));

	sprintf(set_cmd->command, "UID STORE %d %cFLAGS.SILENT (%s)", uid,
		mode ? '-' : '+', buf);
	set_cmd->callback = setflag_callback;

	printf("%s\n",set_cmd->command);

	imap_enqueue_command(imap, set_cmd);
}

/* called after each server response to fetch_message */
static int fetch_callback(buf_t * buf, imap_cmd_t * cmd)
{
	int fd, flags;
	char *p, *arg, *endptr;
	char suffix[_POSIX_PATH_MAX];
	char path[_POSIX_PATH_MAX];
	char newpath[_POSIX_PATH_MAX];
	int fetched = 0;
	unsigned long bytes;

	char debbuf[1024];

	strncpy(&debbuf, buf->buf, 1023);

	DEBUGP("fetch_callback entered\n");

	flags = cmd->private.fetch.flags;

	*suffix = '\0';
	if (flags)
		snprintf(suffix, sizeof(suffix), ":2,%s%s%s%s",
			 (flags & D_FLAGGED) ? "F" : "",
			 (flags & D_ANSWERED) ? "R" : "",
			 (flags & D_SEEN) ? "S" : "",
			 (flags & D_DELETED) ? "D" : "");


	/* loop until we exclusively own a file */
	for (;;) {
		snprintf(path, sizeof(path), "%s/tmp/%s.%ld_%d.%d.UID%d%s",
			 cmd->private.fetch.mbox_path, Hostname, time(0),
			 MaildirCount++,
			 getpid(), cmd->private.fetch.uid, suffix);

		if ((fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600)) >
		    0) break;
		if (errno != -EEXIST) {
			perror("open");
			break;
		}

		sleep(1);
	}

	if (fd < 0)
		return fd;

	fputs(".", stdout);
	fflush(stdout);
	fetched++;

	arg = buf->buf;
	next_arg(&arg);		/* * */
	next_arg(&arg);		/* <ID> */
	next_arg(&arg);		/* FETCH */

	p = arg;

	while ((arg = next_arg(&p)) && *arg != '{');
	if (!arg) {
		printf("error in getting size: %.100s\n", &debbuf);
		return -1;
	}
	bytes = strtoul(arg + 1, &endptr, 10);

	arg = next_arg(&p);

	if (bytes > (buf_cur(buf) - endptr)) {
		printf("error: message bigger than buffer, truncating: "
			"bytes=%lu, %lu\n", bytes, (buf_cur(buf)-endptr));
		bytes = buf_cur(buf) - endptr;
	}

	/* write the buffer to fd, stripping \r's */
	write_strip(fd, arg, bytes);

	if (close(fd))
		perror("close");
	else {
		/* this is the filename without path */
		p = strrchr(path, '/');

		snprintf(newpath, sizeof(newpath), "%s/%s%s",
			 cmd->private.fetch.mbox_path,
			 (flags & D_SEEN) ? "cur" : "new", p);

		/* its ok if this fails, the next time we sync the message
		 * will get pulled down
		 */
		if (link(path, newpath))
			perror("link");
	}

	unlink(path);

	return 0;
}

/* enqueue one fetch command */
int imap_fetch_message(imap_t *imap, mailbox_t *mbox, message_t *cur)
{
	imap_cmd_t *fetch_cmd;

	DEBUGP("fetch message\n");

	fetch_cmd = imap_cmd_alloc();
	 /*FIXME*/ fetch_cmd->private.fetch.uid = cur->uid;
	fetch_cmd->private.fetch.mbox_path = mbox->path;
	fetch_cmd->private.fetch.flags = cur->flags;

	sprintf(fetch_cmd->command, "UID FETCH %d BODY.PEEK[]", cur->uid);
	fetch_cmd->callback = fetch_callback;

	printf("fetch_message: %s\n", fetch_cmd->command);

	imap_enqueue_command(imap, fetch_cmd);

	return 0;
}

/***********************************************************************
 * CONFIGURATION FILE HANDLING
 ***********************************************************************/
/* set defaults from the global configuration section */
static void config_defaults(config_t * conf)
{
	conf->user = global.user;
	conf->pass = global.pass;
	conf->port = global.port;
	conf->box = global.box;
	conf->host = global.host;
	conf->max_size = global.max_size;
	conf->use_namespace = global.use_namespace;
	conf->require_ssl = global.require_ssl;
	conf->use_imaps = global.use_imaps;
	conf->cert_file = global.cert_file;
	conf->use_sslv2 = global.use_sslv2;
	conf->use_sslv3 = global.use_sslv3;
	conf->use_tlsv1 = global.use_tlsv1;
}


static void load_config(char *where)
{
	char path[_POSIX_PATH_MAX];
	char buf[1024];
	struct passwd *pw;
	config_t **cur = &box;
	int line = 0;
	FILE *fp;
	char *p, *cmd, *val;

	if (!where) {
		pw = getpwuid(getuid());
		snprintf(path, sizeof(path), "%s/.isyncrc", pw->pw_dir);
		where = path;
	}
	printf("Reading %s\n", where);

	fp = fopen(where, "r");
	if (!fp) {
		if (errno != ENOENT) {
			perror("fopen");
			return;
		}
	}
	buf[sizeof buf - 1] = 0;
	while ((fgets(buf, sizeof(buf) - 1, fp))) {
		p = buf;
		cmd = next_arg(&p);
		val = next_arg(&p);
		line++;
		if (!cmd || *cmd == '#')
			continue;
		if (!strncasecmp("mailbox", cmd, 7)) {
			if (*cur)
				cur = &(*cur)->next;
			*cur = calloc(1, sizeof(config_t));
			config_defaults(*cur);
			(*cur)->path = strdup(val);
		} else if (!strncasecmp("host", cmd, 4)) {
			if (!strncasecmp("imaps:", val, 6)) {
				val += 6;
				if (*cur) {
					(*cur)->use_imaps = 1;
					(*cur)->port = 993;
				} else {
					global.use_imaps = 1;
					global.port = 993;
				}
			}
			if (*cur)
				(*cur)->host = strdup(val);
			else
				global.host = strdup(val);
		} else if (!strncasecmp("user", cmd, 4)) {
			if (*cur)
				(*cur)->user = strdup(val);
			else
				global.user = strdup(val);
		} else if (!strncasecmp("pass", cmd, 4)) {
			if (*cur)
				(*cur)->pass = strdup(val);
			else
				global.pass = strdup(val);
		} else if (!strncasecmp("port", cmd, 4)) {
			if (*cur)
				(*cur)->port = atoi(val);
			else
				global.port = atoi(val);
		} else if (!strncasecmp("box", cmd, 3)) {
			if (*cur)
				(*cur)->box = strdup(val);
			else
				global.box = strdup(val);
		} else if (!strncasecmp("alias", cmd, 5)) {
			if (*cur)
				(*cur)->alias = strdup(val);
		} else if (!strncasecmp("maxsize", cmd, 7)) {
			if (*cur)
				(*cur)->max_size = atol(val);
			else
				global.max_size = atol(val);
		} else if (!strncasecmp("UseNamespace", cmd, 12)) {
			if (*cur)

				(*cur)->use_namespace =
				    (strcasecmp(val, "yes") == 0);
			else
				global.use_namespace =
				    (strcasecmp(val, "yes") == 0);
		} else if (!strncasecmp("CertificateFile", cmd, 15)) {
			if (*cur)
				(*cur)->cert_file = strdup(val);
			else
				global.cert_file = strdup(val);
		} else if (!strncasecmp("RequireSSL", cmd, 10)) {
			if (*cur)

				(*cur)->require_ssl =
				    (strcasecmp(val, "yes") == 0);
			else
				global.require_ssl =
				    (strcasecmp(val, "yes") == 0);
		} else if (!strncasecmp("UseSSLv2", cmd, 8)) {
			if (*cur)

				(*cur)->use_sslv2 =
				    (strcasecmp(val, "yes") == 0);
			else
				global.use_sslv2 =
				    (strcasecmp(val, "yes") == 0);
		} else if (!strncasecmp("UseSSLv3", cmd, 8)) {
			if (*cur)

				(*cur)->use_sslv3 =
				    (strcasecmp(val, "yes") == 0);
			else
				global.use_sslv3 =
				    (strcasecmp(val, "yes") == 0);
		} else if (!strncasecmp("UseTLSv1", cmd, 8)) {
			if (*cur)

				(*cur)->use_tlsv1 =
				    (strcasecmp(val, "yes") == 0);
			else
				global.use_tlsv1 =
				    (strcasecmp(val, "yes") == 0);
		} else if (!strncasecmp("RequireCRAM", cmd, 11)) {
			if (*cur)

				(*cur)->require_cram =
				    (strcasecmp(val, "yes") == 0);
			else
				global.require_cram =
				    (strcasecmp(val, "yes") == 0);
		} else if (buf[0])
			printf("%s:%d:unknown command:%s", path, line,
			       cmd);
	}
	fclose(fp);
}

/***********************************************************************
 * MAIN program
 ***********************************************************************/

#define BUF_GRANULARITY 102400


/* main loop for receiving and sending data */
static int main_loop(int sock, SSL *ssl, imap_t *imap)
{
	int r, err;
	buf_t *recvbuf;
	char sndbuf[IMAP_CMD_MAXSIZE];
	fd_set readfds, writefds;
	struct timeval tv;

	recvbuf = buf_alloc(BUF_GRANULARITY);
	if (!recvbuf)
		exit_error("OOM\n");

	DEBUGP("entering main_loop\n");

	while (1) {
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);

		/* we're always interested in what the server wants to tell */
		FD_SET(sock, &readfds);

		/* do we have pending commands to send out? */
		if (imap_commands_pending(imap)) {
			DEBUGP("pending command(s), including in writefds\n");
			FD_SET(sock, &writefds);
		} else {
			/* if we're not waiting for any server replies 
			 * we return */
			if (!imap_replies_pending(imap)) {
				DEBUGP("no pending repl(y|ies), returning\n");
				buf_free(recvbuf);
				return 0;
			}
		}

		tv.tv_sec = 60;
		tv.tv_usec = 0;

		r = select(sock + 1, &readfds, &writefds, 0, &tv);
		DEBUGP("select returned\n");

		if (!r) {
			printf("timeout... returning");
			buf_free(recvbuf);
			return -1;
		}


		if (FD_ISSET(sock, &readfds)) {
			DEBUGP("something to read: ");
			do {
				if (buf_space(recvbuf) < 10) {
					/* enlarge buffer */
					DEBUGP("enlarging buffer\n");
					recvbuf = buf_enlarge(recvbuf,
							      BUF_GRANULARITY);
				}

				r = SSL_read(ssl, buf_cur(recvbuf),
					     buf_space(recvbuf));

				DEBUGP("%d bytes\n", r);

				recvbuf->offset += r;

				/* update statistics */
				bytes_received += r;

				err = SSL_get_error(ssl, r);

				switch (err) {
				case SSL_ERROR_NONE:
					imap_receiver(imap, recvbuf);
					break;
				case SSL_ERROR_ZERO_RETURN:
					/* End of data */
					SSL_shutdown(ssl);
					break;
				case SSL_ERROR_WANT_READ:
					DEBUGP("SSL_ERROR_WANT_READ\n");
					break;
				default:
					/* ssl read problem */
					printf("URGS... ssl read error:%d\n",
						err);
					break;
				}
			} while (SSL_pending(ssl));
		}
		if (FD_ISSET(sock, &writefds)) {
			DEBUGP("socket ready for writing\n");
			/* fetch one command */
			if (!imap_sender(imap, sndbuf)) {
				/* send it, if there was one */
				r = SSL_write(ssl, sndbuf, strlen(sndbuf));
				/* FIXME */
				DEBUGP("SSL_write returned with %d\n", r);

				/* update statistics */
				bytes_sent += strlen(sndbuf);
			}
		}
	}
}

int connect_imapssl(imap_t *imap, config_t *box)
{
	struct sockaddr_in sin;
	struct hostent *he;
	imap_cmd_t *cmd;
	int s, ret;

		/* */
		memset(&sin, 0, sizeof(sin));

		sin.sin_port = htons(box->port);
		sin.sin_family = AF_INET;

		he = gethostbyname(box->host);
		sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]);

		s = socket(PF_INET, SOCK_STREAM, 0);

		if (connect(s, (struct sockaddr *) &sin, sizeof(sin))) {
			exit_error("connect failed\n");
		}

		ret = init_ssl(box);

		ssl = SSL_new(SSLContext);
		SSL_set_fd(ssl, s);
		ret = SSL_connect(ssl);

		if (ret <= 0) {
			ret = SSL_get_error(ssl, ret);
			printf("Error, SSL_connect: %s\n",
			       ERR_error_string(ret, 0));
			exit(1);
		}

		printf("SSL connection established\n");

		cmd = imap_cmd_alloc();
		strcpy(cmd->command, "CAPABILITY");
		cmd->callback = cap_callback;
		imap_enqueue_command(imap, cmd);

		/* authenticate */
		cmd = imap_cmd_alloc();
		sprintf(cmd->command, "LOGIN %s %s", box->user, box->pass);
		cmd->callback = cap_callback;
		imap_enqueue_command(imap, cmd);

		main_loop(s, ssl, imap);

		/* FIXME */
		printf("Authenticated\n");

		if (imap->have_namespace) {
			DEBUGP("Issuing namespace command\n");
			imap_get_namespace(imap);
			main_loop(s, ssl, imap);
		}

		return s;
}

#if HAVE_GETOPT_LONG
#define _GNU_SOURCE
#include <getopt.h>

struct option Opts[] = {
	{"config", 1, NULL, 'c'},
	{"delete", 0, NULL, 'd'},
	{"expunge", 0, NULL, 'e'},
	{"fast", 0, NULL, 'f'},
	{"verbose", 0, NULL, 'v'},
	{"version", 0, NULL, 'V'},
	{0, 0, 0, 0}
};

#endif

static mailbox_t *mail = NULL;
static imap_t *imap = NULL;

static void sigterm_handler(int signal)
{
	printf("sigterm received, exiting\n");
	if (mail)
		maildir_close(mail);

	exit(0);
}

int main(int argc, char *argv[])
{
	int i, s = 0, ret;
	imap_cmd_t *cmd;
	static config_t *curbox, *oldbox = NULL;

	char *config = NULL;
	int delete = 0;
	int expunge = 0;
	int fast = 0;
	int verbose = 0;


	imap = (imap_t *) malloc(sizeof(*imap));
	memset(imap, 0, sizeof(*imap));

#ifdef HAVE_GETOPT_LONG
	while ((i = getopt_long(argc, argv, "c:defvV", Opts, NULL)) != -1) {
#else
	while ((i = getopt(argc, argv, "c:defvV")) != -1) {
#endif
		switch (i) {
		case 'c':
			config = optarg;
			break;
		case 'd':
			delete = 1;
			break;
		case 'e':
			expunge = 1;
			break;
		case 'f':
			fast = 1;
			break;
		case 'v':
			verbose = 1;
			break;
		case 'V':
			printf("%s %s\n", PACKAGE, VERSION);
			break;
		default:
			printf("unknown option!\n");
			break;
		}
	}

	load_config(config);

	signal(SIGTERM, &sigterm_handler);

	for (; argv[optind]; optind++) {
		curbox = find_box(argv[optind]);

		if (!curbox) {
			printf("No such mailbox: %s\n", argv[optind]);
			exit(1);
		}

		if (!curbox->pass) {
			if (global.pass)
				curbox->pass = strdup(global.pass);
			else {
				char *pass = getpass("Password:");

				if (!pass) {
					puts("Aborting, no password");
					exit(1);
				}

				curbox->pass = strdup(pass);

				if (!global.pass)
					global.pass = curbox->pass;
			}
		}

		printf("Reading %s\n", curbox->path);

		/* local maildir stuff */
		mail = maildir_open(curbox->path, fast);
		if (!mail)
			exit_error("Can't open local maildir\n");

		if (fast)
			imap->minuid = mail->maxuid + 1;
		else
			imap->minuid = 1;

		if (!oldbox || strcmp(curbox->host, oldbox->host)
			    || (curbox->port != oldbox->port)
			    || strcmp(curbox->user, oldbox->user)
			    || strcmp(curbox->pass, oldbox->pass)) {

			s = connect_imapssl(imap, curbox);

		}


		printf("selecting remote folder %s\n", curbox->box);
		imap_select_folder(imap, curbox);
		main_loop(s, ssl, imap);

		printf("fetching message list...\n");
		imap_fetch_uids(imap, mail);
		main_loop(s, ssl, imap);

		if (!fast) {
			if (expunge && (imap->deleted || mail->deleted)) {
				/* remove messages marked for deletion */
				printf("Expunging %d messages from server\n",
				     imap->deleted);
				
				cmd = imap_cmd_alloc();
				strcpy(cmd->command, "EXPUNGE");
				cmd->callback = expunge_callback;
				imap_enqueue_command(imap, cmd);
				main_loop(s, ssl, imap);

				printf("Expunging %d messages from local mailbox\n",
				     mail->deleted);
				if (maildir_expunge(mail, 0))
					exit(1);
			}
			/* remove messages deleted from server.  this can
			 * safely be an `else' clause since dead messages are
			 * marked as deleted by sync_mailbox.
			 */
			else if (delete)
				maildir_expunge(mail, 1);

			/* write changed flags back to the mailbox */
			printf("Committing changes to %s\n", mail->path);
			if (maildir_sync(mail))
				exit(1);
		}
		maildir_close(mail);

		/* FIXME: free */
		imap->msgs = NULL;

		oldbox = curbox;

	}
	/* gracefully close connection to the IMAP server */
//      imap_close(imap);

	printf("===> Statistics: %d bytes received, %d bytes sent\n",
		bytes_received, bytes_sent);

	exit(0);

}
