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