aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Zucci <zucchi@src.gnome.org>2003-08-02 05:07:43 +0800
committerMichael Zucci <zucchi@src.gnome.org>2003-08-02 05:07:43 +0800
commite2cd78ca9b706f30f51a648db9da220e9b5a68a2 (patch)
treea1a211c5f2e2447b22eb2d610c25f58173961c6f
parent330ecafed407197d31c30170e1c76674d3ed91e8 (diff)
downloadgsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar
gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.gz
gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.bz2
gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.lz
gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.xz
gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.tar.zst
gsoc2013-evolution-e2cd78ca9b706f30f51a648db9da220e9b5a68a2.zip
experimental, non-working imap implementation
svn path=/trunk/; revision=22061
-rw-r--r--camel/providers/imapp/Makefile.am46
-rw-r--r--camel/providers/imapp/camel-imapp-driver.c748
-rw-r--r--camel/providers/imapp/camel-imapp-driver.h81
-rw-r--r--camel/providers/imapp/camel-imapp-engine.c1156
-rw-r--r--camel/providers/imapp/camel-imapp-engine.h154
-rw-r--r--camel/providers/imapp/camel-imapp-exception.h35
-rw-r--r--camel/providers/imapp/camel-imapp-fetch-stream.c183
-rw-r--r--camel/providers/imapp/camel-imapp-fetch-stream.h47
-rw-r--r--camel/providers/imapp/camel-imapp-folder.c268
-rw-r--r--camel/providers/imapp/camel-imapp-folder.h66
-rw-r--r--camel/providers/imapp/camel-imapp-provider.c91
-rw-r--r--camel/providers/imapp/camel-imapp-store-summary.c616
-rw-r--r--camel/providers/imapp/camel-imapp-store-summary.h102
-rw-r--r--camel/providers/imapp/camel-imapp-store.c1221
-rw-r--r--camel/providers/imapp/camel-imapp-store.h77
-rw-r--r--camel/providers/imapp/camel-imapp-stream.c761
-rw-r--r--camel/providers/imapp/camel-imapp-stream.h90
-rw-r--r--camel/providers/imapp/camel-imapp-summary.c166
-rw-r--r--camel/providers/imapp/camel-imapp-summary.h65
-rw-r--r--camel/providers/imapp/camel-imapp-utils.c1339
-rw-r--r--camel/providers/imapp/camel-imapp-utils.h145
-rw-r--r--camel/providers/imapp/libcamelimapp.urls1
22 files changed, 7458 insertions, 0 deletions
diff --git a/camel/providers/imapp/Makefile.am b/camel/providers/imapp/Makefile.am
new file mode 100644
index 0000000000..06ce49dd7b
--- /dev/null
+++ b/camel/providers/imapp/Makefile.am
@@ -0,0 +1,46 @@
+## Process this file with automake to produce Makefile.in
+
+libcamelimappincludedir = $(privincludedir)/camel
+
+camel_provider_LTLIBRARIES = libcamelimapp.la
+camel_provider_DATA = libcamelimapp.urls
+
+INCLUDES = -I.. \
+ -I$(srcdir)/.. \
+ -I$(top_srcdir)/camel \
+ -I$(top_srcdir)/intl \
+ -I$(top_srcdir)/e-util \
+ -I$(top_srcdir) \
+ $(CAMEL_CFLAGS) \
+ $(GNOME_INCLUDEDIR) \
+ $(GTK_INCLUDEDIR) \
+ -DG_LOG_DOMAIN=\"camel-imapp-provider\" \
+ -DG_DISABLE_DEPRECATED
+
+libcamelimapp_la_SOURCES = \
+ camel-imapp-utils.c \
+ camel-imapp-engine.c \
+ camel-imapp-stream.c \
+ camel-imapp-store.c \
+ camel-imapp-folder.c \
+ camel-imapp-provider.c \
+ camel-imapp-store-summary.c \
+ camel-imapp-driver.c \
+ camel-imapp-summary.c
+
+libcamelimappinclude_HEADERS = \
+ camel-imapp-utils.h \
+ camel-imapp-engine.h \
+ camel-imapp-stream.h \
+ camel-imapp-store.h \
+ camel-imapp-folder.h \
+ camel-imapp-store-summary.h \
+ camel-imapp-driver.h \
+ camel-imapp-summary.h
+
+libcamelimapp_la_LDFLAGS = -avoid-version -module
+
+#noinst_HEADERS = \
+# camel-imap-private.h
+
+EXTRA_DIST = libcamelimapp.urls
diff --git a/camel/providers/imapp/camel-imapp-driver.c b/camel/providers/imapp/camel-imapp-driver.c
new file mode 100644
index 0000000000..f9daced770
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-driver.c
@@ -0,0 +1,748 @@
+
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "camel-imapp-driver.h"
+#include "camel-imapp-utils.h"
+#include "camel-imapp-folder.h"
+#include "camel-imapp-engine.h"
+#include "camel-imapp-summary.h"
+#include "camel-imapp-exception.h"
+
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-stream-null.h>
+
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-store.h>
+#include <camel/camel-mime-utils.h>
+#include <camel/camel-sasl.h>
+
+#define d(x) x
+
+static int driver_resp_fetch(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
+static int driver_resp_expunge(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
+static int driver_resp_exists(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
+static int driver_resp_list(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
+
+static void driver_status(CamelIMAPPEngine *ie, struct _status_info *sinfo, CamelIMAPPDriver *sdata);
+
+static void
+class_init(CamelIMAPPDriverClass *ieclass)
+{
+}
+
+static void
+object_init(CamelIMAPPDriver *ie, CamelIMAPPDriverClass *ieclass)
+{
+ ie->summary = g_ptr_array_new();
+ e_dlist_init(&ie->body_fetch);
+ e_dlist_init(&ie->body_fetch_done);
+}
+
+static void
+object_finalise(CamelIMAPPDriver *ie, CamelIMAPPDriverClass *ieclass)
+{
+ if (ie->folder)
+ camel_object_unref((CamelObject *)ie->folder);
+ if (ie->engine)
+ camel_object_unref((CamelObject *)ie->engine);
+ if (ie->summary)
+ g_ptr_array_free(ie->summary, TRUE);
+}
+
+CamelType
+camel_imapp_driver_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (
+ camel_object_get_type (),
+ "CamelIMAPPDriver",
+ sizeof (CamelIMAPPDriver),
+ sizeof (CamelIMAPPDriverClass),
+ (CamelObjectClassInitFunc) class_init,
+ NULL,
+ (CamelObjectInitFunc) object_init,
+ (CamelObjectFinalizeFunc) object_finalise);
+
+ /* blah ... could just use it in object setup? */
+ /* TEMPORARY */
+ camel_exception_setup();
+ }
+
+ return type;
+}
+
+CamelIMAPPDriver *
+camel_imapp_driver_new(CamelIMAPPStream *stream)
+{
+ CamelIMAPPDriver *driver;
+ CamelIMAPPEngine *ie;
+
+ driver = CAMEL_IMAPP_DRIVER (camel_object_new (CAMEL_IMAPP_DRIVER_TYPE));
+ ie = driver->engine = camel_imapp_engine_new(stream);
+
+ camel_imapp_engine_add_handler(ie, "FETCH", (CamelIMAPPEngineFunc)driver_resp_fetch, driver);
+ camel_imapp_engine_add_handler(ie, "EXPUNGE", (CamelIMAPPEngineFunc)driver_resp_expunge, driver);
+ camel_imapp_engine_add_handler(ie, "EXISTS", (CamelIMAPPEngineFunc)driver_resp_exists, driver);
+ camel_imapp_engine_add_handler(ie, "LIST", (CamelIMAPPEngineFunc)driver_resp_list, driver);
+ camel_object_hook_event(ie, "status", (CamelObjectEventHookFunc)driver_status, driver);
+
+ return driver;
+}
+
+void
+camel_imapp_driver_set_sasl_factory(CamelIMAPPDriver *id, CamelIMAPPSASLFunc get_sasl, void *sasl_data)
+{
+ id->get_sasl = get_sasl;
+ id->get_sasl_data = sasl_data;
+}
+
+void
+camel_imapp_driver_set_login_query(CamelIMAPPDriver *id, CamelIMAPPLoginFunc get_login, void *login_data)
+{
+ id->get_login = get_login;
+ id->get_login_data = login_data;
+}
+
+void
+camel_imapp_driver_login(CamelIMAPPDriver *id)
+/* throws SERVICE_CANT_AUTHENTICATE, SYSTEM_IO */
+{
+ CamelIMAPPCommand * volatile ic = NULL;
+
+ /* connect? */
+ /* camel_imapp_engine_connect() */
+ /* or above? */
+
+ CAMEL_TRY {
+ CamelSasl *sasl;
+
+ if (id->get_sasl
+ && (sasl = id->get_sasl(id, id->get_sasl_data))) {
+ ic = camel_imapp_engine_command_new(id->engine, "AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl);
+ camel_object_unref(sasl);
+ } else {
+ char *user, *pass;
+
+ g_assert(id->get_login);
+ id->get_login(id, &user, &pass, id->get_login_data);
+ ic = camel_imapp_engine_command_new(id->engine, "LOGIN", NULL, "LOGIN %s %s", user, pass);
+ g_free(user);
+ g_free(pass);
+ }
+
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic) > 0)
+ ;
+
+ if (ic->status->result != IMAP_OK)
+ camel_exception_throw(CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, "Login failed: %s", ic->status->text);
+ camel_imapp_engine_command_free(id->engine, ic);
+ } CAMEL_CATCH(ex) {
+ if (ic)
+ camel_imapp_engine_command_free(id->engine, ic);
+ camel_exception_throw_ex(ex);
+ } CAMEL_DONE;
+}
+
+void
+camel_imapp_driver_select(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder)
+{
+ CamelIMAPPSelectResponse *sr;
+ CamelIMAPPCommand * volatile ic = NULL;
+ CamelIMAPPCommand * volatile ic2 = NULL;
+ guint32 count;
+ CamelFolderSummary *summary;
+
+ if (id->folder) {
+ if (folder == id->folder)
+ return;
+ camel_imapp_driver_sync(id, FALSE, id->folder);
+ if (camel_folder_change_info_changed(id->folder->changes)) {
+ camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
+ camel_folder_change_info_clear(id->folder->changes);
+ }
+ camel_object_unref(id->folder);
+ id->folder = NULL;
+ }
+
+ summary = ((CamelFolder *)folder)->summary;
+
+ ic = camel_imapp_engine_command_new(id->engine, "SELECT", NULL, "SELECT %t", folder->raw_name);
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+
+ id->folder = folder;
+ camel_object_ref(folder);
+
+ count = camel_folder_summary_count(summary);
+ if (count > 0 && count <= id->exists) {
+ ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
+ "FETCH 1:%u (UID FLAGS)", count);
+ camel_imapp_engine_command_queue(id->engine, ic);
+ if (count < id->exists) {
+ ic2 = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
+ "FETCH %u:* (UID FLAGS ENVELOPE)", count+1);
+ camel_imapp_engine_command_queue(id->engine, ic2);
+ } else {
+ ic2 = NULL;
+ }
+
+ while (camel_imapp_engine_iterate(id->engine, ic2?ic2:ic)>0)
+ ;
+
+ camel_imapp_engine_command_free(id->engine, ic);
+ if (ic2)
+ camel_imapp_engine_command_free(id->engine, ic2);
+ } else {
+ ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
+ "FETCH 1:* (UID FLAGS ENVELOPE)");
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+ }
+
+ /* TODO: need to set exists/etc in summary */
+ folder->exists = id->exists;
+ folder->uidvalidity = id->uidvalidity;
+
+ printf("saving summary '%s'\n", summary->summary_path);
+ camel_folder_summary_save(summary);
+
+ if (camel_folder_change_info_changed(id->folder->changes)) {
+ camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
+ camel_folder_change_info_clear(id->folder->changes);
+ }
+}
+
+static void
+imapp_driver_check(CamelIMAPPDriver *id)
+{
+ guint32 count;
+ CamelIMAPPCommand *ic;
+
+ /* FIXME: exception handling */
+
+ if (id->folder->exists != id->exists) {
+ count = camel_folder_summary_count(((CamelFolder *)id->folder)->summary);
+ if (count < id->exists) {
+ printf("fetching new messages\n");
+ ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
+ "FETCH %u:* (UID FLAGS ENVELOPE)", count+1);
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+ } else if (count > id->exists) {
+ printf("folder shrank with no expunge notificaitons!? uh, dunno what to do\n");
+ }
+ }
+
+ printf("checking for change info changes\n");
+ if (camel_folder_change_info_changed(id->folder->changes)) {
+ printf("got somechanges! added=%d changed=%d removed=%d\n",
+ id->folder->changes->uid_added->len,
+ id->folder->changes->uid_changed->len,
+ id->folder->changes->uid_removed->len);
+ camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
+ camel_folder_change_info_clear(id->folder->changes);
+ }
+}
+
+void
+camel_imapp_driver_update(CamelIMAPPDriver *id, CamelIMAPPFolder *folder)
+{
+ if (id->folder == folder) {
+ CamelIMAPPCommand *ic;
+
+ /* this will automagically update flags & expunge items */
+ ic = camel_imapp_engine_command_new(id->engine, "NOOP", NULL, "NOOP");
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+
+ imapp_driver_check(id);
+ } else {
+ camel_imapp_driver_select(id, folder);
+ }
+}
+
+/* FIXME: this is basically a copy of the same in camel-imapp-utils.c */
+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_IMAPP_MESSAGE_RECENT }, */
+};
+
+/*
+ flags 00101000
+ sflags 01001000
+ ^ 01100000
+~flags 11010111
+& 01000000
+
+&flags 00100000
+*/
+
+static void
+imapp_write_flags(CamelIMAPPDriver *id, guint32 orset, gboolean on, CamelFolderSummary *summary)
+{
+ guint32 i, j, count;
+ CamelIMAPPMessageInfo *info;
+ CamelIMAPPCommand *ic = NULL;
+ struct _uidset_state ss;
+ GSList *commands = NULL;
+
+ /* FIXME: exception handling */
+
+ count = camel_folder_summary_count(summary);
+ for (j=0;j<sizeof(flag_table)/sizeof(flag_table[0]);j++) {
+ int flush;
+
+ if ((orset & flag_table[j].flag) == 0)
+ continue;
+
+ printf("checking/storing %s flags '%s'\n", on?"on":"off", flag_table[j].name);
+
+ flush = 0;
+ imapp_uidset_init(&ss, id->engine);
+ for (i=0;i<count;i++) {
+ info = (CamelIMAPPMessageInfo *)camel_folder_summary_index(summary, i);
+ if (info) {
+ guint32 flags = info->info.flags & CAMEL_IMAPP_SERVER_FLAGS;
+ guint32 sflags = info->server_flags & CAMEL_IMAPP_SERVER_FLAGS;
+
+ if ( (on && (((flags ^ sflags) & flags) & flag_table[j].flag))
+ || (!on && (((flags ^ sflags) & ~flags) & flag_table[j].flag))) {
+ if (ic == NULL)
+ ic = camel_imapp_engine_command_new(id->engine, "STORE", NULL, "UID STORE ");
+ flush = imapp_uidset_add(&ss, ic, camel_message_info_uid(info));
+ }
+ camel_folder_summary_info_free(summary, (CamelMessageInfo *)info);
+ }
+
+ if (i == count-1 && ic != NULL)
+ flush |= imapp_uidset_done(&ss, ic);
+
+ if (flush) {
+ flush = 0;
+ camel_imapp_engine_command_add(id->engine, ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flag_table[j].name);
+ camel_imapp_engine_command_queue(id->engine, ic);
+ commands = g_slist_prepend(commands, ic);
+ ic = NULL;
+ }
+ }
+ }
+
+ /* flush off any requests we may have outstanding */
+ /* TODO: for max benefit, should have this routine do both on and off flags in one go */
+ while (commands) {
+ GSList *next = commands->next;
+
+ ic = commands->data;
+ g_slist_free_1(commands);
+ commands = next;
+
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+ }
+}
+
+void
+camel_imapp_driver_sync(CamelIMAPPDriver *id, gboolean expunge, CamelIMAPPFolder *folder)
+{
+ CamelFolderSummary *summary;
+ guint i, count, on_orset, off_orset;
+ CamelIMAPPMessageInfo *info;
+ CamelIMAPPCommand *ic;
+
+ /* FIXME: exception handling */
+
+ camel_imapp_driver_update(id, folder);
+
+ summary = ((CamelFolder *)folder)->summary;
+ count = camel_folder_summary_count(summary);
+ /* find out which flags have turned on, which have tunred off */
+ off_orset = on_orset = 0;
+ for (i=0;i<count;i++) {
+ guint32 flags, sflags;
+
+ info = (CamelIMAPPMessageInfo *)camel_folder_summary_index(summary, i);
+ if (info == NULL)
+ continue;
+ flags = info->info.flags & CAMEL_IMAPP_SERVER_FLAGS;
+ sflags = info->server_flags & CAMEL_IMAPP_SERVER_FLAGS;
+ if (flags != sflags) {
+ off_orset |= ( flags ^ sflags ) & ~flags;
+ on_orset |= (flags ^ sflags) & flags;
+ }
+ camel_folder_summary_info_free(summary, (CamelMessageInfo *)info);
+ }
+
+ if (on_orset || off_orset) {
+ /* turn on or off all messages matching */
+ if (on_orset)
+ imapp_write_flags(id, on_orset, TRUE, summary);
+ if (off_orset)
+ imapp_write_flags(id, off_orset, FALSE, summary);
+
+ /* success (no exception), make sure we match what we're supposed to */
+ for (i=0;i<count;i++) {
+ guint32 flags, sflags;
+
+ info = (CamelIMAPPMessageInfo *)camel_folder_summary_index(summary, i);
+ if (info == NULL)
+ continue;
+ info->server_flags = info->info.flags & CAMEL_IMAPP_SERVER_FLAGS;
+ camel_folder_summary_info_free(summary, (CamelMessageInfo *)info);
+ }
+ camel_folder_summary_touch(summary);
+ /* could save summary here, incase of failure? */
+ }
+
+ if (expunge) {
+ ic = camel_imapp_engine_command_new(id->engine, "EXPUNGE", NULL, "EXPUNGE");
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+ }
+
+ printf("saving summary '%s'\n", summary->summary_path);
+ camel_folder_summary_save(summary);
+
+ if (camel_folder_change_info_changed(id->folder->changes)) {
+ camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
+ camel_folder_change_info_clear(id->folder->changes);
+ }
+}
+
+struct _fetch_data {
+ struct _fetch_data *next;
+ struct _fetch_data *prev;
+
+ CamelStream *data;
+ const char *uid;
+ const char *section;
+};
+
+CamelStream *
+camel_imapp_driver_fetch(CamelIMAPPDriver *id, CamelIMAPPFolder *folder, const char *uid, const char *section)
+{
+ struct _fetch_data fd;
+ CamelIMAPPCommand *ic;
+
+ fd.data = NULL;
+ fd.uid = uid;
+ fd.section = section;
+ e_dlist_addtail(&id->body_fetch, (EDListNode *)&fd);
+
+ CAMEL_TRY {
+ camel_imapp_driver_select(id, folder);
+
+ ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL, "UID FETCH %t (BODY.PEEK[%t])", uid, section);
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+ imapp_driver_check(id);
+ } CAMEL_CATCH(e) {
+ /* FIXME: do exception properly */
+ } CAMEL_DONE;
+
+ e_dlist_remove((EDListNode *)&fd);
+
+ return fd.data;
+}
+
+GPtrArray *
+camel_imapp_driver_list(CamelIMAPPDriver *id, const char *name, guint32 flags)
+{
+ CamelIMAPPCommand *ic;
+ GPtrArray *res;
+
+ g_assert(id->list_commands == NULL);
+ g_assert(id->list_result == NULL);
+
+ /* FIXME: make sure we only have a single list running at a time */
+ /* sem_wait(id->list_sem); */
+
+ /* FIXME: namespace stuff (done in store code?) */
+
+ /* FIXME: if name != "", we need to also do list "name.%" (. == sep) */
+
+ id->list_result = g_ptr_array_new();
+ id->list_flags = flags;
+ ic = camel_imapp_engine_command_new(id->engine, "LIST", NULL, "LIST \"\" %f", name[0]?name:"%");
+ camel_imapp_engine_command_queue(id->engine, ic);
+ while (ic) {
+ while (camel_imapp_engine_iterate(id->engine, ic)>0)
+ ;
+ camel_imapp_engine_command_free(id->engine, ic);
+
+ if (id->list_commands) {
+ GSList *top = id->list_commands;
+
+ id->list_commands = top->next;
+ ic = top->data;
+ g_slist_free_1(top);
+ } else {
+ ic = NULL;
+ }
+ }
+
+ res = id->list_result;
+ id->list_result = NULL;
+
+ /* sem_post(id->list_sem); */
+
+ return res;
+}
+
+static int
+driver_resp_list(CamelIMAPPEngine *ie, guint32 idx, CamelIMAPPDriver *id)
+{
+ struct _list_info *linfo;
+
+ /* FIXME: exceptions */
+
+ linfo = imap_parse_list(ie->stream);
+ printf("store list: '%s' ('%c')\n", linfo->name, linfo->separator);
+ if (id->list_result) {
+ if ((linfo->flags & CAMEL_FOLDER_NOINFERIORS) == 0
+ && (id->list_flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)
+ && linfo->separator) {
+ int depth = 0;
+ char *p = linfo->name;
+ char c = linfo->separator;
+
+ /* this is expensive ... but if we've listed this deep we're going slow anyway */
+ while (*p && depth < 10) {
+ if (*p == c)
+ depth++;
+ p++;
+ }
+
+ if (depth < 10
+ && (linfo->name[0] == 0 || linfo->name[strlen(linfo->name)-1] != c)) {
+ CamelIMAPPCommand *ic;
+
+ ic = camel_imapp_engine_command_new(id->engine, "LIST", NULL, "LIST \"\" %t%c%%", linfo->name, c);
+ id->list_commands = g_slist_prepend(id->list_commands, ic);
+ camel_imapp_engine_command_queue(id->engine, ic);
+ }
+ }
+ /* FIXME: dont add to list if name ends in separator */
+ g_ptr_array_add(id->list_result, linfo);
+ } else {
+ g_warning("unexpected list response\n");
+ imap_free_list(linfo);
+ }
+
+ return camel_imapp_engine_skip(ie);
+}
+
+/* ********************************************************************** */
+
+static void
+driver_status(CamelIMAPPEngine *ie, struct _status_info *sinfo, CamelIMAPPDriver *sdata)
+{
+ printf("got status response ...\n");
+ switch(sinfo->condition) {
+ case IMAP_READ_WRITE:
+ printf("folder is read-write\n");
+ break;
+ case IMAP_READ_ONLY:
+ printf("folder is read-only\n");
+ break;
+ case IMAP_UIDVALIDITY:
+ sdata->uidvalidity = sinfo->u.uidvalidity;
+ break;
+#if 0
+ /* not defined yet ... */
+ case IMAP_UIDNEXT:
+ printf("got uidnext for folder: %d\n", sinfo->u.uidnext);
+ break;
+#endif
+ case IMAP_UNSEEN:
+ sdata->unseen = sinfo->u.unseen;
+ break;
+ case IMAP_PERMANENTFLAGS:
+ sdata->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;
+ }
+}
+
+static int
+driver_resp_exists(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata)
+{
+ /* should this be an event instead? */
+
+ sdata->exists = id;
+
+ return camel_imapp_engine_skip(ie);
+}
+
+static int
+driver_resp_expunge(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata)
+{
+ printf("got expunge response %u\n", id);
+ if (sdata->folder != NULL) {
+ CamelMessageInfo *info;
+ CamelFolderSummary *summary = ((CamelFolder *)sdata->folder)->summary;
+
+ info = camel_folder_summary_index(summary, id-1);
+ if (info) {
+ printf("expunging msg %d\n", id);
+ camel_folder_summary_remove(summary, info);
+ camel_folder_summary_info_free(summary, info);
+ camel_folder_change_info_remove_uid(sdata->folder->changes, camel_message_info_uid(info));
+ } else {
+ printf("can not find msg %u from expunge\n", id);
+ }
+ }
+
+ return camel_imapp_engine_skip(ie);
+}
+
+static int
+driver_resp_fetch(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata)
+{
+ struct _fetch_info *finfo = NULL;
+ CamelMessageInfo *info, *uinfo;
+ unsigned int i;
+ CamelFolderSummary *summary;
+
+ printf("got fetch response %d\n", id);
+
+ if (sdata->folder == NULL)
+ goto done;
+
+ summary = ((CamelFolder *)sdata->folder)->summary;
+
+ finfo = imap_parse_fetch(ie->stream);
+ imap_dump_fetch(finfo);
+
+ info = camel_folder_summary_index(summary, id-1);
+ if (info == NULL) {
+ if (finfo->uid == NULL) {
+ printf("got fetch response for currently unknown message %u\n", id);
+ goto done;
+ }
+ uinfo = camel_folder_summary_uid(summary, finfo->uid);
+ if (uinfo) {
+ /* we have a problem ... index mismatch */
+ printf("index mismatch, uid '%s' not at index '%u'\n",
+ finfo->uid, id);
+ camel_folder_summary_info_free(summary, uinfo);
+ }
+ /* pad out the summary till we have enough indexes */
+ for (i=camel_folder_summary_count(summary);i<id;i++) {
+ info = camel_folder_summary_info_new(summary);
+ if (i == id-1) {
+ printf("inserting new info @ %u\n", i);
+ camel_message_info_set_uid(info, g_strdup(finfo->uid));
+ } else {
+ char uidtmp[32];
+
+ sprintf(uidtmp, "blank-%u", i);
+ camel_message_info_set_uid(info, g_strdup(uidtmp));
+ printf("inserting empty uid %s\n", uidtmp);
+ }
+
+ camel_folder_summary_add(summary, info);
+ }
+ info = camel_folder_summary_index(summary, id-1);
+ g_assert(info != NULL);
+ } else {
+ if (finfo->uid) {
+ /* FIXME: need to handle blank-* uids, somehow */
+ while (info && strcmp(camel_message_info_uid(info), finfo->uid) != 0) {
+ printf("index mismatch, uid '%s' not at index '%u', got '%s' instead (removing)\n",
+ finfo->uid, id, camel_message_info_uid(info));
+
+ camel_folder_change_info_remove_uid(sdata->folder->changes, camel_message_info_uid(info));
+ camel_folder_summary_remove(summary, info);
+ camel_folder_summary_info_free(summary, info);
+ info = camel_folder_summary_index(summary, id-1);
+ }
+ } else {
+ printf("got info for unknown message %u\n", id);
+ }
+ }
+
+ if (info) {
+ 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_change_info_add_uid(sdata->folder->changes, camel_message_info_uid(info));
+ printf("adding change info uid '%s'\n", camel_message_info_uid(info));
+ }
+
+ if (finfo->got & FETCH_FLAGS) {
+ if ((info->flags & CAMEL_IMAPP_SERVER_FLAGS) != (finfo->flags & CAMEL_IMAPP_SERVER_FLAGS)) {
+ camel_folder_change_info_change_uid(sdata->folder->changes, camel_message_info_uid(info));
+ info->flags = (info->flags & ~(CAMEL_IMAPP_SERVER_FLAGS)) | (finfo->flags & CAMEL_IMAPP_SERVER_FLAGS);
+ camel_folder_summary_touch(summary);
+ }
+ ((CamelIMAPPMessageInfo *)info)->server_flags = finfo->flags & CAMEL_IMAPP_SERVER_FLAGS;
+ }
+
+ if ((finfo->got & (FETCH_BODY|FETCH_UID)) == (FETCH_BODY|FETCH_UID)) {
+ struct _fetch_data *fd, *fn;
+
+ fd = (struct _fetch_data *)sdata->body_fetch.head;
+ fn = fd->next;
+ while (fn) {
+ if (!strcmp(finfo->uid, fd->uid) && !strcmp(finfo->section, fd->section)) {
+ if (fd->data)
+ camel_object_unref(fd->data);
+ fd->data = finfo->body;
+ camel_object_ref(fd->data);
+ e_dlist_remove((EDListNode *)fd);
+ e_dlist_addtail(&sdata->body_fetch_done, (EDListNode *)fd);
+ break;
+ }
+ fd = fn;
+ fn = fn->next;
+ }
+ }
+
+ camel_folder_summary_info_free(summary, info);
+ } else {
+ printf("dont know what to do with message\n");
+ }
+ done:
+ imap_free_fetch(finfo);
+
+ return camel_imapp_engine_skip(ie);
+}
diff --git a/camel/providers/imapp/camel-imapp-driver.h b/camel/providers/imapp/camel-imapp-driver.h
new file mode 100644
index 0000000000..3124700619
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-driver.h
@@ -0,0 +1,81 @@
+
+#ifndef _CAMEL_IMAPP_DRIVER_H
+#define _CAMEL_IMAPP_DRIVER_H
+
+#include <camel/camel-object.h>
+#include "camel-imapp-stream.h"
+#include <e-util/e-msgport.h>
+
+#define CAMEL_IMAPP_DRIVER_TYPE (camel_imapp_driver_get_type ())
+#define CAMEL_IMAPP_DRIVER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_IMAPP_DRIVER_TYPE, CamelIMAPPDriver))
+#define CAMEL_IMAPP_DRIVER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAPP_DRIVER_TYPE, CamelIMAPPDriverClass))
+#define CAMEL_IS_IMAP_DRIVER(o) (CAMEL_CHECK_TYPE((o), CAMEL_IMAPP_DRIVER_TYPE))
+
+typedef struct _CamelIMAPPDriver CamelIMAPPDriver;
+typedef struct _CamelIMAPPDriverClass CamelIMAPPDriverClass;
+
+typedef int (*CamelIMAPPDriverFunc)(struct _CamelIMAPPDriver *driver, void *data);
+typedef struct _CamelSasl * (*CamelIMAPPSASLFunc)(struct _CamelIMAPPDriver *driver, void *data);
+typedef void (*CamelIMAPPLoginFunc)(struct _CamelIMAPPDriver *driver, char **login, char **pass, void *data);
+
+struct _CamelMimeMessage;
+
+struct _CamelIMAPPDriver {
+ CamelObject parent_object;
+
+ struct _CamelIMAPPEngine *engine;
+
+ struct _CamelIMAPPFolder *folder;
+
+ /* current folder stuff */
+ GPtrArray *summary;
+ guint32 uidvalidity;
+ guint32 exists;
+ guint32 recent;
+ guint32 unseen;
+ guint32 permanentflags;
+
+ /* list stuff */
+ GPtrArray *list_result;
+ GSList *list_commands;
+ guint32 list_flags;
+
+ /* sem_t list_sem; for controlled access to list variables */
+
+ /* this is so the node is always in a list - easier exception management */
+ EDList body_fetch;
+ EDList body_fetch_done;
+
+ /* factory to get an appropriate sasl mech */
+ CamelIMAPPSASLFunc get_sasl;
+ void *get_sasl_data;
+
+ /* callbacks, get login username/pass */
+ CamelIMAPPLoginFunc get_login;
+ void *get_login_data;
+};
+
+struct _CamelIMAPPDriverClass {
+ CamelObjectClass parent_class;
+};
+
+CamelType camel_imapp_driver_get_type (void);
+
+CamelIMAPPDriver * camel_imapp_driver_new(CamelIMAPPStream *stream);
+
+void camel_imapp_driver_set_sasl_factory(CamelIMAPPDriver *id, CamelIMAPPSASLFunc get_sasl, void *sasl_data);
+void camel_imapp_driver_set_login_query(CamelIMAPPDriver *id, CamelIMAPPLoginFunc get_login, void *login_data);
+
+void camel_imapp_driver_login(CamelIMAPPDriver *id);
+
+void camel_imapp_driver_select(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder);
+void camel_imapp_driver_update(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder);
+void camel_imapp_driver_sync(CamelIMAPPDriver *id, gboolean expunge, struct _CamelIMAPPFolder *folder);
+
+struct _CamelStream * camel_imapp_driver_fetch(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder, const char *uid, const char *body);
+GPtrArray * camel_imapp_driver_list(CamelIMAPPDriver *id, const char *name, guint32 flags);
+
+struct _CamelStream *camel_imapp_driver_get(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder, const char *uid);
+void camel_imapp_driver_append(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder, struct _CamelDataWrapper *);
+
+#endif
diff --git a/camel/providers/imapp/camel-imapp-engine.c b/camel/providers/imapp/camel-imapp-engine.c
new file mode 100644
index 0000000000..f6cfcbf86c
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-engine.c
@@ -0,0 +1,1156 @@
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "camel-imapp-engine.h"
+#include "camel-imapp-stream.h"
+#include "camel-imapp-utils.h"
+#include "camel-imapp-exception.h"
+
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-stream-null.h>
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-sasl.h>
+
+#include <ctype.h>
+
+#define e(x)
+#define c(x) /* command build debug */
+
+static void imap_engine_command_addv(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, const char *fmt, va_list ap);
+static void imap_engine_command_complete(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic);
+
+struct _handler {
+ CamelIMAPPEngineFunc func;
+ void *data;
+ char name[1];
+};
+
+static void
+class_init(CamelIMAPPEngineClass *ieclass)
+{
+ ieclass->tagprefix = 'A';
+
+ camel_object_class_add_event((CamelObjectClass *)ieclass, "status", NULL);
+}
+
+static void
+object_init(CamelIMAPPEngine *ie, CamelIMAPPEngineClass *ieclass)
+{
+ ie->handlers = g_hash_table_new(g_str_hash, g_str_equal);
+ e_dlist_init(&ie->active);
+ e_dlist_init(&ie->queue);
+
+ ie->tagprefix = ieclass->tagprefix;
+ ieclass->tagprefix++;
+ if (ieclass->tagprefix > 'Z')
+ ieclass->tagprefix = 'A';
+ ie->tagprefix = 'A';
+
+ ie->state = IMAP_ENGINE_DISCONNECT;
+}
+
+static void
+handler_free(void *key, void *mem, void *data)
+{
+ g_free(mem);
+}
+
+static void
+object_finalise(CamelIMAPPEngine *ie, CamelIMAPPEngineClass *ieclass)
+{
+ /* FIXME: need to free the commands ... */
+ while (camel_imapp_engine_iterate(ie, NULL) > 0)
+ ;
+
+ g_hash_table_foreach(ie->handlers, (GHFunc)handler_free, NULL);
+ g_hash_table_destroy(ie->handlers);
+}
+
+CamelType
+camel_imapp_engine_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (
+ camel_object_get_type (),
+ "CamelIMAPPEngine",
+ sizeof (CamelIMAPPEngine),
+ sizeof (CamelIMAPPEngineClass),
+ (CamelObjectClassInitFunc) class_init,
+ NULL,
+ (CamelObjectInitFunc) object_init,
+ (CamelObjectFinalizeFunc) object_finalise);
+ }
+
+ return type;
+}
+
+/* FIXME: check this, just taken from old code, not rfc */
+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 },
+};
+
+
+/*
+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.
+*/
+static int resp_capability(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ int tok, len, i;
+ unsigned char *token, *p, c;
+
+ /* FIXME: handle auth types */
+
+ printf("got capability response:\n");
+ while (1) {
+ tok = camel_imapp_stream_token(ie->stream, &token, &len);
+ switch(tok) {
+ case IMAP_TOK_TOKEN:
+ 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))
+ ie->capa |= capa_table[i].flag;
+ break;
+ case '\n':
+ return 0;
+ case IMAP_TOK_ERROR:
+ case IMAP_TOK_PROTOCOL:
+ camel_imapp_engine_skip(ie);
+ return -1;
+ default:
+ printf("Unknown Response token %02x '%c'\n", tok, isprint(tok)?tok:'.');
+ }
+ } while (tok != '\n');
+
+ return 0;
+}
+
+/* expunge command, id is expunged seq number */
+/* message_data ::= nz_number SPACE ("EXPUNGE" /
+ ("FETCH" SPACE msg_att)) */
+static int resp_expunge(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ printf("message expunged: %d\n", id);
+
+ return camel_imapp_engine_skip(ie);
+}
+
+static int resp_flags(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ guint32 flags;
+
+ imap_parse_flags(ie->stream, &flags);
+
+ printf("flags: %08x\n", flags);
+
+ return camel_imapp_engine_skip(ie);
+}
+
+/* exists count */
+static int resp_exists(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ printf("messages exist: %d\n", id);
+
+ if (ie->select_response)
+ ie->select_response->exists = id;
+
+ return camel_imapp_engine_skip(ie);
+}
+
+static int resp_recent(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ printf("messages recent: %d\n", id);
+
+ if (ie->select_response)
+ ie->select_response->recent = id;
+
+ return camel_imapp_engine_skip(ie);
+}
+
+static int resp_fetch(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ struct _fetch_info *finfo;
+
+ finfo = imap_parse_fetch(ie->stream);
+ imap_dump_fetch(finfo);
+ imap_free_fetch(finfo);
+
+ return camel_imapp_engine_skip(ie);
+}
+
+#if 0
+static int resp_list(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ struct _list_info *linfo;
+
+ linfo = imap_parse_list(ie->stream);
+ printf("list: '%s' (%c)\n", linfo->name, linfo->separator);
+ imap_free_list(linfo);
+
+ return camel_imapp_engine_skip(ie);
+}
+#endif
+
+CamelIMAPPEngine *
+camel_imapp_engine_new(CamelIMAPPStream *stream)
+{
+ CamelIMAPPEngine * volatile engine;
+
+ engine = CAMEL_IMAPP_ENGINE (camel_object_new (CAMEL_IMAPP_ENGINE_TYPE));
+ engine->stream = stream;
+ camel_object_ref((CamelObject *)stream);
+
+ camel_imapp_engine_add_handler(engine, "CAPABILITY", resp_capability, engine);
+
+ /* mailbox_data */
+ camel_imapp_engine_add_handler(engine, "FLAGS", (CamelIMAPPEngineFunc)resp_flags, engine);
+ camel_imapp_engine_add_handler(engine, "EXISTS", (CamelIMAPPEngineFunc)resp_exists, engine);
+ camel_imapp_engine_add_handler(engine, "RECENT", (CamelIMAPPEngineFunc)resp_recent, engine);
+
+#if 0
+ camel_imapp_engine_add_handler(engine, "LIST", (CamelIMAPPEngineFunc)resp_list, engine);
+ camel_imapp_engine_add_handler(engine, "LSUB", (CamelIMAPPEngineFunc)resp_list, engine);
+#endif
+ /* message_data */
+ camel_imapp_engine_add_handler(engine, "EXPUNGE", (CamelIMAPPEngineFunc)resp_expunge, engine);
+ camel_imapp_engine_add_handler(engine, "FETCH", (CamelIMAPPEngineFunc)resp_fetch, engine);
+
+ /* TODO: move this to a driver:connect call? */
+ CAMEL_TRY {
+ unsigned char *token;
+ unsigned int len;
+ int tok;
+
+ tok = camel_imapp_stream_token(stream, &token, &len);
+ if (tok == '*') {
+ struct _status_info *sinfo = imap_parse_status(stream);
+
+ switch (sinfo->result) {
+ case IMAP_OK:
+ engine->state = IMAP_ENGINE_CONNECT;
+ printf("Server connected ok: %s\n", sinfo->text);
+ break;
+ case IMAP_PREAUTH:
+ printf("pre-authenticated ...\n");
+ engine->state = IMAP_ENGINE_AUTH;
+ break;
+ default:
+ imap_free_status(sinfo);
+ camel_exception_throw(1, "Server refused connection: %s", sinfo->text);
+ break;
+ }
+ imap_free_status(sinfo);
+ } else {
+ engine->state = IMAP_ENGINE_CONNECT;
+ printf("unknwon server greeting, ignored\n");
+ camel_imapp_engine_skip(engine);
+ }
+ camel_imapp_engine_capabilities(engine);
+ } CAMEL_CATCH(ex) {
+ printf("connection failed: %s\n", ex->desc);
+ camel_object_unref((CamelObject *)engine);
+ engine = NULL;
+ } CAMEL_DONE;
+
+ return engine;
+}
+
+void
+camel_imapp_engine_add_handler(CamelIMAPPEngine *imap, const char *response, CamelIMAPPEngineFunc func, void *data)
+{
+ struct _handler *h;
+ const unsigned char *p;
+ unsigned char *o, c;
+
+ h = g_malloc0(sizeof(*h) + strlen(response));
+ h->func = func;
+ h->data = data;
+
+ p = response;
+ o = h->name;
+ while ((c = *p++))
+ *o++ = toupper(c);
+ *o = 0;
+
+ g_hash_table_insert(imap->handlers, h->name, h);
+}
+
+int
+camel_imapp_engine_capabilities(CamelIMAPPEngine *ie)
+{
+ CamelIMAPPCommand *ic;
+
+ /* reset capabilities */
+ ie->capa = 0;
+
+ ic = camel_imapp_engine_command_new(ie, "CAPABILITY", NULL, "CAPABILITY");
+ camel_imapp_engine_command_queue(ie, ic);
+ while (camel_imapp_engine_iterate(ie, ic)>0)
+ ;
+ camel_imapp_engine_command_free(ie, ic);
+
+ return 0;
+}
+
+/* skip the rest of the line of tokens */
+int
+camel_imapp_engine_skip(CamelIMAPPEngine *imap)
+{
+ int tok;
+ unsigned char *token;
+ unsigned int len;
+
+ do {
+ tok = camel_imapp_stream_token(imap->stream, &token, &len);
+ if (tok == IMAP_TOK_LITERAL) {
+ camel_imapp_stream_set_literal(imap->stream, len);
+ while ((tok = camel_imapp_stream_getl(imap->stream, &token, &len)) > 0) {
+ printf("Skip literal data '%.*s'\n", (int)len, token);
+ }
+ }
+ } while (tok != '\n' && tok >= 0);
+
+ if (tok < 0)
+ return -1;
+
+ return 0;
+}
+
+/* handle any untagged responses */
+int
+iterate_untagged(CamelIMAPPEngine *imap)
+{
+ unsigned int id, len;
+ unsigned char *token, *p, c;
+ int tok;
+ struct _handler *h;
+ struct _status_info *sinfo;
+
+ e(printf("got untagged response\n"));
+ id = 0;
+ tok = camel_imapp_stream_token(imap->stream, &token, &len);
+ if (tok == IMAP_TOK_INT) {
+ id = strtoul(token, NULL, 10);
+ tok = camel_imapp_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);
+
+ /* first, check for generic unsolicited response */
+ h = g_hash_table_lookup(imap->handlers, token);
+ if (h) {
+ tok = h->func(imap, id, h->data);
+ if (tok < 0)
+ return tok;
+ return 1;
+ }
+
+ /* TODO: apart from bye/preauth, these could be callbacks/events? */
+
+ /* now, check for status responses */
+ switch (imap_tokenise(token, len)) {
+ 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 */
+ /* FIXME: free sinfo */
+ camel_imapp_stream_ungettoken(imap->stream, tok, token, len);
+ sinfo = imap_parse_status(imap->stream);
+ camel_object_trigger_event(imap, "status", sinfo);
+ imap_free_status(sinfo);
+#if 0
+ switch(sinfo->condition) {
+ case IMAP_READ_WRITE:
+ printf("folder is read-write\n");
+ break;
+ case IMAP_READ_ONLY:
+ printf("folder is read-only\n");
+ break;
+ case IMAP_UIDVALIDITY:
+ if (imap->select_response)
+ imap->select_response->uidvalidity = sinfo->u.uidvalidity;
+ break;
+#if 0
+ /* not defined yet ... */
+ case IMAP_UIDNEXT:
+ printf("got uidnext for folder: %d\n", sinfo->u.uidnext);
+ break;
+#endif
+ case IMAP_UNSEEN:
+ if (imap->select_response)
+ imap->select_response->unseen = sinfo->u.unseen;
+ break;
+ case IMAP_PERMANENTFLAGS:
+ if (imap->select_response)
+ imap->select_response->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;
+ }
+#endif
+ break;
+ default:
+ printf("unknown token: %s\n", token);
+ camel_imapp_engine_skip(imap);
+ /* unknown response, just ignore it */
+ }
+
+ return 1;
+}
+
+/* handle any continuation requests
+ either data continuations, or auth continuation */
+int
+iterate_continuation(CamelIMAPPEngine *imap)
+{
+ CamelIMAPPCommand *ic;
+ CamelIMAPPCommandPart *cp;
+
+ printf("got continuation response\n");
+
+ ic = imap->literal;
+ imap->literal = NULL;
+ if (ic == NULL) {
+ camel_imapp_engine_skip(imap);
+ 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_IMAPP_COMMAND_MASK) {
+ case CAMEL_IMAPP_COMMAND_DATAWRAPPER:
+ printf("writing data wrapper to literal\n");
+ camel_data_wrapper_write_to_stream((CamelDataWrapper *)cp->ob, (CamelStream *)imap->stream);
+ break;
+ case CAMEL_IMAPP_COMMAND_STREAM:
+ printf("writing stream to literal\n");
+ camel_stream_write_to_stream((CamelStream *)cp->ob, (CamelStream *)imap->stream);
+ break;
+ case CAMEL_IMAPP_COMMAND_AUTH: {
+ CamelException *ex = camel_exception_new();
+ char *resp;
+ unsigned char *token;
+ int tok, len;
+
+ tok = camel_imapp_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 */
+ imap->literal = ic;
+
+ break; }
+ default:
+ /* should we just ignore? */
+ camel_exception_throw(1, "continuation response for non-continuation request");
+ }
+
+ camel_imapp_engine_skip(imap);
+
+ 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_IMAPP_COMMAND_CONTINUATION) {
+ imap->literal = ic;
+ } else {
+ g_assert(cp->next->next == NULL);
+ }
+ } else {
+ printf("%p: queueing continuation\n", ic);
+ camel_stream_printf((CamelStream *)imap->stream, "\r\n");
+ }
+
+ if (imap->literal == NULL) {
+ ic = (CamelIMAPPCommand *)e_dlist_remhead(&imap->queue);
+ if (ic) {
+ printf("found outstanding op, queueing\n");
+ camel_imapp_engine_command_queue(imap, ic);
+ }
+ }
+
+ return 1;
+}
+
+/* handle a completion line */
+int
+iterate_completion(CamelIMAPPEngine *imap, unsigned char *token)
+{
+ CamelIMAPPCommand *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);
+ ic = camel_imapp_engine_command_find_tag(imap, tag);
+ if (ic) {
+ printf("Got completion response for command %05u '%s'\n", ic->tag, ic->name);
+ printf("%p: removing command from qwueue, we were at '%s'\n", ic, ic->current->data);
+ printf("%p: removing command\n", ic);
+ e_dlist_remove((EDListNode *)ic);
+ if (imap->literal == ic)
+ imap->literal = NULL;
+ ic->status = imap_parse_status(imap->stream);
+ printf("got response code: %s\n", ic->status->text);
+
+ /* TODO: remove this stuff and use a completion handler? */
+ /* TODO: handle 'SELECT' command cleanup here */
+ /* FIXME: have this use tokeniser, have this handle close/logout/select etc as well */
+ /* ok response from login/authenticate, then we're in happy land */
+ if ((!strcmp(ic->name, "LOGIN") || !strcmp(ic->name, "AUTHENTICATE"))
+ && ic->status->result == IMAP_OK)
+ imap->state = IMAP_ENGINE_AUTH;
+
+ if (ic->complete)
+ ic->complete(imap, ic, ic->complete_data);
+ } else {
+ camel_exception_throw(1, "got response tag unexpectedly: %s", token);
+ }
+
+ if (imap->literal != NULL) {
+ printf("Warning: continuation command '%s' finished with outstanding continuation\n", imap->literal->name);
+ ic = imap->literal;
+ /* set the command complete with a failure code? */
+ e_dlist_remove((EDListNode *)ic);
+ imap->literal = NULL;
+ }
+
+ ic = (CamelIMAPPCommand *)e_dlist_remhead(&imap->queue);
+ if (ic) {
+ printf("found outstanding op, queueing\n");
+ camel_imapp_engine_command_queue(imap, ic);
+ }
+
+ return 1;
+}
+
+
+/* Do work if there's any to do */
+int
+camel_imapp_engine_iterate(CamelIMAPPEngine *imap, CamelIMAPPCommand *icwait)
+/* throws IO,PARSE exception */
+{
+ unsigned int len;
+ unsigned char *token;
+ int tok;
+
+ if ((icwait && icwait->status != NULL) || e_dlist_empty(&imap->active))
+ return 0;
+
+ /* handle exceptions here? */
+
+ /* lock here? */
+
+ tok = camel_imapp_stream_token(imap->stream, &token, &len);
+ if (tok == '*')
+ iterate_untagged(imap);
+ else if (tok == IMAP_TOK_TOKEN)
+ iterate_completion(imap, token);
+ else if (tok == '+')
+ iterate_continuation(imap);
+ else
+ camel_exception_throw(1, "unexpected server response: %s", token);
+
+ if (e_dlist_empty(&imap->active))
+ return 0;
+
+ return 1;
+}
+
+CamelIMAPPCommand *
+camel_imapp_engine_command_new(CamelIMAPPEngine *imap, const char *name, const char *select, const char *fmt, ...)
+{
+ CamelIMAPPCommand *ic;
+ va_list ap;
+
+ ic = g_malloc0(sizeof(*ic));
+ ic->tag = imap->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);
+ imap_engine_command_addv(imap, ic, fmt, ap);
+ va_end(ap);
+ }
+
+ return ic;
+}
+
+void
+camel_imapp_engine_command_add(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, const char *fmt, ...)
+{
+ va_list ap;
+
+ g_assert(ic->mem); /* gets reset on queue */
+
+ if (fmt && fmt[0]) {
+ va_start(ap, fmt);
+ imap_engine_command_addv(imap, ic, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+camel_imapp_engine_command_complete(CamelIMAPPEngine *imap, struct _CamelIMAPPCommand *ic, CamelIMAPPCommandFunc func, void *data)
+{
+ ic->complete = func;
+ ic->complete_data = data;
+}
+
+/* FIXME: make imap command's refcounted */
+void
+camel_imapp_engine_command_free (CamelIMAPPEngine *imap, CamelIMAPPCommand *ic)
+{
+ CamelIMAPPCommandPart *cp, *cn;
+
+ if (ic == NULL)
+ return;
+
+ /* validity check - we cant' free items still in any queue ... */
+ /* maybe we should just have another queue to keep them? */
+ {
+ CamelIMAPPCommand *iw;
+
+ iw = (CamelIMAPPCommand *)imap->active.head;
+ while (iw->next) {
+ if (iw == ic)
+ abort();
+ iw = iw->next;
+ }
+ iw = (CamelIMAPPCommand *)imap->queue.head;
+ while (iw->next) {
+ if (iw == ic)
+ abort();
+ iw = iw->next;
+ }
+ }
+
+ if (ic->mem)
+ camel_object_unref((CamelObject *)ic->mem);
+ imap_free_status(ic->status);
+ g_free(ic->select);
+
+ cp = (CamelIMAPPCommandPart *)ic->parts.head;
+ cn = cp->next;
+ while (cn) {
+ g_free(cp->data);
+ if (cp->ob)
+ camel_object_unref(cp->ob);
+ g_free(cp);
+ cp = cn;
+ cn = cn->next;
+ }
+
+ g_free(ic);
+}
+
+/* FIXME: error handling */
+void
+camel_imapp_engine_command_queue(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic)
+{
+ CamelIMAPPCommandPart *cp;
+
+ if (ic->mem)
+ imap_engine_command_complete(imap, ic);
+
+ /* FIXME: remove select stuff */
+
+ /* see if we need to pre-queue a select command to select the right folder first */
+ if (ic->select && (imap->last_select == NULL || strcmp(ic->select, imap->last_select) != 0)) {
+ CamelIMAPPCommand *select;
+
+ /* of course ... we can't do anything like store/search if we have to select
+ first, because it'll mess up all the sequence numbers ... hrm ... bugger */
+
+ select = camel_imapp_engine_command_new(imap, "SELECT", NULL, "SELECT %s", ic->select);
+ g_free(imap->last_select);
+ imap->last_select = g_strdup(ic->select);
+ camel_imapp_engine_command_queue(imap, select);
+ /* how does it get freed? handle inside engine? */
+ }
+
+ /* first, check if command can be sent yet ... queue if not */
+ if (imap->literal != NULL) {
+ printf("%p: queueing while literal active\n", ic);
+ e_dlist_addtail(&imap->queue, (EDListNode *)ic);
+ return;
+ }
+
+ cp = (CamelIMAPPCommandPart *)ic->parts.head;
+ g_assert(cp);
+ ic->current = cp;
+
+ /* how to handle exceptions here? */
+
+ printf("queueing command \"%c%05u %s\"\n", imap->tagprefix, ic->tag, cp->data);
+ camel_stream_printf((CamelStream *)imap->stream, "%c%05u %s\r\n", imap->tagprefix, ic->tag, cp->data);
+
+ if (cp->type & CAMEL_IMAPP_COMMAND_CONTINUATION) {
+ printf("%p: active literal\n", ic);
+ g_assert(cp->next);
+ imap->literal = ic;
+ e_dlist_addtail(&imap->active, (EDListNode *)ic);
+ } else {
+ printf("%p: active non-literal\n", ic);
+ g_assert(cp->next && cp->next->next == NULL);
+ e_dlist_addtail(&imap->active, (EDListNode *)ic);
+ }
+}
+
+CamelIMAPPCommand *
+camel_imapp_engine_command_find (CamelIMAPPEngine *imap, const char *name)
+{
+ CamelIMAPPCommand *ic, *in;
+
+ ic = imap->literal;
+ if (ic && strcmp(ic->name, name) == 0)
+ return ic;
+
+ /* first, try active */
+ ic = (CamelIMAPPCommand *)imap->active.head;
+ in = ic->next;
+ while (in) {
+ if (strcmp(ic->name, name) == 0)
+ return ic;
+ ic = in;
+ in = in->next;
+ }
+
+ return NULL;
+}
+
+CamelIMAPPCommand *
+camel_imapp_engine_command_find_tag(CamelIMAPPEngine *imap, unsigned int tag)
+{
+ CamelIMAPPCommand *ic, *in;
+
+ ic = imap->literal;
+ if (ic && ic->tag == tag)
+ return ic;
+
+ ic = (CamelIMAPPCommand *)imap->active.head;
+ in = ic->next;
+ while (in) {
+ if (ic->tag == tag)
+ return ic;
+ ic = in;
+ in = in->next;
+ }
+
+ return NULL;
+}
+
+/* ********************************************************************** */
+
+CamelIMAPPSelectResponse *
+camel_imapp_engine_select(CamelIMAPPEngine *imap, const char *name)
+{
+ CamelIMAPPSelectResponse * volatile resp;
+ CamelIMAPPCommand * volatile ic = NULL;
+
+ resp = g_malloc0(sizeof(*resp));
+ imap->select_response = resp;
+
+ CAMEL_TRY {
+ ic = camel_imapp_engine_command_new(imap, "SELECT", NULL, "SELECT %s", name);
+ camel_imapp_engine_command_queue(imap, ic);
+ while (camel_imapp_engine_iterate(imap, ic) > 0)
+ ;
+
+ if (ic->status->result != IMAP_OK)
+ camel_exception_throw(1, "select failed: %s", ic->status->text);
+ resp->status = ic->status;
+ ic->status = NULL;
+ } CAMEL_CATCH (e) {
+ camel_imapp_engine_command_free(imap, ic);
+ camel_imapp_engine_select_free(imap, resp);
+ imap->select_response = NULL;
+ camel_exception_throw_ex(e);
+ } CAMEL_DONE;
+
+ camel_imapp_engine_command_free(imap, ic);
+ imap->select_response = NULL;
+
+ return resp;
+}
+
+void
+camel_imapp_engine_select_free(CamelIMAPPEngine *imap, CamelIMAPPSelectResponse *select)
+{
+ if (select) {
+ imap_free_status(select->status);
+ g_free(select);
+ }
+}
+
+/* ********************************************************************** */
+
+static void
+imap_engine_command_add_part(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, camel_imapp_command_part_t type, CamelObject *ob)
+{
+ CamelIMAPPCommandPart *cp;
+ CamelStreamNull *null;
+ unsigned int ob_size = 0;
+
+ switch(type & CAMEL_IMAPP_COMMAND_MASK) {
+ case CAMEL_IMAPP_COMMAND_DATAWRAPPER:
+ case CAMEL_IMAPP_COMMAND_STREAM:
+ null = (CamelStreamNull *)camel_stream_null_new();
+ if ( (type & CAMEL_IMAPP_COMMAND_MASK) == CAMEL_IMAPP_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_IMAPP_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_IMAPP_COMMAND_AUTH:
+ /* 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_IMAPP_COMMAND_CONTINUATION;
+ break;
+ default:
+ ob_size = 0;
+ }
+
+ cp = g_malloc0(sizeof(*cp));
+ cp->type = type;
+ cp->ob_size = ob_size;
+ cp->ob = ob;
+ 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 int len(EDList *list)
+{
+ int count = 0;
+ EDListNode *n = list->head;
+
+ while (n->next) {
+ n = n->next;
+ count++;
+ }
+ return count;
+}
+
+static void
+imap_engine_command_complete(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic)
+{
+ c(printf("completing command buffer is [%d] '%.*s'\n", ic->mem->buffer->len, (int)ic->mem->buffer->len, ic->mem->buffer->data));
+ c(printf("command has %d parts\n", len(&ic->parts)));
+ if (ic->mem->buffer->len > 0)
+ imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_SIMPLE, NULL);
+
+ c(printf("command has %d parts\n", len(&ic->parts)));
+
+ camel_object_unref((CamelObject *)ic->mem);
+ ic->mem = NULL;
+}
+
+static void
+imap_engine_command_addv(CamelIMAPPEngine *imap, CamelIMAPPCommand *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;
+ int d;
+ long int l;
+ guint32 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 {
+ 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 *);
+ imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_AUTH, (CamelObject *)A);
+ break;
+ case 'S': /* stream */
+ S = va_arg(ap, CamelStream *);
+ c(printf("got stream '%p'\n", S));
+ imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_STREAM, (CamelObject *)S);
+ break;
+ case 'D': /* datawrapper */
+ D = va_arg(ap, CamelDataWrapper *);
+ c(printf("got data wrapper '%p'\n", D));
+ imap_engine_command_add_part(imap, ic, CAMEL_IMAPP_COMMAND_DATAWRAPPER, (CamelObject *)D);
+ 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));
+ /* FIXME: escpae chars, convert to literal or literal+, etc */
+ camel_stream_printf((CamelStream *)ic->mem, "\"%s\"", s);
+ 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);
+ imap_write_flags((CamelStream *)ic->mem, 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);
+}
+
+
+/* here temporarily while its experimental */
+
+
+#ifdef ENABLE_THREADS
+#include <pthread.h>
+
+static pthread_key_t handler_key = 0;
+
+void camel_exception_setup(void)
+{
+ pthread_key_create(&handler_key, NULL);
+}
+
+#else
+/* this is per-thread in threaded mode */
+static struct _CamelExceptionEnv *handler = NULL;
+
+void camel_exception_setup(void)
+{
+}
+#endif
+
+void
+camel_exception_try(struct _CamelExceptionEnv *env)
+{
+#ifdef ENABLE_THREADS
+ struct _CamelExceptionEnv *handler;
+
+ handler = pthread_getspecific(handler_key);
+#endif
+ env->parent = handler;
+ handler = env;
+ env->ex = NULL;
+
+#ifdef ENABLE_THREADS
+ pthread_setspecific(handler_key, handler);
+#endif
+}
+
+void
+camel_exception_throw_ex(CamelException *ex)
+{
+ struct _CamelExceptionEnv *env;
+#ifdef ENABLE_THREADS
+ struct _CamelExceptionEnv *handler;
+
+ handler = pthread_getspecific(handler_key);
+#endif
+ printf("throwing exception '%s'\n", ex->desc);
+
+ env = handler;
+ if (env != NULL) {
+ env->ex = ex;
+ handler = env->parent;
+#ifdef ENABLE_THREADS
+ pthread_setspecific(handler_key, handler);
+#endif
+ longjmp(env->env, ex->id);
+ } else {
+ g_warning("Uncaught 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)
+{
+#ifdef ENABLE_THREADS
+ pthread_setspecific(handler_key, env->parent);
+#else
+ handler = env->parent;
+#endif
+}
+
+void
+camel_exception_done(struct _CamelExceptionEnv *env)
+{
+#ifdef ENABLE_THREADS
+ pthread_setspecific(handler_key, env->parent);
+#else
+ handler = env->parent;
+#endif
+ if (env->ex != NULL) {
+ camel_exception_free(env->ex);
+ }
+}
diff --git a/camel/providers/imapp/camel-imapp-engine.h b/camel/providers/imapp/camel-imapp-engine.h
new file mode 100644
index 0000000000..6db9968d48
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-engine.h
@@ -0,0 +1,154 @@
+
+#ifndef _CAMEL_IMAPP_ENGINE_H
+#define _CAMEL_IMAPP_ENGINE_H
+
+#include <camel/camel-object.h>
+
+#include "camel-imapp-stream.h"
+#include <e-util/e-msgport.h>
+#include "camel-imapp-folder.h"
+
+#define CAMEL_IMAPP_ENGINE_TYPE (camel_imapp_engine_get_type ())
+#define CAMEL_IMAPP_ENGINE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_IMAPP_ENGINE_TYPE, CamelIMAPPEngine))
+#define CAMEL_IMAPP_ENGINE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAPP_ENGINE_TYPE, CamelIMAPPEngineClass))
+#define CAMEL_IS_IMAP_ENGINE(o) (CAMEL_CHECK_TYPE((o), CAMEL_IMAPP_ENGINE_TYPE))
+
+typedef struct _CamelIMAPPEngine CamelIMAPPEngine;
+typedef struct _CamelIMAPPEngineClass CamelIMAPPEngineClass;
+
+typedef struct _CamelIMAPPCommandPart CamelIMAPPCommandPart;
+typedef struct _CamelIMAPPCommand CamelIMAPPCommand;
+
+typedef enum {
+ CAMEL_IMAPP_COMMAND_SIMPLE = 0,
+ CAMEL_IMAPP_COMMAND_DATAWRAPPER,
+ CAMEL_IMAPP_COMMAND_STREAM,
+ CAMEL_IMAPP_COMMAND_AUTH,
+ CAMEL_IMAPP_COMMAND_MASK = 0xff,
+ CAMEL_IMAPP_COMMAND_CONTINUATION = 0x8000 /* does this command expect continuation? */
+} camel_imapp_command_part_t;
+
+struct _CamelIMAPPCommandPart {
+ struct _CamelIMAPPCommandPart *next;
+ struct _CamelIMAPPCommandPart *prev;
+
+ struct _CamelIMAPPCommand *parent;
+
+ int data_size;
+ char *data;
+
+ camel_imapp_command_part_t type;
+
+ int ob_size;
+ CamelObject *ob;
+};
+
+typedef int (*CamelIMAPPEngineFunc)(struct _CamelIMAPPEngine *engine, guint32 id, void *data);
+typedef void (*CamelIMAPPCommandFunc)(struct _CamelIMAPPEngine *engine, struct _CamelIMAPPCommand *, void *data);
+
+/* FIXME: make this refcounted */
+struct _CamelIMAPPCommand {
+ struct _CamelIMAPPCommand *next;
+ struct _CamelIMAPPCommand *prev;
+
+ const char *name; /* command name/type (e.g. FETCH) */
+
+ /* FIXME: remove this select stuff */
+ char *select; /* if we need to run against a specific folder */
+ struct _status_info *status; /* status for command, indicates it is complete if != NULL */
+
+ unsigned int tag;
+
+ struct _CamelStreamMem *mem; /* for building the part */
+ EDList parts;
+ CamelIMAPPCommandPart *current;
+
+ CamelIMAPPCommandFunc complete;
+ void *complete_data;
+};
+
+typedef struct _CamelIMAPPSelectResponse CamelIMAPPSelectResponse;
+
+struct _CamelIMAPPSelectResponse {
+ struct _status_info *status;
+ guint32 exists;
+ guint32 recent;
+ guint32 uidvalidity;
+ guint32 unseen;
+ guint32 permanentflags;
+};
+
+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),
+};
+
+/* currently selected states */
+typedef enum _camel_imapp_engine_state_t {
+ IMAP_ENGINE_DISCONNECT, /* only happens during shutdown */
+ IMAP_ENGINE_CONNECT, /* connected, not authenticated */
+ IMAP_ENGINE_AUTH, /* connected, and authenticated */
+ IMAP_ENGINE_SELECT, /* and selected, select holds selected folder */
+} camel_imapp_engine_state_t;
+
+struct _CamelIMAPPEngine {
+ CamelObject parent_object;
+
+ CamelIMAPPStream *stream;
+
+ camel_imapp_engine_state_t state;
+
+ guint32 capa; /* capabilities for this server, refresh with :capabilities() */
+
+ GHashTable *handlers;
+
+ unsigned char tagprefix; /* out tag prefix 'A' 'B' ... 'Z' */
+ unsigned int tag; /* next command tag */
+
+ char *select; /* *currently* selected folder */
+ char *last_select; /* last selected or to-be selected folder (e.g. outstanding queued select) */
+ CamelIMAPPCommand *literal;/* current literal op */
+ EDList active; /* active queue */
+ EDList queue; /* outstanding queue */
+
+ /* keep track of running a select */
+ struct _CamelIMAPPSelectResponse *select_response;
+};
+
+struct _CamelIMAPPEngineClass {
+ CamelObjectClass parent_class;
+
+ unsigned char tagprefix;
+
+ /* Events:
+ status(struct _status_info *);
+ */
+};
+
+CamelType camel_imapp_engine_get_type (void);
+
+CamelIMAPPEngine *camel_imapp_engine_new(CamelIMAPPStream *stream);
+
+void camel_imapp_engine_add_handler(CamelIMAPPEngine *imap, const char *response, CamelIMAPPEngineFunc func, void *data);
+int camel_imapp_engine_iterate(CamelIMAPPEngine *imap, CamelIMAPPCommand *wait); /* throws PARSE,IO exception */
+int camel_imapp_engine_skip(CamelIMAPPEngine *imap);
+int camel_imapp_engine_capabilities(CamelIMAPPEngine *imap);
+
+CamelIMAPPCommand *camel_imapp_engine_command_new (CamelIMAPPEngine *imap, const char *name, const char *select, const char *fmt, ...);
+void camel_imapp_engine_command_complete(CamelIMAPPEngine *imap, struct _CamelIMAPPCommand *, CamelIMAPPCommandFunc func, void *data);
+void camel_imapp_engine_command_add (CamelIMAPPEngine *imap, CamelIMAPPCommand *ic, const char *fmt, ...);
+void camel_imapp_engine_command_free (CamelIMAPPEngine *imap, CamelIMAPPCommand *ic);
+void camel_imapp_engine_command_queue(CamelIMAPPEngine *imap, CamelIMAPPCommand *ic); /* throws IO exception */
+CamelIMAPPCommand *camel_imapp_engine_command_find (CamelIMAPPEngine *imap, const char *name);
+CamelIMAPPCommand *camel_imapp_engine_command_find_tag(CamelIMAPPEngine *imap, unsigned int tag);
+
+/* util functions */
+CamelIMAPPSelectResponse *camel_imapp_engine_select(CamelIMAPPEngine *imap, const char *name);
+void camel_imapp_engine_select_free(CamelIMAPPEngine *imap, CamelIMAPPSelectResponse *select);
+
+#endif
diff --git a/camel/providers/imapp/camel-imapp-exception.h b/camel/providers/imapp/camel-imapp-exception.h
new file mode 100644
index 0000000000..5e18b6c815
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-exception.h
@@ -0,0 +1,35 @@
+
+/* This implements 'real' exceptions that work a bit like c++/java exceptions */
+
+/* Still experimental code */
+
+#ifndef __CAMEL_IMAPP_EXCEPTION_H
+#define __CAMEL_IMAPP_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/imapp/camel-imapp-fetch-stream.c b/camel/providers/imapp/camel-imapp-fetch-stream.c
new file mode 100644
index 0000000000..bf18aac57b
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-fetch-stream.c
@@ -0,0 +1,183 @@
+/* -*- 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-imapp-stream.h"
+#include "camel-imapp-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_IMAPP_FETCH_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+static ssize_t
+stream_read(CamelStream *stream, char *buffer, size_t n)
+{
+ CamelIMAPPFetchStream *is = (CamelIMAPPFetchStream *)stream;
+ ssize_t max;
+
+ /* make sure we have all the data read in */
+ while (camel_imapp_engine_iterate(is->engine, is->command)>0)
+ ;
+
+ 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;
+ }
+
+ is->literal -= max;
+
+ return max;
+}
+
+static ssize_t
+stream_write(CamelStream *stream, const char *buffer, size_t n)
+{
+ CamelIMAPPFetchStream *is = (CamelIMAPPFetchStream *)stream;
+
+ 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)
+{
+ CamelIMAPPFetchStream *is = (CamelIMAPPFetchStream *)stream;
+
+ return is->literal == 0;
+}
+
+static int
+stream_reset(CamelStream *stream)
+{
+ /* nop? reset literal mode? */
+ return 0;
+}
+
+static void
+camel_imapp_fetch_stream_class_init (CamelStreamClass *camel_imapp_fetch_stream_class)
+{
+ CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_imapp_fetch_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_imapp_fetch_stream_init(CamelIMAPPFetchStream *is, CamelIMAPPFetchStreamClass *isclass)
+{
+ ;
+}
+
+static void
+camel_imapp_fetch_stream_finalise(CamelIMAPPFetchStream *is)
+{
+ if (is->engine)
+ camel_object_unref(is->engine);
+}
+
+CamelType
+camel_imapp_fetch_stream_get_type (void)
+{
+ static CamelType camel_imapp_fetch_stream_type = CAMEL_INVALID_TYPE;
+
+ if (camel_imapp_fetch_stream_type == CAMEL_INVALID_TYPE) {
+ setup_table();
+ camel_imapp_fetch_stream_type = camel_type_register( camel_stream_get_type(),
+ "CamelIMAPPFetchStream",
+ sizeof( CamelIMAPPFetchStream ),
+ sizeof( CamelIMAPPFetchStreamClass ),
+ (CamelObjectClassInitFunc) camel_imapp_fetch_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_imapp_fetch_stream_init,
+ (CamelObjectFinalizeFunc) camel_imapp_fetch_stream_finalise );
+ }
+
+ return camel_imapp_fetch_stream_type;
+}
+
+/**
+ * camel_imapp_fetch_stream_new:
+ *
+ * Return value: the stream
+ **/
+CamelStream *
+camel_imapp_fetch_stream_new(CamelIMAPPEngine *ie, const char *uid, const char *body)
+{
+ CamelIMAPPFetchStream *is;
+
+ is = (CamelIMAPPFetchStream *)camel_object_new(camel_imapp_fetch_stream_get_type ());
+ is->engine = ie;
+ camel_object_ref(ie);
+
+ is->command = camel_imapp_engine_command_new(ie, "FETCH", NULL, "FETCH %t (BODY[%t]<0.4096>", uid, body);
+ camel_imapp_engine_command_queue(ie, command);
+
+ return (CamelStream *)is;
+}
+
diff --git a/camel/providers/imapp/camel-imapp-fetch-stream.h b/camel/providers/imapp/camel-imapp-fetch-stream.h
new file mode 100644
index 0000000000..c9281ff64f
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-fetch-stream.h
@@ -0,0 +1,47 @@
+/*
+ * 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_IMAPP_FETCH_STREAM_H
+#define _CAMEL_IMAPP_FETCH_STREAM_H
+
+#include <camel/camel-stream.h>
+
+#define CAMEL_IMAPP_FETCH_STREAM(obj) CAMEL_CHECK_CAST (obj, camel_imapp_fetch_stream_get_type (), CamelIMAPPFetchStream)
+#define CAMEL_IMAPP_FETCH_STREAM_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapp_fetch_stream_get_type (), CamelIMAPPFetchStreamClass)
+#define CAMEL_IS_IMAP_FETCH_STREAM(obj) CAMEL_CHECK_TYPE (obj, camel_imapp_fetch_stream_get_type ())
+
+typedef struct _CamelIMAPPFetchStreamClass CamelIMAPPFetchStreamClass;
+typedef struct _CamelIMAPPFetchStream CamelIMAPPFetchStream;
+
+struct _CamelIMAPPFetchStream {
+ CamelStream parent;
+
+ struct _CamelIMAPPEngine *engine;
+};
+
+struct _CamelIMAPPFetchStreamClass {
+ CamelStreamClass parent_class;
+};
+
+CamelType camel_imapp_fetch_stream_get_type (void);
+
+CamelStream *camel_imapp_fetch_stream_new (struct _CamelIMAPPEngine *src, const char *uid, const char *spec);
+
+#endif /* ! _CAMEL_IMAPP_FETCH_STREAM_H */
diff --git a/camel/providers/imapp/camel-imapp-folder.c b/camel/providers/imapp/camel-imapp-folder.c
new file mode 100644
index 0000000000..19f3795056
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-folder.c
@@ -0,0 +1,268 @@
+/* -*- 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-imapp-store.h"
+#include "camel-imapp-folder.h"
+#include "camel-imapp-summary.h"
+#include "camel-imapp-exception.h"
+
+#include <e-util/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;
+
+static void imap_finalize (CamelObject *object);
+static void imap_refresh_info (CamelFolder *folder, CamelException *ex);
+static void imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
+static CamelMimeMessage *imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex);
+
+static void
+imap_folder_class_init (CamelIMAPPFolderClass *camel_imapp_folder_class)
+{
+ CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_imapp_folder_class);
+
+ parent_class = CAMEL_FOLDER_CLASS(camel_folder_get_type());
+
+ /* virtual method overload */
+ camel_folder_class->refresh_info = imap_refresh_info;
+ camel_folder_class->sync = imap_sync;
+
+ camel_folder_class->get_message = imap_get_message;
+ /*camel_folder_class->set_message_flags = imap_set_message_flags;*/
+}
+
+static void
+imap_folder_init(CamelObject *o, CamelObjectClass *klass)
+{
+ CamelFolder *folder = (CamelFolder *)o;
+ CamelIMAPPFolder *ifolder = (CamelIMAPPFolder *)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;
+
+ /* FIXME: this is just a skeleton */
+
+ ifolder->changes = camel_folder_change_info_new();
+}
+
+CamelType
+camel_imapp_folder_get_type (void)
+{
+ static CamelType camel_imapp_folder_type = CAMEL_INVALID_TYPE;
+
+ if (!camel_imapp_folder_type) {
+ camel_imapp_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelIMAPPFolder",
+ sizeof (CamelIMAPPFolder),
+ sizeof (CamelIMAPPFolderClass),
+ (CamelObjectClassInitFunc) imap_folder_class_init,
+ NULL,
+ imap_folder_init,
+ (CamelObjectFinalizeFunc) imap_finalize);
+ }
+
+ return camel_imapp_folder_type;
+}
+
+void
+imap_finalize (CamelObject *object)
+{
+ CamelIMAPPFolder *folder = (CamelIMAPPFolder *)object;
+
+ camel_folder_change_info_free(folder->changes);
+}
+
+CamelFolder *
+camel_imapp_folder_new(CamelStore *store, const char *path)
+{
+ CamelFolder *folder;
+ char *root;
+
+ d(printf("opening imap folder '%s'\n", path));
+
+ folder = CAMEL_FOLDER (camel_object_new (CAMEL_IMAPP_FOLDER_TYPE));
+ camel_folder_construct(folder, store, path, path);
+
+ ((CamelIMAPPFolder *)folder)->raw_name = g_strdup(path);
+
+ folder->summary = camel_imapp_summary_new();
+
+ root = camel_session_get_storage_path(((CamelService *)store)->session, (CamelService *)store, NULL);
+ if (root) {
+ char *base = g_build_filename(root, path, NULL);
+ char *file = g_build_filename(base, ".ev-summary", NULL);
+
+ camel_mkdir(base, 0777);
+ g_free(base);
+
+ camel_folder_summary_set_filename(folder->summary, file);
+ printf("loading summary from '%s' (root=%s)\n", file, root);
+ g_free(file);
+ camel_folder_summary_load(folder->summary);
+ g_free(root);
+ }
+
+ return folder;
+}
+
+void
+camel_imapp_folder_open(CamelIMAPPFolder *folder, CamelException *ex)
+{
+ /* */
+}
+
+void
+camel_imapp_folder_delete(CamelIMAPPFolder *folder, CamelException *ex)
+{
+}
+
+void
+camel_imapp_folder_rename(CamelIMAPPFolder *folder, const char *new, CamelException *ex)
+{
+}
+
+void
+camel_imapp_folder_close(CamelIMAPPFolder *folder, CamelException *ex)
+{
+}
+
+static void
+imap_refresh_info (CamelFolder *folder, CamelException *ex)
+{
+ printf("imapp refresh info?\n");
+}
+
+static void
+imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
+{
+ camel_imapp_driver_sync(((CamelIMAPPStore *)(folder->parent_store))->driver, expunge, folder);
+}
+
+static CamelMimeMessage *
+imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
+{
+ CamelMimeMessage * volatile msg = NULL;
+ CamelStream * volatile stream = NULL;
+
+ printf("get message '%s'\n", uid);
+
+ CAMEL_TRY {
+ /* simple implementation, just get whole message in 1 go */
+ stream = camel_imapp_driver_fetch(((CamelIMAPPStore *)(folder->parent_store))->driver, (CamelIMAPPFolder *)folder, uid, "");
+ camel_stream_reset(stream);
+ msg = camel_mime_message_new();
+ if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)msg, stream) != -1) {
+ /* do we care? */
+ }
+ } CAMEL_CATCH(e) {
+ if (msg)
+ camel_object_unref(msg);
+ msg = NULL;
+ camel_exception_xfer(ex, e);
+ } CAMEL_DONE;
+
+ if (stream)
+ camel_object_unref(stream);
+
+ return msg;
+}
+
+
+/* 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
+*/
+
diff --git a/camel/providers/imapp/camel-imapp-folder.h b/camel/providers/imapp/camel-imapp-folder.h
new file mode 100644
index 0000000000..329c66b7fc
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-folder.h
@@ -0,0 +1,66 @@
+/* -*- 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_IMAPP_FOLDER_H
+#define CAMEL_IMAPP_FOLDER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <camel/camel-folder.h>
+
+#define CAMEL_IMAPP_FOLDER_TYPE (camel_imapp_folder_get_type ())
+#define CAMEL_IMAPP_FOLDER(obj) (CAMEL_CHECK_CAST((obj), CAMEL_IMAPP_FOLDER_TYPE, CamelIMAPPFolder))
+#define CAMEL_IMAPP_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAPP_FOLDER_TYPE, CamelIMAPPFolderClass))
+#define CAMEL_IS_IMAP_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_IMAPP_FOLDER_TYPE))
+
+typedef struct _CamelIMAPPFolder {
+ CamelFolder parent_object;
+
+ char *raw_name;
+ CamelFolderChangeInfo *changes;
+
+ guint32 exists;
+ guint32 recent;
+ guint32 uidvalidity;
+ guint32 unseen;
+ guint32 permanentflags;
+} CamelIMAPPFolder;
+
+typedef struct _CamelIMAPPFolderClass {
+ CamelFolderClass parent_class;
+} CamelIMAPPFolderClass;
+
+/* Standard Camel function */
+CamelType camel_imapp_folder_get_type (void);
+
+/* public methods */
+CamelFolder *camel_imapp_folder_new(CamelStore *parent, const char *path);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_IMAPP_FOLDER_H */
diff --git a/camel/providers/imapp/camel-imapp-provider.c b/camel/providers/imapp/camel-imapp-provider.c
new file mode 100644
index 0000000000..4abc633498
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-provider.c
@@ -0,0 +1,91 @@
+/* -*- 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-imapp-store.h"
+
+CamelProviderConfEntry imapp_conf_entries[] = {
+ { CAMEL_PROVIDER_CONF_SECTION_START, NULL, NULL,
+ N_("Message storage") },
+ { CAMEL_PROVIDER_CONF_SECTION_END },
+ { CAMEL_PROVIDER_CONF_END }
+};
+
+static CamelProvider imapp_provider = {
+ "imapp",
+
+ 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,
+
+ imapp_conf_entries,
+
+ /* ... */
+};
+
+CamelServiceAuthType camel_imapp_password_authtype = {
+ N_("Password"),
+
+ N_("This option will connect to the IMAP server using a "
+ "plaintext password."),
+
+ "",
+ TRUE
+};
+
+void
+camel_imapp_module_init(CamelSession *session)
+{
+ imapp_provider.object_types[CAMEL_PROVIDER_STORE] = camel_imapp_store_get_type();
+ imapp_provider.url_hash = camel_url_hash;
+ imapp_provider.url_equal = camel_url_equal;
+
+ imapp_provider.authtypes = g_list_prepend(imapp_provider.authtypes, camel_sasl_authtype_list(FALSE));
+ imapp_provider.authtypes = g_list_prepend(imapp_provider.authtypes, &camel_imapp_password_authtype);
+
+ camel_session_register_provider(session, &imapp_provider);
+}
+
+void
+camel_provider_module_init(CamelSession *session)
+{
+ camel_imapp_module_init(session);
+}
diff --git a/camel/providers/imapp/camel-imapp-store-summary.c b/camel/providers/imapp/camel-imapp-store-summary.c
new file mode 100644
index 0000000000..a3ad57fb11
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-store-summary.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2002 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "camel-imapp-store-summary.h"
+
+#include "camel/camel-file-utils.h"
+
+#include "camel-string-utils.h"
+#include "e-util/md5-utils.h"
+#include "e-util/e-memory.h"
+
+#include "camel-private.h"
+#include "camel-utf8.h"
+
+#define d(x)
+#define io(x) /* io debug */
+
+#define CAMEL_IMAPP_STORE_SUMMARY_VERSION_0 (0)
+
+#define CAMEL_IMAPP_STORE_SUMMARY_VERSION (0)
+
+#define _PRIVATE(o) (((CamelIMAPPStoreSummary *)(o))->priv)
+
+static int summary_header_load(CamelStoreSummary *, FILE *);
+static int summary_header_save(CamelStoreSummary *, FILE *);
+
+/*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);*/
+static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
+static int store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *);
+static void store_info_free(CamelStoreSummary *, CamelStoreInfo *);
+
+static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int);
+static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *);
+
+static void camel_imapp_store_summary_class_init (CamelIMAPPStoreSummaryClass *klass);
+static void camel_imapp_store_summary_init (CamelIMAPPStoreSummary *obj);
+static void camel_imapp_store_summary_finalise (CamelObject *obj);
+
+static CamelStoreSummaryClass *camel_imapp_store_summary_parent;
+
+static void
+camel_imapp_store_summary_class_init (CamelIMAPPStoreSummaryClass *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_imapp_store_summary_init (CamelIMAPPStoreSummary *s)
+{
+ /*struct _CamelIMAPPStoreSummaryPrivate *p;
+
+ p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/
+
+ ((CamelStoreSummary *)s)->store_info_size = sizeof(CamelIMAPPStoreInfo);
+ s->version = CAMEL_IMAPP_STORE_SUMMARY_VERSION;
+}
+
+static void
+camel_imapp_store_summary_finalise (CamelObject *obj)
+{
+ /*struct _CamelIMAPPStoreSummaryPrivate *p;*/
+ /*CamelIMAPPStoreSummary *s = (CamelIMAPPStoreSummary *)obj;*/
+
+ /*p = _PRIVATE(obj);
+ g_free(p);*/
+}
+
+CamelType
+camel_imapp_store_summary_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ camel_imapp_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
+ type = camel_type_register((CamelType)camel_imapp_store_summary_parent, "CamelIMAPPStoreSummary",
+ sizeof (CamelIMAPPStoreSummary),
+ sizeof (CamelIMAPPStoreSummaryClass),
+ (CamelObjectClassInitFunc) camel_imapp_store_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_imapp_store_summary_init,
+ (CamelObjectFinalizeFunc) camel_imapp_store_summary_finalise);
+ }
+
+ return type;
+}
+
+/**
+ * camel_imapp_store_summary_new:
+ *
+ * Create a new CamelIMAPPStoreSummary object.
+ *
+ * Return value: A new CamelIMAPPStoreSummary widget.
+ **/
+CamelIMAPPStoreSummary *
+camel_imapp_store_summary_new (void)
+{
+ CamelIMAPPStoreSummary *new = CAMEL_IMAPP_STORE_SUMMARY ( camel_object_new (camel_imapp_store_summary_get_type ()));
+
+ return new;
+}
+
+/**
+ * camel_imapp_store_summary_full_name:
+ * @s:
+ * @path:
+ *
+ * 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().
+ **/
+CamelIMAPPStoreInfo *
+camel_imapp_store_summary_full_name(CamelIMAPPStoreSummary *s, const char *full_name)
+{
+ int count, i;
+ CamelIMAPPStoreInfo *info;
+
+ count = camel_store_summary_count((CamelStoreSummary *)s);
+ for (i=0;i<count;i++) {
+ info = (CamelIMAPPStoreInfo *)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;
+}
+
+char *
+camel_imapp_store_summary_full_to_path(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep)
+{
+ char *path, *p;
+ int c;
+ const char *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 = (char *)full_name;
+
+ return camel_utf7_utf8(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;
+}
+
+char *
+camel_imapp_store_summary_path_to_full(CamelIMAPPStoreSummary *s, const char *path, char dir_sep)
+{
+ unsigned char *full, *f;
+ guint32 c, v = 0;
+ const char *p;
+ int state=0;
+ char *subpath, *last = NULL;
+ CamelStoreInfo *si;
+ CamelIMAPPStoreNamespace *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_imapp_store_info_full_name(s, si));
+ camel_store_summary_info_free((CamelStoreSummary *)s, si);
+ return f;
+ }
+
+ ns = camel_imapp_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 unsigned char **)&p)) ) {
+ switch(state) {
+ case 0:
+ if (c == '%')
+ state = 1;
+ else {
+ if (c == '/')
+ c = dir_sep;
+ camel_utf8_putc(&f, c);
+ }
+ break;
+ case 1:
+ state = 2;
+ v = hexnib(c)<<4;
+ break;
+ case 2:
+ state = 0;
+ v |= hexnib(c);
+ camel_utf8_putc(&f, v);
+ break;
+ }
+ }
+ camel_utf8_putc(&f, c);
+
+ /* merge old path part if required */
+ f = camel_utf8_utf7(full);
+ if (si) {
+ full = g_strdup_printf("%s%s", camel_imapp_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;
+}
+
+CamelIMAPPStoreInfo *
+camel_imapp_store_summary_add_from_full(CamelIMAPPStoreSummary *s, const char *full, char dir_sep)
+{
+ CamelIMAPPStoreInfo *info;
+ char *pathu8, *prefix;
+ int len;
+ char *full_name;
+ CamelIMAPPStoreNamespace *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_imapp_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_imapp_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_imapp_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_imapp_store_summary_full_to_path(s, full_name, dir_sep);
+ }
+
+ info = (CamelIMAPPStoreInfo *)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_IMAPP_STORE_INFO_FULL_NAME, full_name);
+ } else
+ d(printf(" failed\n"));
+
+ return info;
+}
+
+/* should this be const? */
+/* TODO: deprecate/merge this function with path_to_full */
+char *
+camel_imapp_store_summary_full_from_path(CamelIMAPPStoreSummary *s, const char *path)
+{
+ CamelIMAPPStoreNamespace *ns;
+ char *name = NULL;
+
+ ns = camel_imapp_store_summary_namespace_find_path(s, path);
+ if (ns)
+ name = camel_imapp_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 */
+CamelIMAPPStoreNamespace *camel_imapp_store_summary_namespace_new(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep)
+{
+ CamelIMAPPStoreNamespace *ns;
+ char *p;
+ int 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;
+
+ p = ns->path = camel_imapp_store_summary_full_to_path(s, ns->full_name, dir_sep);
+ while (*p) {
+ if (*p == '/')
+ *p = '.';
+ p++;
+ }
+
+ return ns;
+}
+
+void camel_imapp_store_summary_namespace_set(CamelIMAPPStoreSummary *s, CamelIMAPPStoreNamespace *ns)
+{
+ static void namespace_clear(CamelStoreSummary *s);
+
+ 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);
+}
+
+CamelIMAPPStoreNamespace *
+camel_imapp_store_summary_namespace_find_path(CamelIMAPPStoreSummary *s, const char *path)
+{
+ int len;
+ CamelIMAPPStoreNamespace *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;
+}
+
+CamelIMAPPStoreNamespace *
+camel_imapp_store_summary_namespace_find_full(CamelIMAPPStoreSummary *s, const char *full)
+{
+ int len;
+ CamelIMAPPStoreNamespace *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, CamelIMAPPStoreNamespace *ns)
+{
+ g_free(ns->path);
+ g_free(ns->full_name);
+ g_free(ns);
+}
+
+static void
+namespace_clear(CamelStoreSummary *s)
+{
+ CamelIMAPPStoreSummary *is = (CamelIMAPPStoreSummary *)s;
+
+ if (is->namespace)
+ namespace_free(s, is->namespace);
+ is->namespace = NULL;
+}
+
+static CamelIMAPPStoreNamespace *
+namespace_load(CamelStoreSummary *s, FILE *in)
+{
+ CamelIMAPPStoreNamespace *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 int
+namespace_save(CamelStoreSummary *s, FILE *in, CamelIMAPPStoreNamespace *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 int
+summary_header_load(CamelStoreSummary *s, FILE *in)
+{
+ CamelIMAPPStoreSummary *is = (CamelIMAPPStoreSummary *)s;
+ gint32 version, capabilities, count;
+
+ namespace_clear(s);
+
+ if (camel_imapp_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_IMAPP_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 int
+summary_header_save(CamelStoreSummary *s, FILE *out)
+{
+ CamelIMAPPStoreSummary *is = (CamelIMAPPStoreSummary *)s;
+ guint32 count;
+
+ count = is->namespace?1:0;
+
+ /* always write as latest version */
+ if (camel_imapp_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1
+ || camel_file_util_encode_fixed_int32(out, CAMEL_IMAPP_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)
+{
+ CamelIMAPPStoreInfo *mi;
+
+ mi = (CamelIMAPPStoreInfo *)camel_imapp_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;
+ }
+ }
+
+ return (CamelStoreInfo *)mi;
+}
+
+static int
+store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
+{
+ CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
+
+ if (camel_imapp_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)
+{
+ CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
+
+ g_free(isi->full_name);
+ camel_imapp_store_summary_parent->store_info_free(s, mi);
+}
+
+static const char *
+store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
+{
+ CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
+
+ /* FIXME: Locks? */
+
+ g_assert (mi != NULL);
+
+ switch (type) {
+ case CAMEL_IMAPP_STORE_INFO_FULL_NAME:
+ return isi->full_name;
+ default:
+ return camel_imapp_store_summary_parent->store_info_string(s, mi, type);
+ }
+}
+
+static void
+store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
+{
+ CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
+
+ g_assert(mi != NULL);
+
+ switch(type) {
+ case CAMEL_IMAPP_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_imapp_store_summary_parent->store_info_set_string(s, mi, type, str);
+ break;
+ }
+}
diff --git a/camel/providers/imapp/camel-imapp-store-summary.h b/camel/providers/imapp/camel-imapp-store-summary.h
new file mode 100644
index 0000000000..154fe8798e
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-store-summary.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002 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_IMAPP_STORE_SUMMARY_H
+#define _CAMEL_IMAPP_STORE_SUMMARY_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <camel/camel-object.h>
+#include <camel/camel-store-summary.h>
+
+#define CAMEL_IMAPP_STORE_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_imapp_store_summary_get_type (), CamelIMAPPStoreSummary)
+#define CAMEL_IMAPP_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapp_store_summary_get_type (), CamelIMAPPStoreSummaryClass)
+#define CAMEL_IS_IMAP_STORE_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_imapp_store_summary_get_type ())
+
+typedef struct _CamelIMAPPStoreSummary CamelIMAPPStoreSummary;
+typedef struct _CamelIMAPPStoreSummaryClass CamelIMAPPStoreSummaryClass;
+
+typedef struct _CamelIMAPPStoreInfo CamelIMAPPStoreInfo;
+
+enum {
+ CAMEL_IMAPP_STORE_INFO_FULL_NAME = CAMEL_STORE_INFO_LAST,
+ CAMEL_IMAPP_STORE_INFO_LAST,
+};
+
+struct _CamelIMAPPStoreInfo {
+ CamelStoreInfo info;
+ char *full_name;
+};
+
+typedef struct _CamelIMAPPStoreNamespace CamelIMAPPStoreNamespace;
+
+struct _CamelIMAPPStoreNamespace {
+ char *path; /* display path */
+ char *full_name; /* real name */
+ char sep; /* directory separator */
+};
+
+struct _CamelIMAPPStoreSummary {
+ CamelStoreSummary summary;
+
+ struct _CamelIMAPPStoreSummaryPrivate *priv;
+
+ /* header info */
+ guint32 version; /* version of base part of file */
+ guint32 capabilities;
+ CamelIMAPPStoreNamespace *namespace; /* eventually to be a list */
+};
+
+struct _CamelIMAPPStoreSummaryClass {
+ CamelStoreSummaryClass summary_class;
+};
+
+CamelType camel_imapp_store_summary_get_type (void);
+CamelIMAPPStoreSummary *camel_imapp_store_summary_new (void);
+
+/* TODO: this api needs some more work, needs to support lists */
+CamelIMAPPStoreNamespace *camel_imapp_store_summary_namespace_new(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep);
+void camel_imapp_store_summary_namespace_set(CamelIMAPPStoreSummary *s, CamelIMAPPStoreNamespace *ns);
+CamelIMAPPStoreNamespace *camel_imapp_store_summary_namespace_find_path(CamelIMAPPStoreSummary *s, const char *path);
+CamelIMAPPStoreNamespace *camel_imapp_store_summary_namespace_find_full(CamelIMAPPStoreSummary *s, const char *full_name);
+
+/* converts to/from utf8 canonical nasmes */
+char *camel_imapp_store_summary_full_to_path(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep);
+char *camel_imapp_store_summary_path_to_full(CamelIMAPPStoreSummary *s, const char *path, char dir_sep);
+
+CamelIMAPPStoreInfo *camel_imapp_store_summary_full_name(CamelIMAPPStoreSummary *s, const char *full_name);
+CamelIMAPPStoreInfo *camel_imapp_store_summary_add_from_full(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep);
+
+/* a convenience lookup function. always use this if path known */
+char *camel_imapp_store_summary_full_from_path(CamelIMAPPStoreSummary *s, const char *path);
+
+/* helper macro's */
+#define camel_imapp_store_info_full_name(s, i) (camel_store_info_string((CamelStoreSummary *)s, (const CamelStoreInfo *)i, CAMEL_IMAPP_STORE_INFO_FULL_NAME))
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _CAMEL_IMAPP_STORE_SUMMARY_H */
diff --git a/camel/providers/imapp/camel-imapp-store.c b/camel/providers/imapp/camel-imapp-store.c
new file mode 100644
index 0000000000..36dde917bb
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-store.c
@@ -0,0 +1,1221 @@
+/* -*- 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-imapp-store-summary.h"
+#include "camel-imapp-store.h"
+#include "camel-imapp-folder.h"
+#include "camel-imapp-engine.h"
+#include "camel-imapp-exception.h"
+#include "camel-imapp-utils.h"
+#include "camel-imapp-driver.h"
+
+/* Specified in RFC 2060 section 2.1 */
+#define IMAP_PORT 143
+
+static CamelStoreClass *parent_class = NULL;
+
+static void finalize (CamelObject *object);
+
+static void imap_construct(CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex);
+/* static char *imap_get_name(CamelService *service, gboolean brief);*/
+static gboolean imap_connect (CamelService *service, CamelException *ex);
+static gboolean imap_disconnect (CamelService *service, gboolean clean, CamelException *ex);
+static GList *imap_query_auth_types (CamelService *service, CamelException *ex);
+
+static void imap_init_trash (CamelStore *store);
+static CamelFolder *imap_get_trash (CamelStore *store, CamelException *ex);
+
+static CamelFolder *imap_get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex);
+static CamelFolder *imap_get_inbox (CamelStore *store, CamelException *ex);
+static void imap_rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex);
+static CamelFolderInfo *imap_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex);
+static void imap_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex);
+static void imap_rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex);
+static CamelFolderInfo *imap_create_folder(CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex);
+
+static int store_resp_list(CamelIMAPPEngine *ie, guint32 id, void *data);
+
+/* yet to see if this should go global or not */
+void camel_imapp_store_folder_selected(CamelIMAPPStore *store, CamelIMAPPFolder *folder, CamelIMAPPSelectResponse *select);
+
+static void
+camel_imapp_store_class_init (CamelIMAPPStoreClass *camel_imapp_store_class)
+{
+ CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS(camel_imapp_store_class);
+ CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS(camel_imapp_store_class);
+
+ parent_class = CAMEL_STORE_CLASS(camel_type_get_global_classfuncs(camel_store_get_type()));
+
+ /* virtual method overload */
+ camel_service_class->construct = imap_construct;
+ /*camel_service_class->get_name = imap_get_name;*/
+ 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->init_trash = imap_init_trash;
+ 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;
+}
+
+static void
+camel_imapp_store_init (gpointer object, gpointer klass)
+{
+ CamelIMAPPStore *istore = object;
+}
+
+CamelType
+camel_imapp_store_get_type (void)
+{
+ static CamelType camel_imapp_store_type = CAMEL_INVALID_TYPE;
+
+ if (!camel_imapp_store_type) {
+ camel_imapp_store_type = camel_type_register(CAMEL_STORE_TYPE,
+ "CamelIMAPPStore",
+ sizeof (CamelIMAPPStore),
+ sizeof (CamelIMAPPStoreClass),
+ (CamelObjectClassInitFunc) camel_imapp_store_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_imapp_store_init,
+ finalize);
+ }
+
+ return camel_imapp_store_type;
+}
+
+static void
+finalize (CamelObject *object)
+{
+ CamelIMAPPStore *imap_store = CAMEL_IMAPP_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);
+
+ if (imap_store->driver)
+ camel_object_unref(imap_store->driver);
+ if (imap_store->cache)
+ camel_object_unref(imap_store->cache);
+}
+
+static void imap_construct(CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex)
+{
+ char *root, *summary;
+ CamelIMAPPStore *store = (CamelIMAPPStore *)service;
+
+ CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
+ if (camel_exception_is_set(ex))
+ return;
+
+ CAMEL_TRY {
+ store->summary = camel_imapp_store_summary_new();
+ root = camel_session_get_storage_path(service->session, service, ex);
+ if (root) {
+ summary = g_build_filename(root, ".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;
+}
+
+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
+connect_to_server (CamelService *service, int ssl_mode, int try_starttls)
+/* throws IO exception */
+{
+ CamelIMAPPStore *store = CAMEL_IMAPP_STORE (service);
+ CamelStream * volatile tcp_stream = NULL;
+ CamelIMAPPStream * volatile imap_stream = NULL;
+ struct hostent *h = NULL;
+ int ret, port;
+ CamelException *ex;
+
+ ex = camel_exception_new();
+ CAMEL_TRY {
+ /* parent class connect initialization */
+ CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex);
+ if (ex->id)
+ camel_exception_throw_ex(ex);
+
+ h = camel_service_gethost(service, ex);
+ if (ex->id)
+ camel_exception_throw_ex(ex);
+
+ port = service->url->port ? service->url->port : IMAP_PORT;
+
+#ifdef HAVE_SSL
+ if (camel_url_get_param (service->url, "use_ssl")) {
+ if (try_starttls)
+ tcp_stream = camel_tcp_stream_ssl_new_raw (service, service->url->host, STARTTLS_FLAGS);
+ else {
+ port = service->url->port ? service->url->port : 995;
+ tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host, SSL_PORT_FLAGS);
+ }
+ } else {
+ tcp_stream = camel_tcp_stream_raw_new ();
+ }
+#else
+ tcp_stream = camel_tcp_stream_raw_new ();
+#endif /* HAVE_SSL */
+
+ ret = camel_tcp_stream_connect (CAMEL_TCP_STREAM (tcp_stream), h, port);
+ camel_free_host (h);
+ 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 %d): %s"),
+ service->url->host, port, strerror(errno));
+ }
+
+ imap_stream = (CamelIMAPPStream *)camel_imapp_stream_new(tcp_stream);
+ store->driver = camel_imapp_driver_new(imap_stream);
+
+ camel_object_unref(imap_stream);
+ camel_object_unref(tcp_stream);
+ } CAMEL_CATCH(e) {
+ if (tcp_stream)
+ camel_object_unref(tcp_stream);
+ if (imap_stream)
+ camel_object_unref((CamelObject *)imap_stream);
+ camel_exception_throw_ex(e);
+ } CAMEL_DONE;
+
+ camel_exception_free(ex);
+}
+
+#if 0
+
+/* leave this stuff out for now */
+
+
+static struct {
+ char *value;
+ int mode;
+} ssl_options[] = {
+ { "", USE_SSL_ALWAYS },
+ { "always", USE_SSL_ALWAYS },
+ { "when-possible", USE_SSL_WHEN_POSSIBLE },
+ { "never", USE_SSL_NEVER },
+ { NULL, USE_SSL_NEVER },
+};
+
+static gboolean
+connect_to_server_wrapper (CamelService *service, CamelException *ex)
+{
+#ifdef HAVE_SSL
+ const char *use_ssl;
+ int i, ssl_mode;
+
+ use_ssl = camel_url_get_param (service->url, "use_ssl");
+ if (use_ssl) {
+ for (i = 0; ssl_options[i].value; i++)
+ if (!strcmp (ssl_options[i].value, use_ssl))
+ break;
+ ssl_mode = ssl_options[i].mode;
+ } else
+ ssl_mode = USE_SSL_NEVER;
+
+ if (ssl_mode == USE_SSL_ALWAYS) {
+ /* First try the ssl port */
+ if (!connect_to_server (service, ssl_mode, FALSE, ex)) {
+ if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) {
+ /* The ssl port seems to be unavailable, lets try STARTTLS */
+ camel_exception_clear (ex);
+ return connect_to_server (service, ssl_mode, TRUE, ex);
+ } else {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ } else if (ssl_mode == USE_SSL_WHEN_POSSIBLE) {
+ /* If the server supports STARTTLS, use it */
+ return connect_to_server (service, ssl_mode, TRUE, ex);
+ } else {
+ /* User doesn't care about SSL */
+ return connect_to_server (service, ssl_mode, FALSE, ex);
+ }
+#else
+ return connect_to_server (service, USE_SSL_NEVER, FALSE, ex);
+#endif
+}
+#endif
+
+extern CamelServiceAuthType camel_imapp_password_authtype;
+extern CamelServiceAuthType camel_imapp_apop_authtype;
+
+static GList *
+imap_query_auth_types (CamelService *service, CamelException *ex)
+{
+ /*CamelIMAPPStore *store = CAMEL_IMAPP_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 int
+try_sasl(CamelIMAPPStore *store, const char *mech, CamelException *ex)
+{
+ CamelIMAPPStream *stream = store->engine->stream;
+ unsigned char *line, *resp;
+ CamelSasl *sasl;
+ unsigned int len;
+ int ret;
+
+ sasl = camel_sasl_new("imap", mech, (CamelService *)store);
+ if (sasl == NULL) {
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
+ _("Unable to connect to POP server.\n"
+ "No support for requested "
+ "authentication mechanism."));
+ return -1;
+ }
+
+ if (camel_stream_printf((CamelStream *)stream, "AUTH %s\r\n", mech) == -1)
+ goto ioerror;
+
+ while (1) {
+ if (camel_imapp_stream_line(stream, &line, &len) == -1)
+ goto ioerror;
+ if (strncmp(line, "+OK", 3) == 0)
+ break;
+ if (strncmp(line, "-ERR", 4) == 0) {
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
+ _("SASL `%s' Login failed: %s"), mech, line);
+ goto done;
+ }
+ /* If we dont get continuation, or the sasl object's run out of work, or we dont get a challenge,
+ its a protocol error, so fail, and try reset the server */
+ if (strncmp(line, "+ ", 2) != 0
+ || camel_sasl_authenticated(sasl)
+ || (resp = camel_sasl_challenge_base64(sasl, line+2, ex)) == NULL) {
+ camel_stream_printf((CamelStream *)stream, "*\r\n");
+ camel_imapp_stream_line(stream, &line, &len);
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
+ _("SASL Protocol error"));
+ goto done;
+ }
+
+ ret = camel_stream_printf((CamelStream *)stream, "%s\r\n", resp);
+ g_free(resp);
+ if (ret == -1)
+ goto ioerror;
+
+ }
+ camel_object_unref((CamelObject *)sasl);
+ return 0;
+
+ioerror:
+ camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
+ _("I/O Error: %s"), strerror(errno));
+done:
+ camel_object_unref((CamelObject *)sasl);
+ return -1;
+}
+
+static gboolean
+imap_try_authenticate (CamelService *service, const char *errmsg, CamelException *ex)
+{
+ CamelIMAPPStore *store = (CamelIMAPPStore *)service;
+ CamelIMAPPCommand *pcu = NULL, *pcp = NULL;
+ int status;
+
+ /* override, testing only */
+ /*printf("Forcing authmech to 'login'\n");
+ service->url->authmech = g_strdup("LOGIN");*/
+
+ if (!service->url->passwd) {
+ char *prompt;
+
+ prompt = g_strdup_printf (_("%sPlease enter the POP password for %s@%s"),
+ errmsg ? errmsg : "",
+ service->url->user,
+ service->url->host);
+ service->url->passwd = camel_session_get_password (camel_service_get_session (service),
+ prompt, TRUE, service, "password", ex);
+ g_free (prompt);
+ if (!service->url->passwd)
+ return FALSE;
+ }
+
+ if (!service->url->authmech) {
+ /* pop engine will take care of pipelining ability */
+ pcu = camel_imapp_engine_command_new(store->engine, 0, NULL, NULL, "USER %s\r\n", service->url->user);
+ pcp = camel_imapp_engine_command_new(store->engine, 0, NULL, NULL, "PASS %s\r\n", service->url->passwd);
+ } else if (strcmp(service->url->authmech, "+APOP") == 0 && store->engine->apop) {
+ char *secret, md5asc[33], *d;
+ unsigned char md5sum[16], *s;
+
+ secret = alloca(strlen(store->engine->apop)+strlen(service->url->passwd)+1);
+ sprintf(secret, "%s%s", store->engine->apop, service->url->passwd);
+ md5_get_digest(secret, strlen (secret), md5sum);
+
+ for (s = md5sum, d = md5asc; d < md5asc + 32; s++, d += 2)
+ sprintf (d, "%.2x", *s);
+
+ pcp = camel_imapp_engine_command_new(store->engine, 0, NULL, NULL, "APOP %s %s\r\n", service->url->user, md5asc);
+ } else {
+ CamelServiceAuthType *auth;
+ GList *l;
+
+ l = store->engine->auth;
+ while (l) {
+ auth = l->data;
+ if (strcmp(auth->authproto, service->url->authmech) == 0)
+ return try_sasl(store, service->url->authmech, ex) == -1;
+ l = l->next;
+ }
+
+ camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
+ _("Unable to connect to POP server.\n"
+ "No support for requested "
+ "authentication mechanism."));
+ return FALSE;
+ }
+
+ while ((status = camel_imapp_engine_iterate (store->engine, pcp)) > 0)
+ ;
+
+ if (pcp->state != CAMEL_IMAPP_COMMAND_OK) {
+ if (status == -1)
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Unable to connect to POP server.\nError sending password: %s"),
+ errno ? g_strerror (errno) : _("Unknown error"));
+ else
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
+ _("Unable to connect to POP server.\nError sending password: %s"),
+ store->engine->line);
+ }
+ camel_imapp_engine_command_free(store->engine, pcp);
+
+ if (pcu)
+ camel_imapp_engine_command_free(store->engine, pcu);
+
+ return status;
+}
+#endif
+
+static gboolean
+imap_login(CamelService *service, char *msg)
+{
+ CamelIMAPPStore *store = (CamelIMAPPStore *)service;
+ CamelIMAPPCommand * volatile ic = NULL;
+
+ /* override, testing only */
+ /*printf("Forcing authmech to 'login'\n");
+ service->url->authmech = g_strdup("LOGIN");*/
+
+ CAMEL_TRY {
+ if (!service->url->passwd) {
+ char *prompt;
+ CamelException *ex = camel_exception_new();
+
+ prompt = g_strdup_printf (_("%sPlease enter the IMAP password for %s@%s"),
+ msg?msg:"",
+ service->url->user,
+ service->url->host);
+ service->url->passwd = camel_session_get_password(camel_service_get_session(service), prompt, FALSE, TRUE, service, "password", ex);
+ g_free (prompt);
+ if (camel_exception_is_set(ex))
+ camel_exception_throw_ex(ex);
+ }
+
+ if (service->url->authmech) {
+ CamelSasl *sasl = camel_sasl_new("imap", service->url->authmech, service);
+
+ if (sasl == NULL)
+ camel_exception_throw(1, "unsupported authentication mechanism: %s", service->url->authmech);
+ ic = camel_imapp_engine_command_new(store->driver->engine, "AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl);
+ camel_object_unref((CamelObject *)sasl);
+ } else {
+ ic = camel_imapp_engine_command_new(store->driver->engine, "LOGIN", NULL, "LOGIN %s %s", service->url->user, service->url->passwd);
+ }
+
+ camel_imapp_engine_command_queue(store->driver->engine, ic);
+ while (camel_imapp_engine_iterate(store->driver->engine, ic) > 0)
+ ;
+
+ if (ic->status->result != IMAP_OK)
+ camel_exception_throw(CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, "Login failed: %s", ic->status->text);
+ } CAMEL_CATCH(ex) {
+ if (ic)
+ camel_imapp_engine_command_free(store->driver->engine, ic);
+ camel_exception_throw_ex(ex);
+ } CAMEL_DONE;
+
+ if (ic)
+ camel_imapp_engine_command_free(store->driver->engine, ic);
+
+ return TRUE;
+}
+
+static void
+store_get_pass(CamelIMAPPStore *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),
+ prompt, FALSE, TRUE, (CamelService*)store, "password", &ex);
+ g_free (prompt);
+ if (camel_exception_is_set(&ex))
+ camel_exception_throw_ex(&ex);
+ }
+}
+
+static struct _CamelSasl *
+store_get_sasl(struct _CamelIMAPPDriver *driver, CamelIMAPPStore *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 _CamelIMAPPDriver *driver, char **login, char **pass, CamelIMAPPStore *store)
+{
+ store_get_pass(store);
+
+ *login = g_strdup(((CamelService *)store)->url->user);
+ *pass = g_strdup(((CamelService *)store)->url->passwd);
+}
+
+static gboolean
+imap_connect (CamelService *service, CamelException *ex)
+{
+ CamelIMAPPStore *store = (CamelIMAPPStore *)service;
+ volatile int ret = FALSE;
+
+ CAMEL_TRY {
+ volatile int retry = TRUE;
+
+ if (store->cache == NULL) {
+ char *root;
+
+ root = camel_session_get_storage_path(service->session, service, ex);
+ if (root) {
+ store->cache = camel_data_cache_new(root, 0, ex);
+ g_free(root);
+ if (store->cache) {
+ /* Default cache expiry - 1 week or not visited in a day */
+ camel_data_cache_set_expire_age(store->cache, 60*60*24*7);
+ camel_data_cache_set_expire_access(store->cache, 60*60*24);
+ }
+ }
+ if (camel_exception_is_set(ex))
+ camel_exception_throw_ex(ex);
+ }
+
+ connect_to_server(service, USE_SSL_NEVER, FALSE);
+
+ camel_imapp_driver_set_sasl_factory(store->driver, (CamelIMAPPSASLFunc)store_get_sasl, store);
+ camel_imapp_driver_set_login_query(store->driver, (CamelIMAPPLoginFunc)store_get_login, store);
+ store->login_error = NULL;
+
+ do {
+ CAMEL_TRY {
+ if (store->driver->engine->state != IMAP_ENGINE_AUTH)
+ camel_imapp_driver_login(store->driver);
+ ret = TRUE;
+ retry = FALSE;
+ } CAMEL_CATCH(e) {
+ g_free(store->login_error);
+ store->login_error = NULL;
+ switch (e->id) {
+ case CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE:
+ store->login_error = g_strdup_printf("%s\n\n", e->desc);
+ camel_session_forget_password(service->session, service, "password", ex);
+ camel_url_set_passwd(service->url, NULL);
+ break;
+ default:
+ camel_exception_throw_ex(e);
+ break;
+ }
+ } CAMEL_DONE;
+ } while (retry);
+ } CAMEL_CATCH(e) {
+ camel_exception_xfer(ex, e);
+ camel_service_disconnect(service, TRUE, NULL);
+ ret = FALSE;
+ } CAMEL_DONE;
+
+ g_free(store->login_error);
+ store->login_error = NULL;
+
+ return ret;
+}
+
+static gboolean
+imap_disconnect (CamelService *service, gboolean clean, CamelException *ex)
+{
+ CamelIMAPPStore *store = CAMEL_IMAPP_STORE (service);
+
+ /* FIXME: logout */
+
+ if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex))
+ return FALSE;
+
+ /* logout/disconnect */
+ if (store->driver) {
+ camel_object_unref(store->driver);
+ store->driver = NULL;
+ }
+
+ return TRUE;
+}
+
+static void
+imap_init_trash (CamelStore *store)
+{
+ /* no-op */
+ ;
+}
+
+static CamelFolder *
+imap_get_trash (CamelStore *store, CamelException *ex)
+{
+ /* no-op */
+ return NULL;
+}
+
+static CamelFolder *
+imap_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
+{
+ CamelIMAPPStore *istore = (CamelIMAPPStore *)store;
+ CamelIMAPPFolder * volatile folder = NULL;
+
+ /* ??? */
+
+ /* 1. create the folder */
+ /* 2. run select? */
+ /* 3. update the folder */
+
+ CAMEL_TRY {
+ folder = (CamelIMAPPFolder *)camel_imapp_folder_new(store, folder_name);
+ camel_imapp_driver_select(istore->driver, folder);
+ } CAMEL_CATCH (e) {
+ if (folder) {
+ camel_object_unref(folder);
+ folder = NULL;
+ }
+ camel_exception_xfer(ex, e);
+ } CAMEL_DONE;
+
+ return (CamelFolder *)folder;
+}
+
+static CamelFolder *
+imap_get_inbox(CamelStore *store, CamelException *ex)
+{
+ camel_exception_setv(ex, 1, "get_inbox::unimplemented");
+
+ return NULL;
+}
+
+/* 8 bit, string compare */
+static int folders_build_cmp(const void *app, const void *bpp)
+{
+ struct _list_info *a = *((struct _list_info **)app);
+ struct _list_info *b = *((struct _list_info **)bpp);
+ unsigned char *ap = (unsigned char *)(a->name);
+ unsigned char *bp = (unsigned char *)(b->name);
+
+ printf("qsort, cmp '%s' <> '%s'\n", ap, bp);
+
+ while (*ap && *ap == *bp) {
+ ap++;
+ bp++;
+ }
+
+ if (*ap < *bp)
+ return -1;
+ else if (*ap > *bp)
+ return 1;
+ return 0;
+}
+
+/* FIXME: this should go via storesummary? */
+static CamelFolderInfo *
+folders_build_info(CamelURL *base, struct _list_info *li)
+{
+ char *path, *full_name, *name;
+ CamelFolderInfo *fi;
+
+ full_name = imapp_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->url = camel_url_to_string(base, CAMEL_URL_HIDE_ALL);
+ fi->name = g_strdup(name);
+ fi->full_name = full_name;
+ fi->path = g_strdup(path);
+ fi->unread_message_count = -1;
+ fi->flags = li->flags;
+
+ /* 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->sibling = 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->url);
+ if (fi->child)
+ folder_info_dump(fi->child, depth+2);
+ fi = fi->sibling;
+ }
+
+}
+
+static CamelFolderInfo *
+imap_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex)
+{
+ CamelIMAPPStore *istore = (CamelIMAPPStore *)store;
+ CamelFolderInfo * fi= NULL;
+ char *name;
+
+ if (istore->driver == NULL) {
+ camel_exception_setv(ex, 1, "Not connected");
+ return NULL;
+ }
+
+ name = (char *)top;
+ if (name == NULL || name[0] == 0) {
+ /* namespace? */
+ name = "";
+ }
+
+ name = "";
+
+ CAMEL_TRY {
+ CamelURL *base;
+ int i;
+ GPtrArray *folders;
+
+ /* FIXME: subscriptions? lsub? */
+ folders = camel_imapp_driver_list(istore->driver, name, flags);
+
+ /* this greatly simplifies the tree algorithm ... but it might
+ be faster just to use a hashtable to find parents? */
+ qsort(folders->pdata, folders->len, sizeof(folders->pdata[0]), folders_build_cmp);
+
+ 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);
+ } CAMEL_CATCH(e) {
+ camel_exception_xfer(ex, e);
+ } CAMEL_DONE;
+
+ printf("built folder info:\n");
+ folder_info_dump(fi, 2);
+
+ return fi;
+
+#if 0
+ if (top == NULL || !g_ascii_strcasecmp(top, "inbox")) {
+ CamelURL *uri = camel_url_copy(((CamelService *)store)->url);
+
+ camel_url_set_path(uri, "/INBOX");
+ fi = g_malloc0(sizeof(*fi));
+ fi->url = camel_url_to_string(uri, CAMEL_URL_HIDE_ALL);
+ camel_url_free(uri);
+ fi->name = g_strdup("INBOX");
+ fi->full_name = g_strdup("INBOX");
+ fi->path = g_strdup("/INBOX");
+ fi->unread_message_count = -1;
+ fi->flags = 0;
+
+ folder = camel_object_bag_get(store->folders, "INBOX");
+ if (folder) {
+ /*if (!cflags & FAST)*/
+ camel_imapp_driver_update(istore->driver, (CamelIMAPPFolder *)folder);
+ fi->unread_message_count = camel_folder_get_unread_message_count(folder);
+ camel_object_unref(folder);
+ }
+ } else {
+ camel_exception_setv(ex, 1, "not implemented");
+ }
+#endif
+ return fi;
+
+#if 0
+ istore->pending_list = g_ptr_array_new();
+
+ CAMEL_TRY {
+ ic = camel_imapp_engine_command_new(istore->driver->engine, "LIST", NULL, "LIST \"\" %f", top);
+ camel_imapp_engine_command_queue(istore->driver->engine, ic);
+ while (camel_imapp_engine_iterate(istore->driver->engine, ic) > 0)
+ ;
+
+ if (ic->status->result != IMAP_OK)
+ camel_exception_throw(1, "list failed: %s", ic->status->text);
+ } CAMEL_CATCH (e) {
+ camel_exception_xfer(ex, e);
+ } CAMEL_DONE;
+
+ camel_imapp_engine_command_free(istore->driver->engine, ic);
+
+ printf("got folder list:\n");
+ for (i=0;i<(int)istore->pending_list->len;i++) {
+ struct _list_info *linfo = istore->pending_list->pdata[i];
+
+ printf("%s (%c)\n", linfo->name, linfo->separator);
+ imap_free_list(linfo);
+ }
+ istore->pending_list = NULL;
+
+ return NULL;
+#endif
+}
+
+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;
+}
+
+/* ********************************************************************** */
+
+static int store_resp_list(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ struct _list_info *linfo;
+ CamelIMAPPStore *istore = data;
+
+ linfo = imap_parse_list(ie->stream);
+ printf("store list: '%s' ('%c')\n", linfo->name, linfo->separator);
+ if (istore->pending_list)
+ g_ptr_array_add(istore->pending_list, linfo);
+ else {
+ g_warning("unexpected list response\n");
+ imap_free_list(linfo);
+ }
+
+ return camel_imapp_engine_skip(ie);
+}
+
+#if 0
+static int store_resp_fetch(CamelIMAPPEngine *ie, guint32 id, void *data)
+{
+ struct _fetch_info *finfo;
+ CamelIMAPPStore *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_folder_summary_info_free(((CamelFolder *)istore->selected)->summary, 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_folder_summary_info_free(((CamelFolder *)istore->selected)->summary, 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_folder_summary_info_free(((CamelFolder *)istore->selected)->summary, 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_folder_summary_info_free(((CamelFolder *)istore->selected)->summary, 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_imapp_engine_skip(ie);
+}
+#endif
+
+/* ********************************************************************** */
+
+/* should be moved to imapp-utils?
+ stuff in imapp-utils should be moved to imapp-parse? */
+
+/* ********************************************************************** */
+
+#if 0
+void
+camel_imapp_store_folder_selected(CamelIMAPPStore *store, CamelIMAPPFolder *folder, CamelIMAPPSelectResponse *select)
+{
+ CamelIMAPPCommand * volatile ic = NULL;
+ CamelIMAPPStore *istore = (CamelIMAPPStore *)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_imapp_engine_command_new(istore->engine, "FETCH", NULL, "FETCH 1:%d (UID FLAGS)", select->exists);
+ camel_imapp_engine_command_queue(istore->engine, ic);
+ while (camel_imapp_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_imapp_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_imapp_engine_command_add(istore->engine, ic, " (FLAGS RFC822.HEADER)");
+ camel_imapp_engine_command_queue(istore->engine, ic);
+ while (camel_imapp_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_imapp_engine_command_free(istore->engine, ic);
+ ic = camel_imapp_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_folder_summary_info_free(((CamelFolder *)istore->selected)->summary, 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_imapp_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(CamelIMAPPEngine *ie)
+{
+ struct _uidset_state ss;
+ CamelIMAPPCommand *ic;
+ int i;
+
+ /*ic = camel_imapp_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
diff --git a/camel/providers/imapp/camel-imapp-store.h b/camel/providers/imapp/camel-imapp-store.h
new file mode 100644
index 0000000000..97b7afd1d1
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-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_IMAPP_STORE_H
+#define CAMEL_IMAPP_STORE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <camel/camel-types.h>
+#include <camel/camel-store.h>
+#include "camel-imapp-driver.h"
+#include "e-util/e-memory.h"
+
+#define CAMEL_IMAPP_STORE_TYPE (camel_imapp_store_get_type ())
+#define CAMEL_IMAPP_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_IMAPP_STORE_TYPE, CamelIMAPPStore))
+#define CAMEL_IMAPP_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_IMAPP_STORE_TYPE, CamelIMAPPStoreClass))
+#define CAMEL_IS_IMAP_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_IMAPP_STORE_TYPE))
+
+struct _pending_fetch {
+ struct _pending_fetch *next;
+ struct _pending_fetch *prev;
+
+ CamelMessageInfo *info;
+};
+
+typedef struct {
+ CamelStore parent_object;
+
+ struct _CamelIMAPPStoreSummary *summary; /* in-memory list of folders */
+ struct _CamelIMAPPDriver *driver; /* IMAP processing engine */
+ struct _CamelDataCache *cache;
+
+ /* if we had a login error, what to show to user */
+ char *login_error;
+
+ GPtrArray *pending_list;
+} CamelIMAPPStore;
+
+typedef struct {
+ CamelStoreClass parent_class;
+
+} CamelIMAPPStoreClass;
+
+/* Standard Camel function */
+CamelType camel_imapp_store_get_type (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_IMAPP_STORE_H */
+
+
diff --git a/camel/providers/imapp/camel-imapp-stream.c b/camel/providers/imapp/camel-imapp-stream.c
new file mode 100644
index 0000000000..a9567bf4f8
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-stream.c
@@ -0,0 +1,761 @@
+/* -*- 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-imapp-stream.h"
+#include "camel-imapp-exception.h"
+
+#define t(x)
+#define io(x) x
+
+static void setup_table(void);
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_IMAPP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+#define CAMEL_IMAPP_STREAM_SIZE (4096)
+#define CAMEL_IMAPP_STREAM_TOKEN (4096) /* maximum token size */
+
+static int
+stream_fill(CamelIMAPPStream *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_IMAPP_STREAM_SIZE - (is->end - is->buf));
+ if (left > 0) {
+ is->end += left;
+ io(printf("camel_imapp_read: buffer is '%.*s'\n", is->end - is->ptr, is->ptr));
+ return is->end - is->ptr;
+ } else {
+ io(printf("camel_imapp_read: -1\n"));
+ return -1;
+ }
+ }
+
+ printf("camel_imapp_read: 0\n");
+
+ return 0;
+}
+
+static ssize_t
+stream_read(CamelStream *stream, char *buffer, size_t n)
+{
+ CamelIMAPPStream *is = (CamelIMAPPStream *)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;
+ }
+
+ is->literal -= max;
+
+ return max;
+}
+
+static ssize_t
+stream_write(CamelStream *stream, const char *buffer, size_t n)
+{
+ CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
+
+ 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)
+{
+ CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
+
+ return is->literal == 0;
+}
+
+static int
+stream_reset(CamelStream *stream)
+{
+ /* nop? reset literal mode? */
+ return 0;
+}
+
+static void
+camel_imapp_stream_class_init (CamelStreamClass *camel_imapp_stream_class)
+{
+ CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_imapp_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_imapp_stream_init(CamelIMAPPStream *is, CamelIMAPPStreamClass *isclass)
+{
+ /* +1 is room for appending a 0 if we need to for a token */
+ is->ptr = is->end = is->buf = g_malloc(CAMEL_IMAPP_STREAM_SIZE+1);
+ is->tokenptr = is->tokenbuf = g_malloc(CAMEL_IMAPP_STREAM_SIZE+1);
+ is->tokenend = is->tokenbuf + CAMEL_IMAPP_STREAM_SIZE;
+}
+
+static void
+camel_imapp_stream_finalise(CamelIMAPPStream *is)
+{
+ g_free(is->buf);
+ if (is->source)
+ camel_object_unref((CamelObject *)is->source);
+}
+
+CamelType
+camel_imapp_stream_get_type (void)
+{
+ static CamelType camel_imapp_stream_type = CAMEL_INVALID_TYPE;
+
+ if (camel_imapp_stream_type == CAMEL_INVALID_TYPE) {
+ setup_table();
+ camel_imapp_stream_type = camel_type_register( camel_stream_get_type(),
+ "CamelIMAPPStream",
+ sizeof( CamelIMAPPStream ),
+ sizeof( CamelIMAPPStreamClass ),
+ (CamelObjectClassInitFunc) camel_imapp_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_imapp_stream_init,
+ (CamelObjectFinalizeFunc) camel_imapp_stream_finalise );
+ }
+
+ return camel_imapp_stream_type;
+}
+
+/**
+ * camel_imapp_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_imapp_stream_new(CamelStream *source)
+{
+ CamelIMAPPStream *is;
+
+ is = (CamelIMAPPStream *)camel_object_new(camel_imapp_stream_get_type ());
+ camel_object_ref((CamelObject *)source);
+ is->source = source;
+
+ return (CamelStream *)is;
+}
+
+
+/*
+ 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 ::= <"> / "\"
+*/
+
+static unsigned char imap_specials[256] = {
+/* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 20 */0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
+/* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 40 */1, 1, 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, 1, 0, 1, 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 imap_is_atom(c) ((imap_specials[(c)&0xff] & 0x01) != 0)
+#define imap_is_simple(c) ((imap_specials[(c)&0xff] & 0x02) != 0)
+#define imap_not_id(c) ((imap_specials[(c)&0xff] & 0x04) != 0)
+
+/* could be pregenerated, but this is cheap */
+static struct {
+ unsigned char *chars;
+ unsigned char mask;
+} is_masks[] = {
+ { "\n*()[]+", 2 },
+ { " \r\n()[]+", 4 },
+};
+
+static void setup_table(void)
+{
+ int i;
+ unsigned char *p, c;
+
+ for (i=0;i<(int)(sizeof(is_masks)/sizeof(is_masks[0]));i++) {
+ p = is_masks[i].chars;
+ while ((c = *p++))
+ imap_specials[c] |= is_masks[i].mask;
+ }
+}
+
+#if 0
+
+static int
+skip_ws(CamelIMAPPStream *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_imapp_stream_atom(CamelIMAPPStream *is, unsigned char **data, unsigned int *lenp)
+{
+ unsigned char *p, c;
+
+ /* this is only 'approximate' atom */
+ switch(camel_imapp_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_imapp_stream_astring(CamelIMAPPStream *is, unsigned char **data)
+{
+ unsigned char *p, *start;
+ unsigned int len, inlen;
+
+ switch(camel_imapp_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_IMAPP_STREAM_TOKEN) {
+ camel_exception_throw(1, "astring: literal too long");
+ printf("astring too long\n");
+ return IMAP_TOK_PROTOCOL;
+ }
+ p = is->tokenptr;
+ camel_imapp_stream_set_literal(is, len);
+ do {
+ len = camel_imapp_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_imapp_stream_nstring(CamelIMAPPStream *is, unsigned char **data)
+{
+ unsigned char *p, *start;
+ unsigned int len, inlen;
+
+ switch(camel_imapp_stream_token(is, data, &len)) {
+ case IMAP_TOK_STRING:
+ return 0;
+ case IMAP_TOK_LITERAL:
+ /* FIXME: just grow buffer */
+ if (len >= CAMEL_IMAPP_STREAM_TOKEN) {
+ camel_exception_throw(1, "nstring: literal too long");
+ return IMAP_TOK_PROTOCOL;
+ }
+ p = is->tokenptr;
+ camel_imapp_stream_set_literal(is, len);
+ do {
+ len = camel_imapp_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_imapp_stream_nstring_stream(CamelIMAPPStream *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_imapp_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_imapp_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_imapp_stream_number(CamelIMAPPStream *is)
+{
+ unsigned char *token;
+ unsigned int len;
+
+ if (camel_imapp_stream_token(is, &token, &len) != IMAP_TOK_INT) {
+ camel_exception_throw(1, "expecting number");
+ return 0;
+ }
+
+ return strtoul(token, 0, 10);
+}
+
+int
+camel_imapp_stream_text(CamelIMAPPStream *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_imapp_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_imapp_token_t
+/* throws IO,PARSE exception */
+camel_imapp_stream_token(CamelIMAPPStream *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 (imap_is_simple(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_IMAPP_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_IMAPP_STREAM_TOKEN - 1;
+ digits = isdigit(c);
+ *o++ = c;
+ while (1) {
+ while (p < e) {
+ c = *p++;
+ /*if (strchr(" \r\n*()[]+", c) != NULL) {*/
+ if (imap_not_id(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_imapp_stream_ungettoken(CamelIMAPPStream *is, camel_imapp_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_imapp_stream_gets(CamelIMAPPStream *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_imapp_stream_set_literal(CamelIMAPPStream *is, unsigned int literal)
+{
+ is->literal = literal;
+}
+
+/* returns -1 on erorr, 0 if last data, >0 if more data left */
+int camel_imapp_stream_getl(CamelIMAPPStream *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;
+}
diff --git a/camel/providers/imapp/camel-imapp-stream.h b/camel/providers/imapp/camel-imapp-stream.h
new file mode 100644
index 0000000000..802c018672
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-stream.h
@@ -0,0 +1,90 @@
+/*
+ * 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_IMAPP_STREAM_H
+#define _CAMEL_IMAPP_STREAM_H
+
+#include <camel/camel-stream.h>
+
+#define CAMEL_IMAPP_STREAM(obj) CAMEL_CHECK_CAST (obj, camel_imapp_stream_get_type (), CamelIMAPPStream)
+#define CAMEL_IMAPP_STREAM_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapp_stream_get_type (), CamelIMAPPStreamClass)
+#define CAMEL_IS_IMAP_STREAM(obj) CAMEL_CHECK_TYPE (obj, camel_imapp_stream_get_type ())
+
+typedef struct _CamelIMAPPStreamClass CamelIMAPPStreamClass;
+typedef struct _CamelIMAPPStream CamelIMAPPStream;
+
+typedef enum {
+ IMAP_TOK_PROTOCOL = -2,
+ IMAP_TOK_ERROR = -1,
+ IMAP_TOK_TOKEN = 256,
+ IMAP_TOK_STRING,
+ IMAP_TOK_INT,
+ IMAP_TOK_LITERAL,
+} camel_imapp_token_t;
+
+struct _CamelIMAPPStream {
+ CamelStream parent;
+
+ CamelStream *source;
+
+ /*int state;*/
+ unsigned char *buf, *ptr, *end;
+ unsigned int literal;
+
+ unsigned int unget;
+ camel_imapp_token_t unget_tok;
+ unsigned char *unget_token;
+ unsigned int unget_len;
+
+ unsigned char *tokenbuf, *tokenptr, *tokenend;
+};
+
+struct _CamelIMAPPStreamClass {
+ CamelStreamClass parent_class;
+};
+
+CamelType camel_imapp_stream_get_type (void);
+
+CamelStream *camel_imapp_stream_new (CamelStream *source);
+
+camel_imapp_token_t camel_imapp_stream_token (CamelIMAPPStream *is, unsigned char **start, unsigned int *len); /* throws IO,PARSE exception */
+void camel_imapp_stream_ungettoken (CamelIMAPPStream *is, camel_imapp_token_t tok, unsigned char *token, unsigned int len);
+
+void camel_imapp_stream_set_literal (CamelIMAPPStream *is, unsigned int literal);
+int camel_imapp_stream_gets (CamelIMAPPStream *is, unsigned char **start, unsigned int *len);
+int camel_imapp_stream_getl (CamelIMAPPStream *is, unsigned char **start, unsigned int *len);
+
+/* all throw IO,PARSE exceptions */
+
+/* gets an atom, upper-cases */
+int camel_imapp_stream_atom (CamelIMAPPStream *is, unsigned char **start, unsigned int *len);
+/* gets an atom or string */
+int camel_imapp_stream_astring (CamelIMAPPStream *is, unsigned char **start);
+/* gets a NIL or a string, start==NULL if NIL */
+int camel_imapp_stream_nstring (CamelIMAPPStream *is, unsigned char **start);
+/* gets a NIL or string into a stream, stream==NULL if NIL */
+int camel_imapp_stream_nstring_stream(CamelIMAPPStream *is, CamelStream **stream);
+/* gets 'text' */
+int camel_imapp_stream_text (CamelIMAPPStream *is, unsigned char **text);
+
+/* gets a 'number' */
+guint32 camel_imapp_stream_number(CamelIMAPPStream *is);
+
+#endif /* ! _CAMEL_IMAPP_STREAM_H */
diff --git a/camel/providers/imapp/camel-imapp-summary.c b/camel/providers/imapp/camel-imapp-summary.c
new file mode 100644
index 0000000000..a0df21bcb0
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-summary.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright(C) 2000 Ximian Inc.
+ *
+ * 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 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/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "camel-imapp-summary.h"
+#include <camel/camel-file-utils.h>
+
+#define CAMEL_IMAPP_SUMMARY_VERSION (0x1000)
+
+static int summary_header_load(CamelFolderSummary *, FILE *);
+static int summary_header_save(CamelFolderSummary *, FILE *);
+
+static CamelMessageInfo *message_info_load(CamelFolderSummary *s, FILE *in);
+static int message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *info);
+
+static void camel_imapp_summary_class_init(CamelIMAPPSummaryClass *klass);
+static void camel_imapp_summary_init (CamelIMAPPSummary *obj);
+
+static CamelFolderSummaryClass *camel_imapp_summary_parent;
+
+CamelType
+camel_imapp_summary_get_type(void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register(
+ camel_folder_summary_get_type(), "CamelIMAPPSummary",
+ sizeof(CamelIMAPPSummary),
+ sizeof(CamelIMAPPSummaryClass),
+ (CamelObjectClassInitFunc) camel_imapp_summary_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_imapp_summary_init,
+ NULL);
+ }
+
+ return type;
+}
+
+static void
+camel_imapp_summary_class_init(CamelIMAPPSummaryClass *klass)
+{
+ CamelFolderSummaryClass *cfs_class =(CamelFolderSummaryClass *) klass;
+
+ camel_imapp_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_type_get_global_classfuncs(camel_folder_summary_get_type()));
+
+ 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;
+}
+
+static void
+camel_imapp_summary_init(CamelIMAPPSummary *obj)
+{
+ CamelFolderSummary *s =(CamelFolderSummary *)obj;
+
+ /* subclasses need to set the right instance data sizes */
+ s->message_info_size = sizeof(CamelIMAPPMessageInfo);
+ s->content_info_size = sizeof(CamelMessageContentInfo);
+
+ /* and a unique file version */
+ s->version += CAMEL_IMAPP_SUMMARY_VERSION;
+}
+
+/**
+ * camel_imapp_summary_new:
+ * @filename: the file to store the summary in.
+ *
+ * This will create a new CamelIMAPPSummary object and read in the
+ * summary data from disk, if it exists.
+ *
+ * Return value: A new CamelIMAPPSummary object.
+ **/
+CamelFolderSummary *
+camel_imapp_summary_new(void)
+{
+ CamelFolderSummary *summary = CAMEL_FOLDER_SUMMARY(camel_object_new(camel_imapp_summary_get_type()));
+
+ return summary;
+}
+
+
+static int
+summary_header_load(CamelFolderSummary *s, FILE *in)
+{
+ CamelIMAPPSummary *ims = CAMEL_IMAPP_SUMMARY(s);
+
+ if (camel_imapp_summary_parent->summary_header_load(s, in) == -1)
+ return -1;
+
+ return camel_file_util_decode_uint32(in, &ims->uidvalidity);
+}
+
+static int
+summary_header_save(CamelFolderSummary *s, FILE *out)
+{
+ CamelIMAPPSummary *ims = CAMEL_IMAPP_SUMMARY(s);
+
+ if (camel_imapp_summary_parent->summary_header_save(s, out) == -1)
+ return -1;
+
+ return camel_file_util_encode_uint32(out, ims->uidvalidity);
+}
+
+
+static CamelMessageInfo *
+message_info_load(CamelFolderSummary *s, FILE *in)
+{
+ CamelMessageInfo *info;
+ CamelIMAPPMessageInfo *iinfo;
+
+ info = camel_imapp_summary_parent->message_info_load(s, in);
+ if (info) {
+ iinfo =(CamelIMAPPMessageInfo *)info;
+
+ if (camel_file_util_decode_uint32(in, &iinfo->server_flags) == -1)
+ goto error;
+ }
+
+ return info;
+error:
+ camel_folder_summary_info_free(s, info);
+ return NULL;
+}
+
+static int
+message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *info)
+{
+ CamelIMAPPMessageInfo *iinfo =(CamelIMAPPMessageInfo *)info;
+
+ if (camel_imapp_summary_parent->message_info_save(s, out, info) == -1)
+ return -1;
+
+ return camel_file_util_encode_uint32(out, iinfo->server_flags);
+}
diff --git a/camel/providers/imapp/camel-imapp-summary.h b/camel/providers/imapp/camel-imapp-summary.h
new file mode 100644
index 0000000000..e9783625f4
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-summary.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2000 Ximian Inc.
+ *
+ * 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 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_IMAPP_SUMMARY_H
+#define _CAMEL_IMAPP_SUMMARY_H
+
+#include <camel/camel-folder-summary.h>
+#include <camel/camel-exception.h>
+
+#define CAMEL_IMAPP_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_imapp_summary_get_type (), CamelIMAPPSummary)
+#define CAMEL_IMAPP_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_imapp_summary_get_type (), CamelIMAPPSummaryClass)
+#define CAMEL_IS_IMAPP_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_imapp_summary_get_type ())
+
+#define CAMEL_IMAPP_SERVER_FLAGS (CAMEL_MESSAGE_ANSWERED | \
+ CAMEL_MESSAGE_DELETED | \
+ CAMEL_MESSAGE_DRAFT | \
+ CAMEL_MESSAGE_FLAGGED | \
+ CAMEL_MESSAGE_SEEN)
+
+#define CAMEL_IMAPP_MESSAGE_RECENT (1 << 8)
+
+typedef struct _CamelIMAPPSummaryClass CamelIMAPPSummaryClass;
+typedef struct _CamelIMAPPSummary CamelIMAPPSummary;
+
+typedef struct _CamelIMAPPMessageInfo {
+ CamelMessageInfo info;
+
+ guint32 server_flags;
+} CamelIMAPPMessageInfo;
+
+struct _CamelIMAPPSummary {
+ CamelFolderSummary parent;
+
+ guint32 uidvalidity;
+};
+
+struct _CamelIMAPPSummaryClass {
+ CamelFolderSummaryClass parent_class;
+
+};
+
+CamelType camel_imapp_summary_get_type (void);
+CamelFolderSummary *camel_imapp_summary_new (void);
+
+#endif /* ! _CAMEL_IMAPP_SUMMARY_H */
+
diff --git a/camel/providers/imapp/camel-imapp-utils.c b/camel/providers/imapp/camel-imapp-utils.c
new file mode 100644
index 0000000000..57935a81e8
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-utils.c
@@ -0,0 +1,1339 @@
+
+#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-imapp-folder.h"
+#include "camel-imapp-stream.h"
+#include "camel-imapp-utils.h"
+#include "camel-imapp-exception.h"
+#include "camel-imapp-engine.h"
+#include "e-util/e-memory.h"
+
+/* high-level parser state */
+#define p(x)
+/* debug */
+#define d(x)
+
+/* ANSI-C code produced by gperf version 2.7 */
+/* Command-line: gperf -H imap_hash -N imap_tokenise -L ANSI-C -o -t -k1,$ imap-tokens.txt */
+struct _imap_keyword { char *name; enum _imap_id id; };
+/*
+ gperf input file
+ best hash generated using: gperf -o -s-2 -k1,'$' -t -H imap_hash -N imap_tokenise -L ANSI-C
+*/
+
+#define TOTAL_KEYWORDS 23
+#define MIN_WORD_LENGTH 2
+#define MAX_WORD_LENGTH 14
+#define MIN_HASH_VALUE 2
+#define MAX_HASH_VALUE 38
+/* maximum key range = 37, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#endif
+static unsigned int
+imap_hash (register const char *str, register unsigned int len)
+{
+ static unsigned char asso_values[] =
+ {
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 10, 15, 39, 20, 0,
+ 0, 39, 0, 10, 39, 0, 39, 39, 10, 0,
+ 0, 39, 0, 10, 5, 10, 39, 39, 39, 0,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39
+ };
+ return len + asso_values[(unsigned char)str[len - 1]] + asso_values[(unsigned char)str[0]];
+}
+
+#ifdef __GNUC__
+__inline
+#endif
+enum _imap_id
+imap_tokenise (register const char *str, register unsigned int len)
+{
+ static struct _imap_keyword wordlist[] =
+ {
+ {""}, {""},
+ {"OK", IMAP_OK},
+ {""}, {""},
+ {"PARSE", IMAP_PARSE},
+ {""},
+ {"PREAUTH", IMAP_PREAUTH},
+ {"ENVELOPE", IMAP_ENVELOPE},
+ {"READ-ONLY", IMAP_READ_ONLY},
+ {"READ-WRITE", IMAP_READ_WRITE},
+ {"RFC822.SIZE", IMAP_RFC822_SIZE},
+ {"NO", IMAP_NO},
+ {"RFC822.HEADER", IMAP_RFC822_HEADER},
+ {"TRYCREATE", IMAP_TRYCREATE},
+ {"FLAGS", IMAP_FLAGS},
+ {"RFC822.TEXT", IMAP_RFC822_TEXT},
+ {"NEWNAME", IMAP_NEWNAME},
+ {"BYE", IMAP_BYE},
+ {"BODY", IMAP_BODY},
+ {"ALERT", IMAP_ALERT},
+ {"UIDVALIDITY", IMAP_UIDVALIDITY},
+ {"INTERNALDATE", IMAP_INTERNALDATE},
+ {""},
+ {"PERMANENTFLAGS", IMAP_PERMANENTFLAGS},
+ {""},
+ {"UNSEEN", IMAP_UNSEEN},
+ {""},
+ {"BODYSTRUCTURE", IMAP_BODYSTRUCTURE},
+ {""}, {""}, {""}, {""},
+ {"UID", IMAP_UID},
+ {""}, {""}, {""}, {""},
+ {"BAD", IMAP_BAD}
+ };
+
+ 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].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_IMAPP_MESSAGE_RECENT },
+ { "\\*", CAMEL_MESSAGE_USER },
+};
+
+/* utility functions
+ shoudl this be part of imapp-driver? */
+/* mabye this should be a stream op? */
+void
+imap_parse_flags(CamelIMAPPStream *stream, guint32 *flagsp)
+/* throws IO,PARSE exception */
+{
+ int tok, len, i;
+ unsigned char *token, *p, c;
+ guint32 flags = 0;
+
+ *flagsp = flags;
+
+ tok = camel_imapp_stream_token(stream, &token, &len);
+ if (tok == '(') {
+ do {
+ tok = camel_imapp_stream_token(stream, &token, &len);
+ if (tok == IMAP_TOK_TOKEN) {
+ p = token;
+ 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;
+ } 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)
+/* 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)
+ if (camel_stream_write(stream, " ", 1) == -1)
+ camel_exception_throw(1, "io error: %s", strerror(errno));
+ }
+ }
+
+ if (camel_stream_write(stream, ")", 1) == -1)
+ camel_exception_throw(1, "io error: %s", strerror(errno));
+}
+
+/*
+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 {
+ struct _header_content_type *ct;
+ char *msgid, *desc;
+ CamelMimePartEncodingType 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)
+ header_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(CamelIMAPPStream *is, struct _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_imapp_stream_token(is, &token, &len);
+ if (tok == '(') {
+ while (1) {
+ tok = camel_imapp_stream_token(is, &token, &len);
+ if (tok == ')')
+ break;
+ camel_imapp_stream_ungettoken(is, tok, token, len);
+
+ camel_imapp_stream_astring(is, &token);
+ param = alloca(strlen(token)+1);
+ strcpy(param, token);
+ camel_imapp_stream_astring(is, &token);
+ header_set_param(plist, param, token);
+ }
+ } /* else check nil? no need */
+}
+
+struct _CamelMimeDisposition *
+imap_parse_ext_optional(CamelIMAPPStream *is)
+{
+ int tok, len;
+ unsigned char *token;
+ struct _CamelMimeDisposition * 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_imapp_stream_token(is, &token, &len);
+ switch (tok) {
+ case '(':
+ dinfo = g_malloc0(sizeof(*dinfo));
+ dinfo->refcount = 1;
+ /* should be string */
+ camel_imapp_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_imapp_stream_token(is, &token, &len);
+ switch (tok) {
+ case '(':
+ while (1) {
+ tok = camel_imapp_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_imapp_stream_set_literal(is, len);
+ while ((tok = camel_imapp_stream_getl(is, &token, &len)) > 0) {
+ d(printf("Skip literal data '%.*s'\n", (int)len, token));
+ }
+ break;
+
+ }
+ } CAMEL_CATCH(ex) {
+ if (dinfo)
+ header_disposition_unref(dinfo);
+ camel_exception_throw_ex(ex);
+ } CAMEL_DONE;
+
+ return dinfo;
+}
+
+struct _CamelMessageContentInfo *
+imap_parse_body_fields(CamelIMAPPStream *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_imapp_stream_astring(is, &token);
+ type = alloca(strlen(token)+1);
+ strcpy(type, token);
+ camel_imapp_stream_astring(is, &token);
+ cinfo->type = header_content_type_new(type, token);
+ imap_parse_param_list(is, &cinfo->type->params);
+
+ /* body_fld_id ::= nstring */
+ camel_imapp_stream_nstring(is, &token);
+ cinfo->id = g_strdup(token);
+
+ /* body_fld_desc ::= nstring */
+ camel_imapp_stream_nstring(is, &token);
+ cinfo->description = g_strdup(token);
+
+ /* body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
+ "QUOTED-PRINTABLE") <">) / string */
+ camel_imapp_stream_astring(is, &token);
+ cinfo->encoding = g_strdup(token);
+
+ /* body_fld_octets ::= number */
+ cinfo->size = camel_imapp_stream_number(is);
+ } CAMEL_CATCH(ex) {
+ imap_free_body(cinfo);
+ camel_exception_throw_ex(ex);
+ } CAMEL_DONE;
+
+ return cinfo;
+}
+
+struct _header_address *
+imap_parse_address_list(CamelIMAPPStream *is)
+/* throws PARSE,IO exception */
+{
+ int tok, len;
+ unsigned char *token, *host, *mbox;
+ struct _header_address *list = NULL;
+
+ /* "(" 1*address ")" / nil */
+
+ CAMEL_TRY {
+ tok = camel_imapp_stream_token(is, &token, &len);
+ if (tok == '(') {
+ while (1) {
+ struct _header_address *addr, *group = NULL;
+
+ /* address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
+ SPACE addr_host ")" */
+ tok = camel_imapp_stream_token(is, &token, &len);
+ if (tok == ')')
+ break;
+ if (tok != '(')
+ camel_exception_throw(1, "missing '(' for address");
+
+ addr = header_address_new();
+ addr->type = HEADER_ADDRESS_NAME;
+ tok = camel_imapp_stream_nstring(is, &token);
+ addr->name = g_strdup(token);
+ /* we ignore the route, nobody uses it in the real world */
+ tok = camel_imapp_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_imapp_stream_nstring(is, &mbox);
+ mbox = g_strdup(mbox);
+ tok = camel_imapp_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 = HEADER_ADDRESS_GROUP;
+ 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)
+ header_address_add_member(group, addr);
+ else
+ header_address_list_append(&list, addr);
+ }
+ do {
+ tok = camel_imapp_stream_token(is, &token, &len);
+ } while (tok != ')');
+ }
+ } else {
+ d(printf("empty, nil '%s'\n", token));
+ }
+ } CAMEL_CATCH(ex) {
+ header_address_list_clear(&list);
+ camel_exception_throw_ex(ex);
+ } CAMEL_DONE;
+
+ return list;
+}
+
+struct _CamelMessageInfo *
+imap_parse_envelope(CamelIMAPPStream *is)
+{
+ int tok, len;
+ unsigned char *token;
+ struct _header_address *addr, *addr_from;
+ char *addrstr;
+ struct _CamelMessageInfo *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 = camel_message_info_new();
+
+ CAMEL_TRY {
+ tok = camel_imapp_stream_token(is, &token, &len);
+ if (tok != '(')
+ camel_exception_throw(1, "envelope: expecting '('");
+
+ /* env_date ::= nstring */
+ camel_imapp_stream_nstring(is, &token);
+ minfo->date_sent = header_decode_date(token, NULL);
+
+ /* env_subject ::= nstring */
+ tok = camel_imapp_stream_nstring(is, &token);
+ /* DUH: this free's it!: camel_message_info_set_subject(minfo, token); */
+ e_poolv_set(minfo->strings, CAMEL_MESSAGE_INFO_SUBJECT, token, FALSE);
+
+ /* 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) {
+ header_address_list_clear(&addr);
+#if 0
+ if (addr)
+ header_address_list_append_list(&addr_from, &addr);
+#endif
+ } else {
+ if (addr)
+ addr_from = addr;
+ }
+
+ if (addr_from) {
+ addrstr = header_address_list_format(addr_from);
+ camel_message_info_set_from(minfo, addrstr);
+ header_address_list_clear(&addr_from);
+ }
+
+ /* we dont keep reply_to */
+
+ /* env_reply_to ::= "(" 1*address ")" / nil */
+ addr = imap_parse_address_list(is);
+ header_address_list_clear(&addr);
+
+ /* env_to ::= "(" 1*address ")" / nil */
+ addr = imap_parse_address_list(is);
+ if (addr) {
+ addrstr = header_address_list_format(addr);
+ camel_message_info_set_to(minfo, addrstr);
+ header_address_list_clear(&addr);
+ }
+
+ /* env_cc ::= "(" 1*address ")" / nil */
+ addr = imap_parse_address_list(is);
+ if (addr) {
+ addrstr = header_address_list_format(addr);
+ camel_message_info_set_cc(minfo, addrstr);
+ header_address_list_clear(&addr);
+ }
+
+ /* we dont keep bcc either */
+
+ /* env_bcc ::= "(" 1*address ")" / nil */
+ addr = imap_parse_address_list(is);
+ header_address_list_clear(&addr);
+
+ /* FIXME: need to put in-reply-to into references hash list */
+
+ /* env_in_reply_to ::= nstring */
+ tok = camel_imapp_stream_nstring(is, &token);
+
+ /* FIXME: need to put message-id into message-id hash */
+
+ /* env_message_id ::= nstring */
+ tok = camel_imapp_stream_nstring(is, &token);
+
+ tok = camel_imapp_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 minfo;
+}
+
+struct _CamelMessageContentInfo *
+imap_parse_body(CamelIMAPPStream *is)
+{
+ int tok, len;
+ unsigned char *token;
+ struct _CamelMessageContentInfo * volatile cinfo = NULL;
+ struct _CamelMessageContentInfo *subinfo, *last;
+ struct _CamelMimeDisposition * volatile dinfo = NULL;
+ struct _CamelMessageInfo * volatile minfo = NULL;
+
+ /* body ::= "(" body_type_1part / body_type_mpart ")" */
+
+ p(printf("body\n"));
+
+ CAMEL_TRY {
+ tok = camel_imapp_stream_token(is, &token, &len);
+ if (tok != '(')
+ camel_exception_throw(1, "body: expecting '('");
+
+ /* 1*body (optional for multiparts) */
+ tok = camel_imapp_stream_token(is, &token, &len);
+ camel_imapp_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_imapp_stream_token(is, &token, &len);
+ camel_imapp_stream_ungettoken(is, tok, token, len);
+ } while (tok == '(');
+
+ d(printf("media_subtype\n"));
+
+ camel_imapp_stream_astring(is, &token);
+ cinfo->type = header_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_imapp_stream_token(is, &token, &len);
+ camel_imapp_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_imapp_stream_token(is, &token, &len);
+ camel_imapp_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_imapp_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_imapp_stream_token(is, &token, &len);
+ camel_imapp_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?? */
+ 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_imapp_stream_token(is, &token, &len);
+ if (tok == IMAP_TOK_INT) {
+ d(printf("field lines: %s\n", token));
+ tok = camel_imapp_stream_token(is, &token, &len);
+ }
+ camel_imapp_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_imapp_stream_nstring(is, &token);
+
+ d(printf("md5: %s\n", token?(char *)token:"NIL"));
+
+ /* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
+
+ tok = camel_imapp_stream_token(is, &token, &len);
+ camel_imapp_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_imapp_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)
+ header_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)
+ header_disposition_unref(dinfo);
+
+ return cinfo;
+}
+
+char *
+imap_parse_section(CamelIMAPPStream *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_imapp_stream_token(is, &token, &len);
+ if (tok != '[')
+ camel_exception_throw(1, "section: expecting '['");
+
+ tok = camel_imapp_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_imapp_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_imapp_stream_token(is, &token, &len);
+ if (tok == '(') {
+ do {
+ tok = camel_imapp_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_imapp_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);
+ g_free(finfo->date);
+ g_free(finfo->section);
+ g_free(finfo->uid);
+ g_free(finfo);
+}
+
+extern void camel_content_info_dump(CamelMessageContentInfo *ci, int depth);
+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);
+ }
+ if (finfo->text) {
+ camel_stream_printf(sout, "Text content:\n");
+ camel_stream_write_to_stream(finfo->text, sout);
+ }
+ if (finfo->header) {
+ camel_stream_printf(sout, "Header content:\n");
+ camel_stream_write_to_stream(finfo->header, sout);
+ }
+ 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(CamelIMAPPStream *is)
+{
+ int tok, len;
+ unsigned char *token, *p, c;
+ struct _fetch_info *finfo;
+
+ finfo = g_malloc0(sizeof(*finfo));
+
+ CAMEL_TRY {
+ tok = camel_imapp_stream_token(is, &token, &len);
+ if (tok != '(')
+ camel_exception_throw(1, "fetch: expecting '('");
+
+ while ( (tok = camel_imapp_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->got |= FETCH_FLAGS;
+ break;
+ case IMAP_INTERNALDATE:
+ camel_imapp_stream_nstring(is, &token);
+ /* TODO: convert to camel format? */
+ finfo->date = g_strdup(token);
+ finfo->got |= FETCH_DATE;
+ break;
+ case IMAP_RFC822_HEADER:
+ camel_imapp_stream_nstring_stream(is, &finfo->header);
+ finfo->got |= FETCH_HEADER;
+ break;
+ case IMAP_RFC822_TEXT:
+ camel_imapp_stream_nstring_stream(is, &finfo->text);
+ finfo->got |= FETCH_TEXT;
+ break;
+ case IMAP_RFC822_SIZE:
+ finfo->size = camel_imapp_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_imapp_stream_token(is, &token, &len);
+ camel_imapp_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_imapp_stream_token(is, &token, &len);
+ if (token[0] == '<') {
+ finfo->offset = strtoul(token+1, NULL, 10);
+ } else {
+ camel_imapp_stream_ungettoken(is, tok, token, len);
+ }
+ camel_imapp_stream_nstring_stream(is, &finfo->body);
+ finfo->got |= FETCH_BODY;
+ } else {
+ camel_exception_throw(1, "unknown body response");
+ }
+ break;
+ case IMAP_UID:
+ tok = camel_imapp_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(CamelIMAPPStream *is)
+{
+ int tok, len;
+ unsigned char *token;
+ struct _status_info *sinfo;
+
+ sinfo = g_malloc0(sizeof(*sinfo));
+
+ CAMEL_TRY {
+ camel_imapp_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_imapp_stream_token(is, &token, &len);
+ if (tok == '[') {
+ camel_imapp_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_NEWNAME:
+ /* the rfc doesn't specify the bnf for this */
+ camel_imapp_stream_astring(is, &token);
+ sinfo->u.newname.oldname = g_strdup(token);
+ camel_imapp_stream_astring(is, &token);
+ sinfo->u.newname.newname = g_strdup(token);
+ break;
+ case IMAP_PERMANENTFLAGS:
+ imap_parse_flags(is, &sinfo->u.permanentflags);
+ break;
+ case IMAP_UIDVALIDITY:
+ sinfo->u.uidvalidity = camel_imapp_stream_number(is);
+ break;
+ case IMAP_UNSEEN:
+ sinfo->u.unseen = camel_imapp_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_imapp_stream_token(is, &token, &len);
+ if (tok == '\n')
+ camel_exception_throw(1, "server response truncated");
+ } while (tok != ']');
+ } else {
+ camel_imapp_stream_ungettoken(is, tok, token, len);
+ }
+
+ /* and take the human readable response */
+ camel_imapp_stream_text(is, (unsigned char **)&sinfo->text);
+ } CAMEL_CATCH(ex) {
+ imap_free_status(sinfo);
+ camel_exception_throw_ex(ex);
+ } CAMEL_DONE;
+
+ return sinfo;
+}
+
+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(CamelIMAPPStream *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_imapp_stream_token(is, &token, &len);
+ if (tok != '(')
+ camel_exception_throw(1, "list: expecting '('");
+
+ while ( (tok = camel_imapp_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_imapp_stream_nstring(is, &token);
+ linfo->separator = token?*token:0;
+ camel_imapp_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 *
+imapp_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);
+ }
+}
+
+
+/* ********************************************************************** */
+/* utility functions */
+
+/* should the rest of imapp-utils go into imapp-parse? */
+
+/* this creates a uid (or sequence number) set directly into the command,
+ optionally breaking it into smaller chunks */
+
+void
+imapp_uidset_init(struct _uidset_state *ss, CamelIMAPPEngine *ie)
+{
+ ss->ie = ie;
+ ss->len = 0;
+ ss->start = 0;
+ ss->last = 0;
+}
+
+int
+imapp_uidset_done(struct _uidset_state *ss, CamelIMAPPCommand *ic)
+{
+ int ret = 0;
+
+ if (ss->last != 0 && ss->last != ss->start) {
+ camel_imapp_engine_command_add(ss->ie, ic, ":%d", ss->last);
+ printf(":%d", ss->last);
+ }
+
+ ret = ss->last != 0;
+
+ ss->start = 0;
+ ss->last = 0;
+ ss->len = 0;
+
+ return ret;
+}
+
+int
+imapp_uidset_add(struct _uidset_state *ss, CamelIMAPPCommand *ic, const char *uid)
+{
+ guint32 uidn;
+
+ uidn = strtoul(uid, NULL, 10);
+ if (uidn == 0)
+ return -1;
+
+ if (ss->last == 0) {
+ camel_imapp_engine_command_add(ss->ie, ic, "%d", uidn);
+ printf("%d", uidn);
+ ss->len ++;
+ ss->start = uidn;
+ } else {
+ if (ss->last != uidn-1) {
+ if (ss->last == ss->start) {
+ camel_imapp_engine_command_add(ss->ie, ic, ",%d", uidn);
+ printf(",%d", uidn);
+ ss->len ++;
+ } else {
+ camel_imapp_engine_command_add(ss->ie, ic, ":%d,%d", ss->last, uidn);
+ printf(":%d,%d", ss->last, uidn);
+ ss->len+=2;
+ }
+ ss->start = uidn;
+ }
+ }
+
+ ss->last = uidn;
+
+ if (ss->len > 10) {
+ imapp_uidset_done(ss, ic);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/camel/providers/imapp/camel-imapp-utils.h b/camel/providers/imapp/camel-imapp-utils.h
new file mode 100644
index 0000000000..76ec178b63
--- /dev/null
+++ b/camel/providers/imapp/camel-imapp-utils.h
@@ -0,0 +1,145 @@
+
+#ifndef _CAMEL_IMAPP_UTILS_H
+#define _CAMEL_IMAPP_UTILS_H
+
+#include <camel/camel-mime-utils.h>
+
+/* FIXME: the enum should be split up into logical groups, so that testing
+ can be done more accurately? */
+
+/* list of strings we know about that can be *quickly* tokenised */
+enum _imap_id {
+ IMAP_UNKNOWN = 0,
+ IMAP_ALERT,
+ IMAP_BYE,
+ IMAP_BAD,
+ IMAP_NO,
+ IMAP_OK,
+ IMAP_PREAUTH,
+ IMAP_NEWNAME,
+ IMAP_PARSE,
+ IMAP_PERMANENTFLAGS,
+ IMAP_READ_ONLY,
+ IMAP_READ_WRITE,
+ IMAP_TRYCREATE,
+ IMAP_UIDVALIDITY,
+ IMAP_UNSEEN,
+ IMAP_ENVELOPE,
+ IMAP_FLAGS,
+ IMAP_INTERNALDATE,
+ IMAP_RFC822_HEADER,
+ IMAP_RFC822_TEXT,
+ IMAP_RFC822_SIZE,
+ IMAP_BODYSTRUCTURE,
+ IMAP_BODY,
+ IMAP_UID,
+};
+
+/* str MUST be in upper case, tokenised using gperf function */
+enum _imap_id imap_tokenise(register const char *str, register unsigned int len);
+
+/* this flag should be part of imapfoldersummary */
+enum {
+ CAMEL_IMAPP_MESSAGE_RECENT = (1<<8),
+};
+
+/* ********************************************************************** */
+void imap_parse_flags(CamelIMAPPStream *stream, guint32 *flagsp) /* IO,PARSE */;
+void imap_write_flags(CamelStream *stream, guint32 flags) /* IO */;
+
+/* ********************************************************************** */
+void imap_parse_param_list(CamelIMAPPStream *is, struct _header_param **plist) /* IO,PARSE */;
+struct _CamelMimeDisposition *imap_parse_ext_optional(CamelIMAPPStream *is) /* IO,PARSE */;
+struct _CamelMessageContentInfo *imap_parse_body_fields(CamelIMAPPStream *is) /* IO,PARSE */;
+struct _header_address *imap_parse_address_list(CamelIMAPPStream *is) /* IO,PARSE */;
+struct _CamelMessageInfo *imap_parse_envelope(CamelIMAPPStream *is) /* IO, PARSE */;
+struct _CamelMessageContentInfo *imap_parse_body(CamelIMAPPStream *is) /* IO,PARSE */;
+char *imap_parse_section(CamelIMAPPStream *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 */
+ CamelMessageInfo *minfo; /* ENVELOPE */
+ CamelMessageContentInfo *cinfo; /* BODYSTRUCTURE,BODY */
+ guint32 size; /* RFC822.SIZE */
+ guint32 offset; /* start offset of a BODY[]<offset.length> request */
+ guint32 flags; /* 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(CamelIMAPPStream *is);
+void imap_free_fetch(struct _fetch_info *finfo);
+void imap_dump_fetch(struct _fetch_info *finfo);
+
+/* ********************************************************************** */
+
+struct _status_info {
+ enum _imap_id result; /* ok/no/bad/preauth only */
+ enum _imap_id 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;
+ } u;
+
+ char *text;
+};
+
+struct _status_info *imap_parse_status(CamelIMAPPStream *is);
+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(CamelIMAPPStream *is);
+char *imapp_list_get_path(struct _list_info *li);
+void imap_free_list(struct _list_info *linfo);
+
+/* ********************************************************************** */
+
+struct _uidset_state {
+ struct _CamelIMAPPEngine *ie;
+ int len;
+ guint32 start;
+ guint32 last;
+};
+
+struct _CamelIMAPPEngine;
+struct _CamelIMAPPCommand;
+void imapp_uidset_init(struct _uidset_state *ss, struct _CamelIMAPPEngine *ie);
+int imapp_uidset_done(struct _uidset_state *ss, struct _CamelIMAPPCommand *ic);
+int imapp_uidset_add(struct _uidset_state *ss, struct _CamelIMAPPCommand *ic, const char *uid);
+
+#endif
diff --git a/camel/providers/imapp/libcamelimapp.urls b/camel/providers/imapp/libcamelimapp.urls
new file mode 100644
index 0000000000..aad1347834
--- /dev/null
+++ b/camel/providers/imapp/libcamelimapp.urls
@@ -0,0 +1 @@
+imapp