summaryrefslogtreecommitdiffstats
path: root/src/calendar-server/calendar-sources.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/calendar-server/calendar-sources.c')
-rw-r--r--src/calendar-server/calendar-sources.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/src/calendar-server/calendar-sources.c b/src/calendar-server/calendar-sources.c
new file mode 100644
index 0000000..9c25f4e
--- /dev/null
+++ b/src/calendar-server/calendar-sources.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ * William Jon McCann <mccann@jhu.edu>
+ * Martin Grimme <martin@pycage.de>
+ * Christian Kellner <gicmo@xatom.net>
+ */
+
+#include <config.h>
+
+#include "calendar-sources.h"
+
+#include <libintl.h>
+#include <string.h>
+#define HANDLE_LIBICAL_MEMORY
+#define EDS_DISABLE_DEPRECATED
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <libecal/libecal.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+#undef CALENDAR_ENABLE_DEBUG
+#include "calendar-debug.h"
+
+typedef struct _ClientData ClientData;
+typedef struct _CalendarSourceData CalendarSourceData;
+
+struct _ClientData
+{
+ ECalClient *client;
+ gulong backend_died_id;
+};
+
+typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
+
+struct _CalendarSources
+{
+ GObject parent;
+
+ ESourceRegistryWatcher *registry_watcher;
+ gulong filter_id;
+ gulong appeared_id;
+ gulong disappeared_id;
+
+ GMutex clients_lock;
+ GHashTable *clients; /* ESource -> ClientData */
+};
+
+G_DEFINE_TYPE (CalendarSources, calendar_sources, G_TYPE_OBJECT)
+
+enum
+{
+ CLIENT_APPEARED,
+ CLIENT_DISAPPEARED,
+ LAST_SIGNAL
+};
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void
+calendar_sources_client_connected_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (source_object);
+ ESource *source = user_data;
+ EClient *client;
+ g_autoptr (GError) error = NULL;
+
+ /* The calendar_sources_connect_client_sync() already stored the 'client'
+ * into the sources->clients */
+ client = calendar_sources_connect_client_finish (sources, result, &error);
+ if (error)
+ {
+ g_warning ("Could not load source '%s': %s",
+ e_source_get_uid (source),
+ error->message);
+ }
+ else
+ {
+ g_signal_emit (sources, signals[CLIENT_APPEARED], 0, client, NULL);
+ }
+
+ g_clear_object (&client);
+ g_clear_object (&source);
+}
+
+static gboolean
+registry_watcher_filter_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ return e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR) &&
+ e_source_selectable_get_selected (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
+}
+
+static void
+registry_watcher_source_appeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ ECalClientSourceType source_type;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
+ source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+ else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST))
+ source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
+ else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
+ source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
+ else
+ g_return_if_reached ();
+
+ calendar_sources_connect_client (sources, source, source_type, 30, NULL, calendar_sources_client_connected_cb, g_object_ref (source));
+}
+
+static void
+registry_watcher_source_disappeared_cb (ESourceRegistryWatcher *watcher,
+ ESource *source,
+ CalendarSources *sources)
+{
+ gboolean emit;
+
+ g_mutex_lock (&sources->clients_lock);
+
+ emit = g_hash_table_remove (sources->clients, source);
+
+ g_mutex_unlock (&sources->clients_lock);
+
+ if (emit)
+ g_signal_emit (sources, signals[CLIENT_DISAPPEARED], 0, e_source_get_uid (source), NULL);
+}
+
+static void
+client_data_free (ClientData *data)
+{
+ g_signal_handler_disconnect (data->client, data->backend_died_id);
+ g_object_unref (data->client);
+ g_free (data);
+}
+
+static void
+calendar_sources_constructed (GObject *object)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (object);
+ ESourceRegistry *registry = NULL;
+ GError *error = NULL;
+
+ G_OBJECT_CLASS (calendar_sources_parent_class)->constructed (object);
+
+ registry = e_source_registry_new_sync (NULL, &error);
+ if (error != NULL)
+ {
+ /* Any error is fatal, but we don't want to crash gnome-shell-calendar-server
+ because of e-d-s problems. So just exit here.
+ */
+ g_warning ("Failed to start evolution-source-registry: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_return_if_fail (registry != NULL);
+
+ sources->registry_watcher = e_source_registry_watcher_new (registry, NULL);
+
+ g_clear_object (&registry);
+
+ sources->clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
+ (GEqualFunc) e_source_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) client_data_free);
+ sources->filter_id = g_signal_connect (sources->registry_watcher,
+ "filter",
+ G_CALLBACK (registry_watcher_filter_cb),
+ sources);
+ sources->appeared_id = g_signal_connect (sources->registry_watcher,
+ "appeared",
+ G_CALLBACK (registry_watcher_source_appeared_cb),
+ sources);
+ sources->disappeared_id = g_signal_connect (sources->registry_watcher,
+ "disappeared",
+ G_CALLBACK (registry_watcher_source_disappeared_cb),
+ sources);
+
+ e_source_registry_watcher_reclaim (sources->registry_watcher);
+}
+
+static void
+calendar_sources_finalize (GObject *object)
+{
+ CalendarSources *sources = CALENDAR_SOURCES (object);
+
+ g_clear_pointer (&sources->clients, g_hash_table_destroy);
+
+ if (sources->registry_watcher)
+ {
+ g_signal_handler_disconnect (sources->registry_watcher,
+ sources->filter_id);
+ g_signal_handler_disconnect (sources->registry_watcher,
+ sources->appeared_id);
+ g_signal_handler_disconnect (sources->registry_watcher,
+ sources->disappeared_id);
+ g_clear_object (&sources->registry_watcher);
+ }
+
+ g_mutex_clear (&sources->clients_lock);
+
+ G_OBJECT_CLASS (calendar_sources_parent_class)->finalize (object);
+}
+
+static void
+calendar_sources_class_init (CalendarSourcesClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->constructed = calendar_sources_constructed;
+ gobject_class->finalize = calendar_sources_finalize;
+
+ signals [CLIENT_APPEARED] =
+ g_signal_new ("client-appeared",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ E_TYPE_CAL_CLIENT);
+
+ signals [CLIENT_DISAPPEARED] =
+ g_signal_new ("client-disappeared",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING); /* ESource::uid of the disappeared client */
+}
+
+static void
+calendar_sources_init (CalendarSources *sources)
+{
+ g_mutex_init (&sources->clients_lock);
+}
+
+CalendarSources *
+calendar_sources_get (void)
+{
+ static CalendarSources *calendar_sources_singleton = NULL;
+ gpointer singleton_location = &calendar_sources_singleton;
+
+ if (calendar_sources_singleton)
+ return g_object_ref (calendar_sources_singleton);
+
+ calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
+ g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
+ singleton_location);
+
+ return calendar_sources_singleton;
+}
+
+ESourceRegistry *
+calendar_sources_get_registry (CalendarSources *sources)
+{
+ return e_source_registry_watcher_get_registry (sources->registry_watcher);
+}
+
+static void
+gather_event_clients_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSList **plist = user_data;
+ ClientData *cd = value;
+
+ if (cd)
+ *plist = g_slist_prepend (*plist, g_object_ref (cd->client));
+}
+
+GSList *
+calendar_sources_ref_clients (CalendarSources *sources)
+{
+ GSList *list = NULL;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+
+ g_mutex_lock (&sources->clients_lock);
+ g_hash_table_foreach (sources->clients, gather_event_clients_cb, &list);
+ g_mutex_unlock (&sources->clients_lock);
+
+ return list;
+}
+
+gboolean
+calendar_sources_has_clients (CalendarSources *sources)
+{
+ GHashTableIter iter;
+ gpointer value;
+ gboolean has = FALSE;
+
+ g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
+
+ g_mutex_lock (&sources->clients_lock);
+
+ g_hash_table_iter_init (&iter, sources->clients);
+ while (!has && g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ ClientData *cd = value;
+
+ has = cd != NULL;
+ }
+
+ g_mutex_unlock (&sources->clients_lock);
+
+ return has;
+}
+
+static void
+backend_died_cb (EClient *client,
+ CalendarSources *sources)
+{
+ ESource *source;
+ const char *display_name;
+
+ source = e_client_get_source (client);
+ display_name = e_source_get_display_name (source);
+ g_warning ("The calendar backend for '%s' has crashed.", display_name);
+ g_mutex_lock (&sources->clients_lock);
+ g_hash_table_remove (sources->clients, source);
+ g_mutex_unlock (&sources->clients_lock);
+}
+
+static EClient *
+calendar_sources_connect_client_sync (CalendarSources *sources,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EClient *client = NULL;
+ ClientData *client_data;
+
+ g_mutex_lock (&sources->clients_lock);
+ client_data = g_hash_table_lookup (sources->clients, source);
+ if (client_data)
+ client = E_CLIENT (g_object_ref (client_data->client));
+ g_mutex_unlock (&sources->clients_lock);
+
+ if (client)
+ return client;
+
+ client = e_cal_client_connect_sync (source, source_type, wait_for_connected_seconds, cancellable, error);
+ if (!client)
+ return NULL;
+
+ g_mutex_lock (&sources->clients_lock);
+ client_data = g_hash_table_lookup (sources->clients, source);
+ if (client_data)
+ {
+ g_clear_object (&client);
+ client = E_CLIENT (g_object_ref (client_data->client));
+ }
+ else
+ {
+ client_data = g_new0 (ClientData, 1);
+ client_data->client = E_CAL_CLIENT (g_object_ref (client));
+ client_data->backend_died_id = g_signal_connect (client,
+ "backend-died",
+ G_CALLBACK (backend_died_cb),
+ sources);
+
+ g_hash_table_insert (sources->clients, g_object_ref (source), client_data);
+ }
+ g_mutex_unlock (&sources->clients_lock);
+
+ return client;
+}
+
+typedef struct _AsyncContext {
+ ESource *source;
+ ECalClientSourceType source_type;
+ guint32 wait_for_connected_seconds;
+} AsyncContext;
+
+static void
+async_context_free (gpointer ptr)
+{
+ AsyncContext *ctx = ptr;
+
+ if (ctx)
+ {
+ g_clear_object (&ctx->source);
+ g_free (ctx);
+ }
+}
+
+static void
+calendar_sources_connect_client_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ CalendarSources *sources = source_object;
+ AsyncContext *ctx = task_data;
+ EClient *client;
+ GError *local_error = NULL;
+
+ client = calendar_sources_connect_client_sync (sources, ctx->source, ctx->source_type,
+ ctx->wait_for_connected_seconds, cancellable, &local_error);
+ if (!client)
+ {
+ if (local_error)
+ g_task_return_error (task, local_error);
+ else
+ g_task_return_pointer (task, NULL, NULL);
+ } else {
+ g_task_return_pointer (task, client, g_object_unref);
+ }
+}
+
+void
+calendar_sources_connect_client (CalendarSources *sources,
+ ESource *source,
+ ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ AsyncContext *ctx;
+ g_autoptr (GTask) task = NULL;
+
+ ctx = g_new0 (AsyncContext, 1);
+ ctx->source = g_object_ref (source);
+ ctx->source_type = source_type;
+ ctx->wait_for_connected_seconds = wait_for_connected_seconds;
+
+ task = g_task_new (sources, cancellable, callback, user_data);
+ g_task_set_source_tag (task, calendar_sources_connect_client);
+ g_task_set_task_data (task, ctx, async_context_free);
+
+ g_task_run_in_thread (task, calendar_sources_connect_client_thread);
+}
+
+EClient *
+calendar_sources_connect_client_finish (CalendarSources *sources,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, sources), NULL);
+ g_return_val_if_fail (g_async_result_is_tagged (result, calendar_sources_connect_client), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+
+void
+print_debug (const gchar *format,
+ ...)
+{
+ g_autofree char *s = NULL;
+ g_autofree char *timestamp = NULL;
+ va_list ap;
+ g_autoptr (GDateTime) now = NULL;
+ static size_t once_init_value = 0;
+ static gboolean show_debug = FALSE;
+ static guint pid = 0;
+
+ if (g_once_init_enter (&once_init_value))
+ {
+ show_debug = (g_getenv ("CALENDAR_SERVER_DEBUG") != NULL);
+ pid = getpid ();
+ g_once_init_leave (&once_init_value, 1);
+ }
+
+ if (!show_debug)
+ goto out;
+
+ now = g_date_time_new_now_local ();
+ timestamp = g_date_time_format (now, "%H:%M:%S");
+
+ va_start (ap, format);
+ s = g_strdup_vprintf (format, ap);
+ va_end (ap);
+
+ g_print ("gnome-shell-calendar-server[%d]: %s.%03d: %s\n",
+ pid, timestamp, g_date_time_get_microsecond (now), s);
+ out:
+ ;
+}