Whitespace cleanups.
[platform/upstream/evolution-data-server.git] / calendar / libecal / e-cal.c
1 /*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar ecal
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  * Copyright (C) 2009 Intel Corporation
6  *
7  * Authors: Federico Mena-Quintero <federico@ximian.com>
8  *          Rodrigo Moya <rodrigo@novell.com>
9  *          Ross Burton <ross@linux.intel.com>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24
25 /**
26  * SECTION:e-cal
27  *
28  * The old signal "cal-opened" is deprecated since 3.0 and is replaced with
29  * its equivalent "cal_opened_ex", which has a detailed #GError structure
30  * as a parameter, instead of a status code only.
31  *
32  * Deprecated: 3.2: Use #ECalClient instead.
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <unistd.h>
40 #include <string.h>
41 #include <glib/gi18n-lib.h>
42
43 #include <libical/ical.h>
44 #include <libedataserver/e-url.h>
45 #include <libedataserver/e-credentials.h>
46 #include <libedataserver/e-data-server-util.h>
47
48 #include "libedata-cal/e-data-cal-types.h"
49
50 #include "e-cal-check-timezones.h"
51 #include "e-cal-marshal.h"
52 #include "e-cal-time-util.h"
53 #include "e-cal-view-private.h"
54 #include "e-cal.h"
55
56 #include "e-gdbus-cal.h"
57 #include "e-gdbus-cal-view.h"
58 #include "e-gdbus-cal-factory.h"
59
60 #define E_CAL_GET_PRIVATE(obj) \
61         (G_TYPE_INSTANCE_GET_PRIVATE \
62         ((obj), E_TYPE_CAL, ECalPrivate))
63
64 #define CLIENT_BACKEND_PROPERTY_CACHE_DIR               "cache-dir"
65 #define CLIENT_BACKEND_PROPERTY_CAPABILITIES            "capabilities"
66 #define CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS          "cal-email-address"
67 #define CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS        "alarm-email-address"
68 #define CAL_BACKEND_PROPERTY_DEFAULT_OBJECT             "default-object"
69
70 static guint active_cals = 0, cal_connection_closed_id = 0;
71 static EGdbusCalFactory *cal_factory_proxy = NULL;
72 static GStaticRecMutex cal_factory_proxy_lock = G_STATIC_REC_MUTEX_INIT;
73 #define LOCK_FACTORY()   g_static_rec_mutex_lock   (&cal_factory_proxy_lock)
74 #define UNLOCK_FACTORY() g_static_rec_mutex_unlock (&cal_factory_proxy_lock)
75
76 #define LOCK_CACHE()   g_static_rec_mutex_lock   (&priv->cache_lock)
77 #define UNLOCK_CACHE() g_static_rec_mutex_unlock (&priv->cache_lock)
78
79 G_DEFINE_TYPE (ECal, e_cal, G_TYPE_OBJECT)
80
81 static gboolean open_calendar (ECal *ecal, gboolean only_if_exists, GError **error,
82         ECalendarStatus *status,
83         gboolean needs_auth, gboolean async);
84 static void e_cal_dispose (GObject *object);
85 static void e_cal_finalize (GObject *object);
86
87 /* Private part of the ECal structure */
88 struct _ECalPrivate {
89         GDBusProxy *gdbus_cal;
90         guint gone_signal_id;
91
92         /* Load state to avoid multiple loads */
93         ECalLoadState load_state;
94
95         /* URI of the calendar that is being loaded or is already loaded, or
96          * NULL if we are not loaded.
97          */
98         ESource *source;
99         gchar *uri;
100         ECalSourceType type;
101
102         /* Email address associated with this calendar, or NULL */
103         gchar *cal_address;
104         gchar *alarm_email_address;
105         gchar *ldap_attribute;
106
107         /* Scheduling info */
108         gchar *capabilities;
109
110         gint mode;
111         gboolean requires_auth;
112
113         gboolean read_only;
114
115         /* The authentication function */
116         ECalAuthFunc auth_func;
117         gpointer auth_user_data;
118
119         /* A cache of timezones retrieved from the server, to avoid getting
120          * them repeatedly for each get_object () call. */
121         GHashTable *timezones;
122
123         /* The default timezone to use to resolve DATE and floating DATE-TIME
124          * values. */
125         icaltimezone *default_zone;
126
127         gchar *local_attachment_store;
128
129         /* For locking the operation while localling cache values like 
130          * static capabilities, cal address etc. */
131         GStaticRecMutex cache_lock;
132
133         GList **free_busy_data;
134         GMutex *free_busy_data_lock;
135 };
136
137 \f
138
139 /* Signal IDs */
140 enum {
141         CAL_OPENED,
142         CAL_OPENED_EX,
143         CAL_SET_MODE,
144         BACKEND_ERROR,
145         BACKEND_DIED,
146         LAST_SIGNAL
147 };
148
149 static guint e_cal_signals[LAST_SIGNAL];
150
151 #ifdef __PRETTY_FUNCTION__
152 #define e_return_error_if_fail(expr,error_code) G_STMT_START{           \
153      if G_LIKELY (expr) { } else                                                \
154        {                                                                \
155          g_log (G_LOG_DOMAIN,                                           \
156                 G_LOG_LEVEL_CRITICAL,                                   \
157                 "file %s: line %d (%s): assertion `%s' failed",         \
158                 __FILE__,                                               \
159                 __LINE__,                                               \
160                 __PRETTY_FUNCTION__,                                    \
161                 #expr);                                                 \
162          g_set_error (error, E_CALENDAR_ERROR, (error_code),                \
163                 "file %s: line %d (%s): assertion `%s' failed",         \
164                 __FILE__,                                               \
165                 __LINE__,                                               \
166                 __PRETTY_FUNCTION__,                                    \
167                 #expr);                                                 \
168          return FALSE;                                                  \
169        };                               }G_STMT_END
170 #else
171 #define e_return_error_if_fail(expr,error_code) G_STMT_START{           \
172      if G_LIKELY (expr) { } else                                                \
173        {                                                                \
174          g_log (G_LOG_DOMAIN,                                           \
175                 G_LOG_LEVEL_CRITICAL,                                   \
176                 "file %s: line %d: assertion `%s' failed",              \
177                 __FILE__,                                               \
178                 __LINE__,                                               \
179                 #expr);                                                 \
180          g_set_error (error, E_CALENDAR_ERROR, (error_code),                \
181                 "file %s: line %d: assertion `%s' failed",              \
182                 __FILE__,                                               \
183                 __LINE__,                                               \
184                 #expr);                                                 \
185          return FALSE;                                                  \
186        };                               }G_STMT_END
187 #endif
188
189 #define E_CALENDAR_CHECK_STATUS(status,error)                           \
190 G_STMT_START{                                                           \
191         if ((status) == E_CALENDAR_STATUS_OK)                           \
192                 return TRUE;                                            \
193         else {                                                          \
194                 const gchar *msg;                                       \
195                 if (error && *error)                                    \
196                         return unwrap_gerror (error);                   \
197                 msg = e_cal_get_error_message ((status));               \
198                 g_set_error ((error), E_CALENDAR_ERROR, (status), "%s", msg);   \
199                 return FALSE;                                           \
200         }                                                               \
201 } G_STMT_END
202
203 \f
204
205 /* Error quark */
206 GQuark
207 e_calendar_error_quark (void)
208 {
209         static GQuark q = 0;
210         if (q == 0)
211                 q = g_quark_from_static_string ("e-calendar-error-quark");
212
213         return q;
214 }
215
216 /**
217  * If the GError is a remote error, extract the EBookStatus embedded inside.
218  * Otherwise return CORBA_EXCEPTION (I know this is DBus...).
219  */
220 static ECalendarStatus
221 get_status_from_error (const GError *error)
222 {
223         #define err(a,b) "org.gnome.evolution.dataserver.Calendar." a, b
224         static struct {
225                 const gchar *name;
226                 ECalendarStatus err_code;
227         } errors[] = {
228                 { err ("Success",                               E_CALENDAR_STATUS_OK) },
229                 { err ("Busy",                                  E_CALENDAR_STATUS_BUSY) },
230                 { err ("RepositoryOffline",                     E_CALENDAR_STATUS_REPOSITORY_OFFLINE) },
231                 { err ("PermissionDenied",                      E_CALENDAR_STATUS_PERMISSION_DENIED) },
232                 { err ("InvalidRange",                          E_CALENDAR_STATUS_OTHER_ERROR) },
233                 { err ("ObjectNotFound",                        E_CALENDAR_STATUS_OBJECT_NOT_FOUND) },
234                 { err ("InvalidObject",                         E_CALENDAR_STATUS_INVALID_OBJECT) },
235                 { err ("ObjectIdAlreadyExists",                 E_CALENDAR_STATUS_OBJECT_ID_ALREADY_EXISTS) },
236                 { err ("AuthenticationFailed",                  E_CALENDAR_STATUS_AUTHENTICATION_FAILED) },
237                 { err ("AuthenticationRequired",                E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) },
238                 { err ("UnsupportedField",                      E_CALENDAR_STATUS_OTHER_ERROR) },
239                 { err ("UnsupportedMethod",                     E_CALENDAR_STATUS_OTHER_ERROR) },
240                 { err ("UnsupportedAuthenticationMethod",       E_CALENDAR_STATUS_OTHER_ERROR) },
241                 { err ("TLSNotAvailable",                       E_CALENDAR_STATUS_OTHER_ERROR) },
242                 { err ("NoSuchCal",                             E_CALENDAR_STATUS_NO_SUCH_CALENDAR) },
243                 { err ("UnknownUser",                           E_CALENDAR_STATUS_UNKNOWN_USER) },
244                 { err ("OfflineUnavailable",                    E_CALENDAR_STATUS_OTHER_ERROR) },
245                 { err ("SearchSizeLimitExceeded",               E_CALENDAR_STATUS_OTHER_ERROR) },
246                 { err ("SearchTimeLimitExceeded",               E_CALENDAR_STATUS_OTHER_ERROR) },
247                 { err ("InvalidQuery",                          E_CALENDAR_STATUS_OTHER_ERROR) },
248                 { err ("QueryRefused",                          E_CALENDAR_STATUS_OTHER_ERROR) },
249                 { err ("CouldNotCancel",                        E_CALENDAR_STATUS_COULD_NOT_CANCEL) },
250                 { err ("OtherError",                            E_CALENDAR_STATUS_OTHER_ERROR) },
251                 { err ("InvalidServerVersion",                  E_CALENDAR_STATUS_INVALID_SERVER_VERSION) },
252                 { err ("InvalidArg",                            E_CALENDAR_STATUS_INVALID_ARG) },
253                 { err ("NotSupported",                          E_CALENDAR_STATUS_NOT_SUPPORTED) }
254         };
255         #undef err
256
257         if G_LIKELY (error == NULL)
258                 return Success;
259
260         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
261                 gchar *name;
262                 gint i;
263
264                 name = g_dbus_error_get_remote_error (error);
265
266                 for (i = 0; i < G_N_ELEMENTS (errors); i++) {
267                         if (g_ascii_strcasecmp (errors[i].name, name) == 0) {
268                                 g_free (name);
269                                 return errors[i].err_code;
270                         }
271                 }
272
273                 g_warning ("Unmatched error name %s", name);
274                 g_free (name);
275
276                 return E_CALENDAR_STATUS_OTHER_ERROR;
277         } else if (error->domain == E_CALENDAR_ERROR) {
278                 return error->code;
279         } else {
280                 /* In this case the error was caused by DBus */
281                 return E_CALENDAR_STATUS_DBUS_EXCEPTION;
282         }
283 }
284
285 /**
286  * If the specified GError is a remote error, then create a new error
287  * representing the remote error.  If the error is anything else, then leave it
288  * alone.
289  */
290 static gboolean
291 unwrap_gerror (GError **error)
292 {
293         if (*error == NULL)
294                 return TRUE;
295
296         if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
297                 GError *new_error = NULL;
298                 gint code;
299
300                 code = get_status_from_error (*error);
301                 g_dbus_error_strip_remote_error (*error);
302
303                 new_error = g_error_new_literal (E_CALENDAR_ERROR, code, (*error)->message);
304
305                 g_error_free (*error);
306                 *error = new_error;
307         }
308
309         return FALSE;
310 }
311
312 /**
313  * e_cal_source_type_enum_get_type:
314  *
315  * Registers the #ECalSourceTypeEnum type with glib.
316  *
317  * Returns: the ID of the #ECalSourceTypeEnum type.
318  *
319  * Deprecated: 3.2: Use e_cal_client_source_type_enum_get_type() instead.
320  */
321 GType
322 e_cal_source_type_enum_get_type (void)
323 {
324         static volatile gsize enum_type__volatile = 0;
325
326         if (g_once_init_enter (&enum_type__volatile)) {
327                 GType enum_type;
328                 static GEnumValue values[] = {
329                         { E_CAL_SOURCE_TYPE_EVENT, "Event", "Event"},
330                         { E_CAL_SOURCE_TYPE_TODO, "ToDo", "ToDo"},
331                         { E_CAL_SOURCE_TYPE_JOURNAL, "Journal", "Journal"},
332                         { E_CAL_SOURCE_TYPE_LAST, "Invalid", "Invalid"},
333                         { -1, NULL, NULL}
334                 };
335
336                 enum_type = g_enum_register_static ("ECalSourceTypeEnum", values);
337                 g_once_init_leave (&enum_type__volatile, enum_type);
338         }
339
340         return enum_type__volatile;
341 }
342
343 /**
344  * e_cal_set_mode_status_enum_get_type:
345  *
346  * Registers the #ECalSetModeStatusEnum type with glib.
347  *
348  * Returns: the ID of the #ECalSetModeStatusEnum type.
349  *
350  * Deprecated: 3.2: This type has been dropped completely.
351  */
352 GType
353 e_cal_set_mode_status_enum_get_type (void)
354 {
355         static volatile gsize enum_type__volatile = 0;
356
357         if (g_once_init_enter (&enum_type__volatile)) {
358                 GType enum_type;
359                 static GEnumValue values[] = {
360                         { E_CAL_SET_MODE_SUCCESS,          "ECalSetModeSuccess",         "success"     },
361                         { E_CAL_SET_MODE_ERROR,            "ECalSetModeError",           "error"       },
362                         { E_CAL_SET_MODE_NOT_SUPPORTED,    "ECalSetModeNotSupported",    "unsupported" },
363                         { -1,                                   NULL,                              NULL}
364                 };
365
366                 enum_type = g_enum_register_static ("ECalSetModeStatusEnum", values);
367                 g_once_init_leave (&enum_type__volatile, enum_type);
368         }
369
370         return enum_type__volatile;
371 }
372
373 /**
374  * cal_mode_enum_get_type:
375  *
376  * Registers the #CalModeEnum type with glib.
377  *
378  * Returns: the ID of the #CalModeEnum type.
379  *
380  * Deprecated: 3.2: This type has been dropped completely.
381  */
382 GType
383 cal_mode_enum_get_type (void)
384 {
385         static volatile gsize enum_type__volatile = 0;
386
387         if (g_once_init_enter (&enum_type__volatile)) {
388                 GType enum_type;
389                 static GEnumValue values[] = {
390                         { CAL_MODE_INVALID,                     "CalModeInvalid",                  "invalid" },
391                         { CAL_MODE_LOCAL,                       "CalModeLocal",                    "local"   },
392                         { CAL_MODE_REMOTE,                      "CalModeRemote",                   "remote"  },
393                         { CAL_MODE_ANY,                         "CalModeAny",                      "any"     },
394                         { -1,                                   NULL,                              NULL      }
395                 };
396
397                 enum_type = g_enum_register_static ("CalModeEnum", values);
398                 g_once_init_leave (&enum_type__volatile, enum_type);
399         }
400
401         return enum_type__volatile;
402 }
403
404 static EDataCalObjType
405 convert_type (ECalSourceType type)
406 {
407         switch (type) {
408         case E_CAL_SOURCE_TYPE_EVENT:
409                 return Event;
410         case E_CAL_SOURCE_TYPE_TODO:
411                 return Todo;
412         case E_CAL_SOURCE_TYPE_JOURNAL:
413                 return Journal;
414         default:
415                 return AnyType;
416         }
417
418         return AnyType;
419 }
420
421 static void
422 e_cal_init (ECal *ecal)
423 {
424         LOCK_FACTORY ();
425         active_cals++;
426         UNLOCK_FACTORY ();
427
428         ecal->priv = E_CAL_GET_PRIVATE (ecal);
429
430         ecal->priv->load_state = E_CAL_LOAD_NOT_LOADED;
431         ecal->priv->uri = NULL;
432         ecal->priv->local_attachment_store = NULL;
433
434         ecal->priv->cal_address = NULL;
435         ecal->priv->alarm_email_address = NULL;
436         ecal->priv->ldap_attribute = NULL;
437         ecal->priv->capabilities = NULL;
438         ecal->priv->gdbus_cal = NULL;
439         ecal->priv->timezones = g_hash_table_new (g_str_hash, g_str_equal);
440         ecal->priv->default_zone = icaltimezone_get_utc_timezone ();
441         ecal->priv->free_busy_data_lock = g_mutex_new ();
442         g_static_rec_mutex_init (&ecal->priv->cache_lock);
443 }
444
445 static void
446 gdbus_cal_disconnect (ECal *ecal);
447
448 /*
449  * Called when the calendar server dies.
450  */
451 static void
452 gdbus_cal_closed_cb (GDBusConnection *connection,
453                      gboolean remote_peer_vanished,
454                      GError *error,
455                      ECal *ecal)
456 {
457         GError *err = NULL;
458
459         g_return_if_fail (E_IS_CAL (ecal));
460
461         if (error) {
462                 err = g_error_copy (error);
463                 unwrap_gerror (&err);
464         }
465
466         if (err) {
467                 g_debug (G_STRLOC ": ECal GDBus connection is closed%s: %s", remote_peer_vanished ? ", remote peer vanished" : "", err->message);
468                 g_error_free (err);
469         } else {
470                 g_debug (G_STRLOC ": ECal GDBus connection is closed%s", remote_peer_vanished ? ", remote peer vanished" : "");
471         }
472
473         gdbus_cal_disconnect (ecal);
474
475         g_signal_emit (G_OBJECT (ecal), e_cal_signals[BACKEND_DIED], 0);
476 }
477
478 static void
479 gdbus_cal_connection_gone_cb (GDBusConnection *connection,
480                               const gchar *sender_name,
481                               const gchar *object_path,
482                               const gchar *interface_name,
483                               const gchar *signal_name,
484                               GVariant *parameters,
485                               gpointer user_data)
486 {
487         /* signal subscription takes care of correct parameters,
488          * thus just do what is to be done here */
489         gdbus_cal_closed_cb (connection, TRUE, NULL, user_data);
490 }
491
492 static void
493 gdbus_cal_disconnect (ECal *ecal)
494 {
495         /* Ensure that everything relevant is NULL */
496         LOCK_FACTORY ();
497
498         if (ecal->priv->gdbus_cal) {
499                 GError *error = NULL;
500                 GDBusConnection *connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (ecal->priv->gdbus_cal));
501
502                 g_signal_handlers_disconnect_by_func (connection, gdbus_cal_closed_cb, ecal);
503                 g_dbus_connection_signal_unsubscribe (connection, ecal->priv->gone_signal_id);
504                 ecal->priv->gone_signal_id = 0;
505
506                 e_gdbus_cal_call_close_sync (ecal->priv->gdbus_cal, NULL, &error);
507                 g_object_unref (ecal->priv->gdbus_cal);
508                 ecal->priv->gdbus_cal = NULL;
509
510                 if (error) {
511                         unwrap_gerror (&error);
512
513                         g_warning ("%s: Failed to close calendar, %s\n", G_STRFUNC, error->message);
514                         g_error_free (error);
515                 }
516         }
517         UNLOCK_FACTORY ();
518 }
519
520 /* Dispose handler for the calendar ecal */
521 static void
522 e_cal_dispose (GObject *object)
523 {
524         ECal *ecal = E_CAL (object);
525
526         gdbus_cal_disconnect (ecal);
527
528         /* Chain up to parent's dispose() method. */
529         G_OBJECT_CLASS (e_cal_parent_class)->dispose (object);
530 }
531
532 static void
533 free_timezone (gpointer key,
534                gpointer value,
535                gpointer data)
536 {
537         /* Note that the key comes from within the icaltimezone value, so we
538          * don't free that. */
539         icaltimezone_free (value, TRUE);
540 }
541
542 /* Finalize handler for the calendar ecal */
543 static void
544 e_cal_finalize (GObject *object)
545 {
546         ECal *ecal;
547         ECalPrivate *priv;
548
549         g_return_if_fail (object != NULL);
550         g_return_if_fail (E_IS_CAL (object));
551
552         ecal = E_CAL (object);
553         priv = ecal->priv;
554
555         priv->load_state = E_CAL_LOAD_NOT_LOADED;
556
557         if (priv->source) {
558                 g_object_unref (priv->source);
559                 priv->source = NULL;
560         }
561
562         if (priv->uri) {
563                 g_free (priv->uri);
564                 priv->uri = NULL;
565         }
566
567         if (priv->local_attachment_store) {
568                 g_free (priv->local_attachment_store);
569                 priv->local_attachment_store = NULL;
570         }
571
572         if (priv->cal_address) {
573                 g_free (priv->cal_address);
574                 priv->cal_address = NULL;
575         }
576         if (priv->alarm_email_address) {
577                 g_free (priv->alarm_email_address);
578                 priv->alarm_email_address = NULL;
579         }
580         if (priv->ldap_attribute) {
581                 g_free (priv->ldap_attribute);
582                 priv->ldap_attribute = NULL;
583         }
584         if (priv->capabilities) {
585                 g_free (priv->capabilities);
586                 priv->capabilities = NULL;
587         }
588
589         if (priv->free_busy_data) {
590                 g_mutex_lock (priv->free_busy_data_lock);
591                 g_list_foreach (*priv->free_busy_data, (GFunc) g_object_unref, NULL);
592                 g_list_free (*priv->free_busy_data);
593                 *priv->free_busy_data = NULL;
594                 priv->free_busy_data = NULL;
595                 g_mutex_unlock (priv->free_busy_data_lock);
596         }
597
598         g_hash_table_foreach (priv->timezones, free_timezone, NULL);
599         g_hash_table_destroy (priv->timezones);
600         priv->timezones = NULL;
601         g_static_rec_mutex_free (&priv->cache_lock);
602         g_mutex_free (priv->free_busy_data_lock);
603
604         /* Chain up to parent's finalize() method. */
605         G_OBJECT_CLASS (e_cal_parent_class)->finalize (object);
606
607         LOCK_FACTORY ();
608         active_cals--;
609         UNLOCK_FACTORY ();
610 }
611
612 /* Class initialization function for the calendar ecal */
613 static void
614 e_cal_class_init (ECalClass *class)
615 {
616         GObjectClass *object_class;
617
618         object_class = (GObjectClass *) class;
619
620         /* XXX The "cal-opened" signal is deprecated. */
621         e_cal_signals[CAL_OPENED] =
622                 g_signal_new ("cal_opened",
623                               G_TYPE_FROM_CLASS (class),
624                               G_SIGNAL_RUN_FIRST,
625                               G_STRUCT_OFFSET (ECalClass, cal_opened),
626                               NULL, NULL,
627                               g_cclosure_marshal_VOID__INT,
628                               G_TYPE_NONE, 1, G_TYPE_INT);
629
630         /**
631          * ECal::cal-opened-ex:
632          * @ecal:: self
633          * @error: (type glong):
634          */
635         e_cal_signals[CAL_OPENED_EX] =
636                 g_signal_new ("cal_opened_ex",
637                               G_TYPE_FROM_CLASS (class),
638                               G_SIGNAL_RUN_FIRST,
639                               G_STRUCT_OFFSET (ECalClass, cal_opened_ex),
640                               NULL, NULL,
641                               g_cclosure_marshal_VOID__POINTER,
642                               G_TYPE_NONE, 1, G_TYPE_POINTER);
643
644         e_cal_signals[CAL_SET_MODE] =
645                 g_signal_new ("cal_set_mode",
646                               G_TYPE_FROM_CLASS (class),
647                               G_SIGNAL_RUN_FIRST,
648                               G_STRUCT_OFFSET (ECalClass, cal_set_mode),
649                               NULL, NULL,
650                               e_cal_marshal_VOID__ENUM_ENUM,
651                               G_TYPE_NONE, 2,
652                               E_CAL_SET_MODE_STATUS_ENUM_TYPE,
653                               CAL_MODE_ENUM_TYPE);
654         e_cal_signals[BACKEND_ERROR] =
655                 g_signal_new ("backend_error",
656                               G_TYPE_FROM_CLASS (class),
657                               G_SIGNAL_RUN_FIRST,
658                               G_STRUCT_OFFSET (ECalClass, backend_error),
659                               NULL, NULL,
660                               g_cclosure_marshal_VOID__STRING,
661                               G_TYPE_NONE, 1,
662                               G_TYPE_STRING);
663         e_cal_signals[BACKEND_DIED] =
664                 g_signal_new ("backend_died",
665                               G_TYPE_FROM_CLASS (class),
666                               G_SIGNAL_RUN_FIRST,
667                               G_STRUCT_OFFSET (ECalClass, backend_died),
668                               NULL, NULL,
669                               g_cclosure_marshal_VOID__VOID,
670                               G_TYPE_NONE, 0);
671
672         class->cal_opened = NULL;
673         class->cal_opened_ex = NULL;
674         class->backend_died = NULL;
675
676         object_class->dispose = e_cal_dispose;
677         object_class->finalize = e_cal_finalize;
678
679         g_type_class_add_private (class, sizeof (ECalPrivate));
680 }
681
682 static void
683 cal_factory_proxy_closed_cb (GDBusConnection *connection,
684                              gboolean remote_peer_vanished,
685                              GError *error,
686                              gpointer user_data)
687 {
688         GError *err = NULL;
689
690         LOCK_FACTORY ();
691
692         if (cal_connection_closed_id) {
693                 g_dbus_connection_signal_unsubscribe (connection, cal_connection_closed_id);
694                 cal_connection_closed_id = 0;
695                 g_signal_handlers_disconnect_by_func (connection, cal_factory_proxy_closed_cb, NULL);
696         }
697
698         if (cal_factory_proxy) {
699                 g_object_unref (cal_factory_proxy);
700                 cal_factory_proxy = NULL;
701         }
702
703         if (error) {
704                 err = g_error_copy (error);
705                 unwrap_gerror (&err);
706         }
707
708         if (err) {
709                 g_debug ("GDBus connection is closed%s: %s", remote_peer_vanished ? ", remote peer vanished" : "", err->message);
710                 g_error_free (err);
711         } else if (active_cals) {
712                 g_debug ("GDBus connection is closed%s", remote_peer_vanished ? ", remote peer vanished" : "");
713         }
714
715         UNLOCK_FACTORY ();
716 }
717
718 static void
719 cal_factory_connection_gone_cb (GDBusConnection *connection,
720                                 const gchar *sender_name,
721                                 const gchar *object_path,
722                                 const gchar *interface_name,
723                                 const gchar *signal_name,
724                                 GVariant *parameters,
725                                 gpointer user_data)
726 {
727         /* signal subscription takes care of correct parameters,
728          * thus just do what is to be done here */
729         cal_factory_proxy_closed_cb (connection, TRUE, NULL, user_data);
730 }
731
732 /* one-time start up for libecal */
733 static gboolean
734 e_cal_activate (GError **error)
735 {
736         GDBusConnection *connection;
737
738         LOCK_FACTORY ();
739         if (G_LIKELY (cal_factory_proxy)) {
740                 UNLOCK_FACTORY ();
741                 return TRUE;
742         }
743
744         cal_factory_proxy = e_gdbus_cal_factory_proxy_new_for_bus_sync (
745                 G_BUS_TYPE_SESSION,
746                 G_DBUS_PROXY_FLAGS_NONE,
747                 CALENDAR_DBUS_SERVICE_NAME,
748                 "/org/gnome/evolution/dataserver/CalendarFactory",
749                 NULL,
750                 error);
751
752         if (!cal_factory_proxy) {
753                 UNLOCK_FACTORY ();
754                 return FALSE;
755         }
756
757         connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (cal_factory_proxy));
758         cal_connection_closed_id = g_dbus_connection_signal_subscribe (connection,
759                 NULL,                                   /* sender */
760                 "org.freedesktop.DBus",                 /* interface */
761                 "NameOwnerChanged",                     /* member */
762                 "/org/freedesktop/DBus",                /* object_path */
763                 CALENDAR_DBUS_SERVICE_NAME,             /* arg0 */
764                 G_DBUS_SIGNAL_FLAGS_NONE,
765                 cal_factory_connection_gone_cb, NULL, NULL);
766
767         g_signal_connect (connection, "closed", G_CALLBACK (cal_factory_proxy_closed_cb), NULL);
768
769         UNLOCK_FACTORY ();
770
771         return TRUE;
772 }
773
774 static gchar *
775 build_proxy_pass_key (ECal *ecal,
776                       const gchar *parent_user)
777 {
778         gchar *euri_str;
779         const gchar *uri;
780         EUri *euri;
781
782         uri = e_cal_get_uri (ecal);
783
784         euri = e_uri_new (uri);
785         g_free (euri->user);
786         euri->user = g_strdup (parent_user);
787
788         euri_str = e_uri_to_string (euri, FALSE);
789
790         e_uri_free (euri);
791         return euri_str;
792 }
793
794 static gchar *
795 build_pass_key (ECal *ecal)
796 {
797         gchar *euri_str;
798         const gchar *uri;
799         EUri *euri;
800
801         uri = e_cal_get_uri (ecal);
802
803         euri = e_uri_new (uri);
804         euri_str = e_uri_to_string (euri, FALSE);
805
806         e_uri_free (euri);
807         return euri_str;
808 }
809
810 static void async_open_report_result (ECal *ecal, const GError *error);
811
812 static void
813 authenticate_user_ready_cb (GObject *source_object,
814                             GAsyncResult *result,
815                             gpointer user_data)
816 {
817         ECal *cal = user_data;
818         GError *error = NULL;
819
820         g_return_if_fail (cal != NULL);
821
822         if (!e_gdbus_cal_call_authenticate_user_finish (G_DBUS_PROXY (source_object), result, &error))
823                 cal->priv->load_state = E_CAL_LOAD_NOT_LOADED;
824         else
825                 cal->priv->load_state = E_CAL_LOAD_LOADED;
826
827         if (cal->priv->requires_auth && !error) {
828                 cal->priv->load_state = E_CAL_LOAD_NOT_LOADED;
829                 g_set_error_literal (&error, E_CALENDAR_ERROR, E_CALENDAR_STATUS_AUTHENTICATION_FAILED, e_cal_get_error_message (E_CALENDAR_STATUS_AUTHENTICATION_FAILED));
830         }
831
832         unwrap_gerror (&error);
833         async_open_report_result (cal, error);
834
835         if (error)
836                 g_error_free (error);
837 }
838
839 static void
840 finish_backend_opening_phase (ECal *cal)
841 {
842         const gchar *strv[2];
843
844         g_return_if_fail (cal != NULL);
845         g_return_if_fail (E_IS_CAL (cal));
846
847         strv[0] = "";
848         strv[1] = NULL;
849
850         e_gdbus_cal_call_authenticate_user_sync (cal->priv->gdbus_cal, (const gchar * const *) strv, NULL, NULL);
851 }
852
853 static gboolean
854 call_authenticate_user (ECal *cal,
855                         gboolean async,
856                         GError **error)
857 {
858         gchar *username = NULL, *password = NULL;
859         ECredentials *credentials = NULL;
860         ECalPrivate *priv;
861
862         g_return_val_if_fail (cal != NULL, FALSE);
863         g_return_val_if_fail (E_IS_CAL (cal), FALSE);
864
865         priv = cal->priv;
866
867         if (!priv->gdbus_cal) {
868                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
869         }
870
871         /* see if the backend needs authentication */
872         if ((priv->mode != CAL_MODE_LOCAL) && (e_source_get_property (priv->source, "auth") || priv->requires_auth)) {
873                 gchar *prompt, *key, *auth_type = NULL;
874                 gchar *parent_user;
875
876                 priv->load_state = E_CAL_LOAD_AUTHENTICATING;
877
878                 if (priv->auth_func == NULL) {
879                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
880                         finish_backend_opening_phase (cal);
881                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
882                 }
883
884                 username = e_source_get_duped_property (priv->source, "username");
885                 if (!username) {
886                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
887                         finish_backend_opening_phase (cal);
888                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
889                 }
890
891                 prompt = g_strdup_printf (_("Enter password for %s (user %s)"),
892                                 e_source_get_display_name (priv->source), username);
893
894                 auth_type = e_source_get_duped_property (priv->source, "auth-type");
895                 if (auth_type)
896                         key = build_pass_key (cal);
897                 else {
898                         parent_user = e_source_get_duped_property (priv->source, "parent_id_name");
899                         if (parent_user) {
900                                 key = build_proxy_pass_key (cal, parent_user);
901                                 /*
902                                  * This password prompt will be prompted rarely. Since the key that is passed to
903                                  * the auth_func corresponds to the parent user.
904                                  */
905                                 prompt = g_strdup_printf (_("Enter password for %s to enable proxy for user %s"), e_source_get_display_name (priv->source), parent_user);
906                                 g_free (parent_user);
907                         } else
908                                 key = g_strdup (e_cal_get_uri (cal));
909                 }
910                 g_free (auth_type);
911
912                 if (!key) {
913                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
914                         g_free (username);
915                         finish_backend_opening_phase (cal);
916                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
917                 }
918
919                 password = priv->auth_func (cal, prompt, key, priv->auth_user_data);
920
921                 if (!password) {
922                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
923                         g_free (username);
924                         finish_backend_opening_phase (cal);
925                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
926                 }
927
928                 g_free (prompt);
929                 g_free (key);
930         }
931
932         if (username)
933                 credentials = e_credentials_new_args (
934                         E_CREDENTIALS_KEY_USERNAME, username,
935                         E_CREDENTIALS_KEY_PASSWORD, password,
936                         NULL);
937         else
938                 priv->load_state = E_CAL_LOAD_NOT_LOADED;
939
940         g_free (username);
941         e_credentials_util_safe_free_string (password);
942
943         if (credentials) {
944                 gchar **strv = e_credentials_to_strv (credentials);
945
946                 if (async) {
947                         cal->priv->requires_auth = FALSE;
948                         e_gdbus_cal_call_authenticate_user (cal->priv->gdbus_cal, (const gchar * const *) strv, NULL, authenticate_user_ready_cb, cal);
949                 } else if (e_gdbus_cal_call_authenticate_user_sync (cal->priv->gdbus_cal, (const gchar * const *) strv, NULL, error))
950                         priv->load_state = E_CAL_LOAD_LOADED;
951                 else
952                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
953
954                 g_strfreev (strv);
955                 e_credentials_free (credentials);
956
957                 if (error && *error)
958                         unwrap_gerror (error);
959         } else if (priv->requires_auth) {
960                 priv->load_state = E_CAL_LOAD_NOT_LOADED;
961                 finish_backend_opening_phase (cal);
962                 g_set_error_literal (error, E_CALENDAR_ERROR, E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, e_cal_get_error_message (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED));
963         }
964
965         return credentials && (!error || !*error);
966 }
967
968 static gboolean
969 reopen_with_auth (gpointer data)
970 {
971         ECalendarStatus status;
972         GError *error = NULL;
973
974         open_calendar (E_CAL (data), TRUE, &error,
975                 &status,
976                 TRUE, FALSE);
977
978         if (error)
979                 g_error_free (error);
980
981         return FALSE;
982 }
983
984 static void
985 auth_required_cb (EGdbusCal *gdbus_cal,
986                   const gchar * const *credentials_strv,
987                   ECal *cal)
988 {
989         ECalPrivate *priv;
990         g_return_if_fail (E_IS_CAL (cal));
991
992         priv = cal->priv;
993
994         priv->requires_auth = TRUE;
995
996         if (priv->load_state != E_CAL_LOAD_AUTHENTICATING)
997                 g_idle_add (reopen_with_auth, (gpointer) cal);
998 }
999
1000 static void
1001 free_busy_data_cb (EGdbusCal *gdbus_cal,
1002                    const gchar * const *free_busy_strv,
1003                    ECal *cal)
1004 {
1005         ECalPrivate *priv;
1006
1007         g_return_if_fail (E_IS_CAL (cal));
1008
1009         priv = cal->priv;
1010
1011         g_mutex_lock (priv->free_busy_data_lock);
1012
1013         if (priv->free_busy_data) {
1014                 gint ii;
1015                 GList *list = *priv->free_busy_data;
1016
1017                 for (ii = 0; free_busy_strv[ii]; ii++) {
1018                         ECalComponent *comp;
1019                         icalcomponent *icalcomp;
1020                         icalcomponent_kind kind;
1021
1022                         icalcomp = icalcomponent_new_from_string (free_busy_strv[ii]);
1023                         if (!icalcomp)
1024                                 continue;
1025
1026                         kind = icalcomponent_isa (icalcomp);
1027                         if (kind == ICAL_VFREEBUSY_COMPONENT) {
1028                                 comp = e_cal_component_new ();
1029                                 if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
1030                                         icalcomponent_free (icalcomp);
1031                                         g_object_unref (G_OBJECT (comp));
1032                                         continue;
1033                                 }
1034
1035                                 list = g_list_append (list, comp);
1036                         } else {
1037                                 icalcomponent_free (icalcomp);
1038                         }
1039                 }
1040
1041                 *priv->free_busy_data = list;
1042         }
1043
1044         g_mutex_unlock (priv->free_busy_data_lock);
1045 }
1046
1047 typedef struct
1048 {
1049         ECal *ecal;
1050         gchar *message;
1051 }  ECalErrorData;
1052
1053 static gboolean
1054 backend_error_idle_cb (gpointer data)
1055 {
1056         ECalErrorData *error_data = data;
1057
1058         g_signal_emit (G_OBJECT (error_data->ecal), e_cal_signals[BACKEND_ERROR], 0, error_data->message);
1059
1060         g_object_unref (error_data->ecal);
1061         g_free (error_data->message);
1062         g_free (error_data);
1063
1064         return FALSE;
1065 }
1066
1067 /* Handle the error_occurred signal from the listener */
1068 static void
1069 backend_error_cb (EGdbusCal *gdbus_cal,
1070                   const gchar *message,
1071                   ECal *ecal)
1072 {
1073         ECalErrorData *error_data;
1074
1075         g_return_if_fail (E_IS_CAL (ecal));
1076
1077         error_data = g_new0 (ECalErrorData, 1);
1078
1079         error_data->ecal = g_object_ref (ecal);
1080         error_data->message = g_strdup (message);
1081
1082         g_idle_add (backend_error_idle_cb, error_data);
1083 }
1084
1085 static void
1086 readonly_cb (EGdbusCal *gdbus_cal,
1087              gboolean read_only,
1088              ECal *cal)
1089 {
1090         ECalPrivate *priv;
1091
1092         g_return_if_fail (cal && E_IS_CAL (cal));
1093
1094         priv = cal->priv;
1095         priv->read_only = read_only;
1096 }
1097
1098 static void
1099 online_cb (EGdbusCal *gdbus_cal,
1100            gboolean is_online,
1101            ECal *cal)
1102 {
1103         g_return_if_fail (E_IS_CAL (cal));
1104
1105         g_signal_emit (G_OBJECT (cal), e_cal_signals[CAL_SET_MODE],
1106                        0, E_CALENDAR_STATUS_OK, is_online ? Remote : Local);
1107 }
1108
1109 /*
1110 static void
1111 backend_died_cb (EComponentListener *cl,
1112  *               gpointer user_data)
1113 {
1114         ECalPrivate *priv;
1115         ECal *ecal = (ECal *) user_data;
1116  *
1117         priv = ecal->priv;
1118         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1119         g_signal_emit (G_OBJECT (ecal), e_cal_signals[BACKEND_DIED], 0);
1120 }*/
1121
1122 static void
1123 set_local_attachment_store (ECal *ecal)
1124 {
1125         gchar *cache_dir = NULL;
1126         GError *error = NULL;
1127
1128         e_gdbus_cal_call_get_backend_property_sync (
1129                 ecal->priv->gdbus_cal, CLIENT_BACKEND_PROPERTY_CACHE_DIR, &cache_dir, NULL, &error);
1130
1131         if (error == NULL)
1132                 ecal->priv->local_attachment_store = cache_dir;
1133         else {
1134                 unwrap_gerror (&error);
1135                 g_warning ("%s", error->message);
1136                 g_error_free (error);
1137         }
1138 }
1139
1140 /**
1141  * e_cal_new:
1142  * @source: An #ESource to be used for the client.
1143  * @type: Type of the client.
1144  *
1145  * Creates a new calendar client. This does not open the calendar itself,
1146  * for that, #e_cal_open or #e_cal_open_async needs to be called.
1147  *
1148  * Returns: A newly-created calendar client, or NULL if the client could
1149  * not be constructed because it could not contact the calendar server.
1150  *
1151  * Deprecated: 3.2: Use e_cal_client_new() instead.
1152  **/
1153 ECal *
1154 e_cal_new (ESource *source,
1155            ECalSourceType type)
1156 {
1157         ECal *ecal;
1158         ECalPrivate *priv;
1159         gchar *path;
1160         gchar *xml;
1161         gchar **strv;
1162         GError *error = NULL;
1163         GDBusConnection *connection;
1164
1165         g_return_val_if_fail (source && E_IS_SOURCE (source), NULL);
1166         g_return_val_if_fail (type < E_CAL_SOURCE_TYPE_LAST, NULL);
1167
1168         if (!e_cal_activate (&error)) {
1169                 unwrap_gerror (&error);
1170                 g_warning("Cannot activate ECal: %s\n", error ? error->message : "Unknown error");
1171                 if (error)
1172                         g_error_free (error);
1173                 return NULL;
1174         }
1175
1176         ecal = g_object_new (E_TYPE_CAL, NULL);
1177         priv = ecal->priv;
1178
1179         priv->source = g_object_ref (source);
1180         priv->uri = e_source_get_uri (source);
1181         priv->type = type;
1182
1183         xml = e_source_to_standalone_xml (priv->source);
1184         strv = e_gdbus_cal_factory_encode_get_cal (xml, convert_type (priv->type));
1185         if (!e_gdbus_cal_factory_call_get_cal_sync (G_DBUS_PROXY (cal_factory_proxy), (const gchar * const *) strv, &path, NULL, &error)) {
1186                 g_strfreev (strv);
1187                 g_free (xml);
1188                 unwrap_gerror (&error);
1189                 g_warning ("Cannot get cal from factory: %s", error ? error->message : "Unknown error");
1190                 if (error)
1191                         g_error_free (error);
1192                 g_object_unref (ecal);
1193                 return NULL;
1194         }
1195         g_strfreev (strv);
1196         g_free (xml);
1197
1198         priv->gdbus_cal = G_DBUS_PROXY (e_gdbus_cal_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (cal_factory_proxy)),
1199                                                       G_DBUS_PROXY_FLAGS_NONE,
1200                                                       CALENDAR_DBUS_SERVICE_NAME,
1201                                                       path,
1202                                                       NULL,
1203                                                       &error));
1204
1205         if (!priv->gdbus_cal) {
1206                 g_free (path);
1207                 unwrap_gerror (&error);
1208                 g_warning ("Cannot create cal proxy: %s", error ? error->message : "Unknown error");
1209                 if (error)
1210                         g_error_free (error);
1211                 g_object_unref (ecal);
1212                 return NULL;
1213         }
1214
1215         connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->gdbus_cal));
1216         priv->gone_signal_id = g_dbus_connection_signal_subscribe (connection,
1217                 "org.freedesktop.DBus",                 /* sender */
1218                 "org.freedesktop.DBus",                 /* interface */
1219                 "NameOwnerChanged",                     /* member */
1220                 "/org/freedesktop/DBus",                /* object_path */
1221                 CALENDAR_DBUS_SERVICE_NAME,             /* arg0 */
1222                 G_DBUS_SIGNAL_FLAGS_NONE,
1223                 gdbus_cal_connection_gone_cb, ecal, NULL);
1224         g_signal_connect (connection, "closed", G_CALLBACK (gdbus_cal_closed_cb), ecal);
1225
1226         g_signal_connect (priv->gdbus_cal, "auth-required", G_CALLBACK (auth_required_cb), ecal);
1227         g_signal_connect (priv->gdbus_cal, "backend-error", G_CALLBACK (backend_error_cb), ecal);
1228         g_signal_connect (priv->gdbus_cal, "readonly", G_CALLBACK (readonly_cb), ecal);
1229         g_signal_connect (priv->gdbus_cal, "online", G_CALLBACK (online_cb), ecal);
1230         g_signal_connect (priv->gdbus_cal, "free-busy-data", G_CALLBACK (free_busy_data_cb), ecal);
1231
1232         /* Set the local attachment store path for the calendar */
1233         set_local_attachment_store (ecal);
1234
1235         g_free (path);
1236
1237         return ecal;
1238 }
1239
1240 /* for each known source calls check_func, which should return TRUE if the required
1241  * source have been found. Function returns NULL or the source on which was returned
1242  * TRUE by the check_func. Non-NULL pointer should be unreffed by g_object_unref.
1243  *
1244  * 'sources' is an output parameter and cannot be NULL. When returned non-NULL, then
1245  * should be freed with g_object_unref function. */
1246 static ESource *
1247 search_known_sources (ECalSourceType type,
1248                       gboolean (*check_func) (ESource *source,
1249                                               gpointer user_data),
1250                       gpointer user_data,
1251                       ESourceList **sources,
1252                       GError **error)
1253 {
1254         ESource *res = NULL;
1255         GSList *g;
1256         GError *err = NULL;
1257
1258         g_return_val_if_fail (sources != NULL, NULL);
1259         g_return_val_if_fail (check_func != NULL, NULL);
1260
1261         *sources = NULL;
1262
1263         if (!e_cal_get_sources (sources, type, &err)) {
1264                 g_propagate_error (error, err);
1265                 return NULL;
1266         }
1267
1268         for (g = e_source_list_peek_groups (*sources); g; g = g->next) {
1269                 ESourceGroup *group = E_SOURCE_GROUP (g->data);
1270                 GSList *s;
1271
1272                 for (s = e_source_group_peek_sources (group); s; s = s->next) {
1273                         ESource *source = E_SOURCE (s->data);
1274
1275                         if (check_func (source, user_data)) {
1276                                 res = g_object_ref (source);
1277                                 break;
1278                         }
1279                 }
1280
1281                 if (res)
1282                         break;
1283         }
1284
1285         return res;
1286 }
1287
1288 static gboolean
1289 check_uri (ESource *source,
1290            gpointer uri)
1291 {
1292         const gchar *suri;
1293
1294         g_return_val_if_fail (source != NULL, FALSE);
1295         g_return_val_if_fail (uri != NULL, FALSE);
1296
1297         suri = e_source_peek_absolute_uri (source);
1298         if (suri)
1299                 return !g_ascii_strcasecmp (suri, uri);
1300         else {
1301                 gboolean ret;
1302                 gchar *suri2;
1303
1304                 suri2 = e_source_get_uri (source);
1305                 ret = !g_ascii_strcasecmp (suri2, uri);
1306                 g_free (suri2);
1307                 return ret;
1308         }
1309 }
1310
1311 /**
1312  * e_cal_new_from_uri:
1313  * @uri: The URI pointing to the calendar to open.
1314  * @type: Type of the client.
1315  *
1316  * Creates a new calendar client. This does not open the calendar itself,
1317  * for that, #e_cal_open or #e_cal_open_async needs to be called.
1318  *
1319  * Returns: A newly-created calendar client, or NULL if the client could
1320  * not be constructed because it could not contact the calendar server.
1321  *
1322  * Deprecated: 3.2: Use e_cal_client_new_from_uri() instead.
1323  **/
1324 ECal *
1325 e_cal_new_from_uri (const gchar *uri,
1326                     ECalSourceType type)
1327 {
1328         ESourceList *sources = NULL;
1329         ESource *source;
1330         ECal *cal;
1331
1332         source = search_known_sources (type, check_uri, (gpointer) uri, &sources, NULL);
1333         if (!source)
1334                 source = e_source_new_with_absolute_uri ("", uri);
1335
1336         cal = e_cal_new (source, type);
1337
1338         g_object_unref (source);
1339         if (sources)
1340                 g_object_unref (sources);
1341
1342         return cal;
1343 }
1344
1345 static ECal *
1346 get_local_source (ECalSourceType type)
1347 {
1348         ESourceList *sources;
1349         ESourceGroup *on_this_computer;
1350         GSList *local_sources, *iter;
1351         gchar *source_uri = NULL;
1352         ECal *ecal;
1353
1354         if (!e_cal_get_sources (&sources, type, NULL)) {
1355                 g_warning ("Could not get task sources from GConf!");
1356                 goto out;
1357         }
1358
1359         on_this_computer = e_source_list_ensure_group (sources,
1360                                                        _("On This Computer"),
1361                                                        "local:", TRUE);
1362         if (!on_this_computer) {
1363                 g_object_unref (sources);
1364                 goto out;
1365         }
1366
1367         local_sources = e_source_group_peek_sources (on_this_computer);
1368         for (iter = local_sources; !source_uri && iter != NULL; iter = iter->next) {
1369                 ESource *source = iter->data;
1370                 gchar *uri;
1371
1372                 uri = e_source_get_uri (source);
1373                 if (g_strcmp0 (uri, "local:system") == 0)
1374                         source_uri = uri;
1375                 else
1376                         g_free (uri);
1377         }
1378         if (!source_uri) {
1379                 ESource *source;
1380                 source = e_source_new (_("Personal"), "system");
1381                 e_source_set_color_spec (source, "#BECEDD");
1382                 e_source_group_add_source (on_this_computer, source, -1);
1383                 g_object_unref (source);
1384
1385                 if (!e_source_list_sync (sources, NULL))
1386                         g_warning ("Cannot add system source to GConf!");
1387         }
1388         g_object_unref (on_this_computer);
1389         g_object_unref (sources);
1390  out:
1391         ecal = e_cal_new_from_uri (source_uri?:"local:system", type);
1392         g_free (source_uri);
1393         return ecal;
1394 }
1395 /**
1396  * e_cal_new_system_calendar:
1397  *
1398  * Create a calendar client for the system calendar, which should always be
1399  * present in all Evolution installations. This does not open the calendar
1400  * itself -- for that use e_cal_open() or e_cal_open_async().
1401  *
1402  * Returns: A newly-created calendar client, or %NULL if the client could
1403  * not be constructed.
1404  *
1405  * Deprecated: 3.2: Use e_cal_client_new_system() instead.
1406  */
1407 ECal *
1408 e_cal_new_system_calendar (void)
1409 {
1410         return get_local_source (E_CAL_SOURCE_TYPE_EVENT);
1411 }
1412
1413 /**
1414  * e_cal_new_system_tasks:
1415  *
1416  * Create a calendar client for the system task list, which should always
1417  * be present in all Evolution installations. This does not open the task
1418  * list itself -- for that use e_cal_open() or e_cal_open_async().
1419  *
1420  * Returns: A newly-created calendar client, or %NULL if the client could
1421  * not be constructed.
1422  *
1423  * Deprecated: 3.2: Use e_cal_client_new_system() instead.
1424  */
1425 ECal *
1426 e_cal_new_system_tasks (void)
1427 {
1428         return get_local_source (E_CAL_SOURCE_TYPE_TODO);
1429 }
1430
1431 /**
1432  * e_cal_new_system_memos:
1433  *
1434  * Create a calendar client for the system memo list, which should always
1435  * be present in all Evolution installations. This does not open the memo
1436  * list itself -- for that use e_cal_open() or e_cal_open_async().
1437  *
1438  * Returns: A newly-created calendar client, or %NULL if the client could
1439  * not be constructed.
1440  *
1441  * Deprecated: 3.2: Use e_cal_client_new_system() instead.
1442  */
1443 ECal *
1444 e_cal_new_system_memos (void)
1445 {
1446         return get_local_source (E_CAL_SOURCE_TYPE_JOURNAL);
1447 }
1448
1449 /**
1450  * e_cal_set_auth_func
1451  * @ecal: A calendar client.
1452  * @func: The authentication function
1453  * @data: User data to be used when calling the authentication function
1454  *
1455  * Sets the given authentication function on the calendar client. This
1456  * function will be called any time the calendar server needs a
1457  * password for an operation associated with the calendar and should
1458  * be supplied before any calendar is opened.
1459  *
1460  * When a calendar is opened asynchronously, the open function is
1461  * processed in a concurrent thread.  This means that the
1462  * authentication function will also be called from this thread.  As
1463  * such, the authentication callback cannot directly call any
1464  * functions that must be called from the main thread.  For example
1465  * any Gtk+ related functions, which must be proxied synchronously to
1466  * the main thread by the callback.
1467  *
1468  * The authentication function has the following signature
1469  * (ECalAuthFunc):
1470  *      gchar * auth_func (ECal *ecal,
1471  *                        const gchar *prompt,
1472  *                        const gchar *key,
1473  *                        gpointer user_data)
1474  *
1475  * Deprecated: 3.2: Use EClient::authenticate() signal on an #ECalClient instead.
1476  */
1477 void
1478 e_cal_set_auth_func (ECal *ecal,
1479                      ECalAuthFunc func,
1480                      gpointer data)
1481 {
1482         g_return_if_fail (ecal != NULL);
1483         g_return_if_fail (E_IS_CAL (ecal));
1484
1485         ecal->priv->auth_func = func;
1486         ecal->priv->auth_user_data = data;
1487 }
1488
1489 static void
1490 async_open_report_result (ECal *ecal,
1491                           const GError *error)
1492 {
1493         ECalendarStatus status;
1494
1495         g_return_if_fail (ecal && E_IS_CAL (ecal));
1496
1497         if (!error)
1498                 ecal->priv->load_state = E_CAL_LOAD_LOADED;
1499
1500         if (error) {
1501                 status = get_status_from_error (error);
1502         } else {
1503                 status = E_CALENDAR_STATUS_OK;
1504         }
1505
1506         g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED], 0, status);
1507         g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED_EX], 0, error);
1508 }
1509
1510 static gboolean
1511 reschedule_authenticate_cb (gpointer user_data)
1512 {
1513         GError *error = NULL;
1514         ECal *ecal = user_data;
1515
1516         g_return_val_if_fail (ecal && E_IS_CAL (ecal), FALSE);
1517
1518         if (g_main_depth () > 1)
1519                 return TRUE;
1520
1521         if (call_authenticate_user (ecal, TRUE, &error))
1522                 return FALSE;
1523
1524         unwrap_gerror (&error);
1525         async_open_report_result (ecal, error);
1526
1527         if (error)
1528                 g_error_free (error);
1529
1530         return FALSE;
1531 }
1532
1533 static void
1534 async_open_ready_cb (GDBusProxy *gdbus_cal,
1535                      GAsyncResult *res,
1536                      ECal *ecal)
1537 {
1538         GError *error = NULL;
1539
1540         g_return_if_fail (ecal && E_IS_CAL (ecal));
1541
1542         if (e_gdbus_cal_call_open_finish (gdbus_cal, res, &error)) {
1543                 if (g_main_depth () > 1) {
1544                         g_idle_add (reschedule_authenticate_cb, ecal);
1545                         return;
1546                 } else if (call_authenticate_user (ecal, TRUE, &error))
1547                         return;
1548         }
1549
1550         unwrap_gerror (&error);
1551
1552         async_open_report_result (ecal, error);
1553
1554         if (error)
1555                 g_error_free (error);
1556 }
1557
1558 static gboolean
1559 open_calendar (ECal *ecal,
1560                gboolean only_if_exists,
1561                GError **error,
1562                ECalendarStatus *status,
1563                gboolean needs_auth,
1564                gboolean async)
1565 {
1566         ECalPrivate *priv;
1567
1568         g_return_val_if_fail (error != NULL, FALSE);
1569
1570         e_return_error_if_fail (ecal != NULL, E_CALENDAR_STATUS_INVALID_ARG);
1571         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
1572         priv = ecal->priv;
1573         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
1574
1575         if (!needs_auth && priv->load_state == E_CAL_LOAD_LOADED) {
1576                 return TRUE;
1577         }
1578
1579         priv->load_state = E_CAL_LOAD_LOADING;
1580
1581         if ((priv->mode != CAL_MODE_LOCAL) && e_source_get_property (priv->source, "auth")) {
1582                 priv->load_state = E_CAL_LOAD_AUTHENTICATING;
1583
1584                 if (priv->auth_func == NULL) {
1585                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1586                         *status = E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED;
1587                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
1588                 }
1589
1590                 if (!e_source_get_property (priv->source, "username")) {
1591                         priv->load_state = E_CAL_LOAD_NOT_LOADED;
1592                         *status = E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED;
1593                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED, error);
1594                 }
1595
1596                 needs_auth = TRUE;
1597         }
1598
1599         *status = E_CALENDAR_STATUS_OK;
1600         if (!async) {
1601                 if (!e_gdbus_cal_call_open_sync (priv->gdbus_cal, only_if_exists, NULL, error)) {
1602                         *status = E_CALENDAR_STATUS_DBUS_EXCEPTION;
1603                 } else if (needs_auth && !call_authenticate_user (ecal, FALSE, error)) {
1604                         *status = error && *error ? (*error)->code : E_CALENDAR_STATUS_AUTHENTICATION_FAILED;
1605                 }
1606                 if (!*error)
1607                         priv->load_state = E_CAL_LOAD_LOADED;
1608         } else {
1609                 e_gdbus_cal_call_open (priv->gdbus_cal, only_if_exists, NULL, (GAsyncReadyCallback) async_open_ready_cb, ecal);
1610         }
1611
1612         if (*error) {
1613                 unwrap_gerror (error);
1614                 priv->load_state = E_CAL_LOAD_NOT_LOADED;
1615         }
1616
1617         return *error == NULL;
1618 }
1619
1620 /**
1621  * e_cal_open
1622  * @ecal: A calendar client.
1623  * @only_if_exists: FALSE if the calendar should be opened even if there
1624  * was no storage for it, i.e. to create a new calendar or load an existing
1625  * one if it already exists.  TRUE if it should only try to load calendars
1626  * that already exist.
1627  * @error: Placeholder for error information.
1628  *
1629  * Makes a calendar client initiate a request to open a calendar.  The calendar
1630  * client will emit the "cal_opened" signal when the response from the server is
1631  * received. Since 3.0 is emitted also "cal_opened_ex" signal, which contains
1632  * a GError pointer from the open operation (NULL when no error occurred).
1633  * New signal deprecates the old "cal_opened" signal.
1634  *
1635  * Returns: TRUE on success, FALSE on failure to issue the open request.
1636  *
1637  * Deprecated: 3.2: Use e_client_open_sync() on an #ECalClient object instead.
1638  **/
1639 gboolean
1640 e_cal_open (ECal *ecal,
1641             gboolean only_if_exists,
1642             GError **error)
1643 {
1644         ECalendarStatus status;
1645         GError *err = NULL;
1646         gboolean result;
1647
1648         result = open_calendar (ecal, only_if_exists, &err, &status, FALSE, FALSE);
1649         g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED], 0, status);
1650         g_signal_emit (G_OBJECT (ecal), e_cal_signals[CAL_OPENED_EX], 0, err);
1651
1652         if (err)
1653                 g_propagate_error (error, err);
1654
1655         return result;
1656 }
1657
1658 struct idle_async_error_reply_data
1659 {
1660         ECal *ecal; /* ref-ed */
1661         GError *error; /* can be NULL */
1662 };
1663
1664 static gboolean
1665 idle_async_error_reply_cb (gpointer user_data)
1666 {
1667         struct idle_async_error_reply_data *data = user_data;
1668
1669         g_return_val_if_fail (data != NULL, FALSE);
1670         g_return_val_if_fail (data->ecal != NULL, FALSE);
1671
1672         async_open_report_result (data->ecal, data->error);
1673
1674         g_object_unref (data->ecal);
1675         if (data->error)
1676                 g_error_free (data->error);
1677         g_free (data);
1678
1679         return FALSE;
1680 }
1681
1682 /* takes ownership of error */
1683 static void
1684 async_report_idle (ECal *ecal,
1685                    GError *error)
1686 {
1687         struct idle_async_error_reply_data *data;
1688
1689         g_return_if_fail (ecal != NULL);
1690
1691         data = g_new0 (struct idle_async_error_reply_data, 1);
1692         data->ecal = g_object_ref (ecal);
1693         data->error = error;
1694
1695         g_idle_add (idle_async_error_reply_cb, data);
1696 }
1697
1698 /**
1699  * e_cal_open_async:
1700  * @ecal: A calendar client.
1701  * @only_if_exists: If TRUE, then only open the calendar if it already
1702  * exists.  If FALSE, then create a new calendar if it doesn't already
1703  * exist.
1704  *
1705  * Open the calendar asynchronously.  The calendar will emit the
1706  * "cal_opened" signal when the operation has completed.
1707  * Since 3.0 is emitted also "cal_opened_ex" signal, which contains
1708  * a GError pointer from the open operation (NULL when no error occurred).
1709  * New signal deprecates the old "cal_opened" signal.
1710  *
1711  * Because this operation runs in another thread, any authentication
1712  * callback set on the calendar will be called from this other thread.
1713  * See #e_cal_set_auth_func() for details.
1714  *
1715  * Deprecated: 3.2: Use e_client_open()/e_client_open_finish()
1716  * on an #ECalClient object instead.
1717  **/
1718 void
1719 e_cal_open_async (ECal *ecal,
1720                   gboolean only_if_exists)
1721 {
1722         ECalPrivate *priv;
1723         GError *error = NULL;
1724         ECalendarStatus status;
1725
1726         g_return_if_fail (ecal != NULL);
1727         g_return_if_fail (E_IS_CAL (ecal));
1728
1729         priv = ecal->priv;
1730
1731         switch (priv->load_state) {
1732         case E_CAL_LOAD_AUTHENTICATING :
1733         case E_CAL_LOAD_LOADING :
1734                 async_report_idle (ecal, g_error_new_literal (E_CALENDAR_ERROR, E_CALENDAR_STATUS_BUSY, e_cal_get_error_message (E_CALENDAR_STATUS_BUSY)));
1735                 return;
1736         case E_CAL_LOAD_LOADED :
1737                 async_report_idle (ecal, NULL /* success */);
1738                 return;
1739         default:
1740                 /* ignore everything else */
1741                 break;
1742         }
1743
1744         open_calendar (ecal, only_if_exists, &error, &status, FALSE, TRUE);
1745
1746         if (error)
1747                 async_report_idle (ecal, error);
1748 }
1749
1750 /**
1751  * e_cal_refresh:
1752  * @ecal: A calendar client.
1753  * @error: Placeholder for error information.
1754  *
1755  * Invokes refresh on a calendar. See @e_cal_get_refresh_supported.
1756  *
1757  * Returns: TRUE if calendar supports refresh and it was invoked, FALSE otherwise.
1758  *
1759  * Since: 2.30
1760  *
1761  * Deprecated: 3.2: Use e_cal_client_refresh_sync() instead.
1762  **/
1763 gboolean
1764 e_cal_refresh (ECal *ecal,
1765                GError **error)
1766 {
1767         ECalPrivate *priv;
1768
1769         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
1770         priv = ecal->priv;
1771         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
1772
1773         if (!e_gdbus_cal_call_refresh_sync (priv->gdbus_cal, NULL, error)) {
1774                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
1775         }
1776
1777         return TRUE;
1778 }
1779
1780 /**
1781  * e_cal_remove:
1782  * @ecal: A calendar client.
1783  * @error: Placeholder for error information.
1784  *
1785  * Removes a calendar.
1786  *
1787  * Returns: TRUE if the calendar was removed, FALSE if there was an error.
1788  *
1789  * Deprecated: 3.2: Use e_client_remove_sync() on an #ECalClient object instead.
1790  */
1791 gboolean
1792 e_cal_remove (ECal *ecal,
1793               GError **error)
1794 {
1795         ECalPrivate *priv;
1796
1797         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
1798         priv = ecal->priv;
1799         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
1800
1801         if (!e_gdbus_cal_call_remove_sync (priv->gdbus_cal, NULL, error)) {
1802                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
1803         }
1804
1805         return TRUE;
1806 }
1807
1808 #if 0
1809 /* Builds an URI list out of a CORBA string sequence */
1810 static GList *
1811 build_uri_list (GNOME_Evolution_Calendar_StringSeq *seq)
1812 {
1813         GList *uris = NULL;
1814         gint i;
1815
1816         for (i = 0; i < seq->_length; i++)
1817                 uris = g_list_prepend (uris, g_strdup (seq->_buffer[i]));
1818
1819         return uris;
1820 }
1821 #endif
1822
1823 /**
1824  * e_cal_uri_list:
1825  * @ecal: A calendar client.
1826  * @mode: Mode of the URIs to get.
1827  *
1828  * Retrieves a list of all calendar clients for the given mode.
1829  *
1830  * Returns: list of uris.
1831  *
1832  * Deprecated: 3.2: This function has been dropped completely.
1833  */
1834 GList *
1835 e_cal_uri_list (ECal *ecal,
1836                 CalMode mode)
1837 {
1838 #if 0
1839         ECalPrivate *priv;
1840         GNOME_Evolution_Calendar_StringSeq *uri_seq;
1841         GList *uris = NULL;
1842         CORBA_Environment ev;
1843         GList *f;
1844
1845         g_return_val_if_fail (ecal != NULL, NULL);
1846         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
1847
1848         priv = ecal->priv;
1849
1850         for (f = priv->factories; f; f = f->next) {
1851                 CORBA_exception_init (&ev);
1852                 uri_seq = GNOME_Evolution_Calendar_CalFactory_uriList (f->data, mode, &ev);
1853
1854                 if (BONOBO_EX (&ev)) {
1855                         g_message ("e_cal_uri_list(): request failed");
1856
1857                         /* free memory and return */
1858                         g_list_foreach (uris, (GFunc) g_free, NULL);
1859                         g_list_free (uris);
1860                         uris = NULL;
1861                         break;
1862                 }
1863                 else {
1864                         uris = g_list_concat (uris, build_uri_list (uri_seq));
1865                         CORBA_free (uri_seq);
1866                 }
1867
1868                 CORBA_exception_free (&ev);
1869         }
1870
1871         return uris;
1872 #endif
1873
1874         return NULL;
1875 }
1876
1877 /**
1878  * e_cal_get_source_type:
1879  * @ecal: A calendar client.
1880  *
1881  * Gets the type of the calendar client.
1882  *
1883  * Returns: an #ECalSourceType value corresponding to the type
1884  * of the calendar client.
1885  *
1886  * Deprecated: 3.2: Use e_cal_client_get_source_type() instead.
1887  */
1888 ECalSourceType
1889 e_cal_get_source_type (ECal *ecal)
1890 {
1891         ECalPrivate *priv;
1892
1893         g_return_val_if_fail (ecal != NULL, E_CAL_SOURCE_TYPE_LAST);
1894         g_return_val_if_fail (E_IS_CAL (ecal), E_CAL_SOURCE_TYPE_LAST);
1895
1896         priv = ecal->priv;
1897
1898         return priv->type;
1899 }
1900
1901 /**
1902  * e_cal_get_load_state:
1903  * @ecal: A calendar client.
1904  *
1905  * Queries the state of loading of a calendar client.
1906  *
1907  * Returns: A #ECalLoadState value indicating whether the client has
1908  * not been loaded with #e_cal_open yet, whether it is being
1909  * loaded, or whether it is already loaded.
1910  *
1911  * Deprecated: 3.2: Use e_client_is_opened() on an #ECalClient instead.
1912  **/
1913 ECalLoadState
1914 e_cal_get_load_state (ECal *ecal)
1915 {
1916         ECalPrivate *priv;
1917
1918         g_return_val_if_fail (ecal != NULL, E_CAL_LOAD_NOT_LOADED);
1919         g_return_val_if_fail (E_IS_CAL (ecal), E_CAL_LOAD_NOT_LOADED);
1920
1921         priv = ecal->priv;
1922         return priv->load_state;
1923 }
1924
1925 /**
1926  * e_cal_get_source:
1927  * @ecal: A calendar client.
1928  *
1929  * Queries the source that is open in a calendar client.
1930  *
1931  * Returns: The source of the calendar that is already loaded or is being
1932  * loaded, or NULL if the ecal has not started a load request yet.
1933  *
1934  * Deprecated: 3.2: Use e_client_get_source() on an #ECalClient object instead.
1935  **/
1936 ESource *
1937 e_cal_get_source (ECal *ecal)
1938 {
1939         ECalPrivate *priv;
1940
1941         g_return_val_if_fail (ecal != NULL, NULL);
1942         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
1943
1944         priv = ecal->priv;
1945         return priv->source;
1946 }
1947
1948 /**
1949  * e_cal_get_uri:
1950  * @ecal: A calendar client.
1951  *
1952  * Queries the URI that is open in a calendar client.
1953  *
1954  * Returns: The URI of the calendar that is already loaded or is being
1955  * loaded, or NULL if the client has not started a load request yet.
1956  *
1957  * Deprecated: 3.2: Use e_client_get_uri() on an #ECalClient object instead.
1958  **/
1959 const gchar *
1960 e_cal_get_uri (ECal *ecal)
1961 {
1962         ECalPrivate *priv;
1963
1964         g_return_val_if_fail (ecal != NULL, NULL);
1965         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
1966
1967         priv = ecal->priv;
1968         return priv->uri;
1969 }
1970
1971 /**
1972  * e_cal_get_local_attachment_store
1973  * @ecal: A calendar client.
1974  *
1975  * Queries the URL where the calendar attachments are
1976  * serialized in the local filesystem. This enable clients
1977  * to operate with the reference to attachments rather than the data itself
1978  * unless it specifically uses the attachments for open/sending
1979  * operations.
1980  *
1981  * Returns: The URL where the attachments are serialized in the
1982  * local filesystem.
1983  *
1984  * Deprecated: 3.2: Use e_cal_client_get_local_attachment_store() instead.
1985  **/
1986 const gchar *
1987 e_cal_get_local_attachment_store (ECal *ecal)
1988 {
1989         ECalPrivate *priv;
1990
1991         g_return_val_if_fail (ecal != NULL, NULL);
1992         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
1993
1994         priv = ecal->priv;
1995         return (const gchar *) priv->local_attachment_store;
1996 }
1997
1998 /**
1999  * e_cal_is_read_only:
2000  * @ecal: A calendar client.
2001  * @read_only: Return value for read only status.
2002  * @error: Placeholder for error information.
2003  *
2004  * Queries whether the calendar client can perform modifications
2005  * on the calendar or not. Whether the backend is read only or not
2006  * is specified, on exit, in the @read_only argument.
2007  *
2008  * Returns: TRUE if the call was successful, FALSE if there was an error.
2009  *
2010  * Deprecated: 3.2: Use e_cal_client_is_readonly() on an #ECalClient object instead.
2011  */
2012 gboolean
2013 e_cal_is_read_only (ECal *ecal,
2014                     gboolean *read_only,
2015                     GError **error)
2016 {
2017         ECalPrivate *priv;
2018
2019         if (!(ecal && E_IS_CAL (ecal)))
2020                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
2021
2022         priv = ecal->priv;
2023         *read_only = priv->read_only;
2024
2025         return TRUE;
2026 }
2027
2028 /**
2029  * e_cal_get_cal_address:
2030  * @ecal: A calendar client.
2031  * @cal_address: Return value for address information.
2032  * @error: Placeholder for error information.
2033  *
2034  * Queries the calendar address associated with a calendar client.
2035  *
2036  * Returns: TRUE if the operation was successful, FALSE if there
2037  * was an error.
2038  *
2039  * Deprecated: 3.2: Use e_client_get_backend_property_sync()
2040  * with #CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS instead.
2041  **/
2042 gboolean
2043 e_cal_get_cal_address (ECal *ecal,
2044                        gchar **cal_address,
2045                        GError **error)
2046 {
2047         ECalPrivate *priv;
2048
2049         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2050         e_return_error_if_fail (cal_address != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2051         priv = ecal->priv;
2052         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2053         *cal_address = NULL;
2054
2055         LOCK_CACHE ();
2056         if (priv->cal_address == NULL) {
2057                 e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2058                 if (priv->load_state != E_CAL_LOAD_LOADED) {
2059                         UNLOCK_CACHE ();
2060                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2061                 }
2062
2063                 if (!e_gdbus_cal_call_get_backend_property_sync (priv->gdbus_cal, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &priv->cal_address, NULL, error)) {
2064                         UNLOCK_CACHE ();
2065                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2066                 }
2067         }
2068
2069         *cal_address = g_strdup (priv->cal_address);
2070         UNLOCK_CACHE ();
2071
2072         return TRUE;
2073 }
2074
2075 /**
2076  * e_cal_get_alarm_email_address:
2077  * @ecal: A calendar client.
2078  * @alarm_address: Return value for alarm address.
2079  * @error: Placeholder for error information.
2080  *
2081  * Queries the address to be used for alarms in a calendar client.
2082  *
2083  * Returns: TRUE if the operation was successful, FALSE if there was
2084  * an error while contacting the backend.
2085  *
2086  * Deprecated: 3.2: Use e_client_get_backend_property_sync()
2087  * with #CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS instead.
2088  */
2089 gboolean
2090 e_cal_get_alarm_email_address (ECal *ecal,
2091                                gchar **alarm_address,
2092                                GError **error)
2093 {
2094         ECalPrivate *priv;
2095
2096         e_return_error_if_fail (alarm_address != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2097         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2098         priv = ecal->priv;
2099         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2100         *alarm_address = NULL;
2101
2102         if (priv->load_state != E_CAL_LOAD_LOADED) {
2103                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2104         }
2105
2106         if (!e_gdbus_cal_call_get_backend_property_sync (priv->gdbus_cal, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS, alarm_address, NULL, error)) {
2107                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2108         }
2109
2110         return TRUE;
2111 }
2112
2113 /**
2114  * e_cal_get_ldap_attribute:
2115  * @ecal: A calendar client.
2116  * @ldap_attribute: Return value for the LDAP attribute.
2117  * @error: Placeholder for error information.
2118  *
2119  * Queries the LDAP attribute for a calendar client.
2120  *
2121  * Returns: TRUE if the call was successful, FALSE if there was an
2122  * error contacting the backend.
2123  *
2124  * Deprecated: 3.2: This function has been dropped completely.
2125  */
2126 gboolean
2127 e_cal_get_ldap_attribute (ECal *ecal,
2128                           gchar **ldap_attribute,
2129                           GError **error)
2130 {
2131         ECalPrivate *priv;
2132
2133         e_return_error_if_fail (ldap_attribute != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2134         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2135         priv = ecal->priv;
2136         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2137         *ldap_attribute = NULL;
2138
2139         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_NOT_SUPPORTED, error);
2140 }
2141
2142 static gboolean
2143 load_capabilities (ECal *ecal,
2144                    GError **error)
2145 {
2146         ECalPrivate *priv;
2147
2148         priv = ecal->priv;
2149         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2150
2151         if (priv->load_state != E_CAL_LOAD_LOADED) {
2152                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2153         }
2154
2155         LOCK_CACHE ();
2156
2157         if (priv->capabilities) {
2158                 UNLOCK_CACHE ();
2159                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
2160         }
2161
2162         if (!e_gdbus_cal_call_get_backend_property_sync (priv->gdbus_cal, CLIENT_BACKEND_PROPERTY_CAPABILITIES, &priv->capabilities, NULL, error)) {
2163                 UNLOCK_CACHE ();
2164                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2165         }
2166
2167         UNLOCK_CACHE ();
2168
2169         return TRUE;
2170 }
2171
2172 static gboolean
2173 check_capability (ECal *ecal,
2174                   const gchar *cap)
2175 {
2176         ECalPrivate *priv;
2177
2178         priv = ecal->priv;
2179
2180         /* FIXME Check result */
2181         load_capabilities (ecal, NULL);
2182         if (priv->capabilities && strstr (priv->capabilities, cap))
2183                 return TRUE;
2184
2185         return FALSE;
2186 }
2187
2188 /**
2189  * e_cal_get_one_alarm_only:
2190  * @ecal: A calendar client.
2191  *
2192  * Checks if a calendar supports only one alarm per component.
2193  *
2194  * Returns: TRUE if the calendar allows only one alarm, FALSE otherwise.
2195  *
2196  * Deprecated: 3.2: Use e_cal_client_check_one_alarm_only() instead.
2197  */
2198 gboolean
2199 e_cal_get_one_alarm_only (ECal *ecal)
2200 {
2201         g_return_val_if_fail (ecal != NULL, FALSE);
2202         g_return_val_if_fail (ecal && E_IS_CAL (ecal), FALSE);
2203
2204         return check_capability (ecal, CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY);
2205 }
2206
2207 /**
2208  * e_cal_get_organizer_must_attend:
2209  * @ecal: A calendar client.
2210  *
2211  * Checks if a calendar forces organizers of meetings to be also attendees.
2212  *
2213  * Returns: TRUE if the calendar forces organizers to attend meetings,
2214  * FALSE otherwise.
2215  *
2216  * Deprecated: 3.2: Use e_cal_client_check_organizer_must_attend() instead.
2217  */
2218 gboolean
2219 e_cal_get_organizer_must_attend (ECal *ecal)
2220 {
2221         g_return_val_if_fail (ecal != NULL, FALSE);
2222         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2223
2224         return check_capability (ecal, CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ATTEND);
2225 }
2226
2227 /**
2228  * e_cal_get_recurrences_no_master:
2229  * @ecal: A calendar client.
2230  *
2231  * Checks if the calendar has a master object for recurrences.
2232  *
2233  * Returns: TRUE if the calendar has a master object for recurrences,
2234  * FALSE otherwise.
2235  *
2236  * Deprecated: 3.2: Use e_cal_client_check_recurrences_no_master() instead.
2237  */
2238 gboolean
2239 e_cal_get_recurrences_no_master (ECal *ecal)
2240 {
2241         g_return_val_if_fail (ecal != NULL, FALSE);
2242         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2243
2244         return check_capability (ecal, CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER);
2245 }
2246
2247 /**
2248  * e_cal_get_static_capability:
2249  * @ecal: A calendar client.
2250  * @cap: Name of the static capability to check.
2251  *
2252  * Queries the calendar for static capabilities.
2253  *
2254  * Returns: TRUE if the capability is supported, FALSE otherwise.
2255  *
2256  * Deprecated: 3.2: Use e_client_check_capability() on an #ECalClient object instead.
2257  */
2258 gboolean
2259 e_cal_get_static_capability (ECal *ecal,
2260                              const gchar *cap)
2261 {
2262         g_return_val_if_fail (ecal != NULL, FALSE);
2263         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2264
2265         return check_capability (ecal, cap);
2266 }
2267
2268 /**
2269  * e_cal_get_save_schedules:
2270  * @ecal: A calendar client.
2271  *
2272  * Checks whether the calendar saves schedules.
2273  *
2274  * Returns: TRUE if it saves schedules, FALSE otherwise.
2275  *
2276  * Deprecated: 3.2: Use e_cal_client_check_save_schedules() instead.
2277  */
2278 gboolean
2279 e_cal_get_save_schedules (ECal *ecal)
2280 {
2281         g_return_val_if_fail (ecal != NULL, FALSE);
2282         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2283
2284         return check_capability (ecal, CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
2285 }
2286
2287 /**
2288  * e_cal_get_organizer_must_accept:
2289  * @ecal: A calendar client.
2290  *
2291  * Checks whether a calendar requires organizer to accept their attendance to
2292  * meetings.
2293  *
2294  * Returns: TRUE if the calendar requires organizers to accept, FALSE
2295  * otherwise.
2296  *
2297  * Deprecated: 3.2: Use e_cal_client_check_organizer_must_accept() instead.
2298  */
2299 gboolean
2300 e_cal_get_organizer_must_accept (ECal *ecal)
2301 {
2302         g_return_val_if_fail (ecal != NULL, FALSE);
2303         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2304
2305         return check_capability (ecal, CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ACCEPT);
2306 }
2307
2308 /**
2309  * e_cal_get_refresh_supported:
2310  * @ecal: A calendar client.
2311  *
2312  * Checks whether a calendar supports explicit refreshing (see @e_cal_refresh).
2313  *
2314  * Returns: TRUE if the calendar supports refreshing, FALSE otherwise.
2315  *
2316  * Since: 2.30
2317  *
2318  * Deprecated: 3.2: Use e_client_check_refresh_supported() instead.
2319  */
2320 gboolean
2321 e_cal_get_refresh_supported (ECal *ecal)
2322 {
2323         g_return_val_if_fail (ecal != NULL, FALSE);
2324         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2325
2326         return check_capability (ecal, CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED);
2327 }
2328
2329 /**
2330  * e_cal_set_mode:
2331  * @ecal: A calendar client.
2332  * @mode: Mode to switch to.
2333  *
2334  * Switches online/offline mode on the calendar.
2335  *
2336  * Returns: TRUE if the switch was successful, FALSE if there was an error.
2337  *
2338  * Deprecated: 3.2: This function has been dropped completely.
2339  */
2340 gboolean
2341 e_cal_set_mode (ECal *ecal,
2342                 CalMode mode)
2343 {
2344         ECalPrivate *priv;
2345
2346         g_return_val_if_fail (ecal != NULL, FALSE);
2347         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
2348         g_return_val_if_fail (mode & CAL_MODE_ANY, FALSE);
2349
2350         priv = ecal->priv;
2351         g_return_val_if_fail (priv->gdbus_cal, FALSE);
2352         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_LOADED, FALSE);
2353
2354         g_debug ("%s: This function is not supported since 3.2", G_STRFUNC);
2355
2356         return FALSE;
2357 }
2358
2359 /* This is used in the callback which fetches all the timezones needed for an
2360  * object. */
2361 typedef struct _ECalGetTimezonesData ECalGetTimezonesData;
2362 struct _ECalGetTimezonesData {
2363         ECal *ecal;
2364
2365         /* This starts out at E_CALENDAR_STATUS_OK. If an error occurs this
2366          * contains the last error. */
2367         ECalendarStatus status;
2368 };
2369
2370 /**
2371  * e_cal_get_default_object:
2372  * @ecal: A calendar client.
2373  * @icalcomp: Return value for the default object.
2374  * @error: Placeholder for error information.
2375  *
2376  * Retrives an #icalcomponent from the backend that contains the default
2377  * values for properties needed.
2378  *
2379  * Returns: TRUE if the call was successful, FALSE otherwise.
2380  *
2381  * Deprecated: 3.2: Use e_cal_client_get_default_object_sync() instead.
2382  */
2383 gboolean
2384 e_cal_get_default_object (ECal *ecal,
2385                           icalcomponent **icalcomp,
2386                           GError **error)
2387 {
2388         ECalPrivate *priv;
2389         ECalendarStatus status;
2390         gchar *object = NULL;
2391
2392         e_return_error_if_fail (icalcomp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2393         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2394         priv = ecal->priv;
2395         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2396         *icalcomp = NULL;
2397
2398         if (priv->load_state != E_CAL_LOAD_LOADED) {
2399                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2400         }
2401
2402         if (!e_gdbus_cal_call_get_backend_property_sync (priv->gdbus_cal, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT, &object, NULL, error)) {
2403                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2404         }
2405
2406         if (object) {
2407                 *icalcomp = icalparser_parse_string (object);
2408                 g_free (object);
2409
2410                 if (!(*icalcomp))
2411                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
2412                 else
2413                         status = E_CALENDAR_STATUS_OK;
2414
2415                 E_CALENDAR_CHECK_STATUS (status, error);
2416         } else
2417                 status = E_CALENDAR_STATUS_OTHER_ERROR;
2418
2419         E_CALENDAR_CHECK_STATUS (status, error);
2420 }
2421
2422 /**
2423  * e_cal_get_attachments_for_comp:
2424  * @ecal: A calendar client.
2425  * @uid: Unique identifier for a calendar component.
2426  * @rid: Recurrence identifier.
2427  * @list: Return the list of attachment uris.
2428  * @error: Placeholder for error information.
2429  *
2430  * Queries a calendar for a calendar component object based on its unique
2431  * identifier and gets the attachments for the component.
2432  *
2433  * Returns: TRUE if the call was successful, FALSE otherwise.
2434  *
2435  * Deprecated: 3.2: Use e_cal_client_get_attachment_uris_sync() instead.
2436  **/
2437 gboolean
2438 e_cal_get_attachments_for_comp (ECal *ecal,
2439                                 const gchar *uid,
2440                                 const gchar *rid,
2441                                 GSList **list,
2442                                 GError **error)
2443 {
2444         ECalPrivate *priv;
2445         ECalendarStatus status;
2446         gchar **list_array;
2447         gchar **strv;
2448
2449         e_return_error_if_fail (uid != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2450         e_return_error_if_fail (list != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2451         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2452         priv = ecal->priv;
2453         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2454         *list = NULL;
2455
2456         if (priv->load_state != E_CAL_LOAD_LOADED) {
2457                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2458         }
2459
2460         strv = e_gdbus_cal_encode_get_attachment_uris (uid, rid);
2461         if (!e_gdbus_cal_call_get_attachment_uris_sync (priv->gdbus_cal, (const gchar * const *) strv, &list_array, NULL, error)) {
2462                 g_strfreev (strv);
2463
2464                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2465         }
2466
2467         g_strfreev (strv);
2468
2469         if (list_array) {
2470                 gchar **string;
2471                 for (string = list_array; *string; string++) {
2472                         *list = g_slist_append (*list, g_strdup (*string));
2473                 }
2474                 g_strfreev (list_array);
2475                 status = E_CALENDAR_STATUS_OK;
2476         } else
2477                 status = E_CALENDAR_STATUS_OTHER_ERROR;
2478
2479         E_CALENDAR_CHECK_STATUS (status, error);
2480 }
2481
2482 /**
2483  * e_cal_get_object:
2484  * @ecal: A calendar client.
2485  * @uid: Unique identifier for a calendar component.
2486  * @rid: Recurrence identifier.
2487  * @icalcomp: Return value for the calendar component object.
2488  * @error: Placeholder for error information.
2489  *
2490  * Queries a calendar for a calendar component object based on its unique
2491  * identifier.
2492  *
2493  * Returns: TRUE if the call was successful, FALSE otherwise.
2494  *
2495  * Deprecated: 3.2: Use e_cal_client_get_object_sync() instead.
2496  **/
2497 gboolean
2498 e_cal_get_object (ECal *ecal,
2499                   const gchar *uid,
2500                   const gchar *rid,
2501                   icalcomponent **icalcomp,
2502                   GError **error)
2503 {
2504         ECalPrivate *priv;
2505         ECalendarStatus status;
2506         gchar *object = NULL, **strv;
2507         icalcomponent *tmp_icalcomp;
2508         icalcomponent_kind kind;
2509
2510         e_return_error_if_fail (uid != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2511         e_return_error_if_fail (icalcomp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2512         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2513         priv = ecal->priv;
2514         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2515         *icalcomp = NULL;
2516
2517         if (priv->load_state != E_CAL_LOAD_LOADED) {
2518                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2519         }
2520
2521         strv = e_gdbus_cal_encode_get_object (uid, rid);
2522         if (!e_gdbus_cal_call_get_object_sync (priv->gdbus_cal, (const gchar * const *) strv, &object, NULL, error)) {
2523                 g_strfreev (strv);
2524
2525                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2526         }
2527
2528         g_strfreev (strv);
2529
2530         status = E_CALENDAR_STATUS_OK;
2531         tmp_icalcomp = icalparser_parse_string (object);
2532         if (!tmp_icalcomp) {
2533                 status = E_CALENDAR_STATUS_INVALID_OBJECT;
2534                 *icalcomp = NULL;
2535         } else {
2536                 kind = icalcomponent_isa (tmp_icalcomp);
2537                 if ((kind == ICAL_VEVENT_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_EVENT) ||
2538                     (kind == ICAL_VTODO_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_TODO) ||
2539                     (kind == ICAL_VJOURNAL_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_JOURNAL)) {
2540                         *icalcomp = icalcomponent_new_clone (tmp_icalcomp);
2541                 } else if (kind == ICAL_VCALENDAR_COMPONENT) {
2542                         icalcomponent *subcomp = NULL;
2543
2544                         switch (priv->type) {
2545                         case E_CAL_SOURCE_TYPE_EVENT :
2546                                 subcomp = icalcomponent_get_first_component (tmp_icalcomp, ICAL_VEVENT_COMPONENT);
2547                                 break;
2548                         case E_CAL_SOURCE_TYPE_TODO :
2549                                 subcomp = icalcomponent_get_first_component (tmp_icalcomp, ICAL_VTODO_COMPONENT);
2550                                 break;
2551                         case E_CAL_SOURCE_TYPE_JOURNAL :
2552                                 subcomp = icalcomponent_get_first_component (tmp_icalcomp, ICAL_VJOURNAL_COMPONENT);
2553                                 break;
2554                         default:
2555                                 /* ignore everything else */
2556                                 break;
2557                         }
2558
2559                         /* we are only interested in the first component */
2560                         if (subcomp)
2561                                 *icalcomp = icalcomponent_new_clone (subcomp);
2562                 }
2563
2564                 icalcomponent_free (tmp_icalcomp);
2565         }
2566
2567         g_free (object);
2568
2569         E_CALENDAR_CHECK_STATUS (status, error);
2570 }
2571
2572 /**
2573  * e_cal_get_objects_for_uid:
2574  * @ecal: A calendar client.
2575  * @uid: Unique identifier for a calendar component.
2576  * @objects: Return value for the list of objects obtained from the backend.
2577  * @error: Placeholder for error information.
2578  *
2579  * Queries a calendar for all calendar components with the given unique
2580  * ID. This will return any recurring event and all its detached recurrences.
2581  * For non-recurring events, it will just return the object with that ID.
2582  *
2583  * Returns: TRUE if the call was successful, FALSE otherwise.
2584  *
2585  * Deprecated: 3.2: Use e_cal_client_get_objects_for_uid_sync() instead.
2586  **/
2587 gboolean
2588 e_cal_get_objects_for_uid (ECal *ecal,
2589                            const gchar *uid,
2590                            GList **objects,
2591                            GError **error)
2592 {
2593         ECalPrivate *priv;
2594         ECalendarStatus status;
2595         gchar *object = NULL, **strv;
2596
2597         e_return_error_if_fail (uid != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2598         e_return_error_if_fail (objects != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2599         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2600         priv = ecal->priv;
2601         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2602         *objects = NULL;
2603
2604         if (priv->load_state != E_CAL_LOAD_LOADED) {
2605                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2606         }
2607
2608         strv = e_gdbus_cal_encode_get_object (uid, "");
2609         if (!e_gdbus_cal_call_get_object_sync (priv->gdbus_cal, (const gchar * const *) strv, &object, NULL, error)) {
2610                 g_strfreev (strv);
2611
2612                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2613         }
2614
2615         g_strfreev (strv);
2616
2617         status = E_CALENDAR_STATUS_OK;
2618         {
2619                 icalcomponent *icalcomp;
2620                 icalcomponent_kind kind;
2621
2622                 icalcomp = icalparser_parse_string (object);
2623                 if (!icalcomp) {
2624                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
2625                         *objects = NULL;
2626                 } else {
2627                         ECalComponent *comp;
2628
2629                         kind = icalcomponent_isa (icalcomp);
2630                         if ((kind == ICAL_VEVENT_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_EVENT) ||
2631                             (kind == ICAL_VTODO_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_TODO) ||
2632                             (kind == ICAL_VJOURNAL_COMPONENT && priv->type == E_CAL_SOURCE_TYPE_JOURNAL)) {
2633                                 comp = e_cal_component_new ();
2634                                 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
2635                                 *objects = g_list_append (NULL, comp);
2636                         } else if (kind == ICAL_VCALENDAR_COMPONENT) {
2637                                 icalcomponent *subcomp;
2638                                 icalcomponent_kind kind_to_find;
2639
2640                                 switch (priv->type) {
2641                                 case E_CAL_SOURCE_TYPE_TODO :
2642                                         kind_to_find = ICAL_VTODO_COMPONENT;
2643                                         break;
2644                                 case E_CAL_SOURCE_TYPE_JOURNAL :
2645                                         kind_to_find = ICAL_VJOURNAL_COMPONENT;
2646                                         break;
2647                                 case E_CAL_SOURCE_TYPE_EVENT :
2648                                 default:
2649                                         kind_to_find = ICAL_VEVENT_COMPONENT;
2650                                         break;
2651                                 }
2652
2653                                 *objects = NULL;
2654                                 subcomp = icalcomponent_get_first_component (icalcomp, kind_to_find);
2655                                 while (subcomp) {
2656                                         comp = e_cal_component_new ();
2657                                         e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));
2658                                         *objects = g_list_append (*objects, comp);
2659                                         subcomp = icalcomponent_get_next_component (icalcomp, kind_to_find);
2660                                 }
2661                         }
2662
2663                         icalcomponent_free (icalcomp);
2664                 }
2665         }
2666         g_free (object);
2667
2668         E_CALENDAR_CHECK_STATUS (status, error);
2669 }
2670
2671 /**
2672  * e_cal_resolve_tzid_cb:
2673  * @tzid: ID of the timezone to resolve.
2674  * @data: Closure data for the callback.
2675  *
2676  * Resolves TZIDs for the recurrence generator.
2677  *
2678  * Returns: The timezone identified by the @tzid argument, or %NULL if
2679  * it could not be found.
2680  *
2681  * Deprecated: 3.2: Use e_cal_client_resolve_tzid_cb() instead.
2682  */
2683 icaltimezone *
2684 e_cal_resolve_tzid_cb (const gchar *tzid,
2685                        gpointer data)
2686 {
2687         ECal *ecal;
2688         icaltimezone *zone = NULL;
2689
2690         g_return_val_if_fail (data != NULL, NULL);
2691         g_return_val_if_fail (E_IS_CAL (data), NULL);
2692
2693         ecal = E_CAL (data);
2694
2695         /* FIXME: Handle errors. */
2696         e_cal_get_timezone (ecal, tzid, &zone, NULL);
2697
2698         return zone;
2699 }
2700
2701 /**
2702  * e_cal_get_changes:
2703  * @ecal: A calendar client.
2704  * @change_id: ID to use for comparing changes.
2705  * @changes: Return value for the list of changes.
2706  * @error: Placeholder for error information.
2707  *
2708  * Returns a list of changes made to the calendar since a specific time. That time
2709  * is identified by the @change_id argument, which is used by the backend to
2710  * compute the changes done.
2711  *
2712  * Returns: TRUE if the call was successful, FALSE otherwise.
2713  *
2714  * Deprecated: 3.2: This function has been dropped completely.
2715  */
2716 gboolean
2717 e_cal_get_changes (ECal *ecal,
2718                    const gchar *change_id,
2719                    GList **changes,
2720                    GError **error)
2721 {
2722         ECalPrivate *priv;
2723
2724         e_return_error_if_fail (changes != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2725         e_return_error_if_fail (change_id != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2726         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2727         priv = ecal->priv;
2728         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2729         *changes = NULL;
2730
2731         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_NOT_SUPPORTED, error);
2732 }
2733
2734 /**
2735  * e_cal_free_change_list:
2736  * @list: List of changes to be freed.
2737  *
2738  * Free a list of changes as returned by #e_cal_get_changes.
2739  *
2740  * Deprecated: 3.2: Use () instead.
2741  */
2742 void
2743 e_cal_free_change_list (GList *list)
2744 {
2745         ECalChange *c;
2746         GList *l;
2747
2748         for (l = list; l; l = l->next) {
2749                 c = l->data;
2750
2751                 if (c != NULL && c->comp != NULL) {
2752                         g_object_unref (G_OBJECT (c->comp));
2753                         g_free (c);
2754                 } else
2755                         g_warn_if_reached ();
2756         }
2757
2758         g_list_free (list);
2759 }
2760
2761 /**
2762  * e_cal_get_object_list:
2763  * @ecal: A calendar client.
2764  * @query: Query string.
2765  * @objects: (out) (element-type long): Return value for list of objects.
2766  * @error: Placeholder for error information.
2767  *
2768  * Gets a list of objects from the calendar that match the query specified
2769  * by the @query argument. The objects will be returned in the @objects
2770  * argument, which is a list of #icalcomponent. When done, this list
2771  * should be freed by using the #e_cal_free_object_list function.
2772  *
2773  * Returns: TRUE if the operation was successful, FALSE otherwise.
2774  *
2775  * Deprecated: 3.2: Use e_cal_client_get_object_list_sync() instead.
2776  **/
2777 gboolean
2778 e_cal_get_object_list (ECal *ecal,
2779                        const gchar *query,
2780                        GList **objects,
2781                        GError **error)
2782 {
2783         ECalPrivate *priv;
2784         gchar **object_array = NULL, *gdbus_query = NULL;
2785
2786         e_return_error_if_fail (objects != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2787         e_return_error_if_fail (query, E_CALENDAR_STATUS_INVALID_ARG);
2788         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2789         priv = ecal->priv;
2790         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2791         *objects = NULL;
2792
2793         if (priv->load_state != E_CAL_LOAD_LOADED) {
2794                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2795         }
2796
2797         if (!e_gdbus_cal_call_get_object_list_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (query, &gdbus_query), &object_array, NULL, error)) {
2798                 g_free (gdbus_query);
2799
2800                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2801         }
2802
2803         g_free (gdbus_query);
2804
2805         if (object_array) {
2806                 icalcomponent *comp;
2807                 gchar **object;
2808                 for (object = object_array; *object; object++) {
2809                         comp = icalcomponent_new_from_string (*object);
2810                         if (!comp) continue;
2811                         *objects = g_list_prepend (*objects, comp);
2812                 }
2813
2814                 g_strfreev (object_array);
2815
2816                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
2817         }
2818         else
2819                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OTHER_ERROR, error);
2820 }
2821
2822 /**
2823  * e_cal_get_object_list_as_comp:
2824  * @ecal: A calendar client.
2825  * @query: Query string.
2826  * @objects: Return value for list of objects.
2827  * @error: Placeholder for error information.
2828  *
2829  * Gets a list of objects from the calendar that match the query specified
2830  * by the @query argument. The objects will be returned in the @objects
2831  * argument, which is a list of #ECalComponent.
2832  *
2833  * Returns: TRUE if the operation was successful, FALSE otherwise.
2834  *
2835  * Deprecated: 3.2: Use e_cal_client_get_object_list_as_comps_sync() instead.
2836  */
2837 gboolean
2838 e_cal_get_object_list_as_comp (ECal *ecal,
2839                                const gchar *query,
2840                                GList **objects,
2841                                GError **error)
2842 {
2843         GList *ical_objects = NULL;
2844         GList *l;
2845
2846         e_return_error_if_fail (objects != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2847         *objects = NULL;
2848
2849         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2850         e_return_error_if_fail (query, E_CALENDAR_STATUS_INVALID_ARG);
2851
2852         if (!e_cal_get_object_list (ecal, query, &ical_objects, error))
2853                 return FALSE;
2854
2855         for (l = ical_objects; l; l = l->next) {
2856                 ECalComponent *comp;
2857
2858                 comp = e_cal_component_new ();
2859                 e_cal_component_set_icalcomponent (comp, l->data);
2860                 *objects = g_list_prepend (*objects, comp);
2861         }
2862
2863         g_list_free (ical_objects);
2864
2865         return TRUE;
2866 }
2867
2868 /**
2869  * e_cal_free_object_list:
2870  * @objects: List of objects to be freed.
2871  *
2872  * Frees a list of objects as returned by #e_cal_get_object_list.
2873  *
2874  * Deprecated: 3.2: Use e_cal_client_free_icalcomp_slist() instead.
2875  */
2876 void
2877 e_cal_free_object_list (GList *objects)
2878 {
2879         GList *l;
2880
2881         for (l = objects; l; l = l->next)
2882                 icalcomponent_free (l->data);
2883
2884         g_list_free (objects);
2885 }
2886
2887 /**
2888  * e_cal_get_free_busy
2889  * @ecal: A calendar client.
2890  * @users: List of users to retrieve free/busy information for.
2891  * @start: Start time for query.
2892  * @end: End time for query.
2893  * @freebusy: Return value for VFREEBUSY objects.
2894  * @error: Placeholder for error information.
2895  *
2896  * Gets free/busy information from the calendar server.
2897  *
2898  * Returns: TRUE if the operation was successful, FALSE otherwise.
2899  *
2900  * Deprecated: 3.2: Use e_cal_client_get_free_busy_sync() instead.
2901  */
2902 gboolean
2903 e_cal_get_free_busy (ECal *ecal,
2904                      GList *users,
2905                      time_t start,
2906                      time_t end,
2907                      GList **freebusy,
2908                      GError **error)
2909 {
2910         ECalPrivate *priv;
2911         gchar **strv;
2912         GSList *susers;
2913         GList *l;
2914
2915         e_return_error_if_fail (users != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2916         e_return_error_if_fail (freebusy != NULL, E_CALENDAR_STATUS_INVALID_ARG);
2917         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
2918         priv = ecal->priv;
2919         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
2920         *freebusy = NULL;
2921
2922         if (priv->load_state != E_CAL_LOAD_LOADED) {
2923                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
2924         }
2925
2926         susers = NULL;
2927         for (l = users; l; l = l->next) {
2928                 susers = g_slist_prepend (susers, l->data);
2929         }
2930         susers = g_slist_reverse (susers);
2931         strv = e_gdbus_cal_encode_get_free_busy (start, end, susers);
2932         g_slist_free (susers);
2933
2934         g_mutex_lock (priv->free_busy_data_lock);
2935         priv->free_busy_data = freebusy;
2936         g_mutex_unlock (priv->free_busy_data_lock);
2937
2938         if (!e_gdbus_cal_call_get_free_busy_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
2939                 g_strfreev (strv);
2940                 g_mutex_lock (priv->free_busy_data_lock);
2941                 priv->free_busy_data = NULL;
2942                 g_mutex_unlock (priv->free_busy_data_lock);
2943
2944                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
2945         }
2946         g_strfreev (strv);
2947
2948         g_mutex_lock (priv->free_busy_data_lock);
2949         priv->free_busy_data = NULL;
2950         g_mutex_unlock (priv->free_busy_data_lock);
2951
2952         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
2953 }
2954
2955 struct comp_instance {
2956         ECalComponent *comp;
2957         time_t start;
2958         time_t end;
2959 };
2960
2961 struct instances_info {
2962         GList **instances;
2963         icaltimezone *start_zone;
2964 };
2965
2966 /* Called from cal_recur_generate_instances(); adds an instance to the list */
2967 static gboolean
2968 add_instance (ECalComponent *comp,
2969               time_t start,
2970               time_t end,
2971               gpointer data)
2972 {
2973         GList **list;
2974         struct comp_instance *ci;
2975         struct icaltimetype itt;
2976         icalcomponent *icalcomp;
2977         struct instances_info *instances_hold;
2978
2979         instances_hold = data;
2980         list = instances_hold->instances;
2981
2982         ci = g_new (struct comp_instance, 1);
2983
2984         icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
2985
2986         /* add the instance to the list */
2987         ci->comp = e_cal_component_new ();
2988         e_cal_component_set_icalcomponent (ci->comp, icalcomp);
2989
2990         /* set the RECUR-ID for the instance */
2991         if (e_cal_util_component_has_recurrences (icalcomp)) {
2992                 if (!(icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY))) {
2993                         ECalComponentRange *range;
2994                         ECalComponentDateTime datetime;
2995
2996                         e_cal_component_get_dtstart (comp, &datetime);
2997
2998                         if (instances_hold->start_zone)
2999                                 itt = icaltime_from_timet_with_zone (start, datetime.value->is_date, instances_hold->start_zone);
3000                         else {
3001                                 itt = icaltime_from_timet (start, datetime.value->is_date);
3002
3003                                 if (datetime.tzid) {
3004                                         g_free ((gchar *) datetime.tzid);
3005                                         datetime.tzid = NULL;
3006                                 }
3007                         }
3008
3009                         g_free (datetime.value);
3010                         datetime.value = &itt;
3011
3012                         range = g_new0 (ECalComponentRange, 1);
3013                         range->type = E_CAL_COMPONENT_RANGE_SINGLE;
3014                         range->datetime = datetime;
3015
3016                         e_cal_component_set_recurid (ci->comp, range);
3017
3018                         if (datetime.tzid)
3019                                 g_free ((gchar *) datetime.tzid);
3020                         g_free (range);
3021                 }
3022         }
3023
3024         ci->start = start;
3025         ci->end = end;
3026
3027         *list = g_list_prepend (*list, ci);
3028
3029         return TRUE;
3030 }
3031
3032 /* Used from g_list_sort(); compares two struct comp_instance structures */
3033 static gint
3034 compare_comp_instance (gconstpointer a,
3035                        gconstpointer b)
3036 {
3037         const struct comp_instance *cia, *cib;
3038         time_t diff;
3039
3040         cia = a;
3041         cib = b;
3042
3043         diff = cia->start - cib->start;
3044         return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
3045 }
3046
3047 static GList *
3048 process_detached_instances (GList *instances,
3049                             GList *detached_instances)
3050 {
3051         struct comp_instance *ci, *cid;
3052         GList *dl, *unprocessed_instances = NULL;
3053
3054         for (dl = detached_instances; dl != NULL; dl = dl->next) {
3055                 GList *il;
3056                 const gchar *uid;
3057                 gboolean processed;
3058                 ECalComponentRange recur_id, instance_recur_id;
3059
3060                 processed = FALSE;
3061                 recur_id.type = E_CAL_COMPONENT_RANGE_SINGLE;
3062                 instance_recur_id.type = E_CAL_COMPONENT_RANGE_SINGLE;
3063
3064                 cid = dl->data;
3065                 e_cal_component_get_uid (cid->comp, &uid);
3066                 e_cal_component_get_recurid (cid->comp, &recur_id);
3067
3068                 /* search for coincident instances already expanded */
3069                 for (il = instances; il != NULL; il = il->next) {
3070                         const gchar *instance_uid;
3071                         gint cmp;
3072
3073                         ci = il->data;
3074                         e_cal_component_get_uid (ci->comp, &instance_uid);
3075                         e_cal_component_get_recurid (ci->comp, &instance_recur_id);
3076                         if (strcmp (uid, instance_uid) == 0) {
3077                                 gchar *i_rid = NULL, *d_rid = NULL;
3078
3079                                 i_rid = e_cal_component_get_recurid_as_string (ci->comp);
3080                                 d_rid = e_cal_component_get_recurid_as_string (cid->comp);
3081
3082                                 if (i_rid && d_rid && strcmp (i_rid, d_rid) == 0) {
3083                                         g_object_unref (ci->comp);
3084                                         ci->comp = g_object_ref (cid->comp);
3085                                         ci->start = cid->start;
3086                                         ci->end = cid->end;
3087
3088                                         processed = TRUE;
3089                                 } else {
3090                                         if (!instance_recur_id.datetime.value ||
3091                                             !recur_id.datetime.value) {
3092                                                 /*
3093                                                  * Prevent obvious segfault by ignoring missing
3094                                                  * recurrency ids. Real problem might be elsewhere,
3095                                                  * but anything is better than crashing...
3096                                                  */
3097                                                 g_log (G_LOG_DOMAIN,
3098                                                        G_LOG_LEVEL_CRITICAL,
3099                                                        "UID %s: instance RECURRENCE-ID %s + detached instance RECURRENCE-ID %s: cannot compare",
3100                                                        uid,
3101                                                        i_rid,
3102                                                        d_rid);
3103
3104                                                 e_cal_component_free_datetime (&instance_recur_id.datetime);
3105                                                 g_free (i_rid);
3106                                                 g_free (d_rid);
3107                                                 continue;
3108                                         }
3109                                         cmp = icaltime_compare (*instance_recur_id.datetime.value,
3110                                                                 *recur_id.datetime.value);
3111                                         if ((recur_id.type == E_CAL_COMPONENT_RANGE_THISPRIOR && cmp <= 0) ||
3112                                             (recur_id.type == E_CAL_COMPONENT_RANGE_THISFUTURE && cmp >= 0)) {
3113                                                 ECalComponent *comp;
3114
3115                                                 comp = e_cal_component_new ();
3116                                                 e_cal_component_set_icalcomponent (
3117                                                         comp,
3118                                                         icalcomponent_new_clone (e_cal_component_get_icalcomponent (cid->comp)));
3119                                                 e_cal_component_set_recurid (comp, &instance_recur_id);
3120
3121                                                 /* replace the generated instances */
3122                                                 g_object_unref (ci->comp);
3123                                                 ci->comp = comp;
3124                                         }
3125                                 }
3126                                 g_free (i_rid);
3127                                 g_free (d_rid);
3128                         }
3129                         e_cal_component_free_datetime (&instance_recur_id.datetime);
3130                 }
3131
3132                 e_cal_component_free_datetime (&recur_id.datetime);
3133
3134                 if (!processed)
3135                         unprocessed_instances = g_list_prepend (unprocessed_instances, cid);
3136         }
3137
3138         /* add the unprocessed instances (ie, detached instances with no master object */
3139         while (unprocessed_instances != NULL) {
3140                 cid = unprocessed_instances->data;
3141                 ci = g_new0 (struct comp_instance, 1);
3142                 ci->comp = g_object_ref (cid->comp);
3143                 ci->start = cid->start;
3144                 ci->end = cid->end;
3145                 instances = g_list_append (instances, ci);
3146
3147                 unprocessed_instances = g_list_remove (unprocessed_instances, cid);
3148         }
3149
3150         return instances;
3151 }
3152
3153 static void
3154 generate_instances (ECal *ecal,
3155                     time_t start,
3156                     time_t end,
3157                     const gchar *uid,
3158                     ECalRecurInstanceFn cb,
3159                     gpointer cb_data)
3160 {
3161         GList *objects = NULL;
3162         GList *instances, *detached_instances = NULL;
3163         GList *l;
3164         gchar *query;
3165         gchar *iso_start, *iso_end;
3166         ECalPrivate *priv;
3167
3168         priv = ecal->priv;
3169
3170         /* Generate objects */
3171         if (uid && *uid) {
3172                 GError *error = NULL;
3173                 gint tries = 0;
3174
3175 try_again:
3176                 if (!e_cal_get_objects_for_uid (ecal, uid, &objects, &error)) {
3177                         if (error->code == E_CALENDAR_STATUS_BUSY && tries >= 10) {
3178                                 tries++;
3179                                 g_usleep (500);
3180                                 g_clear_error (&error);
3181
3182                                 goto try_again;
3183                         }
3184
3185                         unwrap_gerror (&error);
3186                         g_message ("Failed to get recurrence objects for uid %s \n", error ? error->message : "Unknown error");
3187                         g_clear_error (&error);
3188                         return;
3189                 }
3190         }
3191         else {
3192                 iso_start = isodate_from_time_t (start);
3193                 if (!iso_start)
3194                         return;
3195
3196                 iso_end = isodate_from_time_t (end);
3197                 if (!iso_end) {
3198                         g_free (iso_start);
3199                         return;
3200                 }
3201
3202                 query = g_strdup_printf ("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))",
3203                                          iso_start, iso_end);
3204                 g_free (iso_start);
3205                 g_free (iso_end);
3206                 if (!e_cal_get_object_list_as_comp (ecal, query, &objects, NULL)) {
3207                         g_free (query);
3208                         return;
3209                 }
3210                 g_free (query);
3211         }
3212
3213         instances = NULL;
3214
3215         for (l = objects; l; l = l->next) {
3216                 ECalComponent *comp;
3217                 icaltimezone *default_zone;
3218
3219                 if (priv->default_zone)
3220                         default_zone = priv->default_zone;
3221                 else
3222                         default_zone = icaltimezone_get_utc_timezone ();
3223
3224                 comp = l->data;
3225                 if (e_cal_component_is_instance (comp)) {
3226                         struct comp_instance *ci;
3227                         ECalComponentDateTime dtstart, dtend;
3228                         icaltimezone *start_zone = NULL, *end_zone = NULL;
3229
3230                         /* keep the detached instances apart */
3231                         ci = g_new0 (struct comp_instance, 1);
3232                         ci->comp = comp;
3233
3234                         e_cal_component_get_dtstart (comp, &dtstart);
3235                         e_cal_component_get_dtend (comp, &dtend);
3236
3237                         /* For DATE-TIME values with a TZID, we use
3238                          * e_cal_resolve_tzid_cb to resolve the TZID.
3239                          * For DATE values and DATE-TIME values without a
3240                          * TZID (i.e. floating times) we use the default
3241                          * timezone. */
3242                         if (dtstart.tzid && !dtstart.value->is_date) {
3243                                 start_zone = e_cal_resolve_tzid_cb (dtstart.tzid, ecal);
3244                                 if (!start_zone)
3245                                         start_zone = default_zone;
3246                         } else {
3247                                 start_zone = default_zone;
3248                         }
3249
3250                         if (dtend.tzid && !dtend.value->is_date) {
3251                                 end_zone = e_cal_resolve_tzid_cb (dtend.tzid, ecal);
3252                                 if (!end_zone)
3253                                         end_zone = default_zone;
3254                         } else {
3255                                 end_zone = default_zone;
3256                         }
3257
3258                         ci->start = icaltime_as_timet_with_zone (*dtstart.value, start_zone);
3259
3260                         if (dtend.value)
3261                                 ci->end = icaltime_as_timet_with_zone (*dtend.value, end_zone);
3262                         else if (icaltime_is_date (*dtstart.value))
3263                                 ci->end = time_day_end (ci->start);
3264                         else
3265                                 ci->end = ci->start;
3266
3267                         e_cal_component_free_datetime (&dtstart);
3268                         e_cal_component_free_datetime (&dtend);
3269
3270                         if (ci->start <= end && ci->end >= start) {
3271                                 detached_instances = g_list_prepend (detached_instances, ci);
3272                         } else {
3273                                 /* it doesn't fit to our time range, thus skip it */
3274                                 g_object_unref (G_OBJECT (ci->comp));
3275                                 g_free (ci);
3276                         }
3277                 } else {
3278                         ECalComponentDateTime datetime;
3279                         icaltimezone *start_zone = NULL;
3280                         struct instances_info *instances_hold;
3281
3282                         /* Get the start timezone */
3283                         e_cal_component_get_dtstart (comp, &datetime);
3284                         if (datetime.tzid)
3285                                 e_cal_get_timezone (ecal, datetime.tzid, &start_zone, NULL);
3286                         else
3287                                 start_zone = NULL;
3288                         e_cal_component_free_datetime (&datetime);
3289
3290                         instances_hold = g_new0 (struct instances_info, 1);
3291                         instances_hold->instances = &instances;
3292                         instances_hold->start_zone = start_zone;
3293
3294                         e_cal_recur_generate_instances (comp, start, end, add_instance, instances_hold,
3295                                                         e_cal_resolve_tzid_cb, ecal,
3296                                                         default_zone);
3297
3298                         g_free (instances_hold);
3299                         g_object_unref (comp);
3300                 }
3301         }
3302
3303         g_list_free (objects);
3304
3305         /* Generate instances and spew them out */
3306
3307         instances = g_list_sort (instances, compare_comp_instance);
3308         instances = process_detached_instances (instances, detached_instances);
3309
3310         for (l = instances; l; l = l->next) {
3311                 struct comp_instance *ci;
3312                 gboolean result;
3313
3314                 ci = l->data;
3315
3316                 result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
3317
3318                 if (!result)
3319                         break;
3320         }
3321
3322         /* Clean up */
3323
3324         for (l = instances; l; l = l->next) {
3325                 struct comp_instance *ci;
3326
3327                 ci = l->data;
3328                 g_object_unref (G_OBJECT (ci->comp));
3329                 g_free (ci);
3330         }
3331
3332         g_list_free (instances);
3333
3334         for (l = detached_instances; l; l = l->next) {
3335                 struct comp_instance *ci;
3336
3337                 ci = l->data;
3338                 g_object_unref (G_OBJECT (ci->comp));
3339                 g_free (ci);
3340         }
3341
3342         g_list_free (detached_instances);
3343
3344 }
3345
3346 /**
3347  * e_cal_generate_instances:
3348  * @ecal: A calendar client.
3349  * @start: Start time for query.
3350  * @end: End time for query.
3351  * @cb: Callback for each generated instance.
3352  * @cb_data: Closure data for the callback.
3353  *
3354  * Does a combination of #e_cal_get_object_list () and
3355  * #e_cal_recur_generate_instances().
3356  *
3357  * The callback function should do a g_object_ref() of the calendar component
3358  * it gets passed if it intends to keep it around, since it will be unref'ed
3359  * as soon as the callback returns.
3360  *
3361  * Deprecated: 3.2: Use e_cal_client_generate_instances() instead.
3362  **/
3363 void
3364 e_cal_generate_instances (ECal *ecal,
3365                           time_t start,
3366                           time_t end,
3367                           ECalRecurInstanceFn cb,
3368                           gpointer cb_data)
3369 {
3370         ECalPrivate *priv;
3371
3372         g_return_if_fail (ecal != NULL);
3373         g_return_if_fail (E_IS_CAL (ecal));
3374
3375         priv = ecal->priv;
3376         g_return_if_fail (priv->load_state == E_CAL_LOAD_LOADED);
3377
3378         g_return_if_fail (start >= 0);
3379         g_return_if_fail (end >= 0);
3380         g_return_if_fail (cb != NULL);
3381
3382         generate_instances (ecal, start, end, NULL, cb, cb_data);
3383 }
3384
3385 /**
3386  * e_cal_generate_instances_for_object:
3387  * @ecal: A calendar client.
3388  * @icalcomp: Object to generate instances from.
3389  * @start: Start time for query.
3390  * @end: End time for query.
3391  * @cb: Callback for each generated instance.
3392  * @cb_data: Closure data for the callback.
3393  *
3394  * Does a combination of #e_cal_get_object_list () and
3395  * #e_cal_recur_generate_instances(), like #e_cal_generate_instances(), but
3396  * for a single object.
3397  *
3398  * The callback function should do a g_object_ref() of the calendar component
3399  * it gets passed if it intends to keep it around, since it will be unref'ed
3400  * as soon as the callback returns.
3401  *
3402  * Deprecated: 3.2: Use e_cal_client_generate_instances_for_object() instead.
3403  **/
3404 void
3405 e_cal_generate_instances_for_object (ECal *ecal,
3406                                      icalcomponent *icalcomp,
3407                                      time_t start,
3408                                      time_t end,
3409                                      ECalRecurInstanceFn cb,
3410                                      gpointer cb_data)
3411 {
3412         ECalComponent *comp;
3413         const gchar *uid;
3414         gchar *rid;
3415         gboolean result;
3416         GList *instances = NULL;
3417         ECalComponentDateTime datetime;
3418         icaltimezone *start_zone = NULL;
3419         struct instances_info *instances_hold;
3420         gboolean is_single_instance = FALSE;
3421
3422         g_return_if_fail (E_IS_CAL (ecal));
3423         g_return_if_fail (start >= 0);
3424         g_return_if_fail (end >= 0);
3425         g_return_if_fail (cb != NULL);
3426
3427         comp = e_cal_component_new ();
3428         e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
3429
3430         if (!e_cal_component_has_recurrences (comp))
3431                 is_single_instance = TRUE;
3432
3433         /*If the backend stores it as individual instances and does not
3434          * have a master object - do not expand*/
3435         if (is_single_instance || e_cal_get_static_capability (ecal, CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER)) {
3436
3437                 /*return the same instance */
3438                 result = (* cb)  (comp, icaltime_as_timet_with_zone (icalcomponent_get_dtstart (icalcomp), ecal->priv->default_zone),
3439                                 icaltime_as_timet_with_zone (icalcomponent_get_dtend (icalcomp), ecal->priv->default_zone), cb_data);
3440                 g_object_unref (comp);
3441                 return;
3442         }
3443
3444         e_cal_component_get_uid (comp, &uid);
3445         rid = e_cal_component_get_recurid_as_string (comp);
3446
3447         /* Get the start timezone */
3448         e_cal_component_get_dtstart (comp, &datetime);
3449         if (datetime.tzid)
3450                 e_cal_get_timezone (ecal, datetime.tzid, &start_zone, NULL);
3451         else
3452                 start_zone = NULL;
3453         e_cal_component_free_datetime (&datetime);
3454
3455         instances_hold = g_new0 (struct instances_info, 1);
3456         instances_hold->instances = &instances;
3457         instances_hold->start_zone = start_zone;
3458
3459         /* generate all instances in the given time range */
3460         generate_instances (ecal, start, end, uid, add_instance, instances_hold);
3461
3462         instances = *(instances_hold->instances);
3463         /* now only return back the instances for the given object */
3464         result = TRUE;
3465         while (instances != NULL) {
3466                 struct comp_instance *ci;
3467                 gchar *instance_rid = NULL;
3468
3469                 ci = instances->data;
3470
3471                 if (result) {
3472                         instance_rid = e_cal_component_get_recurid_as_string (ci->comp);
3473
3474                         if (rid && *rid) {
3475                                 if (instance_rid && *instance_rid && strcmp (rid, instance_rid) == 0)
3476                                         result = (* cb) (ci->comp, ci->start, ci->end, cb_data);
3477                         } else
3478                                 result = (* cb)  (ci->comp, ci->start, ci->end, cb_data);
3479                 }
3480
3481                 /* remove instance from list */
3482                 instances = g_list_remove (instances, ci);
3483                 g_object_unref (ci->comp);
3484                 g_free (ci);
3485                 g_free (instance_rid);
3486         }
3487
3488         /* clean up */
3489         g_object_unref (comp);
3490         g_free (instances_hold);
3491         g_free (rid);
3492 }
3493
3494 /* Builds a list of ECalComponentAlarms structures */
3495 static GSList *
3496 build_component_alarms_list (ECal *ecal,
3497                              GList *object_list,
3498                              time_t start,
3499                              time_t end)
3500 {
3501         GSList *comp_alarms;
3502         GList *l;
3503
3504         comp_alarms = NULL;
3505
3506         for (l = object_list; l != NULL; l = l->next) {
3507                 ECalComponent *comp;
3508                 ECalComponentAlarms *alarms;
3509                 ECalComponentAlarmAction omit[] = {-1};
3510
3511                 comp = e_cal_component_new ();
3512                 if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (l->data))) {
3513                         g_object_unref (G_OBJECT (comp));
3514                         continue;
3515                 }
3516
3517                 alarms = e_cal_util_generate_alarms_for_comp (comp, start, end, omit, e_cal_resolve_tzid_cb,
3518                                                               ecal, ecal->priv->default_zone);
3519                 if (alarms)
3520                         comp_alarms = g_slist_prepend (comp_alarms, alarms);
3521         }
3522
3523         return comp_alarms;
3524 }
3525
3526 /**
3527  * e_cal_get_alarms_in_range:
3528  * @ecal: A calendar client.
3529  * @start: Start time for query.
3530  * @end: End time for query.
3531  *
3532  * Queries a calendar for the alarms that trigger in the specified range of
3533  * time.
3534  *
3535  * Returns: A list of #ECalComponentAlarms structures.  This should be freed
3536  * using the #e_cal_free_alarms() function, or by freeing each element
3537  * separately with #e_cal_component_alarms_free() and then freeing the list with
3538  * #g_slist_free().
3539  *
3540  * Deprecated: 3.2: This function has been dropped completely.
3541  **/
3542 GSList *
3543 e_cal_get_alarms_in_range (ECal *ecal,
3544                            time_t start,
3545                            time_t end)
3546 {
3547         ECalPrivate *priv;
3548         GSList *alarms;
3549         gchar *sexp, *iso_start, *iso_end;
3550         GList *object_list = NULL;
3551
3552         g_return_val_if_fail (ecal != NULL, NULL);
3553         g_return_val_if_fail (E_IS_CAL (ecal), NULL);
3554
3555         priv = ecal->priv;
3556         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_LOADED, NULL);
3557
3558         g_return_val_if_fail (start >= 0 && end >= 0, NULL);
3559         g_return_val_if_fail (start <= end, NULL);
3560
3561         iso_start = isodate_from_time_t (start);
3562         if (!iso_start)
3563                 return NULL;
3564
3565         iso_end = isodate_from_time_t (end);
3566         if (!iso_end) {
3567                 g_free (iso_start);
3568                 return NULL;
3569         }
3570
3571         /* build the query string */
3572         sexp = g_strdup_printf ("(has-alarms-in-range? (make-time \"%s\") (make-time \"%s\"))",
3573                                 iso_start, iso_end);
3574         g_free (iso_start);
3575         g_free (iso_end);
3576
3577         /* execute the query on the server */
3578         if (!e_cal_get_object_list (ecal, sexp, &object_list, NULL)) {
3579                 g_free (sexp);
3580                 return NULL;
3581         }
3582
3583         alarms = build_component_alarms_list (ecal, object_list, start, end);
3584
3585         g_list_foreach (object_list, (GFunc) icalcomponent_free, NULL);
3586         g_list_free (object_list);
3587         g_free (sexp);
3588
3589         return alarms;
3590 }
3591
3592 /**
3593  * e_cal_free_alarms:
3594  * @comp_alarms: A list of #ECalComponentAlarms structures.
3595  *
3596  * Frees a list of #ECalComponentAlarms structures as returned by
3597  * e_cal_get_alarms_in_range().
3598  *
3599  * Deprecated: 3.2: This function has been dropped completely.
3600  **/
3601 void
3602 e_cal_free_alarms (GSList *comp_alarms)
3603 {
3604         GSList *l;
3605
3606         for (l = comp_alarms; l; l = l->next) {
3607                 ECalComponentAlarms *alarms;
3608
3609                 alarms = l->data;
3610                 if (alarms != NULL)
3611                         e_cal_component_alarms_free (alarms);
3612                 else
3613                         g_warn_if_reached ();
3614         }
3615
3616         g_slist_free (comp_alarms);
3617 }
3618
3619 /**
3620  * e_cal_get_alarms_for_object:
3621  * @ecal: A calendar client.
3622  * @id: Unique identifier for a calendar component.
3623  * @start: Start time for query.
3624  * @end: End time for query.
3625  * @alarms: Return value for the component's alarm instances.  Will return NULL
3626  * if no instances occur within the specified time range.  This should be freed
3627  * using the e_cal_component_alarms_free() function.
3628  *
3629  * Queries a calendar for the alarms of a particular object that trigger in the
3630  * specified range of time.
3631  *
3632  * Returns: TRUE on success, FALSE if the object was not found.
3633  *
3634  * Deprecated: 3.2: This function has been dropped completely.
3635  **/
3636 gboolean
3637 e_cal_get_alarms_for_object (ECal *ecal,
3638                              const ECalComponentId *id,
3639                              time_t start,
3640                              time_t end,
3641                              ECalComponentAlarms **alarms)
3642 {
3643         ECalPrivate *priv;
3644         icalcomponent *icalcomp;
3645         ECalComponent *comp;
3646         ECalComponentAlarmAction omit[] = {-1};
3647
3648         g_return_val_if_fail (alarms != NULL, FALSE);
3649         *alarms = NULL;
3650
3651         g_return_val_if_fail (ecal != NULL, FALSE);
3652         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
3653
3654         priv = ecal->priv;
3655         g_return_val_if_fail (priv->load_state == E_CAL_LOAD_LOADED, FALSE);
3656
3657         g_return_val_if_fail (id != NULL, FALSE);
3658         g_return_val_if_fail (start >= 0 && end >= 0, FALSE);
3659         g_return_val_if_fail (start <= end, FALSE);
3660
3661         if (!e_cal_get_object (ecal, id->uid, id->rid, &icalcomp, NULL))
3662                 return FALSE;
3663         if (!icalcomp)
3664                 return FALSE;
3665
3666         comp = e_cal_component_new ();
3667         if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
3668                 icalcomponent_free (icalcomp);
3669                 g_object_unref (G_OBJECT (comp));
3670                 return FALSE;
3671         }
3672
3673         *alarms = e_cal_util_generate_alarms_for_comp (comp, start, end, omit, e_cal_resolve_tzid_cb,
3674                                                        ecal, priv->default_zone);
3675
3676         return TRUE;
3677 }
3678
3679 /**
3680  * e_cal_discard_alarm
3681  * @ecal: A calendar ecal.
3682  * @comp: The component to discard the alarm from.
3683  * @auid: Unique identifier of the alarm to be discarded.
3684  * @error: Placeholder for error information.
3685  *
3686  * Tells the calendar backend to get rid of the alarm identified by the
3687  * @auid argument in @comp. Some backends might remove the alarm or
3688  * update internal information about the alarm be discarded, or, like
3689  * the file backend does, ignore the operation.
3690  *
3691  * CALOBJ_MOD_ONLY_THIS is not supported in this call.
3692  *
3693  * Returns: TRUE if the operation was successful, FALSE otherwise.
3694  *
3695  * Deprecated: 3.2: Use e_cal_client_discard_alarm_sync() instead.
3696  */
3697 gboolean
3698 e_cal_discard_alarm (ECal *ecal,
3699                      ECalComponent *comp,
3700                      const gchar *auid,
3701                      GError **error)
3702 {
3703         ECalPrivate *priv;
3704
3705         e_return_error_if_fail (ecal != NULL, E_CALENDAR_STATUS_INVALID_ARG);
3706         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
3707         e_return_error_if_fail (comp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
3708         e_return_error_if_fail (E_IS_CAL_COMPONENT (comp), E_CALENDAR_STATUS_INVALID_ARG);
3709         e_return_error_if_fail (auid != NULL, E_CALENDAR_STATUS_INVALID_ARG);
3710         priv = ecal->priv;
3711         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
3712
3713         if (priv->load_state != E_CAL_LOAD_LOADED) {
3714                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
3715         }
3716
3717         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_NOT_SUPPORTED, error);
3718 }
3719
3720 typedef struct _ForeachTZIDCallbackData ForeachTZIDCallbackData;
3721 struct _ForeachTZIDCallbackData {
3722         ECal *ecal;
3723         GHashTable *timezone_hash;
3724         gboolean include_all_timezones;
3725         gboolean success;
3726 };
3727
3728 /* This adds the VTIMEZONE given by the TZID parameter to the GHashTable in
3729  * data. */
3730 static void
3731 foreach_tzid_callback (icalparameter *param,
3732                        gpointer cbdata)
3733 {
3734         ForeachTZIDCallbackData *data = cbdata;
3735         ECalPrivate *priv;
3736         const gchar *tzid;
3737         icaltimezone *zone = NULL;
3738         icalcomponent *vtimezone_comp;
3739         gchar *vtimezone_as_string;
3740
3741         priv = data->ecal->priv;
3742
3743         /* Get the TZID string from the parameter. */
3744         tzid = icalparameter_get_tzid (param);
3745         if (!tzid)
3746                 return;
3747
3748         /* Check if we've already added it to the GHashTable. */
3749         if (g_hash_table_lookup (data->timezone_hash, tzid))
3750                 return;
3751
3752         if (data->include_all_timezones) {
3753                 if (!e_cal_get_timezone (data->ecal, tzid, &zone, NULL)) {
3754                         data->success = FALSE;
3755                         return;
3756                 }
3757         } else {
3758                 /* Check if it is in our cache. If it is, it must already be
3759                  * on the server so return. */
3760                 if (g_hash_table_lookup (priv->timezones, tzid))
3761                         return;
3762
3763                 /* Check if it is a builtin timezone. If it isn't, return. */
3764                 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
3765                 if (!zone)
3766                         return;
3767         }
3768
3769         /* Convert it to a string and add it to the hash. */
3770         vtimezone_comp = icaltimezone_get_component (zone);
3771         if (!vtimezone_comp)
3772                 return;
3773
3774         vtimezone_as_string = icalcomponent_as_ical_string_r (vtimezone_comp);
3775
3776         g_hash_table_insert (data->timezone_hash, (gchar *) tzid,
3777                              vtimezone_as_string);
3778 }
3779
3780 /* This appends the value string to the GString given in data. */
3781 static void
3782 append_timezone_string (gpointer key,
3783                         gpointer value,
3784                         gpointer data)
3785 {
3786         GString *vcal_string = data;
3787
3788         g_string_append (vcal_string, value);
3789         g_free (value);
3790 }
3791
3792 /* This simply frees the hash values. */
3793 static void
3794 free_timezone_string (gpointer key,
3795                       gpointer value,
3796                       gpointer data)
3797 {
3798         g_free (value);
3799 }
3800
3801 /* This converts the VEVENT/VTODO to a string. If include_all_timezones is
3802  * TRUE, it includes all the VTIMEZONE components needed for the VEVENT/VTODO.
3803  * If not, it only includes builtin timezones that may not be on the server.
3804  *
3805  * To do that we check every TZID in the component to see if it is a builtin
3806  * timezone. If it is, we see if it it in our cache. If it is in our cache,
3807  * then we know the server already has it and we don't need to send it.
3808  * If it isn't in our cache, then we need to send it to the server.
3809  * If we need to send any timezones to the server, then we have to create a
3810  * complete VCALENDAR object, otherwise we can just send a single VEVENT/VTODO
3811  * as before. */
3812 static gchar *
3813 e_cal_get_component_as_string_internal (ECal *ecal,
3814                                         icalcomponent *icalcomp,
3815                                         gboolean include_all_timezones)
3816 {
3817         GHashTable *timezone_hash;
3818         GString *vcal_string;
3819         gint initial_vcal_string_len;
3820         ForeachTZIDCallbackData cbdata;
3821         gchar *obj_string;
3822
3823         timezone_hash = g_hash_table_new (g_str_hash, g_str_equal);
3824
3825         /* Add any timezones needed to the hash. We use a hash since we only
3826          * want to add each timezone once at most. */
3827         cbdata.ecal = ecal;
3828         cbdata.timezone_hash = timezone_hash;
3829         cbdata.include_all_timezones = include_all_timezones;
3830         cbdata.success = TRUE;
3831         icalcomponent_foreach_tzid (icalcomp, foreach_tzid_callback, &cbdata);
3832         if (!cbdata.success) {
3833                 g_hash_table_foreach (timezone_hash, free_timezone_string,
3834                                       NULL);
3835                 return NULL;
3836         }
3837
3838         /* Create the start of a VCALENDAR, to add the VTIMEZONES to,
3839          * and remember its length so we know if any VTIMEZONEs get added. */
3840         vcal_string = g_string_new (NULL);
3841         g_string_append (vcal_string,
3842                          "BEGIN:VCALENDAR\n"
3843                          "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3844                          "VERSION:2.0\n"
3845                          "METHOD:PUBLISH\n");
3846         initial_vcal_string_len = vcal_string->len;
3847
3848         /* Now concatenate all the timezone strings. This also frees the
3849          * timezone strings as it goes. */
3850         g_hash_table_foreach (timezone_hash, append_timezone_string,
3851                               vcal_string);
3852
3853         /* Get the string for the VEVENT/VTODO. */
3854         obj_string = icalcomponent_as_ical_string_r (icalcomp);
3855
3856         /* If there were any timezones to send, create a complete VCALENDAR,
3857          * else just send the VEVENT / VTODO string. */
3858         if (!include_all_timezones
3859             && vcal_string->len == initial_vcal_string_len) {
3860                 g_string_free (vcal_string, TRUE);
3861         } else {
3862                 g_string_append (vcal_string, obj_string);
3863                 g_string_append (vcal_string, "END:VCALENDAR\n");
3864                 g_free (obj_string);
3865                 obj_string = vcal_string->str;
3866                 g_string_free (vcal_string, FALSE);
3867         }
3868
3869         g_hash_table_destroy (timezone_hash);
3870
3871         return obj_string;
3872 }
3873
3874 /**
3875  * e_cal_get_component_as_string:
3876  * @ecal: A calendar client.
3877  * @icalcomp: A calendar component object.
3878  *
3879  * Gets a calendar component as an iCalendar string, with a toplevel
3880  * VCALENDAR component and all VTIMEZONEs needed for the component.
3881  *
3882  * Returns: the component as a complete iCalendar string, or NULL on
3883  * failure. The string should be freed after use.
3884  *
3885  * Deprecated: 3.2: Use e_cal_client_get_component_as_string() instead.
3886  **/
3887 gchar *
3888 e_cal_get_component_as_string (ECal *ecal,
3889                                icalcomponent *icalcomp)
3890 {
3891         return e_cal_get_component_as_string_internal (ecal, icalcomp, TRUE);
3892 }
3893
3894 /**
3895  * e_cal_create_object:
3896  * @ecal: A calendar client.
3897  * @icalcomp: The component to create.
3898  * @uid: Return value for the UID assigned to the new component by the calendar backend.
3899  * @error: Placeholder for error information.
3900  *
3901  * Requests the calendar backend to create the object specified by the @icalcomp
3902  * argument. Some backends would assign a specific UID to the newly created object,
3903  * in those cases that UID would be returned in the @uid argument.
3904  *
3905  * Returns: TRUE if the operation was successful, FALSE otherwise.
3906  *
3907  * Deprecated: 3.2: Use e_cal_client_create_object_sync() instead.
3908  */
3909 gboolean
3910 e_cal_create_object (ECal *ecal,
3911                      icalcomponent *icalcomp,
3912                      gchar **uid,
3913                      GError **error)
3914 {
3915         ECalPrivate *priv;
3916         gchar *obj, *gdbus_obj = NULL;
3917         const gchar *strv[2];
3918         gchar **muids = NULL;
3919
3920         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
3921         e_return_error_if_fail (icalcomp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
3922         e_return_error_if_fail (icalcomponent_is_valid (icalcomp), E_CALENDAR_STATUS_INVALID_ARG);
3923         priv = ecal->priv;
3924         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
3925
3926         if (priv->load_state != E_CAL_LOAD_LOADED) {
3927                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
3928         }
3929
3930         obj = icalcomponent_as_ical_string_r (icalcomp);
3931         strv[0] = e_util_ensure_gdbus_string (obj, &gdbus_obj);
3932         strv[1] = NULL;
3933
3934         if (!e_gdbus_cal_call_create_objects_sync (priv->gdbus_cal, strv, &muids, NULL, error)) {
3935                 g_free (obj);
3936                 g_free (gdbus_obj);
3937
3938                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
3939         }
3940
3941         g_free (obj);
3942         g_free (gdbus_obj);
3943
3944         if (!muids) {
3945                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OTHER_ERROR, error);
3946         } else {
3947                 icalcomponent_set_uid (icalcomp, muids[0]);
3948
3949                 if (uid)
3950                         *uid = g_strdup (muids[0]);
3951
3952                 g_strfreev (muids);
3953
3954                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
3955         }
3956 }
3957
3958 /**
3959  * e_cal_modify_object:
3960  * @ecal: A calendar client.
3961  * @icalcomp: Component to modify.
3962  * @mod: Type of modification.
3963  * @error: Placeholder for error information.
3964  *
3965  * Requests the calendar backend to modify an existing object. If the object
3966  * does not exist on the calendar, an error will be returned.
3967  *
3968  * For recurrent appointments, the @mod argument specifies what to modify,
3969  * if all instances (CALOBJ_MOD_ALL), a single instance (CALOBJ_MOD_THIS),
3970  * or a specific set of instances (CALOBJ_MOD_THISNADPRIOR and
3971  * CALOBJ_MOD_THISANDFUTURE).
3972  *
3973  * Returns: TRUE if the operation was successful, FALSE otherwise.
3974  *
3975  * Deprecated: 3.2: Use e_cal_client_modify_object_sync() instead.
3976  */
3977 gboolean
3978 e_cal_modify_object (ECal *ecal,
3979                      icalcomponent *icalcomp,
3980                      CalObjModType mod,
3981                      GError **error)
3982 {
3983         ECalPrivate *priv;
3984         gchar *obj, **strv;
3985         GSList objs = {0,};
3986
3987         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
3988         e_return_error_if_fail (icalcomp, E_CALENDAR_STATUS_INVALID_ARG);
3989         e_return_error_if_fail (icalcomponent_is_valid (icalcomp), E_CALENDAR_STATUS_INVALID_ARG);
3990         switch (mod) {
3991         case CALOBJ_MOD_THIS:
3992         case CALOBJ_MOD_THISANDPRIOR:
3993         case CALOBJ_MOD_THISANDFUTURE:
3994         case CALOBJ_MOD_ALL:
3995                 break;
3996         default:
3997                 e_return_error_if_fail ("valid CalObjModType" && FALSE, E_CALENDAR_STATUS_INVALID_ARG);
3998         }
3999         priv = ecal->priv;
4000         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4001
4002         if (priv->load_state != E_CAL_LOAD_LOADED) {
4003                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4004         }
4005
4006         obj = icalcomponent_as_ical_string_r (icalcomp);
4007         objs.data = obj;
4008         strv = e_gdbus_cal_encode_modify_objects (&objs, mod);
4009         if (!e_gdbus_cal_call_modify_objects_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
4010                 g_free (obj);
4011                 g_strfreev (strv);
4012
4013                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4014         }
4015
4016         g_free (obj);
4017         g_strfreev (strv);
4018
4019         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4020 }
4021
4022 /**
4023  * e_cal_remove_object_with_mod:
4024  * @ecal: A calendar client.
4025  * @uid: UID of the object to remove.
4026  * @rid: Recurrence ID of the specific recurrence to remove.
4027  * @mod: Type of removal.
4028  * @error: Placeholder for error information.
4029  *
4030  * This function allows the removal of instances of a recurrent
4031  * appointment. If what you want is to remove all instances, use
4032  * e_cal_remove_object instead.
4033  *
4034  * By using a combination of the @uid, @rid and @mod arguments, you
4035  * can remove specific instances. @uid is mandatory.  Empty or NULL
4036  * @rid selects the parent appointment (the one with the recurrence
4037  * rule). A non-empty @rid selects the recurrence at the time specified
4038  * in @rid, using the same time zone as the parent appointment's start
4039  * time.
4040  *
4041  * The exact semantic then depends on @mod. CALOBJ_MOD_THIS,
4042  * CALOBJ_MOD_THISANDPRIOR, CALOBJ_MOD_THISANDFUTURE and
4043  * CALOBJ_MOD_ALL ensure that the event does not recur at the selected
4044  * instance(s). This is done by removing any detached recurrence
4045  * matching the selection criteria and modifying the parent
4046  * appointment (adding EXDATE, adjusting recurrence rules, etc.).  It
4047  * is not an error if @uid+@rid do not match an existing instance.
4048  *
4049  * If not all instances are removed, the client will get a
4050  * "obj_modified" signal for the parent appointment, while it will get
4051  * an "obj_removed" signal when all instances are removed.
4052  *
4053  * CALOBJ_MOD_ONLY_THIS changes the semantic of CALOBJ_MOD_THIS: @uid
4054  * and @rid must select an existing instance. That instance is
4055  * removed without modifying the parent appointment. In other words,
4056  * e_cal_remove_object_with_mod(CALOBJ_MOD_ONLY_THIS) is the inverse
4057  * operation for adding a detached recurrence. The client is
4058  * always sent an "obj_removed" signal.
4059  *
4060  * Note that not all backends support CALOBJ_MOD_ONLY_THIS. Check for
4061  * the CAL_STATIC_CAPABILITY_REMOVE_ONLY_THIS capability before using
4062  * it. Previous releases did not check consistently for unknown
4063  * @mod values, using it with them may have had unexpected results.
4064  *
4065  * Returns: TRUE if the operation was successful, FALSE otherwise.
4066  *
4067  * Deprecated: 3.2: Use e_cal_client_remove_object_sync() instead.
4068  */
4069 gboolean
4070 e_cal_remove_object_with_mod (ECal *ecal,
4071                               const gchar *uid,
4072                               const gchar *rid,
4073                               CalObjModType mod,
4074                               GError **error)
4075 {
4076         ECalPrivate *priv;
4077         gchar **strv;
4078         GSList ids = {0,};
4079         ECalComponentId id;
4080
4081         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4082         e_return_error_if_fail (uid, E_CALENDAR_STATUS_INVALID_ARG);
4083         switch (mod) {
4084         case CALOBJ_MOD_THIS:
4085         case CALOBJ_MOD_THISANDPRIOR:
4086         case CALOBJ_MOD_THISANDFUTURE:
4087         case CALOBJ_MOD_ONLY_THIS:
4088         case CALOBJ_MOD_ALL:
4089                 break;
4090         default:
4091                 e_return_error_if_fail ("valid CalObjModType" && FALSE, E_CALENDAR_STATUS_INVALID_ARG);
4092         }
4093         priv = ecal->priv;
4094         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4095
4096         if (priv->load_state != E_CAL_LOAD_LOADED) {
4097                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4098         }
4099
4100         id.uid = (gchar *) uid;
4101         id.rid = (gchar *) rid;
4102         ids.data = &id;
4103         strv = e_gdbus_cal_encode_remove_objects (&ids, mod);
4104         if (!e_gdbus_cal_call_remove_objects_sync (priv->gdbus_cal, (const gchar * const *) strv, NULL, error)) {
4105                 g_strfreev (strv);
4106
4107                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4108         }
4109
4110         g_strfreev (strv);
4111
4112         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4113 }
4114
4115 /**
4116  * e_cal_remove_object:
4117  * @ecal:  A calendar client.
4118  * @uid: Unique identifier of the calendar component to remove.
4119  * @error: Placeholder for error information.
4120  *
4121  * Asks a calendar to remove all components with the given UID.
4122  * If more control of the removal is desired, then use e_cal_remove_object_with_mod().
4123  * If the server is able to remove the component(s), all clients will
4124  * be notified and they will emit the "obj_removed" signal.
4125  *
4126  * Returns: %TRUE if successful, %FALSE otherwise.
4127  *
4128  * Deprecated: 3.2: Use e_cal_client_remove_object_sync() instead, with rid set to NULL and mod set to CALOBJ_MOD_ALL.
4129  **/
4130 gboolean
4131 e_cal_remove_object (ECal *ecal,
4132                      const gchar *uid,
4133                      GError **error)
4134 {
4135         e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4136         e_return_error_if_fail (uid, E_CALENDAR_STATUS_INVALID_ARG);
4137
4138         return e_cal_remove_object_with_mod (ecal, uid, NULL, CALOBJ_MOD_ALL, error);
4139 }
4140
4141 /**
4142  * e_cal_receive_objects:
4143  * @ecal:  A calendar client.
4144  * @icalcomp: An icalcomponent.
4145  * @error: Placeholder for error information.
4146  *
4147  * Makes the backend receive the set of iCalendar objects specified in the
4148  * @icalcomp argument. This is used for iTIP confirmation/cancellation
4149  * messages for scheduled meetings.
4150  *
4151  * Returns: %TRUE if successful, %FALSE otherwise.
4152  *
4153  * Deprecated: 3.2: Use e_cal_client_receive_objects_sync() instead.
4154  */
4155 gboolean
4156 e_cal_receive_objects (ECal *ecal,
4157                        icalcomponent *icalcomp,
4158                        GError **error)
4159 {
4160         ECalPrivate *priv;
4161         gchar *obj, *gdbus_obj = NULL;
4162
4163         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4164         e_return_error_if_fail (icalcomp, E_CALENDAR_STATUS_INVALID_ARG);
4165         e_return_error_if_fail (icalcomponent_is_valid (icalcomp), E_CALENDAR_STATUS_INVALID_ARG);
4166         priv = ecal->priv;
4167         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4168
4169         if (priv->load_state != E_CAL_LOAD_LOADED) {
4170                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4171         }
4172
4173         obj = icalcomponent_as_ical_string_r (icalcomp);
4174         if (!e_gdbus_cal_call_receive_objects_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (obj, &gdbus_obj), NULL, error)) {
4175                 g_free (obj);
4176                 g_free (gdbus_obj);
4177
4178                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4179         }
4180
4181         g_free (obj);
4182         g_free (gdbus_obj);
4183
4184         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4185 }
4186
4187 /**
4188  * e_cal_send_objects:
4189  * @ecal: A calendar client.
4190  * @icalcomp: An icalcomponent.
4191  * @users: List of users to send the objects to.
4192  * @modified_icalcomp: Return value for the icalcomponent after all the operations
4193  * performed.
4194  * @error: Placeholder for error information.
4195  *
4196  * Requests a calendar backend to send meeting information to the specified list
4197  * of users.
4198  *
4199  * Returns: TRUE if the operation was successful, FALSE otherwise.
4200  *
4201  * Deprecated: 3.2: Use e_cal_client_send_objects_sync() instead.
4202  */
4203 gboolean
4204 e_cal_send_objects (ECal *ecal,
4205                     icalcomponent *icalcomp,
4206                     GList **users,
4207                     icalcomponent **modified_icalcomp,
4208                     GError **error)
4209 {
4210         ECalPrivate *priv;
4211         ECalendarStatus status;
4212         gchar **out_array = NULL;
4213         gchar *obj, *gdbus_obj = NULL;
4214
4215         e_return_error_if_fail (users != NULL, E_CALENDAR_STATUS_INVALID_ARG);
4216         e_return_error_if_fail (modified_icalcomp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
4217         e_return_error_if_fail (icalcomp != NULL, E_CALENDAR_STATUS_INVALID_ARG);
4218         e_return_error_if_fail (icalcomponent_is_valid (icalcomp), E_CALENDAR_STATUS_INVALID_ARG);
4219         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4220         priv = ecal->priv;
4221         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4222         *users = NULL;
4223         *modified_icalcomp = NULL;
4224
4225         if (priv->load_state != E_CAL_LOAD_LOADED) {
4226                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4227         }
4228
4229         obj = icalcomponent_as_ical_string_r (icalcomp);
4230         if (!e_gdbus_cal_call_send_objects_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (obj, &gdbus_obj), &out_array, NULL, error)) {
4231                 g_free (obj);
4232                 g_free (gdbus_obj);
4233
4234                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4235         }
4236
4237         g_free (obj);
4238         g_free (gdbus_obj);
4239
4240         status = E_CALENDAR_STATUS_OK;
4241         if (out_array) {
4242                 GSList *susers = NULL, *iter;
4243                 gchar *object = NULL;
4244
4245                 e_return_error_if_fail (e_gdbus_cal_decode_send_objects ((const gchar * const *) out_array, &object, &susers), E_CALENDAR_STATUS_OTHER_ERROR);
4246
4247                 *modified_icalcomp = icalparser_parse_string (object);
4248                 if (!(*modified_icalcomp))
4249                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
4250
4251                 *users = NULL;
4252                 for (iter = susers; iter; iter = iter->next) {
4253                         *users = g_list_append (*users, iter->data);
4254                 }
4255                 /* do not call g_free() on item's data of susers, it's moved to *users */
4256                 g_slist_free (susers);
4257                 g_strfreev (out_array);
4258                 g_free (object);
4259         } else
4260                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OTHER_ERROR, error);
4261
4262         E_CALENDAR_CHECK_STATUS (status, error);
4263 }
4264
4265 /**
4266  * e_cal_get_timezone:
4267  * @ecal: A calendar client.
4268  * @tzid: ID of the timezone to retrieve.
4269  * @zone: Return value for the timezone.
4270  * @error: Placeholder for error information.
4271  *
4272  * Retrieves a timezone object from the calendar backend.
4273  *
4274  * Returns: TRUE if the operation was successful, FALSE otherwise.
4275  *
4276  * Deprecated: 3.2: Use e_cal_client_get_timezone_sync() instead.
4277  */
4278 gboolean
4279 e_cal_get_timezone (ECal *ecal,
4280                     const gchar *tzid,
4281                     icaltimezone **zone,
4282                     GError **error)
4283 {
4284         ECalPrivate *priv;
4285         ECalendarStatus status = E_CALENDAR_STATUS_OK;
4286         icalcomponent *icalcomp = NULL;
4287         gchar *object = NULL, *gdbus_tzid = NULL;
4288         const gchar *systzid = NULL;
4289
4290         e_return_error_if_fail (zone, E_CALENDAR_STATUS_INVALID_ARG);
4291         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4292         priv = ecal->priv;
4293         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4294         *zone = NULL;
4295
4296         if (priv->load_state != E_CAL_LOAD_LOADED) {
4297                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4298         }
4299
4300         /* Check for well known zones and in the cache */
4301         /* If tzid is NULL or "" we return NULL, since it is a 'local time'. */
4302         if (!tzid || !tzid[0]) {
4303                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4304         }
4305
4306         LOCK_CACHE ();
4307         /* If it is UTC, we return the special UTC timezone. */
4308         if (!strcmp (tzid, "UTC")) {
4309                 *zone = icaltimezone_get_utc_timezone ();
4310         } else {
4311                 /* See if we already have it in the cache. */
4312                 *zone = g_hash_table_lookup (priv->timezones, tzid);
4313         }
4314
4315         if (*zone) {
4316                 UNLOCK_CACHE ();
4317                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4318         }
4319
4320         /*
4321          * Try to replace the original time zone with a more complete
4322          * and/or potentially updated system time zone. Note that this
4323          * also applies to TZIDs which match system time zones exactly:
4324          * they are extracted via icaltimezone_get_builtin_timezone_from_tzid()
4325          * below without a roundtrip to the backend.
4326          */
4327         systzid = e_cal_match_tzid (tzid);
4328         if (!systzid) {
4329                 /* call the backend */
4330                 if (!e_gdbus_cal_call_get_timezone_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (tzid, &gdbus_tzid), &object, NULL, error)) {
4331                         g_free (gdbus_tzid);
4332
4333                         UNLOCK_CACHE ();
4334                         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4335                 }
4336
4337                 g_free (gdbus_tzid);
4338
4339                 icalcomp = icalparser_parse_string (object);
4340                 if (!icalcomp)
4341                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
4342                 g_free (object);
4343         } else {
4344                 /*
4345                  * Use built-in time zone *and* rename it:
4346                  * if the caller is asking for a TZID=FOO,
4347                  * then likely because it has an event with
4348                  * such a TZID. Returning a different TZID
4349                  * would lead to broken VCALENDARs in the
4350                  * caller.
4351                  */
4352                 icaltimezone *syszone = icaltimezone_get_builtin_timezone_from_tzid (systzid);
4353                 if (syszone) {
4354                         gboolean found = FALSE;
4355                         icalproperty *prop;
4356
4357                         icalcomp = icalcomponent_new_clone (icaltimezone_get_component (syszone));
4358                         prop = icalcomponent_get_first_property (icalcomp,
4359                                                                 ICAL_ANY_PROPERTY);
4360                         while (!found && prop) {
4361                                 if (icalproperty_isa (prop) == ICAL_TZID_PROPERTY) {
4362                                         icalproperty_set_value_from_string(prop, tzid, "NO");
4363                                         found = TRUE;
4364                                 }
4365                                 prop = icalcomponent_get_next_property (icalcomp,
4366                                                                        ICAL_ANY_PROPERTY);
4367                         }
4368                 } else {
4369                         status = E_CALENDAR_STATUS_INVALID_OBJECT;
4370                 }
4371         }
4372
4373         if (!icalcomp) {
4374                 UNLOCK_CACHE ();
4375                 E_CALENDAR_CHECK_STATUS (status, error);
4376         }
4377
4378         *zone = icaltimezone_new ();
4379         if (!icaltimezone_set_component (*zone, icalcomp)) {
4380                 icaltimezone_free (*zone, 1);
4381                 UNLOCK_CACHE ();
4382                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OBJECT_NOT_FOUND, error);
4383         }
4384
4385         /* Now add it to the cache, to avoid the server call in future. */
4386         g_hash_table_insert (priv->timezones, (gpointer) icaltimezone_get_tzid (*zone), *zone);
4387
4388         UNLOCK_CACHE ();
4389         return TRUE;
4390 }
4391
4392 /**
4393  * e_cal_add_timezone
4394  * @ecal: A calendar client.
4395  * @izone: The timezone to add.
4396  * @error: Placeholder for error information.
4397  *
4398  * Add a VTIMEZONE object to the given calendar.
4399  *
4400  * Returns: TRUE if successful, FALSE otherwise.
4401  *
4402  * Deprecated: 3.2: Use e_cal_client_add_timezone_sync() instead.
4403  */
4404 gboolean
4405 e_cal_add_timezone (ECal *ecal,
4406                     icaltimezone *izone,
4407                     GError **error)
4408 {
4409         ECalPrivate *priv;
4410         gchar *tzobj, *gdbus_tzobj = NULL;
4411         icalcomponent *icalcomp;
4412
4413         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4414         e_return_error_if_fail (izone, E_CALENDAR_STATUS_INVALID_ARG);
4415         priv = ecal->priv;
4416         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4417
4418         if (priv->load_state != E_CAL_LOAD_LOADED) {
4419                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4420         }
4421
4422         /* Make sure we have a valid component - UTC doesn't, nor do
4423          * we really have to add it */
4424         if (izone == icaltimezone_get_utc_timezone ()) {
4425                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4426         }
4427
4428         icalcomp = icaltimezone_get_component (izone);
4429         if (!icalcomp) {
4430                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
4431         }
4432
4433         /* convert icaltimezone into a string */
4434         tzobj = icalcomponent_as_ical_string_r (icalcomp);
4435
4436         /* call the backend */
4437         if (!e_gdbus_cal_call_add_timezone_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (tzobj, &gdbus_tzobj), NULL, error)) {
4438                 g_free (tzobj);
4439                 g_free (gdbus_tzobj);
4440
4441                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4442         }
4443
4444         g_free (tzobj);
4445         g_free (gdbus_tzobj);
4446
4447         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4448 }
4449
4450 /**
4451  * e_cal_get_query:
4452  * @ecal: A calendar client.
4453  * @sexp: S-expression representing the query.
4454  * @query: (out): Return value for the new query.
4455  * @error: Placeholder for error information.
4456  *
4457  * Creates a live query object from a loaded calendar.
4458  *
4459  * Returns: A query object that will emit notification signals as calendar
4460  * components are added and removed from the query in the server.
4461  *
4462  * Deprecated: 3.2: Use e_cal_client_get_view_sync() instead.
4463  **/
4464 gboolean
4465 e_cal_get_query (ECal *ecal,
4466                  const gchar *sexp,
4467                  ECalView **query,
4468                  GError **error)
4469 {
4470         ECalPrivate *priv;
4471         ECalendarStatus status;
4472         gchar *query_path = NULL, *gdbus_sexp = NULL;
4473         EGdbusCalView *gdbus_calview;
4474
4475         e_return_error_if_fail (sexp, E_CALENDAR_STATUS_INVALID_ARG);
4476         e_return_error_if_fail (query, E_CALENDAR_STATUS_INVALID_ARG);
4477         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4478         priv = ecal->priv;
4479         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4480         *query = NULL;
4481
4482         if (priv->load_state != E_CAL_LOAD_LOADED) {
4483                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_URI_NOT_LOADED, error);
4484         }
4485
4486         if (!e_gdbus_cal_call_get_view_sync (priv->gdbus_cal, e_util_ensure_gdbus_string (sexp, &gdbus_sexp), &query_path, NULL, error)) {
4487                 g_free (gdbus_sexp);
4488
4489                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_DBUS_EXCEPTION, error);
4490         }
4491
4492         g_free (gdbus_sexp);
4493
4494         status = E_CALENDAR_STATUS_OK;
4495
4496         gdbus_calview = e_gdbus_cal_view_proxy_new_sync (g_dbus_proxy_get_connection (G_DBUS_PROXY (cal_factory_proxy)),
4497                                                         G_DBUS_PROXY_FLAGS_NONE,
4498                                                         CALENDAR_DBUS_SERVICE_NAME,
4499                                                         query_path,
4500                                                         NULL,
4501                                                         error);
4502
4503         g_free (query_path);
4504
4505         if (!gdbus_calview) {
4506                 *query = NULL;
4507                 status = E_CALENDAR_STATUS_OTHER_ERROR;
4508         } else {
4509                 *query = _e_cal_view_new (ecal, gdbus_calview);
4510                 g_object_unref (gdbus_calview);
4511         }
4512
4513         E_CALENDAR_CHECK_STATUS (status, error);
4514 }
4515
4516 /**
4517  * e_cal_set_default_timezone:
4518  * @ecal: A calendar client.
4519  * @zone: A timezone object.
4520  * @error: Placeholder for error information.
4521  *
4522  * Sets the default timezone on the calendar. This should be called before opening
4523  * the calendar.
4524  *
4525  * Returns: TRUE if the operation was successful, FALSE otherwise.
4526  *
4527  * Deprecated: 3.2: Use e_cal_client_set_default_timezone() instead.
4528  */
4529 gboolean
4530 e_cal_set_default_timezone (ECal *ecal,
4531                             icaltimezone *zone,
4532                             GError **error)
4533 {
4534         ECalPrivate *priv;
4535
4536         e_return_error_if_fail (E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
4537         e_return_error_if_fail (zone, E_CALENDAR_STATUS_INVALID_ARG);
4538         priv = ecal->priv;
4539         e_return_error_if_fail (priv->gdbus_cal, E_CALENDAR_STATUS_REPOSITORY_OFFLINE);
4540
4541         /* If the same timezone is already set, we don't have to do anything. */
4542         if (priv->default_zone == zone)
4543                 return TRUE;
4544
4545         /* FIXME Adding it to the server to change the tzid */
4546         if (!icaltimezone_get_component (zone)) {
4547                 E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_INVALID_ARG, error);
4548         }
4549
4550         priv->default_zone = zone;
4551
4552         E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
4553 }
4554
4555 /**
4556  * e_cal_get_error_message
4557  * @status: A status code.
4558  *
4559  * Gets an error message for the given status code.
4560  *
4561  * Returns: the error message.
4562  *
4563  * Deprecated: 3.2: Use e_cal_client_error_to_string() instead.
4564  */
4565 const gchar *
4566 e_cal_get_error_message (ECalendarStatus status)
4567 {
4568         switch (status) {
4569         case E_CALENDAR_STATUS_INVALID_ARG :
4570                 return _("Invalid argument");
4571         case E_CALENDAR_STATUS_BUSY :
4572                 return _("Backend is busy");
4573         case E_CALENDAR_STATUS_REPOSITORY_OFFLINE :
4574                 return _("Repository is offline");
4575         case E_CALENDAR_STATUS_NO_SUCH_CALENDAR :
4576                 return _("No such calendar");
4577         case E_CALENDAR_STATUS_OBJECT_NOT_FOUND :
4578                 return _("Object not found");
4579         case E_CALENDAR_STATUS_INVALID_OBJECT :
4580                 return _("Invalid object");
4581         case E_CALENDAR_STATUS_URI_NOT_LOADED :
4582                 return _("URI not loaded");
4583         case E_CALENDAR_STATUS_URI_ALREADY_LOADED :
4584                 return _("URI already loaded");
4585         case E_CALENDAR_STATUS_PERMISSION_DENIED :
4586                 return _("Permission denied");
4587         case E_CALENDAR_STATUS_UNKNOWN_USER :
4588                 return _("Unknown User");
4589         case E_CALENDAR_STATUS_OBJECT_ID_ALREADY_EXISTS :
4590                 return _("Object ID already exists");
4591         case E_CALENDAR_STATUS_PROTOCOL_NOT_SUPPORTED :
4592                 return _("Protocol not supported");
4593         case E_CALENDAR_STATUS_CANCELLED :
4594                 return _("Operation has been canceled");
4595         case E_CALENDAR_STATUS_COULD_NOT_CANCEL :
4596                 return _("Could not cancel operation");
4597         case E_CALENDAR_STATUS_AUTHENTICATION_FAILED :
4598                 return _("Authentication failed");
4599         case E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED :
4600                 return _("Authentication required");
4601         case E_CALENDAR_STATUS_DBUS_EXCEPTION :
4602                 return _("A D-Bus exception has occurred");
4603         case E_CALENDAR_STATUS_OTHER_ERROR :
4604                 return _("Unknown error");
4605         case E_CALENDAR_STATUS_OK :
4606                 return _("No error");
4607         case E_CALENDAR_STATUS_NOT_SUPPORTED :
4608                 /* Translators: The string for NOT_SUPPORTED error */
4609                 return _("Not supported");
4610         default:
4611                 /* ignore everything else */
4612                 break;
4613         }
4614
4615         return NULL;
4616 }
4617
4618 /**
4619  * e_cal_open_default:
4620  * @ecal: A calendar client.
4621  * @type: Type of the calendar.
4622  * @func: Authentication function.
4623  * @data: Closure data for the authentication function.
4624  * @error: Placeholder for error information.
4625  *
4626  * Opens the default calendar.
4627  *
4628  * Returns: TRUE if it opened correctly, FALSE otherwise.
4629  *
4630  * Deprecated: 3.2: Use e_cal_client_new_default_calendar() instead
4631  * and open it on your own.
4632  */
4633 gboolean
4634 e_cal_open_default (ECal **ecal,
4635                     ECalSourceType type,
4636                     ECalAuthFunc func,
4637                     gpointer data,
4638                     GError **error)
4639 {
4640         ESourceList *source_list;
4641         ESource *source;
4642         ECal *client;
4643
4644         g_return_val_if_fail (ecal != NULL, FALSE);
4645
4646         /* In case something goes wrong... */
4647         *ecal = NULL;
4648
4649         if (!e_cal_get_sources (&source_list, type, error))
4650                 return FALSE;
4651
4652         source = e_source_list_peek_default_source (source_list);
4653         if (!source) {
4654                 g_set_error_literal (error, E_CALENDAR_ERROR,
4655                              E_CALENDAR_STATUS_NO_SUCH_CALENDAR,
4656                              e_cal_get_error_message (E_CALENDAR_STATUS_NO_SUCH_CALENDAR));
4657                 g_object_unref (source_list);
4658                 return FALSE;
4659         }
4660
4661         /* XXX So this can fail, but doesn't take a GError!? */
4662         client = e_cal_new (source, type);
4663
4664         if (client == NULL) {
4665                 g_set_error_literal (
4666                         error, E_CALENDAR_ERROR,
4667                         E_CALENDAR_STATUS_OTHER_ERROR,
4668                         e_cal_get_error_message (E_CALENDAR_STATUS_OTHER_ERROR));
4669                 g_object_unref (source_list);
4670                 return FALSE;
4671         }
4672
4673         e_cal_set_auth_func (client, func, data);
4674         if (!e_cal_open (client, TRUE, error)) {
4675                 g_object_unref (client);
4676                 g_object_unref (source_list);
4677                 return FALSE;
4678         }
4679
4680         *ecal = client;
4681
4682         g_object_unref (source_list);
4683
4684         return TRUE;
4685 }
4686
4687 /**
4688  * e_cal_set_default:
4689  * @ecal: an #ECal
4690  * @error: return location for a #GError, or %NULL
4691  *
4692  * Sets the #ESource in @ecal as default.
4693  *
4694  * Returns: TRUE if the operation was successful, FALSE otherwise.
4695  *
4696  * Deprecated: 3.2: Use e_cal_client_set_default() instead.
4697  */
4698 gboolean
4699 e_cal_set_default (ECal *ecal,
4700                    GError **error)
4701 {
4702         ESource *source;
4703         ECalSourceType source_type;
4704
4705         g_return_val_if_fail (E_IS_CAL (ecal), FALSE);
4706
4707         source = e_cal_get_source (ecal);
4708         source_type = e_cal_get_source_type (ecal);
4709
4710         return e_cal_set_default_source (source, source_type, error);
4711 }
4712
4713 static gboolean
4714 set_default_source (ESourceList *sources,
4715                     ESource *source,
4716                     GError **error)
4717 {
4718         const gchar *uid;
4719         GError *err = NULL;
4720         GSList *g;
4721
4722         uid = e_source_get_uid (source);
4723
4724         /* make sure the source is actually in the ESourceList.  if
4725          * it's not we don't bother adding it, just return an error */
4726         source = e_source_list_peek_source_by_uid (sources, uid);
4727         if (!source) {
4728                 g_set_error_literal (error, E_CALENDAR_ERROR,
4729                              E_CALENDAR_STATUS_NO_SUCH_CALENDAR,
4730                              e_cal_get_error_message (E_CALENDAR_STATUS_NO_SUCH_CALENDAR));
4731                 g_object_unref (sources);
4732                 return FALSE;
4733         }
4734
4735         /* loop over all the sources clearing out any "default"
4736          * properties we find */
4737         for (g = e_source_list_peek_groups (sources); g; g = g->next) {
4738                 GSList *s;
4739                 for (s = e_source_group_peek_sources (E_SOURCE_GROUP (g->data));
4740                      s; s = s->next) {
4741                         e_source_set_property (E_SOURCE (s->data), "default", NULL);
4742                 }
4743         }
4744
4745         /* set the "default" property on the source */
4746         e_source_set_property (source, "default", "true");
4747
4748         if (!e_source_list_sync (sources, &err)) {
4749                 g_propagate_error (error, err);
4750                 return FALSE;
4751         }
4752
4753         return TRUE;
4754 }
4755
4756 /**
4757  * e_cal_set_default_source:
4758  * @source: an #ESource
4759  * @type: type of the source
4760  * @error: return location for a #GError, or %NULL
4761  *
4762  * Sets @source as the default source for the specified @type.
4763  *
4764  * Returns: TRUE if the operation was successful, FALSE otherwise.
4765  *
4766  * Deprecated: 3.2: Use e_cal_client_set_default_source() instead.
4767  */
4768 gboolean
4769 e_cal_set_default_source (ESource *source,
4770                           ECalSourceType type,
4771                           GError **error)
4772 {
4773         ESourceList *sources;
4774         GError *err = NULL;
4775
4776         if (!e_cal_get_sources (&sources, type, &err)) {
4777                 g_propagate_error (error, err);
4778                 return FALSE;
4779         }
4780
4781         return set_default_source (sources, source, error);
4782 }
4783
4784 static gboolean
4785 get_sources (ESourceList **sources,
4786              const gchar *key,
4787              GError **error)
4788 {
4789         GConfClient *gconf = gconf_client_get_default ();
4790
4791         *sources = e_source_list_new_for_gconf (gconf, key);
4792         g_object_unref (gconf);
4793
4794         return TRUE;
4795 }
4796
4797 /**
4798  * e_cal_get_sources:
4799  * @sources: Return value for list of sources.
4800  * @type: Type of the sources to get.
4801  * @error: Placeholder for error information.
4802  *
4803  * Gets the list of sources defined in the configuration for the given @type.
4804  *
4805  * Returns: TRUE if the operation was successful, FALSE otherwise.
4806  *
4807  * Deprecated: 3.2: Use e_cal_client_get_sources() instead.
4808  */
4809 gboolean
4810 e_cal_get_sources (ESourceList **sources,
4811                    ECalSourceType type,
4812                    GError **error)
4813 {
4814         e_return_error_if_fail (sources != NULL, E_CALENDAR_STATUS_INVALID_ARG);
4815         *sources = NULL;
4816
4817         switch (type) {
4818         case E_CAL_SOURCE_TYPE_EVENT:
4819                 return get_sources (sources, "/apps/evolution/calendar/sources", error);
4820                 break;
4821         case E_CAL_SOURCE_TYPE_TODO:
4822                 return get_sources (sources, "/apps/evolution/tasks/sources", error);
4823                 break;
4824         case E_CAL_SOURCE_TYPE_JOURNAL:
4825                 return get_sources (sources, "/apps/evolution/memos/sources", error);
4826                 break;
4827         default:
4828                 g_set_error_literal (error, E_CALENDAR_ERROR,
4829                              E_CALENDAR_STATUS_NO_SUCH_CALENDAR,
4830                              e_cal_get_error_message (E_CALENDAR_STATUS_NO_SUCH_CALENDAR));
4831                 return FALSE;
4832         }
4833
4834         g_set_error_literal (error, E_CALENDAR_ERROR,
4835                      E_CALENDAR_STATUS_NO_SUCH_CALENDAR,
4836                      e_cal_get_error_message (E_CALENDAR_STATUS_NO_SUCH_CALENDAR));
4837         return FALSE;
4838 }