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