1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar factory
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 * Copyright (C) 2009 Intel Corporation
8 * Federico Mena-Quintero <federico@ximian.com>
9 * JP Rosevear <jpr@ximian.com>
10 * Ross Burton <ross@linux.intel.com>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of version 2 of the GNU Lesser General Public
14 * License as published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 #include <glib/gi18n.h>
33 #include <libedataserver/e-source-calendar.h>
35 #include "e-cal-backend.h"
36 #include "e-cal-backend-factory.h"
37 #include "e-data-cal.h"
38 #include "e-data-cal-factory.h"
40 #include "e-gdbus-cal-factory.h"
42 #include <libical/ical.h>
46 #define E_DATA_CAL_FACTORY_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE \
48 ((obj), E_TYPE_DATA_CAL_FACTORY, EDataCalFactoryPrivate))
50 struct _EDataCalFactoryPrivate {
51 ESourceRegistry *registry;
52 EGdbusCalFactory *gdbus_object;
54 GMutex *calendars_lock;
55 /* A hash of object paths for calendar URIs to EDataCals */
56 GHashTable *calendars;
58 GMutex *connections_lock;
59 /* This is a hash of client addresses to GList* of EDataCals */
60 GHashTable *connections;
68 /* Forward Declarations */
69 static void e_data_cal_factory_initable_init
70 (GInitableIface *interface);
72 G_DEFINE_TYPE_WITH_CODE (
76 G_IMPLEMENT_INTERFACE (
78 e_data_cal_factory_initable_init))
81 data_cal_factory_ref_backend (EDataFactory *factory,
87 ESourceBackend *extension;
88 const gchar *extension_name;
89 const gchar *type_string;
95 extension_name = E_SOURCE_EXTENSION_CALENDAR;
96 type_string = "VEVENT";
99 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
100 type_string = "VTODO";
103 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
104 type_string = "VJOURNAL";
107 g_return_val_if_reached (NULL);
110 extension = e_source_get_extension (source, extension_name);
111 backend_name = e_source_backend_dup_backend_name (extension);
113 if (backend_name == NULL || *backend_name == '\0') {
115 error, E_DATA_CAL_ERROR, NoSuchCal,
116 _("No backend name in source '%s'"),
117 e_source_get_display_name (source));
118 g_free (backend_name);
122 hash_key = g_strdup_printf ("%s:%s", backend_name, type_string);
123 backend = e_data_factory_ref_backend (factory, hash_key, source);
128 error, E_DATA_CAL_ERROR, NoSuchCal,
129 _("Invalid backend name '%s' in source '%s'"),
130 backend_name, e_source_get_display_name (source));
132 g_free (backend_name);
138 construct_cal_factory_path (void)
140 static volatile gint counter = 1;
142 g_atomic_int_inc (&counter);
144 return g_strdup_printf (
145 "/org/gnome/evolution/dataserver/Calendar/%d/%u",
150 remove_dead_calendar_cb (gpointer path,
152 gpointer dead_calendar)
154 return calendar == dead_calendar;
158 calendar_freed_cb (EDataCalFactory *factory,
161 EDataCalFactoryPrivate *priv = factory->priv;
163 gpointer hkey, hvalue;
165 d (g_debug ("in factory %p (%p) is dead", factory, dead));
167 g_mutex_lock (priv->calendars_lock);
168 g_mutex_lock (priv->connections_lock);
170 g_hash_table_foreach_remove (
171 priv->calendars, remove_dead_calendar_cb, dead);
173 g_hash_table_iter_init (&iter, priv->connections);
174 while (g_hash_table_iter_next (&iter, &hkey, &hvalue)) {
175 GList *calendars = hvalue;
177 if (g_list_find (calendars, dead)) {
178 calendars = g_list_remove (calendars, dead);
179 if (calendars != NULL)
180 g_hash_table_insert (
182 g_strdup (hkey), calendars);
184 g_hash_table_remove (priv->connections, hkey);
190 g_mutex_unlock (priv->connections_lock);
191 g_mutex_unlock (priv->calendars_lock);
193 e_dbus_server_release (E_DBUS_SERVER (factory));
197 impl_CalFactory_get_cal (EGdbusCalFactory *object,
198 GDBusMethodInvocation *invocation,
199 const gchar * const *in_source_type,
200 EDataCalFactory *factory)
204 EDataCalFactoryPrivate *priv = factory->priv;
205 GDBusConnection *connection;
206 ESourceRegistry *registry;
211 GError *error = NULL;
215 sender = g_dbus_method_invocation_get_sender (invocation);
216 connection = g_dbus_method_invocation_get_connection (invocation);
218 registry = e_data_cal_factory_get_registry (factory);
220 if (!e_gdbus_cal_factory_decode_get_cal (in_source_type, &uid, &type)) {
221 error = g_error_new (
222 E_DATA_CAL_ERROR, NoSuchCal, _("Invalid call"));
223 g_dbus_method_invocation_return_gerror (invocation, error);
224 g_error_free (error);
229 if (uid == NULL || *uid == '\0') {
230 error = g_error_new_literal (
231 E_DATA_CAL_ERROR, NoSuchCal,
232 _("Missing source UID"));
233 g_dbus_method_invocation_return_gerror (invocation, error);
234 g_error_free (error);
240 source = e_source_registry_ref_source (registry, uid);
242 if (source == NULL) {
243 error = g_error_new (
244 E_DATA_CAL_ERROR, NoSuchCal,
245 _("No such source for UID '%s'"), uid);
246 g_dbus_method_invocation_return_gerror (invocation, error);
247 g_error_free (error);
253 backend = data_cal_factory_ref_backend (
254 E_DATA_FACTORY (factory), source, type, &error);
256 g_object_unref (source);
259 g_dbus_method_invocation_return_gerror (invocation, error);
260 g_error_free (error);
265 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
267 g_mutex_lock (priv->calendars_lock);
269 e_dbus_server_hold (E_DBUS_SERVER (factory));
271 path = construct_cal_factory_path ();
272 calendar = e_data_cal_new (E_CAL_BACKEND (backend));
273 g_hash_table_insert (priv->calendars, g_strdup (path), calendar);
274 e_cal_backend_add_client (E_CAL_BACKEND (backend), calendar);
275 e_data_cal_register_gdbus_object (calendar, connection, path, &error);
277 G_OBJECT (calendar), (GWeakNotify)
278 calendar_freed_cb, factory);
280 g_object_unref (backend);
282 /* Update the hash of open connections. */
283 g_mutex_lock (priv->connections_lock);
284 list = g_hash_table_lookup (priv->connections, sender);
285 list = g_list_prepend (list, calendar);
286 g_hash_table_insert (priv->connections, g_strdup (sender), list);
287 g_mutex_unlock (priv->connections_lock);
289 g_mutex_unlock (priv->calendars_lock);
293 e_gdbus_cal_factory_complete_get_cal (
294 object, invocation, path, error);
297 g_error_free (error);
305 remove_data_cal_cb (EDataCal *data_cal)
307 ECalBackend *backend;
309 g_return_if_fail (data_cal != NULL);
311 backend = e_data_cal_get_backend (data_cal);
312 e_cal_backend_remove_client (backend, data_cal);
314 g_object_unref (data_cal);
318 data_cal_factory_get_property (GObject *object,
323 switch (property_id) {
327 e_data_cal_factory_get_registry (
328 E_DATA_CAL_FACTORY (object)));
332 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
336 data_cal_factory_dispose (GObject *object)
338 EDataCalFactoryPrivate *priv;
340 priv = E_DATA_CAL_FACTORY_GET_PRIVATE (object);
342 if (priv->registry != NULL) {
343 g_object_unref (priv->registry);
344 priv->registry = NULL;
347 if (priv->gdbus_object != NULL) {
348 g_object_unref (priv->gdbus_object);
349 priv->gdbus_object = NULL;
352 /* Chain up to parent's dispose() method. */
353 G_OBJECT_CLASS (e_data_cal_factory_parent_class)->dispose (object);
357 data_cal_factory_finalize (GObject *object)
359 EDataCalFactoryPrivate *priv;
361 priv = E_DATA_CAL_FACTORY_GET_PRIVATE (object);
363 g_hash_table_destroy (priv->calendars);
364 g_hash_table_destroy (priv->connections);
366 g_mutex_free (priv->calendars_lock);
367 g_mutex_free (priv->connections_lock);
369 /* Chain up to parent's finalize() method. */
370 G_OBJECT_CLASS (e_data_cal_factory_parent_class)->finalize (object);
374 data_cal_factory_bus_acquired (EDBusServer *server,
375 GDBusConnection *connection)
377 EDataCalFactoryPrivate *priv;
378 guint registration_id;
379 GError *error = NULL;
381 priv = E_DATA_CAL_FACTORY_GET_PRIVATE (server);
383 registration_id = e_gdbus_cal_factory_register_object (
386 "/org/gnome/evolution/dataserver/CalendarFactory",
391 "Failed to register a CalendarFactory object: %s",
393 g_assert_not_reached ();
396 g_assert (registration_id > 0);
398 /* Chain up to parent's bus_acquired() method. */
399 E_DBUS_SERVER_CLASS (e_data_cal_factory_parent_class)->
400 bus_acquired (server, connection);
404 data_cal_factory_bus_name_lost (EDBusServer *server,
405 GDBusConnection *connection)
407 EDataCalFactoryPrivate *priv;
411 priv = E_DATA_CAL_FACTORY_GET_PRIVATE (server);
413 g_mutex_lock (priv->connections_lock);
415 while (g_hash_table_lookup_extended (
417 CALENDAR_DBUS_SERVICE_NAME,
418 (gpointer) &key, (gpointer) &list)) {
421 /* this should trigger the calendar's weak ref notify
422 * function, which will remove it from the list before
423 * it's freed, and will remove the connection from
424 * priv->connections once they're all gone */
425 copy = g_list_copy (list);
426 g_list_foreach (copy, (GFunc) remove_data_cal_cb, NULL);
430 g_mutex_unlock (priv->connections_lock);
432 /* Chain up to parent's bus_name_lost() method. */
433 E_DBUS_SERVER_CLASS (e_data_cal_factory_parent_class)->
434 bus_name_lost (server, connection);
438 data_cal_factory_quit_server (EDBusServer *server,
439 EDBusServerExitCode exit_code)
441 /* This factory does not support reloading, so stop the signal
442 * emission and return without chaining up to prevent quitting. */
443 if (exit_code == E_DBUS_SERVER_EXIT_RELOAD) {
444 g_signal_stop_emission_by_name (server, "quit-server");
448 /* Chain up to parent's quit_server() method. */
449 E_DBUS_SERVER_CLASS (e_data_cal_factory_parent_class)->
450 quit_server (server, exit_code);
454 data_cal_factory_initable_init (GInitable *initable,
455 GCancellable *cancellable,
458 EDataCalFactoryPrivate *priv;
460 priv = E_DATA_CAL_FACTORY_GET_PRIVATE (initable);
462 priv->registry = e_source_registry_new_sync (cancellable, error);
464 return (priv->registry != NULL);
468 e_data_cal_factory_class_init (EDataCalFactoryClass *class)
470 GObjectClass *object_class;
471 EDBusServerClass *dbus_server_class;
472 EDataFactoryClass *data_factory_class;
474 g_type_class_add_private (class, sizeof (EDataCalFactoryPrivate));
476 object_class = G_OBJECT_CLASS (class);
477 object_class->get_property = data_cal_factory_get_property;
478 object_class->dispose = data_cal_factory_dispose;
479 object_class->finalize = data_cal_factory_finalize;
481 dbus_server_class = E_DBUS_SERVER_CLASS (class);
482 dbus_server_class->bus_name = CALENDAR_DBUS_SERVICE_NAME;
483 dbus_server_class->module_directory = BACKENDDIR;
484 dbus_server_class->bus_acquired = data_cal_factory_bus_acquired;
485 dbus_server_class->bus_name_lost = data_cal_factory_bus_name_lost;
486 dbus_server_class->quit_server = data_cal_factory_quit_server;
488 data_factory_class = E_DATA_FACTORY_CLASS (class);
489 data_factory_class->backend_factory_type = E_TYPE_CAL_BACKEND_FACTORY;
491 g_object_class_install_property (
494 g_param_spec_object (
497 "Data source registry",
498 E_TYPE_SOURCE_REGISTRY,
500 G_PARAM_STATIC_STRINGS));
504 e_data_cal_factory_initable_init (GInitableIface *interface)
506 interface->init = data_cal_factory_initable_init;
510 e_data_cal_factory_init (EDataCalFactory *factory)
512 factory->priv = E_DATA_CAL_FACTORY_GET_PRIVATE (factory);
514 factory->priv->gdbus_object = e_gdbus_cal_factory_stub_new ();
516 factory->priv->gdbus_object, "handle-get-cal",
517 G_CALLBACK (impl_CalFactory_get_cal), factory);
519 factory->priv->calendars_lock = g_mutex_new ();
520 factory->priv->calendars = g_hash_table_new_full (
521 g_str_hash, g_str_equal,
522 (GDestroyNotify) g_free,
523 (GDestroyNotify) NULL);
525 factory->priv->connections_lock = g_mutex_new ();
526 factory->priv->connections = g_hash_table_new_full (
527 g_str_hash, g_str_equal,
528 (GDestroyNotify) g_free,
529 (GDestroyNotify) NULL);
533 e_data_cal_factory_new (GCancellable *cancellable,
536 icalarray *builtin_timezones;
539 #ifdef HAVE_ICAL_UNKNOWN_TOKEN_HANDLING
540 ical_set_unknown_token_handling_setting (ICAL_DISCARD_TOKEN);
543 /* XXX Pre-load all built-in timezones in libical.
545 * Built-in time zones in libical 0.43 are loaded on demand,
546 * but not in a thread-safe manner, resulting in a race when
547 * multiple threads call icaltimezone_load_builtin_timezone()
548 * on the same time zone. Until built-in time zone loading
549 * in libical is made thread-safe, work around the issue by
550 * loading all built-in time zones now, so libical's internal
551 * time zone array will be fully populated before any threads
554 builtin_timezones = icaltimezone_get_builtin_timezones ();
555 for (ii = 0; ii < builtin_timezones->num_elements; ii++) {
558 zone = icalarray_element_at (builtin_timezones, ii);
560 /* We don't care about the component right now,
561 * we just need some function that will trigger
562 * icaltimezone_load_builtin_timezone(). */
563 icaltimezone_get_component (zone);
566 return g_initable_new (
567 E_TYPE_DATA_CAL_FACTORY,
568 cancellable, error, NULL);
572 e_data_cal_factory_get_registry (EDataCalFactory *factory)
574 g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (factory), NULL);
576 return factory->priv->registry;