From da570c66609a9baea34d4899c4ca7e1f8329d471 Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Tue, 29 Aug 2000 21:28:46 +0000 Subject: CamelRemoteStore: a new generic store for stores that connect to servers. Prepare for the ability to cancel operations (much better exception handling). Clean up IMAP like nobody's business svn path=/trunk/; revision=5103 --- camel/ChangeLog | 61 ++ camel/Makefile.am | 2 + camel/camel-folder.c | 25 + camel/camel-folder.h | 4 + camel/camel-remote-store.c | 496 +++++++++++++++ camel/camel-remote-store.h | 79 +++ camel/camel-service.c | 79 ++- camel/camel-service.h | 10 +- camel/camel-session.c | 2 +- camel/camel-store.c | 1 - camel/providers/imap/camel-imap-folder.c | 197 ++---- camel/providers/imap/camel-imap-store.c | 1018 ++++++++++-------------------- camel/providers/imap/camel-imap-store.h | 16 +- camel/providers/imap/camel-imap-stream.c | 9 +- camel/providers/mbox/camel-mbox-folder.c | 14 +- camel/providers/mbox/camel-mbox-store.c | 3 +- camel/providers/nntp/camel-nntp-folder.c | 15 +- camel/providers/nntp/camel-nntp-store.c | 34 +- camel/providers/pop3/camel-pop3-folder.c | 32 +- camel/providers/pop3/camel-pop3-store.c | 21 + 20 files changed, 1216 insertions(+), 902 deletions(-) create mode 100644 camel/camel-remote-store.c create mode 100644 camel/camel-remote-store.h (limited to 'camel') diff --git a/camel/ChangeLog b/camel/ChangeLog index 54c23dc80d..a9e572537e 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,27 @@ +2000-08-29 Peter Williams + + * 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 * providers/imap/camel-imap-store.c (camel_imap_command_extended): @@ -14,6 +38,43 @@ (imap_copy_message_to): Updated. (imap_move_message_to): Updated. +2000-08-28 Peter Williams + + * 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 * providers/imap/camel-imap-folder.c (imap_get_message): Fixed the diff --git a/camel/Makefile.am b/camel/Makefile.am index 4d2c29a4ca..320f4cc082 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -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 \ diff --git a/camel/camel-folder.c b/camel/camel-folder.c index 201e2cc778..888845aed8 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -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; diff --git a/camel/camel-folder.h b/camel/camel-folder.h index da76618b57..c78ed28b61 100644 --- a/camel/camel-folder.h +++ b/camel/camel-folder.h @@ -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 index 0000000000..e86c05e909 --- /dev/null +++ b/camel/camel-remote-store.c @@ -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 + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000000..a2b88133f6 --- /dev/null +++ b/camel/camel-remote-store.h @@ -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 + * + * 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 */ diff --git a/camel/camel-service.c b/camel/camel-service.c index 6554bad5cb..9df7837bf7 100644 --- a/camel/camel-service.c +++ b/camel/camel-service.c @@ -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) diff --git a/camel/camel-service.h b/camel/camel-service.h index 3558d5a72b..f229c5e559 100644 --- a/camel/camel-service.h +++ b/camel/camel-service.h @@ -36,6 +36,7 @@ extern "C" { #include #include +#include #include #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, diff --git a/camel/camel-session.c b/camel/camel-session.c index f9f0584294..3454517216 100644 --- a/camel/camel-session.c +++ b/camel/camel-session.c @@ -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); diff --git a/camel/camel-store.c b/camel/camel-store.c index a036d52694..039490c088 100644 --- a/camel/camel-store.c +++ b/camel/camel-store.c @@ -370,4 +370,3 @@ camel_store_get_default_folder (CamelStore *store, CamelException *ex) } return folder; } - diff --git a/camel/providers/imap/camel-imap-folder.c b/camel/providers/imap/camel-imap-folder.c index e3ce219b89..b0dbad53a4 100644 --- a/camel/providers/imap/camel-imap-folder.c +++ b/camel/providers/imap/camel-imap-folder.c @@ -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; } @@ -285,6 +283,15 @@ imap_init (CamelFolder *folder, CamelStore *parent_store, CamelFolder *parent_fo imap_folder->lsub = NULL; } +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) { @@ -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); diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index 9e57dc03d7..3830e37104 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -25,15 +25,10 @@ #include -#include -#include -#include -#include #include #include #include #include -#include #include @@ -54,20 +49,16 @@ /* 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); } diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index fcf0bcaf39..c153b11aa4 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -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); diff --git a/camel/providers/imap/camel-imap-stream.c b/camel/providers/imap/camel-imap-stream.c index fcddd4c0b5..f27e782c0a 100644 --- a/camel/providers/imap/camel-imap-stream.c +++ b/camel/providers/imap/camel-imap-stream.c @@ -23,6 +23,7 @@ #include #include "camel-imap-stream.h" +#include "camel/camel-exception.h" #include #include #include @@ -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); diff --git a/camel/providers/mbox/camel-mbox-folder.c b/camel/providers/mbox/camel-mbox-folder.c index 00499c1f75..f924d1d4ab 100644 --- a/camel/providers/mbox/camel-mbox-folder.c +++ b/camel/providers/mbox/camel-mbox-folder.c @@ -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; diff --git a/camel/providers/mbox/camel-mbox-store.c b/camel/providers/mbox/camel-mbox-store.c index ed56e84407..05e5af406b 100644 --- a/camel/providers/mbox/camel-mbox-store.c +++ b/camel/providers/mbox/camel-mbox-store.c @@ -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; } diff --git a/camel/providers/nntp/camel-nntp-folder.c b/camel/providers/nntp/camel-nntp-folder.c index 091d35e591..371c4698e2 100644 --- a/camel/providers/nntp/camel-nntp-folder.c +++ b/camel/providers/nntp/camel-nntp-folder.c @@ -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; diff --git a/camel/providers/nntp/camel-nntp-store.c b/camel/providers/nntp/camel-nntp-store.c index c412c0f6a3..5c9475ed14 100644 --- a/camel/providers/nntp/camel-nntp-store.c +++ b/camel/providers/nntp/camel-nntp-store.c @@ -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); diff --git a/camel/providers/pop3/camel-pop3-folder.c b/camel/providers/pop3/camel-pop3-folder.c index 6dbba64c20..43c8d9a958 100644 --- a/camel/providers/pop3/camel-pop3-folder.c +++ b/camel/providers/pop3/camel-pop3-folder.c @@ -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 diff --git a/camel/providers/pop3/camel-pop3-store.c b/camel/providers/pop3/camel-pop3-store.c index 4f618e521e..6ee7361959 100644 --- a/camel/providers/pop3/camel-pop3-store.c +++ b/camel/providers/pop3/camel-pop3-store.c @@ -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; } + -- cgit v1.2.3