diff options
-rw-r--r-- | camel/ChangeLog | 36 | ||||
-rw-r--r-- | camel/camel-disco-store.c | 41 | ||||
-rw-r--r-- | camel/camel-operation.c | 9 | ||||
-rw-r--r-- | camel/camel-private.h | 1 | ||||
-rw-r--r-- | camel/camel-remote-store.c | 5 | ||||
-rw-r--r-- | camel/camel-service.c | 82 | ||||
-rw-r--r-- | camel/camel-service.h | 13 | ||||
-rw-r--r-- | camel/camel-session.c | 2 |
8 files changed, 160 insertions, 29 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index b4c552572b..3c0abde3f2 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,39 @@ +2001-09-27 Dan Winship <danw@ximian.com> + + * camel-service.c: Change "gboolean connected" to + "CamelServiceConnectionStatus status", which can be disconnected, + connecting, connected, or disconnecting. + (camel_service_init, camel_service_finalize): create/destroy the + connect_op_lock. Refer to service->status rather than + service->connected. + (camel_service_connect): When connecting, note the current + operation (and create a new one if there's none registered) and + mark the connection "connecting" until we succeed or fail. + (camel_service_disconnect): Likewise in reverse. + (camel_service_cancel_connect): New function to cancel a + connection attempt. + (cancel_connect): Default implementation: Call + camel_operation_cancel on the connect_op. + + * camel-disco-store.c (disco_connect): Only call + CamelRemoteStore's connect func if we're online. + (disco_cancel_connect): Fall back to offline if a connection gets + cancelled. + (disco_get_folder_info): Kludge: call connect explicitly before + deciding whether to do the online or offline version, so if the + connect fails, we fall back correctly. + + * camel-session.c (camel_session_get_service_connected): + s/svc->connected/svc->status/ + + * camel-remote-store.c (camel_remote_store_finalise): + Change service->connected check to service->status check. + (remote_connect): Don't set service->connected here: + camel_service_connect() itself does that. + + * camel-operation.c (camel_operation_registered): Deal with the + possibility that there's no registered op. + 2001-09-26 <NotZed@Ximian.com> * camel-filter-driver.c (camel_filter_driver_filter_message): If diff --git a/camel/camel-disco-store.c b/camel/camel-disco-store.c index 0cc5f4feea..145636f58d 100644 --- a/camel/camel-disco-store.c +++ b/camel/camel-disco-store.c @@ -39,6 +39,7 @@ static void disco_construct (CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex); static gboolean disco_connect (CamelService *service, CamelException *ex); +static void disco_cancel_connect (CamelService *service); static gboolean disco_disconnect (CamelService *service, gboolean clean, CamelException *ex); static CamelFolder *disco_get_folder (CamelStore *store, const char *name, guint32 flags, CamelException *ex); @@ -68,6 +69,7 @@ camel_disco_store_class_init (CamelDiscoStoreClass *camel_disco_store_class) camel_service_class->construct = disco_construct; camel_service_class->connect = disco_connect; camel_service_class->disconnect = disco_disconnect; + camel_service_class->cancel_connect = disco_cancel_connect; camel_store_class->get_folder = disco_get_folder; camel_store_class->get_folder_info = disco_get_folder_info; @@ -111,11 +113,19 @@ static gboolean disco_connect (CamelService *service, CamelException *ex) { CamelDiscoStore *store = CAMEL_DISCO_STORE (service); + CamelDiscoStoreStatus status; + + status = camel_disco_store_status (store); + if (status != CAMEL_DISCO_STORE_OFFLINE) { + if (!CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex)) { + status = camel_disco_store_status (store); + if (status != CAMEL_DISCO_STORE_OFFLINE) + return FALSE; + camel_exception_clear (ex); + } + } - if (!CAMEL_SERVICE_CLASS (remote_store_class)->connect (service, ex)) - return FALSE; - - switch (camel_disco_store_status (store)) { + switch (status) { case CAMEL_DISCO_STORE_ONLINE: case CAMEL_DISCO_STORE_RESYNCING: if (!CDS_CLASS (service)->connect_online (service, ex)) @@ -142,6 +152,17 @@ disco_connect (CamelService *service, CamelException *ex) return FALSE; } +static void +disco_cancel_connect (CamelService *service) +{ + CamelDiscoStore *store = CAMEL_DISCO_STORE (service); + + /* Fall back */ + store->status = CAMEL_DISCO_STORE_OFFLINE; + + CAMEL_SERVICE_CLASS (remote_store_class)->cancel_connect (service); +} + static gboolean disco_disconnect (CamelService *service, gboolean clean, CamelException *ex) { @@ -190,7 +211,17 @@ disco_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex) { CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (store); - + + /* Do this first so if we get forced offline, we'll switch to + * the correct branch below. (FIXME: This only works because + * we know that get_folder_info is the first call that the + * mailer makes on a store.) + */ + if (CAMEL_SERVICE (store)->status == CAMEL_SERVICE_DISCONNECTED) { + if (!camel_service_connect (CAMEL_SERVICE (store), ex)) + return NULL; + } + switch (camel_disco_store_status (disco_store)) { case CAMEL_DISCO_STORE_ONLINE: return CDS_CLASS (store)->get_folder_info_online (store, top, flags, ex); diff --git a/camel/camel-operation.c b/camel/camel-operation.c index 42aa448820..95d8fa0f91 100644 --- a/camel/camel-operation.c +++ b/camel/camel-operation.c @@ -105,10 +105,13 @@ CamelOperation *camel_operation_registered(void) CamelOperation *cc = NULL; CAMEL_ACTIVE_LOCK(); - if (operation_active != NULL) + if (operation_active != NULL) { cc = g_hash_table_lookup(operation_active, (void *)pthread_self()); - g_assert(cc->refcount > 0); - cc->refcount++; + if (cc) { + g_assert(cc->refcount > 0); + cc->refcount++; + } + } CAMEL_ACTIVE_UNLOCK(); return cc; diff --git a/camel/camel-private.h b/camel/camel-private.h index 32cde53068..73a1b7bfc3 100644 --- a/camel/camel-private.h +++ b/camel/camel-private.h @@ -90,6 +90,7 @@ struct _CamelTransportPrivate { struct _CamelServicePrivate { #ifdef ENABLE_THREADS EMutex *connect_lock; /* for locking connection operations */ + EMutex *connect_op_lock;/* for locking the connection_op */ #endif }; diff --git a/camel/camel-remote-store.c b/camel/camel-remote-store.c index 00f8dce4f4..e1818e47c0 100644 --- a/camel/camel-remote-store.c +++ b/camel/camel-remote-store.c @@ -129,7 +129,7 @@ camel_remote_store_finalise (CamelObject *object) CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object); CamelService *service = CAMEL_SERVICE (object); - if (service->connected) { + if (service->status == CAMEL_SERVICE_CONNECTED) { CamelException ex; camel_exception_init (&ex); @@ -277,9 +277,6 @@ remote_connect (CamelService *service, CamelException *ex) store->ostream = tcp_stream; store->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ); - /* Okay, good enough for us */ - CAMEL_SERVICE (store)->connected = TRUE; - /* Add a timeout so that we can hopefully prevent getting disconnected */ /* (Only if the implementation supports it) */ if (CRSC (store)->keepalive) { diff --git a/camel/camel-service.c b/camel/camel-service.c index 27d6a5bbee..cc254e1f2d 100644 --- a/camel/camel-service.c +++ b/camel/camel-service.c @@ -59,7 +59,7 @@ static void construct (CamelService *service, CamelSession *session, static gboolean service_connect(CamelService *service, CamelException *ex); static gboolean service_disconnect(CamelService *service, gboolean clean, CamelException *ex); -/*static gboolean is_connected (CamelService *service);*/ +static void cancel_connect (CamelService *service); static GList * query_auth_types (CamelService *service, CamelException *ex); static char * get_name (CamelService *service, gboolean brief); static char * get_path (CamelService *service); @@ -74,6 +74,7 @@ camel_service_class_init (CamelServiceClass *camel_service_class) camel_service_class->construct = construct; camel_service_class->connect = service_connect; camel_service_class->disconnect = service_disconnect; + camel_service_class->cancel_connect = cancel_connect; camel_service_class->query_auth_types = query_auth_types; camel_service_class->get_name = get_name; camel_service_class->get_path = get_path; @@ -87,6 +88,7 @@ camel_service_init (void *o, void *k) service->priv = g_malloc0(sizeof(*service->priv)); #ifdef ENABLE_THREADS service->priv->connect_lock = e_mutex_new(E_MUTEX_REC); + service->priv->connect_op_lock = e_mutex_new(E_MUTEX_SIMPLE); #endif } @@ -95,7 +97,7 @@ camel_service_finalize (CamelObject *object) { CamelService *service = CAMEL_SERVICE (object); - if (service->connected) { + if (service->status == CAMEL_SERVICE_CONNECTED) { CamelException ex; camel_exception_init (&ex); @@ -114,6 +116,7 @@ camel_service_finalize (CamelObject *object) #ifdef ENABLE_THREADS e_mutex_destroy (service->priv->connect_lock); + e_mutex_destroy (service->priv->connect_op_lock); #endif g_free (service->priv); } @@ -178,7 +181,7 @@ construct (CamelService *service, CamelSession *session, service->session = session; camel_object_ref (CAMEL_OBJECT (session)); - service->connected = FALSE; + service->status = CAMEL_SERVICE_DISCONNECTED; } /** @@ -234,17 +237,30 @@ camel_service_connect (CamelService *service, CamelException *ex) CAMEL_SERVICE_LOCK (service, connect_lock); - 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"); - ret = TRUE; - } else if (CSERV_CLASS (service)->connect (service, ex)) { - service->connected = TRUE; - ret = TRUE; + if (service->status == CAMEL_SERVICE_CONNECTED) { + CAMEL_SERVICE_UNLOCK (service, connect_lock); + return TRUE; } - + + /* Register a separate operation for connecting, so that + * the offline code can cancel it. + */ + CAMEL_SERVICE_LOCK (service, connect_op_lock); + service->connect_op = camel_operation_registered (); + if (!service->connect_op) + service->connect_op = camel_operation_new (NULL, NULL); + camel_operation_register (service->connect_op); + CAMEL_SERVICE_UNLOCK (service, connect_op_lock); + + service->status = CAMEL_SERVICE_CONNECTING; + ret = CSERV_CLASS (service)->connect (service, ex); + service->status = ret ? CAMEL_SERVICE_CONNECTED : CAMEL_SERVICE_DISCONNECTED; + + CAMEL_SERVICE_LOCK (service, connect_op_lock); + camel_operation_unref (service->connect_op); + service->connect_op = NULL; + CAMEL_SERVICE_UNLOCK (service, connect_op_lock); + CAMEL_SERVICE_UNLOCK (service, connect_lock); return ret; @@ -282,9 +298,22 @@ camel_service_disconnect (CamelService *service, gboolean clean, CAMEL_SERVICE_LOCK (service, connect_lock); - if (service->connected) { + if (service->status == CAMEL_SERVICE_CONNECTED) { + CAMEL_SERVICE_LOCK (service, connect_op_lock); + service->connect_op = camel_operation_registered (); + if (!service->connect_op) + service->connect_op = camel_operation_new (NULL, NULL); + camel_operation_register (service->connect_op); + CAMEL_SERVICE_UNLOCK (service, connect_op_lock); + + service->status = CAMEL_SERVICE_DISCONNECTING; res = CSERV_CLASS (service)->disconnect (service, clean, ex); - service->connected = FALSE; + service->status = CAMEL_SERVICE_DISCONNECTED; + + CAMEL_SERVICE_LOCK (service, connect_op_lock); + camel_operation_unref (service->connect_op); + service->connect_op = NULL; + CAMEL_SERVICE_UNLOCK (service, connect_op_lock); } CAMEL_SERVICE_UNLOCK (service, connect_lock); @@ -292,6 +321,29 @@ camel_service_disconnect (CamelService *service, gboolean clean, return res; } +static void +cancel_connect (CamelService *service) +{ + camel_operation_cancel (service->connect_op); +} + +/** + * camel_service_cancel_connect: + * @service: a service + * + * If @service is currently attempting to connect to or disconnect + * from a server, this causes it to stop and fail. Otherwise it is a + * no-op. + **/ +void +camel_service_cancel_connect (CamelService *service) +{ + CAMEL_SERVICE_LOCK (service, connect_op_lock); + if (service->connect_op) + CSERV_CLASS (service)->cancel_connect (service); + CAMEL_SERVICE_UNLOCK (service, connect_op_lock); +} + /** * camel_service_get_url: * @service: a service diff --git a/camel/camel-service.h b/camel/camel-service.h index 41ac53c4c7..a4399811cf 100644 --- a/camel/camel-service.h +++ b/camel/camel-service.h @@ -38,6 +38,7 @@ extern "C" { #include <camel/camel-object.h> #include <camel/camel-url.h> #include <camel/camel-provider.h> +#include <camel/camel-operation.h> #define CAMEL_SERVICE_TYPE (camel_service_get_type ()) #define CAMEL_SERVICE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_SERVICE_TYPE, CamelService)) @@ -45,13 +46,21 @@ extern "C" { #define CAMEL_IS_SERVICE(o) (CAMEL_CHECK_TYPE((o), CAMEL_SERVICE_TYPE)) +typedef enum { + CAMEL_SERVICE_DISCONNECTED, + CAMEL_SERVICE_CONNECTING, + CAMEL_SERVICE_CONNECTED, + CAMEL_SERVICE_DISCONNECTING +} CamelServiceConnectionStatus; + struct _CamelService { CamelObject parent_object; struct _CamelServicePrivate *priv; CamelSession *session; CamelProvider *provider; - gboolean connected; + CamelServiceConnectionStatus status; + CamelOperation *connect_op; CamelURL *url; }; @@ -70,6 +79,7 @@ typedef struct { gboolean (*disconnect) (CamelService *service, gboolean clean, CamelException *ex); + void (*cancel_connect) (CamelService *service); GList * (*query_auth_types) (CamelService *service, CamelException *ex); @@ -102,6 +112,7 @@ gboolean camel_service_connect (CamelService *service, gboolean camel_service_disconnect (CamelService *service, gboolean clean, CamelException *ex); +void camel_service_cancel_connect (CamelService *service); char * camel_service_get_url (CamelService *service); char * camel_service_get_name (CamelService *service, gboolean brief); diff --git a/camel/camel-session.c b/camel/camel-session.c index 7f7fbe7a59..facedb9400 100644 --- a/camel/camel-session.c +++ b/camel/camel-session.c @@ -480,7 +480,7 @@ camel_session_get_service_connected (CamelSession *session, if (svc == NULL) return NULL; - if (svc->connected == FALSE) { + if (svc->status != CAMEL_SERVICE_CONNECTED) { if (camel_service_connect (svc, ex) == FALSE) { camel_object_unref (CAMEL_OBJECT (svc)); return NULL; |