CamelRemoteStore: a new generic store for stores that connect to servers. Prepare...
authorPeter Williams <peterw@src.gnome.org>
Tue, 29 Aug 2000 21:28:46 +0000 (21:28 +0000)
committerPeter Williams <peterw@src.gnome.org>
Tue, 29 Aug 2000 21:28:46 +0000 (21:28 +0000)
20 files changed:
camel/ChangeLog
camel/Makefile.am
camel/camel-folder.c
camel/camel-folder.h
camel/camel-remote-store.c [new file with mode: 0644]
camel/camel-remote-store.h [new file with mode: 0644]
camel/camel-service.c
camel/camel-service.h
camel/camel-session.c
camel/camel-store.c
camel/providers/imap/camel-imap-folder.c
camel/providers/imap/camel-imap-store.c
camel/providers/imap/camel-imap-store.h
camel/providers/imap/camel-imap-stream.c
camel/providers/mbox/camel-mbox-folder.c
camel/providers/mbox/camel-mbox-store.c
camel/providers/nntp/camel-nntp-folder.c
camel/providers/nntp/camel-nntp-store.c
camel/providers/pop3/camel-pop3-folder.c
camel/providers/pop3/camel-pop3-store.c

index 54c23dc..a9e5725 100644 (file)
@@ -1,3 +1,27 @@
+2000-08-29  Peter Williams  <peterw@helixcode.com>
+
+       * camel-service.c (camel_service_connect): Uncomment this.
+       (camel_service_disconnect): Same.
+
+       * camel-remote-store.[ch]: New files. Abstract remote storages
+       (IMAP, POP3, NNTP) and hides the lower-level networky stuff.
+       
+       * camel-service.c (camel_service_new): Take an extra argument, the
+       provider that created us, cause it's useful.
+       (camel_service_finalize): Unref our new provider member.
+
+       * camel-session.c (camel_session_get_service): Pass the proper number of
+       arguments to camel_service_new().
+
+       * camel-imap-store.c: Massive update: 1) use the CamelRemoteService to
+       make our life Very Easy (TM). 2) Change the semantics of all 
+       camel_imap_command* functions to take exceptions, centralize tons of
+       duplicate code, and use the handy RemoteStore utility functions
+
+       * camel-imap-folder.c: Use the new semantics of camel_imap_command*
+
+       * camel-imap-stream.c: Same.
+       
 2000-08-29  Jeffrey Stedfast  <fejj@helixcode.com>
 
        * providers/imap/camel-imap-store.c (camel_imap_command_extended):
        (imap_copy_message_to): Updated.
        (imap_move_message_to): Updated.
 
+2000-08-28  Peter Williams  <peterw@helixcode.com>
+
+       * camel-folder.c (camel_folder_refresh_info): New member function,
+       refresh_info, used for rereading folder state after its state has
+       somehow become unknown. Tries to preserve last-known status of
+       messages.
+
+       * providers/mbox/camel-mbox-folder.c (mbox_refresh_info): Implement
+       ::refresh_info (split up ::init)
+
+       * providers/mbox/camel-mbox-store.c (get_folder): Call ::refresh_info.
+
+       * providers/imap/camel-imap-folder.c (camel_imap_folder_new): Call
+       ::refresh_info once initialized.
+       (imap_refresh_info): New member function; reads the summary from
+       the server (used to be in camel_imap_folder_new; split out).
+
+       * providers/imap/camel-imap-store.c (imap_connect): Set
+       CamelService::connected a little early so that 
+       camel_imap_command won't try to connect while already
+       connnecting.
+       (camel_imap_command*): Try to connect if not connected already.
+       
+       * providers/pop3/camel-pop3-folder.c (pop3_refresh_info): Same as above.
+
+       * providers/pop3/camel-pop3-folder.c (camel_pop3_folder_new): Same
+       as above.
+
+       * providers/pop3/camel-pop3-store.c (pop3_connect): Set 
+       CamelService::connected a little early so that
+       camel_pop3_command won't try to connect while already
+       connecting
+       (connect_to_server): Same.
+
+       * providers/nntp/camel-nntp-folder.c (nntp_folder_refresh_info): Same
+       as above.
+
 2000-08-28  Jeffrey Stedfast  <fejj@helixcode.com>
 
        * providers/imap/camel-imap-folder.c (imap_get_message): Fixed the
index 4d2c29a..320f4cc 100644 (file)
@@ -40,6 +40,7 @@ libcamel_la_SOURCES =                                 \
        camel-multipart.c                       \
        camel-object.c                          \
        camel-provider.c                        \
+       camel-remote-store.c                    \
        camel-seekable-stream.c                 \
        camel-seekable-substream.c              \
        camel-service.c                         \
@@ -87,6 +88,7 @@ libcamelinclude_HEADERS =                     \
        camel-multipart.h                       \
        camel-object.h                          \
        camel-provider.h                        \
+       camel-remote-store.h                    \
        camel-seekable-stream.h                 \
        camel-seekable-substream.h              \
        camel-service.h                         \
index 201e2cc..888845a 100644 (file)
@@ -44,6 +44,7 @@ static void init (CamelFolder *folder, CamelStore *parent_store,
 
 static void camel_folder_finalize (CamelObject *object);
 
+static void refresh_info (CamelFolder *folder, CamelException *ex);
 
 static void folder_sync (CamelFolder *folder, gboolean expunge,
                         CamelException *ex);
@@ -134,6 +135,7 @@ camel_folder_class_init (CamelFolderClass *camel_folder_class)
        /* virtual method definition */
        camel_folder_class->init = init;
        camel_folder_class->sync = folder_sync;
+       camel_folder_class->refresh_info = refresh_info;
        camel_folder_class->get_name = get_name;
        camel_folder_class->get_full_name = get_full_name;
        camel_folder_class->can_hold_folders = can_hold_folders;
@@ -320,6 +322,29 @@ void camel_folder_sync(CamelFolder * folder, gboolean expunge, CamelException *
        CF_CLASS(folder)->sync(folder, expunge, ex);
 }
 
+static void
+refresh_info (CamelFolder *folder, CamelException *ex)
+{
+       g_warning ("CamelFolder::refresh_info not implemented for `%s'",
+                  camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
+}
+
+/**
+ * camel_folder_refresh_info:
+ * @folder: The folder object
+ * @ex: exception object
+ *
+ * Updates a folder's summary to be in sync with its backing store
+ * (called upon creation and when the store's connection is lost
+ * and then reestablished).
+ **/
+void camel_folder_refresh_info (CamelFolder * folder, CamelException * ex)
+{
+       g_return_if_fail(CAMEL_IS_FOLDER(folder));
+
+       CF_CLASS(folder)->refresh_info(folder, ex);
+}
+
 static const gchar *get_name(CamelFolder * folder)
 {
        return folder->name;
index da76618..c78ed28 100644 (file)
@@ -80,6 +80,8 @@ typedef struct {
                        gchar *separator, gboolean path_begins_with_sep,
                        CamelException *ex);
 
+       void   (*refresh_info) (CamelFolder *folder, CamelException *ex);
+
        void   (*sync) (CamelFolder *folder, gboolean expunge, 
                        CamelException *ex);
 
@@ -194,6 +196,8 @@ CamelFolder *      camel_folder_get_subfolder          (CamelFolder *folder,
                                                        gboolean create,
                                                        CamelException *ex);
 
+void               camel_folder_refresh_info           (CamelFolder * folder, 
+                                                       CamelException * ex);
 void               camel_folder_sync                   (CamelFolder *folder, 
                                                        gboolean expunge, 
                                                        CamelException *ex);
diff --git a/camel/camel-remote-store.c b/camel/camel-remote-store.c
new file mode 100644 (file)
index 0000000..e86c05e
--- /dev/null
@@ -0,0 +1,496 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-remote-store.c : class for an remote store */
+
+/*
+ *  Authors: Peter Williams <peterw@helixcode.com>
+ *           based on camel-imap-provider.c
+ *
+ *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#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 <e-util/e-util.h>
+
+#include "camel-remote-store.h"
+#include "camel-folder.h"
+#include "camel-exception.h"
+#include "camel-session.h"
+#include "camel-stream.h"
+#include "camel-stream-buffer.h"
+#include "camel-stream-fs.h"
+#include "camel-url.h"
+#include "string-utils.h"
+
+#define d(x) x
+
+#define CSRVC(obj) (CAMEL_SERVICE_CLASS      (CAMEL_OBJECT_GET_CLASS (obj)))
+#define CSTRC(obj) (CAMEL_STORE_CLASS        (CAMEL_OBJECT_GET_CLASS (obj)))
+#define CRSC(obj)  (CAMEL_REMOTE_STORE_CLASS (CAMEL_OBJECT_GET_CLASS (obj)))
+
+static CamelStoreClass *store_class = NULL;
+
+static gboolean remote_connect         (CamelService *service, CamelException *ex);
+static gboolean remote_disconnect      (CamelService *service, CamelException *ex);
+static GList   *remote_query_auth_types_generic   (CamelService *service, CamelException *ex);
+static GList   *remote_query_auth_types_connected (CamelService *service, CamelException *ex);
+static void     remote_free_auth_types (CamelService *service, GList *authtypes);
+static char    *remote_get_name        (CamelService *service, gboolean brief);
+static char    *remote_get_folder_name (CamelStore *store, 
+                                       const char *folder_name, 
+                                       CamelException *ex);
+static void     remote_post_connect    (CamelRemoteStore *store, CamelException *ex);
+static void     remote_pre_disconnect  (CamelRemoteStore *store, CamelException *ex);
+static gint     remote_send_string     (CamelRemoteStore *store, CamelException *ex, 
+                                       char *fmt, va_list ap);
+static gint     remote_send_stream     (CamelRemoteStore *store, CamelStream *stream,
+                                       CamelException *ex);
+static gint     remote_recv_line       (CamelRemoteStore *store, char **dest, 
+                                       CamelException *ex);
+
+static void
+camel_remote_store_class_init (CamelRemoteStoreClass *camel_remote_store_class)
+{
+       /* virtual method overload */
+       CamelServiceClass *camel_service_class =
+               CAMEL_SERVICE_CLASS (camel_remote_store_class);
+       CamelStoreClass *camel_store_class =
+               CAMEL_STORE_CLASS (camel_remote_store_class);
+
+       store_class = CAMEL_STORE_CLASS(camel_type_get_global_classfuncs (camel_store_get_type ()));
+
+       /* virtual method overload */
+       camel_service_class->connect = remote_connect;
+       camel_service_class->disconnect = remote_disconnect;
+       camel_service_class->query_auth_types_generic = remote_query_auth_types_generic;
+       camel_service_class->query_auth_types_connected = remote_query_auth_types_connected;
+       camel_service_class->free_auth_types = remote_free_auth_types;
+       camel_service_class->get_name = remote_get_name;
+
+       camel_store_class->get_folder_name = remote_get_folder_name;
+
+       camel_remote_store_class->post_connect = remote_post_connect;
+       camel_remote_store_class->pre_disconnect = remote_pre_disconnect;
+       camel_remote_store_class->send_string = remote_send_string;
+       camel_remote_store_class->send_stream = remote_send_stream;
+       camel_remote_store_class->recv_line = remote_recv_line;
+       camel_remote_store_class->keepalive = NULL;
+}
+
+static void
+camel_remote_store_init (CamelObject *object)
+{
+       CamelService *service = CAMEL_SERVICE (object);
+       CamelStore *store = CAMEL_STORE (object);
+       CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object);
+
+       service->url_flags |= CAMEL_SERVICE_URL_NEED_HOST;
+
+       store->folders = g_hash_table_new (g_str_hash, g_str_equal);
+
+       remote_store->istream = NULL;
+       remote_store->ostream = NULL;
+       remote_store->timeout_id = 0;
+}
+
+/*
+ *static void
+ *camel_remote_store_finalize (CamelObject *object)
+ *{
+ *     CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object);
+ *
+ *     g_free (remote_store->nice_name);
+ *}
+ */
+
+CamelType
+camel_remote_store_get_type (void)
+{
+       static CamelType camel_remote_store_type = CAMEL_INVALID_TYPE;
+       
+       if (camel_remote_store_type == CAMEL_INVALID_TYPE) {
+               camel_remote_store_type = camel_type_register (CAMEL_STORE_TYPE, "CamelRemoteStore",
+                                                              sizeof (CamelRemoteStore),
+                                                              sizeof (CamelRemoteStoreClass),
+                                                              (CamelObjectClassInitFunc) camel_remote_store_class_init,
+                                                              NULL,
+                                                              (CamelObjectInitFunc) camel_remote_store_init,
+                                                              (CamelObjectFinalizeFunc) NULL);
+       }
+       
+       return camel_remote_store_type;
+}
+
+/* Auth stuff */
+
+/*
+static CamelServiceAuthType password_authtype = {
+       "SSH Tunneling",
+       
+       "This option will connect to the REMOTE server using a "
+       "plaintext password.",
+       
+       "",
+       TRUE
+};
+*/
+
+static GList *
+remote_query_auth_types_connected (CamelService *service, CamelException *ex)
+{
+       g_warning ("remote::query_auth_types_connected: not implemented. Defaulting.");
+       return CSRVC (service)->query_auth_types_generic (service, ex);
+}
+
+static GList *
+remote_query_auth_types_generic (CamelService *service, CamelException *ex)
+{
+       g_warning ("remote::query_auth_types_generic: not implemented. Defaulting.");
+       return NULL;
+}
+
+static void
+remote_free_auth_types (CamelService *service, GList *authtypes)
+{
+       g_list_free (authtypes);
+}
+
+static char *
+remote_get_name (CamelService *service, gboolean brief)
+{
+       if (brief)
+               return g_strdup_printf ("%s server %s", 
+                                       service->provider->name,
+                                       service->url->host);
+       else {
+               return g_strdup_printf ("%s service for %s on %s",
+                                       service->provider->name,
+                                       service->url->user,
+                                       service->url->host);
+       }
+}
+
+static void
+refresh_folder_info (gpointer key, gpointer value, gpointer data)
+{
+       CamelFolder *folder = CAMEL_FOLDER (value);
+
+       camel_folder_refresh_info (folder, (CamelException *) data);
+}
+
+static gboolean
+remote_connect (CamelService *service, CamelException *ex)
+{
+       CamelRemoteStore *store = CAMEL_REMOTE_STORE (service);
+       struct hostent *h;
+       struct sockaddr_in sin;
+       gint fd;
+       gint port;
+
+       h = camel_service_gethost (service, ex);
+       if (!h)
+               return FALSE;
+
+       /* connect to the server */
+       sin.sin_family = h->h_addrtype;
+
+       if (service->url->port)
+               port = service->url->port;
+       else {
+               CamelProvider *prov = camel_service_get_provider (service);
+
+               port = prov->default_ports[CAMEL_PROVIDER_STORE];
+               g_assert (port); /* a remote service MUST define a valid default port */
+       }
+
+       sin.sin_port = htons(port);
+
+       memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr));
+       
+       fd = socket (h->h_addrtype, SOCK_STREAM, 0);
+       if (fd == -1 || connect (fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+                                     "Could not connect to %s (port %d): %s",
+                                     service->url->host ? service->url->host : "(unknown host)", 
+                                     port, strerror(errno));
+               if (fd > -1)
+                       close (fd);
+               
+               return FALSE;
+       }
+
+       /* parent class connect initialization */
+       CAMEL_SERVICE_CLASS (store_class)->connect (service, ex);
+       
+       store->ostream = camel_stream_fs_new_with_fd (fd);
+       store->istream = camel_stream_buffer_new (store->ostream, CAMEL_STREAM_BUFFER_READ);
+
+       /* Okay, good enough for us */
+       CAMEL_SERVICE (store)->connected = TRUE;
+
+       /* implementation of postconnect */
+       CRSC (store)->post_connect (store, ex);
+
+       if (camel_exception_is_set (ex)) {
+               /* FIXME: the real exception may get overridden? */
+               camel_service_disconnect (CAMEL_SERVICE (store), ex);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean timeout_cb (gpointer data)
+{
+       CRSC (data)->keepalive (CAMEL_REMOTE_STORE (data));
+       return TRUE;
+}
+
+static void
+remote_post_connect (CamelRemoteStore *store, CamelException *ex)
+{
+       /* Add a timeout so that we can hopefully prevent getting disconnected */
+       /* (Only if the implementation supports it) */
+       if (CRSC (store)->keepalive) {
+               CamelSession *session = camel_service_get_session (CAMEL_SERVICE (store));
+
+               store->timeout_id = camel_session_register_timeout (session, 10 * 60 * 1000, 
+                                                                   timeout_cb, 
+                                                                   store);
+       }
+
+       /* Let's make sure that any of our folders are brought up to speed */
+       g_hash_table_foreach (CAMEL_STORE (store)->folders, refresh_folder_info, ex);
+}
+
+static void
+remote_pre_disconnect (CamelRemoteStore *store, CamelException *ex)
+{
+       if (store->timeout_id) {
+               camel_session_remove_timeout (camel_service_get_session (CAMEL_SERVICE (store)),
+                                             store->timeout_id);
+               store->timeout_id = 0;
+       }
+}
+
+static gboolean
+remote_disconnect (CamelService *service, CamelException *ex)
+{
+       CamelRemoteStore *store = CAMEL_REMOTE_STORE (service);
+
+       CRSC (service)->pre_disconnect (store, ex);
+       /* if the exception is set, screw it and dconn anyway */
+
+       if (!CAMEL_SERVICE_CLASS (store_class)->disconnect (service, ex))
+               return FALSE;
+
+       if (store->istream) {
+               camel_object_unref (CAMEL_OBJECT (store->istream));
+               store->istream = NULL;
+       }
+
+       if (store->ostream) {
+               camel_object_unref (CAMEL_OBJECT (store->ostream));
+               store->ostream = NULL;
+       }
+
+       return TRUE;
+}
+
+static gchar *
+remote_get_folder_name (CamelStore *store, const char *folder_name, CamelException *ex)
+{
+       return g_strdup (folder_name);
+}
+
+static gint
+remote_send_string (CamelRemoteStore *store, CamelException *ex, char *fmt, va_list ap)
+{
+       gchar *cmdbuf;
+
+       /* Check for connectedness. Failed (or cancelled) operations will
+        * close the connection. */
+
+       if (store->ostream == NULL) {
+               d(g_message ("remote: (send) disconnected, reconnecting."));
+
+               if (!camel_service_connect (CAMEL_SERVICE (store), ex))
+                       return -1;
+       }
+       
+       /* create the command */
+       cmdbuf = g_strdup_vprintf (fmt, ap);
+       
+       d(fprintf (stderr, "sending : \"%s\"\n", cmdbuf));
+       
+       if (camel_stream_printf (store->ostream, "%s", cmdbuf) == -1) {
+               g_free (cmdbuf);
+               camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+                                    g_strerror (errno));
+               /* FIXME: exception may be overridden */
+               camel_service_disconnect (CAMEL_SERVICE (store), ex);
+               return -1;
+       }
+       g_free (cmdbuf);
+       
+       return 0;
+}
+
+/**
+ * camel_remote_store_send_string: Writes a string to the server
+ * @store: a CamelRemoteStore
+ * @ex: a CamelException
+ * @fmt: the printf-style format to use for creating the string to send
+ * @...: the arguments to the printf string @fmt
+ * Return value: 0 on success, nonzero on error
+ *
+ * Formats the string and sends it to the server.
+ **/
+
+gint 
+camel_remote_store_send_string (CamelRemoteStore *store, CamelException *ex,
+                               char *fmt, ...)
+{
+       va_list ap;
+       gint ret;
+
+       g_return_val_if_fail (CAMEL_IS_REMOTE_STORE (store), -1);
+       g_return_val_if_fail (fmt, -1);
+
+       va_start (ap, fmt);
+       ret = CRSC (store)->send_string (store, ex, fmt, ap);
+       va_end (ap);
+
+       return ret;
+}
+
+static gint
+remote_send_stream (CamelRemoteStore *store, CamelStream *stream, CamelException *ex)
+{
+       /* Check for connectedness. Failed (or cancelled) operations will
+        * close the connection. */
+
+       if (store->ostream == NULL) {
+               d(g_message ("remote: (sendstream) disconnected, reconnecting."));
+
+               if (!camel_service_connect (CAMEL_SERVICE (store), ex))
+                       return -1;
+       }
+       
+       d(fprintf (stderr, "(sending stream)\n"));
+       
+       if (camel_stream_write_to_stream (stream, store->ostream) < 0) {
+               camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+                                    g_strerror (errno));
+               /* FIXME: exception may be overridden */
+               camel_service_disconnect (CAMEL_SERVICE (store), ex);
+               return -1;
+       }
+       
+       return 0;
+}
+
+/**
+ * camel_remote_store_send_stream: Writes a CamelStream to the server
+ * @store: a CamelRemoteStore
+ * @stream: the stream to write
+ * @ex: a CamelException
+ * Return value: 0 on success, nonzero on error
+ *
+ * Sends the stream to the server.
+ **/
+
+gint 
+camel_remote_store_send_stream (CamelRemoteStore *store, CamelStream *stream, CamelException *ex)
+{
+       g_return_val_if_fail (CAMEL_IS_REMOTE_STORE (store), -1);
+       g_return_val_if_fail (CAMEL_IS_STREAM (stream), -1);
+
+       return CRSC (store)->send_stream (store, stream, ex);
+}
+
+static gint
+remote_recv_line (CamelRemoteStore *store, char **dest, CamelException *ex)
+{
+       CamelStreamBuffer *stream = CAMEL_STREAM_BUFFER (store->istream);
+
+       (*dest) = NULL;
+
+       /* Check for connectedness. Failed (or cancelled) operations will
+        * close the connection. We can't expect a read to have any
+        * meaning if we reconnect, so always set an exception.
+        */
+
+       if (store->istream == NULL) {
+               g_message ("remote: (recv) disconnected, reconnecting.");
+
+               camel_service_connect (CAMEL_SERVICE (store), ex);
+
+               if (!camel_exception_is_set (ex))
+                       camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_NOT_CONNECTED,
+                                            g_strerror (errno));
+
+               return -1;
+       }
+               
+       (*dest) = camel_stream_buffer_read_line (stream);
+
+       if (!(*dest)) {
+               camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+                                    g_strerror (errno));
+               /* FIXME: exception may be overridden */
+               camel_service_disconnect (CAMEL_SERVICE (store), ex);
+               return -1;
+       }
+
+       d(fprintf (stderr, "received: %s\n", (*dest)));
+
+       return 0;
+}
+
+/**
+ * camel_remote_store_recv_line: Reads a line from the server
+ * @store: a CamelRemoteStore
+ * @dest: a pointer that will be set to the location of a buffer
+ *        holding the server's response
+ * @ex: a CamelException
+ * Return value: 0 on success, -1 on error
+ *
+ * Reads a line from the server (terminated by \n or \r\n).
+ **/
+
+gint 
+camel_remote_store_recv_line (CamelRemoteStore *store, char **dest,
+                             CamelException *ex)
+{
+       g_return_val_if_fail (CAMEL_IS_REMOTE_STORE (store), -1);
+       g_return_val_if_fail (dest, -1);
+
+       return CRSC (store)->recv_line (store, dest, ex);
+}
+
diff --git a/camel/camel-remote-store.h b/camel/camel-remote-store.h
new file mode 100644 (file)
index 0000000..a2b8813
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* camel-remote-store.h : class for a remote store */
+
+/* 
+ * Authors: Peter Williams <peterw@helixcode.com>
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or 
+ * modify it under the terms of the GNU General Public License as 
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef CAMEL_REMOTE_STORE_H
+#define CAMEL_REMOTE_STORE_H 1
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include "camel-store.h"
+
+#define CAMEL_REMOTE_STORE_TYPE     (camel_remote_store_get_type ())
+#define CAMEL_REMOTE_STORE(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_REMOTE_STORE_TYPE, CamelRemoteStore))
+#define CAMEL_REMOTE_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_REMOTE_STORE_TYPE, CamelRemoteStoreClass))
+#define CAMEL_IS_REMOTE_STORE(o)    (CAMEL_CHECK_TYPE((o), CAMEL_REMOTE_STORE_TYPE))
+
+typedef struct {
+       CamelStore parent_object;       
+       
+       CamelStream *istream, *ostream;
+       guint timeout_id;
+} CamelRemoteStore;
+
+
+typedef struct {
+       CamelStoreClass parent_class;
+
+       void (*post_connect)  (CamelRemoteStore *store, CamelException *ex);
+       void (*pre_disconnect)(CamelRemoteStore *store, CamelException *ex);
+       gint (*send_string)   (CamelRemoteStore *store, CamelException *ex, 
+                              char *fmt, va_list ap);
+       gint (*send_stream)   (CamelRemoteStore *store, CamelStream *stream, 
+                              CamelException *ex);
+       gint (*recv_line)     (CamelRemoteStore *store, char **dest, 
+                              CamelException *ex);
+       void (*keepalive)     (CamelRemoteStore *store);
+} CamelRemoteStoreClass;
+
+
+/* Standard Camel function */
+CamelType camel_remote_store_get_type (void);
+
+/* Extra public functions */
+gint camel_remote_store_send_string (CamelRemoteStore *store, CamelException *ex,
+                                    char *fmt, ...);
+gint camel_remote_store_send_stream (CamelRemoteStore *store, CamelStream *stream, 
+                                    CamelException *ex);
+gint camel_remote_store_recv_line (CamelRemoteStore *store, char **dest,
+                                  CamelException *ex);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_REMOTE_STORE_H */
index 6554bad..9df7837 100644 (file)
@@ -151,8 +151,8 @@ check_url (CamelService *service, CamelException *ex)
  * Return value: the CamelService, or NULL.
  **/
 CamelService *
-camel_service_new (CamelType type, CamelSession *session, CamelURL *url,
-                  CamelException *ex)
+camel_service_new (CamelType type, CamelSession *session, CamelProvider *provider,
+                  CamelURL *url, CamelException *ex)
 {
        CamelService *service;
 
@@ -171,6 +171,9 @@ camel_service_new (CamelType type, CamelSession *session, CamelURL *url,
        service->session = session;
        camel_object_ref (CAMEL_OBJECT (session));
 
+       service->provider = provider;
+       /* don't ref -- providers are not CamelObjects */
+
        service->connected = FALSE;
        
        if (!url->empty) {
@@ -205,22 +208,24 @@ service_connect (CamelService *service, CamelException *ex)
  *
  * Return value: whether or not the connection succeeded
  **/
-/**
- *gboolean
- *camel_service_connect (CamelService *service, CamelException *ex)
- *{
- *     g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
- *     g_return_val_if_fail (service->session != NULL, FALSE);
- *     g_return_val_if_fail (service->url != NULL, FALSE);
- *
- *     if (service->connect_level > 0) {
- *             service->connect_level++;
- *             return TRUE;
- *     }
- *
- *     return CSERV_CLASS (service)->connect (service, ex);
- *}
- **/
+
+gboolean
+camel_service_connect (CamelService *service, CamelException *ex)
+{
+       g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+       g_return_val_if_fail (service->session != NULL, FALSE);
+       g_return_val_if_fail (service->url != NULL, FALSE);
+
+       if (service->connected) {
+               /* But we're still connected, so no exception
+                * and return true.
+                */
+               g_warning ("camel_service_connect: trying to connect to an already connected service");
+               return TRUE;
+       }
+
+       return CSERV_CLASS (service)->connect (service, ex);
+}
 
 static gboolean
 service_disconnect (CamelService *service, CamelException *ex)
@@ -244,19 +249,18 @@ service_disconnect (CamelService *service, CamelException *ex)
  * Return value: whether or not the disconnection succeeded without
  * errors. (Consult @ex if %FALSE.)
  **/
-/**
- *gboolean
- *camel_service_disconnect (CamelService *service, CamelException *ex)
- *{
- *     if (service->connect_level < 1) {
- *             camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_NOT_CONNECTED,
- *                                  "Trying to disconnect from a service that isn't connected");
- *             return FALSE;
- *     }
- *
- *     return CSERV_CLASS (service)->disconnect (service, ex);
- *}
- **/
+
+gboolean
+camel_service_disconnect (CamelService *service, CamelException *ex)
+{
+       if (!service->connected) {
+               camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_NOT_CONNECTED,
+                                    "Trying to disconnect from a service that isn't connected");
+               return FALSE;
+       }
+       
+       return CSERV_CLASS (service)->disconnect (service, ex);
+}
 
 /**
  *static gboolean
@@ -341,6 +345,19 @@ camel_service_get_session (CamelService *service)
        return service->session;
 }
 
+/**
+ * camel_service_get_provider:
+ * @service: a service
+ *
+ * Returns the CamelProvider associated with the service.
+ *
+ * Return value: the provider
+ **/
+CamelProvider *
+camel_service_get_provider (CamelService *service)
+{
+       return service->provider;
+}
 
 GList *
 query_auth_types_func (CamelService *service, CamelException *ex)
index 3558d5a..f229c5e 100644 (file)
@@ -36,6 +36,7 @@ extern "C" {
 
 #include <camel/camel-object.h>
 #include <camel/camel-url.h>
+#include <camel/camel-provider.h>
 #include <netdb.h>
 
 #define CAMEL_SERVICE_TYPE     (camel_service_get_type ())
@@ -48,6 +49,7 @@ struct _CamelService {
        CamelObject parent_object;
 
        CamelSession *session;
+       CamelProvider *provider;
        gboolean connected;
        CamelURL *url;
        int url_flags;
@@ -109,14 +111,18 @@ typedef struct {
 /* public methods */
 CamelService *      camel_service_new                (CamelType type, 
                                                      CamelSession *session,
+                                                     CamelProvider *provider,
                                                      CamelURL *url, 
                                                      CamelException *ex);
-
+gboolean            camel_service_connect            (CamelService *service, 
+                                                     CamelException *ex);
+gboolean            camel_service_disconnect         (CamelService *service, 
+                                                     CamelException *ex);
 char *              camel_service_get_url            (CamelService *service);
 char *              camel_service_get_name           (CamelService *service,
                                                      gboolean brief);
 CamelSession *      camel_service_get_session        (CamelService *service);
-
+CamelProvider *     camel_service_get_provider       (CamelService *service);
 GList *             camel_service_query_auth_types   (CamelService *service,
                                                      CamelException *ex);
 void                camel_service_free_auth_types    (CamelService *service,
index f9f0584..3454517 100644 (file)
@@ -244,7 +244,7 @@ camel_session_get_service (CamelSession *session, const char *url_string,
                return service;
        }
 
-       service = camel_service_new (provider->object_types[type], session, url, ex);
+       service = camel_service_new (provider->object_types[type], session, provider, url, ex);
        if (service) {
                g_hash_table_insert (provider->service_cache, url, service);
                camel_object_hook_event (CAMEL_OBJECT (service), "finalize", (CamelObjectEventHookFunc) service_cache_remove, session);
index a036d52..039490c 100644 (file)
@@ -370,4 +370,3 @@ camel_store_get_default_folder (CamelStore *store, CamelException *ex)
        }
        return folder;
 }
-
index e3ce219..b0dbad5 100644 (file)
@@ -63,7 +63,7 @@ static void imap_init (CamelFolder *folder, CamelStore *parent_store,
                       CamelFolder *parent_folder, const gchar *name,
                       gchar *separator, gboolean path_begns_with_sep,
                       CamelException *ex);
-
+static void imap_refresh_info (CamelFolder *folder, CamelException *ex);
 static void imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
 static void imap_expunge (CamelFolder *folder, CamelException *ex);
 
@@ -108,6 +108,7 @@ camel_imap_folder_class_init (CamelImapFolderClass *camel_imap_folder_class)
        
        /* virtual method overload */
        camel_folder_class->init = imap_init;
+       camel_folder_class->refresh_info = imap_refresh_info;
        camel_folder_class->sync = imap_sync;
        camel_folder_class->expunge = imap_expunge;
        
@@ -185,11 +186,8 @@ camel_imap_folder_new (CamelStore *parent, char *folder_name, CamelException *ex
        if (!strcmp (folder_name, url->path + 1))
                folder->can_hold_messages = FALSE;
        
-       imap_get_subfolder_names_internal (folder, ex);
-       
-       if (folder->can_hold_messages)
-               imap_get_summary_internal (folder, ex);
-       
+       CF_CLASS (folder)->refresh_info (folder, ex);
+
        return folder;
 }
 
@@ -286,6 +284,15 @@ imap_init (CamelFolder *folder, CamelStore *parent_store, CamelFolder *parent_fo
 }
 
 static void
+imap_refresh_info (CamelFolder *folder, CamelException *ex)
+{
+       imap_get_subfolder_names_internal (folder, ex);
+       
+       if (folder->can_hold_messages)
+               imap_get_summary_internal (folder, ex);
+}
+
+static void
 imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
 {
        CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
@@ -317,21 +324,12 @@ imap_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
                                        
                                        *(flags + strlen (flags) - 1) = '\0';
                                        s = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store),
-                                                                        folder, &result,
+                                                                        folder, &result, ex,
                                                                         "UID STORE %s FLAGS.SILENT (%s)",
                                                                         info->uid, flags);
                                        
-                                       if (s != CAMEL_IMAP_OK) {
-                                               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-                                               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                                                     "Could not set flags on message %s on IMAP "
-                                                                     "server %s: %s.", info->uid,
-                                                                     service->url->host,
-                                                                     s != CAMEL_IMAP_FAIL && result ? result :
-                                                                     "Unknown error");
-                                               g_free (result);
+                                       if (s != CAMEL_IMAP_OK)
                                                return;
-                                       }
                                        
                                        g_free (result);
                                }
@@ -352,19 +350,11 @@ imap_expunge (CamelFolder *folder, CamelException *ex)
        
        imap_sync (folder, FALSE, ex);
        
-       status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                             &result, "EXPUNGE");
+       status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder, 
+                                             &result, ex, "EXPUNGE");
        
-       if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not EXPUNGE from IMAP server %s: %s.",
-                                     service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
+       if (status != CAMEL_IMAP_OK)
                return;
-       }
        
        /* determine which messages were successfully expunged */
        node = result;
@@ -381,7 +371,7 @@ imap_expunge (CamelFolder *folder, CamelException *ex)
                                
                                word = imap_next_word (word);
                                for (ep = word; *ep && *ep != '\n'; ep++);
-                               reason = g_strndup (word, (gint)(ep - word) + 1);
+                               reason = g_strndup (word, (gint)(ep - word));
                                
                                camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
                                                      "Could not EXPUNGE from IMAP server %s: %s.",
@@ -462,23 +452,15 @@ imap_get_message_count_internal (CamelFolder *folder, CamelException *ex)
        
        if (CAMEL_IMAP_STORE (store)->has_status_capability)
                status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), folder,
-                                                     &result, "STATUS %s (MESSAGES)", folder_path);
+                                                     &result, ex, "STATUS %s (MESSAGES)", folder_path);
        else
                status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), folder,
-                                                     &result, "EXAMINE %s", folder_path);
+                                                     &result, ex, "EXAMINE %s", folder_path);
+
+       g_free (folder_path);
        
-       if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not get message count for %s from IMAP "
-                                     "server %s: %s.", folder_path, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
-               g_free (folder_path);
+       if (status != CAMEL_IMAP_OK)
                return 0;
-       }
-       g_free (folder_path);
        
        /* parse out the message count */
        if (result && *result == '*') {
@@ -582,45 +564,27 @@ imap_append_message (CamelFolder *folder, CamelMimeMessage *message, const Camel
        camel_stream_reset (memstream);
        
        status = camel_imap_command_preliminary (CAMEL_IMAP_STORE (folder->parent_store),
-                                                &result, &cmdid, "APPEND %s%s {%d}",
+                                                &cmdid, ex, "APPEND %s%s {%d}",
                                                 folder_path, flagstr ? flagstr : "", ba->len - 2);
+       g_free (folder_path);
        
        if (status != CAMEL_IMAP_PLUS) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not APPEND message to IMAP server %s: %s.",
-                                     service->url->host, result ? result : "Unknown error");
-               
-               g_free (result);
                g_free (cmdid);
-               g_free (folder_path);
                return;
        }
        
-       g_free (result);
-       g_free (folder_path);
-       
        /* send the rest of our data - the mime message */
        status = camel_imap_command_continuation_with_stream (CAMEL_IMAP_STORE (folder->parent_store),
-                                                             &result, cmdid, memstream);
+                                                             &result, cmdid, memstream, ex);
+       g_free (cmdid);
        
-       if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not APPEND message to IMAP server %s: %s.",
-                                     service->url->host, result ? result : "Unknown error");
-               
-               camel_object_unref (CAMEL_OBJECT (memstream));
-               g_free (result);
-               g_free (cmdid);
+       if (status != CAMEL_IMAP_OK)
                return;
-       }
-       
-       camel_object_unref (CAMEL_OBJECT (memstream));
-       g_free (cmdid);
+
        g_free (result);
        
-       camel_imap_folder_changed (folder, 1, NULL, ex);
+       camel_object_unref (CAMEL_OBJECT (memstream));
+               camel_imap_folder_changed (folder, 1, NULL, ex);
 }
 
 static void
@@ -638,23 +602,14 @@ imap_copy_message_to (CamelFolder *source, const char *uid, CamelFolder *destina
        else
                folder_path = g_strdup (destination->full_name);
        
-       status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), source, &result,
+       status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), source, &result, ex,
                                              "UID COPY %s %s", uid, folder_path);
+       g_free (folder_path);
        
-       if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not COPY message %s to %s on IMAP server %s: %s.",
-                                     uid, folder_path, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
-               g_free (folder_path);
+       if (status != CAMEL_IMAP_OK)
                return;
-       }
        
        g_free (result);
-       g_free (folder_path);
        
        camel_imap_folder_changed (destination, 1, NULL, ex);
 }
@@ -676,23 +631,14 @@ imap_move_message_to (CamelFolder *source, const char *uid, CamelFolder *destina
        else
                folder_path = g_strdup (destination->full_name);
        
-       status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), source, &result,
+       status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), source, &result, ex,
                                              "UID COPY %s %s", uid, folder_path);
+       g_free (folder_path);
        
-       if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not COPY message %s to %s on IMAP server %s: %s.",
-                                     uid, folder_path, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
-               g_free (folder_path);
+       if (status != CAMEL_IMAP_OK)
                return;
-       }
-       
+
        g_free (result);
-       g_free (folder_path);
        
        if (!(info = (CamelMessageInfo *)imap_get_message_info (source, uid))) {
                CamelService *service = CAMEL_SERVICE (store);
@@ -779,17 +725,10 @@ imap_get_subfolder_names_internal (CamelFolder *folder, CamelException *ex)
        }
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), NULL,
-                                             &result, "LIST \"\" \"%s%s*\"", namespace,
+                                             &result, ex, "LIST \"\" \"%s%s*\"", namespace,
                                              *namespace ? dir_sep : "");
        
        if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not get subfolder listing from IMAP "
-                                     "server %s: %s.", service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
                g_free (namespace);
                
                imap_folder->lsub = g_ptr_array_new ();
@@ -871,20 +810,11 @@ imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
                data_item = "RFC822.HEADER";
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                             &result, "UID FETCH %s %s", uid,
+                                             &result, ex, "UID FETCH %s %s", uid,
                                              data_item);
        
-       if (!result || status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not fetch message %s on IMAP server %s: %s",
-                                     uid, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
+       if (!result || status != CAMEL_IMAP_OK)
                return NULL;
-       }
        
        /* parse out the message part */
        for (p = result; *p && *p != '{' && *p != '"' && *p != '\n'; p++);
@@ -933,18 +863,10 @@ imap_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
                data_item = "RFC822.TEXT";
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                             &result, "UID FETCH %s %s", uid,
+                                             &result, ex, "UID FETCH %s %s", uid,
                                              data_item);
        
        if (!result || status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not fetch message %s on IMAP server %s: %s",
-                                     uid, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
                g_free (header);
                return NULL;
        }
@@ -1111,23 +1033,14 @@ imap_get_summary_internal (CamelFolder *folder, CamelException *ex)
        
        if (num == 1) {
                status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                                     &result, "FETCH 1 (%s)", summary_specifier);
+                                                     &result, ex, "FETCH 1 (%s)", summary_specifier);
        } else {
                status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                                     &result, "FETCH 1:%d (%s)", num, summary_specifier);
+                                                     &result, ex, "FETCH 1:%d (%s)", num, summary_specifier);
        }
        g_free (summary_specifier);
        
        if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not get summary for %s on IMAP server %s: %s",
-                                     folder->full_name, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
-               
                if (!imap_folder->summary) {
                        imap_folder->summary = g_ptr_array_new ();
                        imap_folder->summary_hash = g_hash_table_new (g_str_hash, g_str_equal);
@@ -1293,7 +1206,7 @@ imap_get_summary (CamelFolder *folder)
 
 /* get a single message info from the server */
 static CamelMessageInfo *
-imap_get_message_info_internal (CamelFolder *folder, guint id)
+imap_get_message_info_internal (CamelFolder *folder, guint id, CamelException *ex)
 {
        CamelMessageInfo *info = NULL;
        struct _header_raw *h, *tail = NULL;
@@ -1306,14 +1219,12 @@ imap_get_message_info_internal (CamelFolder *folder, guint id)
        summary_specifier = imap_protocol_get_summary_specifier (folder);
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                             &result, "FETCH %d (%s)", id, summary_specifier);
+                                             &result, ex, "FETCH %d (%s)", id, summary_specifier);
        
        g_free (summary_specifier);
        
-       if (status != CAMEL_IMAP_OK) {
-               g_free (result);
+       if (status != CAMEL_IMAP_OK)
                return NULL;
-       }
        
        /* lets grab the UID... */
        if (!(uid = (char *) e_strstrcase (result, "UID "))) {
@@ -1456,17 +1367,9 @@ imap_search_by_expression (CamelFolder *folder, const char *expression, CamelExc
        }
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), folder,
-                                             &result, "UID SEARCH %s", sexp);
+                                             &result, ex, "UID SEARCH %s", sexp);
        
        if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not get summary for %s on IMAP server %s: %s",
-                                     folder->full_name, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
                g_free (sexp);
                return uids;
        }
@@ -1594,7 +1497,7 @@ camel_imap_folder_changed (CamelFolder *folder, gint recent, GPtrArray *expunged
                last = imap_folder->summary->len + 1;
                
                for (i = last, j = 0; j < recent; i++, j++) {
-                       info = imap_get_message_info_internal (folder, i);
+                       info = imap_get_message_info_internal (folder, i, ex);
                        if (info) {
                                g_ptr_array_add (imap_folder->summary, info);
                                g_hash_table_insert (imap_folder->summary_hash, info->uid, info);
index 9e57dc0..3830e37 100644 (file)
 
 #include <config.h>
 
-#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 <e-util/e-util.h>
 
 /* Specified in RFC 2060 */
 #define IMAP_PORT 143
 
-static CamelServiceClass *service_class = NULL;
+static CamelRemoteStoreClass *remote_store_class = NULL;
 
-static void finalize (CamelObject *object);
 static gboolean imap_create (CamelFolder *folder, CamelException *ex);
-static gboolean imap_connect (CamelService *service, CamelException *ex);
-static gboolean imap_disconnect (CamelService *service, CamelException *ex);
+static void imap_post_connect (CamelRemoteStore *remote_store, CamelException *ex);
+static void imap_pre_disconnect (CamelRemoteStore *remote_store, CamelException *ex);
 static GList *query_auth_types_generic (CamelService *service, CamelException *ex);
 static GList *query_auth_types_connected (CamelService *service, CamelException *ex);
-static void free_auth_types (CamelService *service, GList *authtypes);
-static char *get_name (CamelService *service, gboolean brief);
 static CamelFolder *get_folder (CamelStore *store, const char *folder_name, gboolean create,
                                CamelException *ex);
-static char *get_folder_name (CamelStore *store, const char *folder_name, CamelException *ex);
-static gboolean imap_noop (gpointer data);
+static void imap_keepalive (CamelRemoteStore *store);
 /*static gboolean stream_is_alive (CamelStream *istream);*/
 static int camel_imap_status (char *cmdid, char *respbuf);
 
@@ -79,35 +70,35 @@ camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class)
                CAMEL_SERVICE_CLASS (camel_imap_store_class);
        CamelStoreClass *camel_store_class =
                CAMEL_STORE_CLASS (camel_imap_store_class);
+       CamelRemoteStoreClass *camel_remote_store_class =
+               CAMEL_REMOTE_STORE_CLASS (camel_imap_store_class);
        
-       service_class = CAMEL_SERVICE_CLASS(camel_type_get_global_classfuncs (camel_service_get_type ()));
+       remote_store_class = CAMEL_REMOTE_STORE_CLASS(camel_type_get_global_classfuncs 
+                                                     (camel_remote_store_get_type ()));
 
        /* virtual method overload */
-       camel_service_class->connect = imap_connect;
-       camel_service_class->disconnect = imap_disconnect;
        camel_service_class->query_auth_types_generic = query_auth_types_generic;
        camel_service_class->query_auth_types_connected = query_auth_types_connected;
-       camel_service_class->free_auth_types = free_auth_types;
-       camel_service_class->get_name = get_name;
 
        camel_store_class->get_folder = get_folder;
-       camel_store_class->get_folder_name = get_folder_name;
+
+       camel_remote_store_class->post_connect = imap_post_connect;
+       camel_remote_store_class->pre_disconnect = imap_pre_disconnect;
+       camel_remote_store_class->keepalive = imap_keepalive;
 }
 
 static void
 camel_imap_store_init (gpointer object, gpointer klass)
 {
        CamelService *service = CAMEL_SERVICE (object);
-       CamelStore *store = CAMEL_STORE (object);
+       CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
 
-       service->url_flags = (CAMEL_SERVICE_URL_NEED_USER |
-                             CAMEL_SERVICE_URL_NEED_HOST |
-                             CAMEL_SERVICE_URL_ALLOW_PATH);
+       service->url_flags |= (CAMEL_SERVICE_URL_NEED_USER |
+                              CAMEL_SERVICE_URL_NEED_HOST |
+                              CAMEL_SERVICE_URL_ALLOW_PATH);
 
-       store->folders = g_hash_table_new (g_str_hash, g_str_equal);
-       CAMEL_IMAP_STORE (store)->dir_sep = g_strdup ("/"); /*default*/
-       CAMEL_IMAP_STORE (store)->current_folder = NULL;
-       CAMEL_IMAP_STORE (store)->timeout_id = 0;
+       imap_store->dir_sep = g_strdup ("/"); /*default*/
+       imap_store->current_folder = NULL;
 }
 
 CamelType
@@ -116,31 +107,18 @@ camel_imap_store_get_type (void)
        static CamelType camel_imap_store_type = CAMEL_INVALID_TYPE;
        
        if (camel_imap_store_type == CAMEL_INVALID_TYPE)        {
-               camel_imap_store_type = camel_type_register (CAMEL_STORE_TYPE, "CamelImapStore",
+               camel_imap_store_type = camel_type_register (CAMEL_REMOTE_STORE_TYPE, "CamelImapStore",
                                                             sizeof (CamelImapStore),
                                                             sizeof (CamelImapStoreClass),
                                                             (CamelObjectClassInitFunc) camel_imap_store_class_init,
                                                             NULL,
                                                             (CamelObjectInitFunc) camel_imap_store_init,
-                                                            (CamelObjectFinalizeFunc) finalize);
+                                                            (CamelObjectFinalizeFunc) NULL);
        }
        
        return camel_imap_store_type;
 }
 
-static void
-finalize (CamelObject *object)
-{
-       /* Done for us now */
-       /*
-        *CamelException ex;
-        *
-        *camel_exception_init (&ex);
-        *imap_disconnect (CAMEL_SERVICE (object), &ex);
-        *camel_exception_clear (&ex);
-        */
-}
-
 static CamelServiceAuthType password_authtype = {
        "Password",
        
@@ -208,6 +186,7 @@ query_auth_types_connected (CamelService *service, CamelException *ex)
        return ret;
 #else
        g_warning ("imap::query_auth_types_connected: not implemented. Defaulting.");
+       /* FIXME: use the classfunc instead of the local? */
        return query_auth_types_generic (service, ex);
 #endif
 }
@@ -215,90 +194,30 @@ query_auth_types_connected (CamelService *service, CamelException *ex)
 static GList *
 query_auth_types_generic (CamelService *service, CamelException *ex)
 {
-       return g_list_append (NULL, &password_authtype);
-}
+       GList *prev;
 
-static void
-free_auth_types (CamelService *service, GList *authtypes)
-{
-       g_list_free (authtypes);
+       prev = CAMEL_SERVICE_CLASS (remote_store_class)->query_auth_types_generic (service, ex);
+       return g_list_prepend (prev, &password_authtype);
 }
 
-static char *
-get_name (CamelService *service, gboolean brief)
+static void
+imap_post_connect (CamelRemoteStore *remote_store, CamelException *ex)
 {
-       if (brief)
-               return g_strdup_printf ("IMAP server %s", service->url->host);
-       else {
-               return g_strdup_printf ("IMAP service for %s on %s",
-                                       service->url->user,
-                                       service->url->host);
-       }
-}
+       CamelService *service = CAMEL_SERVICE (remote_store);
+       CamelImapStore *store = CAMEL_IMAP_STORE (remote_store);
+       CamelSession *session = camel_service_get_session (CAMEL_SERVICE (store));
 
-static gboolean
-imap_connect (CamelService *service, CamelException *ex)
-{
-       CamelImapStore *store = CAMEL_IMAP_STORE (service);
-       struct hostent *h;
-       struct sockaddr_in sin;
-       gint fd, status;
-       gchar *buf, *msg, *result, *errbuf = NULL;
+       gint status;
+       gchar *buf, *result, *errbuf = NULL;
        gboolean authenticated = FALSE;
 
-       /* FIXME: do we really need this here? */
-       /*
-        *if (store->timeout_id) {
-        *      gtk_timeout_remove (store->timeout_id);
-        *      store->timeout_id = 0;
-        *}
-        */
-
-       h = camel_service_gethost (service, ex);
-       if (!h)
-               return FALSE;
-
-       /* connect to the IMAP server */
-       sin.sin_family = h->h_addrtype;
-       if (service->url->port)
-               sin.sin_port = htons(service->url->port);
-       else
-               sin.sin_port = htons(IMAP_PORT);
-       
-       memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr));
-       
-       fd = socket (h->h_addrtype, SOCK_STREAM, 0);
-       if (fd == -1 || connect (fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not connect to %s (port %d): %s",
-                                     service->url->host ? service->url->host : "(unknown host)", 
-                                     service->url->port ? service->url->port : IMAP_PORT,
-                                     strerror(errno));
-               if (fd > -1)
-                       close (fd);
-               
-               return FALSE;
-       }
-
-       /* parent class conect initialization */
-       service_class->connect (service, ex);
-       
-       store->ostream = camel_stream_fs_new_with_fd (fd);
-       store->istream = camel_stream_buffer_new (store->ostream, CAMEL_STREAM_BUFFER_READ);
        store->command = 0;
        g_free (store->dir_sep);
        store->dir_sep = g_strdup ("/");  /* default dir sep */
        
        /* Read the greeting, if any. */
-       buf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream));
-       if (!buf) {
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not read greeting from IMAP "
-                                     "server: %s",
-                                     camel_exception_get_description (ex));
-               
-               imap_disconnect (service, ex);
-               return FALSE;
+       if (camel_remote_store_recv_line (remote_store, &buf, ex) < 0) {
+               return;
        }
        g_free (buf);
 
@@ -306,7 +225,7 @@ imap_connect (CamelService *service, CamelException *ex)
        while (!authenticated) {
                if (errbuf) {
                        /* We need to un-cache the password before prompting again */
-                       camel_session_query_authenticator (camel_service_get_session (service),
+                       camel_session_query_authenticator (session,
                                                           CAMEL_AUTHENTICATOR_TELL, NULL,
                                                           TRUE, service, "password", ex);
                        g_free (service->url->passwd);
@@ -317,9 +236,9 @@ imap_connect (CamelService *service, CamelException *ex)
                        gchar *prompt;
                        
                        prompt = g_strdup_printf ("%sPlease enter the IMAP password for %s@%s",
-                                                 errbuf ? errbuf : "", service->url->user, h->h_name);
+                                                 errbuf ? errbuf : "", service->url->user, service->url->host);
                        service->url->passwd =
-                               camel_session_query_authenticator (camel_service_get_session (service),
+                               camel_session_query_authenticator (session,
                                                                   CAMEL_AUTHENTICATOR_ASK, prompt,
                                                                   TRUE, service, "password", ex);
                        g_free (prompt);
@@ -327,19 +246,19 @@ imap_connect (CamelService *service, CamelException *ex)
                        errbuf = NULL;
                        
                        if (!service->url->passwd) {
-                               imap_disconnect (service, ex);
-                               return FALSE;
+                               camel_service_disconnect (service, ex);
+                               return;
                        }
                }
 
-               status = camel_imap_command (store, NULL, &msg, "LOGIN \"%s\" \"%s\"",
+               status = camel_imap_command (store, NULL, ex, "LOGIN \"%s\" \"%s\"",
                                             service->url->user,
                                             service->url->passwd);
 
                if (status != CAMEL_IMAP_OK) {
                        errbuf = g_strdup_printf ("Unable to authenticate to IMAP server.\n"
-                                                 "Error sending password: %s\n\n",
-                                                 msg ? msg : "(Unknown)");
+                                                 "%s\n\n",
+                                                 camel_exception_get_description (ex));
                } else {
                        g_message ("IMAP Service sucessfully authenticated user %s", service->url->user);
                        authenticated = TRUE;
@@ -347,17 +266,10 @@ imap_connect (CamelService *service, CamelException *ex)
        }
        
        /* Now lets find out the IMAP capabilities */
-       status = camel_imap_command_extended (store, NULL, &result, "CAPABILITY");
+       status = camel_imap_command_extended (store, NULL, &result, ex, "CAPABILITY");
        
        if (status != CAMEL_IMAP_OK) {
-               /* Non-fatal error, but we should still warn the user... */
-               CamelService *service = CAMEL_SERVICE (store);
-               
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not get capabilities on IMAP server %s: %s.",
-                                     service->url->host, 
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
+               /* Non-fatal error... (ex is set) */
        }
 
        /* parse for capabilities here. */
@@ -376,17 +288,10 @@ imap_connect (CamelService *service, CamelException *ex)
        g_free (result);
        
        /* We now need to find out which directory separator this daemon uses */
-       status = camel_imap_command_extended (store, NULL, &result, "LIST \"\" \"\"");
+       status = camel_imap_command_extended (store, NULL, &result, ex, "LIST \"\" \"\"");
        
        if (status != CAMEL_IMAP_OK) {
                /* Again, this is non-fatal */
-               CamelService *service = CAMEL_SERVICE (store);
-               
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not get directory separator on IMAP server %s: %s.",
-                                     service->url->host, 
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
        } else {
                char *flags, *sep, *folder;
                
@@ -402,65 +307,31 @@ imap_connect (CamelService *service, CamelException *ex)
                g_free (folder);
        }
        
-       /* default directory separator */
-       if (!store->dir_sep)
-               store->dir_sep = g_strdup ("/");
-       
        g_free (result);
 
-       /* Lets add a timeout so that we can hopefully prevent getting disconnected */
-       /* FIXME fast timeout */
-       store->timeout_id = camel_session_register_timeout (camel_service_get_session (service), 
-                                                           10 * 60 * 1000, imap_noop, service);
-       /*store->timeout_id = gtk_timeout_add (600000, imap_noop, store);*/
-       
-       return TRUE;
+       CAMEL_REMOTE_STORE_CLASS (remote_store_class)->post_connect (remote_store, ex);
 }
 
-static gboolean
-imap_disconnect (CamelService *service, CamelException *ex)
+static void
+imap_pre_disconnect (CamelRemoteStore *remote_store, CamelException *ex)
 {
-       CamelImapStore *store = CAMEL_IMAP_STORE (service);
+       CamelImapStore *store = CAMEL_IMAP_STORE (remote_store);
        char *result;
        int status;
        
-
-       /*if (!service->connected)
-        *      return TRUE;
-        */
-
        /* send the logout command */
-       status = camel_imap_command_extended (CAMEL_IMAP_STORE (service), NULL, &result, "LOGOUT");
+       status = camel_imap_command_extended (store, NULL, &result, ex, "LOGOUT");
        if (status != CAMEL_IMAP_OK) {
                /* Oh fuck it, we're disconnecting anyway... */
        }
        g_free (result);
        
-       if (!service_class->disconnect (service, ex))
-               return FALSE;
-
-       if (store->istream) {
-               camel_object_unref (CAMEL_OBJECT (store->istream));
-               store->istream = NULL;
-       }
-
-       if (store->ostream) {
-               camel_object_unref (CAMEL_OBJECT (store->ostream));
-               store->ostream = NULL;
-       }
-
        g_free (store->dir_sep);
        store->dir_sep = NULL;
 
        store->current_folder = NULL;
 
-       if (store->timeout_id) {
-               camel_session_remove_timeout (camel_service_get_session (CAMEL_SERVICE (store)),
-                                             store->timeout_id);
-               store->timeout_id = 0;
-       }
-
-       return TRUE;
+       CAMEL_REMOTE_STORE_CLASS (remote_store_class)->pre_disconnect (remote_store, ex);
 }
 
 const gchar *
@@ -473,7 +344,7 @@ camel_imap_store_get_toplevel_dir (CamelImapStore *store)
 }
 
 static gboolean
-imap_folder_exists (CamelFolder *folder)
+imap_folder_exists (CamelFolder *folder, CamelException *ex)
 {
        CamelStore *store = CAMEL_STORE (folder->parent_store);
        CamelURL *url = CAMEL_SERVICE (store)->url;
@@ -490,7 +361,7 @@ imap_folder_exists (CamelFolder *folder)
                folder_path = g_strdup (folder->full_name);
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), NULL,
-                                             &result, "EXAMINE %s", folder_path);
+                                             &result, ex, "EXAMINE %s", folder_path);
        
        if (status != CAMEL_IMAP_OK) {
                g_free (result);
@@ -522,7 +393,7 @@ imap_create (CamelFolder *folder, CamelException *ex)
        if (!strcmp (folder->full_name, "INBOX"))
                return TRUE;
        
-       if (imap_folder_exists (folder))
+       if (imap_folder_exists (folder, ex))
                return TRUE;
        
         /* create the directory for the subfolder */
@@ -536,27 +407,17 @@ imap_create (CamelFolder *folder, CamelException *ex)
                folder_path = g_strdup (folder->full_name);
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store), NULL,
-                                             &result, "CREATE %s", folder_path);
+                                             &result, ex, "CREATE %s", folder_path);
+       g_free (folder_path);
        
-       if (status != CAMEL_IMAP_OK) {
-               CamelService *service = CAMEL_SERVICE (folder->parent_store);
-               camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-                                     "Could not CREATE %s on IMAP server %s: %s.",
-                                     folder_path, service->url->host,
-                                     status != CAMEL_IMAP_FAIL && result ? result :
-                                     "Unknown error");
-               g_free (result);
-               g_free (folder_path);
+       if (status != CAMEL_IMAP_OK)
                return FALSE;
-       }
-       g_free (folder_path);
-       g_free (result);
        
        return TRUE;
 }
 
 static gboolean
-folder_is_selectable (CamelStore *store, const char *folder_path)
+folder_is_selectable (CamelStore *store, const char *folder_path, CamelException *ex)
 {
        char *result, *flags, *sep, *folder;
        int status;
@@ -565,11 +426,9 @@ folder_is_selectable (CamelStore *store, const char *folder_path)
                return TRUE;
        
        status = camel_imap_command_extended (CAMEL_IMAP_STORE (store), NULL,
-                                             &result, "LIST \"\" %s", folder_path);
-       if (status != CAMEL_IMAP_OK) {
-               g_free (result);
+                                             &result, ex, "LIST \"\" %s", folder_path);
+       if (status != CAMEL_IMAP_OK)
                return FALSE;
-       }
        
        if (imap_parse_list_response (result, "", &flags, &sep, &folder)) {
                gboolean retval;
@@ -613,7 +472,7 @@ get_folder (CamelStore *store, const char *folder_name, gboolean create, CamelEx
                return new_folder;
        
        if (create && !imap_create (new_folder, ex)) {
-               if (!folder_is_selectable (store, folder_path)) {
+               if (!folder_is_selectable (store, folder_path, ex)) {
                        camel_exception_clear (ex);
                        new_folder->can_hold_messages = FALSE;
                        return new_folder;
@@ -627,24 +486,20 @@ get_folder (CamelStore *store, const char *folder_name, gboolean create, CamelEx
        return new_folder;
 }
 
-static gchar *
-get_folder_name (CamelStore *store, const char *folder_name, CamelException *ex)
-{
-       return g_strdup (folder_name);
-}
-
-static gboolean
-imap_noop (gpointer data)
+static void
+imap_keepalive (CamelRemoteStore *store)
 {
-       CamelImapStore *store = CAMEL_IMAP_STORE (data);
+       CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
        char *result;
        int status;
+       CamelException ex;
 
-       status = camel_imap_command_extended (store, store->current_folder, &result, "NOOP");
-
-       g_free (result);
-
-       return TRUE;
+       camel_exception_init (&ex);
+       status = camel_imap_command_extended (imap_store, imap_store->current_folder, 
+                                             &result, &ex, "NOOP");
+       camel_exception_clear (&ex);
+       if (result)
+               g_free (result);
 }
 
 #if 0
@@ -687,11 +542,223 @@ camel_imap_status (char *cmdid, char *respbuf)
        return CAMEL_IMAP_FAIL;
 }
 
+static gint
+check_current_folder (CamelImapStore *store, CamelFolder *folder, char *fmt, CamelException *ex)
+{
+       CamelURL *url = CAMEL_SERVICE (store)->url;
+       char *r, *folder_path, *dir_sep;
+       int s;
+
+       if (!folder)
+               return CAMEL_IMAP_OK;
+       if (store->current_folder == folder)
+               return CAMEL_IMAP_OK;
+       if (strncmp (fmt, "CREATE", 5) == 0)
+               return CAMEL_IMAP_OK;
+
+       dir_sep = store->dir_sep;
+
+       if (url && url->path && *(url->path + 1) && strcmp (folder->full_name, "INBOX"))
+               folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, folder->full_name);
+       else
+               folder_path = g_strdup (folder->full_name);
+               
+       s = camel_imap_command_extended (store, NULL, &r, ex, "SELECT %s", folder_path);
+       g_free (folder_path);
+
+       if (!r || s != CAMEL_IMAP_OK) {
+               store->current_folder = NULL;
+               return s;
+       }
+       
+       g_free (r);
+       store->current_folder = folder;
+       return CAMEL_IMAP_OK;
+}
+
+static gint
+send_command (CamelImapStore *store, char **cmdid, char *fmt, va_list ap, CamelException *ex)
+{
+       gchar *cmdbuf;
+
+       (*cmdid) = g_strdup_printf ("A%.5d", store->command++);
+
+       cmdbuf = g_strdup_vprintf (fmt, ap);
+       
+       if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, "%s %s\r\n", (*cmdid), cmdbuf) < 0) {
+               g_free (cmdbuf);
+               g_free ((*cmdid));
+               (*cmdid) = NULL;
+               return CAMEL_IMAP_FAIL;
+       }
+
+       g_free (cmdbuf);
+       return CAMEL_IMAP_OK;
+}
+
+static gint
+slurp_response (CamelImapStore *store, CamelFolder *folder, char *cmdid, char **ret, gboolean stop_on_plus, CamelException *ex)
+{
+       gint status = CAMEL_IMAP_OK;
+       GPtrArray *data, *expunged;
+       gchar *respbuf;
+       guint32 len = 0;
+       gint recent = 0;
+       gint i;
+
+       data = g_ptr_array_new();
+       expunged = g_ptr_array_new();
+
+       while (1) {
+               if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0)
+                       return CAMEL_IMAP_FAIL;
+
+               g_ptr_array_add (data, respbuf);
+               len += strlen (respbuf) + 1;
+
+               /* IMAP's last response starts with our command id or, sometimes, a plus */
+
+               if (stop_on_plus && *respbuf == '+') {
+                       status = CAMEL_IMAP_PLUS;
+                       break;
+               }
+
+               if (!strncmp (respbuf, cmdid, strlen (cmdid))) {
+                       status = camel_imap_status (cmdid, respbuf);
+                       break;
+               }
+
+               /* If recent was somehow set and this response doesn't begin with a '*'
+                  then recent must have been misdetected */
+               if (recent && *respbuf != '*')
+                       recent = 0;
+
+               /* Check for a RECENT in the untagged response */
+               if (*respbuf == '*') {
+                       if (strstr (respbuf, "RECENT")) {
+                               char *rcnt;
+                       
+                               d(fprintf (stderr, "*** We may have found a 'RECENT' flag: %s\n", respbuf));
+                               /* Make sure it's in the form: "* %d RECENT" */
+                               rcnt = imap_next_word (respbuf);
+                               if (*rcnt >= '0' && *rcnt <= '9' && !strncmp ("RECENT", imap_next_word (rcnt), 6))
+                                       recent = atoi (rcnt);
+                       } else if (strstr (respbuf, "EXPUNGE")) {
+                               char *id_str;
+                               int id;
+                               
+                               d(fprintf (stderr, "*** We may have found an 'EXPUNGE' flag: %s\n", respbuf));
+                               /* Make sure it's in the form: "* %d EXPUNGE" */
+                               id_str = imap_next_word (respbuf);
+                               if (*id_str >= '0' && *id_str <= '9' && !strncmp ("EXPUNGE", imap_next_word (id_str), 7)) {
+                                       id = atoi (id_str);
+                                       g_ptr_array_add (expunged, g_strdup_printf ("%d", id));
+                               }
+                       }
+               }
+       }
+       
+       /* Apply the 'recent' changes */
+       if (folder && recent > 0)
+               camel_imap_folder_changed (folder, recent, expunged, ex);
+
+       if (status == CAMEL_IMAP_OK || status == CAMEL_IMAP_PLUS) {
+               gchar *p;
+
+               /* Command succeeded! Put the output into one big
+                * string of love. */
+
+               (*ret) = g_new (char, len + 1);
+               p = (*ret);
+               
+               for (i = 0; i < data->len; i++) {
+                       char *datap;
+
+                       datap = (char *) data->pdata[i];
+                       if (*datap == '.')
+                               datap++;
+                       len = strlen (datap);
+                       memcpy (p, datap, len);
+                       p += len;
+                       *p++ = '\n';
+               }
+
+               *p = '\0';
+       } else {
+               /* Bummer. Try to grab what the server said. */
+               if (respbuf) {
+                       char *word;
+
+                       word = imap_next_word (respbuf);
+
+                       if (*respbuf != '-')
+                               word = imap_next_word (word);
+
+                       camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+                                             "IMAP command failed: %s",
+                                             word);
+               }
+
+               *ret = NULL;
+       }
+       
+       /* Can this be put into the 'if succeeded' bit?
+        * Or can a failed command generate untagged responses? */
+
+       for (i = 0; i < data->len; i++)
+               g_free (data->pdata[i]);
+       g_ptr_array_free (data, TRUE);
+
+       for (i = 0; i < expunged->len; i++)
+               g_free (expunged->pdata[i]);
+       g_ptr_array_free (expunged, TRUE);
+       
+       return status;
+}
+
+/* frees cmdid! */
+static gint
+parse_single_line (CamelImapStore *store, char *cmdid, CamelException *ex)
+{
+       char *respbuf;
+       gint status;
+       char *word;
+
+       if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0)
+               return CAMEL_IMAP_FAIL;
+
+       /* Assume that buf indeed starts with cmdid; then
+        * it can only start with a plus when the slurper
+        * found a valid plus. So no need to check for
+        * stop_on_plus.
+        */
+
+       if (*respbuf == '+') {
+               g_free (cmdid);
+               return CAMEL_IMAP_PLUS;
+       }
+
+       status = camel_imap_status (cmdid, respbuf);
+       g_free (cmdid);
+
+       if (status == CAMEL_IMAP_OK)
+               return status;
+
+       word = imap_next_word (respbuf);
+       if (*respbuf != '-')
+               word = imap_next_word (word);
+
+       camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+                             "IMAP command failed: %s", word);
+       return status;
+}
+
 /**
  * camel_imap_command: Send a command to a IMAP server.
  * @store: the IMAP store
  * @folder: The folder to perform the operation in
  * @ret: a pointer to return the full server response in
+ * @ex: a CamelException.
  * @fmt: a printf-style format string, followed by arguments
  * 
  * This camel method sends the command specified by @fmt and the following
@@ -709,84 +776,26 @@ camel_imap_status (char *cmdid, char *respbuf)
  * occurred, and Camel is uncertain of the result of the command.)
  **/
 gint
-camel_imap_command (CamelImapStore *store, CamelFolder *folder, char **ret, char *fmt, ...)
+camel_imap_command (CamelImapStore *store, CamelFolder *folder, CamelException *ex, char *fmt, ...)
 {
-       CamelURL *url = CAMEL_SERVICE (store)->url;
-       gchar *cmdbuf, *respbuf;
        gchar *cmdid;
        va_list ap;
        gint status = CAMEL_IMAP_OK;
-       
-       if (folder && store->current_folder != folder && strncmp (fmt, "CREATE", 5)) {
-               /* We need to select the correct mailbox first */
-               char *r, *folder_path, *dir_sep;
-               int s;
-               
-               dir_sep = store->dir_sep;
-               if (url && url->path && *(url->path + 1) && strcmp (folder->full_name, "INBOX"))
-                       folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, folder->full_name);
-               else
-                       folder_path = g_strdup (folder->full_name);
-               
-               s = camel_imap_command_extended (store, NULL, &r, "SELECT %s", folder_path);
-               g_free (folder_path);
-               if (!r || s != CAMEL_IMAP_OK) {
-                       *ret = r;
-                       store->current_folder = NULL;
-                       
-                       return s;
-               }
-               
-               g_free (r);
-               
-               store->current_folder = folder;
-       }
-       
-       /* create the command */
-       cmdid = g_strdup_printf ("A%.5d", store->command++);
+
+       /* check for current folder */
+       status = check_current_folder (store, folder, fmt, ex);
+       if (status != CAMEL_IMAP_OK)
+               return status;
+
+       /* send the command */
        va_start (ap, fmt);
-       cmdbuf = g_strdup_vprintf (fmt, ap);
+       status = send_command (store, &cmdid, fmt, ap, ex);
        va_end (ap);
-       
-       d(fprintf (stderr, "sending : %s %s\r\n", cmdid, cmdbuf));
-       
-       if (camel_stream_printf (store->ostream, "%s %s\r\n", cmdid, cmdbuf) == -1) {
-               g_free (cmdbuf);
-               g_free (cmdid);
-               if (*ret)
-                       *ret = g_strdup (strerror (errno));
-               return CAMEL_IMAP_FAIL;
-       }
-       g_free (cmdbuf);
-       
-       /* Read the response */
-       respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream));
-       if (respbuf == NULL) {
-               if (*ret)
-                       *ret = g_strdup (strerror (errno));
-               return CAMEL_IMAP_FAIL;
-       }
-       
-       d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
-       
-       status = camel_imap_status (cmdid, respbuf);
-       g_free (cmdid);
+       if (status != CAMEL_IMAP_OK)
+               return status;
 
-       if (ret) {
-               if (status != CAMEL_IMAP_FAIL && respbuf) {
-                       char *word;
-                       
-                       word = imap_next_word (respbuf); /* word should now point to NO or BAD */
-                       
-                       *ret = g_strdup (imap_next_word (word));
-               } else {
-                       *ret = NULL;
-               }
-       }
-
-       g_free (respbuf);
-       
-       return status;
+       /* Read the response */
+       return parse_single_line (store, cmdid, ex);
 }
 
 /**
@@ -817,202 +826,32 @@ camel_imap_command (CamelImapStore *store, CamelFolder *folder, char **ret, char
  * message from the server), or CAMEL_IMAP_FAIL (a protocol-level error
  * occurred, and Camel is uncertain of the result of the command.)
  **/
+
 gint
-camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, char **ret, char *fmt, ...)
+camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, char **ret, CamelException *ex, char *fmt, ...)
 {
-       CamelService *service = CAMEL_SERVICE (store);
-       CamelURL *url = service->url;
-       gint len = 0, recent = 0, status = CAMEL_IMAP_OK;
-       gchar *cmdid, *cmdbuf, *respbuf;
-       GPtrArray *data, *expunged = NULL;
-       va_list app;
-       int i;
-       
-#if 0   
-       /* First make sure we're connected... */
-       if (!service->connected || !stream_is_alive (store->istream)) {
-               CamelException *ex;
-               
-               ex = camel_exception_new ();
-               
-               if (!imap_disconnect (service, ex) || !imap_connect (service, ex)) {
-                       camel_exception_free (ex);
-                       
-                       *ret = NULL;
-                       
-                       return CAMEL_IMAP_FAIL;
-               }
-               service->connected = TRUE;
-               
-               camel_exception_free (ex);
-       }
-#endif  
-       
-       if (folder && store->current_folder != folder && strncmp (fmt, "CREATE", 6)) {
-               /* We need to select the correct mailbox first */
-               char *r, *folder_path, *dir_sep;
-               int s;
-               
-               dir_sep = store->dir_sep;
-               
-               if (url && url->path && *(url->path + 1) && strcmp (folder->full_name, "INBOX"))
-                       folder_path = g_strdup_printf ("%s%s%s", url->path + 1, dir_sep, folder->full_name);
-               else
-                       folder_path = g_strdup (folder->full_name);
-               
-               s = camel_imap_command_extended (store, NULL, &r, "SELECT %s", folder_path);
-               g_free (folder_path);
-               if (!r || s != CAMEL_IMAP_OK) {
-                       *ret = r;
-                       store->current_folder = NULL;
-                       
-                       return s;
-               }
-               
-               g_free (r);
-               
-               store->current_folder = folder;
-       }
-       
-       /* Create the command */
-       cmdid = g_strdup_printf ("A%.5d", store->command++);
-       va_start (app, fmt);
-       cmdbuf = g_strdup_vprintf (fmt, app);
-       va_end (app);
-       
-       d(fprintf (stderr, "sending : %s %s\r\n", cmdid, cmdbuf));
-       
-       if (camel_stream_printf (store->ostream, "%s %s\r\n", cmdid, cmdbuf) == -1) {
-               g_free (cmdbuf);
-               g_free (cmdid);
-               
-               *ret = g_strdup (strerror (errno));
-               
-               return CAMEL_IMAP_FAIL;
-       }
-       g_free (cmdbuf);
-       
-       data = g_ptr_array_new ();
-       expunged = g_ptr_array_new ();
-       
-       while (1) {
-               CamelStreamBuffer *stream = CAMEL_STREAM_BUFFER (store->istream);
-               char *ptr;
-               
-               respbuf = camel_stream_buffer_read_line (stream);
-               if (!respbuf || !strncmp (respbuf, cmdid, strlen (cmdid))) {
-                       /* IMAP's last response starts with our command id */
-                       d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
-#if 0                  
-                       if (!respbuf && strcmp (fmt, "LOGOUT")) {
-                               /* we need to force a disconnect here? */
-                               CamelException *ex;
-                               
-                               ex = camel_exception_new ();
-                               imap_disconnect (service, ex);
-                               camel_exception_free (ex);
-                       }
-#endif  
-                       break;
-               }
-               
-               d(fprintf (stderr, "received: %s\n", respbuf));
-               
-               g_ptr_array_add (data, respbuf);
-               len += strlen (respbuf) + 1;
-               
-               /* If recent was somehow set and this response doesn't begin with a '*'
-                  then recent must have been misdetected */
-               if (recent && *respbuf != '*')
-                       recent = 0;
-               
-               if (*respbuf == '*') {
-                       if ((ptr = strstr (respbuf, "RECENT"))) {
-                               char *rcnt;
-                               
-                               d(fprintf (stderr, "*** We may have found a 'RECENT' flag: %s\n", respbuf));
-                               /* Make sure it's in the form: "* %d RECENT" */
-                               rcnt = imap_next_word (respbuf);
-                               if (*rcnt >= '0' && *rcnt <= '9' && !strncmp ("RECENT", imap_next_word (rcnt), 6))
-                                       recent = atoi (rcnt);
-                       } else if ((ptr = strstr (respbuf, "EXPUNGE"))) {
-                               char *id_str;
-                               int id;
-                               
-                               d(fprintf (stderr, "*** We may have found an 'EXPUNGE' flag: %s\n", respbuf));
-                               /* Make sure it's in the form: "* %d EXPUNGE" */
-                               id_str = imap_next_word (respbuf);
-                               if (*id_str >= '0' && *id_str <= '9' && !strncmp ("EXPUNGE", imap_next_word (id_str), 7)) {
-                                       id = atoi (id_str);
-                                       g_ptr_array_add (expunged, g_strdup_printf ("%d", id));
-                               }
-                       }
-               }
-       }
-       
-       if (respbuf) {
-               g_ptr_array_add (data, respbuf);
-               len += strlen (respbuf) + 1;
-               
-               status = camel_imap_status (cmdid, respbuf);
-       } else {
-               status = CAMEL_IMAP_FAIL;
-       }
-       g_free (cmdid);
-       
-       if (status == CAMEL_IMAP_OK) {
-               char *p;
-               
-               *ret = g_malloc0 (len + 1);
-               
-               for (i = 0, p = *ret; i < data->len; i++) {
-                       char *ptr, *datap;
-                       
-                       datap = (char *) data->pdata[i];
-                       ptr = (*datap == '.') ? datap + 1 : datap;
-                       len = strlen (ptr);
-                       memcpy (p, ptr, len);
-                       p += len;
-                       *p++ = '\n';
-               }
-               *p = '\0';
-       } else {
-               if (status != CAMEL_IMAP_FAIL && respbuf) {
-                       char *word;
-                       
-                       word = imap_next_word (respbuf); /* word should now point to NO or BAD */
-                       
-                       *ret = g_strdup (imap_next_word (word));
-               } else {
-                       *ret = NULL;
-               }
-       }
-       
-       for (i = 0; i < data->len; i++)
-               g_free (data->pdata[i]);
-       g_ptr_array_free (data, TRUE);
-       
-       if (folder && recent > 0) {
-               CamelException *ex;
-               
-               ex = camel_exception_new ();
-               camel_imap_folder_changed (folder, recent, expunged, ex);
-               
-               for (i = 0; i < expunged->len; i++)
-                       g_free (expunged->pdata[i]);
-               g_ptr_array_free (expunged, TRUE);
-               
-               camel_exception_free (ex);
-       }
-       
-       return status;
+       gint status = CAMEL_IMAP_OK;
+       gchar *cmdid;
+       va_list ap;
+
+       status = check_current_folder (store, folder, fmt, ex);
+       if (status != CAMEL_IMAP_OK)
+               return status;
+
+       /* send the command */
+       va_start (ap, fmt);
+       status = send_command (store, &cmdid, fmt, ap, ex);
+       va_end (ap);
+       if (status != CAMEL_IMAP_OK)
+               return status;
+
+       return slurp_response (store, folder, cmdid, ret, FALSE, ex);
 }
 
 /**
  * camel_imap_command_preliminary: Send a preliminary command to the
  * IMAP server.
  * @store: the IMAP store
- * @ret: a pointer to return the full server response in
  * @cmdid: a pointer to return the command identifier (for use in
  * camel_imap_command_continuation)
  * @fmt: a printf-style format string, followed by arguments
@@ -1027,57 +866,26 @@ camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, char **
  * containing the rest of the response from the IMAP server. The
  * caller function is responsible for freeing @ret.
  * 
- * Return value: one of CAMEL_IMAP_PLUS or CAMEL_IMAP_FAIL
+ * Return value: one of CAMEL_IMAP_PLUS, CAMEL_IMAP_OK,  or CAMEL_IMAP_FAIL
  * 
  * Note: on success (CAMEL_IMAP_PLUS), you will need to follow up with
  * a camel_imap_command_continuation call.
  **/
 gint
-camel_imap_command_preliminary (CamelImapStore *store, char **ret, char **cmdid, char *fmt, ...)
+camel_imap_command_preliminary (CamelImapStore *store, char **cmdid, CamelException *ex, char *fmt, ...)
 {
-       gchar *cmdbuf, *respbuf;
        gint status = CAMEL_IMAP_OK;
-       va_list app;
-       
-       /* Create the command */
-       *cmdid = g_strdup_printf ("A%.5d", store->command++);
-       va_start (app, fmt);
-       cmdbuf = g_strdup_vprintf (fmt, app);
-       va_end (app);
-       
-       d(fprintf (stderr, "sending : %s %s\r\n", *cmdid, cmdbuf));
-       
-       if (camel_stream_printf (store->ostream, "%s %s\r\n", *cmdid, cmdbuf) == -1) {
-               g_free (cmdbuf);
-               
-               if (ret)
-                       *ret = g_strdup (strerror (errno));
-               
-               return CAMEL_IMAP_FAIL;
-       }
-       g_free (cmdbuf);
-       
-       respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream));
-       
-       if (respbuf) {
-               switch (*respbuf) {
-               case '+':
-                       /* continuation request */
-                       status = CAMEL_IMAP_PLUS;
-                       break;
-               default:
-                       status = camel_imap_status (*cmdid, respbuf);
-               }
-               
-               if (ret)
-                       *ret = g_strdup (imap_next_word (respbuf));
-       } else {
-               status = CAMEL_IMAP_FAIL;
-               if (ret)
-                       *ret = NULL;
-       }
-       
-       return status;
+       va_list ap;
+
+       /* send the command */
+       va_start (ap, fmt);
+       status = send_command (store, cmdid, fmt, ap, ex);
+       va_end (ap);
+       if (status != CAMEL_IMAP_OK)
+               return status;
+
+       /* Read the response */
+       return parse_single_line (store, g_strdup (*cmdid), ex);
 }
 
 /**
@@ -1102,91 +910,15 @@ camel_imap_command_preliminary (CamelImapStore *store, char **ret, char **cmdid,
  * of the result of the command.)
  **/
 gint
-camel_imap_command_continuation (CamelImapStore *store, char **ret, char *cmdid, char *cmdbuf)
+camel_imap_command_continuation (CamelImapStore *store, char **ret, char *cmdid, char *cmdbuf, CamelException *ex)
 {
-       gint len = 0, status = CAMEL_IMAP_OK;
-       gchar *respbuf;
-       GPtrArray *data;
-       int i;
-       
-       d(fprintf (stderr, "sending : %s\r\n", cmdbuf));
-       
-       if (camel_stream_printf (store->ostream, "%s\r\n", cmdbuf) == -1) {
-               *ret = g_strdup (strerror (errno));
-               
-               return CAMEL_IMAP_FAIL;
-       }
-       
-       data = g_ptr_array_new ();
-       
-       while (1) {
-               CamelStreamBuffer *stream = CAMEL_STREAM_BUFFER (store->istream);
-               
-               respbuf = camel_stream_buffer_read_line (stream);
-               if (!respbuf || *respbuf == '+' || !strncmp (respbuf, cmdid, strlen (cmdid))) {
-                       /* IMAP's last response starts with our command id or a continuation request */
-                       d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
-                       
-                       break;
-               }
-               
-               d(fprintf (stderr, "received: %s\n", respbuf));
-               
-               g_ptr_array_add (data, respbuf);
-               len += strlen (respbuf) + 1;
-       }
-       
-       if (respbuf) {
-               g_ptr_array_add (data, respbuf);
-               len += strlen (respbuf) + 1;
-               
-               switch (*respbuf) {
-               case '+':
-                       status = CAMEL_IMAP_PLUS;
-                       break;
-               default:
-                       status = camel_imap_status (cmdid, respbuf);
-               }
-       } else {
-               status = CAMEL_IMAP_FAIL;
-       }
-       
-       if (status == CAMEL_IMAP_OK || status == CAMEL_IMAP_PLUS) {
-               char *p;
-               
-               *ret = g_malloc0 (len + 1);
-               
-               for (i = 0, p = *ret; i < data->len; i++) {
-                       char *ptr, *datap;
-                       
-                       datap = (char *) data->pdata[i];
-                       ptr = (*datap == '.') ? datap + 1 : datap;
-                       len = strlen (ptr);
-                       memcpy (p, ptr, len);
-                       p += len;
-                       *p++ = '\n';
-               }
-               *p = '\0';
-       } else {
-               if (status != CAMEL_IMAP_FAIL && respbuf) {
-                       char *word;
-                       
-                       word = imap_next_word (respbuf);
-                       
-                       if (*respbuf == '-')
-                               *ret = g_strdup (word);
-                       else
-                               *ret = g_strdup (imap_next_word (word));
-               } else {
+       if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, "%s\r\n", cmdbuf) < 0) {
+               if (ret)
                        *ret = NULL;
-               }
+               return CAMEL_IMAP_FAIL;
        }
        
-       for (i = 0; i < data->len; i++)
-               g_free (data->pdata[i]);
-       g_ptr_array_free (data, TRUE);
-       
-       return status;
+       return slurp_response (store, NULL, cmdid, ret, TRUE, ex);
 }
 
 /**
@@ -1211,89 +943,13 @@ camel_imap_command_continuation (CamelImapStore *store, char **ret, char *cmdid,
  * of the result of the command.)
  **/
 gint
-camel_imap_command_continuation_with_stream (CamelImapStore *store, char **ret, char *cmdid, CamelStream *cstream)
+camel_imap_command_continuation_with_stream (CamelImapStore *store, char **ret, char *cmdid, CamelStream *cstream, CamelException *ex)
 {
-       gint len = 0, status = CAMEL_IMAP_OK;
-       gchar *respbuf;
-       GPtrArray *data;
-       int i;
-       
-       d(fprintf (stderr, "sending continuation stream\r\n"));
-       
-       if (camel_stream_write_to_stream (cstream, store->ostream) == -1) {
-               *ret = g_strdup (strerror (errno));
-               
-               return CAMEL_IMAP_FAIL;
-       }
-       
-       data = g_ptr_array_new ();
-       
-       while (1) {
-               CamelStreamBuffer *stream = CAMEL_STREAM_BUFFER (store->istream);
-               
-               respbuf = camel_stream_buffer_read_line (stream);
-               if (!respbuf || *respbuf == '+' || !strncmp (respbuf, cmdid, strlen (cmdid))) {
-                       /* IMAP's last response starts with our command id or a continuation request */
-                       d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
-                       
-                       break;
-               }
-               
-               d(fprintf (stderr, "received: %s\n", respbuf));
-               
-               g_ptr_array_add (data, respbuf);
-               len += strlen (respbuf) + 1;
-       }
-       
-       if (respbuf) {
-               g_ptr_array_add (data, respbuf);
-               len += strlen (respbuf) + 1;
-               
-               switch (*respbuf) {
-               case '+':
-                       status = CAMEL_IMAP_PLUS;
-                       break;
-               default:
-                       status = camel_imap_status (cmdid, respbuf);
-               }
-       } else {
-               status = CAMEL_IMAP_FAIL;
-       }
-       
-       if (status == CAMEL_IMAP_OK || status == CAMEL_IMAP_PLUS) {
-               char *p;
-               
-               *ret = g_malloc0 (len + 1);
-               
-               for (i = 0, p = *ret; i < data->len; i++) {
-                       char *ptr, *datap;
-                       
-                       datap = (char *) data->pdata[i];
-                       ptr = (*datap == '.') ? datap + 1 : datap;
-                       len = strlen (ptr);
-                       memcpy (p, ptr, len);
-                       p += len;
-                       *p++ = '\n';
-               }
-               *p = '\0';
-       } else {
-               if (status != CAMEL_IMAP_FAIL && respbuf) {
-                       char *word;
-                       
-                       word = imap_next_word (respbuf);
-                       
-                       if (*respbuf == '-')
-                               *ret = g_strdup (word);
-                       else
-                               *ret = g_strdup (imap_next_word (word));
-               } else {
+       if (camel_remote_store_send_stream (CAMEL_REMOTE_STORE (store), cstream, ex) < 0) {
+               if (ret)
                        *ret = NULL;
-               }
+               return CAMEL_IMAP_FAIL;
        }
-       
-       for (i = 0; i < data->len; i++)
-               g_free (data->pdata[i]);
-       g_ptr_array_free (data, TRUE);
-       
-       return status;
+
+       return slurp_response (store, NULL, cmdid, ret, TRUE, ex);
 }
index fcf0bca..c153b11 100644 (file)
@@ -32,7 +32,7 @@ extern "C" {
 #pragma }
 #endif /* __cplusplus }*/
 
-#include "camel-store.h"
+#include "camel-remote-store.h"
 
 #define CAMEL_IMAP_STORE_TYPE     (camel_imap_store_get_type ())
 #define CAMEL_IMAP_STORE(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_IMAP_STORE_TYPE, CamelImapStore))
@@ -47,7 +47,7 @@ typedef enum {
 
 
 typedef struct {
-       CamelStore parent_object;       
+       CamelRemoteStore parent_object; 
        
        CamelFolder *current_folder;
        CamelStream *istream, *ostream;
@@ -64,7 +64,7 @@ typedef struct {
 
 
 typedef struct {
-       CamelStoreClass parent_class;
+       CamelRemoteStoreClass parent_class;
 
 } CamelImapStoreClass;
 
@@ -83,13 +83,13 @@ enum {
        CAMEL_IMAP_FAIL
 };
 
-gint camel_imap_command (CamelImapStore *store, CamelFolder *folder, char **ret, char *fmt, ...);
-gint camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, char **ret, char *fmt, ...);
+gint camel_imap_command (CamelImapStore *store, CamelFolder *folder, CamelException *ex, char *fmt, ...);
+gint camel_imap_command_extended (CamelImapStore *store, CamelFolder *folder, char **ret, CamelException *ex, char *fmt, ...);
 
 /* multi-transactional commands... */
-gint camel_imap_command_preliminary (CamelImapStore *store, char **ret, char **cmdid, char *fmt, ...);
-gint camel_imap_command_continuation (CamelImapStore *store, char **ret, char *cmdid, char *cmdbuf);
-gint camel_imap_command_continuation_with_stream (CamelImapStore *store, char **ret, char *cmdid, CamelStream *cstream);
+gint camel_imap_command_preliminary (CamelImapStore *store, char **cmdid, CamelException *ex, char *fmt, ...);
+gint camel_imap_command_continuation (CamelImapStore *store, char **ret, char *cmdid, char *cmdbuf, CamelException *ex);
+gint camel_imap_command_continuation_with_stream (CamelImapStore *store, char **ret, char *cmdid, CamelStream *cstream, CamelException *ex);
 
 /* Standard Camel function */
 CamelType camel_imap_store_get_type (void);
index fcddd4c..f27e782 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <config.h>
 #include "camel-imap-stream.h"
+#include "camel/camel-exception.h"
 #include <sys/types.h>
 #include <errno.h>
 #include <stdlib.h>
@@ -117,14 +118,18 @@ stream_read (CamelStream *stream, char *buffer, size_t n)
        if (!imap_stream->cache) {
                /* We need to send the IMAP command since this is our first fetch */
                CamelFolder *folder = CAMEL_FOLDER (imap_stream->folder);
+               CamelException ex;
                gchar *result, *p, *q;
                gint status, part_len;
                
+               camel_exception_init (&ex);
                status = camel_imap_command_extended (CAMEL_IMAP_STORE (folder->parent_store),
                                                      CAMEL_FOLDER (imap_stream->folder),
-                                                     &result, "%s\r\n",
+                                                     &result, &ex, "%s\r\n",
                                                      imap_stream->command);
-               
+               /* FIXME: exception is ignored */
+               camel_exception_clear (&ex);
+
                if (!result || status != CAMEL_IMAP_OK) {
                        /* we got an error, dump this stuff */
                        g_free (result);
index 00499c1..f924d1d 100644 (file)
@@ -58,9 +58,8 @@ static CamelFolderClass *parent_class = NULL;
 
 static void mbox_init(CamelFolder *folder, CamelStore * parent_store,
                      CamelFolder *parent_folder, const gchar * name,
-
                      gchar * separator, gboolean path_begins_with_sep, CamelException *ex);
-
+static void mbox_refresh_info (CamelFolder *folder, CamelException *ex);
 static void mbox_sync(CamelFolder *folder, gboolean expunge, CamelException *ex);
 static gint mbox_get_message_count(CamelFolder *folder);
 static gint mbox_get_unread_message_count(CamelFolder *folder);
@@ -100,6 +99,7 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class)
 
        /* virtual method overload */
        camel_folder_class->init = mbox_init;
+       camel_folder_class->refresh_info = mbox_refresh_info;
        camel_folder_class->sync = mbox_sync;
        camel_folder_class->get_message_count = mbox_get_message_count;
        camel_folder_class->get_unread_message_count = mbox_get_unread_message_count;
@@ -167,8 +167,6 @@ mbox_init(CamelFolder *folder, CamelStore * parent_store,
        CamelMboxFolder *mbox_folder = (CamelMboxFolder *) folder;
        const gchar *root_dir_path;
        gchar *real_name;
-       int forceindex;
-       struct stat st;
 
        /* call parent method */
        parent_class->init(folder, parent_store, parent_folder, name, separator, path_begins_with_sep, ex);
@@ -202,6 +200,14 @@ mbox_init(CamelFolder *folder, CamelStore * parent_store,
        mbox_folder->summary_file_path = g_strdup_printf("%s/%s-ev-summary", root_dir_path, real_name);
        mbox_folder->folder_dir_path = g_strdup_printf("%s/%s.sdb", root_dir_path, real_name);
        mbox_folder->index_file_path = g_strdup_printf("%s/%s.ibex", root_dir_path, real_name);
+}
+
+static void
+mbox_refresh_info (CamelFolder *folder, CamelException *ex)
+{
+       CamelMboxFolder *mbox_folder = (CamelMboxFolder *) folder;
+       struct stat st;
+       int forceindex;
 
        /* if we have no index file, force it */
        forceindex = stat(mbox_folder->index_file_path, &st) == -1;
index ed56e84..05e5af4 100644 (file)
@@ -155,7 +155,8 @@ get_folder (CamelStore *store, const char *folder_name, gboolean create,
        
        CF_CLASS (new_folder)->init (new_folder, store, NULL,
                                     folder_name, "/", TRUE, ex);
-       
+       CF_CLASS (new_folder)->refresh_info (new_folder, ex);
+
        return new_folder;
 }
 
index 091d35e..371c469 100644 (file)
@@ -63,7 +63,6 @@ nntp_folder_init (CamelFolder *folder, CamelStore *parent_store,
                  gchar *separator, gboolean path_begins_with_sep,
                  CamelException *ex)
 {
-       const gchar *root_dir_path;
        CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
 
        /* call parent method */
@@ -93,12 +92,19 @@ nntp_folder_init (CamelFolder *folder, CamelStore *parent_store,
        if (!(nntp_load_uid_list (nntp_folder) > 0))
                nntp_generate_uid_list (nntp_folder);
 #endif
+}
 
-       root_dir_path = camel_nntp_store_get_toplevel_dir (CAMEL_NNTP_STORE(folder->parent_store));
-
+static void
+nntp_refresh_info (CamelFolder *folder, CamelException *ex)
+{
+       CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
 
        /* load the summary if we have that ability */
        if (folder->has_summary_capability) {
+               const gchar *root_dir_path;
+
+               root_dir_path = camel_nntp_store_get_toplevel_dir (CAMEL_NNTP_STORE(folder->parent_store));
+
                nntp_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary",
                                                          root_dir_path,
                                                          nntp_folder->group_name);
@@ -118,8 +124,6 @@ nntp_folder_init (CamelFolder *folder, CamelStore *parent_store,
                        camel_folder_summary_save (nntp_folder->summary);
                }
        }
-               
-
 }
 
 static void
@@ -364,6 +368,7 @@ camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class)
 
        /* virtual method overload */
        camel_folder_class->init = nntp_folder_init;
+       camel_folder_class->refresh_info = nntp_refresh_info;
        camel_folder_class->sync = nntp_folder_sync;
        camel_folder_class->get_subfolder = nntp_folder_get_subfolder;
        camel_folder_class->get_message_count = nntp_folder_get_message_count;
index c412c0f..5c9475e 100644 (file)
@@ -176,6 +176,8 @@ nntp_store_get_folder (CamelStore *store, const gchar *folder_name,
        CF_CLASS (new_folder)->init (new_folder, store, NULL,
                                     folder_name, ".", FALSE, ex);
 
+       CF_CLASS (new_folder)->refresh_info (new_folder, ex);
+
        return new_folder;
 }
 
@@ -271,21 +273,22 @@ camel_nntp_command (CamelNNTPStore *store, char **ret, char *fmt, ...)
        va_list ap;
        int status;
        int resp_code;
-       CamelException *ex;
 
        va_start (ap, fmt);
        cmdbuf = g_strdup_vprintf (fmt, ap);
        va_end (ap);
 
-       ex = camel_exception_new();
-
        /* make sure we're connected */
-       if (store->ostream == NULL)
-               nntp_store_connect (CAMEL_SERVICE (store), ex);
-
-       if (camel_exception_get_id (ex)) {
-               camel_exception_free (ex);
-               return CAMEL_NNTP_FAIL;
+       if (store->ostream == NULL) {
+               CamelException ex;
+       
+               camel_exception_init (&ex);
+               nntp_store_connect (CAMEL_SERVICE (store), &ex);
+               if (camel_exception_get_id (&ex)) {
+                       camel_exception_clear (&ex);
+                       return CAMEL_NNTP_FAIL;
+               }
+               camel_exception_clear (&ex);
        }
 
        /* Send the command */
@@ -344,6 +347,19 @@ camel_nntp_command_get_additional_data (CamelNNTPStore *store)
        char *buf;
        int i, status = CAMEL_NNTP_OK;
 
+       /* make sure we're connected */
+       if (store->ostream == NULL) {
+               CamelException ex;
+       
+               camel_exception_init (&ex);
+               nntp_store_connect (CAMEL_SERVICE (store), &ex);
+               if (camel_exception_get_id (&ex)) {
+                       camel_exception_clear (&ex);
+                       return NULL;
+               }
+               camel_exception_clear (&ex);
+       }
+
        data = g_ptr_array_new ();
        while (1) {
                buf = camel_stream_buffer_read_line (stream);
index 6dbba64..43c8d9a 100644 (file)
@@ -38,6 +38,7 @@ static CamelFolderClass *parent_class;
 
 static void pop3_finalize (CamelObject *object);
 
+static void pop3_refresh_info (CamelFolder *folder, CamelException *ex);
 static void pop3_sync (CamelFolder *folder, gboolean expunge,
                       CamelException *ex);
 
@@ -60,6 +61,7 @@ camel_pop3_folder_class_init (CamelPop3FolderClass *camel_pop3_folder_class)
        parent_class = CAMEL_FOLDER_CLASS(camel_type_get_global_classfuncs (camel_folder_get_type ()));
 
        /* virtual method overload */
+       camel_folder_class->refresh_info = pop3_refresh_info;
        camel_folder_class->sync = pop3_sync;
 
        camel_folder_class->get_message_count = pop3_get_message_count;
@@ -111,21 +113,36 @@ pop3_finalize (CamelObject *object)
 CamelFolder *
 camel_pop3_folder_new (CamelStore *parent, CamelException *ex)
 {
-       CamelPop3Store *pop3_store = CAMEL_POP3_STORE (parent);
        CamelPop3Folder *pop3_folder;
+
+       pop3_folder = CAMEL_POP3_FOLDER(camel_object_new (CAMEL_POP3_FOLDER_TYPE));
+       CF_CLASS (pop3_folder)->init ((CamelFolder *)pop3_folder, parent,
+                                     NULL, "inbox", "/", TRUE, ex);
+       pop3_folder->uids = NULL;
+       pop3_folder->flags = NULL;
+       CF_CLASS (pop3_folder)->refresh_info ((CamelFolder *)pop3_folder, ex);
+
+       return (CamelFolder *)pop3_folder;
+}
+
+static void 
+pop3_refresh_info (CamelFolder *folder, CamelException *ex)
+{
        GPtrArray *uids;
        int status, count;
        char *data;
+       CamelPop3Folder *pop3_folder = (CamelPop3Folder *) folder;
+       CamelPop3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
 
        status = camel_pop3_command (pop3_store, &data, "STAT");
        if (status != CAMEL_POP3_OK) {
-               CamelService *service = CAMEL_SERVICE (parent);
+               CamelService *service = CAMEL_SERVICE (pop3_store);
                camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
                                      "Could not get message count from POP "
                                      "server %s: %s.", service->url->host,
                                      data ? data : "Unknown error");
                g_free (data);
-               return NULL;
+               return;
        }
 
        count = atoi (data);
@@ -148,7 +165,7 @@ camel_pop3_folder_new (CamelStore *parent, CamelException *ex)
        } else {
                data = camel_pop3_command_get_additional_data (pop3_store, ex);
                if (camel_exception_is_set (ex))
-                       return NULL;
+                       return;
 
                uids = parse_listing (count, data);
                g_free (data);
@@ -157,17 +174,12 @@ camel_pop3_folder_new (CamelStore *parent, CamelException *ex)
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
                                              "Could not open folder: message "
                                              "listing was incomplete.");
-                       return NULL;
+                       return;
                }
        }
 
-       pop3_folder = CAMEL_POP3_FOLDER(camel_object_new (CAMEL_POP3_FOLDER_TYPE));
-       CF_CLASS (pop3_folder)->init ((CamelFolder *)pop3_folder, parent,
-                                     NULL, "inbox", "/", TRUE, ex);
        pop3_folder->uids = uids;
        pop3_folder->flags = g_new0 (guint32, uids->len);
-
-       return (CamelFolder *)pop3_folder;
 }
 
 static void
index 4f618e5..6ee7361 100644 (file)
@@ -277,6 +277,9 @@ connect_to_server (CamelService *service, gboolean real, CamelException *ex)
        store->supports_uidl = -1;
        store->expires = -1;
 
+       /* good enough for us */
+       service->connected = TRUE;
+
        status = camel_pop3_command (store, NULL, "CAPA");
        if (status == CAMEL_POP3_OK) {
                char *p;
@@ -285,6 +288,7 @@ connect_to_server (CamelService *service, gboolean real, CamelException *ex)
                buf = camel_pop3_command_get_additional_data (store, ex);
                if (camel_exception_is_set (ex)) {
                        pop3_disconnect (service, ex);
+                       service->connected = FALSE;
                        return FALSE;
                }
 
@@ -534,6 +538,7 @@ pop3_connect (CamelService *service, CamelException *ex)
 
        if (camel_exception_is_set (ex)) {
                pop3_disconnect (service, NULL);
+               service->connected = FALSE;
                return FALSE;
        }
 
@@ -628,6 +633,21 @@ camel_pop3_command (CamelPop3Store *store, char **ret, char *fmt, ...)
        char *cmdbuf;
        va_list ap;
 
+       /* Check for connectedness. Failed (or cancelled) operations will
+        * close the connection. */
+       if (CAMEL_SERVICE (store)->connected == FALSE) {
+               CamelException ex;
+
+               d(g_message ("pop3: disconnected, reconnecting."));
+               camel_exception_init (&ex);
+               CAMEL_SERVICE_CLASS (CAMEL_OBJECT_GET_CLASS (store))->connect (store, &ex);
+               if (camel_exception_is_set (&ex)) {
+                       camel_exception_clear (&ex);
+                       return CAMEL_POP3_FAIL;
+               }
+               camel_exception_clear (&ex);
+       }
+
        if (!store->ostream) {
                /*CamelException ex;
                 *
@@ -769,3 +789,4 @@ camel_pop3_command_get_additional_data (CamelPop3Store *store, CamelException *e
 
        return buf;
 }
+