s/EVOLUTION_LOCALEDIR/LOCALEDIR/
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-data-cal-factory.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar factory
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  * Copyright (C) 2009 Intel Corporation
6  *
7  * Authors:
8  *   Federico Mena-Quintero <federico@ximian.com>
9  *   JP Rosevear <jpr@ximian.com>
10  *   Ross Burton <ross@linux.intel.com>
11  *
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.
15  *
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.
20  *
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.
24  */
25
26 #include <config.h>
27 #include <locale.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <glib/gi18n.h>
32 #include <glib-object.h>
33 #include <dbus/dbus-glib.h>
34 #include <dbus/dbus-glib-lowlevel.h>
35 #include <dbus/dbus-glib-bindings.h>
36
37 #include "libedataserver/e-url.h"
38 #include "libedataserver/e-source.h"
39 #include "libedataserver/e-source-list.h"
40 #include "libebackend/e-data-server-module.h"
41 #include <libebackend/e-offline-listener.h>
42 #include "libecal/e-cal.h"
43 #include "e-cal-backend.h"
44 #include "e-cal-backend-factory.h"
45 #include "e-data-cal.h"
46 #include "e-data-cal-factory.h"
47 #include "e-cal-backend-loader-factory.h"
48
49 #define d(x)
50
51 static void impl_CalFactory_getCal (EDataCalFactory *factory, const gchar *IN_uri, EDataCalObjType type, DBusGMethodInvocation *context);
52 #include "e-data-cal-factory-glue.h"
53
54 static GMainLoop *loop;
55 static EDataCalFactory *factory;
56 extern DBusGConnection *connection;
57
58 /* Convenience macro to test and set a GError/return on failure */
59 #define g_set_error_val_if_fail(test, returnval, error, domain, code) G_STMT_START{ \
60                 if G_LIKELY (test) {} else {                            \
61                         g_set_error (error, domain, code, #test);       \
62                         g_warning(#test " failed");                     \
63                         return (returnval);                             \
64                 }                                                       \
65         } G_STMT_END
66
67 G_DEFINE_TYPE(EDataCalFactory, e_data_cal_factory, G_TYPE_OBJECT);
68
69 #define E_DATA_CAL_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), E_TYPE_DATA_CAL_FACTORY, EDataCalFactoryPrivate))
70
71 struct _EDataCalFactoryPrivate {
72         /* Hash table from URI method strings to GType * for backend class types */
73         GHashTable *methods;
74
75         GHashTable *backends;
76         /* mutex to access backends hash table */
77         GMutex *backends_mutex;
78
79         GHashTable *calendars;
80
81         GHashTable *connections;
82
83         gint mode;
84
85         /* this is for notifications of source changes */
86         ESourceList *lists[E_CAL_SOURCE_TYPE_LAST];
87
88         /* backends divided by their type */
89         GSList *backends_by_type[E_CAL_SOURCE_TYPE_LAST];
90
91         guint exit_timeout;
92 };
93
94 /* Create the EDataCalFactory error quark */
95 GQuark
96 e_data_cal_factory_error_quark (void)
97 {
98         static GQuark quark = 0;
99         if (!quark)
100                 quark = g_quark_from_static_string ("e_data_cal_factory_error");
101         return quark;
102 }
103
104 static icalcomponent_kind
105 calobjtype_to_icalkind (const EDataCalObjType type)
106 {
107         switch (type) {
108         case Event:
109                 return ICAL_VEVENT_COMPONENT;
110         case Todo:
111                 return ICAL_VTODO_COMPONENT;
112         case Journal:
113                 return ICAL_VJOURNAL_COMPONENT;
114         case AnyType:
115                 return ICAL_NO_COMPONENT;
116         }
117
118         return ICAL_NO_COMPONENT;
119 }
120
121 static const gchar *
122 calobjtype_to_string (const EDataCalObjType type)
123 {
124         switch (type) {
125         case Event:
126                 return "VEVENT";
127         case Todo:
128                 return "VTODO";
129         case Journal:
130                 return "VJOURNAL";
131         case AnyType:
132                 break;
133         }
134
135         return "UNKNOWN COMPONENT";
136 }
137
138 static ECalSourceType
139 icalkind_to_ecalsourcetype (const icalcomponent_kind kind)
140 {
141         switch (kind) {
142         case ICAL_VEVENT_COMPONENT:
143                 return E_CAL_SOURCE_TYPE_EVENT;
144         case ICAL_VTODO_COMPONENT:
145                 return E_CAL_SOURCE_TYPE_TODO;
146         case ICAL_VJOURNAL_COMPONENT:
147                 return E_CAL_SOURCE_TYPE_JOURNAL;
148         default:
149                 break;
150         }
151
152         return E_CAL_SOURCE_TYPE_LAST;
153 }
154
155 static void
156 update_source_in_backend (ECalBackend *backend, ESource *updated_source)
157 {
158         xmlNodePtr xml;
159
160         g_return_if_fail (backend != NULL);
161         g_return_if_fail (updated_source != NULL);
162
163         xml = xmlNewNode (NULL, (const xmlChar *)"dummy");
164         e_source_dump_to_xml_node (updated_source, xml);
165         e_source_update_from_xml_node (e_cal_backend_get_source (backend), xml->children, NULL);
166         xmlFreeNode (xml);
167 }
168
169 static void
170 source_list_changed_cb (ESourceList *list, EDataCalFactory *factory)
171 {
172         EDataCalFactoryPrivate *priv;
173         gint i;
174
175         g_return_if_fail (list != NULL);
176         g_return_if_fail (factory != NULL);
177         g_return_if_fail (E_IS_DATA_CAL_FACTORY (factory));
178
179         priv = factory->priv;
180
181         g_mutex_lock (priv->backends_mutex);
182
183         for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) {
184                 if (list == priv->lists[i]) {
185                         GSList *l;
186
187                         for (l = priv->backends_by_type [i]; l; l = l->next) {
188                                 ECalBackend *backend = l->data;
189                                 ESource *source, *list_source;
190
191                                 source = e_cal_backend_get_source (backend);
192                                 list_source = e_source_list_peek_source_by_uid (priv->lists[i], e_source_peek_uid (source));
193
194                                 if (list_source) {
195                                         update_source_in_backend (backend, list_source);
196                                 }
197                         }
198
199                         break;
200                 }
201         }
202
203         g_mutex_unlock (priv->backends_mutex);
204 }
205
206 static ECalBackendFactory *
207 get_backend_factory (GHashTable *methods, const gchar *method, icalcomponent_kind kind)
208 {
209         GHashTable *kinds;
210         ECalBackendFactory *factory;
211
212         kinds = g_hash_table_lookup (methods, method);
213         if (!kinds) {
214                 return NULL;
215         }
216
217         factory = g_hash_table_lookup (kinds, GINT_TO_POINTER (kind));
218
219         return factory;
220 }
221
222 static gchar *
223 construct_cal_factory_path (void)
224 {
225         static volatile gint counter = 1;
226
227         return g_strdup_printf (
228                 "/org/gnome/evolution/dataserver/calendar/%d/%u",
229                 getpid (),
230                 g_atomic_int_exchange_and_add (&counter, 1));
231 }
232
233 static void
234 my_remove (gchar *key, GObject *dead)
235 {
236         EDataCalFactoryPrivate *priv = factory->priv;
237         GHashTableIter iter;
238         gpointer hkey, hvalue;
239
240         d (g_debug ("%s (%p) is dead", key, dead));
241
242         g_hash_table_remove (priv->calendars, key);
243
244         g_hash_table_iter_init (&iter, priv->connections);
245         while (g_hash_table_iter_next (&iter, &hkey, &hvalue)) {
246                 GList *calendars = hvalue;
247
248                 if (g_list_find (calendars, dead)) {
249                         calendars = g_list_remove (calendars, dead);
250                         if (calendars) {
251                                 g_hash_table_insert (priv->connections, g_strdup (hkey), calendars);
252                         } else {
253                                 g_hash_table_remove (priv->connections, hkey);
254                         }
255
256                         break;
257                 }
258         }
259
260         g_free (key);
261
262         /* If there are no open calendars, start a timer to quit */
263         if (priv->exit_timeout == 0 && g_hash_table_size (priv->calendars) == 0) {
264                 priv->exit_timeout = g_timeout_add (10000, (GSourceFunc)g_main_loop_quit, loop);
265         }
266 }
267
268 struct find_backend_data
269 {
270         const gchar *str_uri;
271         ECalBackend *backend;
272         icalcomponent_kind kind;
273 };
274
275 static void
276 find_backend_cb (gpointer key, gpointer value, gpointer data)
277 {
278         struct find_backend_data *fbd = data;
279
280         if (fbd && fbd->str_uri && !fbd->backend) {
281                 ECalBackend *backend = value;
282                 gchar *str_uri;
283
284                 str_uri = e_source_get_uri (e_cal_backend_get_source (backend));
285
286                 if (str_uri && g_str_equal (str_uri, fbd->str_uri)) {
287                         const gchar *uid_kind = key, *pos;
288
289                         pos = strrchr (uid_kind, ':');
290                         if (pos && atoi (pos + 1) == fbd->kind)
291                                 fbd->backend = backend;
292                 }
293
294                 g_free (str_uri);
295         }
296 }
297
298 static void
299 impl_CalFactory_getCal (EDataCalFactory         *factory,
300                         const gchar             *source_xml,
301                         EDataCalObjType          type,
302                         DBusGMethodInvocation   *context)
303 {
304         EDataCal *calendar;
305         EDataCalFactoryPrivate *priv = factory->priv;
306         ECalBackendFactory *backend_factory;
307         ECalBackend *backend;
308         ESource *source;
309         gchar *str_uri;
310         EUri *uri;
311         gchar *uid_type_string;
312         gchar *path = NULL, *sender;
313         GList *list;
314         GError *error = NULL;
315
316         /* Remove a pending exit */
317         if (priv->exit_timeout) {
318                 g_source_remove (priv->exit_timeout);
319                 priv->exit_timeout = 0;
320         }
321
322         source = e_source_new_from_standalone_xml (source_xml);
323         if (!source) {
324                 dbus_g_method_return_error (context, g_error_new (E_DATA_CAL_ERROR, NoSuchCal, _("Invalid source")));
325                 return;
326         }
327
328         /* Get the URI so we can extract the protocol */
329         str_uri = e_source_get_uri (source);
330         if (!str_uri) {
331                 g_object_unref (source);
332
333                 dbus_g_method_return_error (context, g_error_new (E_DATA_CAL_ERROR, NoSuchCal, _("Invalid source")));
334                 return;
335         }
336
337         /* Parse the uri */
338         uri = e_uri_new (str_uri);
339         if (!uri) {
340                 dbus_g_method_return_error (context, g_error_new (E_DATA_CAL_ERROR, NoSuchCal, _("Invalid URI")));
341                 return;
342         }
343
344         uid_type_string = g_strdup_printf ("%s:%d", e_source_peek_uid (source), (gint)calobjtype_to_icalkind (type));
345
346         /* Find the associated backend factory (if any) */
347         backend_factory = get_backend_factory (priv->methods, uri->protocol, calobjtype_to_icalkind (type));
348         if (!backend_factory) {
349                 error = g_error_new (
350                         E_DATA_CAL_ERROR, NoSuchCal,
351                         _("No backend factory for '%s' of '%s'"),
352                         uri->protocol, calobjtype_to_string (type));
353
354                 goto cleanup2;
355         }
356
357         g_mutex_lock (priv->backends_mutex);
358
359         /* Look for an existing backend */
360         backend = g_hash_table_lookup (factory->priv->backends, uid_type_string);
361
362         if (!backend) {
363                 /* find backend by URL, if opened, thus functions like e_cal_system_new_* will not
364                    create new backends for the same url */
365                 struct find_backend_data fbd;
366
367                 fbd.str_uri = str_uri;
368                 fbd.kind = calobjtype_to_icalkind (type);
369                 fbd.backend = NULL;
370
371                 g_hash_table_foreach (priv->backends, find_backend_cb, &fbd);
372
373                 if (fbd.backend) {
374                         backend = fbd.backend;
375                         g_object_unref (source);
376                         source = g_object_ref (e_cal_backend_get_source (backend));
377                 }
378         }
379
380         if (!backend) {
381                 ECalSourceType st;
382
383                 /* There was no existing backend, create a new one */
384                 if (E_IS_CAL_BACKEND_LOADER_FACTORY (backend_factory)) {
385                         backend = E_CAL_BACKEND_LOADER_FACTORY_GET_CLASS (backend_factory)->new_backend_with_protocol ((ECalBackendLoaderFactory *)backend_factory,
386                                                                                                                        source, uri->protocol);
387                 } else
388                         backend = e_cal_backend_factory_new_backend (backend_factory, source);
389
390                 if (!backend) {
391                         error = g_error_new (E_DATA_CAL_ERROR, NoSuchCal, _("Could not instantiate backend"));
392                         goto cleanup;
393                 }
394
395                 st = icalkind_to_ecalsourcetype (e_cal_backend_get_kind (backend));
396                 if (st != E_CAL_SOURCE_TYPE_LAST) {
397                         if (!priv->lists[st] && e_cal_get_sources (&(priv->lists[st]), st, NULL)) {
398                                 g_signal_connect (priv->lists[st], "changed", G_CALLBACK (source_list_changed_cb), factory);
399                         }
400
401                         if (priv->lists[st])
402                                 priv->backends_by_type[st] = g_slist_prepend (priv->backends_by_type[st], backend);
403                 }
404
405                 /* Track the backend */
406                 g_hash_table_insert (priv->backends, g_strdup (uid_type_string), backend);
407
408                 e_cal_backend_set_mode (backend, priv->mode);
409         } else if (!e_source_equal (source, e_cal_backend_get_source (backend))) {
410                 /* source changed, update it in a backend */
411                 update_source_in_backend (backend, source);
412         }
413
414         calendar = e_data_cal_new (backend, source);
415         e_cal_backend_add_client (backend, calendar);
416
417         path = construct_cal_factory_path ();
418         dbus_g_connection_register_g_object (connection, path, G_OBJECT (calendar));
419         g_object_weak_ref (G_OBJECT (calendar), (GWeakNotify)my_remove, path);
420
421         g_hash_table_insert (priv->calendars, g_strdup (path), calendar);
422
423         sender = dbus_g_method_get_sender (context);
424         list = g_hash_table_lookup (priv->connections, sender);
425         list = g_list_prepend (list, calendar);
426         g_hash_table_insert (priv->connections, sender, list);
427
428  cleanup:
429         /* The reason why the lock is held for such a long time is that there is
430            a subtle race where e_cal_backend_add_client() can be called just
431            before e_cal_backend_finalize() is called from the
432            backend_last_client_gone_cb(), for details see bug 506457. */
433         g_mutex_unlock (priv->backends_mutex);
434  cleanup2:
435         g_free (str_uri);
436         e_uri_free (uri);
437         g_free (uid_type_string);
438         g_object_unref (source);
439
440         if (error)
441                 dbus_g_method_return_error (context, error);
442         else
443                 dbus_g_method_return (context, path);
444 }
445
446 static void
447 remove_data_cal_cb (gpointer data_cl, gpointer user_data)
448 {
449         EDataCal *data_cal;
450
451         data_cal = E_DATA_CAL (data_cl);
452         g_return_if_fail (data_cal != NULL);
453
454         e_cal_backend_remove_client (e_data_cal_get_backend (data_cal), data_cal);
455
456         g_object_unref (data_cal);
457 }
458
459 static void
460 name_owner_changed (DBusGProxy      *proxy,
461                     const gchar      *name,
462                     const gchar      *prev_owner,
463                     const gchar      *new_owner,
464                     EDataCalFactory *factory)
465 {
466         if (strcmp (new_owner, "") == 0 && strcmp (name, prev_owner) == 0) {
467                 gchar *key;
468                 GList *list = NULL;
469                 while (g_hash_table_lookup_extended (factory->priv->connections, prev_owner, (gpointer)&key, (gpointer)&list)) {
470                         GList *copy = g_list_copy (list);
471
472                         /* this should trigger the book's weak ref notify
473                          * function, which will remove it from the list before
474                          * it's freed, and will remove the connection from
475                          * priv->connections once they're all gone */
476                         g_list_foreach (copy, remove_data_cal_cb, NULL);
477                         g_list_free (copy);
478                 }
479         }
480 }
481
482 /* Class initialization function for the calendar factory */
483 static void
484 e_data_cal_factory_class_init (EDataCalFactoryClass *klass)
485 {
486         g_type_class_add_private (klass, sizeof (EDataCalFactoryPrivate));
487         dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_e_data_cal_factory_object_info);
488 }
489
490 /* Instance init */
491 static void
492 e_data_cal_factory_init (EDataCalFactory *factory)
493 {
494         factory->priv = E_DATA_CAL_FACTORY_GET_PRIVATE (factory);
495
496         factory->priv->methods = g_hash_table_new_full (g_str_hash, g_str_equal,
497                                                         (GDestroyNotify) g_free, (GDestroyNotify) g_hash_table_destroy);
498
499         factory->priv->backends_mutex = g_mutex_new ();
500         factory->priv->backends = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
501
502         factory->priv->calendars = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
503         factory->priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
504
505         e_data_server_module_init ();
506         e_data_cal_factory_register_backends (factory);
507 }
508
509 static void
510 set_backend_online_status (gpointer key, gpointer value, gpointer data)
511 {
512         ECalBackend *backend = E_CAL_BACKEND (value);
513
514         e_cal_backend_set_mode (backend,  GPOINTER_TO_INT (data));
515 }
516
517 /**
518  * e_data_cal_factory_set_backend_mode:
519  * @factory: A calendar factory.
520  * @mode: Online mode to set.
521  *
522  * Sets the online mode for all backends created by the given factory.
523  */
524 void
525 e_data_cal_factory_set_backend_mode (EDataCalFactory *factory, gint mode)
526 {
527         EDataCalFactoryPrivate *priv = factory->priv;
528
529         priv->mode = mode;
530         g_mutex_lock (priv->backends_mutex);
531         g_hash_table_foreach (priv->backends, set_backend_online_status, GINT_TO_POINTER (priv->mode));
532         g_mutex_unlock (priv->backends_mutex);
533 }
534
535 /**
536  * e_data_cal_factory_register_backend:
537  * @factory: A calendar factory.
538  * @backend_factory: The object responsible for creating backends.
539  *
540  * Registers an #ECalBackend subclass that will be used to handle URIs
541  * with a particular method.  When the factory is asked to open a
542  * particular URI, it will look in its list of registered methods and
543  * create a backend of the appropriate type.
544  **/
545 void
546 e_data_cal_factory_register_backend (EDataCalFactory *factory, ECalBackendFactory *backend_factory)
547 {
548         EDataCalFactoryPrivate *priv;
549         const gchar *method;
550         GHashTable *kinds;
551         GType type;
552         icalcomponent_kind kind;
553         GSList *methods = NULL, *l;
554
555         g_return_if_fail (factory && E_IS_DATA_CAL_FACTORY (factory));
556         g_return_if_fail (backend_factory && E_IS_CAL_BACKEND_FACTORY (backend_factory));
557
558         priv = factory->priv;
559
560         if (E_IS_CAL_BACKEND_LOADER_FACTORY (backend_factory)) {
561                 GSList *list = E_CAL_BACKEND_LOADER_FACTORY_GET_CLASS (backend_factory)->get_protocol_list ((ECalBackendLoaderFactory *) backend_factory);
562                 methods = g_slist_copy (list);
563         } else if (E_CAL_BACKEND_FACTORY_GET_CLASS (backend_factory)->get_protocol) {
564                 method = E_CAL_BACKEND_FACTORY_GET_CLASS (backend_factory)->get_protocol (backend_factory);
565                 methods = g_slist_append (methods, (gpointer) method);
566         } else {
567                 g_assert_not_reached ();
568                 return;
569         }
570
571         kind = E_CAL_BACKEND_FACTORY_GET_CLASS (backend_factory)->get_kind (backend_factory);
572
573         for (l= methods; l != NULL; l = g_slist_next (l)) {
574                 gchar *method_str;
575
576                 method = l->data;
577
578                 method_str = g_ascii_strdown (method, -1);
579
580                 kinds = g_hash_table_lookup (priv->methods, method_str);
581                 if (kinds) {
582                         type = GPOINTER_TO_INT (g_hash_table_lookup (kinds, GINT_TO_POINTER (kind)));
583                         if (type) {
584                                 g_warning (G_STRLOC ": method `%s' already registered", method_str);
585                                 g_free (method_str);
586                                 g_slist_free (methods);
587                                 return;
588                         }
589
590                         g_free (method_str);
591                 } else {
592                         kinds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
593                         g_hash_table_insert (priv->methods, method_str, kinds);
594                 }
595
596                 g_hash_table_insert (kinds, GINT_TO_POINTER (kind), backend_factory);
597         }
598         g_slist_free (methods);
599 }
600
601 /**
602  * e_data_cal_factory_register_backends:
603  * @cal_factory: A calendar factory.
604  *
605  * Register all backends for the given factory.
606  */
607 void
608 e_data_cal_factory_register_backends (EDataCalFactory *cal_factory)
609 {
610         GList *factories, *f;
611
612         factories = e_data_server_get_extensions_for_type (E_TYPE_CAL_BACKEND_FACTORY);
613
614         for (f = factories; f; f = f->next) {
615                 ECalBackendFactory *backend_factory = f->data;
616
617                 e_data_cal_factory_register_backend (cal_factory, g_object_ref (backend_factory));
618         }
619
620         e_data_server_extension_list_free (factories);
621         e_data_server_module_remove_unused ();
622 }
623
624 /**
625  * e_data_cal_factory_get_n_backends
626  * @factory: A calendar factory.
627  *
628  * Get the number of backends currently active in the given factory.
629  *
630  * Returns: the number of backends.
631  */
632 gint
633 e_data_cal_factory_get_n_backends (EDataCalFactory *factory)
634 {
635         EDataCalFactoryPrivate *priv;
636         gint sz;
637
638         g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (factory), 0);
639
640         priv = factory->priv;
641         g_mutex_lock (priv->backends_mutex);
642         sz = g_hash_table_size (priv->backends);
643         g_mutex_unlock (priv->backends_mutex);
644
645         return sz;
646 }
647
648 /* Frees a uri/backend pair from the backends hash table */
649 static void
650 dump_backend (gpointer key, gpointer value, gpointer data)
651 {
652         gchar *uri;
653         ECalBackend *backend;
654
655         uri = key;
656         backend = value;
657
658         g_message ("  %s: %p", uri, (gpointer) backend);
659 }
660
661 /**
662  * e_data_cal_factory_dump_active_backends:
663  * @factory: A calendar factory.
664  *
665  * Dumps to standard output a list of all active backends for the given
666  * factory.
667  */
668 void
669 e_data_cal_factory_dump_active_backends (EDataCalFactory *factory)
670 {
671         EDataCalFactoryPrivate *priv;
672
673         g_message ("Active PCS backends");
674
675         priv = factory->priv;
676         g_mutex_lock (priv->backends_mutex);
677         g_hash_table_foreach (priv->backends, dump_backend, NULL);
678         g_mutex_unlock (priv->backends_mutex);
679 }
680
681 /* Convenience function to print an error and exit */
682 G_GNUC_NORETURN static void
683 die (const gchar *prefix, GError *error)
684 {
685         g_error("%s: %s", prefix, error->message);
686         g_error_free (error);
687         exit(1);
688 }
689
690 static void
691 offline_state_changed_cb (EOfflineListener *eol, EDataCalFactory *factory)
692 {
693         EOfflineListenerState state = e_offline_listener_get_state (eol);
694
695         g_return_if_fail (state == EOL_STATE_ONLINE || state == EOL_STATE_OFFLINE);
696
697         e_data_cal_factory_set_backend_mode (factory, state == EOL_STATE_ONLINE ? GNOME_Evolution_Calendar_MODE_REMOTE : GNOME_Evolution_Calendar_MODE_LOCAL);
698 }
699
700 #define E_DATA_CAL_FACTORY_SERVICE_NAME "org.gnome.evolution.dataserver.Calendar"
701
702 gint
703 main (gint argc, gchar **argv)
704 {
705         GError *error = NULL;
706         DBusGProxy *bus_proxy;
707         guint32 request_name_ret;
708         EOfflineListener *eol;
709
710         setlocale (LC_ALL, "");
711         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
712         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
713
714         g_type_init ();
715         g_set_prgname (E_PRGNAME);
716         if (!g_thread_supported ()) g_thread_init (NULL);
717         dbus_g_thread_init ();
718
719         loop = g_main_loop_new (NULL, FALSE);
720
721         /* Obtain a connection to the session bus */
722         connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
723         if (connection == NULL)
724                 die ("Failed to open connection to bus", error);
725
726         bus_proxy = dbus_g_proxy_new_for_name (connection,
727                                                DBUS_SERVICE_DBUS,
728                                                DBUS_PATH_DBUS,
729                                                DBUS_INTERFACE_DBUS);
730
731         factory = g_object_new (E_TYPE_DATA_CAL_FACTORY, NULL);
732         dbus_g_connection_register_g_object (connection,
733                                              "/org/gnome/evolution/dataserver/calendar/CalFactory",
734                                              G_OBJECT (factory));
735
736         dbus_g_proxy_add_signal (bus_proxy, "NameOwnerChanged",
737                                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
738         dbus_g_proxy_connect_signal (bus_proxy, "NameOwnerChanged", G_CALLBACK (name_owner_changed), factory, NULL);
739
740         if (!org_freedesktop_DBus_request_name (bus_proxy, E_DATA_CAL_FACTORY_SERVICE_NAME,
741                                                 0, &request_name_ret, &error))
742                 die ("Failed to get name", error);
743
744         if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
745                 g_error ("Got result code %u from requesting name", request_name_ret);
746                 exit (1);
747         }
748
749         eol = e_offline_listener_new ();
750         offline_state_changed_cb (eol, factory);
751         g_signal_connect (eol, "changed", G_CALLBACK (offline_state_changed_cb), factory);
752
753         printf ("Server is up and running...\n");
754
755         g_main_loop_run (loop);
756
757         dbus_g_connection_unref (connection);
758
759         g_object_unref (eol);
760
761         printf ("Bye.\n");
762
763         return 0;
764 }