Adapt libedata-cal to the new ESource API.
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-cal-backend.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - generic backend class
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  *
6  * Authors: Federico Mena-Quintero <federico@ximian.com>
7  *          JP Rosevear <jpr@ximian.com>
8  *          Rodrigo Moya <rodrigo@ximian.com>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23
24 #include <config.h>
25
26 #include <glib/gi18n-lib.h>
27
28 #include <libedataserver/e-data-server-util.h>
29
30 #include "e-cal-backend.h"
31 #include "e-cal-backend-cache.h"
32
33 #define E_CAL_BACKEND_GET_PRIVATE(obj) \
34         (G_TYPE_INSTANCE_GET_PRIVATE \
35         ((obj), E_TYPE_CAL_BACKEND, ECalBackendPrivate))
36
37 #define EDC_ERROR(_code)        e_data_cal_create_error (_code, NULL)
38 #define EDC_OPENING_ERROR       e_data_cal_create_error (Busy, _("Cannot process, calendar backend is opening"))
39 #define EDC_NOT_OPENED_ERROR    e_data_cal_create_error (NotOpened, NULL)
40
41 /* Private part of the CalBackend structure */
42 struct _ECalBackendPrivate {
43         ESourceRegistry *registry;
44
45         /* The kind of components for this backend */
46         icalcomponent_kind kind;
47
48         gboolean opening, opened, readonly, removed;
49
50         gchar *cache_dir;
51
52         /* List of Cal objects */
53         GMutex *clients_mutex;
54         GSList *clients;
55
56         GMutex *views_mutex;
57         GSList *views;
58
59         /* ECalBackend to pass notifications on to */
60         ECalBackend *notification_proxy;
61
62 };
63
64 /* Property IDs */
65 enum {
66         PROP_0,
67         PROP_CACHE_DIR,
68         PROP_KIND,
69         PROP_REGISTRY
70 };
71
72 static void e_cal_backend_remove_client_private (ECalBackend *backend, EDataCal *cal, gboolean weak_unref);
73
74 G_DEFINE_TYPE (ECalBackend, e_cal_backend, E_TYPE_BACKEND);
75
76 static void
77 cal_backend_set_default_cache_dir (ECalBackend *backend)
78 {
79         ESource *source;
80         icalcomponent_kind kind;
81         const gchar *component_type;
82         const gchar *user_cache_dir;
83         const gchar *uid;
84         gchar *filename;
85
86         user_cache_dir = e_get_user_cache_dir ();
87
88         kind = e_cal_backend_get_kind (backend);
89         source = e_backend_get_source (E_BACKEND (backend));
90
91         uid = e_source_get_uid (source);
92         g_return_if_fail (uid != NULL);
93
94         switch (kind) {
95                 case ICAL_VEVENT_COMPONENT:
96                         component_type = "calendar";
97                         break;
98                 case ICAL_VTODO_COMPONENT:
99                         component_type = "tasks";
100                         break;
101                 case ICAL_VJOURNAL_COMPONENT:
102                         component_type = "memos";
103                         break;
104                 default:
105                         g_return_if_reached ();
106         }
107
108         filename = g_build_filename (
109                 user_cache_dir, component_type, uid, NULL);
110         e_cal_backend_set_cache_dir (backend, filename);
111         g_free (filename);
112 }
113
114 static void
115 cal_backend_get_backend_property (ECalBackend *backend,
116                                   EDataCal *cal,
117                                   guint32 opid,
118                                   GCancellable *cancellable,
119                                   const gchar *prop_name)
120 {
121         g_return_if_fail (backend != NULL);
122         g_return_if_fail (E_IS_CAL_BACKEND (backend));
123         g_return_if_fail (cal != NULL);
124         g_return_if_fail (prop_name != NULL);
125
126         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
127                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_opened (backend) ? "TRUE" : "FALSE");
128         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
129                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_opening (backend) ? "TRUE" : "FALSE");
130         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
131                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_backend_get_online (E_BACKEND (backend)) ? "TRUE" : "FALSE");
132         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
133                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_readonly (backend) ? "TRUE" : "FALSE");
134         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
135                 e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_get_cache_dir (backend));
136         } else {
137                 e_data_cal_respond_get_backend_property (cal, opid, e_data_cal_create_error_fmt (NotSupported, _("Unknown calendar property '%s'"), prop_name), NULL);
138         }
139 }
140
141 static void
142 cal_backend_set_backend_property (ECalBackend *backend,
143                                   EDataCal *cal,
144                                   guint32 opid,
145                                   GCancellable *cancellable,
146                                   const gchar *prop_name,
147                                   const gchar *prop_value)
148 {
149         g_return_if_fail (backend != NULL);
150         g_return_if_fail (E_IS_CAL_BACKEND (backend));
151         g_return_if_fail (cal != NULL);
152         g_return_if_fail (prop_name != NULL);
153
154         e_data_cal_respond_set_backend_property (cal, opid, e_data_cal_create_error_fmt (NotSupported, _("Cannot change value of calendar property '%s'"), prop_name));
155 }
156
157 static void
158 cal_backend_set_kind (ECalBackend *backend,
159                       icalcomponent_kind kind)
160 {
161         backend->priv->kind = kind;
162 }
163
164 static void
165 cal_backend_set_registry (ECalBackend *backend,
166                           ESourceRegistry *registry)
167 {
168         g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
169         g_return_if_fail (backend->priv->registry == NULL);
170
171         backend->priv->registry = g_object_ref (registry);
172 }
173
174 static void
175 cal_backend_set_property (GObject *object,
176                           guint property_id,
177                           const GValue *value,
178                           GParamSpec *pspec)
179 {
180         switch (property_id) {
181                 case PROP_CACHE_DIR:
182                         e_cal_backend_set_cache_dir (
183                                 E_CAL_BACKEND (object),
184                                 g_value_get_string (value));
185                         return;
186
187                 case PROP_KIND:
188                         cal_backend_set_kind (
189                                 E_CAL_BACKEND (object),
190                                 g_value_get_ulong (value));
191                         return;
192
193                 case PROP_REGISTRY:
194                         cal_backend_set_registry (
195                                 E_CAL_BACKEND (object),
196                                 g_value_get_object (value));
197                         return;
198         }
199
200         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
201 }
202
203 static void
204 cal_backend_get_property (GObject *object,
205                           guint property_id,
206                           GValue *value,
207                           GParamSpec *pspec)
208 {
209         switch (property_id) {
210                 case PROP_CACHE_DIR:
211                         g_value_set_string (
212                                 value, e_cal_backend_get_cache_dir (
213                                 E_CAL_BACKEND (object)));
214                         return;
215
216                 case PROP_KIND:
217                         g_value_set_ulong (
218                                 value, e_cal_backend_get_kind (
219                                 E_CAL_BACKEND (object)));
220                         return;
221
222                 case PROP_REGISTRY:
223                         g_value_set_object (
224                                 value, e_cal_backend_get_registry (
225                                 E_CAL_BACKEND (object)));
226                         return;
227         }
228
229         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
230 }
231
232 static void
233 cal_backend_dispose (GObject *object)
234 {
235         ECalBackendPrivate *priv;
236
237         priv = E_CAL_BACKEND_GET_PRIVATE (object);
238
239         if (priv->registry != NULL) {
240                 g_object_unref (priv->registry);
241                 priv->registry = NULL;
242         }
243
244         /* Chain up to parent's dispose() method. */
245         G_OBJECT_CLASS (e_cal_backend_parent_class)->dispose (object);
246 }
247
248 static void
249 cal_backend_finalize (GObject *object)
250 {
251         ECalBackendPrivate *priv;
252
253         priv = E_CAL_BACKEND_GET_PRIVATE (object);
254
255         g_assert (priv->clients == NULL);
256
257         g_slist_free (priv->views);
258         /* should be NULL, anyway */
259         g_slist_free (priv->clients);
260
261         g_mutex_free (priv->clients_mutex);
262         g_mutex_free (priv->views_mutex);
263
264         g_free (priv->cache_dir);
265
266         /* Chain up to parent's finalize() method. */
267         G_OBJECT_CLASS (e_cal_backend_parent_class)->finalize (object);
268 }
269
270 static void
271 cal_backend_constructed (GObject *object)
272 {
273         cal_backend_set_default_cache_dir (E_CAL_BACKEND (object));
274
275         G_OBJECT_CLASS (e_cal_backend_parent_class)->constructed (object);
276 }
277
278 static void
279 e_cal_backend_class_init (ECalBackendClass *class)
280 {
281         GObjectClass *object_class;
282
283         g_type_class_add_private (class, sizeof (ECalBackendPrivate));
284
285         object_class = G_OBJECT_CLASS (class);
286         object_class->set_property = cal_backend_set_property;
287         object_class->get_property = cal_backend_get_property;
288         object_class->dispose = cal_backend_dispose;
289         object_class->finalize = cal_backend_finalize;
290         object_class->constructed = cal_backend_constructed;
291
292         class->get_backend_property = cal_backend_get_backend_property;
293         class->set_backend_property = cal_backend_set_backend_property;
294
295         g_object_class_install_property (
296                 object_class,
297                 PROP_CACHE_DIR,
298                 g_param_spec_string (
299                         "cache-dir",
300                         "Cache Dir",
301                         "The backend's cache directory",
302                         NULL,
303                         G_PARAM_READWRITE |
304                         G_PARAM_STATIC_STRINGS));
305
306         g_object_class_install_property (
307                 object_class,
308                 PROP_KIND,
309                 g_param_spec_ulong (
310                         "kind",
311                         "Kind",
312                         "The kind of iCalendar components "
313                         "this backend manages",
314                         ICAL_NO_COMPONENT,
315                         ICAL_XLICMIMEPART_COMPONENT,
316                         ICAL_NO_COMPONENT,
317                         G_PARAM_READWRITE |
318                         G_PARAM_CONSTRUCT_ONLY |
319                         G_PARAM_STATIC_STRINGS));
320
321         g_object_class_install_property (
322                 object_class,
323                 PROP_REGISTRY,
324                 g_param_spec_object (
325                         "registry",
326                         "Registry",
327                         "Data source registry",
328                         E_TYPE_SOURCE_REGISTRY,
329                         G_PARAM_READWRITE |
330                         G_PARAM_CONSTRUCT_ONLY |
331                         G_PARAM_STATIC_STRINGS));
332 }
333
334 static void
335 e_cal_backend_init (ECalBackend *backend)
336 {
337         backend->priv = E_CAL_BACKEND_GET_PRIVATE (backend);
338
339         backend->priv->clients = NULL;
340         backend->priv->clients_mutex = g_mutex_new ();
341
342         backend->priv->views = NULL;
343         backend->priv->views_mutex = g_mutex_new ();
344
345         backend->priv->readonly = TRUE;
346 }
347
348 /**
349  * e_cal_backend_get_kind:
350  * @backend: an #ECalBackend
351  *
352  * Gets the kind of components the given backend stores.
353  *
354  * Returns: The kind of components for this backend.
355  */
356 icalcomponent_kind
357 e_cal_backend_get_kind (ECalBackend *backend)
358 {
359         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), ICAL_NO_COMPONENT);
360
361         return backend->priv->kind;
362 }
363
364 /**
365  * e_cal_backend_get_registry:
366  * @backend: an #ECalBackend
367  *
368  * Returns the data source registry to which #EBackend:source belongs.
369  *
370  * Returns: an #ESourceRegistry
371  *
372  * Since: 3.6
373  **/
374 ESourceRegistry *
375 e_cal_backend_get_registry (ECalBackend *backend)
376 {
377         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
378
379         return backend->priv->registry;
380 }
381
382 /**
383  * e_cal_backend_is_opened:
384  * @backend: an #ECalBackend
385  *
386  * Checks if @backend's storage has been opened (and
387  * authenticated, if necessary) and the backend itself
388  * is ready for accessing. This property is changed automatically
389  * within call of e_cal_backend_notify_opened().
390  *
391  * Returns: %TRUE if fully opened, %FALSE otherwise.
392  *
393  * Since: 3.2
394  **/
395 gboolean
396 e_cal_backend_is_opened (ECalBackend *backend)
397 {
398         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
399
400         return backend->priv->opened;
401 }
402
403 /**
404  * e_cal_backend_is_opening:
405  * @backend: an #ECalBackend
406  *
407  * Checks if @backend is processing its opening phase, which
408  * includes everything since the e_cal_backend_open() call,
409  * through authentication, up to e_cal_backend_notify_opened().
410  * This property is managed automatically and the backend deny
411  * every operation except of cancel and authenticate_user while
412  * it is being opening.
413  *
414  * Returns: %TRUE if opening phase is in the effect, %FALSE otherwise.
415  *
416  * Since: 3.2
417  **/
418 gboolean
419 e_cal_backend_is_opening (ECalBackend *backend)
420 {
421         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
422
423         return backend->priv->opening;
424 }
425
426 /**
427  * e_cal_backend_is_readonly:
428  * @backend: an #ECalBackend
429  *
430  * Returns: Whether is backend read-only. This value is the last used
431  * in a call of e_cal_backend_notify_readonly().
432  *
433  * Since: 3.2
434  **/
435 gboolean
436 e_cal_backend_is_readonly (ECalBackend *backend)
437 {
438         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
439
440         return backend->priv->readonly;
441 }
442
443 /**
444  * e_cal_backend_is_removed:
445  * @backend: an #ECalBackend
446  *
447  * Checks if @backend has been removed from its physical storage.
448  *
449  * Returns: %TRUE if @backend has been removed, %FALSE otherwise.
450  *
451  * Since: 3.2
452  **/
453 gboolean
454 e_cal_backend_is_removed (ECalBackend *backend)
455 {
456         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
457
458         return backend->priv->removed;
459 }
460
461 /**
462  * e_cal_backend_set_is_removed:
463  * @backend: an #ECalBackend
464  * @is_removed: A flag indicating whether the backend's storage was removed
465  *
466  * Sets the flag indicating whether @backend was removed to @is_removed.
467  * Meant to be used by backend implementations.
468  *
469  * Since: 3.2
470  **/
471 void
472 e_cal_backend_set_is_removed (ECalBackend *backend,
473                               gboolean is_removed)
474 {
475         g_return_if_fail (E_IS_CAL_BACKEND (backend));
476
477         backend->priv->removed = is_removed;
478 }
479
480 /**
481  * e_cal_backend_get_cache_dir:
482  * @backend: an #ECalBackend
483  *
484  * Returns the cache directory for the given backend.
485  *
486  * Returns: the cache directory for the backend
487  *
488  * Since: 2.32
489  **/
490 const gchar *
491 e_cal_backend_get_cache_dir (ECalBackend *backend)
492 {
493         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
494
495         return backend->priv->cache_dir;
496 }
497
498 /**
499  * e_cal_backend_set_cache_dir:
500  * @backend: an #ECalBackend
501  * @cache_dir: a local cache directory
502  *
503  * Sets the cache directory for the given backend.
504  *
505  * Note that #ECalBackend is initialized with a usable default based on
506  * #ECalBackend:source and #ECalBackend:kind properties.  Backends should
507  * not override the default without good reason.
508  *
509  * Since: 2.32
510  **/
511 void
512 e_cal_backend_set_cache_dir (ECalBackend *backend,
513                              const gchar *cache_dir)
514 {
515         g_return_if_fail (E_IS_CAL_BACKEND (backend));
516         g_return_if_fail (cache_dir != NULL);
517
518         g_free (backend->priv->cache_dir);
519         backend->priv->cache_dir = g_strdup (cache_dir);
520
521         g_object_notify (G_OBJECT (backend), "cache-dir");
522 }
523
524 /**
525  * e_cal_backend_create_cache_filename:
526  * @backend: an #ECalBackend
527  * @uid: a component UID
528  * @filename: a filename to use; can be NULL
529  * @fileindex: index of a file; used only when @filename is NULL
530  *
531  * Returns: a filename for an attachment in a local cache dir. Free returned
532  * pointer with a g_free().
533  *
534  * Since: 3.4
535  **/
536 gchar *
537 e_cal_backend_create_cache_filename (ECalBackend *backend,
538                                      const gchar *uid,
539                                      const gchar *filename,
540                                      gint fileindex)
541 {
542         g_return_val_if_fail (backend != NULL, NULL);
543         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
544
545         return e_filename_mkdir_encoded (e_cal_backend_get_cache_dir (backend), uid, filename, fileindex);
546 }
547
548 /**
549  * e_cal_backend_get_backend_property:
550  * @backend: an #ECalBackend
551  * @cal: an #EDataCal
552  * @opid: the ID to use for this operation
553  * @cancellable: a #GCancellable for the operation
554  * @prop_name: property name to get value of; cannot be NULL
555  *
556  * Calls the get_backend_property method on the given backend.
557  * This might be finished with e_data_cal_respond_get_backend_property().
558  * Default implementation takes care of common properties and returns
559  * an 'unsupported' error for any unknown properties. The subclass may
560  * always call this default implementation for properties which fetching
561  * it doesn't overwrite.
562  *
563  * Since: 3.2
564  **/
565 void
566 e_cal_backend_get_backend_property (ECalBackend *backend,
567                                     EDataCal *cal,
568                                     guint32 opid,
569                                     GCancellable *cancellable,
570                                     const gchar *prop_name)
571 {
572         g_return_if_fail (backend != NULL);
573         g_return_if_fail (E_IS_CAL_BACKEND (backend));
574         g_return_if_fail (prop_name != NULL);
575         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property != NULL);
576
577         (* E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property) (backend, cal, opid, cancellable, prop_name);
578 }
579
580 /**
581  * e_cal_backend_set_backend_property:
582  * @backend: an #ECalBackend
583  * @cal: an #EDataCal
584  * @opid: the ID to use for this operation
585  * @cancellable: a #GCancellable for the operation
586  * @prop_name: property name to change; cannot be NULL
587  * @prop_value: value to set to @prop_name; cannot be NULL
588  *
589  * Calls the set_backend_property method on the given backend.
590  * This might be finished with e_data_cal_respond_set_backend_property().
591  * Default implementation simply returns an 'unsupported' error.
592  * The subclass may always call this default implementation for properties
593  * which fetching it doesn't overwrite.
594  *
595  * Since: 3.2
596  **/
597 void
598 e_cal_backend_set_backend_property (ECalBackend *backend,
599                                     EDataCal *cal,
600                                     guint32 opid,
601                                     GCancellable *cancellable,
602                                     const gchar *prop_name,
603                                     const gchar *prop_value)
604 {
605         g_return_if_fail (backend != NULL);
606         g_return_if_fail (E_IS_CAL_BACKEND (backend));
607         g_return_if_fail (prop_name != NULL);
608         g_return_if_fail (prop_value != NULL);
609         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property != NULL);
610
611         (* E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property) (backend, cal, opid, cancellable, prop_name, prop_value);
612 }
613
614 static void
615 cal_destroy_cb (gpointer data,
616                 GObject *where_cal_was)
617 {
618         e_cal_backend_remove_client_private (E_CAL_BACKEND (data),
619                                              (EDataCal *) where_cal_was, FALSE);
620 }
621
622 /**
623  * e_cal_backend_add_client:
624  * @backend: an #ECalBackend
625  * @cal: an #EDataCal
626  *
627  * Adds a new client to the given backend. For any event, the backend will
628  * notify all clients added via this function.
629  */
630 void
631 e_cal_backend_add_client (ECalBackend *backend,
632                           EDataCal *cal)
633 {
634         ECalBackendPrivate *priv;
635
636         g_return_if_fail (backend != NULL);
637         g_return_if_fail (E_IS_CAL_BACKEND (backend));
638         g_return_if_fail (cal != NULL);
639         g_return_if_fail (E_IS_DATA_CAL (cal));
640
641         priv = backend->priv;
642
643         g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
644
645         g_mutex_lock (priv->clients_mutex);
646         priv->clients = g_slist_append (priv->clients, cal);
647         g_mutex_unlock (priv->clients_mutex);
648 }
649
650 static void
651 e_cal_backend_remove_client_private (ECalBackend *backend,
652                                      EDataCal *cal,
653                                      gboolean weak_unref)
654 {
655         g_return_if_fail (E_IS_CAL_BACKEND (backend));
656         g_return_if_fail (E_IS_DATA_CAL (cal));
657
658         if (weak_unref)
659                 g_object_weak_unref (G_OBJECT (cal), cal_destroy_cb, backend);
660
661         /* Make sure the backend stays alive while holding the mutex. */
662         g_object_ref (backend);
663
664         /* Disconnect */
665         g_mutex_lock (backend->priv->clients_mutex);
666         backend->priv->clients = g_slist_remove (backend->priv->clients, cal);
667
668         if (backend->priv->clients == NULL)
669                 backend->priv->opening = FALSE;
670
671         g_mutex_unlock (backend->priv->clients_mutex);
672
673         g_object_unref (backend);
674 }
675
676 /**
677  * e_cal_backend_remove_client:
678  * @backend: an #ECalBackend
679  * @cal: an #EDataCal
680  *
681  * Removes a client from the list of connected clients to the given backend.
682  */
683 void
684 e_cal_backend_remove_client (ECalBackend *backend,
685                              EDataCal *cal)
686 {
687         e_cal_backend_remove_client_private (backend, cal, TRUE);
688 }
689
690 /**
691  * e_cal_backend_add_view:
692  * @backend: an #ECalBackend
693  * @view: An #EDataCalView object.
694  *
695  * Adds a view to the list of live views being run by the given backend.
696  * Doing so means that any listener on the view will get notified of any
697  * change that affect the live view.
698  *
699  * Since: 3.2
700  */
701 void
702 e_cal_backend_add_view (ECalBackend *backend,
703                         EDataCalView *view)
704 {
705         g_return_if_fail (backend != NULL);
706         g_return_if_fail (E_IS_CAL_BACKEND (backend));
707
708         g_mutex_lock (backend->priv->views_mutex);
709
710         backend->priv->views = g_slist_append (backend->priv->views, view);
711
712         g_mutex_unlock (backend->priv->views_mutex);
713 }
714
715 /**
716  * e_cal_backend_remove_view
717  * @backend: an #ECalBackend
718  * @view: An #EDataCalView object, previously added with @ref e_cal_backend_add_view.
719  *
720  * Removes view from the list of live views for the backend.
721  *
722  * Since: 3.2
723  **/
724 void
725 e_cal_backend_remove_view (ECalBackend *backend,
726                            EDataCalView *view)
727 {
728         g_return_if_fail (backend != NULL);
729         g_return_if_fail (E_IS_CAL_BACKEND (backend));
730
731         g_mutex_lock (backend->priv->views_mutex);
732
733         backend->priv->views = g_slist_remove (backend->priv->views, view);
734
735         g_mutex_unlock (backend->priv->views_mutex);
736 }
737
738 /**
739  * e_cal_backend_foreach_view:
740  * @backend: an #ECalBackend
741  * @callback: callback to call
742  * @user_data: user_data passed into the @callback
743  *
744  * Calls @callback for each known calendar view of this @backend.
745  * @callback returns %FALSE to stop further processing.
746  *
747  * Since: 3.2
748  **/
749 void
750 e_cal_backend_foreach_view (ECalBackend *backend,
751                             gboolean (*callback) (EDataCalView *view,
752                                                   gpointer user_data),
753                             gpointer user_data)
754 {
755         const GSList *views;
756         EDataCalView *view;
757         gboolean stop = FALSE;
758
759         g_return_if_fail (backend != NULL);
760         g_return_if_fail (callback != NULL);
761
762         g_mutex_lock (backend->priv->views_mutex);
763
764         for (views = backend->priv->views; views && !stop; views = views->next) {
765                 view = E_DATA_CAL_VIEW (views->data);
766
767                 g_object_ref (view);
768                 stop = !callback (view, user_data);
769                 g_object_unref (view);
770         }
771
772         g_mutex_unlock (backend->priv->views_mutex);
773 }
774
775 /**
776  * e_cal_backend_set_notification_proxy:
777  * @backend: an #ECalBackend
778  * @proxy: The calendar backend to act as notification proxy.
779  *
780  * Sets the backend that will act as notification proxy for the given backend.
781  *
782  * Since: 3.2
783  */
784 void
785 e_cal_backend_set_notification_proxy (ECalBackend *backend,
786                                       ECalBackend *proxy)
787 {
788         g_return_if_fail (E_IS_CAL_BACKEND (backend));
789
790         backend->priv->notification_proxy = proxy;
791 }
792
793 /**
794  * e_cal_backend_open:
795  * @backend: an #ECalBackend
796  * @cal: an #EDataCal
797  * @opid: the ID to use for this operation
798  * @cancellable: a #GCancellable for the operation
799  * @only_if_exists: Whether the calendar should be opened only if it already
800  * exists.  If FALSE, a new calendar will be created when the specified @uri
801  * does not exist.
802  *
803  * Opens a calendar backend with data from a calendar stored at the specified URI.
804  * This might be finished with e_data_cal_respond_open() or e_cal_backend_respond_opened(),
805  * though the overall opening phase finishes only after call
806  * of e_cal_backend_notify_opened() after which call the backend
807  * is either fully opened (including authentication against (remote)
808  * server/storage) or an error was encountered during this opening phase.
809  * 'opened' and 'opening' properties are updated automatically.
810  * The backend refuses all other operations until the opening phase is finished.
811  *
812  * The e_cal_backend_notify_opened() is called either from this function
813  * or from e_cal_backend_authenticate_user(), or after necessary steps
814  * initiated by these two functions.
815  *
816  * The opening phase usually works like this:
817  * 1) client requests open for the backend
818  * 2) server receives this request and calls e_cal_backend_open() - the opening phase begun
819  * 3) either the backend is opened during this call, and notifies client
820  *    with e_cal_backend_notify_opened() about that. This is usually
821  *    for local backends; their opening phase is finished
822  * 4) or the backend requires authentication, thus it notifies client
823  *    about that with e_cal_backend_notify_auth_required() and is
824  *    waiting for credentials, which will be received from client
825  *    by e_cal_backend_authenticate_user() call. Backend's opening
826  *    phase is still running in this case, thus it doesn't call
827  *    e_cal_backend_notify_opened() within e_cal_backend_open() call.
828  * 5) when backend receives credentials in e_cal_backend_authenticate_user()
829  *    then it tries to authenticate against a server/storage with them
830  *    and only after it knows result of the authentication, whether user
831  *    was or wasn't authenticated, it notifies client with the result
832  *    by e_cal_backend_notify_opened() and it's opening phase is
833  *    finished now. If there was no error returned then the backend is
834  *    considered opened, otherwise it's considered closed. Use AuthenticationFailed
835  *    error when the given credentials were rejected by the server/store, which
836  *    will result in a re-prompt on the client side, otherwise use AuthenticationRequired
837  *    if there was anything wrong with the given credentials. Set error's
838  *    message to a reason for a re-prompt, it'll be shown to a user.
839  * 6) client checks error returned from e_cal_backend_notify_opened() and
840  *    reprompts for a password if it was AuthenticationFailed. Otherwise
841  *    considers backend opened based on the error presence (no error means success).
842  *
843  * In any case, the call of e_cal_backend_open() should be always finished
844  * with e_data_cal_respond_open(), which has no influence on the opening phase,
845  * or alternatively with e_cal_backend_respond_opened(). Never use authentication
846  * errors in e_data_cal_respond_open() to notify the client the authentication is
847  * required, there is e_cal_backend_notify_auth_required() for this.
848  **/
849 void
850 e_cal_backend_open (ECalBackend *backend,
851                     EDataCal *cal,
852                     guint32 opid,
853                     GCancellable *cancellable,
854                     gboolean only_if_exists)
855 {
856         g_return_if_fail (backend != NULL);
857         g_return_if_fail (E_IS_CAL_BACKEND (backend));
858         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->open != NULL);
859
860         g_mutex_lock (backend->priv->clients_mutex);
861
862         if (e_cal_backend_is_opened (backend)) {
863                 gboolean online;
864
865                 g_mutex_unlock (backend->priv->clients_mutex);
866
867                 e_data_cal_report_readonly (cal, backend->priv->readonly);
868
869                 online = e_backend_get_online (E_BACKEND (backend));
870                 e_data_cal_report_online (cal, online);
871
872                 e_cal_backend_respond_opened (backend, cal, opid, NULL);
873         } else if (e_cal_backend_is_opening (backend)) {
874                 g_mutex_unlock (backend->priv->clients_mutex);
875
876                 e_data_cal_respond_open (cal, opid, EDC_OPENING_ERROR);
877         } else {
878                 backend->priv->opening = TRUE;
879                 g_mutex_unlock (backend->priv->clients_mutex);
880
881                 (* E_CAL_BACKEND_GET_CLASS (backend)->open) (backend, cal, opid, cancellable, only_if_exists);
882         }
883 }
884
885 /**
886  * e_cal_backend_remove:
887  * @backend: an #ECalBackend
888  * @cal: an #EDataCal
889  * @opid: the ID to use for this operation
890  * @cancellable: a #GCancellable for the operation
891  *
892  * Removes the calendar being accessed by the given backend.
893  * This might be finished with e_data_cal_respond_remove().
894  **/
895 void
896 e_cal_backend_remove (ECalBackend *backend,
897                       EDataCal *cal,
898                       guint32 opid,
899                       GCancellable *cancellable)
900 {
901         g_return_if_fail (backend != NULL);
902         g_return_if_fail (E_IS_CAL_BACKEND (backend));
903         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove != NULL);
904
905         if (e_cal_backend_is_opening (backend))
906                 e_data_cal_respond_remove (cal, opid, EDC_OPENING_ERROR);
907         else
908                 (* E_CAL_BACKEND_GET_CLASS (backend)->remove) (backend, cal, opid, cancellable);
909 }
910
911 /**
912  * e_cal_backend_refresh:
913  * @backend: an #ECalBackend
914  * @cal: an #EDataCal
915  * @opid: the ID to use for this operation
916  * @cancellable: a #GCancellable for the operation
917  *
918  * Refreshes the calendar being accessed by the given backend.
919  * This might be finished with e_data_cal_respond_refresh(),
920  * and it might be called as soon as possible; it doesn't mean
921  * that the refreshing is done after calling that, the backend
922  * is only notifying client whether it started the refresh process
923  * or not.
924  *
925  * Since: 2.30
926  **/
927 void
928 e_cal_backend_refresh (ECalBackend *backend,
929                        EDataCal *cal,
930                        guint32 opid,
931                        GCancellable *cancellable)
932 {
933         g_return_if_fail (backend != NULL);
934         g_return_if_fail (E_IS_CAL_BACKEND (backend));
935
936         if (e_cal_backend_is_opening (backend))
937                 e_data_cal_respond_refresh (cal, opid, EDC_OPENING_ERROR);
938         else if (!E_CAL_BACKEND_GET_CLASS (backend)->refresh)
939                 e_data_cal_respond_refresh (cal, opid, EDC_ERROR (UnsupportedMethod));
940         else if (!e_cal_backend_is_opened (backend))
941                 e_data_cal_respond_refresh (cal, opid, EDC_NOT_OPENED_ERROR);
942         else
943                 (* E_CAL_BACKEND_GET_CLASS (backend)->refresh) (backend, cal, opid, cancellable);
944 }
945
946 /**
947  * e_cal_backend_get_object:
948  * @backend: an #ECalBackend
949  * @cal: an #EDataCal
950  * @opid: the ID to use for this operation
951  * @cancellable: a #GCancellable for the operation
952  * @uid: Unique identifier for a calendar object.
953  * @rid: ID for the object's recurrence to get.
954  *
955  * Queries a calendar backend for a calendar object based on its unique
956  * identifier and its recurrence ID (if a recurrent appointment).
957  * This might be finished with e_data_cal_respond_get_object().
958  **/
959 void
960 e_cal_backend_get_object (ECalBackend *backend,
961                           EDataCal *cal,
962                           guint32 opid,
963                           GCancellable *cancellable,
964                           const gchar *uid,
965                           const gchar *rid)
966 {
967         g_return_if_fail (backend != NULL);
968         g_return_if_fail (E_IS_CAL_BACKEND (backend));
969         g_return_if_fail (uid != NULL);
970         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object != NULL);
971
972         if (e_cal_backend_is_opening (backend))
973                 e_data_cal_respond_get_object (cal, opid, EDC_OPENING_ERROR, NULL);
974         else if (!e_cal_backend_is_opened (backend))
975                 e_data_cal_respond_get_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
976         else
977                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_object) (backend, cal, opid, cancellable, uid, rid);
978 }
979
980 /**
981  * e_cal_backend_get_object_list:
982  * @backend: an #ECalBackend
983  * @cal: an #EDataCal
984  * @opid: the ID to use for this operation
985  * @cancellable: a #GCancellable for the operation
986  * @sexp: Expression to search for.
987  *
988  * Calls the get_object_list method on the given backend.
989  * This might be finished with e_data_cal_respond_get_object_list().
990  **/
991 void
992 e_cal_backend_get_object_list (ECalBackend *backend,
993                                EDataCal *cal,
994                                guint32 opid,
995                                GCancellable *cancellable,
996                                const gchar *sexp)
997 {
998         g_return_if_fail (backend != NULL);
999         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1000         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object_list != NULL);
1001
1002         if (e_cal_backend_is_opening (backend))
1003                 e_data_cal_respond_get_object_list (cal, opid, EDC_OPENING_ERROR, NULL);
1004         else if (!e_cal_backend_is_opened (backend))
1005                 e_data_cal_respond_get_object_list (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1006         else
1007                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_object_list) (backend, cal, opid, cancellable, sexp);
1008 }
1009
1010 /**
1011  * e_cal_backend_get_free_busy:
1012  * @backend: an #ECalBackend
1013  * @cal: an #EDataCal
1014  * @opid: the ID to use for this operation
1015  * @cancellable: a #GCancellable for the operation
1016  * @users: List of users to get free/busy information for.
1017  * @start: Start time for query.
1018  * @end: End time for query.
1019  *
1020  * Gets a free/busy object for the given time interval. Client side is
1021  * notified about free/busy objects throug e_data_cal_report_free_busy_data().
1022  * This might be finished with e_data_cal_respond_get_free_busy().
1023  **/
1024 void
1025 e_cal_backend_get_free_busy (ECalBackend *backend,
1026                              EDataCal *cal,
1027                              guint32 opid,
1028                              GCancellable *cancellable,
1029                              const GSList *users,
1030                              time_t start,
1031                              time_t end)
1032 {
1033         g_return_if_fail (backend != NULL);
1034         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1035         g_return_if_fail (start != -1 && end != -1);
1036         g_return_if_fail (start <= end);
1037         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy != NULL);
1038
1039         if (e_cal_backend_is_opening (backend))
1040                 e_data_cal_respond_get_free_busy (cal, opid, EDC_OPENING_ERROR);
1041         else if (!e_cal_backend_is_opened (backend))
1042                 e_data_cal_respond_get_free_busy (cal, opid, EDC_NOT_OPENED_ERROR);
1043         else
1044                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy) (backend, cal, opid, cancellable, users, start, end);
1045 }
1046
1047 /**
1048  * e_cal_backend_create_objects:
1049  * @backend: an #ECalBackend
1050  * @cal: an #EDataCal
1051  * @opid: the ID to use for this operation
1052  * @cancellable: a #GCancellable for the operation
1053  * @calobjs: The objects to create (list of gchar *).
1054  *
1055  * Calls the create_object method on the given backend.
1056  * This might be finished with e_data_cal_respond_create_objects().
1057  **/
1058 void
1059 e_cal_backend_create_objects (ECalBackend *backend,
1060                               EDataCal *cal,
1061                               guint32 opid,
1062                               GCancellable *cancellable,
1063                               const GSList *calobjs)
1064 {
1065         g_return_if_fail (backend != NULL);
1066         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1067         g_return_if_fail (calobjs != NULL);
1068
1069         if (e_cal_backend_is_opening (backend))
1070                 e_data_cal_respond_create_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1071         else if (!E_CAL_BACKEND_GET_CLASS (backend)->create_objects)
1072                 e_data_cal_respond_create_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
1073         else if (!e_cal_backend_is_opened (backend))
1074                 e_data_cal_respond_create_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1075         else
1076                 (* E_CAL_BACKEND_GET_CLASS (backend)->create_objects) (backend, cal, opid, cancellable, calobjs);
1077 }
1078
1079 /**
1080  * e_cal_backend_modify_objects:
1081  * @backend: an #ECalBackend
1082  * @cal: an #EDataCal
1083  * @opid: the ID to use for this operation
1084  * @cancellable: a #GCancellable for the operation
1085  * @calobjs: Objects to be modified (list of gchar *).
1086  * @mod: Type of modification.
1087  *
1088  * Calls the modify_objects method on the given backend.
1089  * This might be finished with e_data_cal_respond_modify_objects().
1090  **/
1091 void
1092 e_cal_backend_modify_objects (ECalBackend *backend,
1093                               EDataCal *cal,
1094                               guint32 opid,
1095                               GCancellable *cancellable,
1096                               const GSList *calobjs,
1097                               CalObjModType mod)
1098 {
1099         g_return_if_fail (backend != NULL);
1100         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1101         g_return_if_fail (calobjs != NULL);
1102
1103         if (e_cal_backend_is_opening (backend))
1104                 e_data_cal_respond_modify_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1105         else if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_objects)
1106                 e_data_cal_respond_modify_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
1107         else if (!e_cal_backend_is_opened (backend))
1108                 e_data_cal_respond_modify_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1109         else
1110                 (* E_CAL_BACKEND_GET_CLASS (backend)->modify_objects) (backend, cal, opid, cancellable, calobjs, mod);
1111 }
1112
1113 /**
1114  * e_cal_backend_remove_objects:
1115  * @backend: an #ECalBackend
1116  * @cal: an #EDataCal
1117  * @opid: the ID to use for this operation
1118  * @cancellable: a #GCancellable for the operation
1119  * @ids: List of #ECalComponentId objects identifying the objects to remove
1120  * @mod: Type of removal.
1121  *
1122  * Removes objects in a calendar backend.  The backend will notify all of its
1123  * clients about the change.
1124  * This might be finished with e_data_cal_respond_remove_objects().
1125  **/
1126 void
1127 e_cal_backend_remove_objects (ECalBackend *backend,
1128                               EDataCal *cal,
1129                               guint32 opid,
1130                               GCancellable *cancellable,
1131                               const GSList *ids,
1132                               CalObjModType mod)
1133 {
1134         g_return_if_fail (backend != NULL);
1135         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1136         g_return_if_fail (ids != NULL);
1137         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_objects != NULL);
1138
1139         if (e_cal_backend_is_opening (backend))
1140                 e_data_cal_respond_remove_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
1141         else if (!e_cal_backend_is_opened (backend))
1142                 e_data_cal_respond_remove_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
1143         else
1144                 (* E_CAL_BACKEND_GET_CLASS (backend)->remove_objects) (backend, cal, opid, cancellable, ids, mod);
1145 }
1146
1147 /**
1148  * e_cal_backend_receive_objects:
1149  * @backend: an #ECalBackend
1150  * @cal: an #EDataCal
1151  * @opid: the ID to use for this operation
1152  * @cancellable: a #GCancellable for the operation
1153  * @calobj: iCalendar object.
1154  *
1155  * Calls the receive_objects method on the given backend.
1156  * This might be finished with e_data_cal_respond_receive_objects().
1157  **/
1158 void
1159 e_cal_backend_receive_objects (ECalBackend *backend,
1160                                EDataCal *cal,
1161                                guint32 opid,
1162                                GCancellable *cancellable,
1163                                const gchar *calobj)
1164 {
1165         g_return_if_fail (backend != NULL);
1166         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1167         g_return_if_fail (calobj != NULL);
1168         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->receive_objects != NULL);
1169
1170         if (e_cal_backend_is_opening (backend))
1171                 e_data_cal_respond_receive_objects (cal, opid, EDC_OPENING_ERROR);
1172         else if (!e_cal_backend_is_opened (backend))
1173                 e_data_cal_respond_receive_objects (cal, opid, EDC_NOT_OPENED_ERROR);
1174         else
1175                 (* E_CAL_BACKEND_GET_CLASS (backend)->receive_objects) (backend, cal, opid, cancellable, calobj);
1176 }
1177
1178 /**
1179  * e_cal_backend_send_objects:
1180  * @backend: an #ECalBackend
1181  * @cal: an #EDataCal
1182  * @opid: the ID to use for this operation
1183  * @cancellable: a #GCancellable for the operation
1184  * @calobj: iCalendar object to be sent.
1185  *
1186  * Calls the send_objects method on the given backend.
1187  * This might be finished with e_data_cal_respond_send_objects().
1188  **/
1189 void
1190 e_cal_backend_send_objects (ECalBackend *backend,
1191                             EDataCal *cal,
1192                             guint32 opid,
1193                             GCancellable *cancellable,
1194                             const gchar *calobj)
1195 {
1196         g_return_if_fail (backend != NULL);
1197         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1198         g_return_if_fail (calobj != NULL);
1199         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->send_objects != NULL);
1200
1201         if (e_cal_backend_is_opening (backend))
1202                 e_data_cal_respond_send_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
1203         else if (!e_cal_backend_is_opened (backend))
1204                 e_data_cal_respond_send_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
1205         else
1206                 (* E_CAL_BACKEND_GET_CLASS (backend)->send_objects) (backend, cal, opid, cancellable, calobj);
1207 }
1208
1209 /**
1210  * e_cal_backend_get_attachment_uris:
1211  * @backend: an #ECalBackend
1212  * @cal: an #EDataCal
1213  * @opid: the ID to use for this operation
1214  * @cancellable: a #GCancellable for the operation
1215  * @uid: Unique identifier for a calendar object.
1216  * @rid: ID for the object's recurrence to get.
1217  *
1218  * Queries a calendar backend for attachments present in a calendar object based
1219  * on its unique identifier and its recurrence ID (if a recurrent appointment).
1220  * This might be finished with e_data_cal_respond_get_attachment_uris().
1221  *
1222  * Since: 3.2
1223  **/
1224 void
1225 e_cal_backend_get_attachment_uris (ECalBackend *backend,
1226                                    EDataCal *cal,
1227                                    guint32 opid,
1228                                    GCancellable *cancellable,
1229                                    const gchar *uid,
1230                                    const gchar *rid)
1231 {
1232         g_return_if_fail (backend != NULL);
1233         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1234         g_return_if_fail (uid != NULL);
1235         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris != NULL);
1236
1237         if (e_cal_backend_is_opening (backend))
1238                 e_data_cal_respond_get_attachment_uris (cal, opid, EDC_OPENING_ERROR, NULL);
1239         else if (!e_cal_backend_is_opened (backend))
1240                 e_data_cal_respond_get_attachment_uris (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1241         else
1242                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris) (backend, cal, opid, cancellable, uid, rid);
1243 }
1244
1245 /**
1246  * e_cal_backend_discard_alarm:
1247  * @backend: an #ECalBackend
1248  * @cal: an #EDataCal
1249  * @opid: the ID to use for this operation
1250  * @cancellable: a #GCancellable for the operation
1251  * @uid: Unique identifier for a calendar object.
1252  * @rid: ID for the object's recurrence to discard alarm in.
1253  * @auid: Unique identifier of the alarm itself.
1254  *
1255  * Discards alarm @auid from the object identified by @uid and @rid.
1256  * This might be finished with e_data_cal_respond_discard_alarm().
1257  * Default implementation of this method returns Not Supported error.
1258  **/
1259 void
1260 e_cal_backend_discard_alarm (ECalBackend *backend,
1261                              EDataCal *cal,
1262                              guint32 opid,
1263                              GCancellable *cancellable,
1264                              const gchar *uid,
1265                              const gchar *rid,
1266                              const gchar *auid)
1267 {
1268         g_return_if_fail (backend != NULL);
1269         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1270         g_return_if_fail (uid != NULL);
1271         g_return_if_fail (auid != NULL);
1272
1273         if (e_cal_backend_is_opening (backend))
1274                 e_data_cal_respond_discard_alarm (cal, opid, EDC_OPENING_ERROR);
1275         else if (!E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm)
1276                 e_data_cal_respond_discard_alarm (cal, opid, e_data_cal_create_error (NotSupported, NULL));
1277         else if (!e_cal_backend_is_opened (backend))
1278                 e_data_cal_respond_discard_alarm (cal, opid, EDC_NOT_OPENED_ERROR);
1279         else
1280                 (* E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm) (backend, cal, opid, cancellable, uid, rid, auid);
1281 }
1282
1283 /**
1284  * e_cal_backend_get_timezone:
1285  * @backend: an #ECalBackend
1286  * @cal: an #EDataCal
1287  * @opid: the ID to use for this operation
1288  * @cancellable: a #GCancellable for the operation
1289  * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
1290  * NULL.
1291  *
1292  * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
1293  * can't be found.
1294  * This might be finished with e_data_cal_respond_get_timezone().
1295  **/
1296 void
1297 e_cal_backend_get_timezone (ECalBackend *backend,
1298                             EDataCal *cal,
1299                             guint32 opid,
1300                             GCancellable *cancellable,
1301                             const gchar *tzid)
1302 {
1303         g_return_if_fail (backend != NULL);
1304         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1305         g_return_if_fail (tzid != NULL);
1306         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_timezone != NULL);
1307
1308         if (e_cal_backend_is_opening (backend))
1309                 e_data_cal_respond_get_timezone (cal, opid, EDC_OPENING_ERROR, NULL);
1310         else if (!e_cal_backend_is_opened (backend))
1311                 e_data_cal_respond_get_timezone (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
1312         else
1313                 (* E_CAL_BACKEND_GET_CLASS (backend)->get_timezone) (backend, cal, opid, cancellable, tzid);
1314 }
1315
1316 /**
1317  * e_cal_backend_add_timezone
1318  * @backend: an #ECalBackend
1319  * @cal: an #EDataCal
1320  * @opid: the ID to use for this operation
1321  * @cancellable: a #GCancellable for the operation
1322  * @tzobject: The timezone object, in a string.
1323  *
1324  * Add a timezone object to the given backend.
1325  * This might be finished with e_data_cal_respond_add_timezone().
1326  **/
1327 void
1328 e_cal_backend_add_timezone (ECalBackend *backend,
1329                             EDataCal *cal,
1330                             guint32 opid,
1331                             GCancellable *cancellable,
1332                             const gchar *tzobject)
1333 {
1334         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1335         g_return_if_fail (tzobject != NULL);
1336         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->add_timezone != NULL);
1337
1338         if (e_cal_backend_is_opening (backend))
1339                 e_data_cal_respond_add_timezone (cal, opid, EDC_OPENING_ERROR);
1340         else if (!e_cal_backend_is_opened (backend))
1341                 e_data_cal_respond_add_timezone (cal, opid, EDC_NOT_OPENED_ERROR);
1342         else
1343                 (* E_CAL_BACKEND_GET_CLASS (backend)->add_timezone) (backend, cal, opid, cancellable, tzobject);
1344 }
1345
1346 /**
1347  * e_cal_backend_internal_get_timezone:
1348  * @backend: an #ECalBackend
1349  * @tzid: ID of the timezone to get.
1350  *
1351  * Calls the internal_get_timezone method on the given backend.
1352  */
1353 icaltimezone *
1354 e_cal_backend_internal_get_timezone (ECalBackend *backend,
1355                                      const gchar *tzid)
1356 {
1357         g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
1358         g_return_val_if_fail (tzid != NULL, NULL);
1359         g_return_val_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone != NULL, NULL);
1360
1361         return (* E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone) (backend, tzid);
1362 }
1363
1364 /**
1365  * e_cal_backend_start_view:
1366  * @backend: an #ECalBackend
1367  * @view: The view to be started.
1368  *
1369  * Starts a new live view on the given backend.
1370  *
1371  * Since: 3.2
1372  */
1373 void
1374 e_cal_backend_start_view (ECalBackend *backend,
1375                           EDataCalView *view)
1376 {
1377         g_return_if_fail (backend != NULL);
1378         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1379         g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->start_view != NULL);
1380
1381         (* E_CAL_BACKEND_GET_CLASS (backend)->start_view) (backend, view);
1382 }
1383
1384 /**
1385  * e_cal_backend_stop_view:
1386  * @backend: an #ECalBackend
1387  * @view: The view to be stopped.
1388  *
1389  * Stops a previously started live view on the given backend.
1390  *
1391  * Since: 3.2
1392  */
1393 void
1394 e_cal_backend_stop_view (ECalBackend *backend,
1395                          EDataCalView *view)
1396 {
1397         g_return_if_fail (backend != NULL);
1398         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1399
1400         /* backward compatibility, do not force each backend define this function */
1401         if (!E_CAL_BACKEND_GET_CLASS (backend)->stop_view)
1402                 return;
1403
1404         (* E_CAL_BACKEND_GET_CLASS (backend)->stop_view) (backend, view);
1405 }
1406
1407 static gboolean
1408 component_created_cb (EDataCalView *view,
1409                       gpointer data)
1410 {
1411         ECalComponent *comp = data;
1412
1413         if (e_data_cal_view_component_matches (view, comp))
1414                 e_data_cal_view_notify_components_added_1 (view, comp);
1415
1416         return TRUE;
1417 }
1418
1419 /**
1420  * e_cal_backend_notify_component_created:
1421  * @backend: an #ECalBackend
1422  * @component: the newly created #ECalComponent
1423  *
1424  * Notifies each of the backend's listeners about a new object.
1425  *
1426  * Like e_cal_backend_notify_object_created() except takes an #ECalComponent
1427  * instead of an ical string representation and uses the #EDataCalView's
1428  * fields-of-interest to filter out unwanted information from ical strings
1429  * sent over the bus.
1430  *
1431  * Since: 3.4
1432  **/
1433 void
1434 e_cal_backend_notify_component_created (ECalBackend *backend,
1435                                         /* const */ ECalComponent *component)
1436 {
1437         ECalBackendPrivate *priv;
1438
1439         priv = backend->priv;
1440
1441         if (priv->notification_proxy) {
1442                 e_cal_backend_notify_component_created (priv->notification_proxy, component);
1443                 return;
1444         }
1445
1446         e_cal_backend_foreach_view (backend, component_created_cb, component);
1447 }
1448
1449 static void
1450 match_view_and_notify_component (EDataCalView *view,
1451                                  ECalComponent *old_component,
1452                                  ECalComponent *new_component)
1453 {
1454         gboolean old_match = FALSE, new_match = FALSE;
1455
1456         if (old_component)
1457                 old_match = e_data_cal_view_component_matches (view, old_component);
1458
1459         new_match = e_data_cal_view_component_matches (view, new_component);
1460
1461         if (old_match && new_match)
1462                 e_data_cal_view_notify_components_modified_1 (view, new_component);
1463         else if (new_match)
1464                 e_data_cal_view_notify_components_added_1 (view, new_component);
1465         else if (old_match) {
1466
1467                 ECalComponentId *id = e_cal_component_get_id (old_component);
1468
1469                 e_data_cal_view_notify_objects_removed_1 (view, id);
1470
1471                 e_cal_component_free_id (id);
1472         }
1473 }
1474
1475 struct component_call_data {
1476         ECalComponent         *old_component;
1477         ECalComponent         *new_component;
1478         const ECalComponentId *id;
1479 };
1480
1481 static gboolean
1482 call_match_and_notify_component (EDataCalView *view,
1483                                  gpointer user_data)
1484 {
1485         struct component_call_data *cd = user_data;
1486
1487         g_return_val_if_fail (user_data != NULL, FALSE);
1488
1489         match_view_and_notify_component (view, cd->old_component, cd->new_component);
1490
1491         return TRUE;
1492 }
1493
1494 /**
1495  * e_cal_backend_notify_component_modified:
1496  * @backend: an #ECalBackend
1497  * @old_component: the #ECalComponent before the modification
1498  * @new_component: the #ECalComponent after the modification
1499  *
1500  * Notifies each of the backend's listeners about a modified object.
1501  *
1502  * Like e_cal_backend_notify_object_modified() except takes an #ECalComponent
1503  * instead of an ical string representation and uses the #EDataCalView's
1504  * fields-of-interest to filter out unwanted information from ical strings
1505  * sent over the bus.
1506  *
1507  * Since: 3.4
1508  **/
1509 void
1510 e_cal_backend_notify_component_modified (ECalBackend *backend,
1511                                          /* const */ ECalComponent *old_component,
1512                                          /* const */ ECalComponent *new_component)
1513 {
1514         ECalBackendPrivate *priv;
1515         struct component_call_data cd;
1516
1517         priv = backend->priv;
1518
1519         if (priv->notification_proxy) {
1520                 e_cal_backend_notify_component_modified (priv->notification_proxy, old_component, new_component);
1521                 return;
1522         }
1523
1524         cd.old_component = old_component;
1525         cd.new_component = new_component;
1526         cd.id            = NULL;
1527
1528         e_cal_backend_foreach_view (backend, call_match_and_notify_component, &cd);
1529 }
1530
1531 static gboolean
1532 component_removed_cb (EDataCalView *view,
1533                       gpointer user_data)
1534 {
1535         struct component_call_data *cd = user_data;
1536
1537         g_return_val_if_fail (user_data != NULL, FALSE);
1538
1539         if (cd->new_component == NULL) {
1540                 /* if object == NULL, it means the object has been completely
1541                  * removed from the backend */
1542                 if (!cd->old_component || e_data_cal_view_component_matches (view, cd->old_component))
1543                         e_data_cal_view_notify_objects_removed_1 (view, cd->id);
1544         } else
1545                 match_view_and_notify_component (view, cd->old_component, cd->new_component);
1546
1547         return TRUE;
1548 }
1549
1550 /**
1551  * e_cal_backend_notify_component_removed:
1552  * @backend: an #ECalBackend
1553  * @id: the Id of the removed object
1554  * @old_component: the removed component
1555  * @new_component: the component after the removal. This only applies to recurrent 
1556  * appointments that had an instance removed. In that case, this function
1557  * notifies a modification instead of a removal.
1558  *
1559  * Notifies each of the backend's listeners about a removed object.
1560  *
1561  * Like e_cal_backend_notify_object_removed() except takes an #ECalComponent
1562  * instead of an ical string representation and uses the #EDataCalView's
1563  * fields-of-interest to filter out unwanted information from ical strings
1564  * sent over the bus.
1565  *
1566  * Since: 3.4
1567  **/
1568 void
1569 e_cal_backend_notify_component_removed (ECalBackend *backend,
1570                                         const ECalComponentId *id,
1571                                         /* const */ ECalComponent *old_component,
1572                                         /* const */ ECalComponent *new_component)
1573 {
1574         ECalBackendPrivate *priv;
1575         struct component_call_data cd;
1576
1577         priv = backend->priv;
1578
1579         if (priv->notification_proxy) {
1580                 e_cal_backend_notify_component_removed (priv->notification_proxy, id, old_component, new_component);
1581                 return;
1582         }
1583
1584         cd.old_component = old_component;
1585         cd.new_component = new_component;
1586         cd.id            = id;
1587
1588         e_cal_backend_foreach_view (backend, component_removed_cb, &cd);
1589 }
1590
1591 static gboolean
1592 object_created_cb (EDataCalView *view,
1593                    gpointer data)
1594 {
1595         const gchar *calobj = data;
1596
1597         if (e_data_cal_view_object_matches (view, calobj))
1598                 e_data_cal_view_notify_objects_added_1 (view, calobj);
1599
1600         return TRUE;
1601 }
1602
1603 /**
1604  * e_cal_backend_notify_object_created:
1605  * @backend: an #ECalBackend
1606  * @calobj: the newly created object
1607  *
1608  * Notifies each of the backend's listeners about a new object.
1609  *
1610  * #e_data_cal_notify_object_created() calls this for you. You only need to
1611  * call e_cal_backend_notify_object_created() yourself to report objects
1612  * created by non-EDS clients.
1613  *
1614  * Deprecated: 3.4: Use e_cal_backend_notify_component_created() instead.
1615  **/
1616 void
1617 e_cal_backend_notify_object_created (ECalBackend *backend,
1618                                      const gchar *calobj)
1619 {
1620         ECalBackendPrivate *priv;
1621
1622         priv = backend->priv;
1623
1624         if (priv->notification_proxy) {
1625                 e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
1626                 return;
1627         }
1628
1629         e_cal_backend_foreach_view (backend, object_created_cb, (gpointer) calobj);
1630 }
1631
1632 /**
1633  * e_cal_backend_notify_objects_added:
1634  *
1635  * Since: 2.24
1636  *
1637  * Deprecated: 3.4: Use e_data_cal_view_notify_objects_added() instead.
1638  **/
1639 void
1640 e_cal_backend_notify_objects_added (ECalBackend *backend,
1641                                     EDataCalView *view,
1642                                     const GSList *objects)
1643 {
1644         e_data_cal_view_notify_objects_added (view, objects);
1645 }
1646
1647 static void
1648 match_view_and_notify_object (EDataCalView *view,
1649                               const gchar *old_object,
1650                               const gchar *object)
1651 {
1652         gboolean old_match = FALSE, new_match = FALSE;
1653
1654         if (old_object)
1655                 old_match = e_data_cal_view_object_matches (view, old_object);
1656
1657         new_match = e_data_cal_view_object_matches (view, object);
1658         if (old_match && new_match)
1659                 e_data_cal_view_notify_objects_modified_1 (view, object);
1660         else if (new_match)
1661                 e_data_cal_view_notify_objects_added_1 (view, object);
1662         else if (old_match) {
1663                 ECalComponent *comp = NULL;
1664
1665                 comp = e_cal_component_new_from_string (old_object);
1666                 if (comp) {
1667                         ECalComponentId *id = e_cal_component_get_id (comp);
1668
1669                         e_data_cal_view_notify_objects_removed_1 (view, id);
1670
1671                         e_cal_component_free_id (id);
1672                         g_object_unref (comp);
1673                 }
1674         }
1675 }
1676
1677 struct object_call_data {
1678         const gchar *old_object;
1679         const gchar *object;
1680         const ECalComponentId *id;
1681 };
1682
1683 static gboolean
1684 call_match_and_notify_object (EDataCalView *view,
1685                               gpointer user_data)
1686 {
1687         struct object_call_data *cd = user_data;
1688
1689         g_return_val_if_fail (user_data != NULL, FALSE);
1690
1691         match_view_and_notify_object (view, cd->old_object, cd->object);
1692
1693         return TRUE;
1694 }
1695
1696 /**
1697  * e_cal_backend_notify_object_modified:
1698  * @backend: an #ECalBackend
1699  * @old_object: iCalendar representation of the original form of the object
1700  * @object: iCalendar representation of the new form of the object
1701  *
1702  * Notifies each of the backend's listeners about a modified object.
1703  *
1704  * #e_data_cal_notify_object_modified() calls this for you. You only need to
1705  * call e_cal_backend_notify_object_modified() yourself to report objects
1706  * modified by non-EDS clients.
1707  *
1708  * Deprecated: 3.4: Use e_cal_backend_notify_component_modified() instead.
1709  **/
1710 void
1711 e_cal_backend_notify_object_modified (ECalBackend *backend,
1712                                       const gchar *old_object,
1713                                       const gchar *object)
1714 {
1715         ECalBackendPrivate *priv;
1716         struct object_call_data cd;
1717
1718         priv = backend->priv;
1719
1720         if (priv->notification_proxy) {
1721                 e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
1722                 return;
1723         }
1724
1725         cd.old_object = old_object;
1726         cd.object = object;
1727         cd.id = NULL;
1728
1729         e_cal_backend_foreach_view (backend, call_match_and_notify_object, &cd);
1730 }
1731
1732 /**
1733  * e_cal_backend_notify_objects_modified:
1734  *
1735  * Since: 2.24
1736  *
1737  * Deprecated: 3.4: Use e_data_cal_view_notify_objects_modified() instead.
1738  **/
1739 void
1740 e_cal_backend_notify_objects_modified (ECalBackend *backend,
1741                                        EDataCalView *view,
1742                                        const GSList *objects)
1743 {
1744         e_data_cal_view_notify_objects_modified (view, objects);
1745 }
1746
1747 static gboolean
1748 object_removed_cb (EDataCalView *view,
1749                    gpointer user_data)
1750 {
1751         struct object_call_data *cd = user_data;
1752
1753         g_return_val_if_fail (user_data != NULL, FALSE);
1754
1755         if (cd->object == NULL) {
1756                 /* if object == NULL, it means the object has been completely
1757                  * removed from the backend */
1758                 if (!cd->old_object || e_data_cal_view_object_matches (view, cd->old_object))
1759                         e_data_cal_view_notify_objects_removed_1 (view, cd->id);
1760         } else
1761                 match_view_and_notify_object (view, cd->old_object, cd->object);
1762
1763         return TRUE;
1764 }
1765
1766 /**
1767  * e_cal_backend_notify_object_removed:
1768  * @backend: an #ECalBackend
1769  * @id: the Id of the removed object
1770  * @old_object: iCalendar representation of the removed object
1771  * @new_object: iCalendar representation of the object after the removal. This
1772  * only applies to recurrent appointments that had an instance removed. In that
1773  * case, this function notifies a modification instead of a removal.
1774  *
1775  * Notifies each of the backend's listeners about a removed object.
1776  *
1777  * e_data_cal_notify_object_removed() calls this for you. You only need to
1778  * call e_cal_backend_notify_object_removed() yourself to report objects
1779  * removed by non-EDS clients.
1780  *
1781  * Deprecated: 3.4: Use e_cal_backend_notify_component_removed() instead.
1782  **/
1783 void
1784 e_cal_backend_notify_object_removed (ECalBackend *backend,
1785                                      const ECalComponentId *id,
1786                                      const gchar *old_object,
1787                                      const gchar *new_object)
1788 {
1789         ECalBackendPrivate *priv;
1790         struct object_call_data cd;
1791
1792         priv = backend->priv;
1793
1794         if (priv->notification_proxy) {
1795                 e_cal_backend_notify_object_removed (priv->notification_proxy, id, old_object, new_object);
1796                 return;
1797         }
1798
1799         cd.old_object = old_object;
1800         cd.object = new_object;
1801         cd.id = id;
1802
1803         e_cal_backend_foreach_view (backend, object_removed_cb, &cd);
1804 }
1805
1806 /**
1807  * e_cal_backend_notify_objects_removed:
1808  *
1809  * Since: 2.24
1810  *
1811  * Deprecated: 3.4: Use e_data_cal_view_notify_objects_removed() instead.
1812  **/
1813 void
1814 e_cal_backend_notify_objects_removed (ECalBackend *backend,
1815                                       EDataCalView *view,
1816                                       const GSList *ids)
1817 {
1818         e_data_cal_view_notify_objects_removed (view, ids);
1819 }
1820
1821 /**
1822  * e_cal_backend_notify_error:
1823  * @backend: an #ECalBackend
1824  * @message: Error message
1825  *
1826  * Notifies each of the backend's listeners about an error
1827  **/
1828 void
1829 e_cal_backend_notify_error (ECalBackend *backend,
1830                             const gchar *message)
1831 {
1832         ECalBackendPrivate *priv = backend->priv;
1833         GSList *l;
1834
1835         if (priv->notification_proxy) {
1836                 e_cal_backend_notify_error (priv->notification_proxy, message);
1837                 return;
1838         }
1839
1840         g_mutex_lock (priv->clients_mutex);
1841
1842         for (l = priv->clients; l; l = l->next)
1843                 e_data_cal_report_error (l->data, message);
1844
1845         g_mutex_unlock (priv->clients_mutex);
1846 }
1847
1848 /**
1849  * e_cal_backend_notify_readonly:
1850  * @backend: an #ECalBackend
1851  * @is_readonly: flag indicating readonly status
1852  *
1853  * Notifies all backend's clients about the current readonly state.
1854  * Meant to be used by backend implementations.
1855  **/
1856 void
1857 e_cal_backend_notify_readonly (ECalBackend *backend,
1858                                gboolean is_readonly)
1859 {
1860         ECalBackendPrivate *priv;
1861         GSList *l;
1862
1863         priv = backend->priv;
1864         priv->readonly = is_readonly;
1865
1866         if (priv->notification_proxy) {
1867                 e_cal_backend_notify_readonly (priv->notification_proxy, is_readonly);
1868                 return;
1869         }
1870
1871         g_mutex_lock (priv->clients_mutex);
1872
1873         for (l = priv->clients; l; l = l->next)
1874                 e_data_cal_report_readonly (l->data, is_readonly);
1875
1876         g_mutex_unlock (priv->clients_mutex);
1877 }
1878
1879 /**
1880  * e_cal_backend_notify_online:
1881  * @backend: an #ECalBackend
1882  * @is_online: flag indicating whether @backend is connected and online
1883  *
1884  * Notifies clients of @backend's connection status indicated by @is_online.
1885  * Meant to be used by backend implementations.
1886  *
1887  * Since: 3.2
1888  **/
1889 void
1890 e_cal_backend_notify_online (ECalBackend *backend,
1891                              gboolean is_online)
1892 {
1893         ECalBackendPrivate *priv;
1894         GSList *clients;
1895
1896         priv = backend->priv;
1897
1898         if (priv->notification_proxy) {
1899                 e_cal_backend_notify_online (priv->notification_proxy, is_online);
1900                 return;
1901         }
1902
1903         g_mutex_lock (priv->clients_mutex);
1904
1905         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1906                 e_data_cal_report_online (E_DATA_CAL (clients->data), is_online);
1907
1908         g_mutex_unlock (priv->clients_mutex);
1909 }
1910
1911 /**
1912  * e_cal_backend_notify_opened:
1913  * @backend: an #ECalBackend
1914  * @error: a #GError corresponding to the error encountered during
1915  *    the opening phase. Use %NULL for success. The @error is freed
1916  *    automatically if not %NULL.
1917  *
1918  * Notifies clients that @backend finished its opening phase.
1919  * See e_cal_backend_open() for more information how the opening
1920  * phase works. Calling this function changes 'opening' property,
1921  * same as 'opened'. 'opening' is set to %FALSE and the backend
1922  * is considered 'opened' only if the @error is %NULL.
1923  *
1924  * See also: e_cal_backend_respond_opened()
1925  *
1926  * Note: The @error is freed automatically if not %NULL.
1927  *
1928  * Meant to be used by backend implementations.
1929  *
1930  * Since: 3.2
1931  **/
1932 void
1933 e_cal_backend_notify_opened (ECalBackend *backend,
1934                              GError *error)
1935 {
1936         ECalBackendPrivate *priv;
1937         GSList *clients;
1938
1939         priv = backend->priv;
1940         g_mutex_lock (priv->clients_mutex);
1941
1942         priv->opening = FALSE;
1943         priv->opened = error == NULL;
1944
1945         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1946                 e_data_cal_report_opened (E_DATA_CAL (clients->data), error);
1947
1948         g_mutex_unlock (priv->clients_mutex);
1949
1950         if (error)
1951                 g_error_free (error);
1952 }
1953
1954 /**
1955  * e_cal_backend_notify_property_changed:
1956  * @backend: an #ECalBackend
1957  * @prop_name: property name, which changed
1958  * @prop_value: new property value
1959  *
1960  * Notifies client about property value change.
1961  *
1962  * Since: 3.2
1963  **/
1964 void
1965 e_cal_backend_notify_property_changed (ECalBackend *backend,
1966                                        const gchar *prop_name,
1967                                        const gchar *prop_value)
1968 {
1969         ECalBackendPrivate *priv;
1970         GSList *clients;
1971
1972         g_return_if_fail (E_IS_CAL_BACKEND (backend));
1973         g_return_if_fail (prop_name != NULL);
1974         g_return_if_fail (*prop_name != '\0');
1975         g_return_if_fail (prop_value != NULL);
1976
1977         priv = backend->priv;
1978         g_mutex_lock (priv->clients_mutex);
1979
1980         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1981                 e_data_cal_report_backend_property_changed (E_DATA_CAL (clients->data), prop_name, prop_value);
1982
1983         g_mutex_unlock (priv->clients_mutex);
1984 }
1985
1986 /**
1987  * e_cal_backend_respond_opened:
1988  * @backend: an #ECalBackend
1989  * @cal: an #EDataCal
1990  * @opid: an operation ID
1991  * @error: result error; can be %NULL, if it isn't then it's automatically freed
1992  *
1993  * This is a replacement for e_data_cal_respond_open() for cases where
1994  * the finish of 'open' method call also finishes backend opening phase.
1995  * This function covers calling of both e_cal_backend_notify_opened() and
1996  * e_data_cal_respond_open() with the same @error.
1997  *
1998  * See e_cal_backend_open() for more details how the opening phase works.
1999  *
2000  * Since: 3.2
2001  **/
2002 void
2003 e_cal_backend_respond_opened (ECalBackend *backend,
2004                               EDataCal *cal,
2005                               guint32 opid,
2006                               GError *error)
2007 {
2008         GError *copy = NULL;
2009
2010         g_return_if_fail (backend != NULL);
2011         g_return_if_fail (E_IS_CAL_BACKEND (backend));
2012         g_return_if_fail (cal != NULL);
2013         g_return_if_fail (opid != 0);
2014
2015         if (error)
2016                 copy = g_error_copy (error);
2017
2018         e_cal_backend_notify_opened (backend, copy);
2019         e_data_cal_respond_open (cal, opid, error);
2020 }
2021
2022 /**
2023  * e_cal_backend_empty_cache:
2024  * @backend: an #ECalBackend
2025  * @cache: Backend's cache to empty.
2026  *
2027  * Empties backend's cache with all notifications and so on, thus all listening
2028  * will know there is nothing in this backend.
2029  *
2030  * Since: 2.28
2031  **/
2032 void
2033 e_cal_backend_empty_cache (ECalBackend *backend,
2034                            ECalBackendCache *cache)
2035 {
2036         GList *comps_in_cache;
2037
2038         g_return_if_fail (backend != NULL);
2039         g_return_if_fail (E_IS_CAL_BACKEND (backend));
2040
2041         if (!cache)
2042                 return;
2043
2044         g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
2045
2046         e_file_cache_freeze_changes (E_FILE_CACHE (cache));
2047
2048         for (comps_in_cache = e_cal_backend_cache_get_components (cache);
2049              comps_in_cache;
2050              comps_in_cache = comps_in_cache->next) {
2051                 ECalComponentId *id;
2052                 ECalComponent *comp = comps_in_cache->data;
2053
2054                 id = e_cal_component_get_id (comp);
2055
2056                 e_cal_backend_cache_remove_component (cache, id->uid, id->rid);
2057
2058                 e_cal_backend_notify_component_removed  (backend, id, comp, NULL);
2059
2060                 e_cal_component_free_id (id);
2061                 g_object_unref (comp);
2062         }
2063
2064         g_list_free (comps_in_cache);
2065
2066         e_file_cache_thaw_changes (E_FILE_CACHE (cache));
2067 }