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