Commit e0ac4d79 authored by Matthew Barnes's avatar Matthew Barnes
Browse files

Rework how CamelServices are added to CamelSession.

* Give CamelServices a simple unique ID string.  That will be its
  identity from now on, not its URL.

* Split adding a CamelService and retrieving a CamelService into two
  separate operations.  Adding a CamelService requires both a UID and
  CamelURL (for now), retrieving a CamelService just requires the UID.

* CamelService now implements the GInitable interface, replacing its
  construct() method.
parent d051278f
......@@ -38,25 +38,25 @@
G_DEFINE_TYPE (CamelDiscoStore, camel_disco_store, CAMEL_TYPE_STORE)
static gboolean
disco_store_construct (CamelService *service,
CamelSession *session,
CamelProvider *provider,
CamelURL *url,
GError **error)
static void
disco_store_constructed (GObject *object)
{
CamelServiceClass *service_class;
CamelDiscoStore *disco = CAMEL_DISCO_STORE (service);
CamelDiscoStore *disco;
CamelService *service;
CamelSession *session;
/* Chain up to parent's construct() method. */
service_class = CAMEL_SERVICE_CLASS (camel_disco_store_parent_class);
if (!service_class->construct (service, session, provider, url, error))
return FALSE;
disco = CAMEL_DISCO_STORE (object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (camel_disco_store_parent_class)->constructed (object);
disco->status = camel_session_get_online (session) ?
CAMEL_DISCO_STORE_ONLINE : CAMEL_DISCO_STORE_OFFLINE;
service = CAMEL_SERVICE (object);
session = camel_service_get_session (service);
return TRUE;
if (camel_session_get_online (session))
disco->status = CAMEL_DISCO_STORE_ONLINE;
else
disco->status = CAMEL_DISCO_STORE_OFFLINE;
}
static void
......@@ -311,11 +311,14 @@ disco_store_set_status (CamelDiscoStore *disco_store,
static void
camel_disco_store_class_init (CamelDiscoStoreClass *class)
{
GObjectClass *object_class;
CamelServiceClass *service_class;
CamelStoreClass *store_class;
object_class = G_OBJECT_CLASS (class);
object_class->constructed = disco_store_constructed;
service_class = CAMEL_SERVICE_CLASS (class);
service_class->construct = disco_store_construct;
service_class->cancel_connect = disco_store_cancel_connect;
service_class->connect_sync = disco_store_connect_sync;
service_class->disconnect_sync = disco_store_disconnect_sync;
......
......@@ -31,49 +31,47 @@
#include "camel-offline-store.h"
#include "camel-session.h"
#define CAMEL_OFFLINE_STORE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_OFFLINE_STORE, CamelOfflineStorePrivate))
struct _CamelOfflineStorePrivate {
gboolean online;
};
G_DEFINE_TYPE (CamelOfflineStore, camel_offline_store, CAMEL_TYPE_STORE)
static gboolean
offline_store_construct (CamelService *service,
CamelSession *session,
CamelProvider *provider,
CamelURL *url,
GError **error)
static void
offline_store_constructed (GObject *object)
{
CamelOfflineStore *store = CAMEL_OFFLINE_STORE (service);
CamelServiceClass *service_class;
CamelOfflineStorePrivate *priv;
CamelSession *session;
/* Chain up to parent's construct() method. */
service_class = CAMEL_SERVICE_CLASS (camel_offline_store_parent_class);
if (!service_class->construct (service, session, provider, url, error))
return FALSE;
priv = CAMEL_OFFLINE_STORE_GET_PRIVATE (object);
store->priv->online = camel_session_get_online (session);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (camel_offline_store_parent_class)->
constructed (object);
return TRUE;
session = camel_service_get_session (CAMEL_SERVICE (object));
priv->online = camel_session_get_online (session);
}
static void
camel_offline_store_class_init (CamelOfflineStoreClass *class)
{
CamelServiceClass *service_class;
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (CamelOfflineStorePrivate));
service_class = CAMEL_SERVICE_CLASS (class);
service_class->construct = offline_store_construct;
object_class = G_OBJECT_CLASS (class);
object_class->constructed = offline_store_constructed;
}
static void
camel_offline_store_init (CamelOfflineStore *store)
{
store->priv = G_TYPE_INSTANCE_GET_PRIVATE (
store, CAMEL_TYPE_OFFLINE_STORE, CamelOfflineStorePrivate);
store->priv->online = TRUE;
store->priv = CAMEL_OFFLINE_STORE_GET_PRIVATE (store);
}
/**
......
......@@ -243,12 +243,6 @@ camel_provider_register (CamelProvider *provider)
return;
}
for (i = 0; i < CAMEL_NUM_PROVIDER_TYPES; i++) {
if (provider->object_types[i])
provider->service_cache[i] = camel_object_bag_new (provider->url_hash, provider->url_equal,
(CamelCopyFunc)camel_url_copy, (GFreeFunc)camel_url_free);
}
/* Translate all strings here */
#define P_(string) dgettext (provider->translation_domain, string)
......
......@@ -223,8 +223,6 @@ typedef struct {
/* GList of CamelServiceAuthTypes the provider supports */
GList *authtypes;
CamelObjectBag *service_cache[CAMEL_NUM_PROVIDER_TYPES];
GHashFunc url_hash;
GCompareFunc url_equal;
......
......@@ -65,33 +65,57 @@ sasl_popb4smtp_challenge_sync (CamelSasl *sasl,
GCancellable *cancellable,
GError **error)
{
gchar *popuri;
CamelService *service;
CamelSession *session;
CamelStore *store;
time_t now, *timep;
const gchar *type_name;
const gchar *uid;
gchar *pop_uid;
service = camel_sasl_get_service (sasl);
session = camel_service_get_session (service);
uid = camel_service_get_uid (service);
camel_sasl_set_authenticated (sasl, FALSE);
popuri = camel_session_get_password (
session, service, NULL, _("POP Source URI"),
"popb4smtp_uri", 0, error);
pop_uid = camel_session_get_password (
session, service, NULL, _("POP Source UID"),
"popb4smtp_uid", 0, error);
if (pop_uid != NULL)
service = camel_session_get_service (session, pop_uid);
else
service = NULL;
if (service == NULL) {
g_set_error (
error, CAMEL_SERVICE_ERROR,
CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
_("POP Before SMTP authentication "
"using an unknown transport"));
g_free (pop_uid);
return NULL;
}
type_name = G_OBJECT_TYPE_NAME (service);
if (popuri == NULL) {
if (!CAMEL_IS_STORE (service)) {
g_set_error (
error, CAMEL_SERVICE_ERROR,
CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
_("POP Before SMTP authentication using an unknown transport"));
_("POP Before SMTP authentication attempted "
"with a %s service"), type_name);
g_free (pop_uid);
return NULL;
}
if (g_ascii_strncasecmp(popuri, "pop:", 4) != 0) {
if (strstr (type_name, "POP") == NULL) {
g_set_error (
error, CAMEL_SERVICE_ERROR,
CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
_("POP Before SMTP authentication using a non-POP source"));
_("POP Before SMTP authentication attempted "
"with a %s service"), type_name);
g_free (pop_uid);
return NULL;
}
......@@ -101,24 +125,22 @@ sasl_popb4smtp_challenge_sync (CamelSasl *sasl,
/* need to lock around the whole thing until finished with timep */
POPB4SMTP_LOCK (lock);
timep = g_hash_table_lookup (poplast, popuri);
timep = g_hash_table_lookup (poplast, pop_uid);
if (timep) {
if ((*timep + POPB4SMTP_TIMEOUT) > now) {
camel_sasl_set_authenticated (sasl, TRUE);
POPB4SMTP_UNLOCK (lock);
g_free (popuri);
g_free (pop_uid);
return NULL;
}
} else {
timep = g_malloc0 (sizeof (*timep));
g_hash_table_insert (poplast, g_strdup (popuri), timep);
g_hash_table_insert (poplast, g_strdup (pop_uid), timep);
}
/* connect to pop session */
store = camel_session_get_store (session, popuri, error);
if (store) {
if (camel_service_connect_sync (service, error)) {
camel_sasl_set_authenticated (sasl, TRUE);
g_object_unref (store);
*timep = now;
} else {
camel_sasl_set_authenticated (sasl, FALSE);
......@@ -127,7 +149,7 @@ sasl_popb4smtp_challenge_sync (CamelSasl *sasl,
POPB4SMTP_UNLOCK (lock);
g_free (popuri);
g_free (pop_uid);
return NULL;
}
......
......@@ -32,6 +32,7 @@
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <glib/gi18n-lib.h>
#include <libedataserver/e-data-server-util.h>
......@@ -44,11 +45,18 @@
#define d(x)
#define w(x)
#define CAMEL_SERVICE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_SERVICE, CamelServicePrivate))
struct _CamelServicePrivate {
CamelSession *session;
CamelProvider *provider;
CamelSession *session;
CamelURL *url;
gchar *user_data_dir;
gchar *uid;
GCancellable *connect_op;
CamelServiceConnectionStatus status;
......@@ -56,166 +64,284 @@ struct _CamelServicePrivate {
GStaticMutex connect_op_lock; /* for locking the connection_op */
};
G_DEFINE_ABSTRACT_TYPE (CamelService, camel_service, CAMEL_TYPE_OBJECT)
enum {
PROP_0,
PROP_PROVIDER,
PROP_SESSION,
PROP_UID,
PROP_URL
};
static void
service_finalize (GObject *object)
/* Forward Declarations */
static void camel_service_initable_init (GInitableIface *interface);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
CamelService, camel_service, CAMEL_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, camel_service_initable_init))
static gchar *
service_find_old_data_dir (CamelService *service)
{
CamelService *service = CAMEL_SERVICE (object);
CamelProvider *provider;
CamelSession *session;
CamelURL *url;
GString *path;
gboolean allows_host;
gboolean allows_user;
gboolean needs_host;
gboolean needs_path;
gboolean needs_user;
const gchar *base_dir;
gchar *old_data_dir;
provider = camel_service_get_provider (service);
session = camel_service_get_session (service);
url = camel_service_get_camel_url (service);
if (service->priv->status == CAMEL_SERVICE_CONNECTED)
CAMEL_SERVICE_GET_CLASS (service)->disconnect_sync (
service, TRUE, NULL, NULL);
allows_host = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_HOST);
allows_user = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_USER);
needs_host = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST);
needs_path = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH);
needs_user = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER);
/* This function reproduces the way service data directories used
* to be determined before we moved to just using the UID. If the
* old data directory exists, try renaming it to the new form.
*
* A virtual class method was used to determine the directory path,
* but no known CamelProviders ever overrode the default algorithm
* below. So this should work for everyone. */
path = g_string_new (provider->protocol);
if (allows_user) {
g_string_append_c (path, '/');
if (url->user != NULL)
g_string_append (path, url->user);
if (allows_host) {
g_string_append_c (path, '@');
if (url->host != NULL)
g_string_append (path, url->host);
if (url->port) {
g_string_append_c (path, ':');
g_string_append_printf (path, "%d", url->port);
}
} else if (!needs_user) {
g_string_append_c (path, '@');
}
if (service->priv->url)
camel_url_free (service->priv->url);
} else if (allows_host) {
g_string_append_c (path, '/');
if (!needs_host)
g_string_append_c (path, '@');
if (url->host != NULL)
g_string_append (path, url->host);
if (url->port) {
g_string_append_c (path, ':');
g_string_append_printf (path, "%d", url->port);
}
}
if (service->priv->session)
g_object_unref (service->priv->session);
if (needs_path) {
if (*url->path != '/')
g_string_append_c (path, '/');
g_string_append (path, url->path);
}
g_static_rec_mutex_free (&service->priv->connect_lock);
g_static_mutex_free (&service->priv->connect_op_lock);
base_dir = camel_session_get_user_data_dir (session);
old_data_dir = g_build_filename (base_dir, path->str, NULL);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (camel_service_parent_class)->finalize (object);
}
g_string_free (path, TRUE);
static gboolean
service_construct (CamelService *service,
CamelSession *session,
CamelProvider *provider,
CamelURL *url,
GError **error)
{
gchar *err, *url_string;
if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER) &&
(url->user == NULL || url->user[0] == '\0')) {
err = _("URL '%s' needs a username component");
goto fail;
} else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST) &&
(url->host == NULL || url->host[0] == '\0')) {
err = _("URL '%s' needs a host component");
goto fail;
} else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH) &&
(url->path == NULL || url->path[0] == '\0')) {
err = _("URL '%s' needs a path component");
goto fail;
if (!g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
g_free (old_data_dir);
old_data_dir = NULL;
}
return old_data_dir;
}
static void
service_set_provider (CamelService *service,
CamelProvider *provider)
{
g_return_if_fail (provider != NULL);
g_return_if_fail (service->priv->provider == NULL);
service->priv->provider = provider;
service->priv->url = camel_url_copy (url);
}
static void
service_set_session (CamelService *service,
CamelSession *session)
{
g_return_if_fail (CAMEL_IS_SESSION (session));
g_return_if_fail (service->priv->session == NULL);
service->priv->session = g_object_ref (session);
}
service->priv->status = CAMEL_SERVICE_DISCONNECTED;
static void
service_set_uid (CamelService *service,
const gchar *uid)
{
g_return_if_fail (uid != NULL);
g_return_if_fail (service->priv->uid == NULL);
return TRUE;
service->priv->uid = g_strdup (uid);
}
fail:
url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
g_set_error (
error, CAMEL_SERVICE_ERROR,
CAMEL_SERVICE_ERROR_URL_INVALID,
err, url_string);
g_free (url_string);
static void
service_set_url (CamelService *service,
CamelURL *url)
{
g_return_if_fail (url != NULL);
g_return_if_fail (service->priv->url == NULL);
return FALSE;
service->priv->url = camel_url_copy (url);
}
static gchar *
service_get_name (CamelService *service,
gboolean brief)
static void
service_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
g_warning (
"%s does not implement CamelServiceClass::get_name()",
G_OBJECT_TYPE_NAME (service));
switch (property_id) {
case PROP_PROVIDER:
service_set_provider (
CAMEL_SERVICE (object),
g_value_get_pointer (value));
return;
case PROP_SESSION:
service_set_session (
CAMEL_SERVICE (object),
g_value_get_object (value));
return;
case PROP_UID:
service_set_uid (
CAMEL_SERVICE (object),
g_value_get_string (value));
return;
case PROP_URL:
service_set_url (
CAMEL_SERVICE (object),
g_value_get_boxed (value));
return;
}
return g_strdup (G_OBJECT_TYPE_NAME (service));
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static gchar *
service_get_path (CamelService *service)
{
CamelProvider *prov = service->priv->provider;
CamelURL *url = service->priv->url;
GString *use_path1 = NULL, *use_path2 = NULL;
gchar *ret_path = NULL;
/* A sort of ad-hoc default implementation that works for our
* current set of services.
*/
if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_USER)) {
use_path1 = g_string_new ("");
if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
g_string_append_printf (use_path1, "%s@%s",
url->user ? url->user : "",
url->host ? url->host : "");
if (url->port)
g_string_append_printf (use_path1, ":%d", url->port);
} else {
g_string_append_printf (use_path1, "%s%s", url->user ? url->user : "",
CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_USER) ? "" : "@");
}
static void
service_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_PROVIDER:
g_value_set_pointer (
value, camel_service_get_provider (
CAMEL_SERVICE (object)));
return;
case PROP_SESSION:
g_value_set_object (
value, camel_service_get_session (
CAMEL_SERVICE (object)));
return;
case PROP_UID:
g_value_set_string (
value, camel_service_get_uid (
CAMEL_SERVICE (object)));
return;
case PROP_URL:
g_value_set_boxed (
value, camel_service_get_url (
CAMEL_SERVICE (object)));
return;
}
e_filename_make_safe (use_path1->str);
} else if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
use_path1 = g_string_new ("");
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
g_string_append_printf (use_path1, "%s%s",
CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_HOST) ? "" : "@",
url->host ? url->host : "");
static void
service_dispose (GObject *object)
{
CamelServicePrivate *priv;
if (url->port)
g_string_append_printf (use_path1, ":%d", url->port);
priv = CAMEL_SERVICE_GET_PRIVATE (object);
e_filename_make_safe (use_path1->str);
if (priv->session != NULL) {
g_object_unref (priv->session);
priv->session = NULL;
}
if (CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_PATH) && url->path && *url->path) {
use_path2 = g_string_new (*url->path == '/' ? url->path + 1 : url->path);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (camel_service_parent_class)->dispose (object);
}
/* fix directory separators, if needed */
if (G_DIR_SEPARATOR != '/') {
gchar **elems = g_strsplit (use_path2->str, "/", -1);
static void
service_finalize (GObject *object)
{
CamelServicePrivate *priv;
if (elems) {
gint ii;
priv = CAMEL_SERVICE_GET_PRIVATE (object);