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