gnome-shell-3.6.3.1/src/calendar-server/calendar-sources.c

No issues found

  1 /*
  2  * Copyright (C) 2004 Free Software Foundation, Inc.
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU General Public License as
  6  * published by the Free Software Foundation; either version 2 of the
  7  * License, or (at your option) any later version.
  8  *
  9  * This program is distributed in the hope that it will be useful, but
 10  * WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU General Public License
 15  * along with this program; if not, write to the Free Software
 16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 17  * 02111-1307, USA.
 18  *
 19  * Authors:
 20  *     Mark McLoughlin  <mark@skynet.ie>
 21  *     William Jon McCann  <mccann@jhu.edu>
 22  *     Martin Grimme  <martin@pycage.de>
 23  *     Christian Kellner  <gicmo@xatom.net>
 24  */
 25 
 26 #include <config.h>
 27 
 28 #include "calendar-sources.h"
 29 
 30 #include <libintl.h>
 31 #include <string.h>
 32 #define HANDLE_LIBICAL_MEMORY
 33 #include <libecal/libecal.h>
 34 
 35 #undef CALENDAR_ENABLE_DEBUG
 36 #include "calendar-debug.h"
 37 
 38 #ifndef _
 39 #define _(x) gettext(x)
 40 #endif
 41 
 42 #ifndef N_
 43 #define N_(x) x
 44 #endif
 45 
 46 #define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate))
 47 
 48 typedef struct _ClientData ClientData;
 49 typedef struct _CalendarSourceData CalendarSourceData;
 50 
 51 struct _ClientData
 52 {
 53   ECalClient *client;
 54   gulong backend_died_id;
 55 };
 56 
 57 struct _CalendarSourceData
 58 {
 59   ECalClientSourceType source_type;
 60   CalendarSources *sources;
 61   guint            changed_signal;
 62 
 63   /* ESource -> EClient */
 64   GHashTable      *clients;
 65 
 66   guint            timeout_id;
 67 
 68   guint            loaded : 1;
 69 };
 70 
 71 struct _CalendarSourcesPrivate
 72 {
 73   ESourceRegistry    *registry;
 74   gulong              source_added_id;
 75   gulong              source_changed_id;
 76   gulong              source_removed_id;
 77 
 78   CalendarSourceData  appointment_sources;
 79   CalendarSourceData  task_sources;
 80 };
 81 
 82 static void calendar_sources_class_init (CalendarSourcesClass *klass);
 83 static void calendar_sources_init       (CalendarSources      *sources);
 84 static void calendar_sources_finalize   (GObject             *object);
 85 
 86 static void backend_died_cb (EClient *client, CalendarSourceData *source_data);
 87 static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
 88                                                          ESource         *source,
 89                                                          CalendarSources *sources);
 90 static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
 91                                                          ESource         *source,
 92                                                          CalendarSources *sources);
 93 
 94 enum
 95 {
 96   APPOINTMENT_SOURCES_CHANGED,
 97   TASK_SOURCES_CHANGED,
 98   LAST_SIGNAL
 99 };
100 static guint signals [LAST_SIGNAL] = { 0, };
101 
102 static GObjectClass    *parent_class = NULL;
103 static CalendarSources *calendar_sources_singleton = NULL;
104 
105 static void
106 client_data_free (ClientData *data)
107 {
108   g_signal_handler_disconnect (data->client, data->backend_died_id);
109   g_object_unref (data->client);
110   g_slice_free (ClientData, data);
111 }
112 
113 GType
114 calendar_sources_get_type (void)
115 {
116   static GType sources_type = 0;
117   
118   if (!sources_type)
119     {
120       static const GTypeInfo sources_info =
121       {
122 	sizeof (CalendarSourcesClass),
123 	NULL,		/* base_init */
124 	NULL,		/* base_finalize */
125 	(GClassInitFunc) calendar_sources_class_init,
126 	NULL,           /* class_finalize */
127 	NULL,		/* class_data */
128 	sizeof (CalendarSources),
129 	0,		/* n_preallocs */
130 	(GInstanceInitFunc) calendar_sources_init,
131       };
132       
133       sources_type = g_type_register_static (G_TYPE_OBJECT,
134 					     "CalendarSources",
135 					     &sources_info, 0);
136     }
137   
138   return sources_type;
139 }
140 
141 static void
142 calendar_sources_class_init (CalendarSourcesClass *klass)
143 {
144   GObjectClass *gobject_class = (GObjectClass *) klass;
145 
146   parent_class = g_type_class_peek_parent (klass);
147 
148   gobject_class->finalize = calendar_sources_finalize;
149 
150   g_type_class_add_private (klass, sizeof (CalendarSourcesPrivate));
151 
152   signals [APPOINTMENT_SOURCES_CHANGED] =
153     g_signal_new ("appointment-sources-changed",
154 		  G_TYPE_FROM_CLASS (gobject_class),
155 		  G_SIGNAL_RUN_LAST,
156 		  G_STRUCT_OFFSET (CalendarSourcesClass,
157 				   appointment_sources_changed),
158 		  NULL,
159 		  NULL,
160                   NULL,
161 		  G_TYPE_NONE,
162 		  0);
163 
164   signals [TASK_SOURCES_CHANGED] =
165     g_signal_new ("task-sources-changed",
166 		  G_TYPE_FROM_CLASS (gobject_class),
167 		  G_SIGNAL_RUN_LAST,
168 		  G_STRUCT_OFFSET (CalendarSourcesClass,
169 				   task_sources_changed),
170 		  NULL,
171 		  NULL,
172                   NULL,
173 		  G_TYPE_NONE,
174 		  0);
175 }
176 
177 static void
178 calendar_sources_init (CalendarSources *sources)
179 {
180   GError *error = NULL;
181 
182   sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources);
183 
184   /* XXX Not sure what to do if this fails.
185    *     Should this class implement GInitable or pass the
186    *     registry in as a G_PARAM_CONSTRUCT_ONLY property? */
187   sources->priv->registry = e_source_registry_new_sync (NULL, &error);
188   if (error != NULL)
189     {
190       g_error ("%s: %s", G_STRFUNC, error->message);
191     }
192 
193   sources->priv->source_added_id   = g_signal_connect (sources->priv->registry,
194                                                        "source-added",
195                                                        G_CALLBACK (calendar_sources_registry_source_changed_cb),
196                                                        sources);
197   sources->priv->source_changed_id = g_signal_connect (sources->priv->registry,
198                                                        "source-changed",
199                                                        G_CALLBACK (calendar_sources_registry_source_changed_cb),
200                                                        sources);
201   sources->priv->source_removed_id = g_signal_connect (sources->priv->registry,
202                                                        "source-removed",
203                                                        G_CALLBACK (calendar_sources_registry_source_removed_cb),
204                                                        sources);
205 
206   sources->priv->appointment_sources.source_type    = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
207   sources->priv->appointment_sources.sources        = sources;
208   sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
209   sources->priv->appointment_sources.clients        = g_hash_table_new_full ((GHashFunc) e_source_hash,
210                                                                              (GEqualFunc) e_source_equal,
211                                                                              (GDestroyNotify) g_object_unref,
212                                                                              (GDestroyNotify) client_data_free);
213   sources->priv->appointment_sources.timeout_id     = 0;
214 
215   sources->priv->task_sources.source_type    = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
216   sources->priv->task_sources.sources        = sources;
217   sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
218   sources->priv->task_sources.clients        = g_hash_table_new_full ((GHashFunc) e_source_hash,
219                                                                       (GEqualFunc) e_source_equal,
220                                                                       (GDestroyNotify) g_object_unref,
221                                                                       (GDestroyNotify) client_data_free);
222   sources->priv->task_sources.timeout_id     = 0;
223 }
224 
225 static void
226 calendar_sources_finalize_source_data (CalendarSources    *sources,
227 				       CalendarSourceData *source_data)
228 {
229   if (source_data->loaded)
230     {
231       g_hash_table_destroy (source_data->clients);
232       source_data->clients = NULL;
233 
234       if (source_data->timeout_id != 0)
235         {
236           g_source_remove (source_data->timeout_id);
237           source_data->timeout_id = 0;
238         }
239 
240       source_data->loaded = FALSE;
241     }
242 }
243 
244 static void
245 calendar_sources_finalize (GObject *object)
246 {
247   CalendarSources *sources = CALENDAR_SOURCES (object);
248 
249   if (sources->priv->registry)
250     {
251       g_signal_handler_disconnect (sources->priv->registry,
252                                    sources->priv->source_added_id);
253       g_signal_handler_disconnect (sources->priv->registry,
254                                    sources->priv->source_changed_id);
255       g_signal_handler_disconnect (sources->priv->registry,
256                                    sources->priv->source_removed_id);
257       g_object_unref (sources->priv->registry);
258     }
259   sources->priv->registry = NULL;
260 
261   calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
262   calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
263 
264   if (G_OBJECT_CLASS (parent_class)->finalize)
265     G_OBJECT_CLASS (parent_class)->finalize (object);
266 }
267 
268 CalendarSources *
269 calendar_sources_get (void)
270 {
271   gpointer singleton_location = &calendar_sources_singleton;
272 
273   if (calendar_sources_singleton)
274     return g_object_ref (calendar_sources_singleton);
275 
276   calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
277   g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
278 			     singleton_location);
279 
280   return calendar_sources_singleton;
281 }
282 
283 /* The clients are just created here but not loaded */
284 static void
285 create_client_for_source (ESource              *source,
286 		          ECalClientSourceType  source_type,
287 		          CalendarSourceData   *source_data)
288 {
289   ClientData *data;
290   ECalClient *client;
291   GError *error = NULL;
292 
293   client = g_hash_table_lookup (source_data->clients, source);
294   g_return_if_fail (client == NULL);
295 
296   client = e_cal_client_new (source, source_type, &error);
297   if (!client)
298     {
299       g_warning ("Could not load source '%s': %s",
300 		 e_source_get_uid (source),
301 		 error->message);
302       g_clear_error(&error);
303       return;
304     }
305 
306   data = g_slice_new0 (ClientData);
307   data->client = client;  /* takes ownership */
308   data->backend_died_id = g_signal_connect (client,
309                                             "backend-died",
310                                             G_CALLBACK (backend_died_cb),
311                                             source_data);
312 
313   g_hash_table_insert (source_data->clients, g_object_ref (source), data);
314 }
315 
316 static inline void
317 debug_dump_ecal_list (GHashTable *clients)
318 {
319 #ifdef CALENDAR_ENABLE_DEBUG
320   GList *list, *link;
321 
322   dprintf ("Loaded clients:\n");
323   list = g_hash_table_get_keys (clients);
324   for (link = list; link != NULL; link = g_list_next (link))
325     {
326       ESource *source = E_SOURCE (link->data);
327 
328       dprintf ("  %s %s\n",
329 	       e_source_get_uid (source),
330 	       e_source_get_display_name (source));
331     }
332   g_list_free (list);
333 #endif
334 }
335 
336 static void
337 calendar_sources_load_esource_list (ESourceRegistry *registry,
338                                     CalendarSourceData *source_data);
339 
340 static gboolean
341 backend_restart (gpointer data)
342 {
343   CalendarSourceData *source_data = data;
344   ESourceRegistry *registry;
345 
346   registry = source_data->sources->priv->registry;
347   calendar_sources_load_esource_list (registry, source_data);
348   g_signal_emit (source_data->sources, source_data->changed_signal, 0);
349 
350   source_data->timeout_id = 0;
351     
352   return FALSE;
353 }
354 
355 static void
356 backend_died_cb (EClient *client, CalendarSourceData *source_data)
357 {
358   ESource *source;
359   const char *display_name;
360 
361   source = e_client_get_source (client);
362   display_name = e_source_get_display_name (source);
363   g_warning ("The calendar backend for '%s' has crashed.", display_name);
364   g_hash_table_remove (source_data->clients, source);
365 
366   if (source_data->timeout_id != 0)
367     {
368       g_source_remove (source_data->timeout_id);
369       source_data->timeout_id = 0;
370     }
371 
372   source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
373 		  				   source_data);
374 }
375 
376 static void
377 calendar_sources_load_esource_list (ESourceRegistry *registry,
378                                     CalendarSourceData *source_data)
379 {
380   GList   *list, *link;
381   const gchar *extension_name;
382 
383   switch (source_data->source_type)
384     {
385       case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
386         extension_name = E_SOURCE_EXTENSION_CALENDAR;
387         break;
388       case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
389         extension_name = E_SOURCE_EXTENSION_TASK_LIST;
390         break;
391       default:
392         g_return_if_reached ();
393     }
394 
395   list = e_source_registry_list_sources (registry, extension_name);
396 
397   for (link = list; link != NULL; link = g_list_next (link))
398     {
399       ESource *source = E_SOURCE (link->data);
400       ESourceSelectable *extension;
401       gboolean show_source;
402 
403       extension = e_source_get_extension (source, extension_name);
404       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
405 
406       if (show_source)
407         create_client_for_source (source, source_data->source_type, source_data);
408     }
409 
410   debug_dump_ecal_list (source_data->clients);
411 
412   g_list_free_full (list, g_object_unref);
413 }
414 
415 static void
416 calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
417                                              ESource         *source,
418                                              CalendarSources *sources)
419 {
420   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
421     {
422       CalendarSourceData *source_data;
423       ESourceSelectable *extension;
424       gboolean have_client;
425       gboolean show_source;
426 
427       source_data = &sources->priv->appointment_sources;
428       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
429       have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
430       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
431 
432       if (!show_source && have_client)
433         {
434           g_hash_table_remove (source_data->clients, source);
435           g_signal_emit (sources, source_data->changed_signal, 0);
436         }
437       if (show_source && !have_client)
438         {
439           create_client_for_source (source, source_data->source_type, source_data);
440           g_signal_emit (sources, source_data->changed_signal, 0);
441         }
442     }
443 
444   if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
445     {
446       CalendarSourceData *source_data;
447       ESourceSelectable *extension;
448       gboolean have_client;
449       gboolean show_source;
450 
451       source_data = &sources->priv->task_sources;
452       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
453       have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
454       show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
455 
456       if (!show_source && have_client)
457         {
458           g_hash_table_remove (source_data->clients, source);
459           g_signal_emit (sources, source_data->changed_signal, 0);
460         }
461       if (show_source && !have_client)
462         {
463           create_client_for_source (source, source_data->source_type, source_data);
464           g_signal_emit (sources, source_data->changed_signal, 0);
465         }
466     }
467 }
468 
469 static void
470 calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
471                                              ESource         *source,
472                                              CalendarSources *sources)
473 {
474   if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
475     {
476       CalendarSourceData *source_data;
477 
478       source_data = &sources->priv->appointment_sources;
479       g_hash_table_remove (source_data->clients, source);
480       g_signal_emit (sources, source_data->changed_signal, 0);
481     }
482 
483   if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
484     {
485       CalendarSourceData *source_data;
486 
487       source_data = &sources->priv->task_sources;
488       g_hash_table_remove (source_data->clients, source);
489       g_signal_emit (sources, source_data->changed_signal, 0);
490     }
491 }
492 
493 GList *
494 calendar_sources_get_appointment_clients (CalendarSources *sources)
495 {
496   GList *list, *link;
497 
498   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
499 
500   if (!sources->priv->appointment_sources.loaded)
501     {
502       calendar_sources_load_esource_list (sources->priv->registry,
503                                           &sources->priv->appointment_sources);
504       sources->priv->appointment_sources.loaded = TRUE;
505     }
506 
507   list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
508 
509   for (link = list; link != NULL; link = g_list_next (link))
510     link->data = ((ClientData *) link->data)->client;
511 
512   return list;
513 }
514 
515 GList *
516 calendar_sources_get_task_clients (CalendarSources *sources)
517 {
518   GList *list, *link;
519 
520   g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
521 
522   if (!sources->priv->task_sources.loaded)
523     {
524       calendar_sources_load_esource_list (sources->priv->registry,
525                                           &sources->priv->task_sources);
526       sources->priv->task_sources.loaded = TRUE;
527     }
528 
529   list = g_hash_table_get_values (sources->priv->task_sources.clients);
530 
531   for (link = list; link != NULL; link = g_list_next (link))
532     link->data = ((ClientData *) link->data)->client;
533 
534   return list;
535 }