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