[evolution-data-server] Initial commit - add imapx provider.



commit 32ac97b41e9ce755ed496a4f5caf208f6ed6ef46
Author: Chenthill Palanisamy <pchenthill novell com>
Date:   Tue Sep 29 11:38:19 2009 +0530

    Initial commit - add imapx provider.

 camel/providers/Makefile.am                       |    2 +-
 camel/providers/imapx/ChangeLog                   |   77 +
 camel/providers/imapx/Makefile.am                 |   40 +
 camel/providers/imapx/camel-imapx-exception.c     |   80 +
 camel/providers/imapx/camel-imapx-exception.h     |   35 +
 camel/providers/imapx/camel-imapx-folder.c        |  263 ++
 camel/providers/imapx/camel-imapx-folder.h        |   60 +
 camel/providers/imapx/camel-imapx-provider.c      |  102 +
 camel/providers/imapx/camel-imapx-server.c        | 2841 +++++++++++++++++++++
 camel/providers/imapx/camel-imapx-server.h        |  105 +
 camel/providers/imapx/camel-imapx-store-summary.c |  624 +++++
 camel/providers/imapx/camel-imapx-store-summary.h |   96 +
 camel/providers/imapx/camel-imapx-store.c         |  873 +++++++
 camel/providers/imapx/camel-imapx-store.h         |   77 +
 camel/providers/imapx/camel-imapx-stream.c        |  728 ++++++
 camel/providers/imapx/camel-imapx-stream.h        |   95 +
 camel/providers/imapx/camel-imapx-summary.c       |  489 ++++
 camel/providers/imapx/camel-imapx-summary.h       |   82 +
 camel/providers/imapx/camel-imapx-tokenise.h      |  175 ++
 camel/providers/imapx/camel-imapx-tokens.txt      |   35 +
 camel/providers/imapx/camel-imapx-utils.c         | 1384 ++++++++++
 camel/providers/imapx/camel-imapx-utils.h         |  189 ++
 camel/providers/imapx/camel-imapx-view-summary.c  |  163 ++
 camel/providers/imapx/camel-imapx-view-summary.h  |   63 +
 camel/providers/imapx/libcamelimapx.urls          |    1 +
 configure.ac                                      |    1 +
 26 files changed, 8679 insertions(+), 1 deletions(-)
---
diff --git a/camel/providers/Makefile.am b/camel/providers/Makefile.am
index f392074..ed4c665 100644
--- a/camel/providers/Makefile.am
+++ b/camel/providers/Makefile.am
@@ -21,6 +21,6 @@ else
 SENDMAIL_DIR=sendmail
 endif
 
-SUBDIRS = pop3 $(SENDMAIL_DIR) smtp imap $(IMAP4_DIR) $(NNTP_DIR) local $(IMAPP_DIR) groupwise $(HULA_DIR)
+SUBDIRS = pop3 $(SENDMAIL_DIR) smtp imap imapx $(IMAP4_DIR) $(NNTP_DIR) local $(IMAPP_DIR) groupwise $(HULA_DIR)
 
 
diff --git a/camel/providers/imapx/ChangeLog b/camel/providers/imapx/ChangeLog
new file mode 100644
index 0000000..53b5c6e
--- /dev/null
+++ b/camel/providers/imapx/ChangeLog
@@ -0,0 +1,77 @@
+2005-09-08    <NotZed Ximian com>
+
+	* camel-imapx-utils.c: use the messageinfo recent flag, not the
+	imapx specific one.
+
+2005-09-07    <NotZed Ximian com>
+
+	* camel-imapx-server.c (camel_imapx_server_sync_changes): init
+	on/off_user.
+
+	* camel-imapx-utils.c (imap_dump_fetch): remove bad stream reset
+	call.
+	(imap_parse_flags): use the right flag set func/fix warning.
+
+2005-09-06    <NotZed Ximian com>
+
+	* camel-imapx-utils.c (imap_free_fetch): fix flag_list_clear call.
+
+2005-08-22    <NotZed Ximian com>
+
+	* camel-imapx-utils.h: 
+	* camel-imapx-server.c: fix some compilation issues.
+	
+2005-08-22    <NotZed Ximian com>
+
+	* camel-imapx-server.c (imapx_command_addv): implement proper
+	string escaping.
+	(imapx_job_sync_changes_start): implement proper user-flag storage.
+
+	* camel-imapx-utils.c: move type functions here.
+
+	* camel-imapx-view-summary.c: implement new view-summary stuff.
+
+2005-06-27    <NotZed Ximian com>
+
+	* camel-imapx-server.c: Fixed some problems with multi-folder
+	support.
+
+	* camel-imapx-store.c: Simple first-cut of listing code.
+
+2005-06-23  Not Zed  <NotZed Ximian com>
+
+	* camel-imapx-server.c (imapx_command_addv): added support for raw
+	filename literals, sends content directly.
+	(imapx_command_start_next): support no-select commands, like append.
+
+	* camel-imapx-summary.c (imapx_uid_cmp): modified for temporary uids.
+
+	* camel-imapx-server.c (camel_imapx_server_expunge): added.
+
+	* camel-imapx-folder.c (imap_sync): implement.
+
+	* camel-imapx-server.c (imapx_expunged): add code to support
+	expunge response.
+
+2004-11-11  Not Zed  <NotZed Ximian com>
+
+	* updates for camel folder summary api changes.
+
+2004-09-30  Not Zed  <NotZed Ximian com>
+
+	* camel-imapp-engine.c: make the build again, warnings, doesn't
+	work.
+
+2004-09-28  Not Zed  <NotZed Ximian com>
+
+	* camel-imapp-engine.c (camel_imapp_engine_command_free): assume
+	the command isn't in a list now.
+	(cie_worker): worker thread code.
+
+	* camel-imapp-engine.h: make the imappcommand a
+	message.
+
+2004-09-28  Not Zed  <NotZed Ximian com>
+
+	* added new changelog.
+
diff --git a/camel/providers/imapx/Makefile.am b/camel/providers/imapx/Makefile.am
new file mode 100644
index 0000000..a95f896
--- /dev/null
+++ b/camel/providers/imapx/Makefile.am
@@ -0,0 +1,40 @@
+## Process this file with automake to produce Makefile.in
+
+camel_provider_LTLIBRARIES = libcamelimapx.la
+camel_provider_DATA = libcamelimapx.urls
+
+INCLUDES = -I.. \
+	-I$(srcdir)/..				\
+	-I$(top_srcdir)/camel			\
+	-I$(top_srcdir)/intl			\
+	-I$(top_srcdir)/e-util			\
+	-I$(top_srcdir)				\
+	$(CAMEL_CFLAGS)				\
+	-DG_LOG_DOMAIN=\"camel-imapx\"
+
+libcamelimapx_la_SOURCES =			\
+	camel-imapx-exception.c			\
+	camel-imapx-provider.c			\
+	camel-imapx-store-summary.c		\
+	camel-imapx-summary.c			\
+	camel-imapx-store.c			\
+	camel-imapx-folder.c			\
+	camel-imapx-server.c			\
+	camel-imapx-stream.c			
+
+noinst_HEADERS =				\
+	camel-imapx-exception.h			\
+	camel-imapx-stream.h			\
+	camel-imapx-store-summary.h		\
+	camel-imapx-summary.h			\
+	camel-imapx-folder.h			\
+	camel-imapx-store.h			\
+	camel-imapx-server.h			\
+	camel-imapx-utils.h
+
+camel-imapx-tokenise.h: camel-imapx-tokens.txt
+	gperf -H imap_hash -N imap_tokenise_struct -L ANSI-C -o -t -k1,$$ $< > $@
+
+libcamelimapx_la_LDFLAGS = -avoid-version -module
+
+EXTRA_DIST = libcamelimapx.urls
diff --git a/camel/providers/imapx/camel-imapx-exception.c b/camel/providers/imapx/camel-imapx-exception.c
new file mode 100644
index 0000000..95add85
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-exception.c
@@ -0,0 +1,80 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <glib.h>
+
+#include "camel-imapx-exception.h"
+
+#include <pthread.h>
+
+static pthread_key_t handler_key = 0;
+
+void camel_exception_setup(void)
+{
+	pthread_key_create(&handler_key, NULL);
+}
+
+void
+camel_exception_try(struct _CamelExceptionEnv *env)
+{
+	struct _CamelExceptionEnv *handler;
+
+	handler = pthread_getspecific(handler_key);
+	env->parent = handler;
+	handler = env;
+	env->ex = NULL;
+
+	pthread_setspecific(handler_key, handler);
+}
+
+void
+camel_exception_throw_ex(CamelException *ex)
+{
+	struct _CamelExceptionEnv *env;
+
+	printf("throwing exception '%s'\n", ex->desc);
+
+	env = pthread_getspecific(handler_key);
+	if (env != NULL) {
+		env->ex = ex;
+		pthread_setspecific(handler_key, env->parent);
+		longjmp(env->env, ex->id);
+	} else {
+		fprintf(stderr, "\nUncaught exception: %s\n", ex->desc);
+		/* we just crash and burn, this is a code problem */
+		/* we dont use g_assert_not_reached() since its not a noreturn function */
+		abort();
+	}
+}
+
+void
+camel_exception_throw(int id, char *fmt, ...)
+{
+	CamelException *ex;
+	va_list ap;
+
+	ex = camel_exception_new();
+	ex->id = id;
+	va_start(ap, fmt);
+	ex->desc = g_strdup_vprintf(fmt, ap);
+	va_end(ap);
+
+	camel_exception_throw_ex(ex);
+}
+
+void
+camel_exception_drop(struct _CamelExceptionEnv *env)
+{
+	pthread_setspecific(handler_key, env->parent);
+}
+
+void
+camel_exception_done(struct _CamelExceptionEnv *env)
+{
+	pthread_setspecific(handler_key, env->parent);
+	if (env->ex != NULL) {
+		camel_exception_free(env->ex);
+	}
+}
diff --git a/camel/providers/imapx/camel-imapx-exception.h b/camel/providers/imapx/camel-imapx-exception.h
new file mode 100644
index 0000000..4132044
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-exception.h
@@ -0,0 +1,35 @@
+
+/* This implements 'real' exceptions that work a bit like c++/java exceptions */
+
+/* Still experimental code */
+
+#ifndef __CAMEL_IMAPX_EXCEPTION_H
+#define __CAMEL_IMAPX_EXCEPTION_H
+
+#include <setjmp.h>
+#include "camel/camel-exception.h"
+
+struct _CamelExceptionEnv {
+	struct _CamelExceptionEnv *parent;
+	CamelException *ex;
+	jmp_buf env;
+};
+
+#define CAMEL_TRY { struct _CamelExceptionEnv __env; camel_exception_try(&__env); if (setjmp(__env.env) == 0)
+#define CAMEL_IGNORE camel_exception_done(&__env); }
+#define CAMEL_CATCH(x) { CamelException *x; x=__env.ex; if (x != NULL)
+#define CAMEL_DONE } camel_exception_done(&__env); }
+#define CAMEL_DROP() camel_exception_drop(&__env)
+
+void camel_exception_setup(void);
+
+/* internal functions, use macro's above */
+void camel_exception_try(struct _CamelExceptionEnv *env);
+void camel_exception_done(struct _CamelExceptionEnv *env);
+void camel_exception_drop(struct _CamelExceptionEnv *env);
+
+/* user functions */
+void camel_exception_throw_ex(CamelException *ex) __attribute__ ((noreturn));
+void camel_exception_throw(int id, char *fmt, ...) __attribute__ ((noreturn));
+
+#endif
diff --git a/camel/providers/imapx/camel-imapx-folder.c b/camel/providers/imapx/camel-imapx-folder.c
new file mode 100644
index 0000000..dbd7cb3
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-folder.c
@@ -0,0 +1,263 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-imap-folder.c : class for a imap folder */
+
+/* 
+ * Authors: Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of version 2 of the GNU General Public 
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include "camel/camel-exception.h"
+#include "camel/camel-stream-mem.h"
+#include "camel/camel-stream-filter.h"
+#include "camel/camel-mime-message.h"
+#include "camel/camel-operation.h"
+#include "camel/camel-data-cache.h"
+#include "camel/camel-session.h"
+#include "camel/camel-file-utils.h"
+
+#include "camel-imapx-store.h"
+#include "camel-imapx-folder.h"
+#include "camel-imapx-summary.h"
+#include "camel-imapx-exception.h"
+#include "camel-imapx-server.h"
+
+#include <libedataserver/md5-utils.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#define d(x)
+
+#define CF_CLASS(o) (CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(o)))
+static CamelFolderClass *parent_class;
+
+CamelFolder *
+camel_imapx_folder_new(CamelStore *store, const char *path, const char *raw)
+{
+	CamelFolder *folder;
+
+	d(printf("opening imap folder '%s'\n", path));
+	
+	folder = CAMEL_FOLDER (camel_object_new (CAMEL_IMAPX_FOLDER_TYPE));
+	camel_folder_construct(folder, store, path, path);
+
+	((CamelIMAPXFolder *)folder)->raw_name = g_strdup(raw);
+
+	folder->summary = camel_imapx_summary_new(folder, raw);
+
+	return folder;
+}
+
+#if 0
+/* experimental interfaces */
+void
+camel_imapx_folder_open(CamelIMAPXFolder *folder, CamelException *ex)
+{
+	/* */
+}
+
+void
+camel_imapx_folder_delete(CamelIMAPXFolder *folder, CamelException *ex)
+{
+}
+
+void
+camel_imapx_folder_rename(CamelIMAPXFolder *folder, const char *new, CamelException *ex)
+{
+}
+
+void
+camel_imapx_folder_close(CamelIMAPXFolder *folder, CamelException *ex)
+{
+}
+#endif
+
+static void 
+imapx_refresh_info (CamelFolder *folder, CamelException *ex)
+{
+	CamelIMAPXStore *is = (CamelIMAPXStore *)folder->parent_store;
+
+	if (is->server)
+		camel_imapx_server_refresh_info(is->server, folder, ex);
+
+}
+
+static void
+imapx_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
+{
+	CamelIMAPXStore *is = (CamelIMAPXStore *)folder->parent_store;
+
+	/* Sync twice - make sure deleted flags are written out,
+	   then sync again incase expunge changed anything */
+	camel_exception_clear(ex);
+
+	if (is->server && expunge) {
+		camel_imapx_server_expunge(is->server, folder, ex);
+		camel_exception_clear(ex);
+	}
+
+}
+
+static CamelMimeMessage *
+imapx_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
+{
+	CamelMimeMessage *msg = NULL;
+	CamelStream *stream;
+	CamelIMAPXStore *is = (CamelIMAPXStore *)folder->parent_store;
+
+	if (is->server) {
+		stream = camel_imapx_server_get_message(is->server, folder, uid, ex);
+		if (stream) {
+			msg = camel_mime_message_new();
+			if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)msg, stream) == -1) {
+				camel_exception_setv(ex, 1, "error building message?");
+				camel_object_unref(msg);
+				msg = NULL;
+			}
+			camel_object_unref(stream);
+		}
+	} else {
+		camel_exception_setv(ex, 1, "not ready");
+	}
+
+	return msg;
+}
+
+static void
+imapx_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, char **appended_uid, CamelException *ex)
+{
+	CamelIMAPXStore *is = (CamelIMAPXStore *)folder->parent_store;
+
+	if (appended_uid)
+		*appended_uid = NULL;
+
+	camel_imapx_server_append_message(is->server, folder, message, info, ex);
+}
+
+/* Algorithm for selecting a folder:
+
+  - If uidvalidity == old uidvalidity
+    and exsists == old exists
+    and recent == old recent
+    and unseen == old unseen
+    Assume our summary is correct
+  for each summary item
+    mark the summary item as 'old/not updated'
+  rof
+  fetch flags from 1:*
+  for each fetch response
+    info = summary[index]
+    if info.uid != uid
+      info = summary_by_uid[uid]
+    fi
+    if info == NULL
+      create new info @ index
+    fi
+    if got.flags
+      update flags
+    fi
+    if got.header
+      update based on header
+      mark as retrieved
+    else if got.body
+      update based on imap body
+      mark as retrieved
+    fi
+
+  Async fetch response:
+    info = summary[index]
+    if info == null
+       if uid == null
+          force resync/select?
+       info = empty @ index
+    else if uid && info.uid != uid
+       force a resync?
+       return
+    fi
+
+    if got.flags {
+      info.flags = flags
+    }
+    if got.header {
+      info.init(header)
+      info.empty = false
+    }
+
+info.state - 2 bit field in flags
+   0 = empty, nothing set
+   1 = uid & flags set
+   2 = update required
+   3 = up to date
+*/
+
+static void
+imap_folder_class_init (CamelIMAPXFolderClass *klass)
+{
+	((CamelFolderClass *)klass)->refresh_info = imapx_refresh_info;
+	((CamelFolderClass *)klass)->sync = imapx_sync;
+	
+	((CamelFolderClass *)klass)->get_message = imapx_get_message;
+	((CamelFolderClass *)klass)->append_message = imapx_append_message;
+}
+
+static void
+imap_folder_init(CamelObject *o, CamelObjectClass *klass)
+{
+	CamelFolder *folder = (CamelFolder *)o;
+	CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *)o;
+
+	folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
+				 CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
+	
+	folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
+		CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_DRAFT |
+		CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_USER;
+
+}
+
+static void
+imap_finalise(CamelObject *object)
+{
+	CamelIMAPXFolder *folder = (CamelIMAPXFolder *)object;
+
+}
+
+CamelType
+camel_imapx_folder_get_type (void)
+{
+	static CamelType camel_imapx_folder_type = CAMEL_INVALID_TYPE;
+	
+	if (!camel_imapx_folder_type) {
+		camel_imapx_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelIMAPXFolder",
+							      sizeof (CamelIMAPXFolder),
+							      sizeof (CamelIMAPXFolderClass),
+							      (CamelObjectClassInitFunc)imap_folder_class_init,
+							      NULL,
+							      imap_folder_init,
+							      (CamelObjectFinalizeFunc)imap_finalise);
+		parent_class = (CamelFolderClass *)camel_folder_get_type();
+	}
+	
+	return camel_imapx_folder_type;
+}
diff --git a/camel/providers/imapx/camel-imapx-folder.h b/camel/providers/imapx/camel-imapx-folder.h
new file mode 100644
index 0000000..1ed3a89
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-folder.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-imap-folder.h : Class for a IMAP folder */
+
+/* 
+ * Authors: Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of version 2 of the GNU General Public 
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifndef CAMEL_IMAPX_FOLDER_H
+#define CAMEL_IMAPX_FOLDER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <camel/camel-folder.h>
+
+#define CAMEL_IMAPX_FOLDER_TYPE     (camel_imapx_folder_get_type ())
+#define CAMEL_IMAPX_FOLDER(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_IMAPP_FOLDER_TYPE, CamelIMAPPFolder))
+#define CAMEL_IMAPX_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAPP_FOLDER_TYPE, CamelIMAPPFolderClass))
+#define CAMEL_IS_IMAP_FOLDER(o)    (CAMEL_CHECK_TYPE((o), CAMEL_IMAPX_FOLDER_TYPE))
+
+typedef struct _CamelIMAPXFolder {
+	CamelFolder parent_object;
+
+	char *raw_name;
+//	CamelChangeInfo *changes;
+} CamelIMAPXFolder;
+
+typedef struct _CamelIMAPXFolderClass {
+	CamelFolderClass parent_class;
+} CamelIMAPXFolderClass;
+
+/* Standard Camel function */
+CamelType camel_imapx_folder_get_type (void);
+
+/* public methods */
+CamelFolder *camel_imapx_folder_new(CamelStore *parent, const char *path, const char *raw);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_IMAPX_FOLDER_H */
diff --git a/camel/providers/imapx/camel-imapx-provider.c b/camel/providers/imapx/camel-imapx-provider.c
new file mode 100644
index 0000000..47aa041
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-provider.c
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-pop3-provider.c: pop3 provider registration code */
+
+/* 
+ * Authors :
+ *   Dan Winship <danw ximian com>
+ *   Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of version 2 of the GNU General Public 
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "camel/camel-provider.h"
+#include "camel/camel-session.h"
+#include "camel/camel-url.h"
+#include "camel/camel-sasl.h"
+#include "camel/camel-i18n.h"
+
+#include "camel-imapx-store.h"
+
+CamelProviderConfEntry imapx_conf_entries[] = {
+	{ CAMEL_PROVIDER_CONF_SECTION_START, "storage", NULL,
+	  N_("Message storage") },
+	{ CAMEL_PROVIDER_CONF_SECTION_END },
+	{ CAMEL_PROVIDER_CONF_END }
+};
+
+static CamelProvider imapx_provider = {
+	"imapx",
+	
+	N_("IMAP+"),
+	
+	N_("Experimental IMAP 4(.1) client\n"
+	   "This is untested and unsupported code, you want to use plain imap instead.\n\n"
+	   " !!! DO NOT USE THIS FOR PRODUCTION EMAIL  !!!\n"),
+	"mail",
+	
+	CAMEL_PROVIDER_IS_REMOTE | CAMEL_PROVIDER_IS_SOURCE |
+	CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_SUPPORTS_SSL,
+	
+	CAMEL_URL_NEED_USER | CAMEL_URL_NEED_HOST | CAMEL_URL_ALLOW_AUTH,
+	
+	imapx_conf_entries,
+	
+	/* ... */
+};
+
+CamelServiceAuthType camel_imapx_password_authtype = {
+	N_("Password"),
+	
+	N_("This option will connect to the IMAP server using a "
+	   "plaintext password."),
+	
+	"",
+	TRUE
+};
+
+void camel_imapx_module_init(void);
+
+extern void camel_exception_setup(void);
+extern void imapx_utils_init(void);
+
+void
+camel_imapx_module_init(void)
+{
+	imapx_provider.object_types[CAMEL_PROVIDER_STORE] = camel_imapx_store_get_type();
+	imapx_provider.url_hash = camel_url_hash;
+	imapx_provider.url_equal = camel_url_equal;
+
+	imapx_provider.authtypes = g_list_prepend(imapx_provider.authtypes, camel_sasl_authtype_list(FALSE));
+	imapx_provider.authtypes = g_list_prepend(imapx_provider.authtypes, &camel_imapx_password_authtype);
+
+	/* blah ... could just use it in object setup? */
+	/* TEMPORARY */
+	camel_exception_setup();
+	imapx_utils_init();
+
+	camel_provider_register(&imapx_provider);
+}
+
+void
+camel_provider_module_init(void)
+{
+	camel_imapx_module_init();
+}
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
new file mode 100644
index 0000000..6ba3ff5
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -0,0 +1,2841 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+
+// fixme, use own type funcs
+#include <ctype.h>
+
+#ifdef HAVE_NSS
+#include <nspr.h>
+#include <prio.h>
+#include <prerror.h>
+#include <prerr.h>
+#endif
+
+#include <camel/camel-object.h>
+#include <libedataserver/e-msgport.h>
+#include <camel/camel-url.h>
+#include <camel/camel-session.h>
+#include <camel/camel-stream-fs.h>
+#include <camel/camel-stream-null.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter-canon.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-net-utils.h>
+#include <camel/camel-tcp-stream-ssl.h>
+#include <camel/camel-tcp-stream-raw.h>
+
+#include <camel/camel-sasl.h>
+#include <camel/camel-i18n.h>
+#include <camel/camel-file-utils.h>
+
+#include "camel-imapx-utils.h"
+#include "camel-imapx-exception.h"
+#include "camel-imapx-stream.h"
+#include "camel-imapx-server.h"
+#include "camel-imapx-folder.h"
+#include "camel-imapx-store.h"
+#include "camel-imapx-summary.h"
+
+#define c(x) 
+#define e(x) 
+
+#define CFS_CLASS(x) ((CamelFolderSummaryClass *)((CamelObject *)x)->klass)
+
+#define CIF(x) ((CamelIMAPXFolder *)x)
+
+#define QUEUE_LOCK(x) (g_mutex_lock((x)->queue_lock))
+#define QUEUE_UNLOCK(x) (g_mutex_unlock((x)->queue_lock))
+
+/* All comms with server go here */
+
+
+/* Try pipelining fetch requests, 'in bits' */
+#define MULTI_FETCH
+#define MULTI_SIZE (8196)
+
+/* How many outstanding commands do we allow before we just queue them? */
+#define MAX_COMMANDS (10)
+
+struct _uidset_state {
+	struct _CamelIMAPXEngine *ie;
+	int entries, uids;
+	int total, limit;
+	guint32 start;
+	guint32 last;
+};
+
+struct _CamelIMAPXCommand;
+void imapx_uidset_init(struct _uidset_state *ss, int total, int limit);
+int imapx_uidset_done(struct _uidset_state *ss, struct _CamelIMAPXCommand *ic);
+int imapx_uidset_add(struct _uidset_state *ss, struct _CamelIMAPXCommand *ic, const char *uid);
+
+
+typedef struct _CamelIMAPXCommandPart CamelIMAPXCommandPart;
+typedef struct _CamelIMAPXCommand CamelIMAPXCommand;
+
+typedef enum {
+	CAMEL_IMAPX_COMMAND_SIMPLE = 0,
+	CAMEL_IMAPX_COMMAND_DATAWRAPPER,
+	CAMEL_IMAPX_COMMAND_STREAM,
+	CAMEL_IMAPX_COMMAND_AUTH,
+	CAMEL_IMAPX_COMMAND_FILE,
+	CAMEL_IMAPX_COMMAND_STRING,
+	CAMEL_IMAPX_COMMAND_MASK = 0xff,
+	CAMEL_IMAPX_COMMAND_CONTINUATION = 0x8000 /* does this command expect continuation? */
+} camel_imapx_command_part_t;
+
+struct _CamelIMAPXCommandPart {
+	struct _CamelIMAPXCommandPart *next;
+	struct _CamelIMAPXCommandPart *prev;
+
+	struct _CamelIMAPXCommand *parent;
+
+	int data_size;
+	char *data;
+
+	camel_imapx_command_part_t type;
+
+	int ob_size;
+	void *ob;
+};
+
+typedef int (*CamelIMAPXEngineFunc)(struct _CamelIMAPXServer *engine, guint32 id, void *data);
+typedef void (*CamelIMAPXCommandFunc)(struct _CamelIMAPXServer *engine, struct _CamelIMAPXCommand *);
+
+struct _CamelIMAPXCommand {
+	struct _CamelIMAPXCommand *next, *prev;
+
+	char pri;
+
+	const char *name;	/* command name/type (e.g. FETCH) */
+
+	char *select;		/* folder to select */
+
+	struct _status_info *status; /* status for command, indicates it is complete if != NULL */
+
+	guint32 tag;
+
+	struct _CamelStreamMem *mem;	/* for building the part TOOD: just use a GString? */
+	EDList parts;
+	CamelIMAPXCommandPart *current;
+
+	CamelIMAPXCommandFunc complete;
+	struct _CamelIMAPXJob *job;
+};
+
+CamelIMAPXCommand *camel_imapx_command_new(const char *name, const char *select, const char *fmt, ...);
+void camel_imapx_command_add(CamelIMAPXCommand *ic, const char *fmt, ...);
+void camel_imapx_command_free(CamelIMAPXCommand *ic);
+void camel_imapx_command_close(CamelIMAPXCommand *ic);
+
+/* states for the connection? */
+enum {
+	IMAPX_DISCONNECTED,
+	IMAPX_CONNECTED,
+	IMAPX_AUTHENTICATED,
+	IMAPX_SELECTED
+};
+
+struct _refresh_info {
+	char *uid;
+	guint32 server_flags;
+	CamelFlag *server_user_flags;
+};
+
+enum {
+	IMAPX_JOB_GET_MESSAGE,
+	IMAPX_JOB_APPEND_MESSAGE,
+	IMAPX_JOB_REFRESH_INFO,
+	IMAPX_JOB_SYNC_CHANGES,
+	IMAPX_JOB_EXPUNGE,
+	IMAPX_JOB_LIST,
+};
+
+struct _imapx_flag_change {
+	GPtrArray *infos;
+	char *name;
+};
+
+typedef struct _CamelIMAPXJob CamelIMAPXJob;
+struct _CamelIMAPXJob {
+	EMsg msg;
+
+	CamelException *ex;
+
+	void (*start)(CamelIMAPXServer *is, struct _CamelIMAPXJob *job);
+
+	// ??
+	//CamelOperation *op;
+
+	int noreply:1;		/* dont wait for reply */
+	char type;		/* operation type */
+	char pri;		/* the command priority */
+	short commands;		/* counts how many commands are outstanding */
+
+	CamelFolder *folder;
+
+	union {
+		struct {
+			/* in: uid requested */
+			char *uid;
+			/* in/out: message content stream output */
+			CamelStream *stream;
+			/* working variables */
+			size_t body_offset;
+			ssize_t body_len;
+			size_t fetch_offset;
+		} get_message;
+		struct {
+			/* array of refresh info's */
+			GArray *infos;
+			/* used for biulding uidset stuff */
+			int index;
+			int last_index;
+			struct _uidset_state uidset;
+			/* changes during refresh */
+//			CamelChangeInfo *changes;
+		} refresh_info;
+		struct {
+			GPtrArray *infos;
+			guint32 on_set;
+			guint32 off_set;
+			GArray *on_user; /* imapx_flag_change */
+			GArray *off_user;
+		} sync_changes;
+		struct {
+			char *path;
+			CamelMessageInfo *info;
+		} append_message;
+		struct {
+			char *pattern;
+			guint32 flags;
+			GHashTable *folders;
+		} list;
+	} u;
+};
+
+enum {
+	USE_SSL_NEVER,
+	USE_SSL_ALWAYS,
+	USE_SSL_WHEN_POSSIBLE
+};
+
+#define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
+#define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
+
+
+
+static void imapx_select(CamelIMAPXServer *is, CamelFolder *folder);
+
+
+
+/*
+  this creates a uid (or sequence number) set directly into a command,
+  if total is set, then we break it up into total uids. (i.e. command time)
+  if limit is set, then we break it up into limit entries (i.e. command length)
+*/
+void
+imapx_uidset_init(struct _uidset_state *ss, int total, int limit)
+{
+	ss->uids = 0;
+	ss->entries = 0;
+	ss->start = 0;
+	ss->last = 0;
+	ss->total = total;
+	ss->limit = limit;
+}
+
+int
+imapx_uidset_done(struct _uidset_state *ss, CamelIMAPXCommand *ic)
+{
+	int ret = 0;
+
+	if (ss->last != 0 && ss->last != ss->start) {
+		camel_imapx_command_add(ic, ":%d", ss->last);
+	}
+
+	ret = ss->last != 0;
+
+	ss->start = 0;
+	ss->last = 0;
+	ss->uids = 0;
+	ss->entries = 0;
+
+	return ret;
+}
+
+int
+imapx_uidset_add(struct _uidset_state *ss, CamelIMAPXCommand *ic, const char *uid)
+{
+	guint32 uidn;
+
+	uidn = strtoul(uid, NULL, 10);
+	if (uidn == 0)
+		return -1;
+
+	ss->uids++;
+
+	printf("uidset add '%s'\n", uid);
+
+	if (ss->last == 0) {
+		printf(" start\n");
+		camel_imapx_command_add(ic, "%d", uidn);
+		ss->entries++;
+		ss->start = uidn;
+	} else {
+		if (ss->last != uidn-1) {
+			if (ss->last == ss->start) {
+				printf(" ,next\n");
+				camel_imapx_command_add(ic, ",%d", uidn);
+				ss->entries++;
+			} else {
+				printf(" :range\n");
+				camel_imapx_command_add(ic, ":%d,%d", ss->last, uidn);
+				ss->entries+=2;
+			}
+			ss->start = uidn;
+		}
+	}
+
+	ss->last = uidn;
+
+	if ((ss->limit && ss->entries >= ss->limit)
+	    || (ss->total && ss->uids >= ss->total)) {
+		printf(" done, %d entries, %d uids\n", ss->entries, ss->uids);
+		imapx_uidset_done(ss, ic);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+
+
+
+
+static void
+imapx_command_add_part(CamelIMAPXCommand *ic, camel_imapx_command_part_t type, void *o)
+{
+	CamelIMAPXCommandPart *cp;
+	CamelStreamNull *null;
+	unsigned int ob_size = 0;
+
+	/* TODO: literal+? */
+	
+	switch(type & CAMEL_IMAPX_COMMAND_MASK) {
+	case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
+	case CAMEL_IMAPX_COMMAND_STREAM: {
+		CamelObject *ob = o;
+
+		/* TODO: seekable streams we could just seek to the end and back */
+		null = (CamelStreamNull *)camel_stream_null_new();
+		if ( (type & CAMEL_IMAPX_COMMAND_MASK) == CAMEL_IMAPX_COMMAND_DATAWRAPPER) {
+			camel_data_wrapper_write_to_stream((CamelDataWrapper *)ob, (CamelStream *)null);
+		} else {
+			camel_stream_reset((CamelStream *)ob);
+			camel_stream_write_to_stream((CamelStream *)ob, (CamelStream *)null);
+			camel_stream_reset((CamelStream *)ob);
+		}
+		type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
+		camel_object_ref(ob);
+		ob_size = null->written;
+		camel_object_unref((CamelObject *)null);
+		camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
+		break;
+	}
+	case CAMEL_IMAPX_COMMAND_AUTH: {
+		CamelObject *ob = o;
+
+		/* we presume we'll need to get additional data only if we're not authenticated yet */
+		camel_object_ref(ob);
+		camel_stream_printf((CamelStream *)ic->mem, "%s", ((CamelSasl *)ob)->mech);
+		if (!camel_sasl_authenticated((CamelSasl *)ob))
+			type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
+		break;
+	}
+	case CAMEL_IMAPX_COMMAND_FILE: {
+		char *path = o;
+		struct stat st;
+
+		if (stat(path, &st) == 0) {
+			o = g_strdup(o);
+			ob_size = st.st_size;
+		} else
+			o = NULL;
+
+		camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
+		type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
+		break;
+	}
+	case CAMEL_IMAPX_COMMAND_STRING:
+		o = g_strdup(o);
+		ob_size = strlen(o);
+		camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
+		type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
+		break;
+	default:
+		ob_size = 0;
+	}
+
+	cp = g_malloc0(sizeof(*cp));
+	cp->type = type;
+	cp->ob_size = ob_size;
+	cp->ob = o;
+	cp->data_size = ic->mem->buffer->len;
+	cp->data = g_malloc(cp->data_size+1);
+	memcpy(cp->data, ic->mem->buffer->data, cp->data_size);
+	cp->data[cp->data_size] = 0;
+
+	camel_stream_reset((CamelStream *)ic->mem);
+	/* FIXME: hackish? */
+	g_byte_array_set_size(ic->mem->buffer, 0);
+
+	e_dlist_addtail(&ic->parts, (EDListNode *)cp);
+}
+
+static void
+imapx_command_addv(CamelIMAPXCommand *ic, const char *fmt, va_list ap)
+{
+	const unsigned char *p, *ps, *start;
+	unsigned char c;
+	unsigned int width;
+	char ch;
+	int llong;
+	int left;
+	int fill;
+	int zero;
+	char *s;
+	char *P;
+	int d;
+	long int l;
+	guint32 f;
+	CamelFlag *F;
+	CamelStream *S;
+	CamelDataWrapper *D;
+	CamelSasl *A;
+	char buffer[16];
+
+	c(printf("adding command, fmt = '%s'\n", fmt));
+
+	p = fmt;
+	ps = fmt;
+	while ( ( c = *p++ ) ) {
+		switch(c) {
+		case '%':
+			if (*p == '%') {
+				camel_stream_write((CamelStream *)ic->mem, ps, p-ps);
+				p++;
+				ps = p;
+			} else {
+				camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1);
+				start = p-1;
+				width = 0;
+				left = FALSE;
+				fill = FALSE;
+				zero = FALSE;
+				llong = FALSE;
+
+				do {
+					c = *p++;
+					if (c == '0')
+						zero = TRUE;
+					else if ( c== '-')
+						left = TRUE;
+					else
+						break;
+				} while (c);
+
+				do {
+					// FIXME: ascii isdigit
+					if (isdigit(c))
+						width = width * 10 + (c-'0');
+					else
+						break;
+				} while ((c = *p++));
+
+				if (c == 'l') {
+					llong = TRUE;
+					c = *p++;
+				}
+
+				switch(c) {
+				case 'A': /* auth object - sasl auth, treat as special kind of continuation */
+					A = va_arg(ap, CamelSasl *);
+					imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_AUTH, A);
+					break;
+				case 'S': /* stream */
+					S = va_arg(ap, CamelStream *);
+					c(printf("got stream '%p'\n", S));
+					imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_STREAM, S);
+					break;
+				case 'D': /* datawrapper */
+					D = va_arg(ap, CamelDataWrapper *);
+					c(printf("got data wrapper '%p'\n", D));
+					imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_DATAWRAPPER, D);
+					break;
+				case 'P': /* filename path */
+					P = va_arg(ap, char *);
+					c(printf("got file path '%s'\n", P));
+					imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_FILE, P);
+					break;
+				case 't': /* token */
+					s = va_arg(ap, char *);
+					camel_stream_write((CamelStream *)ic->mem, s, strlen(s));
+					break;
+				case 's': /* simple string */
+					s = va_arg(ap, char *);
+					c(printf("got string '%s'\n", s));
+					if (*s) {
+						unsigned char mask = imapx_is_mask(s);
+
+						if (mask & IMAPX_TYPE_ATOM_CHAR)
+							camel_stream_write((CamelStream *)ic->mem, s, strlen(s));
+						else if (mask & IMAPX_TYPE_TEXT_CHAR) {
+							camel_stream_write((CamelStream *)ic->mem, "\"", 1);
+							while (*s) {
+								char *start = s;
+
+								while (*s && imapx_is_quoted_char(*s))
+									s++;
+								camel_stream_write((CamelStream *)ic->mem, start, s-start);
+								if (*s) {
+									camel_stream_write((CamelStream *)ic->mem, "\\", 1);
+									camel_stream_write((CamelStream *)ic->mem, s, 1);
+									s++;
+								}
+							}
+							camel_stream_write((CamelStream *)ic->mem, "\"", 1);
+						} else {
+							imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_STRING, s);
+						}
+					} else {
+						camel_stream_write((CamelStream *)ic->mem, "\"\"", 2);
+					}
+					break;
+				case 'f': /* imap folder name */
+					s = va_arg(ap, char *);
+					c(printf("got folder '%s'\n", s));
+					/* FIXME: encode folder name */
+					/* FIXME: namespace? */
+					camel_stream_printf((CamelStream *)ic->mem, "\"%s\"", s?s:"");
+					break;
+				case 'F': /* IMAP flags set */
+					f = va_arg(ap, guint32);
+					F = va_arg(ap, CamelFlag *);
+					imap_write_flags((CamelStream *)ic->mem, f, F);
+					break;
+				case 'c':
+					d = va_arg(ap, int);
+					ch = d;
+					camel_stream_write((CamelStream *)ic->mem, &ch, 1);
+					break;
+				case 'd': /* int/unsigned */
+				case 'u':
+					if (llong) {
+						l = va_arg(ap, long int);
+						c(printf("got long int '%d'\n", (int)l));
+						memcpy(buffer, start, p-start);
+						buffer[p-start] = 0;
+						camel_stream_printf((CamelStream *)ic->mem, buffer, l);
+					} else {
+						d = va_arg(ap, int);
+						c(printf("got int '%d'\n", d));
+						memcpy(buffer, start, p-start);
+						buffer[p-start] = 0;
+						camel_stream_printf((CamelStream *)ic->mem, buffer, d);
+					}
+					break;
+				}
+
+				ps = p;
+			}
+			break;
+		case '\\':	/* only for \\ really, we dont support \n\r etc at all */
+			c = *p;
+			if (c) {
+				g_assert(c == '\\');
+				camel_stream_write((CamelStream *)ic->mem, ps, p-ps);
+				p++;
+				ps = p;
+			}
+		}
+	}
+
+	camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+CamelIMAPXCommand *
+camel_imapx_command_new(const char *name, const char *select, const char *fmt, ...)
+{
+	CamelIMAPXCommand *ic;
+	static int tag = 0;
+	va_list ap;
+
+	ic = g_malloc0(sizeof(*ic));
+	ic->tag = tag++;
+	ic->name = name;
+	ic->mem = (CamelStreamMem *)camel_stream_mem_new();
+	ic->select = g_strdup(select);
+	e_dlist_init(&ic->parts);
+
+	if (fmt && fmt[0]) {
+		va_start(ap, fmt);
+		imapx_command_addv(ic, fmt, ap);
+		va_end(ap);
+	}
+
+	return ic;
+}
+
+void
+camel_imapx_command_add(CamelIMAPXCommand *ic, const char *fmt, ...)
+{
+	va_list ap;
+
+	g_assert(ic->mem);	/* gets reset on queue */
+
+	if (fmt && fmt[0]) {
+		va_start(ap, fmt);
+		imapx_command_addv(ic, fmt, ap);
+		va_end(ap);
+	}
+}
+
+void
+camel_imapx_command_free(CamelIMAPXCommand *ic)
+{
+	CamelIMAPXCommandPart *cp;
+
+	if (ic == NULL)
+		return;
+
+	if (ic->mem)
+		camel_object_unref((CamelObject *)ic->mem);
+	imap_free_status(ic->status);
+	g_free(ic->select);
+
+	while ( (cp = ((CamelIMAPXCommandPart *)e_dlist_remhead(&ic->parts))) ) {
+		g_free(cp->data);
+		if (cp->ob) {
+			switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) {
+			case CAMEL_IMAPX_COMMAND_FILE:
+			case CAMEL_IMAPX_COMMAND_STRING:
+				g_free(cp->ob);
+				break;
+			default:
+				camel_object_unref(cp->ob);
+			}
+		}
+		g_free(cp);
+	}
+
+	g_free(ic);
+}
+
+void
+camel_imapx_command_close(CamelIMAPXCommand *ic)
+{
+	if (ic->mem) {
+		c(printf("completing command buffer is [%d] '%.*s'\n", ic->mem->buffer->len, (int)ic->mem->buffer->len, ic->mem->buffer->data));
+		if (ic->mem->buffer->len > 0)
+			imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_SIMPLE, NULL);
+	
+		camel_object_unref((CamelObject *)ic->mem);
+		ic->mem = NULL;
+	}
+}
+
+/* FIXME: error handling */
+#if 0
+void
+camel_imapx_engine_command_queue(CamelIMAPXEngine *imap, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXCommandPart *cp;
+
+	if (ic->mem) {
+		c(printf("completing command buffer is [%d] '%.*s'\n", ic->mem->buffer->len, (int)ic->mem->buffer->len, ic->mem->buffer->data));
+		if (ic->mem->buffer->len > 0)
+			imapx_command_add_part(imap, ic, CAMEL_IMAPX_COMMAND_SIMPLE, NULL);
+	
+		camel_object_unref((CamelObject *)ic->mem);
+		ic->mem = NULL;
+	}
+
+	/* now have completed command? */
+}
+#endif
+
+/* Get a path into the cache, works like maildir, but isn't */
+static char *
+imapx_get_path_uid(CamelIMAPXServer *is, CamelFolder *folder, const char *bit, const char *uid)
+{
+	char *dir, *path;
+
+	// big fixme of course, we need to create the path if it doesn't exist,
+	// base it on the server, blah blah
+	if (bit == NULL)
+		bit = strchr(uid, '-') == NULL?"cur":"new";
+	dir = g_strdup_printf("/tmp/imap-cache/%s/%s", folder->full_name, bit);
+
+	camel_mkdir(dir, 0777);
+	path = g_strdup_printf("%s/%s", dir, uid);
+	g_free(dir);
+
+	return path;
+}
+
+/* Must hold QUEUE_LOCK */
+static void
+imapx_command_start(CamelIMAPXServer *imap, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXCommandPart *cp;
+
+	camel_imapx_command_close(ic);
+
+	/* FIXME: assert the selected folder == ic->selected */
+
+	cp = (CamelIMAPXCommandPart *)ic->parts.head;
+	g_assert(cp->next);
+
+	ic->current = cp;
+
+	/* TODO: If we support literal+ we should be able to write the whole command out
+	   at this point .... >here< */
+
+	if (cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION)
+		imap->literal = ic;
+
+	e_dlist_addtail(&imap->active, (EDListNode *)ic);
+
+	printf("Staring command (active=%d,%s) %c%05u %s\r\n", e_dlist_length(&imap->active), imap->literal?" literal":"", imap->tagprefix, ic->tag, cp->data);
+	camel_stream_printf((CamelStream *)imap->stream, "%c%05u %s\r\n", imap->tagprefix, ic->tag, cp->data);
+}
+
+/* must have QUEUE lock */
+static void
+imapx_command_start_next(CamelIMAPXServer *imap)
+{
+	CamelIMAPXCommand *ic, *nc;
+	int count = 0;
+	int pri = -128;
+
+	/* See if we can start another task yet.
+
+	If we're waiting for a literal, we cannot proceed.
+
+	If we're about to change the folder we're
+	looking at from user-direction, we dont proceed.
+
+	If we have a folder selected, first see if any
+	jobs are waiting on it, but only if they are
+	at least as high priority as anything we
+	have running.
+
+	If we dont, select the first folder required,
+	then queue all the outstanding jobs on it, that
+	are at least as high priority as the first.
+
+	This is very shitty code!
+	*/
+
+	printf("** Starting next command\n");
+
+	if (imap->literal != NULL || imap->select_pending != NULL) {
+		printf("* no, waiting for literal/pending select '%s'\n", imap->select_pending->full_name);
+		return;
+	}
+
+	ic = (CamelIMAPXCommand *)imap->queue.head;
+	nc = ic->next;
+	if (nc == NULL) {
+		printf("* no, no jobs\n");
+		return;
+	}
+
+	/* See if any queued jobs on this select first */
+	if (imap->select) {
+		printf("- we're selected on '%s', current jobs?\n", imap->select);
+		for (ic = (CamelIMAPXCommand *)imap->active.head;ic->next;ic=ic->next) {
+			printf("-  %3d '%s'\n", (int)ic->pri, ic->name);
+			if (ic->pri > pri)
+				pri = ic->pri;
+			count++;
+			if (count > MAX_COMMANDS) {
+				printf("** too many jobs busy, waiting for results for now\n");
+				return;
+			}
+		}
+
+		printf("-- Checking job queue\n");
+		count = 0;
+		ic = (CamelIMAPXCommand *)imap->queue.head;
+		nc = ic->next;
+		while (nc && imap->literal == NULL && count < MAX_COMMANDS && ic->pri >= pri) {
+			printf("-- %3d '%s'?\n", (int)ic->pri, ic->name);
+			if (ic->select == NULL || strcmp(ic->select, imap->select) == 0) {
+				printf("--> starting '%s'\n", ic->name);
+				pri = ic->pri;
+				e_dlist_remove((EDListNode *)ic);
+				imapx_command_start(imap, ic);
+				count++;
+			}
+			ic = nc;
+			nc = nc->next;
+		}
+
+		if (count)
+			return;
+
+		ic = (CamelIMAPXCommand *)imap->queue.head;
+	}
+
+	/* If we need to select a folder for the first command, do it now, once
+	   it is complete it will re-call us if it succeeded */
+	if (ic->job->folder) {
+		imapx_select(imap, ic->job->folder);
+	} else {
+		pri = ic->pri;
+		nc = ic->next;
+		count = 0;
+		while (nc && imap->literal == NULL && count < MAX_COMMANDS && ic->pri >= pri) {
+			if (ic->select == NULL || (imap->select && strcmp(ic->select, imap->select))) {
+				printf("* queueing job %3d '%s'\n", (int)ic->pri, ic->name);
+				pri = ic->pri;
+				e_dlist_remove((EDListNode *)ic);
+				imapx_command_start(imap, ic);
+				count++;
+			}
+			ic = nc;
+			nc = nc->next;
+		}
+	}
+}
+
+static void
+imapx_command_queue(CamelIMAPXServer *imap, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXCommand *scan;
+
+	/* We enqueue in priority order, new messages have
+	   higher priority than older messages with the same priority */
+
+	camel_imapx_command_close(ic);
+
+	printf("enqueue job '%.*s'\n", ((CamelIMAPXCommandPart *)ic->parts.head)->data_size, ((CamelIMAPXCommandPart *)ic->parts.head)->data);
+
+	QUEUE_LOCK(imap);
+
+	scan = (CamelIMAPXCommand *)imap->queue.head;
+	if (scan->next == NULL)
+		e_dlist_addtail(&imap->queue, (EDListNode *)ic);
+	else {
+		while (scan->next) {
+			if (ic->pri >= scan->pri)
+				break;
+			scan = scan->next;
+		}
+
+		scan->prev->next = ic;
+		ic->next = scan;
+		ic->prev = scan->prev;
+		scan->prev = ic;
+	}
+
+	imapx_command_start_next(imap);
+
+	QUEUE_UNLOCK(imap);
+}
+
+/* Must have QUEUE lock */
+static CamelIMAPXCommand *
+imapx_find_command_tag(CamelIMAPXServer *imap, unsigned int tag)
+{
+	CamelIMAPXCommand *ic;
+
+	ic = imap->literal;
+	if (ic && ic->tag == tag)
+		return ic;
+
+	for (ic = (CamelIMAPXCommand *)imap->active.head;ic->next;ic=ic->next)
+		if (ic->tag == tag)
+			return ic;
+
+	return NULL;
+}
+
+/* Must not have QUEUE lock */
+static CamelIMAPXJob *
+imapx_find_job(CamelIMAPXServer *imap, int type, const char *uid)
+{
+	CamelIMAPXJob *job;
+
+	QUEUE_LOCK(imap);
+
+	for (job = (CamelIMAPXJob *)imap->jobs.head;job->msg.ln.next;job = (CamelIMAPXJob *)job->msg.ln.next) {
+		if (job->type != type)
+			continue;
+
+		switch (type) {
+		case IMAPX_JOB_GET_MESSAGE:
+			if (imap->select
+			    && strcmp(job->folder->full_name, imap->select) == 0
+			    && strcmp(job->u.get_message.uid, uid) == 0)
+				goto found;
+			break;
+		case IMAPX_JOB_REFRESH_INFO:
+			if (imap->select
+			    && strcmp(job->folder->full_name, imap->select) == 0)
+				goto found;
+			break;
+		case IMAPX_JOB_LIST:
+			goto found;
+		}
+	}
+
+	job = NULL;
+found:
+
+	QUEUE_UNLOCK(imap);
+
+	return job;
+}
+
+/* Process all expunged results we had from the last command.
+   This can be somewhat slow ... */
+static void
+imapx_expunged(CamelIMAPXServer *imap)
+{
+	int count = 1, index=0, expunge;
+	const CamelMessageInfo *iterinfo;
+	CamelIterator *iter;
+
+	g_assert(imap->select_folder);
+
+	if (imap->expunged->len == 0)
+		return;
+
+	printf("Processing '%d' expunges\n", imap->expunged->len);
+
+	expunge = g_array_index(imap->expunged, guint32, index++);
+	iter = camel_folder_summary_search(imap->select_folder->summary, NULL, NULL, NULL, NULL);
+	while ((iterinfo = camel_iterator_next(iter, NULL))) {
+		if (count == expunge) {
+			printf("expunging '%d' - '%s'\n", expunge, camel_message_info_subject(iterinfo));
+			camel_folder_summary_remove(imap->select_folder->summary, (CamelMessageInfo *)iterinfo);
+			if (index >= imap->expunged->len)
+				break;
+			expunge = g_array_index(imap->expunged, guint32, index++);
+		} else
+			//FIXME: skip over offline uids
+			count++;
+	}
+	camel_iterator_free(iter);
+	g_array_set_size(imap->expunged, 0);
+}
+
+/* handle any untagged responses */
+static int
+imapx_untagged(CamelIMAPXServer *imap)
+{
+	unsigned int id, len;
+	unsigned char *token, *p, c;
+	int tok;
+	struct _status_info *sinfo;
+	
+	e(printf("got untagged response\n"));
+	id = 0;
+	tok = camel_imapx_stream_token(imap->stream, &token, &len);
+	if (tok == IMAP_TOK_INT) {
+		id = strtoul(token, NULL, 10);
+		tok = camel_imapx_stream_token(imap->stream, &token, &len);
+	}
+
+	if (tok == '\n')
+		camel_exception_throw(1, "truncated server response");
+
+	e(printf("Have token '%s' id %d\n", token, id));
+	p = token;
+	while ((c = *p))
+		*p++ = toupper(c);
+
+	switch (imap_tokenise(token, len)) {
+	case IMAP_CAPABILITY:
+		if (imap->cinfo)
+			imap_free_capability(imap->cinfo);
+		imap->cinfo = imap_parse_capability(imap->stream);
+		printf("got capability flags %08x\n", imap->cinfo->capa);
+		return 0;
+	case IMAP_EXPUNGE: {
+		guint32 expunge = id;
+
+		printf("expunged: %d\n", id);
+		g_array_append_val(imap->expunged, expunge);
+		break;
+	}
+	case IMAP_EXISTS:
+		printf("exists: %d\n", id);
+		imap->exists = id;
+		break;
+	case IMAP_FLAGS: {
+		guint32 flags;
+
+		imap_parse_flags(imap->stream, &flags, NULL);
+
+		printf("flags: %08x\n", flags);
+		break;
+	}
+	case IMAP_FETCH: {
+		struct _fetch_info *finfo;
+
+		finfo = imap_parse_fetch(imap->stream);
+
+		//imap_dump_fetch(finfo);
+
+		if ((finfo->got & (FETCH_BODY|FETCH_UID)) == (FETCH_BODY|FETCH_UID)) {
+			CamelIMAPXJob *job = imapx_find_job(imap, IMAPX_JOB_GET_MESSAGE, finfo->uid);
+
+			/* This must've been a get-message request, fill out the body stream,
+			   in the right spot */
+
+			if (job) {
+#ifdef MULTI_FETCH
+				job->u.get_message.body_offset = finfo->offset;
+				camel_seekable_stream_seek((CamelSeekableStream *)job->u.get_message.stream, finfo->offset, CAMEL_STREAM_SET);
+#endif
+				job->u.get_message.body_len = camel_stream_write_to_stream(finfo->body, job->u.get_message.stream);
+				if (job->u.get_message.body_len == -1) {
+					camel_exception_setv(job->ex, 1, "error writing to cache stream: %s\n", g_strerror(errno));
+					camel_object_unref(job->u.get_message.stream);
+					job->u.get_message.stream = NULL;
+				}
+			}
+		}
+
+		if ((finfo->got & (FETCH_FLAGS|FETCH_UID)) == (FETCH_FLAGS|FETCH_UID)) {
+			CamelIMAPXJob *job = imapx_find_job(imap, IMAPX_JOB_REFRESH_INFO, NULL);
+
+			/* This is either a refresh_info job, check to see if it is and update
+			   if so, otherwise it must've been an unsolicited response, so update
+			   the summary to match */
+
+			if (job) {
+				struct _refresh_info r;
+
+				r.uid = finfo->uid;
+				finfo->uid = NULL;
+				r.server_flags = finfo->flags;
+				r.server_user_flags = finfo->user_flags;
+				finfo->user_flags = NULL;
+				g_array_append_val(job->u.refresh_info.infos, r);
+			} else {
+				printf("Unsolicited flags response '%s' %08x\n", finfo->uid, finfo->flags);
+				// TODO, we need the folder as well as the name in the select field.
+			}
+		}
+
+		if ((finfo->got & (FETCH_HEADER|FETCH_UID)) == (FETCH_HEADER|FETCH_UID)) {
+			CamelIMAPXJob *job = imapx_find_job(imap, IMAPX_JOB_REFRESH_INFO, NULL);
+
+			/* This must be a refresh info job as well, but it has asked for
+			   new messages to be added to the index */
+
+			if (job) {
+				CamelMimeParser *mp;
+				CamelMessageInfo *mi;
+
+				/* Do we want to save these headers for later too?  Do we care? */
+
+				mp = camel_mime_parser_new();
+				camel_mime_parser_init_with_stream(mp, finfo->header);
+				mi = camel_message_info_new_from_parser(job->folder->summary, mp);
+				camel_object_unref(mp);
+
+				if (mi) {
+					GArray *infos = job->u.refresh_info.infos;
+					int i = job->u.refresh_info.last_index;
+
+					/* This is rather inefficent, but should be ok if we're expecting it
+					   since we break each fetch into lots of 100 */
+					mi->uid = g_strdup(finfo->uid);
+					for (i=0;i<infos->len;i++) {
+						struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
+
+						if (r->uid && !strcmp(r->uid, finfo->uid)) {
+							((CamelMessageInfoBase *)mi)->flags = r->server_flags;
+							((CamelIMAPXMessageInfo *)mi)->server_flags = r->server_flags;
+							camel_flag_list_copy(&((CamelMessageInfoBase *)mi)->user_flags, &r->server_user_flags);
+							((CamelIMAPXMessageInfo *)mi)->server_flags = r->server_user_flags; /*check this*/
+							break;
+						}
+					}
+					camel_folder_summary_add(job->folder->summary, mi);
+				}
+			}
+		}
+
+		imap_free_fetch(finfo);
+		break;
+	}
+	case IMAP_LIST: case IMAP_LSUB: {
+		struct _list_info *linfo = imap_parse_list(imap->stream);
+		CamelIMAPXJob *job = imapx_find_job(imap, IMAPX_JOB_LIST, linfo->name);
+
+		// TODO: we want to make sure the names match?
+
+		printf("list: '%s' (%c)\n", linfo->name, linfo->separator);
+		if (job && g_hash_table_lookup(job->u.list.folders, linfo->name) == NULL) {
+			g_hash_table_insert(job->u.list.folders, linfo->name, linfo);
+		} else {
+			g_warning("got list response but no current listing job happening?\n");
+			imap_free_list(linfo);
+		}
+		break;
+	}
+	case IMAP_RECENT:
+		printf("recent: %d\n", id);
+		imap->recent = id;
+		break;
+	case IMAP_BYE: case IMAP_OK: case IMAP_NO: case IMAP_BAD: case IMAP_PREAUTH:
+		/* TODO: validate which ones of these can happen as unsolicited responses */
+		/* TODO: handle bye/preauth differently */
+		camel_imapx_stream_ungettoken(imap->stream, tok, token, len);
+		sinfo = imap_parse_status(imap->stream);
+		camel_object_trigger_event(imap, "status", sinfo);
+		switch(sinfo->condition) {
+		case IMAP_READ_WRITE:
+			imap->mode = IMAPX_MODE_READ|IMAPX_MODE_WRITE;
+			printf("folder is read-write\n");
+			break;
+		case IMAP_READ_ONLY:
+			imap->mode = IMAPX_MODE_READ;
+			printf("folder is read-only\n");
+			break;
+		case IMAP_UIDVALIDITY:
+			imap->uidvalidity = sinfo->u.uidvalidity;
+			break;
+		case IMAP_UNSEEN:
+			imap->unseen = sinfo->u.unseen;
+			break;
+		case IMAP_PERMANENTFLAGS:
+			imap->permanentflags = sinfo->u.permanentflags;
+			break;
+		case IMAP_ALERT:
+			printf("ALERT!: %s\n", sinfo->text);
+			break;
+		case IMAP_PARSE:
+			printf("PARSE: %s\n", sinfo->text);
+			break;
+		default:
+			break;
+		}
+		imap_free_status(sinfo);
+		return 0;
+	default:
+		/* unknown response, just ignore it */
+		printf("unknown token: %s\n", token);
+	}
+
+	return camel_imapx_stream_skip(imap->stream);
+}
+
+/* handle any continuation requests
+   either data continuations, or auth continuation */
+static int
+imapx_continuation(CamelIMAPXServer *imap)
+{
+	CamelIMAPXCommand *ic, *newliteral = NULL;
+	CamelIMAPXCommandPart *cp;
+	
+	printf("got continuation response\n");
+
+	/* The 'literal' pointer is like a write-lock, nothing else
+	   can write while we have it ... so we dont need any
+	   ohter lock here.  All other writes go through
+	   queue-lock */
+
+	ic = imap->literal;
+	if (ic == NULL) {
+		camel_imapx_stream_skip(imap->stream);
+		printf("got continuation response with no outstanding continuation requests?\n");
+		return 1;
+	}
+
+	printf("got continuation response for data\n");
+	cp = ic->current;
+	switch(cp->type & CAMEL_IMAPX_COMMAND_MASK) {
+	case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
+		printf("writing data wrapper to literal\n");
+		camel_data_wrapper_write_to_stream((CamelDataWrapper *)cp->ob, (CamelStream *)imap->stream);
+		break;
+	case CAMEL_IMAPX_COMMAND_STREAM:
+		printf("writing stream to literal\n");
+		camel_stream_write_to_stream((CamelStream *)cp->ob, (CamelStream *)imap->stream);
+		break;
+	case CAMEL_IMAPX_COMMAND_AUTH: {
+		CamelException *ex = camel_exception_new();
+		char *resp;
+		unsigned char *token;
+		int tok, len;
+		
+		tok = camel_imapx_stream_token(imap->stream, &token, &len);
+		resp = camel_sasl_challenge_base64((CamelSasl *)cp->ob, token, ex);
+		if (camel_exception_is_set(ex))
+			camel_exception_throw_ex(ex);
+		camel_exception_free(ex);
+		
+		printf("got auth continuation, feeding token '%s' back to auth mech\n", resp);
+		
+		camel_stream_write((CamelStream *)imap->stream, resp, strlen(resp));
+		
+		/* we want to keep getting called until we get a status reponse from the server
+		   ignore what sasl tells us */
+		newliteral = ic;
+		
+		break; }
+	case CAMEL_IMAPX_COMMAND_FILE: {
+		CamelStream *file;
+
+		printf("writing file '%s' to literal\n", (char *)cp->ob);
+
+		// FIXME: errors
+		if (cp->ob && (file = camel_stream_fs_new_with_name(cp->ob, O_RDONLY, 0))) {
+			camel_stream_write_to_stream(file, (CamelStream *)imap->stream);
+			camel_object_unref(file);
+		} else if (cp->ob_size > 0) {
+			// Server is expecting data ... ummm, send it zeros?  abort?
+		}
+		break; }
+	case CAMEL_IMAPX_COMMAND_STRING:
+		camel_stream_write((CamelStream *)imap->stream, cp->ob, cp->ob_size);
+		break;
+	default:
+		/* should we just ignore? */
+		imap->literal = NULL;
+		camel_exception_throw(1, "continuation response for non-continuation request");
+	}
+	
+	camel_imapx_stream_skip(imap->stream);
+	
+	cp = cp->next;
+	if (cp->next) {
+		ic->current = cp;
+		printf("next part of command \"A%05u: %s\"\n", ic->tag, cp->data);
+		camel_stream_printf((CamelStream *)imap->stream, "%s\r\n", cp->data);
+		if (cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION) {
+			newliteral = ic;
+		} else {
+			g_assert(cp->next->next == NULL);
+		}
+	} else {
+		printf("%p: queueing continuation\n", ic);
+		camel_stream_printf((CamelStream *)imap->stream, "\r\n");
+	}
+
+	QUEUE_LOCK(imap);
+	imap->literal = newliteral;
+
+	imapx_command_start_next(imap);
+	QUEUE_UNLOCK(imap);
+
+	return 1;
+}
+
+/* handle a completion line */
+static int
+imapx_completion(CamelIMAPXServer *imap, unsigned char *token, int len)
+{
+	CamelIMAPXCommand * volatile ic;
+	unsigned int tag;
+
+	if (token[0] != imap->tagprefix)
+		camel_exception_throw(1, "Server sent unexpected response: %s", token);
+
+	tag = strtoul(token+1, NULL, 10);
+
+	QUEUE_LOCK(imap);
+	if ((ic = imapx_find_command_tag(imap, tag)) == NULL) {
+		QUEUE_UNLOCK(imap);
+		camel_exception_throw(1, "got response tag unexpectedly: %s", token);
+	}
+
+	printf("Got completion response for command %05u '%s'\n", ic->tag, ic->name);
+
+	e_dlist_remove((EDListNode *)ic);
+	e_dlist_addtail(&imap->done, (EDListNode *)ic);
+	if (imap->literal == ic)
+		imap->literal = NULL;
+
+	if (ic->current->next->next) {
+		QUEUE_UNLOCK(imap);
+		camel_exception_throw(1, "command still has unsent parts?", ic->name);
+	}
+
+	/* A follow-on command might've already queued a new literal since were were done with ours? */
+//	if (imap->literal != NULL) {
+//		QUEUE_UNLOCK(imap);
+//		camel_exception_throw(1, "command still has outstanding continuation", imap->literal->name);
+//	}
+
+	QUEUE_UNLOCK(imap);
+	ic->status = imap_parse_status(imap->stream);
+
+	if (ic->complete)
+		ic->complete(imap, ic);
+
+	if (imap->expunged->len)
+		imapx_expunged(imap);
+
+	QUEUE_LOCK(imap);
+	if (ic->complete) {
+		e_dlist_remove((EDListNode *)ic);
+		camel_imapx_command_free(ic);
+	}
+	imapx_command_start_next(imap);
+	QUEUE_UNLOCK(imap);
+
+	return 1;
+}
+
+static void
+imapx_step(CamelIMAPXServer *is)
+/* throws IO,PARSE exception */
+{
+	unsigned int len;
+	unsigned char *token;
+	int tok;
+
+	// poll ?  wait for other stuff? loop?
+	tok = camel_imapx_stream_token(is->stream, &token, &len);
+	if (tok == '*')
+		imapx_untagged(is);
+	else if (tok == IMAP_TOK_TOKEN)
+		imapx_completion(is, token, len);
+	else if (tok == '+')
+		imapx_continuation(is);
+	else
+		camel_exception_throw(1, "unexpected server response: %s", token);
+}
+
+/* Used to run 1 command synchronously,
+   use for capa, login, and selecting only. */
+static void
+imapx_command_run(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+/* throws IO,PARSE exception */
+{
+	camel_imapx_command_close(ic);
+	QUEUE_LOCK(is);
+	g_assert(e_dlist_empty(&is->active));
+	imapx_command_start(is, ic);
+	QUEUE_UNLOCK(is);
+	do {
+		imapx_step(is);
+	} while (ic->status == NULL);
+
+	e_dlist_remove((EDListNode *)ic);
+}
+
+/* ********************************************************************** */
+static void
+imapx_select_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+
+	if (ic->status->result != IMAP_OK) {
+		EDList failed = E_DLIST_INITIALISER(failed);
+		CamelIMAPXCommand *cw, *cn;
+
+		printf("Select failed\n");
+
+		QUEUE_LOCK(is);
+		cw = (CamelIMAPXCommand *)is->queue.head;
+		cn = cw->next;
+		while (cn) {
+			if (cw->select && strcmp(cw->select, is->select_pending->full_name) == 0) {
+				e_dlist_remove((EDListNode *)cw);
+				e_dlist_addtail(&failed, (EDListNode *)cw);
+			}
+			cw = cn;
+			cn = cn->next;
+		}
+		QUEUE_UNLOCK(is);
+
+		cw = (CamelIMAPXCommand *)failed.head;
+		cn = cw->next;
+		while (cn) {
+			cw->status = imap_copy_status(ic->status);
+			cw->complete(is, cw);
+			camel_imapx_command_free(cw);
+			cw = cn;
+			cn = cn->next;
+		}
+
+		camel_object_unref(is->select_pending);
+	} else {
+		printf("Select ok!\n");
+
+		is->select_folder = is->select_pending;
+		is->select = g_strdup(is->select_folder->full_name);
+		is->state = IMAPX_SELECTED;
+#if 0
+		/* This must trigger a complete index rebuild! */
+		if (is->uidvalidity && is->uidvalidity != ((CamelIMAPXSummary *)is->select_folder->summary)->uidvalidity)
+			g_warning("uidvalidity doesn't match!");
+
+		/* This should trigger a new messages scan */
+		if (is->exists != is->select_folder->summary->root_view->total_count)
+			g_warning("exists is %d our summary is %d and summary exists is %d\n", is->exists,
+				  is->select_folder->summary->root_view->total_count,
+				  ((CamelIMAPXSummary *)is->select_folder->summary)->exists);
+#endif
+	}
+
+	is->select_pending = NULL;
+}
+
+static void
+imapx_select(CamelIMAPXServer *is, CamelFolder *folder)
+{
+	CamelIMAPXCommand *ic;
+
+	/* Select is complicated by the fact we may have commands
+	   active on the server for a different selection.
+
+	   So this waits for any commands to complete, selects the
+	   new folder, and halts the queuing of any new commands.
+	   It is assumed whomever called is us about to issue
+	   a high-priority command anyway */
+
+	/* TODO check locking here, pending_select will do
+	   most of the work for normal commands, but not
+	   for another select */
+
+	if (is->select_pending)
+		return;
+
+	if (is->select && strcmp(is->select, folder->full_name) == 0)
+		return;
+
+	is->select_pending = folder;
+	camel_object_ref(folder);
+	if (is->select_folder) {
+		while (!e_dlist_empty(&is->active)) {
+			QUEUE_UNLOCK(is);
+			imapx_step(is);
+			QUEUE_LOCK(is);
+		}
+		g_free(is->select);
+		camel_object_unref(is->select_folder);
+		is->select = NULL;
+		is->select_folder = NULL;
+	}
+
+	is->uidvalidity = 0;
+	is->unseen = 0;
+	is->permanentflags = 0;
+	is->exists = 0;
+	is->recent = 0;
+	is->mode = 0;
+
+	/* Hrm, what about reconnecting? */
+	is->state = IMAPX_AUTHENTICATED;
+
+	ic = camel_imapx_command_new("SELECT", NULL, "SELECT %s", CIF(folder)->raw_name);
+	ic->complete = imapx_select_done;
+	imapx_command_start(is, ic);
+}
+
+static void
+imapx_connect(CamelIMAPXServer *is, int ssl_mode, int try_starttls)
+/* throws IO exception */
+{
+	CamelStream * volatile tcp_stream = NULL;
+	int ret;
+
+	CAMEL_TRY {
+#ifdef HAVE_SSL
+		const char *mode;
+#endif
+		unsigned char *buffer;
+		int len;
+		char *serv;
+		const char *port = NULL;
+		struct addrinfo *ai, hints = { 0 };
+		CamelException ex = { 0 };
+		CamelIMAPXCommand *ic;
+
+		if (is->url->port) {
+			serv = g_alloca(16);
+			sprintf(serv, "%d", is->url->port);
+		} else {
+			serv = "imap";
+			port = "143";
+		}
+#ifdef HAVE_SSL
+		mode = camel_url_get_param(is->url, "use_ssl");
+		if (mode && strcmp(mode, "never") != 0) {
+			if (!strcmp(mode, "when-possible")) {
+				tcp_stream = camel_tcp_stream_ssl_new_raw(is->session, is->url->host, STARTTLS_FLAGS);
+			} else {
+				if (is->url->port == 0) {
+					serv = "imaps";
+					port = "993";
+				}
+				tcp_stream = camel_tcp_stream_ssl_new(is->session, is->url->host, SSL_PORT_FLAGS);
+			}
+		} else {
+			tcp_stream = camel_tcp_stream_raw_new ();
+		}
+#else	
+		tcp_stream = camel_tcp_stream_raw_new ();
+#endif /* HAVE_SSL */
+
+		hints.ai_socktype = SOCK_STREAM;
+		ai = camel_getaddrinfo(is->url->host, serv, &hints, &ex);
+		if (ex.id && ex.id != CAMEL_EXCEPTION_USER_CANCEL && port != NULL) {
+			camel_exception_clear(&ex);
+			ai = camel_getaddrinfo(is->url->host, port, &hints, &ex);
+		}
+
+		if (ex.id)
+			camel_exception_throw_ex(&ex);
+	
+		ret = camel_tcp_stream_connect(CAMEL_TCP_STREAM(tcp_stream), ai);
+		camel_freeaddrinfo(ai);
+		if (ret == -1) {
+			if (errno == EINTR)
+				camel_exception_throw(CAMEL_EXCEPTION_USER_CANCEL, _("Connection cancelled"));
+			else
+				camel_exception_throw(CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+						      _("Could not connect to %s (port %s): %s"),
+						      is->url->host, serv, g_strerror(errno));
+		}
+
+		is->stream = (CamelIMAPXStream *)camel_imapx_stream_new(tcp_stream);
+		camel_object_unref(tcp_stream);
+		tcp_stream = NULL;
+
+		camel_imapx_stream_gets(is->stream, &buffer, &len);
+		printf("Got greeting '%.*s'\n", len, buffer);
+
+		ic = camel_imapx_command_new("CAPABILITY", NULL, "CAPABILITY");
+		imapx_command_run(is, ic);
+		camel_imapx_command_free(ic);
+	} CAMEL_CATCH(e) {
+		if (tcp_stream)
+			camel_object_unref(tcp_stream);
+		camel_exception_throw_ex(e);
+	} CAMEL_DONE;
+}
+
+static void
+imapx_reconnect(CamelIMAPXServer *is)
+{
+retry:
+	CAMEL_TRY {
+		CamelSasl *sasl;
+		CamelIMAPXCommand *ic;
+
+		imapx_connect(is, 0, 0);
+
+		if (is->url->passwd == NULL) {
+			CamelException ex = { 0 };
+			char *prompt = g_strdup_printf(_("%sPlease enter the IMAP password for %s %s"), "", is->url->user, is->url->host);
+
+			is->url->passwd = camel_session_get_password(is->session, (CamelService *)is->store,
+								     camel_url_get_param(is->url, "auth-domain"),
+								     prompt, "password", 0, &ex);
+			g_free(prompt);
+			if (ex.id)
+				camel_exception_throw_ex(&ex);
+		}
+
+		if (is->url->authmech
+		    && (sasl = camel_sasl_new("imap", is->url->authmech, NULL))) {
+			ic = camel_imapx_command_new("AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl);
+			camel_object_unref(sasl);
+		} else {
+			ic = camel_imapx_command_new("LOGIN", NULL, "LOGIN %s %s", is->url->user, is->url->passwd);
+		}
+
+		// TODO freeing data?
+		imapx_command_run(is, ic);
+		if (ic->status->result != IMAP_OK)
+			camel_exception_throw(CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, "Login failed: %s", ic->status->text);
+		camel_imapx_command_free(ic);
+
+		/* After login we re-capa */
+		if (is->cinfo) {
+			imap_free_capability(is->cinfo);
+			is->cinfo = NULL;
+		}
+		ic = camel_imapx_command_new("CAPABILITY", NULL, "CAPABILITY");
+		imapx_command_run(is, ic);
+		camel_imapx_command_free(ic);
+		is->state = IMAPX_AUTHENTICATED;
+	} CAMEL_CATCH(e) {
+		/* Shrug, either way this re-loops back ... */
+		if (TRUE /*e->ex != CAMEL_EXCEPTION_USER_CANCEL*/) {
+			printf("Re Connection failed: %s\n", e->desc);
+			sleep(5);
+			// camelexception_done?
+			goto retry;
+		}
+	} CAMEL_DONE;
+}
+
+/* ********************************************************************** */
+
+static void
+imapx_job_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXJob *job = ic->job;
+
+	e_dlist_remove((EDListNode *)job);
+	if (job->noreply) {
+		camel_exception_clear(job->ex);
+		g_free(job);
+	} else
+		e_msgport_reply((EMsg *)job);
+}
+
+/* ********************************************************************** */
+
+static void
+imapx_job_get_message_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXJob *job = ic->job;
+
+	/* We either have more to fetch (partial mode?), we are complete,
+	   or we failed.  Failure is handled in the fetch code, so
+	   we just return the job, or keep it alive with more requests */
+
+	// FIXME FIXME Who and how do we free the command???
+
+	job->commands--;
+
+	if (ic->status->result != IMAP_OK) {
+		job->u.get_message.body_len = -1;
+		if (job->u.get_message.stream) {
+			camel_object_unref(job->u.get_message.stream);
+			job->u.get_message.stream = 0;
+		}
+
+		camel_exception_setv(job->ex, 1, "Error retriving message: %s", ic->status->text);
+	}
+
+#ifdef MULTI_FETCH
+	if (job->u.get_message.body_len == MULTI_SIZE) {
+		ic = camel_imapx_command_new("FETCH", job->folder->full_name,
+					     "UID FETCH %t (BODY.PEEK[]", job->u.get_message.uid);
+		camel_imapx_command_add(ic, "<%u.%u>", job->u.get_message.fetch_offset, MULTI_SIZE);
+		camel_imapx_command_add(ic, ")");
+		ic->complete = imapx_job_get_message_done;
+		ic->job = job;
+		ic->pri = job->pri;
+		job->u.get_message.fetch_offset += MULTI_SIZE;
+		job->commands++;
+		imapx_command_queue(is, ic);
+	}
+#endif
+	if (job->commands == 0)
+		imapx_job_done(is, ic);
+}
+
+static void
+imapx_job_get_message_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	CamelIMAPXCommand *ic;
+#ifdef MULTI_FETCH
+	int i;
+#endif
+	// FIXME: MUST ensure we never try to get the same message
+	// twice at the same time.
+
+	/* If this is a high-priority get, then we also
+	   select the folder to make sure it runs immmediately ...
+
+	   This doesn't work yet, so we always force a select every time
+	*/
+	//imapx_select(is, job->folder);
+#ifdef MULTI_FETCH
+	for (i=0;i<3;i++) {
+		ic = camel_imapx_command_new("FETCH", job->folder->full_name,
+						     "UID FETCH %t (BODY.PEEK[]", job->u.get_message.uid);
+		camel_imapx_command_add(ic, "<%u.%u>", job->u.get_message.fetch_offset, MULTI_SIZE);
+		camel_imapx_command_add(ic, ")");
+		ic->complete = imapx_job_get_message_done;
+		ic->job = job;
+		ic->pri = job->pri;
+		job->u.get_message.fetch_offset += MULTI_SIZE;
+		job->commands++;
+		imapx_command_queue(is, ic);
+	}
+#else
+	ic = camel_imapx_command_new("FETCH", job->folder->full_name,
+				     "UID FETCH %t (BODY.PEEK[])", job->u.get_message.uid);
+	ic->complete = imapx_job_get_message_done;
+	ic->job = job;
+	ic->pri = job->pri;
+	job->commands++;
+	imapx_command_queue(is, ic);
+#endif
+}
+
+/* ********************************************************************** */
+
+static void
+imapx_job_append_message_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXJob *job = ic->job;
+	CamelMessageInfo *mi;
+	char *cur;
+
+	/* Append done.  If we the server supports UIDPLUS we will get an APPENDUID response
+	   with the new uid.  This lets us move the message we have directly to the cache
+	   and also create a correctly numbered MessageInfo, without losing any information.
+	   Otherwise we have to wait for the server to less us know it was appended. */
+
+	if (ic->status->result == IMAP_OK) {
+		if (ic->status->condition == IMAP_APPENDUID) {
+			printf("Got appenduid %d %d\n", (int)ic->status->u.appenduid.uidvalidity, (int)ic->status->u.appenduid.uid);
+			if (ic->status->u.appenduid.uidvalidity == is->uidvalidity) {
+				mi = camel_message_info_clone(job->u.append_message.info);
+				mi->uid = g_strdup_printf("%u", (unsigned int)ic->status->u.appenduid.uid);
+				cur = imapx_get_path_uid(is, job->folder, NULL, mi->uid);
+				printf("Moving cache item %s to %s\n", job->u.append_message.path, cur);
+				link(job->u.append_message.path, cur);
+				g_free(cur);
+				camel_folder_summary_add(job->folder->summary, mi);
+				camel_message_info_free(mi);
+			} else {
+				printf("but uidvalidity changed, uh ...\n");
+			}
+		}
+		camel_folder_summary_remove(job->folder->summary, job->u.append_message.info);
+		// should the folder-summary remove the file ?
+		unlink(job->u.append_message.path);
+	} else {
+		camel_exception_setv(job->ex, 1, "Error appending message: %s", ic->status->text);
+	}
+
+	camel_message_info_free(job->u.append_message.info);
+	g_free(job->u.append_message.path);
+	camel_object_unref(job->folder);
+
+	imapx_job_done(is, ic);
+}
+
+static void
+imapx_job_append_message_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	CamelIMAPXCommand *ic;
+
+	// FIXME: We dont need anything selected for this command to run ... 
+	//imapx_select(is, job->folder);
+	/* TODO: we could supply the original append date from the file timestamp */
+	ic = camel_imapx_command_new("APPEND", NULL,
+				     "APPEND %f %F %P",
+				     job->folder->full_name,
+				     ((CamelMessageInfoBase *)job->u.append_message.info)->flags,
+				     ((CamelMessageInfoBase *)job->u.append_message.info)->user_flags,
+				     job->u.append_message.path);
+	ic->complete = imapx_job_append_message_done;
+	ic->job = job;
+	ic->pri = job->pri;
+	job->commands++;
+	imapx_command_queue(is, ic);
+}
+
+/* ********************************************************************** */
+
+static int
+imapx_refresh_info_uid_cmp(const void *ap, const void *bp)
+{
+	unsigned int av, bv;
+
+	av = strtoul((const char *)ap, NULL, 10);
+	bv = strtoul((const char *)bp, NULL, 10);
+
+	if (av<bv)
+		return -1;
+	else if (av>bv)
+		return 1;
+	else
+		return 0;
+}
+
+static int
+imapx_refresh_info_cmp(const void *ap, const void *bp)
+{
+	const struct _refresh_info *a = ap;
+	const struct _refresh_info *b = bp;
+
+	return imapx_refresh_info_uid_cmp(a->uid, b->uid);
+}
+
+/* skips over non-server uids (pending appends) */
+static const CamelMessageInfo *
+imapx_iterator_next(CamelIterator *iter, CamelException *ex)
+{
+	const CamelMessageInfo *iterinfo;
+
+	while ((iterinfo = camel_iterator_next(iter, NULL))
+	       && strchr(camel_message_info_uid(iterinfo), '-') != NULL)
+		printf("Ignoring offline uid '%s'\n", camel_message_info_uid(iterinfo));
+
+	return iterinfo;
+}
+
+static void
+imapx_job_refresh_info_step_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXJob *job = ic->job;
+	int i = job->u.refresh_info.index;
+	GArray *infos = job->u.refresh_info.infos;
+
+/*	if (camel_change_info_changed(job->u.refresh_info.changes))
+		camel_object_trigger_event(job->folder, "folder_changed", job->u.refresh_info.changes);
+	camel_change_info_clear(job->u.refresh_info.changes); */
+
+	if (i<infos->len) {
+		ic = camel_imapx_command_new("FETCH", job->folder->full_name, "UID FETCH ");
+		ic->complete = imapx_job_refresh_info_step_done;
+		ic->job = job;
+		job->u.refresh_info.last_index = i;
+
+		for (;i<infos->len;i++) {
+			int res;
+			struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
+
+			if (r->uid) {
+				res = imapx_uidset_add(&job->u.refresh_info.uidset, ic, r->uid);
+				if (res == 1) {
+					camel_imapx_command_add(ic, " (RFC822.SIZE RFC822.HEADER)");
+					job->u.refresh_info.index = i;
+					imapx_command_queue(is, ic);
+					return;
+				}
+			}
+		}
+
+		job->u.refresh_info.index = i;
+		if (imapx_uidset_done(&job->u.refresh_info.uidset, ic)) {
+			camel_imapx_command_add(ic, " (RFC822.SIZE RFC822.HEADER)");
+			imapx_command_queue(is, ic);
+			return;
+		}
+	}
+
+	for (i=0;i<infos->len;i++) {
+		struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
+
+		g_free(r->uid);
+	}
+	g_array_free(job->u.refresh_info.infos, TRUE);
+	e_dlist_remove((EDListNode *)job);
+	e_msgport_reply((EMsg *)job);
+}
+
+static int
+imapx_uid_cmp(const void *ap, const void *bp, void *data)
+{
+	const char *a = ap, *b = bp;
+	char *ae, *be;
+	unsigned long av, bv;
+
+	av = strtoul(a, &ae, 10);
+	bv = strtoul(b, &be, 10);
+
+	if (av < bv)
+		return -1;
+	else if (av > bv)
+		return 1;
+
+	if (*ae == '-')
+		ae++;
+	if (*be == '-')
+		be++;
+
+	return strcmp(ae, be);
+}
+
+static void
+imapx_job_refresh_info_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXJob *job = ic->job;
+	int i;
+	GArray *infos = job->u.refresh_info.infos;
+
+	if (ic->status->result == IMAP_OK) {
+		GCompareDataFunc uid_cmp = imapx_uid_cmp;
+		CamelIterator *iter;
+		const CamelMessageInfo *iterinfo;
+		CamelIMAPXMessageInfo *info;
+		CamelFolderSummary *s = job->folder->summary;
+		int i, count=0;
+
+		/* Here we do the typical sort/iterate/merge loop.
+		   If the server flags dont match what we had, we modify our
+		   flags to pick up what the server now has - but we merge
+		   not overwrite */
+
+		/* FIXME: We also have to check the offline directory for
+		   anything missing in our summary, and also queue up jobs
+		   for all outstanding messages to be uploaded */
+
+		qsort(infos->data, infos->len, sizeof(struct _refresh_info), imapx_refresh_info_cmp);
+
+		iter = camel_folder_summary_search(s, NULL, NULL, NULL, NULL);
+		iterinfo = imapx_iterator_next(iter, NULL);
+
+		for (i=0;i<infos->len;i++) {
+			struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
+
+			while (iterinfo && uid_cmp(camel_message_info_uid(iterinfo), r->uid, s) < 0) {
+				printf("Message %s vanished\n", iterinfo->uid);
+//				camel_change_info_remove(job->u.refresh_info.changes, iterinfo);
+				camel_folder_summary_remove(s, (CamelMessageInfo *)iterinfo);
+				iterinfo = imapx_iterator_next(iter, NULL);
+			}
+
+			info = NULL;
+			if (iterinfo && uid_cmp(iterinfo->uid, r->uid, s) == 0) {
+				printf("already have '%s'\n", r->uid);
+				info = (CamelIMAPXMessageInfo *)iterinfo;
+//				if (info->server_flags !=  r->server_flags
+//				    && camel_message_info_set_flags((CamelMessageInfo *)info, info->server_flags ^ r->server_flags, r->server_flags))
+//					camel_change_info_change(job->u.refresh_info.changes, iterinfo);
+				iterinfo = imapx_iterator_next(iter, NULL);
+				g_free(r->uid);
+				r->uid = NULL;
+			} else {
+				count++;
+			}
+		}
+
+		while (iterinfo) {
+			printf("Message %s vanished\n", iterinfo->uid);
+//			camel_change_info_remove(job->u.refresh_info.changes, iterinfo);
+			camel_folder_summary_remove(s, (CamelMessageInfo *)iterinfo);
+			iterinfo = imapx_iterator_next(iter, NULL);
+		}
+
+/*		if (camel_change_info_changed(job->u.refresh_info.changes))
+			camel_object_trigger_event(job->folder, "folder_changed", job->u.refresh_info.changes);
+		camel_change_info_clear(job->u.refresh_info.changes);*/
+
+		/* If we have any new messages, download their headers, but only a few (100?) at a time */
+		if (count) {
+			imapx_uidset_init(&job->u.refresh_info.uidset, 100, 0);
+			imapx_job_refresh_info_step_done(is, ic);
+			return;
+		}
+	} else {
+		camel_exception_setv(job->ex, 1, "Error retriving message: %s", ic->status->text);
+	}
+
+	for (i=0;i<infos->len;i++) {
+		struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
+
+		g_free(r->uid);
+	}
+	g_array_free(job->u.refresh_info.infos, TRUE);
+	e_dlist_remove((EDListNode *)job);
+	e_msgport_reply((EMsg *)job);
+}
+
+static void
+imapx_job_refresh_info_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	CamelIMAPXCommand *ic;
+
+	// temp ... we shouldn't/dont want to force select?  or do we?
+	//imapx_select(is, job->folder);
+	ic = camel_imapx_command_new("FETCH", job->folder->full_name,
+				     "FETCH 1:* (UID FLAGS)");
+	ic->job = job;
+	ic->complete = imapx_job_refresh_info_done;
+	job->u.refresh_info.infos = g_array_new(0, 0, sizeof(struct _refresh_info));
+	imapx_command_queue(is, ic);
+}
+
+/* ********************************************************************** */
+
+static void
+imapx_job_expunge_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	CamelIMAPXCommand *ic;
+
+	//imapx_select(is, job->folder);
+	ic = camel_imapx_command_new("EXPUNGE", job->folder->full_name, "EXPUNGE");
+	ic->job = job;
+	ic->complete = imapx_job_done;
+	imapx_command_queue(is, ic);
+}
+
+/* ********************************************************************** */
+
+static void
+imapx_job_list_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	CamelIMAPXCommand *ic;
+
+	ic = camel_imapx_command_new("LIST", NULL, "%s \"\" %s",
+				     (job->u.list.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)?"LSUB":"LIST",
+				     job->u.list.pattern);
+	ic->pri = job->pri;
+	ic->job = job;
+	ic->complete = imapx_job_done;
+	imapx_command_queue(is, ic);
+}
+
+/* ********************************************************************** */
+
+/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */
+static struct {
+	char *name;
+	guint32 flag;
+} flags_table[] = {
+	{ "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
+	{ "\\DELETED", CAMEL_MESSAGE_DELETED },
+	{ "\\DRAFT", CAMEL_MESSAGE_DRAFT },
+	{ "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
+	{ "\\SEEN", CAMEL_MESSAGE_SEEN },
+	/* { "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT }, */
+};
+
+/*
+  flags 00101000
+ sflags 01001000
+ ^      01100000
+~flags  11010111
+&       01000000
+
+&flags  00100000
+*/
+
+static void
+imapx_job_sync_changes_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
+{
+	CamelIMAPXJob *job = ic->job;
+
+	job->commands--;
+
+	/* If this worked, we should really just update the changes that we sucessfully
+	   stored, so we dont have to worry about sending them again ...
+	   But then we'd have to track which uid's we actually updated, so its easier
+	   just to refresh all of the ones we got.
+
+	   Not that ... given all the asynchronicity going on, we're guaranteed
+	   that what we just set is actually what is on the server now .. but
+	   if it isn't, i guess we'll fix up next refresh */
+	   
+	if (ic->status->result != IMAP_OK && !camel_exception_is_set(job->ex))
+		camel_exception_setv(job->ex, 1, "Error syncing changes: %s", ic->status->text);
+
+	if (job->commands == 0) {
+		if (!camel_exception_is_set(job->ex)) {
+			int i;
+/*
+			for (i=0;i<job->u.sync_changes.infos->len;i++) {
+				CamelIMAPXMessageInfo *info = job->u.sync_changes.infos->pdata[i];
+
+				info->server_flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
+
+				/* FIXME: move over user flags too 
+			}*/ 
+		}
+		e_dlist_remove((EDListNode *)job);
+		e_msgport_reply((EMsg *)job);
+	}
+}
+
+static void
+imapx_job_sync_changes_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	guint32 i, j;
+	struct _uidset_state ss;
+	GPtrArray *infos = job->u.sync_changes.infos;
+	int on;
+
+	for (on=0;on<2;on++) {
+		guint32 orset = on?job->u.sync_changes.on_set:job->u.sync_changes.off_set;
+		GArray *user_set = on?job->u.sync_changes.on_user:job->u.sync_changes.off_user;
+
+		for (j=0;j<sizeof(flags_table)/sizeof(flags_table[0]);j++) {
+			guint32 flag = flags_table[j].flag;
+			CamelIMAPXCommand *ic = NULL;
+
+			if ((orset & flag) == 0)
+				continue;
+
+			printf("checking/storing %s flags '%s'\n", on?"on":"off", flags_table[j].name);
+			imapx_uidset_init(&ss, 0, 100);
+			for (i=0;i<infos->len;i++) {
+				CamelIMAPXMessageInfo *info = infos->pdata[i];
+				guint32 flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
+				guint32 sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
+				int send = 0;
+
+				if ( (on && (((flags ^ sflags) & flags) & flag))
+				     || (!on && (((flags ^ sflags) & ~flags) & flag))) {
+					if (ic == NULL) {
+						ic = camel_imapx_command_new("STORE", job->folder->full_name, "UID STORE ");
+						ic->complete = imapx_job_sync_changes_done;
+						ic->job = job;
+						ic->pri = job->pri;
+					}
+					send = imapx_uidset_add(&ss, ic, camel_message_info_uid(info));
+				}
+				if (send || (i == infos->len-1 && imapx_uidset_done(&ss, ic))) {
+					job->commands++;
+					camel_imapx_command_add(ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name);
+					imapx_command_queue(is, ic);
+					ic = NULL;
+				}
+			}
+		}
+
+		if (user_set) {
+			CamelIMAPXCommand *ic = NULL;
+
+			for (j=0;j<user_set->len;j++) {
+				struct _imapx_flag_change *c = &g_array_index(user_set, struct _imapx_flag_change, i);
+
+				for (i=0;i<c->infos->len;i++) {
+					CamelIMAPXMessageInfo *info = c->infos->pdata[i];
+
+					if (ic == NULL) {
+						ic = camel_imapx_command_new("STORE", job->folder->full_name, "UID STORE ");
+						ic->complete = imapx_job_sync_changes_done;
+						ic->job = job;
+						ic->pri = job->pri;
+					}
+					if (imapx_uidset_add(&ss, ic, camel_message_info_uid(info))
+					    || (i==c->infos->len-1 && imapx_uidset_done(&ss, ic))) {
+						job->commands++;
+						camel_imapx_command_add(ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name);
+						imapx_command_queue(is, ic);
+						ic = NULL;
+					}
+				}
+			}
+		}
+	}
+
+	/* Since this may start in another thread ... we need to
+	   lock the commands count, ho hum */
+
+	if (job->commands == 0) {
+		printf("Hmm, we didn't have any work to do afterall?  hmm, this isn't right\n");
+
+		e_dlist_remove((EDListNode *)job);
+		e_msgport_reply((EMsg *)job);
+	}
+}
+
+/* ********************************************************************** */
+
+static void *
+imapx_server_loop(void *d)
+{
+	CamelIMAPXServer *is = d;
+	CamelIMAPXJob *job;
+
+	/*
+	  The main processing (reading) loop.
+
+	  Incoming requests are added as jobs and tasks from other threads,
+	  we just read the results from the server continously, and match
+	  them up with the queued tasks as they come back.
+
+	  Of course this loop can also initiate its own commands as well.
+
+	  So, multiple threads can submit jobs, and write to the
+	  stream (issue: locking stream for write?), but only this
+	  thread can ever read from the stream.  This simplifies
+	  locking, and greatly simplifies working out when new
+	  work is ready.
+	*/
+
+	printf("imapx server loop started\n");
+
+	// FIXME: handle exceptions
+	while (1) {
+		CAMEL_TRY {
+			if (!is->stream)
+				imapx_reconnect(is);
+
+			job = (CamelIMAPXJob *)e_msgport_get(is->port);
+			if (job) {
+				e_dlist_addtail(&is->jobs, (EDListNode *)job);
+				job->start(is, job);
+			}
+
+			if (!e_dlist_empty(&is->active)
+			    || camel_imapx_stream_buffered(is->stream))
+				imapx_step(is);
+			else
+				e_msgport_wait(is->port);
+#if 0
+			/* TODO:
+			   This poll stuff wont work - we might block
+			   waiting for results inside loops etc.
+
+			   Requires a different approach:
+
+			   New commands are queued in other threads as well
+			   as this thread, and get pipelined over the socket.
+
+			   Main area of locking required is command_queue
+			   and command_start_next, the 'literal' command,
+			   the jobs queue, the active queue, the queue
+			   queue. */
+
+			/* if ssl stream ... */
+#ifdef HAVE_SSL
+			{
+				PRPollDesc pollfds[2] = { };
+				int res;
+
+				printf("\nGoing to sleep waiting for work to do\n\n");
+
+				pollfds[0].fd = camel_tcp_stream_ssl_sockfd((CamelTcpStreamSSL *)is->stream->source);
+				pollfds[0].in_flags = PR_POLL_READ;
+				pollfds[1].fd = e_msgport_prfd(is->port);
+				pollfds[1].in_flags = PR_POLL_READ;
+
+				res = PR_Poll(pollfds, 2, PR_TicksPerSecond() / 10);
+				if (res == -1)
+					sleep(1) /* ?? */ ;
+				else if ((pollfds[0].out_flags & PR_POLL_READ)) {
+					printf(" * woken * have data ready\n");
+					do {
+						/* This is quite shitty, it will often block on each
+						   part of the decode, causing significant
+						   processing delays. */
+						imapx_step(is);
+					} while (camel_imapx_stream_buffered(is->stream));
+				} else if (pollfds[1].out_flags & PR_POLL_READ) {
+					printf(" * woken * have new job\n");
+					/* job is handled in main loop */
+				}
+			}
+#else
+			{
+				struct pollfd[2] = { 0 };
+				int res;
+
+				pollfd[0].fd = ((CamelTcpStreamRaw *)is->stream->source)->sockfd;
+				pollfd[0].events = POLLIN;
+				pollfd[1].fd = e_msgport_fd(is->port);
+				pollfd[1].events = POLLIN;
+
+				res = poll(pollfd, 2, 1000*30);
+				if (res == -1)
+					sleep(1) /* ?? */ ;
+				else if (res == 0)
+					/* timed out */;
+				else if (pollfds[0].revents & POLLIN) {
+					do {
+						imapx_step(is);
+					} while (camel_imapx_stream_buffered(is->stream));
+				}
+			}
+#endif
+#endif
+		} CAMEL_CATCH(e) {
+			printf("######### Got main loop exception: %s\n", e->desc);
+			sleep(1);
+		} CAMEL_DONE;
+	}
+
+	return NULL;
+}
+
+static void
+imapx_server_class_init(CamelIMAPXServerClass *ieclass)
+{
+	ieclass->tagprefix = 'A';
+
+//	camel_object_class_add_event((CamelObjectClass *)ieclass, "status", NULL);
+}
+
+static void
+imapx_server_init(CamelIMAPXServer *ie, CamelIMAPXServerClass *ieclass)
+{
+	e_dlist_init(&ie->queue);
+	e_dlist_init(&ie->active);
+	e_dlist_init(&ie->done);
+	e_dlist_init(&ie->jobs);
+
+	ie->queue_lock = g_mutex_new();
+
+	ie->tagprefix = ieclass->tagprefix;
+	ieclass->tagprefix++;
+	if (ieclass->tagprefix > 'Z')
+		ieclass->tagprefix = 'A';
+	ie->tagprefix = 'A';
+
+	ie->state = IMAPX_DISCONNECTED;
+
+	ie->port = e_msgport_new();
+
+	ie->expunged = g_array_new(FALSE, FALSE, sizeof(guint32));
+}
+
+static void
+imapx_server_finalise(CamelIMAPXServer *ie, CamelIMAPXServerClass *ieclass)
+{
+	g_mutex_free(ie->queue_lock);
+
+	g_array_free(ie->expunged, TRUE);
+}
+
+CamelType
+camel_imapx_server_get_type (void)
+{
+	static CamelType type = CAMEL_INVALID_TYPE;
+
+	if (type == CAMEL_INVALID_TYPE) {
+		type = camel_type_register (
+			camel_object_get_type (),
+			"CamelIMAPXServer",
+			sizeof (CamelIMAPXServer),
+			sizeof (CamelIMAPXServerClass),
+			(CamelObjectClassInitFunc) imapx_server_class_init,
+			NULL,
+			(CamelObjectInitFunc) imapx_server_init,
+			(CamelObjectFinalizeFunc) imapx_server_finalise);
+	}
+	
+	return type;
+}
+
+CamelIMAPXServer *
+camel_imapx_server_new(CamelStore *store, CamelURL *url)
+{
+	CamelIMAPXServer *is = (CamelIMAPXServer *)camel_object_new(camel_imapx_server_get_type());
+
+	is->session = ((CamelService *)store)->session;
+	camel_object_ref(is->session);
+	is->store = store;
+
+	is->url = camel_url_copy(url);
+	camel_url_set_user(is->url, "camel");
+	camel_url_set_passwd(is->url, "camel");
+
+	return is;
+}
+
+/* Client commands */
+
+void
+camel_imapx_server_connect(CamelIMAPXServer *is, int state)
+{
+	if (state) {
+		pthread_t id;
+
+		pthread_create(&id, NULL, imapx_server_loop, is);
+	} else {
+		/* tell processing thread to die, and wait till it does? */
+	}
+}
+
+static void
+imapx_run_job(CamelIMAPXServer *is, CamelIMAPXJob *job)
+{
+	EMsgPort *reply;
+
+	if (!job->noreply) {
+		reply = e_msgport_new();
+		job->msg.reply_port = reply;
+	}
+
+	/* Umm, so all these jobs 'select' first, which means reading(!)
+	   we can't read from this thread ... hrm ... */
+	if (FALSE /*is->state >= IMAPX_AUTHENTICATED*/) {
+		/* NB: Must catch exceptions, cleanup/etc if we fail here? */
+		QUEUE_LOCK(is);
+		e_dlist_addhead(&is->jobs, (EDListNode *)job);
+		QUEUE_UNLOCK(is);
+		job->start(is, job);
+	} else {
+		e_msgport_put(is->port, (EMsg *)job);
+	}
+
+	if (!job->noreply) {
+		e_msgport_wait(reply);
+		g_assert(e_msgport_get(reply) == (EMsg *)job);
+		e_msgport_destroy(reply);
+	}
+}
+
+static CamelStream *
+imapx_server_get_message(CamelIMAPXServer *is, CamelFolder *folder, const char *uid, int pri, CamelException *ex)
+{
+	CamelStream *stream;
+	CamelIMAPXJob *job;
+	char *tmp, *name;
+
+	/* Get a message, we either get it from the local cache,
+	   Or we ask for it, which will put it in the local cache,
+	   then return that copy */
+
+	/* FIXME: The storage logic should use camel-data-cache,
+	   which handles concurrent adds properly.
+	   EXCEPT!  It wont handle the 'new' dir directly ... do we care? */
+
+	name = imapx_get_path_uid(is, folder, NULL, uid);
+	stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0);
+	if (stream) {
+		g_free(name);
+		return stream;
+	} else if (strchr(uid, '-')) {
+		camel_exception_setv(ex, 2, "Offline message vanished from disk: %s", uid);
+		g_free(name);
+		camel_object_unref(stream);
+		return NULL;
+	}
+
+	tmp = imapx_get_path_uid(is, folder, "tmp", uid);
+
+	job = g_malloc0(sizeof(*job));
+	job->pri = pri;
+	job->type = IMAPX_JOB_GET_MESSAGE;
+	job->start = imapx_job_get_message_start;
+	job->folder = folder;
+	job->u.get_message.uid = (char *)uid;
+	job->u.get_message.stream = camel_stream_fs_new_with_name(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+	if (job->u.get_message.stream == NULL) {
+		g_free(tmp);
+		tmp = NULL;
+		job->u.get_message.stream = camel_stream_mem_new();
+	}
+	job->ex = ex;
+
+	imapx_run_job(is, job);
+
+	stream = job->u.get_message.stream;
+	g_free(job);
+
+	if (stream) {
+		if (tmp == NULL)
+			camel_stream_reset(stream);
+		else {
+			if (camel_stream_flush(stream) == 0 && camel_stream_close(stream) == 0) {
+				camel_object_unref(stream);
+				stream = NULL;
+				if (link(tmp, name) == 0)
+					stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0);
+			} else {
+				camel_exception_setv(ex, 1, "closing tmp stream failed: %s", g_strerror(errno));
+				camel_object_unref(stream);
+				stream = NULL;
+			}
+			unlink(tmp);
+		}
+	}
+
+	g_free(tmp);
+	g_free(name);
+
+	return stream;
+}
+
+CamelStream *
+camel_imapx_server_get_message(CamelIMAPXServer *is, CamelFolder *folder, const char *uid, CamelException *ex)
+{
+	return imapx_server_get_message(is, folder, uid, 100, ex);
+}
+
+void
+camel_imapx_server_append_message(CamelIMAPXServer *is, CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *mi, CamelException *ex)
+{
+	char *uid = NULL, *tmp = NULL, *new = NULL;
+	CamelStream *stream, *filter;
+	CamelMimeFilter *canon;
+	CamelIMAPXJob *job;
+	CamelMessageInfo *info;
+	int res;
+
+	/* Append just assumes we have no/a dodgy connection.  We dump stuff into the 'new'
+	   directory, and let the summary know it's there.  Then we fire off a no-reply
+	   job which will asynchronously upload the message at some point in the future,
+	   and fix up the summary to match */
+
+	// FIXME: assign a real uid!  start with last known uid, add some maildir-like stuff?
+	do {
+		static int nextappend;
+
+		g_free(uid);
+		g_free(tmp);
+		uid = g_strdup_printf("%s-%d", "1000", nextappend++);
+		tmp = imapx_get_path_uid(is, folder, "tmp", uid);
+		stream = camel_stream_fs_new_with_name(tmp, O_WRONLY|O_CREAT|O_EXCL, 0666);
+	} while (stream == NULL && (errno == EINTR || errno == EEXIST));
+
+	if (stream == NULL) {
+		camel_exception_setv(ex, 2, "Cannot create spool file: %s", g_strerror(errno));
+		goto fail;
+	}
+
+	filter = (CamelStream *)camel_stream_filter_new_with_stream(stream);
+	camel_object_unref(stream);
+	canon = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_CRLF);
+	camel_stream_filter_add((CamelStreamFilter *)filter, canon);
+	res = camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, filter);
+	camel_object_unref(canon);
+	camel_object_unref(filter);
+	
+	if (res == -1) {
+		camel_exception_setv(ex, 2, "Cannot create spool file: %s", g_strerror(errno));
+		goto fail;
+	}
+
+	new = imapx_get_path_uid(is, folder, "new", uid);
+	if (link(tmp, new) == -1) {
+		camel_exception_setv(ex, 2, "Cannot create spool file: %s", g_strerror(errno));
+		goto fail;
+	}
+
+	info = camel_message_info_new_from_message(folder->summary, message, mi);
+	info->uid = uid;
+	uid = NULL;
+	camel_folder_summary_add(folder->summary, info);
+
+	// FIXME
+
+	/* So, we actually just want to let the server loop that
+	   messages need appending, i think.  This is so the same
+	   mechanism is used for normal uploading as well as
+	   offline re-syncing when we go back online */
+
+	job = g_malloc0(sizeof(*job));
+	job->pri = -60;
+	job->type = IMAPX_JOB_APPEND_MESSAGE;
+	job->noreply = 1;
+	job->start = imapx_job_append_message_start;
+	job->folder = folder;
+	camel_object_ref(folder);
+	job->u.append_message.info = info;
+	job->u.append_message.path = new;
+	new = NULL;
+
+	imapx_run_job(is, job);
+fail:
+	if (tmp)
+		unlink(tmp);
+	g_free(uid);
+	g_free(tmp);
+	g_free(new);
+}
+
+#include "camel-imapx-store.h"
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static CamelIterator *threaditer;
+static CamelFolder *threadfolder;
+
+static void *
+getmsgs(void *d)
+{
+	CamelIMAPXServer *is = ((CamelIMAPXStore *)threadfolder->parent_store)->server;
+	const CamelMessageInfo *info;
+	CamelException ex = { 0 };
+
+	/* FIXME: detach? */
+
+	printf("Checking thread, downloading messages in the background ...\n");
+
+	pthread_mutex_lock(&lock);
+	while ((info = imapx_iterator_next(threaditer, NULL))) {
+		char *cur = imapx_get_path_uid(is, threadfolder, NULL, camel_message_info_uid(info));
+		char *uid = g_strdup(camel_message_info_uid(info));
+		struct stat st;
+
+		pthread_mutex_unlock(&lock);
+
+		if (stat(cur, &st) == -1 && errno == ENOENT) {
+			CamelStream *stream;
+
+			printf(" getting uncached message '%s'\n", uid);
+			stream = imapx_server_get_message(is, threadfolder, uid, -100, &ex);
+			if (stream)
+				camel_object_unref(stream);
+			camel_exception_clear(&ex);
+		} else
+			printf(" already cached message '%s'\n", uid);
+
+		g_free(uid);
+		g_free(cur);
+		pthread_mutex_lock(&lock);
+	}
+	pthread_mutex_unlock(&lock);
+
+	return NULL;
+}
+
+void
+camel_imapx_server_refresh_info(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
+{
+	CamelIMAPXJob *job;
+
+	job = g_malloc0(sizeof(*job));
+	job->type = IMAPX_JOB_REFRESH_INFO;
+	job->start = imapx_job_refresh_info_start;
+	job->folder = folder;
+	job->ex = ex;
+//	job->u.refresh_info.changes = camel_change_info_new(NULL);
+
+	imapx_run_job(is, job);
+
+/*	if (camel_change_info_changed(job->u.refresh_info.changes))
+		camel_object_trigger_event(folder, "folder_changed", job->u.refresh_info.changes);
+	camel_change_info_free(job->u.refresh_info.changes);*/
+
+	g_free(job);
+
+	{
+		int i;
+		int c = 3;
+		pthread_t ids[10];
+
+		threadfolder = folder;
+		threaditer = camel_folder_search(folder, NULL, NULL, NULL, NULL);
+		for (i=0;i<c;i++)
+			pthread_create(&ids[i], NULL, getmsgs, NULL);
+
+		for (i=0;i<c;i++)
+			pthread_join(ids[i], NULL);
+		camel_iterator_free(threaditer);
+	}
+}
+
+static void
+imapx_sync_free_user(GArray *user_set)
+{
+	int i;
+
+	if (user_set == NULL)
+		return;
+
+	for (i=0;i<user_set->len;i++)
+		g_ptr_array_free(g_array_index(user_set, struct _imapx_flag_change, i).infos, TRUE);
+	g_array_free(user_set, TRUE);
+}
+
+void
+camel_imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, GPtrArray *infos, CamelException *ex)
+{
+	guint i, on_orset, off_orset;
+	GArray *on_user = NULL, *off_user = NULL;
+	CamelIMAPXMessageInfo *info;
+	CamelIMAPXJob *job;
+
+	/* We calculate two masks, a mask of all flags which have been
+	   turned off and a mask of all flags which have been turned
+	   on. If either of these aren't 0, then we have work to do,
+	   and we fire off a job to do it.
+
+	   User flags are a bit more tricky, we rely on the user
+	   flags being sorted, and then we create a bunch of lists;
+	   one for each flag being turned off, including each
+	   info being turned off, and one for each flag being turned on.
+	*/
+
+	off_orset = on_orset = 0;
+	for (i=0;i<infos->len;i++) {
+		guint32 flags, sflags;
+		CamelFlag *uflags, *suflags;
+
+		info = infos->pdata[i];
+		flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
+		sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
+		if (flags != sflags) {
+			off_orset |= ( flags ^ sflags ) & ~flags;
+			on_orset |= (flags ^ sflags) & flags;
+		}
+
+		uflags = ((CamelMessageInfoBase *)info)->user_flags;
+		suflags = info->server_flags; /*check*/
+		while (uflags || suflags) {
+			int res;
+
+			if (uflags) {
+				if (suflags)
+					res = strcmp(uflags->name, suflags->name);
+				else
+					res = -1;
+			} else {
+				res = 1;
+			}
+
+			if (res == 0) {
+				uflags = uflags->next;
+				suflags = suflags->next;
+			} else {
+				GArray *user_set;
+				CamelFlag *user_flag;
+				struct _imapx_flag_change *change, add;
+
+				if (res < 0) {
+					if (on_user == NULL)
+						on_user = g_array_new(sizeof(*change), 0, 0);
+					user_set = on_user;
+					user_flag = uflags;
+					uflags = uflags->next;
+				} else {
+					if (off_user == NULL)
+						off_user = g_array_new(sizeof(*change), 0, 0);
+					user_set = off_user;
+					user_flag = suflags;
+					suflags = suflags->next;
+				}
+
+				/* Could sort this and binary search */
+				for (i=0;i<user_set->len;i++) {
+					change = &g_array_index(user_set, struct _imapx_flag_change, i);
+					if (strcmp(change->name, user_flag->name) == 0)
+						goto found;
+				}
+				add.name = g_strdup(user_flag->name);
+				add.infos = g_ptr_array_new();
+				g_array_append_val(user_set, add);
+				change = &add;
+			found:
+				g_ptr_array_add(change->infos, info);
+			}
+		}
+	}
+
+	if ((on_orset|off_orset) == 0 && on_user == NULL && off_user == NULL)
+		return;
+
+	job = g_malloc0(sizeof(*job));
+	job->type = IMAPX_JOB_SYNC_CHANGES;
+	job->start = imapx_job_sync_changes_start;
+	job->pri = -50;
+	job->folder = folder;
+	job->ex = ex;
+	job->u.sync_changes.infos = infos;
+	job->u.sync_changes.on_set = on_orset;
+	job->u.sync_changes.off_set = off_orset;
+	job->u.sync_changes.on_user = on_user;
+	job->u.sync_changes.off_user = off_user;
+
+	imapx_run_job(is, job);
+
+	g_free(job);
+
+	imapx_sync_free_user(on_user);
+	imapx_sync_free_user(off_user);
+}
+
+/* expunge-uids? */
+void
+camel_imapx_server_expunge(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
+{
+	CamelIMAPXJob *job;
+
+	/* Do we really care to wait for this one to finish? */
+
+	job = g_malloc0(sizeof(*job));
+	job->type = IMAPX_JOB_EXPUNGE;
+	job->start = imapx_job_expunge_start;
+	job->pri = -120;
+	job->folder = folder;
+	job->ex = ex;
+
+	imapx_run_job(is, job);
+
+	g_free(job);
+}
+
+static guint
+imapx_name_hash(gconstpointer key)
+{
+	if (g_ascii_strcasecmp(key, "INBOX") == 0)
+		return g_str_hash("INBOX");
+	else
+		return g_str_hash(key);
+}
+
+static gint
+imapx_name_equal(gconstpointer a, gconstpointer b)
+{
+	gconstpointer aname = a, bname = b;
+
+	if (g_ascii_strcasecmp(a, "INBOX") == 0)
+		aname = "INBOX";
+	if (g_ascii_strcasecmp(b, "INBOX") == 0)
+		bname = "INBOX";
+	return g_str_equal(aname, bname);
+}
+
+static void
+imapx_list_flatten(void *k, void *v, void *d)
+{
+	GPtrArray *folders = d;
+
+	g_ptr_array_add(folders, v);
+}
+
+static int
+imapx_list_cmp(const void *ap, const void *bp)
+{
+	struct _list_info *a = ((struct _list_info **)ap)[0];
+	struct _list_info *b = ((struct _list_info **)bp)[0];
+
+	return strcmp(a->name, b->name);
+}
+
+GPtrArray *
+camel_imapx_server_list(CamelIMAPXServer *is, const char *top, guint32 flags, CamelException *ex)
+{
+	CamelIMAPXJob *job;
+	GPtrArray *folders;
+
+	job = g_malloc0(sizeof(*job));
+	job->type = IMAPX_JOB_LIST;
+	job->start = imapx_job_list_start;
+	job->pri = -80;
+	job->ex = ex;
+	job->u.list.flags = flags;
+	job->u.list.folders = g_hash_table_new(imapx_name_hash, imapx_name_equal);
+	job->u.list.pattern = g_alloca(strlen(top)+5);
+	if (flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)
+		sprintf(job->u.list.pattern, "%s*", top);
+	else
+		sprintf(job->u.list.pattern, "%s", top);
+
+	imapx_run_job(is, job);
+
+	folders = g_ptr_array_new();
+	g_hash_table_foreach(job->u.list.folders, imapx_list_flatten, folders);
+	g_hash_table_destroy(job->u.list.folders);
+
+	g_free(job);
+
+	qsort(folders->pdata, folders->len, sizeof(folders->pdata[0]), imapx_list_cmp);
+
+	return folders;
+}
diff --git a/camel/providers/imapx/camel-imapx-server.h b/camel/providers/imapx/camel-imapx-server.h
new file mode 100644
index 0000000..633606c
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-server.h
@@ -0,0 +1,105 @@
+/*
+ *  Copyright (C) 2005 Novell Inc.
+ *
+ *  Authors:
+ *    Michael Zucchi <notzed ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _CAMEL_IMAPX_SERVER_H
+#define _CAMEL_IMAPX_SERVER_H
+
+#include <libedataserver/e-msgport.h>
+
+struct _CamelFolder;
+struct _CamelException;
+struct _CamelMimeMessage;
+struct _CamelMessageInfo;
+
+#define CAMEL_IMAPX_SERVER(obj)         CAMEL_CHECK_CAST (obj, camel_imapx_server_get_type (), CamelIMAPPServer)
+#define CAMEL_IMAPX_SERVER_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapx_server_get_type (), CamelIMAPPServerClass)
+#define CAMEL_IS_IMAPX_SERVER(obj)      CAMEL_CHECK_TYPE (obj, camel_imapx_server_get_type ())
+
+typedef struct _CamelIMAPXServer CamelIMAPXServer;
+typedef struct _CamelIMAPXServerClass CamelIMAPXServerClass;
+
+#define IMAPX_MODE_READ (1<<0)
+#define IMAPX_MODE_WRITE (1<<1)
+
+struct _CamelIMAPXServer {
+	CamelObject cobject;
+
+	struct _CamelStore *store;
+	struct _CamelSession *session;
+
+	/* Info about the current connection */
+	struct _CamelURL *url;
+	struct _CamelIMAPXStream *stream;
+	struct _capability_info *cinfo;
+
+	/* incoming jobs */
+	EMsgPort *port;
+	EDList jobs;
+
+	char tagprefix;
+	int state:4;
+
+	/* Current command/work queue.  All commands are stored in one list,
+	   all the time, so they can be cleaned up in exception cases */
+	void *queue_lock;
+	struct _CamelIMAPXCommand *literal;
+	EDList queue;
+	EDList active;
+	EDList done;
+
+	/* info on currently selected folder */
+	struct _CamelFolder *select_folder;
+	char *select;
+	struct _CamelFolderChangeInfo *changes;
+	struct _CamelFolder *select_pending;
+	guint32 permanentflags;
+	guint32 uidvalidity;
+	guint32 unseen;
+	guint32 exists;
+	guint32 recent;
+	guint32 mode;
+
+	/* any expunges that happened from the last command, they are
+	   processed after the command completes. */
+	GArray *expunged;
+};
+
+struct _CamelIMAPXServerClass {
+	CamelObjectClass cclass;
+
+	char tagprefix;
+};
+
+CamelType               camel_imapx_server_get_type     (void);
+CamelIMAPXServer *camel_imapx_server_new(struct _CamelStore *store, struct _CamelURL *url);
+
+void camel_imapx_server_connect(CamelIMAPXServer *is, int state);
+
+GPtrArray *camel_imapx_server_list(CamelIMAPXServer *is, const char *top, guint32 flags, CamelException *ex);
+
+void camel_imapx_server_refresh_info(CamelIMAPXServer *is, CamelFolder *folder, struct _CamelException *ex);
+void camel_imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, GPtrArray *infos, CamelException *ex);
+void camel_imapx_server_expunge(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex);
+
+CamelStream *camel_imapx_server_get_message(CamelIMAPXServer *is, CamelFolder *folder, const char *uid, struct _CamelException *ex);
+void camel_imapx_server_append_message(CamelIMAPXServer *is, CamelFolder *folder, struct _CamelMimeMessage *message, const struct _CamelMessageInfo *mi, CamelException *ex);
+
+#endif /* ! _CAMEL_IMAPX_SERVER_H */
diff --git a/camel/providers/imapx/camel-imapx-store-summary.c b/camel/providers/imapx/camel-imapx-store-summary.c
new file mode 100644
index 0000000..04f49c2
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-store-summary.c
@@ -0,0 +1,624 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * Authors: Michael Zucchi <notzed ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "camel-file-utils.h"
+#include "camel-private.h"
+#include "camel-store.h"
+#include "camel-utf8.h"
+
+#include "camel-imapx-store-summary.h"
+
+#define d(x)
+#define io(x)			/* io debug */
+
+#define CAMEL_IMAPX_STORE_SUMMARY_VERSION_0 (0)
+
+#define CAMEL_IMAPX_STORE_SUMMARY_VERSION (0)
+
+#define _PRIVATE(o) (((CamelIMAPXStoreSummary *)(o))->priv)
+
+static void namespace_clear(CamelStoreSummary *s);
+
+static gint summary_header_load(CamelStoreSummary *, FILE *);
+static gint summary_header_save(CamelStoreSummary *, FILE *);
+
+/*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const gchar *);*/
+static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
+static gint		 store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *);
+static void		 store_info_free(CamelStoreSummary *, CamelStoreInfo *);
+
+static const gchar *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, gint);
+static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const gchar *);
+
+static void camel_imapx_store_summary_class_init (CamelIMAPXStoreSummaryClass *klass);
+static void camel_imapx_store_summary_init       (CamelIMAPXStoreSummary *obj);
+static void camel_imapx_store_summary_finalise   (CamelObject *obj);
+
+static CamelStoreSummaryClass *camel_imapx_store_summary_parent;
+
+static void
+camel_imapx_store_summary_class_init (CamelIMAPXStoreSummaryClass *klass)
+{
+	CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass;
+
+	ssklass->summary_header_load = summary_header_load;
+	ssklass->summary_header_save = summary_header_save;
+
+	/*ssklass->store_info_new  = store_info_new;*/
+	ssklass->store_info_load = store_info_load;
+	ssklass->store_info_save = store_info_save;
+	ssklass->store_info_free = store_info_free;
+
+	ssklass->store_info_string = store_info_string;
+	ssklass->store_info_set_string = store_info_set_string;
+}
+
+static void
+camel_imapx_store_summary_init (CamelIMAPXStoreSummary *s)
+{
+	/*struct _CamelImapStoreSummaryPrivate *p;
+
+	  p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/
+
+	((CamelStoreSummary *)s)->store_info_size = sizeof(CamelIMAPXStoreInfo);
+	s->version = CAMEL_IMAPX_STORE_SUMMARY_VERSION;
+}
+
+static void
+camel_imapx_store_summary_finalise (CamelObject *obj)
+{
+	/*struct _CamelImapStoreSummaryPrivate *p;*/
+	/*CamelImapStoreSummary *s = (CamelImapStoreSummary *)obj;*/
+
+	/*p = _PRIVATE(obj);
+	  g_free(p);*/
+}
+
+CamelType
+camel_imapx_store_summary_get_type (void)
+{
+	static CamelType type = CAMEL_INVALID_TYPE;
+
+	if (type == CAMEL_INVALID_TYPE) {
+		camel_imapx_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
+		type = camel_type_register((CamelType)camel_imapx_store_summary_parent, "CamelIMAPXStoreSummary",
+					   sizeof (CamelIMAPXStoreSummary),
+					   sizeof (CamelIMAPXStoreSummaryClass),
+					   (CamelObjectClassInitFunc) camel_imapx_store_summary_class_init,
+					   NULL,
+					   (CamelObjectInitFunc) camel_imapx_store_summary_init,
+					   (CamelObjectFinalizeFunc) camel_imapx_store_summary_finalise);
+	}
+
+	return type;
+}
+
+/**
+ * camel_imapx_store_summary_new:
+ *
+ * Create a new CamelIMAPXStoreSummary object.
+ *
+ * Return value: A new CamelIMAPXStoreSummary widget.
+ **/
+CamelIMAPXStoreSummary *
+camel_imapx_store_summary_new (void)
+{
+	CamelIMAPXStoreSummary *new = CAMEL_IMAPX_STORE_SUMMARY ( camel_object_new (camel_imapx_store_summary_get_type ()));
+
+	return new;
+}
+
+/**
+ * camel_imapx_store_summary_full_name:
+ * @s:
+ * @full_name:
+ *
+ * Retrieve a summary item by full name.
+ *
+ * A referenced to the summary item is returned, which may be
+ * ref'd or free'd as appropriate.
+ *
+ * Return value: The summary item, or NULL if the @full_name name
+ * is not available.
+ * It must be freed using camel_store_summary_info_free().
+ **/
+CamelIMAPXStoreInfo *
+camel_imapx_store_summary_full_name(CamelIMAPXStoreSummary *s, const gchar *full_name)
+{
+	gint count, i;
+	CamelIMAPXStoreInfo *info;
+
+	count = camel_store_summary_count((CamelStoreSummary *)s);
+	for (i=0;i<count;i++) {
+		info = (CamelIMAPXStoreInfo *)camel_store_summary_index((CamelStoreSummary *)s, i);
+		if (info) {
+			if (strcmp(info->full_name, full_name) == 0)
+				return info;
+			camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
+		}
+	}
+
+	return NULL;
+}
+
+gchar *
+camel_imapx_store_summary_full_to_path(CamelIMAPXStoreSummary *s, const gchar *full_name, gchar dir_sep)
+{
+	gchar *path, *p;
+	gint c;
+	const gchar *f;
+
+	if (dir_sep != '/') {
+		p = path = alloca(strlen(full_name)*3+1);
+		f = full_name;
+		while ( (c = *f++ & 0xff) ) {
+			if (c == dir_sep)
+				*p++ = '/';
+			else if (c == '/' || c == '%')
+				p += sprintf(p, "%%%02X", c);
+			else
+				*p++ = c;
+		}
+		*p = 0;
+	} else
+		path = (gchar *)full_name;
+
+	return g_strdup(path);
+}
+
+static guint32 hexnib(guint32 c)
+{
+	if (c >= '0' && c <= '9')
+		return c-'0';
+	else if (c>='A' && c <= 'Z')
+		return c-'A'+10;
+	else
+		return 0;
+}
+
+gchar *
+camel_imapx_store_summary_path_to_full(CamelIMAPXStoreSummary *s, const gchar *path, gchar dir_sep)
+{
+	gchar *full, *f;
+	guint32 c, v = 0;
+	const gchar *p;
+	gint state=0;
+	gchar *subpath, *last = NULL;
+	CamelStoreInfo *si;
+	CamelIMAPXStoreNamespace *ns;
+
+	/* check to see if we have a subpath of path already defined */
+	subpath = alloca(strlen(path)+1);
+	strcpy(subpath, path);
+	do {
+		si = camel_store_summary_path((CamelStoreSummary *)s, subpath);
+		if (si == NULL) {
+			last = strrchr(subpath, '/');
+			if (last)
+				*last = 0;
+		}
+	} while (si == NULL && last);
+
+	/* path is already present, use the raw version we have */
+	if (si && strlen(subpath) == strlen(path)) {
+		f = g_strdup(camel_imapx_store_info_full_name(s, si));
+		camel_store_summary_info_free((CamelStoreSummary *)s, si);
+		return f;
+	}
+
+	ns = camel_imapx_store_summary_namespace_find_path(s, path);
+
+	f = full = alloca(strlen(path)*2+1);
+	if (si)
+		p = path + strlen(subpath);
+	else if (ns)
+		p = path + strlen(ns->path);
+	else
+		p = path;
+
+	while ((c = camel_utf8_getc((const guchar **)&p))) {
+		switch (state) {
+		case 0:
+			if (c == '%')
+				state = 1;
+			else {
+				if (c == '/')
+					c = dir_sep;
+				camel_utf8_putc((guchar **) &f, c);
+			}
+			break;
+		case 1:
+			state = 2;
+			v = hexnib(c)<<4;
+			break;
+		case 2:
+			state = 0;
+			v |= hexnib(c);
+			camel_utf8_putc((guchar **) &f, v);
+			break;
+		}
+	}
+	camel_utf8_putc((guchar **) &f, c);
+
+	/* merge old path part if required */
+	f = g_strdup(full);
+	if (si) {
+		full = g_strdup_printf("%s%s", camel_imapx_store_info_full_name(s, si), f);
+		g_free(f);
+		camel_store_summary_info_free((CamelStoreSummary *)s, si);
+		f = full;
+	} else if (ns) {
+		full = g_strdup_printf("%s%s", ns->full_name, f);
+		g_free(f);
+		f = full;
+	}
+
+	return f;
+}
+
+CamelIMAPXStoreInfo *
+camel_imapx_store_summary_add_from_full(CamelIMAPXStoreSummary *s, const gchar *full, gchar dir_sep)
+{
+	CamelIMAPXStoreInfo *info;
+	gchar *pathu8, *prefix;
+	gint len;
+	gchar *full_name;
+	CamelIMAPXStoreNamespace *ns;
+
+	d(printf("adding full name '%s' '%c'\n", full, dir_sep));
+
+	len = strlen(full);
+	full_name = alloca(len+1);
+	strcpy(full_name, full);
+	if (full_name[len-1] == dir_sep)
+		full_name[len-1] = 0;
+
+	info = camel_imapx_store_summary_full_name(s, full_name);
+	if (info) {
+		camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
+		d(printf("  already there\n"));
+		return info;
+	}
+
+	ns = camel_imapx_store_summary_namespace_find_full(s, full_name);
+	if (ns) {
+		d(printf("(found namespace for '%s' ns '%s') ", full_name, ns->path));
+		len = strlen(ns->full_name);
+		if (len >= strlen(full_name)) {
+			pathu8 = g_strdup(ns->path);
+		} else {
+			if (full_name[len] == ns->sep)
+				len++;
+
+			prefix = camel_imapx_store_summary_full_to_path(s, full_name+len, ns->sep);
+			if (*ns->path) {
+				pathu8 = g_strdup_printf ("%s/%s", ns->path, prefix);
+				g_free (prefix);
+			} else {
+				pathu8 = prefix;
+			}
+		}
+		d(printf(" (pathu8 = '%s')", pathu8));
+	} else {
+		d(printf("(Cannot find namespace for '%s')\n", full_name));
+		pathu8 = camel_imapx_store_summary_full_to_path(s, full_name, dir_sep);
+	}
+
+	info = (CamelIMAPXStoreInfo *)camel_store_summary_add_from_path((CamelStoreSummary *)s, pathu8);
+	if (info) {
+		d(printf("  '%s' -> '%s'\n", pathu8, full_name));
+		camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_IMAP_STORE_INFO_FULL_NAME, full_name);
+
+		if (!g_ascii_strcasecmp(full_name, "inbox"))
+			info->info.flags |= CAMEL_FOLDER_SYSTEM|CAMEL_FOLDER_TYPE_INBOX;
+	} else {
+		d(printf("  failed\n"));
+	}
+
+	return info;
+}
+
+/* should this be const? */
+/* TODO: deprecate/merge this function with path_to_full */
+gchar *
+camel_imapx_store_summary_full_from_path(CamelIMAPXStoreSummary *s, const gchar *path)
+{
+	CamelIMAPXStoreNamespace *ns;
+	gchar *name = NULL;
+
+	ns = camel_imapx_store_summary_namespace_find_path(s, path);
+	if (ns)
+		name = camel_imapx_store_summary_path_to_full(s, path, ns->sep);
+
+	d(printf("looking up path %s -> %s\n", path, name?name:"not found"));
+
+	return name;
+}
+
+/* TODO: this api needs some more work */
+CamelIMAPXStoreNamespace *camel_imapx_store_summary_namespace_new(CamelIMAPXStoreSummary *s, const gchar *full_name, gchar dir_sep)
+{
+	CamelIMAPXStoreNamespace *ns;
+	gchar *p, *o, c;
+	gint len;
+
+	ns = g_malloc0(sizeof(*ns));
+	ns->full_name = g_strdup(full_name);
+	len = strlen(ns->full_name)-1;
+	if (len >= 0 && ns->full_name[len] == dir_sep)
+		ns->full_name[len] = 0;
+	ns->sep = dir_sep;
+
+	o = p = ns->path = camel_imapx_store_summary_full_to_path(s, ns->full_name, dir_sep);
+	while ((c = *p++)) {
+		if (c != '#') {
+			if (c == '/')
+				c = '.';
+			*o++ = c;
+		}
+	}
+	*o = 0;
+
+	return ns;
+}
+
+void camel_imapx_store_summary_namespace_set(CamelIMAPXStoreSummary *s, CamelIMAPXStoreNamespace *ns)
+{
+	d(printf("Setting namesapce to '%s' '%c' -> '%s'\n", ns->full_name, ns->sep, ns->path));
+	namespace_clear((CamelStoreSummary *)s);
+	s->namespace = ns;
+	camel_store_summary_touch((CamelStoreSummary *)s);
+}
+
+CamelIMAPXStoreNamespace *
+camel_imapx_store_summary_namespace_find_path(CamelIMAPXStoreSummary *s, const gchar *path)
+{
+	gint len;
+	CamelIMAPXStoreNamespace *ns;
+
+	/* NB: this currently only compares against 1 namespace, in future compare against others */
+	ns = s->namespace;
+	while (ns) {
+		len = strlen(ns->path);
+		if (len == 0
+		    || (strncmp(ns->path, path, len) == 0
+			&& (path[len] == '/' || path[len] == 0)))
+			break;
+		ns = NULL;
+	}
+
+	/* have a default? */
+	return ns;
+}
+
+CamelIMAPXStoreNamespace *
+camel_imapx_store_summary_namespace_find_full(CamelIMAPXStoreSummary *s, const gchar *full)
+{
+	gint len;
+	CamelIMAPXStoreNamespace *ns;
+
+	/* NB: this currently only compares against 1 namespace, in future compare against others */
+	ns = s->namespace;
+	while (ns) {
+		len = strlen(ns->full_name);
+		d(printf("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full));
+		if (len == 0
+		    || (strncmp(ns->full_name, full, len) == 0
+			&& (full[len] == ns->sep || full[len] == 0)))
+			break;
+		ns = NULL;
+	}
+
+	/* have a default? */
+	return ns;
+}
+
+static void
+namespace_free(CamelStoreSummary *s, CamelIMAPXStoreNamespace *ns)
+{
+	g_free(ns->path);
+	g_free(ns->full_name);
+	g_free(ns);
+}
+
+static void
+namespace_clear(CamelStoreSummary *s)
+{
+	CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *)s;
+
+	if (is->namespace)
+		namespace_free(s, is->namespace);
+	is->namespace = NULL;
+}
+
+static CamelIMAPXStoreNamespace *
+namespace_load(CamelStoreSummary *s, FILE *in)
+{
+	CamelIMAPXStoreNamespace *ns;
+	guint32 sep = '/';
+
+	ns = g_malloc0(sizeof(*ns));
+	if (camel_file_util_decode_string(in, &ns->path) == -1
+	    || camel_file_util_decode_string(in, &ns->full_name) == -1
+	    || camel_file_util_decode_uint32(in, &sep) == -1) {
+		namespace_free(s, ns);
+		ns = NULL;
+	} else {
+		ns->sep = sep;
+	}
+
+	return ns;
+}
+
+static gint
+namespace_save(CamelStoreSummary *s, FILE *in, CamelIMAPXStoreNamespace *ns)
+{
+	if (camel_file_util_encode_string(in, ns->path) == -1
+	    || camel_file_util_encode_string(in, ns->full_name) == -1
+	    || camel_file_util_encode_uint32(in, (guint32)ns->sep) == -1)
+		return -1;
+
+	return 0;
+}
+
+static gint
+summary_header_load(CamelStoreSummary *s, FILE *in)
+{
+	CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *)s;
+	gint32 version, capabilities, count;
+
+	namespace_clear(s);
+
+	if (camel_imapx_store_summary_parent->summary_header_load((CamelStoreSummary *)s, in) == -1
+	    || camel_file_util_decode_fixed_int32(in, &version) == -1)
+		return -1;
+
+	is->version = version;
+
+	if (version < CAMEL_IMAPX_STORE_SUMMARY_VERSION_0) {
+		g_warning("Store summary header version too low");
+		return -1;
+	}
+
+	/* note file format can be expanded to contain more namespaces, but only 1 at the moment */
+	if (camel_file_util_decode_fixed_int32(in, &capabilities) == -1
+	    || camel_file_util_decode_fixed_int32(in, &count) == -1
+	    || count > 1)
+		return -1;
+
+	is->capabilities = capabilities;
+	if (count == 1) {
+		if ((is->namespace = namespace_load(s, in)) == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+static gint
+summary_header_save(CamelStoreSummary *s, FILE *out)
+{
+	CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *)s;
+	guint32 count;
+
+	count = is->namespace?1:0;
+
+	/* always write as latest version */
+	if (camel_imapx_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1
+	    || camel_file_util_encode_fixed_int32(out, CAMEL_IMAPX_STORE_SUMMARY_VERSION) == -1
+	    || camel_file_util_encode_fixed_int32(out, is->capabilities) == -1
+	    || camel_file_util_encode_fixed_int32(out, count) == -1)
+		return -1;
+
+	if (is->namespace && namespace_save(s, out, is->namespace) == -1)
+		return -1;
+
+	return 0;
+}
+
+static CamelStoreInfo *
+store_info_load(CamelStoreSummary *s, FILE *in)
+{
+	CamelIMAPXStoreInfo *mi;
+
+	mi = (CamelIMAPXStoreInfo *)camel_imapx_store_summary_parent->store_info_load(s, in);
+	if (mi) {
+		if (camel_file_util_decode_string(in, &mi->full_name) == -1) {
+			camel_store_summary_info_free(s, (CamelStoreInfo *)mi);
+			mi = NULL;
+		} else {
+			/* NB: this is done again for compatability */
+			if (g_ascii_strcasecmp(mi->full_name, "inbox") == 0)
+				mi->info.flags |= CAMEL_FOLDER_SYSTEM|CAMEL_FOLDER_TYPE_INBOX;
+		}
+	}
+
+	return (CamelStoreInfo *)mi;
+}
+
+static gint
+store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
+{
+	CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *)mi;
+
+	if (camel_imapx_store_summary_parent->store_info_save(s, out, mi) == -1
+	    || camel_file_util_encode_string(out, isi->full_name) == -1)
+		return -1;
+
+	return 0;
+}
+
+static void
+store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
+{
+	CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *)mi;
+
+	g_free(isi->full_name);
+	camel_imapx_store_summary_parent->store_info_free(s, mi);
+}
+
+static const gchar *
+store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, gint type)
+{
+	CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *)mi;
+
+	/* FIXME: Locks? */
+
+	g_assert (mi != NULL);
+
+	switch (type) {
+	case CAMEL_IMAP_STORE_INFO_FULL_NAME:
+		return isi->full_name;
+	default:
+		return camel_imapx_store_summary_parent->store_info_string(s, mi, type);
+	}
+}
+
+static void
+store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, gint type, const gchar *str)
+{
+	CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *)mi;
+
+	g_assert(mi != NULL);
+
+	switch (type) {
+	case CAMEL_IMAP_STORE_INFO_FULL_NAME:
+		d(printf("Set full name %s -> %s\n", isi->full_name, str));
+		CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
+		g_free(isi->full_name);
+		isi->full_name = g_strdup(str);
+		CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
+		break;
+	default:
+		camel_imapx_store_summary_parent->store_info_set_string(s, mi, type, str);
+		break;
+	}
+}
diff --git a/camel/providers/imapx/camel-imapx-store-summary.h b/camel/providers/imapx/camel-imapx-store-summary.h
new file mode 100644
index 0000000..b1ff121
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-store-summary.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * Authors: Michael Zucchi <notzed ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CAMEL_IMAPX_STORE_SUMMARY_H
+#define _CAMEL_IMAPX_STORE_SUMMARY_H
+
+#include <camel/camel-object.h>
+#include <camel/camel-store-summary.h>
+
+#define CAMEL_IMAPX_STORE_SUMMARY(obj)         CAMEL_CHECK_CAST (obj, camel_iMAPX_store_summary_get_type (), CamelIMAPXStoreSummary)
+#define CAMEL_IMAPX_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_iMAPX_store_summary_get_type (), CamelIMAPXStoreSummaryClass)
+#define CAMEL_IS_IMAPX_STORE_SUMMARY(obj)      CAMEL_CHECK_TYPE (obj, camel_iMAPX_store_summary_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _CamelIMAPXStoreSummary      CamelIMAPXStoreSummary;
+typedef struct _CamelIMAPXStoreSummaryClass CamelIMAPXStoreSummaryClass;
+
+typedef struct _CamelIMAPXStoreInfo CamelIMAPXStoreInfo;
+
+enum {
+	CAMEL_IMAP_STORE_INFO_FULL_NAME = CAMEL_STORE_INFO_LAST,
+	CAMEL_IMAP_STORE_INFO_LAST
+};
+
+struct _CamelIMAPXStoreInfo {
+	CamelStoreInfo info;
+	gchar *full_name;
+};
+
+typedef struct _CamelIMAPXStoreNamespace CamelIMAPXStoreNamespace;
+
+struct _CamelIMAPXStoreNamespace {
+	gchar *path;		/* display path */
+	gchar *full_name;	/* real name */
+	gchar sep;		/* directory separator */
+};
+
+struct _CamelIMAPXStoreSummary {
+	CamelStoreSummary summary;
+
+	struct _CamelIMAPXStoreSummaryPrivate *priv;
+
+	/* header info */
+	guint32 version;	/* version of base part of file */
+	guint32 capabilities;
+	CamelIMAPXStoreNamespace *namespace; /* eventually to be a list */
+};
+
+struct _CamelIMAPXStoreSummaryClass {
+	CamelStoreSummaryClass summary_class;
+};
+
+CamelType			 camel_imapx_store_summary_get_type	(void);
+CamelIMAPXStoreSummary      *camel_iMAPX_store_summary_new	(void);
+
+/* TODO: this api needs some more work, needs to support lists */
+CamelIMAPXStoreNamespace *camel_imapx_store_summary_namespace_new(CamelIMAPXStoreSummary *s, const gchar *full_name, gchar dir_sep);
+void camel_imapx_store_summary_namespace_set(CamelIMAPXStoreSummary *s, CamelIMAPXStoreNamespace *ns);
+CamelIMAPXStoreNamespace *camel_imapx_store_summary_namespace_find_path(CamelIMAPXStoreSummary *s, const gchar *path);
+CamelIMAPXStoreNamespace *camel_imapx_store_summary_namespace_find_full(CamelIMAPXStoreSummary *s, const gchar *full_name);
+
+/* converts to/from utf8 canonical nasmes */
+gchar *camel_imapx_store_summary_full_to_path(CamelIMAPXStoreSummary *s, const gchar *full_name, gchar dir_sep);
+gchar *camel_imapx_store_summary_path_to_full(CamelIMAPXStoreSummary *s, const gchar *path, gchar dir_sep);
+
+CamelIMAPXStoreInfo *camel_imapx_store_summary_full_name(CamelIMAPXStoreSummary *s, const gchar *full_name);
+CamelIMAPXStoreInfo *camel_imapx_store_summary_add_from_full(CamelIMAPXStoreSummary *s, const gchar *full_name, gchar dir_sep);
+
+/* a convenience lookup function. always use this if path known */
+gchar *camel_imapx_store_summary_full_from_path(CamelIMAPXStoreSummary *s, const gchar *path);
+
+/* helpe macro's */
+#define camel_imapx_store_info_full_name(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_IMAP_STORE_INFO_FULL_NAME))
+
+G_END_DECLS
+
+#endif /* ! _CAMEL_IMAP_STORE_SUMMARY_H */
diff --git a/camel/providers/imapx/camel-imapx-store.c b/camel/providers/imapx/camel-imapx-store.c
new file mode 100644
index 0000000..0defd11
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-store.c
@@ -0,0 +1,873 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-imap-store.c : class for a imap store */
+
+/* 
+ * Authors: Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 2000-2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of version 2 of the GNU General Public 
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "camel/camel-operation.h"
+
+#include "camel/camel-stream-buffer.h"
+#include "camel/camel-session.h"
+#include "camel/camel-exception.h"
+#include "camel/camel-url.h"
+#include "camel/camel-sasl.h"
+#include "camel/camel-data-cache.h"
+#include "camel/camel-tcp-stream.h"
+#include "camel/camel-tcp-stream-raw.h"
+#ifdef HAVE_SSL
+#include "camel/camel-tcp-stream-ssl.h"
+#endif
+#include "camel/camel-i18n.h"
+
+#include "camel-imapx-store.h"
+#include "camel-imapx-folder.h"
+#include "camel-imapx-exception.h"
+#include "camel-imapx-utils.h"
+#include "camel-imapx-server.h"
+#include "camel-net-utils.h"
+
+/* Specified in RFC 2060 section 2.1 */
+#define IMAP_PORT 143
+
+static CamelStoreClass *parent_class = NULL;
+
+static guint
+imapx_name_hash(gconstpointer key)
+{
+	if (g_ascii_strcasecmp(key, "INBOX") == 0)
+		return g_str_hash("INBOX");
+	else
+		return g_str_hash(key);
+}
+
+static gint
+imapx_name_equal(gconstpointer a, gconstpointer b)
+{
+	gconstpointer aname = a, bname = b;
+
+	if (g_ascii_strcasecmp(a, "INBOX") == 0)
+		aname = "INBOX";
+	if (g_ascii_strcasecmp(b, "INBOX") == 0)
+		bname = "INBOX";
+	return g_str_equal(aname, bname);
+}
+
+static void imap_construct(CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex)
+{
+	char *base, *summary;
+	CamelIMAPXStore *store = (CamelIMAPXStore *)service;
+
+	CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
+	if (camel_exception_is_set(ex))
+		return;
+
+	CAMEL_TRY {
+		store->summary = camel_imapx_store_summary_new();
+		store->storage_path = camel_session_get_storage_path(session, service, ex);
+		if (store->storage_path) {
+			summary = g_build_filename(store->storage_path, ".ev-store-summary", NULL);
+			camel_store_summary_set_filename((CamelStoreSummary *)store->summary, summary);
+			/* FIXME: need to remove params, passwords, etc */
+			camel_store_summary_set_uri_base((CamelStoreSummary *)store->summary, service->url);
+			camel_store_summary_load((CamelStoreSummary *)store->summary);
+		}
+	} CAMEL_CATCH(e) {
+		camel_exception_xfer(ex, e);
+	} CAMEL_DONE;
+}
+
+
+extern CamelServiceAuthType camel_imapx_password_authtype;
+extern CamelServiceAuthType camel_imapx_apop_authtype;
+
+static GList *
+imap_query_auth_types (CamelService *service, CamelException *ex)
+{
+	/*CamelIMAPXStore *store = CAMEL_IMAPX_STORE (service);*/
+	GList *types = NULL;
+
+        types = CAMEL_SERVICE_CLASS (parent_class)->query_auth_types (service, ex);
+	if (types == NULL)
+		return NULL;
+
+#if 0
+	if (connect_to_server_wrapper (service, NULL)) {
+		types = g_list_concat(types, g_list_copy(store->engine->auth));
+		imap_disconnect (service, TRUE, NULL);
+	} else {
+		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+				      _("Could not connect to POP server on %s"),
+				      service->url->host);
+	}
+#endif
+	return types;
+}
+
+#if 0
+static void
+store_get_pass(CamelIMAPXStore *store)
+{
+	if (((CamelService *)store)->url->passwd == NULL) {
+		char *prompt;
+		CamelException ex;
+
+		camel_exception_init(&ex);
+		
+		prompt = g_strdup_printf (_("%sPlease enter the IMAP password for %s %s"),
+					  store->login_error?store->login_error:"",
+					  ((CamelService *)store)->url->user,
+					  ((CamelService *)store)->url->host);
+		((CamelService *)store)->url->passwd = camel_session_get_password(camel_service_get_session((CamelService *)store),
+										  (CamelService *)store, NULL,
+										  prompt, "password", CAMEL_SESSION_PASSWORD_SECRET, &ex);
+		g_free (prompt);
+		if (camel_exception_is_set(&ex))
+			camel_exception_throw_ex(&ex);
+	}
+}
+
+static struct _CamelSasl *
+store_get_sasl(struct _CamelIMAPXDriver *driver, CamelIMAPXStore *store)
+{
+	store_get_pass(store);
+
+	if (((CamelService *)store)->url->authmech)
+		return camel_sasl_new("imap", ((CamelService *)store)->url->authmech, (CamelService *)store);
+
+	return NULL;
+}
+
+static void
+store_get_login(struct _CamelIMAPXDriver *driver, char **login, char **pass, CamelIMAPXStore *store)
+{
+	store_get_pass(store);
+	
+	*login = g_strdup(((CamelService *)store)->url->user);
+	*pass = g_strdup(((CamelService *)store)->url->passwd);
+}
+#endif
+
+static gboolean
+imap_connect (CamelService *service, CamelException *ex)
+{
+	CamelIMAPXStore *store = (CamelIMAPXStore *)service;
+
+	/* We never really are 'connected' or 'disconnected' */
+	if (store->server == NULL)
+		store->server = camel_imapx_server_new((CamelStore *)store, service->url);
+
+	camel_imapx_server_connect(store->server, 1);
+
+	return TRUE;
+}
+
+static gboolean
+imap_disconnect (CamelService *service, gboolean clean, CamelException *ex)
+{
+	CamelIMAPXStore *store = CAMEL_IMAPX_STORE (service);
+
+	CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex);
+
+	if (store->server)
+		camel_imapx_server_connect(store->server, 0);
+	
+	return TRUE;
+}
+
+static CamelFolder *
+imap_get_trash (CamelStore *store, CamelException *ex)
+{
+	/* no-op */
+	return NULL;
+}
+
+static CamelFolder *
+get_folder_offline (CamelStore *store, const gchar *folder_name,
+		    guint32 flags, CamelException *ex)
+{
+	CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
+	CamelFolder *new_folder = NULL;
+	CamelStoreInfo *si;
+
+	si = camel_store_summary_path((CamelStoreSummary *)imapx_store->summary, folder_name);
+	if (si) {
+		gchar *folder_dir, *storage_path;
+
+		/* Note: Although the INBOX is defined to be case-insensitive in the IMAP RFC
+		 * it is still up to the server how to acutally name it in a LIST response. Since
+		 * we stored the name as the server provided it us in the summary we take that name
+		 * to look up the folder.
+		 * But for the on-disk cache we do always capitalize the Inbox no matter what the
+		 * server provided.
+		 */
+		if (!g_ascii_strcasecmp (folder_name, "INBOX"))
+			folder_name = "INBOX";
+
+		storage_path = g_strdup_printf("%s/folders", imapx_store->storage_path);
+		folder_dir = imap_path_to_physical (storage_path, folder_name);
+		g_free(storage_path);
+		new_folder = camel_imap_folder_new (store, folder_name, folder_dir, ex);
+		g_free(folder_dir);
+
+		camel_store_summary_info_free((CamelStoreSummary *)imapx_store->summary, si);
+	} else {
+		camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
+				      _("No such folder %s"), folder_name);
+	}
+
+	return new_folder;
+}
+
+static CamelFolder *
+imap_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
+{
+	CamelIMAPXStore *istore = (CamelIMAPXStore *)store;
+	CamelIMAPXFolder *folder;
+
+	folder = get_folder_offline(store, folder_name, flags, ex);
+	if (folder == NULL) {
+		camel_exception_setv(ex, 2, "No such folder: %s", folder_name);
+		return NULL;
+	}
+
+	return (CamelFolder *)folder;
+}
+
+static CamelFolder *
+imap_get_inbox(CamelStore *store, CamelException *ex)
+{
+	camel_exception_setv(ex, 1, "get_inbox::unimplemented");
+
+	return NULL;
+}
+
+static CamelFolderInfo *
+folders_build_info(CamelURL *base, struct _list_info *li)
+{
+	char *path, *full_name, *name;
+	CamelFolderInfo *fi;
+
+	full_name = imapx_list_get_path(li);
+	name = strrchr(full_name, '/');
+	if (name)
+		name++;
+	else
+		name = full_name;
+
+	path = alloca(strlen(full_name)+2);
+	sprintf(path, "/%s", full_name);
+	camel_url_set_path(base, path);
+
+	fi = g_malloc0(sizeof(*fi));
+	fi->uri = camel_url_to_string(base, CAMEL_URL_HIDE_ALL);
+	fi->name = g_strdup(name);
+	fi->full_name = full_name;
+	fi->unread = -1;
+	fi->total = -1;
+	fi->flags = li->flags;
+
+	if (!g_ascii_strcasecmp(fi->full_name, "inbox"))
+		fi->flags |= CAMEL_FOLDER_SYSTEM;
+
+	/* TODO: could look up count here ... */
+	/* ?? */
+	/*folder = camel_object_bag_get(store->folders, "INBOX");*/
+
+	return fi;
+}
+
+/*
+  a
+  a/b
+  a/b/c
+  a/d
+  b
+  c/d
+
+*/
+
+/* note, pname is the raw name, not the folderinfo name */
+/* note also this free's as we go, since we never go 'backwards' */
+static CamelFolderInfo *
+folders_build_rec(CamelURL *base, GPtrArray *folders, int *ip, CamelFolderInfo *pfi, char *pname)
+{
+	int plen = 0;
+	CamelFolderInfo *last = NULL, *first = NULL;
+
+	if (pfi)
+		plen = strlen(pname);
+
+	for(;(*ip)<(int)folders->len;) {
+		CamelFolderInfo *fi;
+		struct _list_info *li;
+
+		li = folders->pdata[*ip];
+		printf("checking '%s' is child of '%s'\n", li->name, pname);
+
+		/* is this a child of the parent? */
+		if (pfi != NULL
+		    && (strncmp(pname, li->name, strlen(pname)) != 0
+			|| li->name[plen] != li->separator)) {
+			printf("  nope\n");
+			break;
+		}
+		printf("  yep\n");
+
+		/* is this not an immediate child of the parent? */
+#if 0
+		char *p;
+		if (pfi != NULL
+		    && li->separator != 0
+		    && (p = strchr(li->name + plen + 1, li->separator)) != NULL) {
+			if (last == NULL) {
+				struct _list_info tli;
+
+				tli.flags = CAMEL_FOLDER_NOSELECT|CAMEL_FOLDER_CHILDREN;
+				tli.separator = li->separator;
+				tli.name = g_strndup(li->name, p-li->name+1);
+				fi = folders_build_info(base, &tli);
+				fi->parent = pfi;
+				if (pfi && pfi->child == NULL)
+					pfi->child = fi;
+				i = folders_build_rec(folders, i, fi, tli.name);
+				break;
+			}
+		}
+#endif
+
+		fi = folders_build_info(base, li);
+		fi->parent = pfi;
+		if (last != NULL)
+			last->next = fi;
+		last = fi;
+		if (first == NULL)
+			first = fi;
+
+		(*ip)++;
+		fi->child = folders_build_rec(base, folders, ip, fi, li->name);
+		imap_free_list(li);
+	}
+
+	return first;
+}
+
+static void
+folder_info_dump(CamelFolderInfo *fi, int depth)
+{
+	char *s;
+
+	s = alloca(depth+1);
+	memset(s, ' ', depth);
+	s[depth] = 0;
+	while (fi) {
+		printf("%s%s (%s)\n", s, fi->name, fi->uri);
+		if (fi->child)
+			folder_info_dump(fi->child, depth+2);
+		fi = fi->next;
+	}
+	
+}
+
+// THIS IS VERY CRAP AND VERY TEMPORARY
+// we re-lookup the table, after we just looked it up
+// in the first place to create the list, to get the counts.
+static void
+folder_info_fill(CamelStore *store, CamelFolderInfo *fi)
+{
+//	CamelView *view;
+
+	while (fi) {
+/*		view = camel_view_summary_get(store->view_summary, fi->full_name);
+		if (view) {
+			fi->total = view->total_count;
+			fi->unread = view->unread_count;
+			camel_view_unref(view); */
+//		}
+		if (fi->child)
+			folder_info_fill(store, fi->child);
+		fi = fi->next;
+	}
+}
+
+static CamelFolderInfo *
+get_folder_info_offline (CamelStore *store, const gchar *top,
+			 guint32 flags, CamelException *ex)
+{
+	CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
+	gboolean include_inbox = FALSE;
+	CamelFolderInfo *fi;
+	GPtrArray *folders;
+	gchar *pattern, *name;
+	gint i;
+
+	if (camel_debug("imap:folder_info"))
+		printf("get folder info offline\n");
+
+	/* FIXME: obey other flags */
+
+	folders = g_ptr_array_new ();
+
+	if (top == NULL || top[0] == '\0') {
+		include_inbox = TRUE;
+		top = "";
+	}
+
+	/* get starting point */
+	if (top[0] == 0) {
+		if (imapx_store->namespace && imapx_store->namespace[0]) {
+			name = g_strdup(imapx_store->summary->namespace->full_name);
+			top = imapx_store->summary->namespace->path;
+		} else
+			name = g_strdup("");
+	} else {
+		name = camel_imapx_store_summary_full_from_path(imapx_store->summary, top);
+		if (name == NULL)
+			name = camel_imapx_store_summary_path_to_full(imapx_store->summary, top, imapx_store->dir_sep);
+	}
+
+	pattern = imap_concat(imapx_store, name, "*");
+
+	/* folder_info_build will insert parent nodes as necessary and mark
+	 * them as noselect, which is information we actually don't have at
+	 * the moment. So let it do the right thing by bailing out if it's
+	 * not a folder we're explicitly interested in. */
+
+	for (i=0;i<camel_store_summary_count((CamelStoreSummary *)imapx_store->summary);i++) {
+		CamelStoreInfo *si = camel_store_summary_index((CamelStoreSummary *)imapx_store->summary, i);
+		const gchar *full_name;
+
+		if (si == NULL)
+			continue;
+
+		full_name = camel_imapx_store_info_full_name (imapx_store->summary, si);
+		if (!full_name || !*full_name) {
+			camel_store_summary_info_free ((CamelStoreSummary *)imapx_store->summary, si);
+			continue;
+		}
+
+		if ((!strcmp(name, full_name)
+		     || imap_match_pattern(imapx_store->dir_sep, pattern, full_name)
+		     || (include_inbox && !g_ascii_strcasecmp (full_name, "INBOX")))
+//		    && ((imapx_store->parameters & IMAP_PARAM_SUBSCRIPTIONS) == 0
+			&&((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) == 0
+			|| (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED))) {
+
+			fi = imap_build_folder_info(imapx_store, camel_store_info_path((CamelStoreSummary *)imapx_store->summary, si));
+			fi->unread = si->unread;
+			fi->total = si->total;
+			fi->flags = si->flags;
+			/* HACK: some servers report noinferiors for all folders (uw-imapd)
+			   We just translate this into nochildren, and let the imap layer enforce
+			   it.  See create folder */
+			if (fi->flags & CAMEL_FOLDER_NOINFERIORS)
+				fi->flags = (fi->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
+
+			/* blah, this gets lost somewhere, i can't be bothered finding out why */
+			if (!g_ascii_strcasecmp(fi->full_name, "inbox"))
+				fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | CAMEL_FOLDER_TYPE_INBOX;
+
+			if (si->flags & CAMEL_FOLDER_NOSELECT) {
+				CamelURL *url = camel_url_new(fi->uri, NULL);
+
+				camel_url_set_param (url, "noselect", "yes");
+				g_free(fi->uri);
+				fi->uri = camel_url_to_string (url, 0);
+				camel_url_free (url);
+			} else {
+				fill_fi((CamelStore *)imapx_store, fi, 0);
+			}
+			if (!fi->child)
+				fi->flags |= CAMEL_FOLDER_NOCHILDREN;
+			g_ptr_array_add (folders, fi);
+		}
+		camel_store_summary_info_free((CamelStoreSummary *)imapx_store->summary, si);
+	}
+	g_free(pattern);
+
+	fi = camel_folder_info_build (folders, top, '/', TRUE);
+	g_ptr_array_free (folders, TRUE);
+	g_free(name);
+
+	return fi;
+}
+
+static CamelFolderInfo *
+imap_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex)
+{
+	CamelIMAPXStore *istore = (CamelIMAPXStore *)store;
+	CamelFolderInfo * fi= NULL;
+	GPtrArray *folders = NULL;
+	CamelURL *base;
+	CamelIterator *iter;
+	int i;
+
+	if (istore->server == NULL) {
+		camel_service_connect((CamelService *)store, ex);
+		if (camel_exception_is_set(ex))
+			return NULL;
+	}
+
+	if (top == NULL)
+		top = "";
+
+	if (folders == NULL) {
+		folders = camel_imapx_server_list(istore->server, "", flags, ex);
+		for (i=0;!camel_exception_is_set(ex) && i<folders->len;i++) {
+			struct _list_info *li = folders->pdata[i];
+			char *name;
+
+			name = imapx_list_get_path(li);
+			g_free(name);
+		}
+	}
+
+	i = 0;
+	base = camel_url_copy(((CamelService *)store)->url);
+	fi = folders_build_rec(base, folders, &i, NULL, NULL);
+	camel_url_free(base);
+
+	g_ptr_array_free(folders, TRUE);
+
+	// FIXME: temporary
+	folder_info_fill(store, fi);
+
+	return fi;
+}
+
+static void
+imap_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex)
+{
+	camel_exception_setv(ex, 1, "delete_folder::unimplemented");
+}
+
+static void
+imap_rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex)
+{
+	camel_exception_setv(ex, 1, "rename_folder::unimplemented");
+}
+
+static CamelFolderInfo *
+imap_create_folder(CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex)
+{
+	camel_exception_setv(ex, 1, "create_folder::unimplemented");
+	return NULL;
+}
+
+/* ********************************************************************** */
+#if 0
+static int store_resp_fetch(CamelIMAPXEngine *ie, guint32 id, void *data)
+{
+	struct _fetch_info *finfo;
+	CamelIMAPXStore *istore = data;
+	CamelMessageInfo *info;
+	struct _pending_fetch *pending;
+
+	finfo = imap_parse_fetch(ie->stream);
+	if (istore->selected) {
+		if ((finfo->got & FETCH_UID) == 0) {
+			printf("didn't get uid in fetch response?\n");
+		} else {
+			info = camel_folder_summary_index(((CamelFolder *)istore->selected)->summary, id-1);
+			/* exists, check/update */
+			if (info) {
+				if (strcmp(finfo->uid, camel_message_info_uid(info)) != 0) {
+					printf("summary at index %d has uid %s expected %s\n", id, camel_message_info_uid(info), finfo->uid);
+					/* uid mismatch???  try do it based on uid instead? try to reorder?  i dont know? */
+					camel_message_info_free(info);
+					info = camel_folder_summary_uid(((CamelFolder *)istore->selected)->summary, finfo->uid);
+				}
+			}
+
+			if (info) {
+				if (finfo->got & (FETCH_FLAGS)) {
+					printf("updating flags for uid '%s'\n", finfo->uid);
+					info->flags = finfo->flags;
+					camel_folder_change_info_change_uid(istore->selected->changes, finfo->uid);
+				}
+				if (finfo->got & FETCH_MINFO) {
+					printf("got envelope unexpectedly?\n");
+				}
+				/* other things go here, like body fetches */
+			} else {
+				pending = g_hash_table_lookup(istore->pending_fetch_table, finfo->uid);
+
+				/* we need to create a new info, we only care about flags and minfo */
+
+				if (pending)
+					info = pending->info;
+				else {
+					info = camel_folder_summary_info_new(((CamelFolder *)istore->selected)->summary);
+					camel_message_info_set_uid(info, g_strdup(finfo->uid));
+				}
+
+				if (finfo->got & FETCH_FLAGS)
+					info->flags = finfo->flags;
+
+				if (finfo->got & FETCH_MINFO) {
+					/* if we only use ENVELOPE? */
+					camel_message_info_set_subject(info, g_strdup(camel_message_info_subject(finfo->minfo)));
+					camel_message_info_set_from(info, g_strdup(camel_message_info_from(finfo->minfo)));
+					camel_message_info_set_to(info, g_strdup(camel_message_info_to(finfo->minfo)));
+					camel_message_info_set_cc(info, g_strdup(camel_message_info_cc(finfo->minfo)));
+					info->date_sent = finfo->minfo->date_sent;
+					camel_folder_summary_add(((CamelFolder *)istore->selected)->summary, info);
+					camel_folder_change_info_add_uid(istore->selected->changes, finfo->uid);
+					if (pending) {
+						e_dlist_remove((EDListNode *)pending);
+						g_hash_table_remove(istore->pending_fetch_table, finfo->uid);
+						/*e_memchunk_free(istore->pending_fetch_chunks, pending);*/
+					}
+				} else if (finfo->got & FETCH_HEADER) {
+					/* if we only use HEADER? */
+					CamelMimeParser *mp;
+
+					if (pending == NULL)
+						camel_message_info_free(info);
+					mp = camel_mime_parser_new();
+					camel_mime_parser_init_with_stream(mp, finfo->header);
+					info = camel_folder_summary_info_new_from_parser(((CamelFolder *)istore->selected)->summary, mp);
+					camel_object_unref(mp);
+					camel_message_info_set_uid(info, g_strdup(finfo->uid));
+
+					camel_folder_summary_add(((CamelFolder *)istore->selected)->summary, info);
+					camel_folder_change_info_add_uid(istore->selected->changes, finfo->uid);
+					if (pending) {
+						/* FIXME: use a dlist */
+						e_dlist_remove((EDListNode *)pending);
+						g_hash_table_remove(istore->pending_fetch_table, camel_message_info_uid(pending->info));
+						camel_message_info_free(pending->info);
+						/*e_memchunk_free(istore->pending_fetch_chunks, pending);*/
+					}
+				} else if (finfo->got & FETCH_FLAGS) {
+					if (pending == NULL) {
+						pending = e_memchunk_alloc(istore->pending_fetch_chunks);
+						pending->info = info;
+						g_hash_table_insert(istore->pending_fetch_table, (char *)camel_message_info_uid(info), pending);
+						e_dlist_addtail(&istore->pending_fetch_list, (EDListNode *)pending);
+					}
+				} else {
+					if (pending == NULL)
+						camel_message_info_free(info);
+					printf("got unexpected fetch response?\n");
+					imap_dump_fetch(finfo);
+				}
+			}
+		}
+	} else {
+		printf("unexpected fetch response, no folder selected?\n");
+	}
+	/*imap_dump_fetch(finfo);*/
+	imap_free_fetch(finfo);
+
+	return camel_imapx_engine_skip(ie);
+}
+#endif
+
+/* ********************************************************************** */
+
+/* should be moved to imapx-utils?
+   stuff in imapx-utils should be moved to imapx-parse? */
+
+/* ********************************************************************** */
+
+#if 0
+void
+camel_imapx_store_folder_selected(CamelIMAPXStore *store, CamelIMAPXFolder *folder, CamelIMAPXSelectResponse *select)
+{
+	CamelIMAPXCommand * volatile ic = NULL;
+	CamelIMAPXStore *istore = (CamelIMAPXStore *)store;
+	int i;
+	struct _uidset_state ss;
+	GPtrArray *fetch;
+	CamelMessageInfo *info;
+	struct _pending_fetch *fw, *fn;
+
+	printf("imap folder selected\n");
+
+	if (select->uidvalidity == folder->uidvalidity
+	    && select->exists == folder->exists
+	    && select->recent == folder->recent
+	    && select->unseen == folder->unseen) {
+		/* no work to do? */
+		return;
+	}
+
+	istore->pending_fetch_table = g_hash_table_new(g_str_hash, g_str_equal);
+	istore->pending_fetch_chunks = e_memchunk_new(256, sizeof(struct _pending_fetch));
+
+	/* perform an update - flags first (and see what we have) */
+	CAMEL_TRY {
+		ic = camel_imapx_engine_command_new(istore->engine, "FETCH", NULL, "FETCH 1:%d (UID FLAGS)", select->exists);
+		camel_imapx_engine_command_queue(istore->engine, ic);
+		while (camel_imapx_engine_iterate(istore->engine, ic) > 0)
+			;
+
+		if (ic->status->result != IMAP_OK)
+			camel_exception_throw(1, "fetch failed: %s", ic->status->text);
+
+		/* pending_fetch_list now contains any new messages */
+		/* FIXME: how do we work out no-longer present messages? */
+		printf("now fetching info for messages?\n");
+		uidset_init(&ss, store->engine);
+		ic = camel_imapx_engine_command_new(istore->engine, "FETCH", NULL, "UID FETCH ");
+		fw = (struct _pending_fetch *)istore->pending_fetch_list.head;
+		fn = fw->next;
+		while (fn) {
+			info = fw->info;
+			/* if the uid set fills, then flush the command out */
+			if (uidset_add(&ss, ic, camel_message_info_uid(info))
+			    || (fn->next == NULL && uidset_done(&ss, ic))) {
+				camel_imapx_engine_command_add(istore->engine, ic, " (FLAGS RFC822.HEADER)");
+				camel_imapx_engine_command_queue(istore->engine, ic);
+				while (camel_imapx_engine_iterate(istore->engine, ic) > 0)
+					;
+				if (ic->status->result != IMAP_OK)
+					camel_exception_throw(1, "fetch failed: %s", ic->status->text);
+				/* if not end ... */
+				camel_imapx_engine_command_free(istore->engine, ic);
+				ic = camel_imapx_engine_command_new(istore->engine, "FETCH", NULL, "UID FETCH ");
+			}
+			fw = fn;
+			fn = fn->next;
+		}
+
+		printf("The pending list should now be empty: %s\n", e_dlist_empty(&istore->pending_fetch_list)?"TRUE":"FALSE");
+		for (i=0;i<10;i++) {
+			info = camel_folder_summary_index(((CamelFolder *)istore->selected)->summary, i);
+			if (info) {
+				printf("message info [%d] =\n", i);
+				camel_message_info_dump(info);
+				camel_message_info_free(info);
+			}
+		}
+	} CAMEL_CATCH (e) {
+		/* FIXME: cleanup */
+		camel_exception_throw_ex(e);
+	} CAMEL_DONE;
+
+	g_hash_table_destroy(istore->pending_fetch_table);
+	istore->pending_fetch_table = NULL;
+	e_memchunk_destroy(istore->pending_fetch_chunks);
+
+	camel_imapx_engine_command_free(istore->engine, ic);
+}
+#endif
+
+#if 0
+/*char *uids[] = {"1", "2", "4", "5", "6", "7", "9", "11", "12", 0};*/
+/*char *uids[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", 0};*/
+char *uids[] = {"1", "3", "5", "7", "9", "11", "12", "13", "14", "15", "20", "21", "24", "25", "26", 0};
+
+void
+uidset_test(CamelIMAPXEngine *ie)
+{
+	struct _uidset_state ss;
+	CamelIMAPXCommand *ic;
+	int i;
+
+	/*ic = camel_imapx_engine_command_new(ie, 0, "FETCH", NULL, "FETCH ");*/
+	uidset_init(&ss, 0, 0);
+	for (i=0;uids[i];i++) {
+		if (uidset_add(&ss, uids[i])) {
+			printf("\n[%d] flushing uids\n", i);
+		}
+	}
+
+	if (uidset_done(&ss)) {
+		printf("\nflushing uids\n");
+	}
+}
+#endif
+
+static void
+camel_imapx_store_class_init(CamelIMAPXStoreClass *klass)
+{
+	CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS(klass);
+	CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS(klass);
+
+	parent_class = CAMEL_STORE_CLASS(camel_type_get_global_classfuncs(camel_store_get_type()));
+	
+	camel_service_class->construct = imap_construct;
+	camel_service_class->query_auth_types = imap_query_auth_types;
+	camel_service_class->connect = imap_connect;
+	camel_service_class->disconnect = imap_disconnect;
+
+	camel_store_class->get_trash = imap_get_trash;
+	camel_store_class->get_folder = imap_get_folder;
+	camel_store_class->get_inbox = imap_get_inbox;
+
+	camel_store_class->create_folder = imap_create_folder;
+	camel_store_class->rename_folder = imap_rename_folder;
+	camel_store_class->delete_folder = imap_delete_folder;
+	camel_store_class->get_folder_info = imap_get_folder_info;
+
+	((CamelStoreClass *)klass)->hash_folder_name = imapx_name_hash;
+	((CamelStoreClass *)klass)->compare_folder_name = imapx_name_equal;
+}
+
+static void
+camel_imapx_store_init (gpointer object, gpointer klass)
+{
+	/*CamelIMAPXStore *istore = object;*/
+}
+
+static void
+imapx_store_finalise(CamelObject *object)
+{
+	CamelIMAPXStore *imap_store = CAMEL_IMAPX_STORE (object);
+
+	/* force disconnect so we dont have it run later, after we've cleaned up some stuff */
+	/* SIGH */
+
+	camel_service_disconnect((CamelService *)imap_store, TRUE, NULL);
+}
+
+CamelType
+camel_imapx_store_get_type (void)
+{
+	static CamelType camel_imapx_store_type = CAMEL_INVALID_TYPE;
+
+	if (!camel_imapx_store_type) {
+		camel_imapx_store_type = camel_type_register(CAMEL_STORE_TYPE,
+							    "CamelIMAPXStore",
+							    sizeof (CamelIMAPXStore),
+							    sizeof (CamelIMAPXStoreClass),
+							    (CamelObjectClassInitFunc) camel_imapx_store_class_init,
+							    NULL,
+							    (CamelObjectInitFunc) camel_imapx_store_init,
+							     imapx_store_finalise);
+	}
+
+	return camel_imapx_store_type;
+}
diff --git a/camel/providers/imapx/camel-imapx-store.h b/camel/providers/imapx/camel-imapx-store.h
new file mode 100644
index 0000000..78b8ee0
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-store.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-imap-store.h : class for an imap store */
+
+/* 
+ * Authors: Michael Zucchi <notzed ximian com>
+ *
+ * Copyright (C) 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of version 2 of the GNU General Public 
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+
+#ifndef CAMEL_IMAPX_STORE_H
+#define CAMEL_IMAPX_STORE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <camel/camel-types.h>
+#include <camel/camel-store.h>
+#include "camel-imapx-store-summary.h"
+
+#define CAMEL_IMAPX_STORE_TYPE     (camel_imapx_store_get_type ())
+#define CAMEL_IMAPX_STORE(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_IMAPX_STORE_TYPE, CamelIMAPXStore))
+#define CAMEL_IMAPX_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAPX_STORE_TYPE, CamelIMAPXStoreClass))
+#define CAMEL_IS_IMAP_STORE(o)    (CAMEL_CHECK_TYPE((o), CAMEL_IMAPX_STORE_TYPE))
+
+struct _pending_fetch {
+	struct _pending_fetch *next;
+	struct _pending_fetch *prev;
+
+	struct _CamelMessageInfo *info;
+};
+
+typedef struct {
+	CamelStore parent_object;
+
+	struct _CamelIMAPXServer *server;
+
+	CamelIMAPXStoreSummary *summary; /* in-memory list of folders */
+	gchar *namespace, *dir_sep, *base_url, *storage_path;
+
+	/* if we had a login error, what to show to user */
+	char *login_error;
+
+	GPtrArray *pending_list;
+} CamelIMAPXStore;
+
+typedef struct {
+	CamelStoreClass parent_class;
+
+} CamelIMAPXStoreClass;
+
+/* Standard Camel function */
+CamelType camel_imapx_store_get_type (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_IMAPX_STORE_H */
+
+
diff --git a/camel/providers/imapx/camel-imapx-stream.c b/camel/providers/imapx/camel-imapx-stream.c
new file mode 100644
index 0000000..b9a7c27
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-stream.c
@@ -0,0 +1,728 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
+ *
+ * Author:
+ *  Michael Zucchi <notzed ximian com>
+ *
+ * Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <camel/camel-stream-mem.h>
+
+#include "camel-imapx-utils.h"
+#include "camel-imapx-stream.h"
+#include "camel-imapx-exception.h"
+
+#define t(x) 
+#define io(x) x
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_IMAPX_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+#define CAMEL_IMAPX_STREAM_SIZE (4096)
+#define CAMEL_IMAPX_STREAM_TOKEN (4096) /* maximum token size */
+
+static int
+stream_fill(CamelIMAPXStream *is)
+{
+	int left = 0;
+
+	if (is->source) {
+		left = is->end - is->ptr;
+		memcpy(is->buf, is->ptr, left);
+		is->end = is->buf + left;
+		is->ptr = is->buf;
+		left = camel_stream_read(is->source, is->end, CAMEL_IMAPX_STREAM_SIZE - (is->end - is->buf));
+		if (left > 0) {
+			is->end += left;
+			io(printf("camel_imapx_read: buffer is '%.*s'\n", (int)(is->end - is->ptr), is->ptr));
+			return is->end - is->ptr;
+		} else {
+			io(printf("camel_imapx_read: -1\n"));
+			return -1;
+		}
+	}
+
+	printf("camel_imapx_read: -1\n");
+
+	return -1;
+}
+
+static ssize_t
+stream_read(CamelStream *stream, char *buffer, size_t n)
+{
+	CamelIMAPXStream *is = (CamelIMAPXStream *)stream;
+	ssize_t max;
+
+	if (is->literal == 0 || n == 0)
+		return 0;
+
+	max = is->end - is->ptr;
+	if (max > 0) {
+		max = MIN(max, is->literal);
+		max = MIN(max, n);
+		memcpy(buffer, is->ptr, max);
+		is->ptr += max;
+	} else {
+		max = MIN(is->literal, n);
+		max = camel_stream_read(is->source, buffer, max);
+		if (max <= 0)
+			return max;
+	}
+
+	io(printf("camel_imapx_read(literal): '%.*s'\n", (int)max, buffer));
+
+	is->literal -= max;
+	
+	return max;
+}
+
+static ssize_t
+stream_write(CamelStream *stream, const char *buffer, size_t n)
+{
+	CamelIMAPXStream *is = (CamelIMAPXStream *)stream;
+
+	io(printf("camel_imapx_write: '%.*s'\n", (int)n, buffer));
+
+	return camel_stream_write(is->source, buffer, n);
+}
+
+static int
+stream_close(CamelStream *stream)
+{
+	/* nop? */
+	return 0;
+}
+
+static int
+stream_flush(CamelStream *stream)
+{
+	/* nop? */
+	return 0;
+}
+
+static gboolean
+stream_eos(CamelStream *stream)
+{
+	CamelIMAPXStream *is = (CamelIMAPXStream *)stream;
+
+	return is->literal == 0;
+}
+
+static int
+stream_reset(CamelStream *stream)
+{
+	/* nop?  reset literal mode? */
+	return 0;
+}
+
+static void
+camel_imapx_stream_class_init (CamelStreamClass *camel_imapx_stream_class)
+{
+	CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_imapx_stream_class;
+
+	parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
+
+	/* virtual method definition */
+	camel_stream_class->read = stream_read;
+	camel_stream_class->write = stream_write;
+	camel_stream_class->close = stream_close;
+	camel_stream_class->flush = stream_flush;
+	camel_stream_class->eos = stream_eos;
+	camel_stream_class->reset = stream_reset;
+}
+
+static void
+camel_imapx_stream_init(CamelIMAPXStream *is, CamelIMAPXStreamClass *isclass)
+{
+	/* +1 is room for appending a 0 if we need to for a token */
+	is->ptr = is->end = is->buf = g_malloc(CAMEL_IMAPX_STREAM_SIZE+1);
+	is->tokenptr = is->tokenbuf = g_malloc(CAMEL_IMAPX_STREAM_SIZE+1);
+	is->tokenend = is->tokenbuf + CAMEL_IMAPX_STREAM_SIZE;
+}
+
+static void
+camel_imapx_stream_finalise(CamelIMAPXStream *is)
+{
+	g_free(is->buf);
+	if (is->source)
+		camel_object_unref((CamelObject *)is->source);
+}
+
+CamelType
+camel_imapx_stream_get_type (void)
+{
+	static CamelType camel_imapx_stream_type = CAMEL_INVALID_TYPE;
+
+	if (camel_imapx_stream_type == CAMEL_INVALID_TYPE) {
+		camel_imapx_stream_type = camel_type_register( camel_stream_get_type(),
+							    "CamelIMAPXStream",
+							    sizeof( CamelIMAPXStream ),
+							    sizeof( CamelIMAPXStreamClass ),
+							    (CamelObjectClassInitFunc) camel_imapx_stream_class_init,
+							    NULL,
+							    (CamelObjectInitFunc) camel_imapx_stream_init,
+							    (CamelObjectFinalizeFunc) camel_imapx_stream_finalise );
+	}
+
+	return camel_imapx_stream_type;
+}
+
+/**
+ * camel_imapx_stream_new:
+ *
+ * Returns a NULL stream.  A null stream is always at eof, and
+ * always returns success for all reads and writes.
+ *
+ * Return value: the stream
+ **/
+CamelStream *
+camel_imapx_stream_new(CamelStream *source)
+{
+	CamelIMAPXStream *is;
+
+	is = (CamelIMAPXStream *)camel_object_new(camel_imapx_stream_get_type ());
+	camel_object_ref((CamelObject *)source);
+	is->source = source;
+
+	return (CamelStream *)is;
+}
+
+/* Returns if there is any data buffered that is ready for processing */
+int
+camel_imapx_stream_buffered(CamelIMAPXStream *is)
+{
+	return is->end - is->ptr;
+}
+
+#if 0
+
+static int
+skip_ws(CamelIMAPXStream *is, unsigned char *pp, unsigned char *pe)
+{
+	register unsigned char c, *p;
+	unsigned char *e;
+
+	p = is->ptr;
+	e = is->end;
+
+	do {
+		while (p >= e ) {
+			is->ptr = p;
+			if (stream_fill(is) == IMAP_TOK_ERROR)
+				return IMAP_TOK_ERROR;
+			p = is->ptr;
+			e = is->end;
+		}
+		c = *p++;
+	} while (c == ' ' || c == '\r');
+
+	is->ptr = p;
+	is->end = e;
+
+	return c;
+}
+#endif
+
+/* FIXME: these should probably handle it themselves,
+   and get rid of the token interface? */
+int
+camel_imapx_stream_atom(CamelIMAPXStream *is, unsigned char **data, unsigned int *lenp)
+{
+	unsigned char *p, c;
+
+	/* this is only 'approximate' atom */
+	switch(camel_imapx_stream_token(is, data, lenp)) {
+	case IMAP_TOK_TOKEN:
+		p = *data;
+		while ((c = *p))
+			*p++ = toupper(c);
+	case IMAP_TOK_INT:
+		return 0;
+	case IMAP_TOK_ERROR:
+		return IMAP_TOK_ERROR;
+	default:
+		camel_exception_throw(1, "expecting atom");
+		printf("expecting atom!\n");
+		return IMAP_TOK_PROTOCOL;
+	}
+}
+
+/* gets an atom, a quoted_string, or a literal */
+int
+camel_imapx_stream_astring(CamelIMAPXStream *is, unsigned char **data)
+{
+	unsigned char *p, *start;
+	unsigned int len, inlen;
+
+	switch(camel_imapx_stream_token(is, data, &len)) {
+	case IMAP_TOK_TOKEN:
+	case IMAP_TOK_INT:
+	case IMAP_TOK_STRING:
+		return 0;
+	case IMAP_TOK_LITERAL:
+		/* FIXME: just grow buffer */
+		if (len >= CAMEL_IMAPX_STREAM_TOKEN) {
+			camel_exception_throw(1, "astring: literal too long");
+			printf("astring too long\n");
+			return IMAP_TOK_PROTOCOL;
+		}
+		p = is->tokenptr;
+		camel_imapx_stream_set_literal(is, len);
+		do {
+			len = camel_imapx_stream_getl(is, &start, &inlen);
+			if (len < 0)
+				return len;
+			memcpy(p, start, inlen);
+			p += inlen;
+		} while (len > 0);
+		*data = is->tokenptr;
+		return 0;
+	case IMAP_TOK_ERROR:
+		/* wont get unless no exception hanlder*/
+		return IMAP_TOK_ERROR;
+	default:
+		camel_exception_throw(1, "expecting astring");
+		printf("expecting astring!\n");
+		return IMAP_TOK_PROTOCOL;
+	}
+}
+
+/* check for NIL or (small) quoted_string or literal */
+int
+camel_imapx_stream_nstring(CamelIMAPXStream *is, unsigned char **data)
+{
+	unsigned char *p, *start;
+	unsigned int len, inlen;
+
+	switch(camel_imapx_stream_token(is, data, &len)) {
+	case IMAP_TOK_STRING:
+		return 0;
+	case IMAP_TOK_LITERAL:
+		/* FIXME: just grow buffer */
+		if (len >= CAMEL_IMAPX_STREAM_TOKEN) {
+			camel_exception_throw(1, "nstring: literal too long");
+			return IMAP_TOK_PROTOCOL;
+		}
+		p = is->tokenptr;
+		camel_imapx_stream_set_literal(is, len);
+		do {
+			len = camel_imapx_stream_getl(is, &start, &inlen);
+			if (len < 0)
+				return len;
+			memcpy(p, start, inlen);
+			p += inlen;
+		} while (len > 0);
+		*data = is->tokenptr;
+		return 0;
+	case IMAP_TOK_TOKEN:
+		p = *data;
+		if (toupper(p[0]) == 'N' && toupper(p[1]) == 'I' && toupper(p[2]) == 'L' && p[3] == 0) {
+			*data = NULL;
+			return 0;
+		}
+	default:
+		camel_exception_throw(1, "expecting nstring");
+		return IMAP_TOK_PROTOCOL;
+	case IMAP_TOK_ERROR:
+		/* we'll never get this unless there are no exception  handlers anyway */
+		return IMAP_TOK_ERROR;
+
+	}
+}
+
+/* parse an nstring as a stream */
+int
+camel_imapx_stream_nstring_stream(CamelIMAPXStream *is, CamelStream **stream)
+/* throws IO,PARSE exception */
+{
+	unsigned char *token;
+	unsigned int len;
+	int ret = 0;
+	CamelStream * volatile mem = NULL;
+
+	*stream = NULL;
+
+	CAMEL_TRY {
+		switch(camel_imapx_stream_token(is, &token, &len)) {
+		case IMAP_TOK_STRING:
+			mem = camel_stream_mem_new_with_buffer(token, len);
+			*stream = mem;
+			break;
+		case IMAP_TOK_LITERAL:
+			/* if len is big, we could automatically use a file backing */
+			camel_imapx_stream_set_literal(is, len);
+			mem = camel_stream_mem_new();
+			if (camel_stream_write_to_stream((CamelStream *)is, mem) == -1)
+				camel_exception_throw(1, "nstring: io error: %s", strerror(errno));
+			camel_stream_reset(mem);
+			*stream = mem;
+			break;
+		case IMAP_TOK_TOKEN:
+			if (toupper(token[0]) == 'N' && toupper(token[1]) == 'I' && toupper(token[2]) == 'L' && token[3] == 0) {
+				*stream = NULL;
+				break;
+			}
+		default:
+			ret = -1;
+			camel_exception_throw(1, "nstring: token not string");
+		}
+	} CAMEL_CATCH(ex) {
+		if (mem)
+			camel_object_unref((CamelObject *)mem);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	/* never reaches here anyway */
+	return ret;
+}
+
+guint32
+camel_imapx_stream_number(CamelIMAPXStream *is)
+{
+	unsigned char *token;
+	unsigned int len;
+
+	if (camel_imapx_stream_token(is, &token, &len) != IMAP_TOK_INT) {
+		camel_exception_throw(1, "expecting number");
+		return 0;
+	}
+
+	return strtoul(token, 0, 10);
+}
+
+int
+camel_imapx_stream_text(CamelIMAPXStream *is, unsigned char **text)
+{
+	GByteArray *build = g_byte_array_new();
+	unsigned char *token;
+	unsigned int len;
+	int tok;
+
+	CAMEL_TRY {
+		while (is->unget > 0) {
+			switch (is->unget_tok) {
+			case IMAP_TOK_TOKEN:
+			case IMAP_TOK_STRING:
+			case IMAP_TOK_INT:
+				g_byte_array_append(build, is->unget_token, is->unget_len);
+				g_byte_array_append(build, " ", 1);
+			default: /* invalid, but we'll ignore */
+				break;
+			}
+			is->unget--;
+		}
+
+		do {
+			tok = camel_imapx_stream_gets(is, &token, &len);
+			if (tok < 0)
+				camel_exception_throw(1, "io error: %s", strerror(errno));
+			if (len)
+				g_byte_array_append(build, token, len);
+		} while (tok > 0);
+	} CAMEL_CATCH(ex) {
+		*text = NULL;
+		g_byte_array_free(build, TRUE);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	g_byte_array_append(build, "", 1);
+	*text = build->data;
+	g_byte_array_free(build, FALSE);
+
+	return 0;
+}
+
+/* Get one token from the imap stream */
+camel_imapx_token_t
+/* throws IO,PARSE exception */
+camel_imapx_stream_token(CamelIMAPXStream *is, unsigned char **data, unsigned int *len)
+{
+	register unsigned char c, *p, *o, *oe;
+	unsigned char *e;
+	unsigned int literal;
+	int digits;
+
+	if (is->unget > 0) {
+		is->unget--;
+		*data = is->unget_token;
+		*len = is->unget_len;
+		/*printf("token UNGET '%c' %s\n", is->unget_tok, is->unget_token);*/
+		return is->unget_tok;
+	}
+
+	if (is->literal > 0)
+		g_warning("stream_token called with literal %d", is->literal);
+
+	p = is->ptr;
+	e = is->end;
+
+	/* skip whitespace/prefill buffer */
+	do {
+		while (p >= e ) {
+			is->ptr = p;
+			if (stream_fill(is) == IMAP_TOK_ERROR)
+				goto io_error;
+			p = is->ptr;
+			e = is->end;
+		}
+		c = *p++;
+	} while (c == ' ' || c == '\r');
+
+	/*strchr("\n*()[]+", c)*/
+	if (imapx_is_token_char(c)) {
+		is->ptr = p;
+		t(printf("token '%c'\n", c));
+		return c;
+	} else if (c == '{') {
+		literal = 0;
+		*data = p;
+		while (1) {
+			while (p < e) {
+				c = *p++;
+				if (isdigit(c) && literal < (UINT_MAX/10)) {
+					literal = literal * 10 + (c - '0');
+				} else if (c == '}') {
+					while (1) {
+						while (p < e) {
+							c = *p++;
+							if (c == '\n') {
+								*len = literal;
+								is->ptr = p;
+								is->literal = literal;
+								t(printf("token LITERAL %d\n", literal));
+								return IMAP_TOK_LITERAL;
+							}
+						}
+						is->ptr = p;
+						if (stream_fill(is) == IMAP_TOK_ERROR)
+							goto io_error;
+						p = is->ptr;
+						e = is->end;
+					}
+				} else {
+					if (isdigit(c))
+						printf("Protocol error: literal too big\n");
+					else
+						printf("Protocol error: literal contains invalid char %02x '%c'\n", c, isprint(c)?c:c);
+					goto protocol_error;
+				}
+			}
+			is->ptr = p;
+			if (stream_fill(is) == IMAP_TOK_ERROR)
+				goto io_error;
+			p = is->ptr;
+			e = is->end;
+		}
+	} else if (c == '"') {
+		o = is->tokenptr;
+		oe = is->tokenptr + CAMEL_IMAPX_STREAM_TOKEN - 1;
+		while (1) {
+			while (p < e) {
+				c = *p++;
+				if (c == '\\') {
+					while (p >= e) {
+						is->ptr = p;
+						if (stream_fill(is) == IMAP_TOK_ERROR)
+							goto io_error;
+						p = is->ptr;
+						e = is->end;
+					}
+					c = *p++;
+				} else if (c == '\"') {
+					is->ptr = p;
+					*o = 0;
+					*data = is->tokenbuf;
+					*len = o - is->tokenbuf;
+					t(printf("token STRING '%s'\n", is->tokenbuf));
+					return IMAP_TOK_STRING;
+				}
+
+				if (c == '\n' || c == '\r' || o>=oe) {
+					if (o >= oe)
+						printf("Protocol error: string too long\n");
+					else
+						printf("Protocol error: truncated string\n");
+					goto protocol_error;
+				} else {
+					*o++ = c;
+				}
+			}
+			is->ptr = p;
+			if (stream_fill(is) == IMAP_TOK_ERROR)
+				goto io_error;
+			p = is->ptr;
+			e = is->end;
+		}
+	} else {
+		o = is->tokenptr;
+		oe = is->tokenptr + CAMEL_IMAPX_STREAM_TOKEN - 1;
+		digits = isdigit(c);
+		*o++ = c;
+		while (1) {
+			while (p < e) {
+				c = *p++;
+				/*if (strchr(" \r\n*()[]+", c) != NULL) {*/
+				if (imapx_is_notid_char(c)) {
+					if (c == ' ' || c == '\r')
+						is->ptr = p;
+					else
+						is->ptr = p-1;
+					*o = 0;
+					*data = is->tokenbuf;
+					*len = o - is->tokenbuf;
+					t(printf("token TOKEN '%s'\n", is->tokenbuf));
+					return digits?IMAP_TOK_INT:IMAP_TOK_TOKEN;
+				} else if (o < oe) {
+					digits &= isdigit(c);
+					*o++ = c;
+				} else {
+					printf("Protocol error: token too long\n");
+					goto protocol_error;
+				}
+			}
+			is->ptr = p;
+			if (stream_fill(is) == IMAP_TOK_ERROR)
+				goto io_error;
+			p = is->ptr;
+			e = is->end;
+		}
+	}
+
+	/* Had an i/o erorr */
+io_error:
+	printf("Got io error\n");
+	camel_exception_throw(1, "io error");
+	return IMAP_TOK_ERROR;
+
+	/* Protocol error, skip until next lf? */
+protocol_error:
+	printf("Got protocol error\n");
+
+	if (c == '\n')
+		is->ptr = p-1;
+	else
+		is->ptr = p;
+
+	camel_exception_throw(1, "protocol error");
+	return IMAP_TOK_PROTOCOL;
+}
+
+void
+camel_imapx_stream_ungettoken(CamelIMAPXStream *is, camel_imapx_token_t tok, unsigned char *token, unsigned int len)
+{
+	/*printf("ungettoken: '%c' '%s'\n", tok, token);*/
+	is->unget_tok = tok;
+	is->unget_token = token;
+	is->unget_len = len;
+	is->unget++;
+}
+
+/* returns -1 on error, 0 if last lot of data, >0 if more remaining */
+int camel_imapx_stream_gets(CamelIMAPXStream *is, unsigned char **start, unsigned int *len)
+{
+	int max;
+	unsigned char *end;
+
+	*len = 0;
+
+	max = is->end - is->ptr;
+	if (max == 0) {
+		max = stream_fill(is);
+		if (max <= 0)
+			return max;
+	}
+
+	*start = is->ptr;
+	end = memchr(is->ptr, '\n', max);
+	if (end)
+		max = (end - is->ptr) + 1;
+	*start = is->ptr;
+	*len = max;
+	is->ptr += max;
+
+	return end == NULL?1:0;
+}
+
+void camel_imapx_stream_set_literal(CamelIMAPXStream *is, unsigned int literal)
+{
+	is->literal = literal;
+}
+
+/* returns -1 on erorr, 0 if last data, >0 if more data left */
+int camel_imapx_stream_getl(CamelIMAPXStream *is, unsigned char **start, unsigned int *len)
+{
+	int max;
+
+	*len = 0;
+
+	if (is->literal > 0) {
+		max = is->end - is->ptr;
+		if (max == 0) {
+			max = stream_fill(is);
+			if (max <= 0)
+				return max;
+		}
+
+		max = MIN(max, is->literal);
+		*start = is->ptr;
+		*len = max;
+		is->ptr += max;
+		is->literal -= max;
+	}
+
+	if (is->literal > 0)
+		return 1;
+
+	return 0;
+}
+
+/* skip the rest of the line of tokens */
+int
+camel_imapx_stream_skip(CamelIMAPXStream *is)
+{
+	int tok;
+	unsigned char *token;
+	unsigned int len;
+
+	do {
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok == IMAP_TOK_LITERAL) {
+			camel_imapx_stream_set_literal(is, len);
+			while ((tok = camel_imapx_stream_getl(is, &token, &len)) > 0) {
+				printf("Skip literal data '%.*s'\n", (int)len, token);
+			}
+		}
+	} while (tok != '\n' && tok >= 0);
+
+	if (tok < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/camel/providers/imapx/camel-imapx-stream.h b/camel/providers/imapx/camel-imapx-stream.h
new file mode 100644
index 0000000..b565b4c
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-stream.h
@@ -0,0 +1,95 @@
+/*
+ *  Copyright (C) 2000 Ximian Inc.
+ *
+ *  Authors: Michael Zucchi <notzed ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _CAMEL_IMAPX_STREAM_H
+#define _CAMEL_IMAPX_STREAM_H
+
+#include <camel/camel-stream.h>
+
+#define CAMEL_IMAPX_STREAM(obj)         CAMEL_CHECK_CAST (obj, camel_imapx_stream_get_type (), CamelIMAPXStream)
+#define CAMEL_IMAPX_STREAM_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapx_stream_get_type (), CamelIMAPXStreamClass)
+#define CAMEL_IS_IMAPX_STREAM(obj)      CAMEL_CHECK_TYPE (obj, camel_imapx_stream_get_type ())
+
+typedef struct _CamelIMAPXStreamClass CamelIMAPXStreamClass;
+typedef struct _CamelIMAPXStream CamelIMAPXStream;
+
+typedef enum {
+	IMAP_TOK_PROTOCOL = -2,
+	IMAP_TOK_ERROR = -1,
+	IMAP_TOK_TOKEN = 256,
+	IMAP_TOK_STRING,
+	IMAP_TOK_INT,
+	IMAP_TOK_LITERAL,
+} camel_imapx_token_t;
+
+struct _CamelIMAPXStream {
+	CamelStream parent;
+
+	CamelStream *source;
+
+	/*int state;*/
+	unsigned char *buf, *ptr, *end;
+	unsigned int literal;
+
+	unsigned int unget;
+	camel_imapx_token_t unget_tok;
+	unsigned char *unget_token;
+	unsigned int unget_len;
+
+	unsigned char *tokenbuf, *tokenptr, *tokenend;
+};
+
+struct _CamelIMAPXStreamClass {
+	CamelStreamClass parent_class;
+};
+
+CamelType	 camel_imapx_stream_get_type	(void);
+
+CamelStream     *camel_imapx_stream_new		(CamelStream *source);
+
+int 		 camel_imapx_stream_buffered	(CamelIMAPXStream *is);
+
+camel_imapx_token_t camel_imapx_stream_token	(CamelIMAPXStream *is, unsigned char **start, unsigned int *len); /* throws IO,PARSE exception */
+void		 camel_imapx_stream_ungettoken	(CamelIMAPXStream *is, camel_imapx_token_t tok, unsigned char *token, unsigned int len);
+
+void		 camel_imapx_stream_set_literal	(CamelIMAPXStream *is, unsigned int literal);
+int 		 camel_imapx_stream_gets		(CamelIMAPXStream *is, unsigned char **start, unsigned int *len);
+int 		 camel_imapx_stream_getl		(CamelIMAPXStream *is, unsigned char **start, unsigned int *len);
+
+/* all throw IO,PARSE exceptions */
+
+/* gets an atom, upper-cases */
+int		 camel_imapx_stream_atom		(CamelIMAPXStream *is, unsigned char **start, unsigned int *len);
+/* gets an atom or string */
+int		 camel_imapx_stream_astring	(CamelIMAPXStream *is, unsigned char **start);
+/* gets a NIL or a string, start==NULL if NIL */
+int		 camel_imapx_stream_nstring	(CamelIMAPXStream *is, unsigned char **start);
+/* gets a NIL or string into a stream, stream==NULL if NIL */
+int		 camel_imapx_stream_nstring_stream(CamelIMAPXStream *is, CamelStream **stream);
+/* gets 'text' */
+int		 camel_imapx_stream_text		(CamelIMAPXStream *is, unsigned char **text);
+
+/* gets a 'number' */
+guint32		 camel_imapx_stream_number(CamelIMAPXStream *is);
+
+/* skips the rest of a line, including literals, etc */
+int camel_imapx_stream_skip(CamelIMAPXStream *is);
+
+#endif /* ! _CAMEL_IMAPX_STREAM_H */
diff --git a/camel/providers/imapx/camel-imapx-summary.c b/camel/providers/imapx/camel-imapx-summary.c
new file mode 100644
index 0000000..8e9f5f9
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-summary.c
@@ -0,0 +1,489 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ *  Authors:
+ *    Michael Zucchi <notzed ximian com>
+ *    Dan Winship <danw ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "camel-db.h"
+#include "camel-folder.h"
+#include "camel-file-utils.h"
+#include "camel-string-utils.h"
+#include "camel-store.h"
+
+#include "camel-imapx-summary.h"
+//#include "camel-imap-utils.h"
+
+#define CAMEL_IMAPX_SUMMARY_VERSION (3)
+
+#define EXTRACT_FIRST_DIGIT(val) val=strtoul (part, &part, 10);
+#define EXTRACT_DIGIT(val) if (*part) part++; val=strtoul (part, &part, 10);
+
+static gint summary_header_load (CamelFolderSummary *, FILE *);
+static gint summary_header_save (CamelFolderSummary *, FILE *);
+
+static CamelMessageInfo *message_info_load (CamelFolderSummary *s, FILE *in);
+static gint message_info_save (CamelFolderSummary *s, FILE *out,
+			      CamelMessageInfo *info);
+static gboolean info_set_user_flag (CamelMessageInfo *info, const gchar *id, gboolean state);
+static CamelMessageContentInfo *content_info_load (CamelFolderSummary *s, FILE *in);
+static gint content_info_save (CamelFolderSummary *s, FILE *out,
+			      CamelMessageContentInfo *info);
+
+static gint summary_header_from_db (CamelFolderSummary *s, CamelFIRecord *mir);
+static CamelFIRecord * summary_header_to_db (CamelFolderSummary *s, CamelException *ex);
+static CamelMIRecord * message_info_to_db (CamelFolderSummary *s, CamelMessageInfo *info);
+static CamelMessageInfo * message_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir);
+static gint content_info_to_db (CamelFolderSummary *s, CamelMessageContentInfo *info, CamelMIRecord *mir);
+static CamelMessageContentInfo * content_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir);
+
+static void camel_imapx_summary_class_init (CamelIMAPXSummaryClass *klass);
+static void camel_imapx_summary_init       (CamelIMAPXSummary *obj);
+
+static CamelFolderSummaryClass *camel_imapx_summary_parent;
+
+CamelType
+camel_imapx_summary_get_type (void)
+{
+	static CamelType type = CAMEL_INVALID_TYPE;
+
+	if (type == CAMEL_INVALID_TYPE) {
+		type = camel_type_register(
+			camel_folder_summary_get_type(), "CamelIMAPXSummary",
+			sizeof (CamelIMAPXSummary),
+			sizeof (CamelIMAPXSummaryClass),
+			(CamelObjectClassInitFunc) camel_imapx_summary_class_init,
+			NULL,
+			(CamelObjectInitFunc) camel_imapx_summary_init,
+			NULL);
+	}
+
+	return type;
+}
+
+static CamelMessageInfo *
+imapx_message_info_clone(CamelFolderSummary *s, const CamelMessageInfo *mi)
+{
+	CamelIMAPXMessageInfo *to;
+	const CamelIMAPXMessageInfo *from = (const CamelIMAPXMessageInfo *)mi;
+
+	to = (CamelIMAPXMessageInfo *)camel_imapx_summary_parent->message_info_clone(s, mi);
+	to->server_flags = from->server_flags;
+
+	/* FIXME: parent clone should do this */
+	to->info.content = camel_folder_summary_content_info_new(s);
+
+	return (CamelMessageInfo *)to;
+}
+
+static void
+camel_imapx_summary_class_init (CamelIMAPXSummaryClass *klass)
+{
+	CamelFolderSummaryClass *cfs_class = (CamelFolderSummaryClass *) klass;
+
+	camel_imapx_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS (camel_type_get_global_classfuncs (camel_folder_summary_get_type()));
+
+	cfs_class->message_info_clone = imapx_message_info_clone;
+
+	cfs_class->summary_header_load = summary_header_load;
+	cfs_class->summary_header_save = summary_header_save;
+	cfs_class->message_info_load = message_info_load;
+	cfs_class->message_info_save = message_info_save;
+	cfs_class->content_info_load = content_info_load;
+	cfs_class->content_info_save = content_info_save;
+
+	cfs_class->summary_header_to_db = summary_header_to_db;
+	cfs_class->summary_header_from_db = summary_header_from_db;
+	cfs_class->message_info_to_db = message_info_to_db;
+	cfs_class->message_info_from_db = message_info_from_db;
+	cfs_class->content_info_to_db = content_info_to_db;
+	cfs_class->content_info_from_db = content_info_from_db;
+
+	cfs_class->info_set_user_flag = info_set_user_flag;
+}
+
+static void
+camel_imapx_summary_init (CamelIMAPXSummary *obj)
+{
+	CamelFolderSummary *s = (CamelFolderSummary *)obj;
+
+	/* subclasses need to set the right instance data sizes */
+	s->message_info_size = sizeof(CamelIMAPXMessageInfo);
+	s->content_info_size = sizeof(CamelIMAPXMessageContentInfo);
+}
+
+static gint
+sort_uid_cmp (gpointer enc, gint len1, gpointer  data1, gint len2, gpointer data2)
+{
+	static gchar *sa1=NULL, *sa2=NULL;
+	static gint l1=0, l2=0;
+	gint a1, a2;
+
+	if (l1 < len1+1) {
+		sa1 = g_realloc (sa1, len1+1);
+		l1 = len1+1;
+	}
+	if (l2 < len2+1) {
+		sa2 = g_realloc (sa2, len2+1);
+		l2 = len2+1;
+	}
+	strncpy (sa1, data1, len1);sa1[len1] = 0;
+	strncpy (sa2, data2, len2);sa2[len2] = 0;
+
+	a1 = strtoul (sa1, NULL, 10);
+	a2 = strtoul (sa2, NULL, 10);
+
+	return (a1 < a1) ? -1 : (a1 > a2) ? 1 : 0;
+}
+
+static gint
+uid_compare (gconstpointer va, gconstpointer vb)
+{
+	const gchar **sa = (const gchar **)va, **sb = (const gchar **)vb;
+	gulong a, b;
+
+	a = strtoul (*sa, NULL, 10);
+	b = strtoul (*sb, NULL, 10);
+	if (a < b)
+		return -1;
+	else if (a == b)
+		return 0;
+	else
+		return 1;
+}
+
+/**
+ * camel_imapx_summary_new:
+ * @folder: Parent folder.
+ * @filename: the file to store the summary in.
+ *
+ * This will create a new CamelIMAPXSummary object and read in the
+ * summary data from disk, if it exists.
+ *
+ * Return value: A new CamelIMAPXSummary object.
+ **/
+CamelFolderSummary *
+camel_imapx_summary_new (struct _CamelFolder *folder, const gchar *filename)
+{
+	CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY (camel_object_new (camel_imapx_summary_get_type ()));
+	CamelException ex;
+	camel_exception_init (&ex);
+
+	summary->folder = folder;
+	/* Don't do DB sort. Its pretty slow to load */
+	if (folder && 0) {
+		camel_db_set_collate (folder->parent_store->cdb_r, "uid", "imapx_uid_sort", (CamelDBCollate)sort_uid_cmp);
+		summary->sort_by = "uid";
+		summary->collate = "imapx_uid_sort";
+	}
+
+	camel_folder_summary_set_build_content (summary, TRUE);
+	camel_folder_summary_set_filename (summary, filename);
+
+	if (camel_folder_summary_load_from_db (summary, &ex) == -1) {
+		/* FIXME: Isn't this dangerous ? We clear the summary
+		if it cannot be loaded, for some random reason.
+		We need to pass the ex and find out why it is not loaded etc. ? */
+		camel_folder_summary_clear_db (summary);
+		g_message ("Unable to load summary: %s\n", camel_exception_get_description (&ex));
+		camel_exception_clear (&ex);
+	}
+
+	g_ptr_array_sort (summary->uids, (GCompareFunc) uid_compare);
+
+	return summary;
+}
+
+static gint
+summary_header_from_db (CamelFolderSummary *s, CamelFIRecord *mir)
+{
+	CamelIMAPXSummary *ims = CAMEL_IMAPX_SUMMARY (s);
+	gchar *part;
+
+	if (camel_imapx_summary_parent->summary_header_from_db (s, mir) == -1)
+		return -1;
+
+	part = mir->bdata;
+
+	if (part) {
+		EXTRACT_FIRST_DIGIT (ims->version)
+	}
+
+	if (part) {
+		EXTRACT_DIGIT (ims->validity)
+	}
+
+	if (ims->version > CAMEL_IMAPX_SUMMARY_VERSION) {
+		g_warning("Unkown summary version\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+}
+
+static gint
+summary_header_load (CamelFolderSummary *s, FILE *in)
+{
+	CamelIMAPXSummary *ims = CAMEL_IMAPX_SUMMARY (s);
+
+	if (camel_imapx_summary_parent->summary_header_load (s, in) == -1)
+		return -1;
+
+	/* Legacy version */
+	if (s->version == 0x30c)
+		return camel_file_util_decode_uint32(in, &ims->validity);
+
+	/* Version 1 */
+	if (camel_file_util_decode_fixed_int32(in, (gint32 *) &ims->version) == -1)
+		return -1;
+
+	if (ims->version == 2) {
+		/* Version 2: for compat with version 2 of the imap4 summary files */
+		gint have_mlist;
+
+		if (camel_file_util_decode_fixed_int32 (in, &have_mlist) == -1)
+			return -1;
+	}
+
+	if (camel_file_util_decode_fixed_int32(in, (gint32 *) &ims->validity) == -1)
+		return -1;
+
+	if (ims->version > CAMEL_IMAPX_SUMMARY_VERSION) {
+		g_warning("Unkown summary version\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+}
+
+static CamelFIRecord *
+summary_header_to_db (CamelFolderSummary *s, CamelException *ex)
+{
+	CamelIMAPXSummary *ims = CAMEL_IMAPX_SUMMARY(s);
+	struct _CamelFIRecord *fir;
+
+	fir = camel_imapx_summary_parent->summary_header_to_db (s, ex);
+	if (!fir)
+		return NULL;
+	fir->bdata = g_strdup_printf ("%d %u", CAMEL_IMAPX_SUMMARY_VERSION, ims->validity);
+
+	return fir;
+}
+
+static gint
+summary_header_save (CamelFolderSummary *s, FILE *out)
+{
+	CamelIMAPXSummary *ims = CAMEL_IMAPX_SUMMARY(s);
+
+	if (camel_imapx_summary_parent->summary_header_save (s, out) == -1)
+		return -1;
+
+	camel_file_util_encode_fixed_int32(out, CAMEL_IMAPX_SUMMARY_VERSION);
+
+	return camel_file_util_encode_fixed_int32(out, ims->validity);
+}
+
+static CamelMessageInfo *
+message_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir)
+{
+	CamelMessageInfo *info;
+	CamelIMAPXMessageInfo *iinfo;
+
+	info = camel_imapx_summary_parent->message_info_from_db (s, mir);
+	if (info) {
+		gchar *part = g_strdup (mir->bdata), *tmp;
+		tmp = part;
+		iinfo = (CamelIMAPXMessageInfo *)info;
+		EXTRACT_FIRST_DIGIT (iinfo->server_flags)
+		g_free (tmp);
+	}
+
+	return info;
+}
+
+static CamelMessageInfo *
+message_info_load (CamelFolderSummary *s, FILE *in)
+{
+	CamelMessageInfo *info;
+	CamelIMAPXMessageInfo *iinfo;
+
+	info = camel_imapx_summary_parent->message_info_load (s, in);
+	if (info) {
+		iinfo = (CamelIMAPXMessageInfo *)info;
+
+		if (camel_file_util_decode_uint32 (in, &iinfo->server_flags) == -1)
+			goto error;
+	}
+
+	return info;
+error:
+	camel_message_info_free(info);
+	return NULL;
+}
+
+static CamelMIRecord *
+message_info_to_db (CamelFolderSummary *s, CamelMessageInfo *info)
+{
+	CamelIMAPXMessageInfo *iinfo = (CamelIMAPXMessageInfo *)info;
+	struct _CamelMIRecord *mir;
+
+	mir = camel_imapx_summary_parent->message_info_to_db (s, info);
+	if (mir)
+		mir->bdata = g_strdup_printf ("%u", iinfo->server_flags);
+
+	return mir;
+}
+
+static gint
+message_info_save (CamelFolderSummary *s, FILE *out, CamelMessageInfo *info)
+{
+	CamelIMAPXMessageInfo *iinfo = (CamelIMAPXMessageInfo *)info;
+
+	if (camel_imapx_summary_parent->message_info_save (s, out, info) == -1)
+		return -1;
+
+	return camel_file_util_encode_uint32 (out, iinfo->server_flags);
+}
+
+static gboolean
+info_set_user_flag (CamelMessageInfo *info, const gchar *id, gboolean state)
+{
+	gboolean res;
+
+	res = camel_imapx_summary_parent->info_set_user_flag (info, id, state);
+
+	/* there was a change, so do not forget to store it to server */
+	if (res)
+		((CamelIMAPXMessageInfo *)info)->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
+
+	return res;
+}
+
+static CamelMessageContentInfo *
+content_info_from_db (CamelFolderSummary *s, CamelMIRecord *mir)
+{
+	gchar *part = mir->cinfo;
+	guint32 type=0;
+
+	if (part) {
+		if (*part == ' ')
+			part++;
+		if (part) {
+			EXTRACT_FIRST_DIGIT (type);
+		}
+	}
+	mir->cinfo = part;
+	if (type)
+		return camel_imapx_summary_parent->content_info_from_db (s, mir);
+	else
+		return camel_folder_summary_content_info_new (s);
+}
+
+static CamelMessageContentInfo *
+content_info_load (CamelFolderSummary *s, FILE *in)
+{
+	if (fgetc (in))
+		return camel_imapx_summary_parent->content_info_load (s, in);
+	else
+		return camel_folder_summary_content_info_new (s);
+}
+
+static gint
+content_info_to_db (CamelFolderSummary *s, CamelMessageContentInfo *info, CamelMIRecord *mir)
+{
+	gchar *oldr;
+	if (info->type) {
+		oldr = mir->cinfo;
+		mir->cinfo = oldr ? g_strdup_printf("%s 1", oldr) : g_strdup ("1");
+		g_free(oldr);
+		return camel_imapx_summary_parent->content_info_to_db (s, info, mir);
+	} else {
+		oldr = mir->cinfo;
+		mir->cinfo = oldr ? g_strdup_printf("%s 0", oldr) : g_strdup ("0");
+		g_free(oldr);
+		return 0;
+	}
+}
+
+static gint
+content_info_save (CamelFolderSummary *s, FILE *out,
+		   CamelMessageContentInfo *info)
+{
+	if (info->type) {
+		fputc (1, out);
+		return camel_imapx_summary_parent->content_info_save (s, out, info);
+	} else
+		return fputc (0, out);
+}
+
+void
+camel_imapx_summary_add_offline (CamelFolderSummary *summary, const gchar *uid,
+				CamelMimeMessage *message,
+				const CamelMessageInfo *info)
+{
+	CamelIMAPXMessageInfo *mi;
+	const CamelFlag *flag;
+	const CamelTag *tag;
+
+	/* Create summary entry */
+	mi = (CamelIMAPXMessageInfo *)camel_folder_summary_info_new_from_message (summary, message);
+
+	/* Copy flags 'n' tags */
+	mi->info.flags = camel_message_info_flags(info);
+
+	flag = camel_message_info_user_flags(info);
+	while (flag) {
+		camel_message_info_set_user_flag((CamelMessageInfo *)mi, flag->name, TRUE);
+		flag = flag->next;
+	}
+	tag = camel_message_info_user_tags(info);
+	while (tag) {
+		camel_message_info_set_user_tag((CamelMessageInfo *)mi, tag->name, tag->value);
+		tag = tag->next;
+	}
+
+	mi->info.size = camel_message_info_size(info);
+	mi->info.uid = camel_pstring_strdup (uid);
+
+	camel_folder_summary_add (summary, (CamelMessageInfo *)mi);
+}
+
+void
+camel_imapx_summary_add_offline_uncached (CamelFolderSummary *summary, const gchar *uid,
+					 const CamelMessageInfo *info)
+{
+	CamelIMAPXMessageInfo *mi;
+
+	mi = camel_message_info_clone(info);
+	mi->info.uid = camel_pstring_strdup(uid);
+
+	camel_folder_summary_add (summary, (CamelMessageInfo *)mi);
+}
diff --git a/camel/providers/imapx/camel-imapx-summary.h b/camel/providers/imapx/camel-imapx-summary.h
new file mode 100644
index 0000000..6da7910
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-summary.h
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ *  Authors:
+ *    Michael Zucchi <notzed ximian com>
+ *    Dan Winship <danw ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CAMEL_IMAPX_SUMMARY_H
+#define _CAMEL_IMAPX_SUMMARY_H
+
+//#include "camel-imap-types.h"
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-exception.h>
+
+#define CAMEL_IMAPX_SUMMARY(obj)         CAMEL_CHECK_CAST (obj, camel_imapx_summary_get_type (), CamelIMAPXSummary)
+#define CAMEL_IMAPX_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapx_summary_get_type (), CamelIMAPXSummaryClass)
+#define CAMEL_IS_IMAPX_SUMMARY(obj)      CAMEL_CHECK_TYPE (obj, camel_imapx_summary_get_type ())
+
+#define CAMEL_IMAPX_SERVER_FLAGS (CAMEL_MESSAGE_ANSWERED | \
+				 CAMEL_MESSAGE_DELETED | \
+				 CAMEL_MESSAGE_DRAFT | \
+				 CAMEL_MESSAGE_FLAGGED | \
+				 CAMEL_MESSAGE_SEEN)
+
+G_BEGIN_DECLS
+
+typedef struct _CamelIMAPXSummaryClass CamelIMAPXSummaryClass;
+typedef struct _CamelIMAPXSummary CamelIMAPXSummary;
+
+typedef struct _CamelIMAPXMessageContentInfo {
+	CamelMessageContentInfo info;
+
+} CamelIMAPXMessageContentInfo;
+
+typedef struct _CamelIMAPXMessageInfo {
+	CamelMessageInfoBase info;
+
+	guint32 server_flags;
+} CamelIMAPXMessageInfo;
+
+struct _CamelIMAPXSummary {
+	CamelFolderSummary parent;
+
+	guint32 version;
+	guint32 validity;
+};
+
+struct _CamelIMAPXSummaryClass {
+	CamelFolderSummaryClass parent_class;
+
+};
+
+CamelType               camel_imapx_summary_get_type     (void);
+CamelFolderSummary *camel_imapx_summary_new          (struct _CamelFolder *folder, const gchar *filename);
+
+void camel_imapx_summary_add_offline (CamelFolderSummary *summary,
+				     const gchar *uid,
+				     CamelMimeMessage *message,
+				     const CamelMessageInfo *info);
+
+void camel_imapx_summary_add_offline_uncached (CamelFolderSummary *summary,
+					      const gchar *uid,
+					      const CamelMessageInfo *info);
+
+G_END_DECLS
+
+#endif /* ! _CAMEL_IMAPX_SUMMARY_H */
diff --git a/camel/providers/imapx/camel-imapx-tokenise.h b/camel/providers/imapx/camel-imapx-tokenise.h
new file mode 100644
index 0000000..0309d39
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-tokenise.h
@@ -0,0 +1,175 @@
+/* ANSI-C code produced by gperf version 3.0.1 */
+/* Command-line: gperf -H imap_hash -N imap_tokenise_struct -L ANSI-C -o -t -k'1,$' camel-imapx-tokens.txt  */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf gnu org>."
+#endif
+
+#line 3 "camel-imapx-tokens.txt"
+struct _imap_keyword { char *name; camel_imapx_id_t id; };
+
+#define TOTAL_KEYWORDS 31
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 14
+#define MIN_HASH_VALUE 2
+#define MAX_HASH_VALUE 49
+/* maximum key range = 48, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+imap_hash (register const char *str, register unsigned int len)
+{
+  static unsigned char asso_values[] =
+    {
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 20, 15,  0, 20,  0,
+      20, 50,  5,  0, 50,  0, 20, 50, 15,  0,
+      15, 50,  0,  0, 10, 10, 50, 50, 50,  5,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50
+    };
+  return len + asso_values[(unsigned char)str[len - 1]] + asso_values[(unsigned char)str[0]];
+}
+
+#ifdef __GNUC__
+__inline
+#endif
+struct _imap_keyword *
+imap_tokenise_struct (register const char *str, register unsigned int len)
+{
+  static struct _imap_keyword wordlist[] =
+    {
+      {""}, {""},
+#line 22 "camel-imapx-tokens.txt"
+      {"OK",		IMAP_OK},
+      {""}, {""}, {""},
+#line 13 "camel-imapx-tokens.txt"
+      {"EXISTS",		IMAP_EXISTS},
+#line 14 "camel-imapx-tokens.txt"
+      {"EXPUNGE",	IMAP_EXPUNGE},
+#line 12 "camel-imapx-tokens.txt"
+      {"ENVELOPE",	IMAP_ENVELOPE},
+      {""},
+#line 27 "camel-imapx-tokens.txt"
+      {"READ-WRITE",	IMAP_READ_WRITE},
+#line 30 "camel-imapx-tokens.txt"
+      {"RFC822.SIZE",	IMAP_RFC822_SIZE},
+#line 17 "camel-imapx-tokens.txt"
+      {"INTERNALDATE",	IMAP_INTERNALDATE},
+#line 29 "camel-imapx-tokens.txt"
+      {"RFC822.HEADER",	IMAP_RFC822_HEADER},
+#line 26 "camel-imapx-tokens.txt"
+      {"READ-ONLY",	IMAP_READ_ONLY},
+#line 11 "camel-imapx-tokens.txt"
+      {"CAPABILITY",	IMAP_CAPABILITY},
+#line 28 "camel-imapx-tokens.txt"
+      {"RECENT",		IMAP_RECENT},
+#line 21 "camel-imapx-tokens.txt"
+      {"NO",		IMAP_NO},
+#line 10 "camel-imapx-tokens.txt"
+      {"BYE",		IMAP_BYE},
+#line 32 "camel-imapx-tokens.txt"
+      {"TRYCREATE",	IMAP_TRYCREATE},
+#line 23 "camel-imapx-tokens.txt"
+      {"PARSE",		IMAP_PARSE},
+#line 31 "camel-imapx-tokens.txt"
+      {"RFC822.TEXT",	IMAP_RFC822_TEXT},
+#line 20 "camel-imapx-tokens.txt"
+      {"NEWNAME",	IMAP_NEWNAME},
+      {""},
+#line 8 "camel-imapx-tokens.txt"
+      {"BODY",		IMAP_BODY},
+#line 16 "camel-imapx-tokens.txt"
+      {"FLAGS",		IMAP_FLAGS},
+#line 34 "camel-imapx-tokens.txt"
+      {"UIDVALIDITY",	IMAP_UIDVALIDITY},
+#line 25 "camel-imapx-tokens.txt"
+      {"PREAUTH",	IMAP_PREAUTH},
+#line 9 "camel-imapx-tokens.txt"
+      {"BODYSTRUCTURE",	IMAP_BODYSTRUCTURE},
+#line 24 "camel-imapx-tokens.txt"
+      {"PERMANENTFLAGS",	IMAP_PERMANENTFLAGS},
+#line 15 "camel-imapx-tokens.txt"
+      {"FETCH",		IMAP_FETCH},
+#line 35 "camel-imapx-tokens.txt"
+      {"UNSEEN",		IMAP_UNSEEN},
+      {""},
+#line 33 "camel-imapx-tokens.txt"
+      {"UID",		IMAP_UID},
+#line 18 "camel-imapx-tokens.txt"
+      {"LIST",		IMAP_LIST},
+#line 5 "camel-imapx-tokens.txt"
+      {"ALERT",          IMAP_ALERT},
+      {""}, {""},
+#line 7 "camel-imapx-tokens.txt"
+      {"BAD",		IMAP_BAD},
+#line 19 "camel-imapx-tokens.txt"
+      {"LSUB",		IMAP_LSUB},
+      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 6 "camel-imapx-tokens.txt"
+      {"APPENDUID",	IMAP_APPENDUID}
+    };
+
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = imap_hash (str, len);
+
+      if (key <= MAX_HASH_VALUE && key >= 0)
+        {
+          register const char *s = wordlist[key].name;
+
+          if (*str == *s && !strcmp (str + 1, s + 1))
+            return &wordlist[key];
+        }
+    }
+  return 0;
+}
diff --git a/camel/providers/imapx/camel-imapx-tokens.txt b/camel/providers/imapx/camel-imapx-tokens.txt
new file mode 100644
index 0000000..bd91561
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-tokens.txt
@@ -0,0 +1,35 @@
+/* This contains all of the keywords we care about.  These
+   can be converted to an id very efficiently */
+struct _imap_keyword { char *name; camel_imapx_id_t id; };
+%%
+ALERT,          IMAP_ALERT
+APPENDUID,	IMAP_APPENDUID
+BAD,		IMAP_BAD
+BODY,		IMAP_BODY
+BODYSTRUCTURE,	IMAP_BODYSTRUCTURE
+BYE,		IMAP_BYE
+CAPABILITY,	IMAP_CAPABILITY
+ENVELOPE,	IMAP_ENVELOPE
+EXISTS,		IMAP_EXISTS
+EXPUNGE,	IMAP_EXPUNGE
+FETCH,		IMAP_FETCH
+FLAGS,		IMAP_FLAGS
+INTERNALDATE,	IMAP_INTERNALDATE
+LIST,		IMAP_LIST
+LSUB,		IMAP_LSUB
+NEWNAME,	IMAP_NEWNAME
+NO,		IMAP_NO
+OK,		IMAP_OK
+PARSE,		IMAP_PARSE
+PERMANENTFLAGS,	IMAP_PERMANENTFLAGS
+PREAUTH,	IMAP_PREAUTH
+READ-ONLY,	IMAP_READ_ONLY
+READ-WRITE,	IMAP_READ_WRITE
+RECENT,		IMAP_RECENT
+RFC822.HEADER,	IMAP_RFC822_HEADER
+RFC822.SIZE,	IMAP_RFC822_SIZE
+RFC822.TEXT,	IMAP_RFC822_TEXT
+TRYCREATE,	IMAP_TRYCREATE
+UID,		IMAP_UID
+UIDVALIDITY,	IMAP_UIDVALIDITY
+UNSEEN,		IMAP_UNSEEN
diff --git a/camel/providers/imapx/camel-imapx-utils.c b/camel/providers/imapx/camel-imapx-utils.c
new file mode 100644
index 0000000..33f4403
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-utils.c
@@ -0,0 +1,1384 @@
+
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-store.h>
+#include <camel/camel-utf8.h>
+#include <camel/camel-string-utils.h>
+
+#include "camel-imapx-folder.h"
+#include "camel-imapx-stream.h"
+#include "camel-imapx-utils.h"
+#include "camel-imapx-exception.h"
+#include "libedataserver/e-memory.h"
+
+/* high-level parser state */
+#define p(x)
+/* debug */
+#define d(x)
+
+#include "camel-imapx-tokenise.h"
+
+#ifdef __GNUC__
+__inline
+#endif
+camel_imapx_id_t
+imap_tokenise (register const char *str, register unsigned int len)
+{
+	struct _imap_keyword *k = imap_tokenise_struct(str, len);
+
+	if (k)
+		return k->id;
+	return 0;
+}
+
+/* flag table */
+static struct {
+	char *name;
+	guint32 flag;
+} flag_table[] = {
+	{ "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
+	{ "\\DELETED", CAMEL_MESSAGE_DELETED },
+	{ "\\DRAFT", CAMEL_MESSAGE_DRAFT },
+	{ "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
+	{ "\\SEEN", CAMEL_MESSAGE_SEEN },
+	{ "\\RECENT", CAMEL_MESSAGE_RECENT },
+	{ "\\*", CAMEL_MESSAGE_USER },
+};
+
+/* utility functions
+   shoudl this be part of imapx-driver? */
+/* mabye this should be a stream op? */
+void
+imap_parse_flags(CamelIMAPXStream *stream, guint32 *flagsp, CamelFlag **user_flagsp)
+/* throws IO,PARSE exception */
+{
+	int tok, len, i;
+	unsigned char *token, *p, c;
+	guint32 flags = 0;
+
+	*flagsp = flags;
+
+	tok = camel_imapx_stream_token(stream, &token, &len);
+	if (tok == '(') {
+		do {
+			tok = camel_imapx_stream_token(stream, &token, &len);
+			if (tok == IMAP_TOK_TOKEN) {
+				p = token;
+				// FIXME: ascii_toupper
+				while ((c=*p))
+					*p++ = toupper(c);
+				for (i=0;i<(int)(sizeof(flag_table)/sizeof(flag_table[0]));i++)
+					if (!strcmp(token, flag_table[i].name)) {
+						flags |= flag_table[i].flag;
+						goto found;
+					}
+				if (user_flagsp)
+					camel_flag_set(user_flagsp, token, TRUE);
+			found:
+				tok = tok; /* fixes stupid warning */
+			} else if (tok != ')') {
+				camel_exception_throw(1, "expecting flag");
+			}
+		} while (tok != ')');
+	} else {
+		camel_exception_throw(1, "expecting flag list");
+	}
+
+	*flagsp = flags;
+}
+
+void
+imap_write_flags(CamelStream *stream, guint32 flags, CamelFlag *user_flags)
+/* throws IO exception */
+{
+	int i;
+
+	/* all this ugly exception throwing goes away once camel streams throw their own? */
+	if (camel_stream_write(stream, "(", 1) == -1)
+		camel_exception_throw(1, "io error: %s", strerror(errno));
+
+	for (i=0;flags!=0 && i<(int)(sizeof(flag_table)/sizeof(flag_table[0]));i++) {
+		if (flag_table[i].flag & flags) {
+			if (camel_stream_write(stream, flag_table[i].name, strlen(flag_table[i].name)) == -1)
+				camel_exception_throw(1, "io error: %s", strerror(errno));
+			flags &= ~flag_table[i].flag;
+			if (flags != 0 && user_flags == NULL)
+				if (camel_stream_write(stream, " ", 1) == -1)
+					camel_exception_throw(1, "io error: %s", strerror(errno));
+		}
+	}
+
+	while (user_flags) {
+		if (camel_stream_write(stream, user_flags->name, strlen(user_flags->name)) == -1)
+			camel_exception_throw(1, "io error: %s", strerror(errno));
+		if (user_flags->next && camel_stream_write(stream, " ", 1) == -1)
+			camel_exception_throw(1, "io error: %s", strerror(errno));
+		user_flags = user_flags->next;
+	}
+
+	if (camel_stream_write(stream, ")", 1) == -1)
+		camel_exception_throw(1, "io error: %s", strerror(errno));
+}
+
+/*
+capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1"
+                    [SPACE 1#capability]
+                    ;; IMAP4rev1 servers which offer RFC 1730
+                    ;; compatibility MUST list "IMAP4" as the first
+                    ;; capability.
+*/
+
+struct {
+	char *name;
+	guint32 flag;
+} capa_table[] = {
+	{ "IMAP4", IMAP_CAPABILITY_IMAP4 },
+	{ "IMAP4REV1", IMAP_CAPABILITY_IMAP4REV1 },
+	{ "STATUS",  IMAP_CAPABILITY_STATUS } ,
+	{ "NAMESPACE", IMAP_CAPABILITY_NAMESPACE },
+	{ "UIDPLUS",  IMAP_CAPABILITY_UIDPLUS },
+	{ "LITERAL+", IMAP_CAPABILITY_LITERALPLUS },
+	{ "STARTTLS", IMAP_CAPABILITY_STARTTLS },
+};
+
+struct _capability_info *
+imap_parse_capability(CamelIMAPXStream *stream)
+{
+	int tok, len, i;
+	unsigned char *token, *p, c;
+	struct _capability_info * volatile cinfo;
+
+	cinfo = g_malloc0(sizeof(*cinfo));
+
+	/* FIXME: handle auth types */
+	CAMEL_TRY {
+		while ( (tok = camel_imapx_stream_token(stream, &token, &len)) != '\n') {
+			switch(tok) {
+			case IMAP_TOK_TOKEN:
+			case IMAP_TOK_STRING:
+				p = token;
+				while ((c = *p))
+					*p++ = toupper(c);
+			case IMAP_TOK_INT:
+				printf(" cap: '%s'\n", token);
+				for (i=0;i<(int)(sizeof(capa_table)/sizeof(capa_table[0]));i++)
+					if (strcmp(token, capa_table[i].name))
+						cinfo->capa |= capa_table[i].flag;
+				break;
+			default:
+				camel_exception_throw(1, "capability: expecting name");
+				break;
+			}
+		}
+	} CAMEL_CATCH(ex) {
+		imap_free_capability(cinfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return cinfo;
+}
+
+void imap_free_capability(struct _capability_info *cinfo)
+{
+	g_free(cinfo);
+}
+
+/*
+body            ::= "(" body_type_1part / body_type_mpart ")"
+
+body_extension  ::= nstring / number / "(" 1#body_extension ")"
+                    ;; Future expansion.  Client implementations
+                    ;; MUST accept body_extension fields.  Server
+                    ;; implementations MUST NOT generate
+                    ;; body_extension fields except as defined by
+                    ;; future standard or standards-track
+                    ;; revisions of this specification.
+
+body_ext_1part  ::= body_fld_md5 [SPACE body_fld_dsp
+                    [SPACE body_fld_lang
+                    [SPACE 1#body_extension]]]
+                    ;; MUST NOT be returned on non-extensible
+                    ;; "BODY" fetch
+
+body_ext_mpart  ::= body_fld_param
+                    [SPACE body_fld_dsp SPACE body_fld_lang
+                    [SPACE 1#body_extension]]
+                    ;; MUST NOT be returned on non-extensible
+                    ;; "BODY" fetch
+
+body_fields     ::= body_fld_param SPACE body_fld_id SPACE
+                    body_fld_desc SPACE body_fld_enc SPACE
+                    body_fld_octets
+
+body_fld_desc   ::= nstring
+
+body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil
+
+body_fld_enc    ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
+                    "QUOTED-PRINTABLE") <">) / string
+
+body_fld_id     ::= nstring
+
+body_fld_lang   ::= nstring / "(" 1#string ")"
+
+body_fld_lines  ::= number
+
+body_fld_md5    ::= nstring
+
+body_fld_octets ::= number
+
+body_fld_param  ::= "(" 1#(string SPACE string) ")" / nil
+
+body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
+                    [SPACE body_ext_1part]
+
+body_type_basic ::= media_basic SPACE body_fields
+                    ;; MESSAGE subtype MUST NOT be "RFC822"
+
+body_type_mpart ::= 1*body SPACE media_subtype
+                    [SPACE body_ext_mpart]
+
+body_type_msg   ::= media_message SPACE body_fields SPACE envelope
+                    SPACE body SPACE body_fld_lines
+
+body_type_text  ::= media_text SPACE body_fields SPACE body_fld_lines
+
+envelope        ::= "(" env_date SPACE env_subject SPACE env_from
+                    SPACE env_sender SPACE env_reply_to SPACE env_to
+                    SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
+                    SPACE env_message_id ")"
+
+env_bcc         ::= "(" 1*address ")" / nil
+
+env_cc          ::= "(" 1*address ")" / nil
+
+env_date        ::= nstring
+
+env_from        ::= "(" 1*address ")" / nil
+
+env_in_reply_to ::= nstring
+
+env_message_id  ::= nstring
+
+env_reply_to    ::= "(" 1*address ")" / nil
+
+env_sender      ::= "(" 1*address ")" / nil
+
+env_subject     ::= nstring
+
+env_to          ::= "(" 1*address ")" / nil
+
+media_basic     ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" /
+                    "MESSAGE" / "VIDEO") <">) / string)
+                    SPACE media_subtype
+                    ;; Defined in [MIME-IMT]
+
+media_message   ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <">
+                    ;; Defined in [MIME-IMT]
+
+media_subtype   ::= string
+                    ;; Defined in [MIME-IMT]
+
+media_text      ::= <"> "TEXT" <"> SPACE media_subtype
+                    ;; Defined in [MIME-IMT]
+
+
+
+ ( "type" "subtype"  body_fields [envelope body body_fld_lines]
+                                 [body_fld_lines]
+
+
+
+ (("TEXT" "PLAIN" ("CHARSET"
+                     "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN"
+                     ("CHARSET" "US-ASCII" "NAME" "cc.diff")
+                     "<960723163407 20117h cac washington edu>"
+                     "Compiler diff" "BASE64" 4554 73) "MIXED"))
+
+*/
+
+/*
+struct _body_fields {
+	CamelContentType *ct;
+	char *msgid, *desc;
+	CamelTransferEncoding encoding;
+	guint32 size;
+	};*/
+
+void
+imap_free_body(struct _CamelMessageContentInfo *cinfo)
+{
+	struct _CamelMessageContentInfo *list, *next;
+
+	list = cinfo->childs;
+	while (list) {
+		next = list->next;
+		imap_free_body(list);
+		list = next;
+	}
+	
+	if (cinfo->type)
+		camel_content_type_unref(cinfo->type);
+	g_free(cinfo->id);
+	g_free(cinfo->description);
+	g_free(cinfo->encoding);
+	g_free(cinfo);
+}
+
+void
+imap_parse_param_list(CamelIMAPXStream *is, struct _camel_header_param **plist)
+{
+	int tok, len;
+	unsigned char *token, *param;
+
+	p(printf("body_fld_param\n"));
+
+	/* body_fld_param  ::= "(" 1#(string SPACE string) ")" / nil */
+	tok = camel_imapx_stream_token(is, &token, &len);
+	if (tok == '(') {
+		while (1) {
+			tok = camel_imapx_stream_token(is, &token, &len);
+			if (tok == ')')
+				break;
+			camel_imapx_stream_ungettoken(is, tok, token, len);
+			
+			camel_imapx_stream_astring(is, &token);
+			param = alloca(strlen(token)+1);
+			strcpy(param, token);
+			camel_imapx_stream_astring(is, &token);
+			camel_header_set_param(plist, param, token);
+		}
+	} /* else check nil?  no need */
+}
+
+struct _CamelContentDisposition *
+imap_parse_ext_optional(CamelIMAPXStream *is)
+{
+	int tok, len;
+	unsigned char *token;
+	struct _CamelContentDisposition * volatile dinfo = NULL;
+
+	/* this parses both extension types, from the body_fld_dsp onwards */
+	/* although the grammars are different, they can be parsed the same way */
+
+	/* body_ext_1part  ::= body_fld_md5 [SPACE body_fld_dsp
+	   [SPACE body_fld_lang
+	   [SPACE 1#body_extension]]]
+	   ;; MUST NOT be returned on non-extensible
+	   ;; "BODY" fetch */
+
+	/* body_ext_mpart  ::= body_fld_param
+	   [SPACE body_fld_dsp SPACE body_fld_lang
+	   [SPACE 1#body_extension]]
+	   ;; MUST NOT be returned on non-extensible
+	   ;; "BODY" fetch */
+
+	CAMEL_TRY {
+		/* body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil */
+
+		tok = camel_imapx_stream_token(is, &token, &len);
+		switch (tok) {
+		case '(':
+			dinfo = g_malloc0(sizeof(*dinfo));
+			dinfo->refcount = 1;
+			/* should be string */
+			camel_imapx_stream_astring(is, &token);
+		
+			dinfo->disposition = g_strdup(token);
+			imap_parse_param_list(is, &dinfo->params);
+		case IMAP_TOK_TOKEN:
+			d(printf("body_fld_dsp: NIL\n"));
+			break;
+		default:
+			camel_exception_throw(1, "body_fld_disp: expecting nil or list");
+		}
+	
+		p(printf("body_fld_lang\n"));
+
+		/* body_fld_lang   ::= nstring / "(" 1#string ")" */
+	
+		/* we just drop the lang string/list, save it somewhere? */
+	
+		tok = camel_imapx_stream_token(is, &token, &len);
+		switch (tok) {
+		case '(':
+			while (1) {
+				tok = camel_imapx_stream_token(is, &token, &len);
+				if (tok == ')') {
+					break;
+				} else if (tok != IMAP_TOK_STRING) {
+					camel_exception_throw(1, "expecting string");
+				}
+			}
+			break;
+		case IMAP_TOK_TOKEN:
+			d(printf("body_fld_lang = nil\n"));
+			/* treat as 'nil' */
+			break;
+		case IMAP_TOK_STRING:
+			/* we have a string */
+			break;
+		case IMAP_TOK_LITERAL:
+			/* we have a literal string */
+			camel_imapx_stream_set_literal(is, len);
+			while ((tok = camel_imapx_stream_getl(is, &token, &len)) > 0) {
+				d(printf("Skip literal data '%.*s'\n", (int)len, token));
+			}
+			break;
+
+		}
+	} CAMEL_CATCH(ex) {
+		if (dinfo)
+			camel_content_disposition_unref(dinfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return dinfo;
+}
+
+struct _CamelMessageContentInfo *
+imap_parse_body_fields(CamelIMAPXStream *is)
+{
+	unsigned char *token, *type;
+	struct _CamelMessageContentInfo *cinfo;
+		
+	/* body_fields     ::= body_fld_param SPACE body_fld_id SPACE
+	   body_fld_desc SPACE body_fld_enc SPACE
+	   body_fld_octets */
+
+	p(printf("body_fields\n"));
+
+	cinfo = g_malloc0(sizeof(*cinfo));
+
+	CAMEL_TRY {
+		/* this should be string not astring */
+		camel_imapx_stream_astring(is, &token);
+		type = alloca(strlen(token)+1);
+		strcpy(type, token);
+		camel_imapx_stream_astring(is, &token);
+		cinfo->type = camel_content_type_new(type, token);
+		imap_parse_param_list(is, &cinfo->type->params);
+	
+		/* body_fld_id     ::= nstring */
+		camel_imapx_stream_nstring(is, &token);
+		cinfo->id = g_strdup(token);
+	
+		/* body_fld_desc   ::= nstring */
+		camel_imapx_stream_nstring(is, &token);
+		cinfo->description = g_strdup(token);
+	
+		/* body_fld_enc    ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
+		   "QUOTED-PRINTABLE") <">) / string */
+		camel_imapx_stream_astring(is, &token);
+		cinfo->encoding = g_strdup(token);
+	
+		/* body_fld_octets ::= number */
+		cinfo->size = camel_imapx_stream_number(is);
+	} CAMEL_CATCH(ex) {
+		imap_free_body(cinfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return cinfo;
+}
+
+struct _camel_header_address *
+imap_parse_address_list(CamelIMAPXStream *is)
+/* throws PARSE,IO exception */
+{
+	int tok, len;
+	unsigned char *token, *host, *mbox;
+	struct _camel_header_address *list = NULL;
+
+	/* "(" 1*address ")" / nil */
+
+	CAMEL_TRY {
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok == '(') {
+			while (1) {
+				struct _camel_header_address *addr, *group = NULL;
+
+				/* address         ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
+				   SPACE addr_host ")" */
+				tok = camel_imapx_stream_token(is, &token, &len);
+				if (tok == ')')
+					break;
+				if (tok != '(')
+					camel_exception_throw(1, "missing '(' for address");
+
+				addr = camel_header_address_new();
+				addr->type = CAMEL_HEADER_ADDRESS_NAME;
+				tok = camel_imapx_stream_nstring(is, &token);
+				addr->name = g_strdup(token);
+				/* we ignore the route, nobody uses it in the real world */
+				tok = camel_imapx_stream_nstring(is, &token);
+
+				/* [RFC-822] group syntax is indicated by a special
+				   form of address structure in which the host name
+				   field is NIL.  If the mailbox name field is also
+				   NIL, this is an end of group marker (semi-colon in
+				   RFC 822 syntax).  If the mailbox name field is
+				   non-NIL, this is a start of group marker, and the
+				   mailbox name field holds the group name phrase. */
+
+				tok = camel_imapx_stream_nstring(is, &mbox);
+				mbox = g_strdup(mbox);
+				tok = camel_imapx_stream_nstring(is, &host);
+				if (host == NULL) {
+					if (mbox == NULL) {
+						group = NULL;
+					} else {
+						d(printf("adding group '%s'\n", mbox));
+						g_free(addr->name);
+						addr->name = mbox;
+						addr->type = CAMEL_HEADER_ADDRESS_GROUP;
+						camel_header_address_list_append(&list, addr);
+						group = addr;
+					}
+				} else {
+					addr->v.addr = g_strdup_printf("%s%s%s", mbox?(char *)mbox:"", host?"@":"", host?(char *)host:"");
+					g_free(mbox);
+					d(printf("adding address '%s'\n", addr->v.addr));
+					if (group != NULL)
+						camel_header_address_add_member(group, addr);
+					else
+						camel_header_address_list_append(&list, addr);
+				}
+				do {
+					tok = camel_imapx_stream_token(is, &token, &len);
+				} while (tok != ')');
+			}
+		} else {
+			d(printf("empty, nil '%s'\n", token));
+		}
+	} CAMEL_CATCH(ex) {
+		camel_header_address_list_clear(&list);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return list;
+}
+
+struct _CamelMessageInfo *
+imap_parse_envelope(CamelIMAPXStream *is)
+{
+	int tok, len;
+	unsigned char *token;
+	struct _camel_header_address *addr, *addr_from;
+	char *addrstr;
+	struct _CamelMessageInfoBase *minfo;
+
+	/* envelope        ::= "(" env_date SPACE env_subject SPACE env_from
+	   SPACE env_sender SPACE env_reply_to SPACE env_to
+	   SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
+	   SPACE env_message_id ")" */
+
+	p(printf("envelope\n"));
+
+	minfo = (CamelMessageInfoBase *)camel_message_info_new(NULL);
+
+	CAMEL_TRY {
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok != '(')
+			camel_exception_throw(1, "envelope: expecting '('");
+
+		/* env_date        ::= nstring */
+		camel_imapx_stream_nstring(is, &token);
+		minfo->date_sent = camel_header_decode_date(token, NULL);
+
+		/* env_subject     ::= nstring */
+		tok = camel_imapx_stream_nstring(is, &token);
+		minfo->subject = camel_pstring_strdup(token);
+
+		/* we merge from/sender into from, append should probably merge more smartly? */
+
+		/* env_from        ::= "(" 1*address ")" / nil */
+		addr_from = imap_parse_address_list(is);
+
+		/* env_sender      ::= "(" 1*address ")" / nil */
+		addr = imap_parse_address_list(is);
+		if (addr_from) {
+			camel_header_address_list_clear(&addr);
+#if 0
+			if (addr)
+				camel_header_address_list_append_list(&addr_from, &addr);
+#endif
+		} else {
+			if (addr)
+				addr_from = addr;
+		}
+
+		if (addr_from) {
+			addrstr = camel_header_address_list_format(addr_from);
+			minfo->from = camel_pstring_strdup(addrstr);
+			g_free(addrstr);
+			camel_header_address_list_clear(&addr_from);
+		}
+
+		/* we dont keep reply_to */
+
+		/* env_reply_to    ::= "(" 1*address ")" / nil */
+		addr = imap_parse_address_list(is);
+		camel_header_address_list_clear(&addr);
+
+		/* env_to          ::= "(" 1*address ")" / nil */
+		addr = imap_parse_address_list(is);
+		if (addr) {
+			addrstr = camel_header_address_list_format(addr);
+			minfo->to = camel_pstring_strdup(addrstr);
+			g_free(addrstr);
+			camel_header_address_list_clear(&addr);
+		}
+
+		/* env_cc          ::= "(" 1*address ")" / nil */
+		addr = imap_parse_address_list(is);
+		if (addr) {
+			addrstr = camel_header_address_list_format(addr);
+			minfo->cc = camel_pstring_strdup(addrstr);
+			g_free(addrstr);
+			camel_header_address_list_clear(&addr);
+		}
+
+		/* we dont keep bcc either */
+
+		/* env_bcc         ::= "(" 1*address ")" / nil */
+		addr = imap_parse_address_list(is);
+		camel_header_address_list_clear(&addr);
+
+		/* FIXME: need to put in-reply-to into references hash list */
+
+		/* env_in_reply_to ::= nstring */
+		tok = camel_imapx_stream_nstring(is, &token);
+
+		/* FIXME: need to put message-id into message-id hash */
+
+		/* env_message_id  ::= nstring */
+		tok = camel_imapx_stream_nstring(is, &token);
+
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok != ')')
+			camel_exception_throw(1, "expecting ')'");
+	} CAMEL_CATCH(ex) {
+		camel_message_info_free(minfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return (CamelMessageInfo *)minfo;
+}
+	
+struct _CamelMessageContentInfo *
+imap_parse_body(CamelIMAPXStream *is)
+{
+	int tok, len;
+	unsigned char *token;
+	struct _CamelMessageContentInfo * volatile cinfo = NULL;
+	struct _CamelMessageContentInfo *subinfo, *last;
+	struct _CamelContentDisposition * volatile dinfo = NULL;
+	struct _CamelMessageInfo * volatile minfo = NULL;
+
+	/* body            ::= "(" body_type_1part / body_type_mpart ")" */
+
+	p(printf("body\n"));
+
+	CAMEL_TRY {
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok != '(')
+			camel_exception_throw(1, "body: expecting '('");
+
+		/* 1*body (optional for multiparts) */
+		tok = camel_imapx_stream_token(is, &token, &len);
+		camel_imapx_stream_ungettoken(is, tok, token, len);
+		if (tok == '(') {
+			/* body_type_mpart ::= 1*body SPACE media_subtype
+			   [SPACE body_ext_mpart] */
+
+			cinfo = g_malloc0(sizeof(*cinfo));
+			last = (struct _CamelMessageContentInfo *)&cinfo->childs;
+			do {
+				subinfo = imap_parse_body(is);
+				last->next = subinfo;
+				last = subinfo;
+				subinfo->parent = cinfo;
+				tok = camel_imapx_stream_token(is, &token, &len);
+				camel_imapx_stream_ungettoken(is, tok, token, len);
+			} while (tok == '(');
+
+			d(printf("media_subtype\n"));
+
+			camel_imapx_stream_astring(is, &token);
+			cinfo->type = camel_content_type_new("multipart", token);
+
+			/* body_ext_mpart  ::= body_fld_param
+			   [SPACE body_fld_dsp SPACE body_fld_lang
+			   [SPACE 1#body_extension]]
+			   ;; MUST NOT be returned on non-extensible
+			   ;; "BODY" fetch */
+
+			d(printf("body_ext_mpart\n"));
+
+			tok = camel_imapx_stream_token(is, &token, &len);
+			camel_imapx_stream_ungettoken(is, tok, token, len);
+			if (tok == '(') {
+				imap_parse_param_list(is, &cinfo->type->params);
+
+				/* body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil */
+
+				tok = camel_imapx_stream_token(is, &token, &len);
+				camel_imapx_stream_ungettoken(is, tok, token, len);
+				if (tok == '(' || tok == IMAP_TOK_TOKEN) {
+					dinfo = imap_parse_ext_optional(is);
+					/* other extension fields?, soaked up below */
+				} else {
+					camel_imapx_stream_ungettoken(is, tok, token, len);
+				}
+			}
+		} else {
+			/* body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
+			   [SPACE body_ext_1part]
+			   
+			   body_type_basic ::= media_basic SPACE body_fields
+			   body_type_text  ::= media_text SPACE body_fields SPACE body_fld_lines
+			   body_type_msg   ::= media_message SPACE body_fields SPACE envelope
+			   SPACE body SPACE body_fld_lines */
+
+			d(printf("Single part body\n"));
+
+			cinfo = imap_parse_body_fields(is);
+
+			d(printf("envelope?\n"));
+
+			/* do we have an envelope following */
+			tok = camel_imapx_stream_token(is, &token, &len);
+			camel_imapx_stream_ungettoken(is, tok, token, len);
+			if (tok == '(') {
+				/* what do we do with the envelope?? */
+				minfo = imap_parse_envelope(is);
+				/* what do we do with the message content info?? */
+				//((CamelMessageInfoBase *)minfo)->content = imap_parse_body(is);
+				camel_message_info_free(minfo);
+				minfo = NULL;
+				d(printf("Scanned envelope - what do i do with it?\n"));
+			}
+
+			d(printf("fld_lines?\n"));
+
+			/* do we have fld_lines following? */
+			tok = camel_imapx_stream_token(is, &token, &len);
+			if (tok == IMAP_TOK_INT) {
+				d(printf("field lines: %s\n", token));
+				tok = camel_imapx_stream_token(is, &token, &len);
+			}
+			camel_imapx_stream_ungettoken(is, tok, token, len);
+
+			/* body_ext_1part  ::= body_fld_md5 [SPACE body_fld_dsp
+			   [SPACE body_fld_lang
+			   [SPACE 1#body_extension]]]
+			   ;; MUST NOT be returned on non-extensible
+			   ;; "BODY" fetch */
+
+			d(printf("extension data?\n"));
+
+			if (tok != ')') {
+				camel_imapx_stream_nstring(is, &token);
+
+				d(printf("md5: %s\n", token?(char *)token:"NIL"));
+
+				/* body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil */
+
+				tok = camel_imapx_stream_token(is, &token, &len);
+				camel_imapx_stream_ungettoken(is, tok, token, len);
+				if (tok == '(' || tok == IMAP_TOK_TOKEN) {
+					dinfo = imap_parse_ext_optional(is);
+					/* then other extension fields, soaked up below */
+				}
+			}
+		}
+
+		/* soak up any other extension fields that may be present */
+		/* there should only be simple tokens, no lists */
+		do {
+			tok = camel_imapx_stream_token(is, &token, &len);
+			if (tok != ')')
+				d(printf("Dropping extension data '%s'\n", token));
+		} while (tok != ')');
+	} CAMEL_CATCH(ex) {
+		if (cinfo)
+			imap_free_body(cinfo);
+		if (dinfo)
+			camel_content_disposition_unref(dinfo);
+		if (minfo)
+			camel_message_info_free(minfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	/* FIXME: do something with the disposition, currently we have no way to pass it out? */
+	if (dinfo)
+		camel_content_disposition_unref(dinfo);
+
+	return cinfo;
+}
+
+char *
+imap_parse_section(CamelIMAPXStream *is)
+{
+	int tok, len;
+	unsigned char *token;
+	char * volatile section = NULL;
+
+	/* currently we only return the part within the [section] specifier
+	   any header fields are parsed, but dropped */
+
+	/*
+	  section         ::= "[" [section_text /
+	  (nz_number *["." nz_number] ["." (section_text / "MIME")])] "]"
+
+	  section_text    ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
+	  SPACE header_list / "TEXT"
+	*/
+
+	CAMEL_TRY {
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok != '[')
+			camel_exception_throw(1, "section: expecting '['");
+
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok == IMAP_TOK_INT || tok == IMAP_TOK_TOKEN)
+			section = g_strdup(token);
+		else if (tok == ']') {
+			section = g_strdup("");
+			camel_imapx_stream_ungettoken(is, tok, token, len);
+		} else
+			camel_exception_throw(1, "section: expecting token");
+
+		/* header_list     ::= "(" 1#header_fld_name ")"
+		   header_fld_name ::= astring */
+
+		/* we dont need the header specifiers */
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok == '(') {
+			do {
+				tok = camel_imapx_stream_token(is, &token, &len);
+				if (tok == IMAP_TOK_STRING || tok == IMAP_TOK_TOKEN || tok == IMAP_TOK_INT) {
+					/* ?do something? */
+				} else if (tok != ')')
+					camel_exception_throw(1, "section: header fields: expecting string");
+			} while (tok != ')');
+			tok = camel_imapx_stream_token(is, &token, &len);
+		}
+
+		if (tok != ']')
+			camel_exception_throw(1, "section: expecting ']'");
+	} CAMEL_CATCH(ex) {
+		g_free(section);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return section;
+}
+
+void
+imap_free_fetch(struct _fetch_info *finfo)
+{
+	if (finfo == NULL)
+		return;
+
+	if (finfo->body)
+		camel_object_unref((CamelObject *)finfo->body);
+	if (finfo->text)
+		camel_object_unref((CamelObject *)finfo->text);
+	if (finfo->header)
+		camel_object_unref((CamelObject *)finfo->header);
+	if (finfo->minfo)
+		camel_message_info_free(finfo->minfo);
+	if (finfo->cinfo)
+		imap_free_body(finfo->cinfo);
+	camel_flag_list_free(&finfo->user_flags);
+	g_free(finfo->date);
+	g_free(finfo->section);
+	g_free(finfo->uid);
+	g_free(finfo);
+}
+
+extern void camel_message_info_dump(CamelMessageInfo *mi);
+
+#include <camel/camel-stream-fs.h>
+
+/* debug, dump one out */
+void
+imap_dump_fetch(struct _fetch_info *finfo)
+{
+	CamelStream *sout;
+	int fd;
+
+	printf("Fetch info:\n");
+	if (finfo == NULL) {
+		printf("Empty\n");
+		return;
+	}
+
+	fd = dup(1);
+	sout = camel_stream_fs_new_with_fd(fd);
+	if (finfo->body) {
+		camel_stream_printf(sout, "Body content:\n");
+		camel_stream_write_to_stream(finfo->body, sout);
+		camel_stream_reset(finfo->body);
+	}
+	if (finfo->text) {
+		camel_stream_printf(sout, "Text content:\n");
+		camel_stream_write_to_stream(finfo->text, sout);
+		camel_stream_reset(finfo->text);
+	}
+	if (finfo->header) {
+		camel_stream_printf(sout, "Header content:\n");
+		camel_stream_write_to_stream(finfo->header, sout);
+		camel_stream_reset(finfo->header);
+	}
+	if (finfo->minfo) {
+		camel_stream_printf(sout, "Message Info:\n");
+		camel_message_info_dump(finfo->minfo);
+	}
+	if (finfo->cinfo) {
+		camel_stream_printf(sout, "Content Info:\n");
+		//camel_content_info_dump(finfo->cinfo, 0);
+	}
+	if (finfo->got & FETCH_SIZE)
+		camel_stream_printf(sout, "Size: %d\n", (int)finfo->size);
+	if (finfo->got & FETCH_BODY)
+		camel_stream_printf(sout, "Offset: %d\n", (int)finfo->offset);
+	if (finfo->got & FETCH_FLAGS)
+		camel_stream_printf(sout, "Flags: %08x\n", (int)finfo->flags);
+	if (finfo->date)
+		camel_stream_printf(sout, "Date: '%s'\n", finfo->date);
+	if (finfo->section)
+		camel_stream_printf(sout, "Section: '%s'\n", finfo->section);
+	if (finfo->date)
+		camel_stream_printf(sout, "UID: '%s'\n", finfo->uid);
+	camel_object_unref((CamelObject *)sout);
+}
+
+struct _fetch_info *
+imap_parse_fetch(CamelIMAPXStream *is)
+{
+	int tok, len;
+	unsigned char *token, *p, c;
+	struct _fetch_info *finfo;
+
+	finfo = g_malloc0(sizeof(*finfo));
+
+	CAMEL_TRY {
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok != '(')
+			camel_exception_throw(1, "fetch: expecting '('");
+
+		while ( (tok = camel_imapx_stream_token(is, &token, &len)) == IMAP_TOK_TOKEN ) {
+
+			p = token;
+			while ((c=*p))
+				*p++ = toupper(c);
+
+			switch(imap_tokenise(token, len)) {
+			case IMAP_ENVELOPE:
+				finfo->minfo = imap_parse_envelope(is);
+				finfo->got |= FETCH_MINFO;
+				break;
+			case IMAP_FLAGS:
+				imap_parse_flags(is, &finfo->flags, &finfo->user_flags);
+				finfo->got |= FETCH_FLAGS;
+				break;
+			case IMAP_INTERNALDATE:
+				camel_imapx_stream_nstring(is, &token);
+				/* TODO: convert to camel format? */
+				finfo->date = g_strdup(token);
+				finfo->got |= FETCH_DATE;
+				break;
+			case IMAP_RFC822_HEADER:
+				camel_imapx_stream_nstring_stream(is, &finfo->header);
+				finfo->got |= FETCH_HEADER;
+				break;
+			case IMAP_RFC822_TEXT:
+				camel_imapx_stream_nstring_stream(is, &finfo->text);
+				finfo->got |= FETCH_TEXT;
+				break;
+			case IMAP_RFC822_SIZE:
+				finfo->size = camel_imapx_stream_number(is);
+				finfo->got |= FETCH_SIZE;
+				break;
+			case IMAP_BODYSTRUCTURE:
+				finfo->cinfo = imap_parse_body(is);
+				finfo->got |= FETCH_CINFO;
+				break;
+			case IMAP_BODY:
+				tok = camel_imapx_stream_token(is, &token, &len);
+				camel_imapx_stream_ungettoken(is, tok, token, len);
+				if (tok == '(') {
+					finfo->cinfo = imap_parse_body(is);
+					finfo->got |= FETCH_CINFO;
+				} else if (tok == '[') {
+					finfo->section = imap_parse_section(is);
+					finfo->got |= FETCH_SECTION;
+					tok = camel_imapx_stream_token(is, &token, &len);
+					if (token[0] == '<') {
+						finfo->offset = strtoul(token+1, NULL, 10);
+					} else {
+						camel_imapx_stream_ungettoken(is, tok, token, len);
+					}
+					camel_imapx_stream_nstring_stream(is, &finfo->body);
+					finfo->got |= FETCH_BODY;					
+				} else {
+					camel_exception_throw(1, "unknown body response");
+				}
+				break;
+			case IMAP_UID:
+				tok = camel_imapx_stream_token(is, &token, &len);
+				if (tok != IMAP_TOK_INT)
+					camel_exception_throw(1, "uid not integer");
+				finfo->uid = g_strdup(token);
+				finfo->got |= FETCH_UID;
+				break;
+			default:
+				camel_exception_throw(1, "unknown body response");
+			}
+		}
+
+		if (tok != ')')
+			camel_exception_throw(1, "missing closing ')' on fetch response");
+	} CAMEL_CATCH(ex) {
+		imap_free_fetch(finfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return finfo;
+}
+
+/* rfc 2060 section 7.1 Status Responses */
+/* shoudl this start after [ or before the [? token_unget anyone? */
+struct _status_info *
+imap_parse_status(CamelIMAPXStream *is)
+{
+	int tok, len;
+	unsigned char *token;
+	struct _status_info *sinfo;
+
+	sinfo = g_malloc0(sizeof(*sinfo));
+
+	CAMEL_TRY {
+		camel_imapx_stream_atom(is, &token, &len);
+
+		/*
+		  resp_cond_auth  ::= ("OK" / "PREAUTH") SPACE resp_text
+		  ;; Authentication condition
+
+		  resp_cond_bye   ::= "BYE" SPACE resp_text
+
+		  resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
+		  ;; Status condition
+		*/
+
+		sinfo->result = imap_tokenise(token, len);
+		switch (sinfo->result) {
+		case IMAP_OK:
+		case IMAP_NO:
+		case IMAP_BAD:
+		case IMAP_PREAUTH:
+		case IMAP_BYE:
+			break;
+		default:
+			camel_exception_throw(1, "expecting OK/NO/BAD");
+		}
+
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok == '[') {
+			camel_imapx_stream_atom(is, &token, &len);
+			sinfo->condition = imap_tokenise(token, len);
+
+			/* parse any details */
+			switch (sinfo->condition) {
+			case IMAP_READ_ONLY:
+			case IMAP_READ_WRITE:
+			case IMAP_ALERT:
+			case IMAP_PARSE:
+			case IMAP_TRYCREATE:
+				break;
+			case IMAP_APPENDUID:
+				sinfo->u.appenduid.uidvalidity = camel_imapx_stream_number(is);
+				sinfo->u.appenduid.uid = camel_imapx_stream_number(is);
+				break;
+			case IMAP_NEWNAME:
+				/* the rfc doesn't specify the bnf for this */
+				camel_imapx_stream_astring(is, &token);
+				sinfo->u.newname.oldname = g_strdup(token);
+				camel_imapx_stream_astring(is, &token);
+				sinfo->u.newname.newname = g_strdup(token);
+				break;
+			case IMAP_PERMANENTFLAGS:
+				/* we only care about \* for permanent flags, not user flags */
+				imap_parse_flags(is, &sinfo->u.permanentflags, NULL);
+				break;
+			case IMAP_UIDVALIDITY:
+				sinfo->u.uidvalidity = camel_imapx_stream_number(is);
+				break;
+			case IMAP_UNSEEN:
+				sinfo->u.unseen = camel_imapx_stream_number(is);
+				break;
+			default:
+				sinfo->condition = IMAP_UNKNOWN;
+				printf("Got unknown response code: %s: ignored\n", token);
+			}
+			
+			/* ignore anything we dont know about */
+			do {
+				tok = camel_imapx_stream_token(is, &token, &len);
+				if (tok == '\n')
+					camel_exception_throw(1, "server response truncated");
+			} while (tok != ']');
+		} else {
+			camel_imapx_stream_ungettoken(is, tok, token, len);
+		}
+
+		/* and take the human readable response */
+		camel_imapx_stream_text(is, (unsigned char **)&sinfo->text);
+	} CAMEL_CATCH(ex) {
+		imap_free_status(sinfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return sinfo;
+}
+
+struct _status_info *
+imap_copy_status(struct _status_info *sinfo)
+{
+	struct _status_info *out;
+
+	out = g_malloc(sizeof(*out));
+	memcpy(out, sinfo, sizeof(*out));
+	out->text = g_strdup(out->text);
+	if (out->condition == IMAP_NEWNAME) {
+		out->u.newname.oldname = g_strdup(out->u.newname.oldname);
+		out->u.newname.newname = g_strdup(out->u.newname.newname);
+	}
+
+	return out;
+}
+
+void
+imap_free_status(struct _status_info *sinfo)
+{
+	if (sinfo == NULL)
+		return;
+
+	switch (sinfo->condition) {
+	case IMAP_NEWNAME:
+		g_free(sinfo->u.newname.oldname);
+		g_free(sinfo->u.newname.newname);
+	default:
+		break;
+	}
+
+	g_free(sinfo->text);
+	g_free(sinfo);
+}
+
+/* FIXME: use tokeniser? */
+/* FIXME: real flags */
+static struct {
+	char *name;
+	guint32 flag;
+} list_flag_table[] = {
+	{ "\\NOINFERIORS", CAMEL_FOLDER_NOINFERIORS },
+	{ "\\NOSELECT", CAMEL_FOLDER_NOSELECT },
+	{ "\\MARKED", 1<<8 },
+	{ "\\UNMARKED", 1<<9 },
+};
+
+struct _list_info *
+imap_parse_list(CamelIMAPXStream *is)
+/* throws io, parse */
+{
+	int tok, len, i;
+	unsigned char *token, *p, c;
+	struct _list_info * volatile linfo;
+
+	linfo = g_malloc0(sizeof(*linfo));
+	
+	CAMEL_TRY {
+		/* mailbox_list    ::= "(" #("\Marked" / "\Noinferiors" /
+		   "\Noselect" / "\Unmarked" / flag_extension) ")"
+		   SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox */
+
+		tok = camel_imapx_stream_token(is, &token, &len);
+		if (tok != '(')
+			camel_exception_throw(1, "list: expecting '('");
+
+		while ( (tok = camel_imapx_stream_token(is, &token, &len)) != ')' ) {
+			if (tok == IMAP_TOK_STRING || tok == IMAP_TOK_TOKEN) {
+				p = token;
+				while ((c=*p))
+					*p++ = toupper(c);
+				for (i=0;i<(int)(sizeof(list_flag_table)/sizeof(list_flag_table[0]));i++)
+					if (!strcmp(token, list_flag_table[i].name))
+						linfo->flags |= list_flag_table[i].flag;
+			} else {
+				camel_exception_throw(1, "list: expecting flag or ')'");
+			}
+		}
+
+		camel_imapx_stream_nstring(is, &token);
+		linfo->separator = token?*token:0;
+		camel_imapx_stream_astring(is, &token);
+		linfo->name = g_strdup(token);
+	} CAMEL_CATCH(ex) {
+		imap_free_list(linfo);
+		camel_exception_throw_ex(ex);
+	} CAMEL_DONE;
+
+	return linfo;
+}
+
+char *
+imapx_list_get_path(struct _list_info *li)
+{
+	char *path, *p;
+	int c;
+	const char *f;
+
+	if (li->separator != 0 && li->separator != '/') {
+		p = path = alloca(strlen(li->name)*3+1);
+		f = li->name;
+		while ( (c = *f++ & 0xff) ) {
+			if (c == li->separator)
+				*p++ = '/';
+			else if (c == '/' || c == '%')
+				p += sprintf(p, "%%%02X", c);
+			else
+				*p++ = c;
+		}
+		*p = 0;
+	} else
+		path = li->name;
+
+	return camel_utf7_utf8(path);
+}
+
+void
+imap_free_list(struct _list_info *linfo)
+{
+	if (linfo) {
+		g_free(linfo->name);
+		g_free(linfo);
+	}
+}
+
+/* ********************************************************************** */
+
+/*
+ From rfc2060
+
+ATOM_CHAR       ::= <any CHAR except atom_specials>
+
+atom_specials   ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
+                    quoted_specials
+
+CHAR            ::= <any 7-bit US-ASCII character except NUL,
+                     0x01 - 0x7f>
+
+CTL             ::= <any ASCII control character and DEL,
+                        0x00 - 0x1f, 0x7f>
+
+SPACE           ::= <ASCII SP, space, 0x20>
+
+list_wildcards  ::= "%" / "*"
+
+quoted_specials ::= <"> / "\"
+
+string          ::= quoted / literal
+
+literal         ::= "{" number "}" CRLF *CHAR8
+                    ;; Number represents the number of CHAR8 octets
+
+quoted          ::= <"> *QUOTED_CHAR <">
+
+QUOTED_CHAR     ::= <any TEXT_CHAR except quoted_specials> /
+                    "\" quoted_specials
+
+TEXT_CHAR       ::= <any CHAR except CR and LF>
+
+*/
+
+/*
+ATOM = 1
+SIMPLE? = 2
+NOTID? = 4
+
+QSPECIAL = 8
+
+*/
+
+unsigned char imapx_specials[256] = {
+/* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 4, 0, 0,
+/* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 20 */4, 1, 0, 1, 1, 0, 1, 1, 0, 0, 2, 7, 1, 1, 1, 1,
+/* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 40 */7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 0, 7, 1, 1,
+/* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+};
+
+#define list_wildcards "*%"
+#define quoted_specials "\\\""
+#define atom_specials "(){" list_wildcards quoted_specials /* + CTL */
+
+/* special types for the tokeniser, come out as raw tokens */
+#define token_specials "\n*()[]+"
+#define notid_specials "\x20\r\n()[]+"
+
+void imapx_utils_init(void)
+{
+	int i;
+	unsigned char v;
+
+	for (i=0;i<128;i++) {
+		v = 0;
+		if (i>=1 && i<=0x7f) {
+			v |= IMAPX_TYPE_CHAR;
+			if (i != 0x0a && i != 0x0d) {
+				v |= IMAPX_TYPE_TEXT_CHAR;
+				if (i != '"' && i != '\\')
+					v |= IMAPX_TYPE_QUOTED_CHAR;
+			}
+			if (i> 0x20 && i <0x7f && strchr(atom_specials, i) == NULL)
+				v |= IMAPX_TYPE_ATOM_CHAR;
+			if (strchr(token_specials, i) != NULL)
+				v |= IMAPX_TYPE_TOKEN_CHAR;
+			if (strchr(notid_specials, i) != NULL)
+				v |= IMAPX_TYPE_NOTID_CHAR;
+		}
+		
+		imapx_specials[i] = v;
+	}
+}
+
+unsigned char imapx_is_mask(const char *p)
+{
+	unsigned char v = 0xff;
+
+	while (*p) {
+		v &= imapx_specials[((unsigned char)*p) & 0xff];
+		p++;
+	}
+
+	return v;
+}
+
diff --git a/camel/providers/imapx/camel-imapx-utils.h b/camel/providers/imapx/camel-imapx-utils.h
new file mode 100644
index 0000000..bf3558c
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-utils.h
@@ -0,0 +1,189 @@
+
+#ifndef _CAMEL_IMAPX_UTILS_H
+#define _CAMEL_IMAPX_UTILS_H
+
+#include <camel/camel-mime-utils.h>
+#include <camel/camel-folder-summary.h>
+
+struct _CamelIMAPXStream;
+struct _CamelFlag;
+
+/* list of strings we know about that can be *quickly* tokenised */
+typedef enum _camel_imapx_id_t {
+	IMAP_UNKNOWN = 0,
+	IMAP_ALERT,
+	IMAP_APPENDUID,
+	IMAP_BAD,
+	IMAP_BODY,
+	IMAP_BODYSTRUCTURE,
+	IMAP_BYE,
+	IMAP_CAPABILITY,
+	IMAP_ENVELOPE,
+	IMAP_EXISTS,
+	IMAP_EXPUNGE,
+	IMAP_FETCH,
+	IMAP_FLAGS,
+	IMAP_INTERNALDATE,
+	IMAP_LIST,
+	IMAP_LSUB,
+	IMAP_NEWNAME,
+	IMAP_NO,
+	IMAP_OK,
+	IMAP_PARSE,
+	IMAP_PERMANENTFLAGS,
+	IMAP_PREAUTH,
+	IMAP_READ_ONLY,
+	IMAP_READ_WRITE,
+	IMAP_RECENT,
+	IMAP_RFC822_HEADER,
+	IMAP_RFC822_SIZE,
+	IMAP_RFC822_TEXT,
+	IMAP_TRYCREATE,
+	IMAP_UID,
+	IMAP_UIDVALIDITY,
+	IMAP_UNSEEN,
+} camel_imapx_id_t;
+
+/* str MUST be in upper case, tokenised using gperf function */
+camel_imapx_id_t imap_tokenise(register const char *str, register unsigned int len);
+
+/* this flag should be part of imapfoldersummary */
+enum {
+	CAMEL_IMAPX_MESSAGE_RECENT = (1<<8),
+};
+
+/* ********************************************************************** */
+
+void imap_parse_flags(struct _CamelIMAPXStream *stream, guint32 *flagsp, struct _CamelFlag **user_flagsp);
+void imap_write_flags(CamelStream *stream, guint32 flags, struct _CamelFlag *user_flags);
+
+/* ********************************************************************** */
+enum {
+	IMAP_CAPABILITY_IMAP4			= (1 << 0),
+	IMAP_CAPABILITY_IMAP4REV1		= (1 << 1),
+	IMAP_CAPABILITY_STATUS			= (1 << 2),
+	IMAP_CAPABILITY_NAMESPACE		= (1 << 3),
+	IMAP_CAPABILITY_UIDPLUS			= (1 << 4),
+	IMAP_CAPABILITY_LITERALPLUS		= (1 << 5),
+	IMAP_CAPABILITY_STARTTLS                = (1 << 6),
+};
+
+struct _capability_info {
+	guint32 capa;
+	/* auth stuff here */
+};
+
+struct _capability_info *imap_parse_capability(struct _CamelIMAPXStream *stream);
+void imap_free_capability(struct _capability_info *);
+
+void imap_parse_param_list(struct _CamelIMAPXStream *is, struct _camel_header_param **plist) /* IO,PARSE */;
+struct _CamelContentDisposition *imap_parse_ext_optional(struct _CamelIMAPXStream *is) /* IO,PARSE */;
+struct _CamelMessageContentInfo *imap_parse_body_fields(struct _CamelIMAPXStream *is) /* IO,PARSE */;
+struct _camel_header_address *imap_parse_address_list(struct _CamelIMAPXStream *is) /* IO,PARSE */;
+struct _CamelMessageInfo *imap_parse_envelope(struct _CamelIMAPXStream *is) /* IO, PARSE */;
+struct _CamelMessageContentInfo *imap_parse_body(struct _CamelIMAPXStream *is) /* IO,PARSE */;
+char *imap_parse_section(struct _CamelIMAPXStream *is) /* IO,PARSE */;
+void imap_free_body(struct _CamelMessageContentInfo *cinfo);
+
+/* ********************************************************************** */
+/* all the possible stuff we might get from a fetch request */
+/* this assumes the caller/server doesn't send any one of these types twice */
+struct _fetch_info {
+	guint32 got;		/* what we got, see below */
+	CamelStream *body;	/* BODY[.*](<.*>)? */
+	CamelStream *text;	/* RFC822.TEXT */
+	CamelStream *header;	/* RFC822.HEADER */
+	struct _CamelMessageInfo *minfo; /* ENVELOPE */
+	struct _CamelMessageContentInfo *cinfo;	/* BODYSTRUCTURE,BODY */
+	guint32 size;		/* RFC822.SIZE */
+	guint32 offset;		/* start offset of a BODY[]<offset.length> request */
+	guint32 flags;		/* FLAGS */
+	struct _CamelFlag *user_flags;
+	char *date;		/* INTERNALDATE */
+	char *section;		/* section for a BODY[section] request */
+	char *uid;		/* UID */
+};
+
+#define FETCH_BODY (1<<0)
+#define FETCH_TEXT (1<<1)
+#define FETCH_HEADER (1<<2)
+#define FETCH_MINFO (1<<3)
+#define FETCH_CINFO (1<<4)
+#define FETCH_SIZE (1<<5)
+#define FETCH_OFFSET (1<<6)
+#define FETCH_FLAGS (1<<7)
+#define FETCH_DATE (1<<8)
+#define FETCH_SECTION (1<<9)
+#define FETCH_UID (1<<10)
+
+struct _fetch_info *imap_parse_fetch(struct _CamelIMAPXStream *is);
+void imap_free_fetch(struct _fetch_info *finfo);
+void imap_dump_fetch(struct _fetch_info *finfo);
+
+/* ********************************************************************** */
+
+struct _status_info {
+	camel_imapx_id_t result; /* ok/no/bad/preauth only */
+	camel_imapx_id_t condition; /* read-only/read-write/alert/parse/trycreate/newname/permanentflags/uidvalidity/unseen */
+
+	union {
+		struct {
+			char *oldname;
+			char *newname;
+		} newname;
+		guint32 permanentflags;
+		guint32 uidvalidity;
+		guint32 unseen;
+		struct {
+			guint32 uidvalidity;
+			guint32 uid;
+		} appenduid;
+	} u;
+
+	char *text;
+};
+
+struct _status_info *imap_parse_status(struct _CamelIMAPXStream *is);
+struct _status_info *imap_copy_status(struct _status_info *sinfo);
+void imap_free_status(struct _status_info *sinfo);
+
+/* ********************************************************************** */
+
+/* should this just return a FolderInfo? 
+   should this just return the name & flags & separator by reference? */
+struct _list_info {
+	guint32 flags:24;
+	char separator;
+	char *name;
+};
+
+struct _list_info *imap_parse_list(struct _CamelIMAPXStream *is);
+char *imapx_list_get_path(struct _list_info *li);
+void imap_free_list(struct _list_info *linfo);
+
+/* ********************************************************************** */
+
+extern unsigned char imapx_specials[256];
+
+#define IMAPX_TYPE_CHAR (1<<0)
+#define IMAPX_TYPE_TEXT_CHAR (1<<1)
+#define IMAPX_TYPE_QUOTED_CHAR (1<<2)
+#define IMAPX_TYPE_ATOM_CHAR (1<<3)
+#define IMAPX_TYPE_TOKEN_CHAR (1<<4)
+#define IMAPX_TYPE_NOTID_CHAR (1<<5)
+
+unsigned char imapx_is_mask(const char *p);
+
+#define imapx_is_text_char(c) ((imapx_specials[((unsigned char)(c))&0xff] & IMAPX_TYPE_TEXT_CHAR) != 0)
+#define imapx_is_quoted_char(c) ((imapx_specials[((unsigned char)(c))&0xff] & IMAPX_TYPE_QUOTED_CHAR) != 0)
+#define imapx_is_atom_char(c) ((imapx_specials[((unsigned char)(c))&0xff] & IMAPX_TYPE_ATOM_CHAR) != 0)
+#define imapx_is_token_char(c) ((imapx_specials[((unsigned char)(c))&0xff] & IMAPX_TYPE_TOKEN_CHAR) != 0)
+#define imapx_is_notid_char(c) ((imapx_specials[((unsigned char)(c))&0xff] & IMAPX_TYPE_NOTID_CHAR) != 0)
+
+#define imapx_is_atom(s) (imapx_is_mask(s) & IMAPX_TYPE_ATOM_CHAR)
+
+/* ********************************************************************** */
+
+void imapx_utils_init(void);
+
+#endif
diff --git a/camel/providers/imapx/camel-imapx-view-summary.c b/camel/providers/imapx/camel-imapx-view-summary.c
new file mode 100644
index 0000000..a5f967f
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-view-summary.c
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
+ *
+ *  Copyright (C) 2005 Novell Inc.
+ *
+ *  Authors: Michael Zucchi <notzed ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "camel-record.h"
+#include "camel-imapx-view-summary.h"
+
+/* NB, this is only for the messy iterator_get interface, which could be better hidden */
+#include "libdb/dist/db.h"
+
+#define io(x)
+#define d(x) (printf("%s(%d): ", __FILE__, __LINE__),(x))
+
+#define CVSD_CLASS(x) ((CamelViewSummaryDiskClass *)((CamelObject *)x)->klass)
+#define CVS_CLASS(x) ((CamelViewSummaryClass *)((CamelObject *)x)->klass)
+
+static CamelViewSummaryDiskClass *cmvs_parent;
+
+/*
+ * camel_imapx_view_summary_new:
+ *
+ * Create a new CamelIMAPXViewSummary object.
+ * 
+ * Return value: A new CamelIMAPXViewSummary widget.
+ **/
+CamelIMAPXViewSummary *
+camel_imapx_view_summary_new(const char *base, CamelException *ex)
+{
+	return (CamelIMAPXViewSummary *)camel_view_summary_disk_construct(camel_object_new(camel_imapx_view_summary_get_type()), base, ex);
+}
+
+/* NB: must have write lock on folder */
+guint32 camel_imapx_view_next_uid(CamelIMAPXView *view)
+{
+#if 0
+	guint32 uid;
+
+	uid = view->nextuid++;
+	camel_view_changed((CamelView *)view);
+
+	return uid;
+#endif
+}
+
+/* NB: must have write lock on folder */
+void camel_imapx_view_last_uid(CamelIMAPXView *view, guint32 uid)
+{
+#if 0
+	uid++;
+	if (uid > view->nextuid) {
+		view->nextuid = uid;
+		camel_view_changed((CamelView *)view);
+	}
+#endif
+}
+
+static int
+imapx_view_decode(CamelViewSummaryDisk *s, CamelView *view, CamelRecordDecoder *crd)
+{
+	int tag, ver;
+
+	((CamelViewSummaryDiskClass *)cmvs_parent)->decode(s, view, crd);
+
+	if (strchr(view->vid, 1) == NULL) {
+		camel_record_decoder_reset(crd);
+		while ((tag = camel_record_decoder_next_section(crd, &ver)) != CR_SECTION_INVALID) {
+			switch (tag) {
+			case CVS_IMAPX_SECTION_VIEWINFO:
+				((CamelIMAPXView *)view)->uidvalidity = camel_record_decoder_int32(crd);
+				((CamelIMAPXView *)view)->permanentflags = camel_record_decoder_int32(crd);
+				((CamelIMAPXView *)view)->exists = camel_record_decoder_int32(crd);
+				((CamelIMAPXView *)view)->separator = camel_record_decoder_int8(crd);
+				((CamelIMAPXView *)view)->raw_name = g_strdup(camel_record_decoder_string(crd));
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void
+imapx_view_encode(CamelViewSummaryDisk *s, CamelView *view, CamelRecordEncoder *cre)
+{
+	((CamelViewSummaryDiskClass *)cmvs_parent)->encode(s, view, cre);
+
+	/* We only store extra data on the root view */
+
+	if (strchr(view->vid, 1) == NULL) {
+		camel_record_encoder_start_section(cre, CVS_IMAPX_SECTION_VIEWINFO, 0);
+		camel_record_encoder_int32(cre, ((CamelIMAPXView *)view)->uidvalidity);
+		camel_record_encoder_int32(cre, ((CamelIMAPXView *)view)->permanentflags);
+		camel_record_encoder_int32(cre, ((CamelIMAPXView *)view)->exists);
+		camel_record_encoder_int8(cre, ((CamelIMAPXView *)view)->separator);
+		camel_record_encoder_string(cre, ((CamelIMAPXView *)view)->raw_name);
+		camel_record_encoder_end_section(cre);
+	}
+}
+
+static void
+camel_imapx_view_summary_init(CamelIMAPXViewSummary *obj)
+{
+	struct _CamelFolderSummary *s = (CamelFolderSummary *)obj;
+
+	s = s;
+}
+
+static void
+camel_imapx_view_summary_finalise(CamelObject *obj)
+{
+	/*CamelIMAPXViewSummary *mbs = CAMEL_IMAPX_VIEW_SUMMARY(obj);*/
+}
+
+static void
+camel_imapx_view_summary_class_init(CamelIMAPXViewSummaryClass *klass)
+{
+	((CamelViewSummaryClass *)klass)->view_sizeof = sizeof(CamelIMAPXView);
+
+	((CamelViewSummaryDiskClass *)klass)->encode = imapx_view_encode;
+	((CamelViewSummaryDiskClass *)klass)->decode = imapx_view_decode;
+}
+
+CamelType
+camel_imapx_view_summary_get_type(void)
+{
+	static CamelType type = CAMEL_INVALID_TYPE;
+	
+	if (type == CAMEL_INVALID_TYPE) {
+		cmvs_parent = (CamelViewSummaryDiskClass *)camel_view_summary_disk_get_type();
+		type = camel_type_register((CamelType)cmvs_parent, "CamelIMAPXViewSummary",
+					   sizeof (CamelIMAPXViewSummary),
+					   sizeof (CamelIMAPXViewSummaryClass),
+					   (CamelObjectClassInitFunc) camel_imapx_view_summary_class_init,
+					   NULL,
+					   (CamelObjectInitFunc) camel_imapx_view_summary_init,
+					   (CamelObjectFinalizeFunc) camel_imapx_view_summary_finalise);
+	}
+	
+	return type;
+}
diff --git a/camel/providers/imapx/camel-imapx-view-summary.h b/camel/providers/imapx/camel-imapx-view-summary.h
new file mode 100644
index 0000000..e7b244b
--- /dev/null
+++ b/camel/providers/imapx/camel-imapx-view-summary.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (C) 2000 Ximian Inc.
+ *
+ *  Authors: Michael Zucchi <notzed ximian com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _CAMEL_IMAPX_VIEW_SUMMARY_H
+#define _CAMEL_IMAPX_VIEW_SUMMARY_H
+
+#include "camel-view-summary-disk.h"
+
+typedef struct _CamelIMAPXViewSummary      CamelIMAPXViewSummary;
+typedef struct _CamelIMAPXViewSummaryClass CamelIMAPXViewSummaryClass;
+
+enum {
+	CVS_IMAPX_SECTION_VIEWINFO = CVSD_SECTION_LAST,
+};
+
+typedef struct _CamelIMAPXView CamelIMAPXView;
+
+struct _CamelIMAPXView {
+	CamelViewDisk view;
+
+	char separator;
+
+	/* This data is only set on the root views */
+	char *raw_name;
+	guint32 exists;
+	guint32 uidvalidity;
+	guint32 permanentflags;	
+};
+
+struct _CamelIMAPXViewSummary {
+	CamelViewSummaryDisk parent;
+};
+
+struct _CamelIMAPXViewSummaryClass {
+	CamelViewSummaryDiskClass parent_class;
+};
+
+CamelType		camel_imapx_view_summary_get_type	(void);
+CamelIMAPXViewSummary      *camel_imapx_view_summary_new	(const char *base, CamelException *ex);
+
+/* called on root view */
+guint32 camel_imapx_view_next_uid(CamelIMAPXView *view);
+void camel_imapx_view_last_uid(CamelIMAPXView *view, guint32 uid);
+
+#endif /* ! _CAMEL_IMAPX_VIEW_SUMMARY_H */
+
diff --git a/camel/providers/imapx/libcamelimapx.urls b/camel/providers/imapx/libcamelimapx.urls
new file mode 100644
index 0000000..273c336
--- /dev/null
+++ b/camel/providers/imapx/libcamelimapx.urls
@@ -0,0 +1 @@
+imapx
diff --git a/configure.ac b/configure.ac
index 228559a..acfcf3d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1758,6 +1758,7 @@ camel/providers/Makefile
 camel/providers/groupwise/Makefile
 camel/providers/hula/Makefile
 camel/providers/imap/Makefile
+camel/providers/imapx/Makefile
 camel/providers/imapp/Makefile
 camel/providers/imap4/Makefile
 camel/providers/local/Makefile



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]