1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * JP Rosevear <jpr@ximian.com>
5 * Rodrigo Moya <rodrigo@ximian.com>
6 * Harish Krishnaswamy <kharish@novell.com>
8 * Copyright 2003, Novell, Inc.
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU Lesser General Public
12 * License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
30 #include <sys/types.h>
34 #include <glib/gstdio.h>
35 #include <glib/gi18n-lib.h>
36 #include <libgnomevfs/gnome-vfs-uri.h>
37 #include <libgnomevfs/gnome-vfs.h>
38 #include "libedataserver/e-xml-hash-utils.h"
39 #include "libedataserver/e-url.h"
40 #include <libedata-cal/e-cal-backend-cache.h>
41 #include <libedata-cal/e-cal-backend-util.h>
42 #include <libecal/e-cal-component.h>
43 #include <libecal/e-cal-time-util.h>
44 #include "e-cal-backend-groupwise.h"
45 #include "e-cal-backend-groupwise-utils.h"
46 #include "e-gw-connection.h"
53 /* Undef the similar macro from pthread.h, it doesn't check if
54 * gmtime() returns NULL.
58 /* The gmtime() in Microsoft's C library is MT-safe */
59 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
62 /* Private part of the CalBackendGroupwise structure */
63 struct _ECalBackendGroupwisePrivate {
64 /* A mutex to control access to the private structure */
67 ECalBackendCache *cache;
75 gboolean mode_changed;
76 icaltimezone *default_zone;
77 GHashTable *categories_by_id;
78 GHashTable *categories_by_name;
80 /* number of calendar items in the folder */
83 /* timeout handler for syncing sendoptions */
84 guint sendoptions_sync_timeout;
86 /* fields for storing info while offline */
88 char *local_attachments_store;
91 static void e_cal_backend_groupwise_dispose (GObject *object);
92 static void e_cal_backend_groupwise_finalize (GObject *object);
93 static void sanitize_component (ECalBackendSync *backend, ECalComponent *comp, char *server_uid);
94 static ECalBackendSyncStatus
95 e_cal_backend_groupwise_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj);
96 static const char * get_gw_item_id (icalcomponent *icalcomp);
97 static void get_retract_data (ECalComponent *comp, const char **retract_comment, gboolean *all_instances);
98 static const char * get_element_type (icalcomponent_kind kind);
100 #define PARENT_TYPE E_TYPE_CAL_BACKEND_SYNC
101 static ECalBackendClass *parent_class = NULL;
103 /* Time interval in milliseconds for obtaining changes from server and refresh the cache. */
104 #define CACHE_REFRESH_INTERVAL 600000
105 #define CURSOR_ITEM_LIMIT 100
106 #define CURSOR_ICALID_LIMIT 500
109 e_cal_backend_groupwise_get_connection (ECalBackendGroupwise *cbgw) {
111 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
113 return cbgw->priv->cnc;
117 e_cal_backend_groupwise_get_categories_by_id (ECalBackendGroupwise *cbgw) {
119 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
121 return cbgw->priv->categories_by_id;
125 e_cal_backend_groupwise_get_categories_by_name (ECalBackendGroupwise *cbgw) {
127 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
129 return cbgw->priv->categories_by_name;
133 e_cal_backend_groupwise_get_default_zone (ECalBackendGroupwise *cbgw) {
135 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
137 return cbgw->priv->default_zone;
140 static GMutex *mutex = NULL;
143 get_element_type (icalcomponent_kind kind)
148 if (kind == ICAL_VEVENT_COMPONENT)
149 type = "Appointment";
150 else if (kind == ICAL_VTODO_COMPONENT)
159 /* Initialy populate the cache from the server */
160 static EGwConnectionStatus
161 populate_cache (ECalBackendGroupwise *cbgw)
163 ECalBackendGroupwisePrivate *priv;
164 EGwConnectionStatus status;
166 GList *list = NULL, *l;
167 gboolean done = FALSE, forward = FALSE;
169 guint32 total, num = 0;
171 const char *position = E_GW_CURSOR_POSITION_END;
172 icalcomponent_kind kind;
174 EGwFilter* filter[3];
179 time_t h_time, l_time;
182 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
183 total = priv->total_count;
186 mutex = g_mutex_new ();
189 g_mutex_lock (mutex);
191 type = get_element_type (kind);
193 /* Fetch the data with a bias to present, near past/future */
194 temp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
195 i = g_ascii_strtod (g_getenv ("PRELOAD_WINDOW_DAYS")? g_getenv ("PRELOAD_WINDOW_DAYS"):"15", NULL);
197 icaltime_normalize (temp);
198 l_time = icaltime_as_timet_with_zone (temp, icaltimezone_get_utc_timezone ());
199 gmtime_r (&l_time, &tm);
200 strftime (l_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
202 icaltime_normalize (temp);
203 h_time = icaltime_as_timet_with_zone (temp, icaltimezone_get_utc_timezone ());
204 gmtime_r (&h_time, &tm);
205 strftime (h_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
207 filter[0] = e_gw_filter_new ();
208 e_gw_filter_add_filter_component (filter[0], E_GW_FILTER_OP_GREATERTHAN_OR_EQUAL, "startDate", l_str);
209 e_gw_filter_add_filter_component (filter[0], E_GW_FILTER_OP_LESSTHAN_OR_EQUAL, "startDate", h_str);
210 e_gw_filter_add_filter_component (filter[0], E_GW_FILTER_OP_EQUAL, "@type", type);
211 e_gw_filter_group_conditions (filter[0], E_GW_FILTER_OP_AND, 3);
212 filter[1] = e_gw_filter_new ();
213 e_gw_filter_add_filter_component (filter[1], E_GW_FILTER_OP_GREATERTHAN, "startDate", h_str);
214 e_gw_filter_add_filter_component (filter[1], E_GW_FILTER_OP_EQUAL, "@type", type);
215 e_gw_filter_group_conditions (filter[1], E_GW_FILTER_OP_AND, 2);
216 filter[2] = e_gw_filter_new ();
217 e_gw_filter_add_filter_component (filter[2], E_GW_FILTER_OP_LESSTHAN, "startDate", l_str);
218 e_gw_filter_add_filter_component (filter[2], E_GW_FILTER_OP_EQUAL, "@type", type);
219 e_gw_filter_group_conditions (filter[2], E_GW_FILTER_OP_AND, 2);
221 for (i = 0; i < 3; i++) {
222 status = e_gw_connection_create_cursor (priv->cnc,
224 "recipients message recipientStatus attachments default peek", filter[i], &cursor);
225 if (status != E_GW_CONNECTION_STATUS_OK) {
226 e_cal_backend_groupwise_notify_error_code (cbgw, status);
227 g_mutex_unlock (mutex);
232 position = E_GW_CURSOR_POSITION_START;
236 position = E_GW_CURSOR_POSITION_END;
242 status = e_gw_connection_read_cursor (priv->cnc, priv->container_id, cursor, forward, CURSOR_ITEM_LIMIT, position, &list);
243 if (status != E_GW_CONNECTION_STATUS_OK) {
244 e_cal_backend_groupwise_notify_error_code (cbgw, status);
245 g_mutex_unlock (mutex);
248 for (l = list; l != NULL; l = g_list_next(l)) {
250 char *progress_string = NULL;
252 item = E_GW_ITEM (l->data);
253 comp = e_gw_item_to_cal_component (item, cbgw);
254 g_object_unref (item);
256 /* Show the progress information */
258 percent = ((float) num/total) * 100;
260 /* FIXME The total obtained from the server is wrong. Sometimes the num can
261 be greater than the total. The following makes sure that the percentage is not >= 100 */
266 progress_string = g_strdup_printf (_("Loading %s items"), type);
267 e_cal_backend_notify_view_progress (E_CAL_BACKEND (cbgw), progress_string, percent);
269 if (E_IS_CAL_COMPONENT (comp)) {
272 e_cal_component_commit_sequence (comp);
273 comp_str = e_cal_component_get_as_string (comp);
274 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), (const char *) comp_str);
276 e_cal_backend_cache_put_component (priv->cache, comp);
277 g_object_unref (comp);
279 g_free (progress_string);
282 if (!list || g_list_length (list) == 0)
286 position = E_GW_CURSOR_POSITION_CURRENT;
288 e_gw_connection_destroy_cursor (priv->cnc, priv->container_id, cursor);
289 g_object_unref (filter[i]);
291 e_cal_backend_notify_view_done (E_CAL_BACKEND (cbgw), GNOME_Evolution_Calendar_Success);
293 g_mutex_unlock (mutex);
294 return E_GW_CONNECTION_STATUS_OK;
298 compare_prefix (gconstpointer a, gconstpointer b)
300 return !(g_str_has_prefix ((const char *)a, (const char *)b));
304 get_deltas (gpointer handle)
306 ECalBackendGroupwise *cbgw;
307 ECalBackendGroupwisePrivate *priv;
309 ECalBackendCache *cache;
310 EGwConnectionStatus status;
311 icalcomponent_kind kind;
312 GList *item_list, *total_list = NULL, *l;
313 GSList *cache_keys = NULL, *ls;
314 GPtrArray *uid_array = NULL;
315 char *time_string = NULL;
317 const char *serv_time;
318 static GStaticMutex connecting = G_STATIC_MUTEX_INIT;
319 const char *time_interval_string;
320 const char *key = "attempts";
321 const char *attempts;
322 const char *position ;
328 gboolean done = FALSE;
332 gboolean needs_to_get = FALSE;
336 cbgw = (ECalBackendGroupwise *) handle;
338 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
343 if (priv->mode == CAL_MODE_LOCAL)
346 attempts = e_cal_backend_cache_get_key_value (cache, key);
348 g_static_mutex_lock (&connecting);
350 serv_time = e_cal_backend_cache_get_server_utc_time (cache);
353 g_strlcpy (t_str, e_cal_backend_cache_get_server_utc_time (cache), 26);
354 if (!*t_str || !strcmp (t_str, "")) {
355 /* FIXME: When time-stamp is crashed, getting changes from current time */
356 g_warning ("\n\a Could not get the correct time stamp. \n\a");
357 tmp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
358 current_time = icaltime_as_timet_with_zone (tmp, icaltimezone_get_utc_timezone ());
359 gmtime_r (¤t_time, &tm);
360 strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
364 /* FIXME: When time-stamp is crashed, getting changes from current time */
365 g_warning ("\n\a Could not get the correct time stamp. \n\a");
366 tmp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
367 current_time = icaltime_as_timet_with_zone (tmp, icaltimezone_get_utc_timezone ());
368 gmtime_r (¤t_time, &tm);
369 strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
371 time_string = g_strdup (t_str);
373 filter = e_gw_filter_new ();
374 /* Items modified after the time-stamp */
375 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_GREATERTHAN, "modified", time_string);
376 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "@type", get_element_type (kind));
377 e_gw_filter_group_conditions (filter, E_GW_FILTER_OP_AND, 2);
379 status = e_gw_connection_get_items (cnc, cbgw->priv->container_id, "attachments recipients message recipientStatus default peek", filter, &item_list);
380 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
381 status = e_gw_connection_get_items (cnc, cbgw->priv->container_id, "attachments recipients message recipientStatus default peek", filter, &item_list);
382 g_object_unref (filter);
384 if (status != E_GW_CONNECTION_STATUS_OK) {
386 const char *msg = NULL;
389 e_cal_backend_cache_put_key_value (cache, key, "2");
392 failures = g_ascii_strtod(attempts, NULL) + 1;
393 e_cal_backend_cache_put_key_value (cache, key, GINT_TO_POINTER (failures));
396 if (status == E_GW_CONNECTION_STATUS_NO_RESPONSE) {
397 g_static_mutex_unlock (&connecting);
401 msg = e_gw_connection_get_error_message (status);
403 g_static_mutex_unlock (&connecting);
407 e_file_cache_freeze_changes (E_FILE_CACHE (cache));
409 for (; item_list != NULL; item_list = g_list_next(item_list)) {
410 EGwItem *item = NULL;
411 ECalComponent *modified_comp = NULL, *cache_comp = NULL;
412 char *cache_comp_str = NULL;
413 const char *uid, *rid = NULL;
416 item = E_GW_ITEM(item_list->data);
417 modified_comp = e_gw_item_to_cal_component (item, cbgw);
418 if (!modified_comp) {
419 g_message ("Invalid component returned in update");
422 if ((r_key = e_gw_item_get_recurrence_key (item)) != 0)
423 rid = e_cal_component_get_recurid_as_string (modified_comp);
425 e_cal_component_get_uid (modified_comp, &uid);
426 cache_comp = e_cal_backend_cache_get_component (cache, uid, rid);
427 e_cal_component_commit_sequence (modified_comp);
428 e_cal_component_commit_sequence (cache_comp);
430 cache_comp_str = e_cal_component_get_as_string (cache_comp);
431 e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbgw), cache_comp_str, e_cal_component_get_as_string (modified_comp));
432 g_free (cache_comp_str);
433 cache_comp_str = NULL;
434 e_cal_backend_cache_put_component (cache, modified_comp);
436 g_object_unref (item);
437 g_object_unref (modified_comp);
439 e_file_cache_thaw_changes (E_FILE_CACHE (cache));
441 temp = icaltime_from_string (time_string);
442 current_time = icaltime_as_timet_with_zone (temp, icaltimezone_get_utc_timezone ());
443 gmtime_r (¤t_time, &tm);
445 time_interval = (CACHE_REFRESH_INTERVAL / 60000);
446 time_interval_string = g_getenv ("GETQM_TIME_INTERVAL");
447 if (time_interval_string) {
448 time_interval = g_ascii_strtod (time_interval_string, NULL);
451 tm.tm_min += (time_interval * g_ascii_strtod (attempts, NULL));
452 e_cal_backend_cache_put_key_value (cache, key, NULL);
454 tm.tm_min += time_interval;
456 strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
457 time_string = g_strdup (t_str);
459 e_cal_backend_cache_put_server_utc_time (cache, time_string);
461 g_free (time_string);
465 g_list_free (item_list);
469 /* handle deleted items here by going over the entire cache and
470 * checking for deleted items.*/
471 position = E_GW_CURSOR_POSITION_END;
473 filter = e_gw_filter_new ();
474 e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "@type", get_element_type (kind));
476 status = e_gw_connection_create_cursor (cnc, cbgw->priv->container_id, "id iCalId recurrenceKey startDate", filter, &cursor);
477 g_object_unref (filter);
479 if (status != E_GW_CONNECTION_STATUS_OK) {
480 if (status == E_GW_CONNECTION_STATUS_NO_RESPONSE) {
481 g_static_mutex_unlock (&connecting);
485 e_cal_backend_groupwise_notify_error_code (cbgw, status);
486 g_static_mutex_unlock (&connecting);
490 cache_keys = e_cal_backend_cache_get_keys (cache);
494 status = e_gw_connection_read_cal_ids (cnc, cbgw->priv->container_id, cursor, FALSE, CURSOR_ICALID_LIMIT, position, &item_list);
495 if (status != E_GW_CONNECTION_STATUS_OK) {
496 if (status == E_GW_CONNECTION_STATUS_NO_RESPONSE) {
497 g_static_mutex_unlock (&connecting);
500 e_cal_backend_groupwise_notify_error_code (cbgw, status);
501 g_static_mutex_unlock (&connecting);
505 if (!item_list || g_list_length (item_list) == 0)
509 total_list = item_list;
511 total_list = g_list_concat (total_list, item_list);
516 position = E_GW_CURSOR_POSITION_CURRENT;
519 e_gw_connection_destroy_cursor (cnc, cbgw->priv->container_id, cursor);
520 e_file_cache_freeze_changes (E_FILE_CACHE (cache));
522 uid_array = g_ptr_array_new ();
523 for (l = total_list; l != NULL; l = l->next) {
524 EGwItemCalId *calid = (EGwItemCalId *) l->data;
525 GCompareFunc func = NULL;
526 GSList *remove = NULL;
527 char *real_key = NULL;
528 const char *recur_key;
530 if (calid->recur_key && calid->ical_id) {
531 const char *rid = NULL;
532 icaltimetype tt = icaltime_from_string (calid->ical_id);
534 tt = icaltime_convert_to_zone (tt, priv->default_zone);
535 icaltime_set_timezone (&tt, priv->default_zone);
536 rid = icaltime_as_ical_string (tt);
538 rid = calid->ical_id;
539 real_key = g_strconcat (calid->recur_key, "@", rid, NULL);
542 if (!calid->recur_key || real_key) {
543 recur_key = real_key;
544 func = (GCompareFunc) strcmp;
546 recur_key = calid->recur_key;
547 func = (GCompareFunc) compare_prefix;
550 if (!(remove = g_slist_find_custom (cache_keys, calid->recur_key ? recur_key :
551 calid->ical_id, func))) {
552 g_ptr_array_add (uid_array, g_strdup (calid->item_id));
555 cache_keys = g_slist_remove_link (cache_keys, remove);
561 for (ls = cache_keys; ls ; ls = g_slist_next (ls)) {
562 ECalComponent *comp = NULL;
563 icalcomponent *icalcomp = NULL;
566 comp = e_cal_backend_cache_get_component (cache, (const char *) ls->data, NULL);
570 icalcomp = e_cal_component_get_icalcomponent (comp);
571 if (kind == icalcomponent_isa (icalcomp)) {
572 char *comp_str = NULL;
573 ECalComponentId *id = e_cal_component_get_id (comp);
575 comp_str = e_cal_component_get_as_string (comp);
576 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw),
578 e_cal_backend_cache_remove_component (cache, (const char *) id->uid, id->rid);
580 e_cal_component_free_id (id);
583 g_object_unref (comp);
587 e_gw_connection_get_items_from_ids (priv->cnc,
589 "attachments recipients message recipientStatus default peek",
590 uid_array, &item_list);
592 for (l = item_list; l != NULL; l = l->next) {
593 ECalComponent *comp = NULL;
594 EGwItem *item = NULL;
597 item = (EGwItem *) l->data;
598 comp = e_gw_item_to_cal_component (item, cbgw);
600 e_cal_component_commit_sequence (comp);
601 sanitize_component (E_CAL_BACKEND_SYNC (cbgw), comp, (char *) e_gw_item_get_id (item));
602 e_cal_backend_cache_put_component (priv->cache, comp);
605 if (kind == icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
606 tmp = e_cal_component_get_as_string (comp);
607 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), tmp);
610 g_object_unref (comp);
613 g_object_unref (item);
617 e_file_cache_thaw_changes (E_FILE_CACHE (cache));
619 g_ptr_array_free (uid_array, TRUE);
622 g_list_free (item_list);
627 g_list_foreach (total_list, (GFunc) e_gw_item_free_cal_id, NULL);
628 g_list_free (total_list);
632 /*FIXME this is a memory leak, but free'ing it causes crash in gslice */
633 // g_slist_free (cache_keys);
636 g_static_mutex_unlock (&connecting);
641 get_deltas_timeout (gpointer cbgw)
644 GError *error = NULL;
649 thread = g_thread_create ((GThreadFunc) get_deltas, cbgw, FALSE, &error);
651 g_warning (G_STRLOC ": %s", error->message);
652 g_error_free (error);
660 form_uri (ESource *source)
669 uri = e_source_get_uri (source);
673 parsed_uri = e_uri_new (uri);
674 if (parsed_uri == NULL)
677 port = e_source_get_property (source, "port");
680 use_ssl = e_source_get_property (source, "use_ssl");
682 if (use_ssl && !g_str_equal (use_ssl, "never"))
683 formed_uri = g_strconcat ("https://", parsed_uri->host,":", port, "/soap", NULL );
685 formed_uri = g_strconcat ("http://", parsed_uri->host,":", port, "/soap", NULL );
688 e_uri_free (parsed_uri);
693 static ECalBackendSyncStatus
694 cache_init (ECalBackendGroupwise *cbgw)
696 ECalBackendGroupwisePrivate *priv = cbgw->priv;
697 EGwConnectionStatus cnc_status;
698 icalcomponent_kind kind;
699 EGwSendOptions *opts;
700 const char *time_interval_string;
703 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
704 time_interval = CACHE_REFRESH_INTERVAL;
705 time_interval_string = g_getenv ("GETQM_TIME_INTERVAL");
706 if (time_interval_string) {
707 time_interval = g_ascii_strtod (time_interval_string, NULL);
708 time_interval *= (60*1000);
711 cnc_status = e_gw_connection_get_settings (priv->cnc, &opts);
712 if (cnc_status == E_GW_CONNECTION_STATUS_OK) {
713 GwSettings *hold = g_new0 (GwSettings, 1);
718 /* We now sync the sendoptions into e-source using the GLIB main loop. Doing this operation
719 in a thread causes crashes. */
720 priv->sendoptions_sync_timeout = g_idle_add ((GSourceFunc ) e_cal_backend_groupwise_store_settings, hold);
722 g_warning (G_STRLOC ": Could not get the settings from the server");
724 /* get the list of category ids and corresponding names from the server */
725 cnc_status = e_gw_connection_get_categories (priv->cnc, &priv->categories_by_id, &priv->categories_by_name);
726 if (cnc_status != E_GW_CONNECTION_STATUS_OK) {
727 g_warning (G_STRLOC ": Could not get the categories from the server");
730 /* We poke the cache for a default timezone. Its
731 * absence indicates that the cache file has not been
732 * populated before. */
733 if (!e_cal_backend_cache_get_marker (priv->cache)) {
734 /* Populate the cache for the first time.*/
735 /* start a timed polling thread set to 1 minute*/
736 cnc_status = populate_cache (cbgw);
737 if (cnc_status != E_GW_CONNECTION_STATUS_OK) {
738 g_warning (G_STRLOC ": Could not populate the cache");
739 /*FIXME why dont we do a notify here */
740 return GNOME_Evolution_Calendar_PermissionDenied;
744 utc_str = (char *) e_gw_connection_get_server_time (priv->cnc);
745 e_cal_backend_cache_set_marker (priv->cache);
746 e_cal_backend_cache_put_server_utc_time (priv->cache, utc_str);
748 /* Set up deltas only if it is a Calendar backend */
749 priv->timeout_id = g_timeout_add (time_interval, (GSourceFunc) get_deltas_timeout, (gpointer) cbgw);
750 priv->mode = CAL_MODE_REMOTE;
752 return GNOME_Evolution_Calendar_Success;
756 /* get the deltas from the cache */
757 if (get_deltas (cbgw)) {
758 priv->timeout_id = g_timeout_add (time_interval, (GSourceFunc) get_deltas_timeout, (gpointer) cbgw);
759 priv->mode = CAL_MODE_REMOTE;
760 return GNOME_Evolution_Calendar_Success;
762 g_warning (G_STRLOC ": Could not populate the cache");
763 /*FIXME why dont we do a notify here */
764 return GNOME_Evolution_Calendar_PermissionDenied;
769 static ECalBackendSyncStatus
770 set_container_id_with_count (ECalBackendGroupwise *cbgw)
772 ECalBackendGroupwisePrivate *priv;
773 GList *container_list = NULL, *l;
774 EGwConnectionStatus status;
775 icalcomponent_kind kind;
776 ECalBackendSyncStatus res;
780 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
783 case ICAL_VEVENT_COMPONENT:
784 case ICAL_VTODO_COMPONENT:
785 case ICAL_VJOURNAL_COMPONENT:
786 e_source_set_name (e_cal_backend_get_source (E_CAL_BACKEND (cbgw)), _("Calendar"));
789 priv->container_id = NULL;
790 return GNOME_Evolution_Calendar_UnsupportedMethod;
793 status = e_gw_connection_get_container_list (priv->cnc, "folders", &container_list);
795 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
796 status = e_gw_connection_get_container_list (priv->cnc, "folders", &container_list);
798 if (status != E_GW_CONNECTION_STATUS_OK)
799 return GNOME_Evolution_Calendar_OtherError;
801 res = GNOME_Evolution_Calendar_ObjectNotFound;
802 for (l = container_list; l != NULL; l = l->next) {
803 EGwContainer *container = E_GW_CONTAINER (l->data);
804 const char *name = e_gw_container_get_name (container);
806 if (name && strcmp (name, "Calendar") == 0) {
807 priv->container_id = g_strdup (e_gw_container_get_id (container));
808 priv->total_count = e_gw_container_get_total_count (container);
809 res = GNOME_Evolution_Calendar_Success;
814 e_gw_connection_free_container_list (container_list);
819 static ECalBackendSyncStatus
820 connect_to_server (ECalBackendGroupwise *cbgw)
823 ECalBackendGroupwisePrivate *priv;
825 ECalSourceType source_type;
830 GError *error = NULL;
831 char *parent_user = NULL;
832 icalcomponent_kind kind;
836 source = e_cal_backend_get_source (E_CAL_BACKEND (cbgw));
839 real_uri = form_uri (source);
840 use_ssl = e_source_get_property (source, "use_ssl");
843 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Invalid server URI"));
844 return GNOME_Evolution_Calendar_NoSuchCal;
847 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
849 parent_user = (char *) e_source_get_property (source, "parent_id_name");
850 /* create connection to server */
853 /* create connection to server */
854 cnc = e_gw_connection_new (real_uri, parent_user, priv->password);
855 if (!E_IS_GW_CONNECTION(cnc) && use_ssl && g_str_equal (use_ssl, "when-possible")) {
856 http_uri = g_strconcat ("http://", real_uri + 8, NULL);
857 cnc = e_gw_connection_new (http_uri, parent_user, priv->password);
862 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Authentication failed"));
863 return GNOME_Evolution_Calendar_AuthenticationFailed;
866 priv->cnc = e_gw_connection_get_proxy_connection (cnc, parent_user, priv->password, priv->username, &permissions);
871 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Authentication failed"));
872 return GNOME_Evolution_Calendar_AuthenticationFailed;
876 cbgw->priv->read_only = TRUE;
878 if (kind == ICAL_VEVENT_COMPONENT && (permissions & E_GW_PROXY_APPOINTMENT_WRITE) )
879 cbgw->priv->read_only = FALSE;
880 else if (kind == ICAL_VTODO_COMPONENT && (permissions & E_GW_PROXY_TASK_WRITE))
881 cbgw->priv->read_only = FALSE;
882 else if (kind == ICAL_VJOURNAL_COMPONENT && (permissions & E_GW_PROXY_NOTES_WRITE))
883 cbgw->priv->read_only = FALSE;
887 priv->cnc = e_gw_connection_new (
892 if (!E_IS_GW_CONNECTION(priv->cnc) && use_ssl && g_str_equal (use_ssl, "when-possible")) {
893 http_uri = g_strconcat ("http://", real_uri + 8, NULL);
894 priv->cnc = e_gw_connection_new (http_uri, priv->username, priv->password);
897 cbgw->priv->read_only = FALSE;
901 if (priv->cnc && priv->cache && priv->container_id) {
903 priv->mode = CAL_MODE_REMOTE;
904 if (priv->mode_changed && !priv->timeout_id ) {
906 priv->mode_changed = FALSE;
908 thread1 = g_thread_create ((GThreadFunc) get_deltas, cbgw, FALSE, &error);
910 g_warning (G_STRLOC ": %s", error->message);
911 g_error_free (error);
913 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create thread for getting deltas"));
914 return GNOME_Evolution_Calendar_OtherError;
916 priv->timeout_id = g_timeout_add (CACHE_REFRESH_INTERVAL, (GSourceFunc) get_deltas_timeout, (gpointer)cbgw);
918 utc_str = (char *) e_gw_connection_get_server_time (priv->cnc);
919 e_cal_backend_cache_put_server_utc_time (priv->cache, utc_str);
921 return GNOME_Evolution_Calendar_Success;
923 priv->mode_changed = FALSE;
926 case ICAL_VEVENT_COMPONENT:
927 source_type = E_CAL_SOURCE_TYPE_EVENT;
929 case ICAL_VTODO_COMPONENT:
930 source_type = E_CAL_SOURCE_TYPE_TODO;
932 case ICAL_VJOURNAL_COMPONENT:
933 source_type = E_CAL_SOURCE_TYPE_JOURNAL;
936 source_type = E_CAL_SOURCE_TYPE_EVENT;
940 if (E_IS_GW_CONNECTION (priv->cnc)) {
941 ECalBackendSyncStatus status;
942 /* get the ID for the container */
943 if (priv->container_id)
944 g_free (priv->container_id);
946 if ((status = set_container_id_with_count (cbgw)) != GNOME_Evolution_Calendar_Success) {
950 priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbgw)), source_type);
952 g_mutex_unlock (priv->mutex);
953 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create cache file"));
954 return GNOME_Evolution_Calendar_OtherError;
957 e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
959 /* spawn a new thread for opening the calendar */
960 thread = g_thread_create ((GThreadFunc) cache_init, cbgw, FALSE, &error);
962 g_warning (G_STRLOC ": %s", error->message);
963 g_error_free (error);
965 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create thread for populating cache"));
966 return GNOME_Evolution_Calendar_OtherError;
971 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Authentication failed"));
972 return GNOME_Evolution_Calendar_AuthenticationFailed;
975 if (!e_gw_connection_get_version (priv->cnc))
976 return GNOME_Evolution_Calendar_InvalidServerVersion;
978 return GNOME_Evolution_Calendar_Success;
981 /* Dispose handler for the file backend */
983 e_cal_backend_groupwise_dispose (GObject *object)
985 ECalBackendGroupwise *cbgw;
986 ECalBackendGroupwisePrivate *priv;
988 cbgw = E_CAL_BACKEND_GROUPWISE (object);
991 if (G_OBJECT_CLASS (parent_class)->dispose)
992 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
995 /* Finalize handler for the file backend */
997 e_cal_backend_groupwise_finalize (GObject *object)
999 ECalBackendGroupwise *cbgw;
1000 ECalBackendGroupwisePrivate *priv;
1002 g_return_if_fail (object != NULL);
1003 g_return_if_fail (E_IS_CAL_BACKEND_GROUPWISE (object));
1005 cbgw = E_CAL_BACKEND_GROUPWISE (object);
1010 g_mutex_free (priv->mutex);
1015 g_object_unref (priv->cnc);
1020 g_object_unref (priv->cache);
1024 if (priv->username) {
1025 g_free (priv->username);
1026 priv->username = NULL;
1029 if (priv->password) {
1030 g_free (priv->password);
1031 priv->password = NULL;
1034 if (priv->container_id) {
1035 g_free (priv->container_id);
1036 priv->container_id = NULL;
1039 if (priv->user_email) {
1040 g_free (priv->user_email);
1041 priv->user_email = NULL;
1044 if (priv->local_attachments_store) {
1045 g_free (priv->local_attachments_store);
1046 priv->local_attachments_store = NULL;
1049 if (priv->timeout_id) {
1050 g_source_remove (priv->timeout_id);
1051 priv->timeout_id = 0;
1054 if (priv->sendoptions_sync_timeout) {
1055 g_source_remove (priv->sendoptions_sync_timeout);
1056 priv->sendoptions_sync_timeout = 0;
1059 if (priv->default_zone) {
1060 icaltimezone_free (priv->default_zone, 1);
1061 priv->default_zone = NULL;
1067 if (G_OBJECT_CLASS (parent_class)->finalize)
1068 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
1071 /* Calendar backend methods */
1073 /* Is_read_only handler for the file backend */
1074 static ECalBackendSyncStatus
1075 e_cal_backend_groupwise_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
1077 ECalBackendGroupwise *cbgw;
1079 cbgw = E_CAL_BACKEND_GROUPWISE(backend);
1080 *read_only = cbgw->priv->read_only;
1082 return GNOME_Evolution_Calendar_Success;
1085 /* return email address of the person who opened the calender */
1086 static ECalBackendSyncStatus
1087 e_cal_backend_groupwise_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
1089 ECalBackendGroupwise *cbgw;
1090 ECalBackendGroupwisePrivate *priv;
1092 cbgw = E_CAL_BACKEND_GROUPWISE(backend);
1095 if (priv->mode == CAL_MODE_REMOTE) {
1096 if (priv->user_email)
1097 g_free (priv->user_email);
1099 priv->user_email = g_strdup (e_gw_connection_get_user_email (cbgw->priv->cnc));
1102 *address = g_strdup (priv->user_email);
1104 return GNOME_Evolution_Calendar_Success;
1107 static ECalBackendSyncStatus
1108 e_cal_backend_groupwise_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
1110 /* ldap attribute is specific to Sun ONE connector to get free busy information*/
1111 /* retun NULL here as group wise backend know how to get free busy information */
1115 return GNOME_Evolution_Calendar_Success;
1118 static ECalBackendSyncStatus
1119 e_cal_backend_groupwise_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
1121 /*group wise does not support email based alarms */
1125 return GNOME_Evolution_Calendar_Success;
1128 static ECalBackendSyncStatus
1129 e_cal_backend_groupwise_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
1131 *capabilities = g_strdup (CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS ","
1132 CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY ","
1133 CAL_STATIC_CAPABILITY_REMOVE_ALARMS ","
1134 CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
1135 CAL_STATIC_CAPABILITY_NO_THISANDFUTURE ","
1136 CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK ","
1137 CAL_STATIC_CAPABILITY_NO_CONV_TO_RECUR ","
1138 CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS ","
1139 CAL_STATIC_CAPABILITY_SAVE_SCHEDULES ","
1140 CAL_STATIC_CAPABILITY_ORGANIZER_MUST_ACCEPT ","
1141 CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED ","
1142 CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY ","
1143 CAL_STATIC_CAPABILITY_NO_ORGANIZER ","
1144 CAL_STATIC_CAPABILITY_RECURRENCES_NO_MASTER ","
1145 CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING ","
1146 CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
1148 return GNOME_Evolution_Calendar_Success;
1153 in_offline (ECalBackendGroupwise *cbgw) {
1154 ECalBackendGroupwisePrivate *priv;
1157 priv->read_only = TRUE;
1159 if (priv->timeout_id) {
1160 g_source_remove (priv->timeout_id);
1161 priv->timeout_id =0;
1165 g_object_unref (priv->cnc);
1172 /* Open handler for the file backend */
1173 static ECalBackendSyncStatus
1174 e_cal_backend_groupwise_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists,
1175 const char *username, const char *password)
1177 ECalBackendGroupwise *cbgw;
1178 ECalBackendGroupwisePrivate *priv;
1179 ECalBackendSyncStatus status;
1180 ECalSourceType source_type;
1181 char *source = NULL;
1186 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1189 g_mutex_lock (priv->mutex);
1191 cbgw->priv->read_only = FALSE;
1193 switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
1194 case ICAL_VEVENT_COMPONENT:
1195 source_type = E_CAL_SOURCE_TYPE_EVENT;
1196 source = "calendar";
1198 case ICAL_VTODO_COMPONENT:
1199 source_type = E_CAL_SOURCE_TYPE_TODO;
1202 case ICAL_VJOURNAL_COMPONENT:
1203 source_type = E_CAL_SOURCE_TYPE_JOURNAL;
1207 source_type = E_CAL_SOURCE_TYPE_EVENT;
1210 if (priv->mode == CAL_MODE_LOCAL) {
1212 const char *display_contents = NULL;
1214 cbgw->priv->read_only = TRUE;
1215 esource = e_cal_backend_get_source (E_CAL_BACKEND (cbgw));
1216 display_contents = e_source_get_property (esource, "offline_sync");
1218 if (!display_contents || !g_str_equal (display_contents, "1")) {
1219 g_mutex_unlock (priv->mutex);
1220 return GNOME_Evolution_Calendar_RepositoryOffline;
1224 priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbgw)), source_type);
1226 g_mutex_unlock (priv->mutex);
1227 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create cache file"));
1229 return GNOME_Evolution_Calendar_OtherError;
1233 e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
1235 g_mutex_unlock (priv->mutex);
1236 return GNOME_Evolution_Calendar_Success;
1239 priv->username = g_strdup (username);
1240 priv->password = g_strdup (password);
1242 /* Set the local attachment store*/
1243 mangled_uri = g_strdup (e_cal_backend_get_uri (E_CAL_BACKEND (cbgw)));
1244 /* mangle the URI to not contain invalid characters */
1245 for (i = 0; i < strlen (mangled_uri); i++) {
1246 switch (mangled_uri[i]) {
1249 mangled_uri[i] = '_';
1253 filename = g_build_filename (g_get_home_dir (),
1254 ".evolution/cache/", source,
1257 g_free (mangled_uri);
1258 priv->local_attachments_store =
1259 g_filename_to_uri (filename, NULL, NULL);
1262 /* FIXME: no need to set it online here when we implement the online/offline stuff correctly */
1263 status = connect_to_server (cbgw);
1265 g_mutex_unlock (priv->mutex);
1269 static ECalBackendSyncStatus
1270 e_cal_backend_groupwise_remove (ECalBackendSync *backend, EDataCal *cal)
1272 ECalBackendGroupwise *cbgw;
1273 ECalBackendGroupwisePrivate *priv;
1275 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1278 g_mutex_lock (priv->mutex);
1280 /* remove the cache */
1282 e_file_cache_remove (E_FILE_CACHE (priv->cache));
1284 g_mutex_unlock (priv->mutex);
1286 return GNOME_Evolution_Calendar_Success;
1289 /* is_loaded handler for the file backend */
1291 e_cal_backend_groupwise_is_loaded (ECalBackend *backend)
1293 ECalBackendGroupwise *cbgw;
1294 ECalBackendGroupwisePrivate *priv;
1296 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1299 return priv->cache ? TRUE : FALSE;
1302 /* is_remote handler for the file backend */
1304 e_cal_backend_groupwise_get_mode (ECalBackend *backend)
1306 ECalBackendGroupwise *cbgw;
1307 ECalBackendGroupwisePrivate *priv;
1309 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1315 /* Set_mode handler for the file backend */
1317 e_cal_backend_groupwise_set_mode (ECalBackend *backend, CalMode mode)
1319 ECalBackendGroupwise *cbgw;
1320 ECalBackendGroupwisePrivate *priv;
1322 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1325 if (priv->mode == mode) {
1326 e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
1327 cal_mode_to_corba (mode));
1331 g_mutex_lock (priv->mutex);
1333 priv->mode_changed = TRUE;
1335 case CAL_MODE_REMOTE :/* go online */
1336 priv->mode = CAL_MODE_REMOTE;
1337 priv->read_only = FALSE;
1338 e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
1339 GNOME_Evolution_Calendar_MODE_REMOTE);
1340 e_cal_backend_notify_readonly (backend, priv->read_only);
1341 if(e_cal_backend_groupwise_is_loaded (backend))
1342 e_cal_backend_notify_auth_required(backend);
1345 case CAL_MODE_LOCAL : /* go offline */
1346 /* FIXME: make sure we update the cache before closing the connection */
1347 priv->mode = CAL_MODE_LOCAL;
1349 e_cal_backend_notify_readonly (backend, priv->read_only);
1350 e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
1351 GNOME_Evolution_Calendar_MODE_LOCAL);
1355 e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
1356 cal_mode_to_corba (mode));
1359 g_mutex_unlock (priv->mutex);
1362 static ECalBackendSyncStatus
1363 e_cal_backend_groupwise_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
1366 ECalComponent *comp;
1368 comp = e_cal_component_new ();
1370 switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
1371 case ICAL_VEVENT_COMPONENT:
1372 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1374 case ICAL_VTODO_COMPONENT:
1375 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1378 g_object_unref (comp);
1379 return GNOME_Evolution_Calendar_ObjectNotFound;
1382 *object = e_cal_component_get_as_string (comp);
1383 g_object_unref (comp);
1385 return GNOME_Evolution_Calendar_Success;
1388 /* Get_object_component handler for the groupwise backend */
1389 static ECalBackendSyncStatus
1390 e_cal_backend_groupwise_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
1392 ECalComponent *comp;
1393 ECalBackendGroupwisePrivate *priv;
1394 ECalBackendGroupwise *cbgw = (ECalBackendGroupwise *) backend;
1396 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_OtherError);
1400 g_mutex_lock (priv->mutex);
1402 /* search the object in the cache */
1403 comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
1405 g_mutex_unlock (priv->mutex);
1406 if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) ==
1407 icalcomponent_isa (e_cal_component_get_icalcomponent (comp)))
1408 *object = e_cal_component_get_as_string (comp);
1412 g_object_unref (comp);
1414 return *object ? GNOME_Evolution_Calendar_Success : GNOME_Evolution_Calendar_ObjectNotFound;
1417 g_mutex_unlock (priv->mutex);
1419 /* callers will never have a uuid that is in server but not in cache */
1420 return GNOME_Evolution_Calendar_ObjectNotFound;
1423 /* Get_timezone_object handler for the groupwise backend */
1424 static ECalBackendSyncStatus
1425 e_cal_backend_groupwise_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
1427 ECalBackendGroupwise *cbgw;
1428 ECalBackendGroupwisePrivate *priv;
1430 icalcomponent *icalcomp;
1432 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1435 g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1437 if (!strcmp (tzid, "UTC")) {
1438 zone = icaltimezone_get_utc_timezone ();
1440 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1442 return GNOME_Evolution_Calendar_ObjectNotFound;
1445 icalcomp = icaltimezone_get_component (zone);
1447 return GNOME_Evolution_Calendar_InvalidObject;
1449 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1451 return GNOME_Evolution_Calendar_Success;
1454 /* Add_timezone handler for the groupwise backend */
1455 static ECalBackendSyncStatus
1456 e_cal_backend_groupwise_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
1458 icalcomponent *tz_comp;
1459 ECalBackendGroupwise *cbgw;
1460 ECalBackendGroupwisePrivate *priv;
1462 cbgw = (ECalBackendGroupwise *) backend;
1464 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_OtherError);
1465 g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
1469 tz_comp = icalparser_parse_string (tzobj);
1471 return GNOME_Evolution_Calendar_InvalidObject;
1473 if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
1476 zone = icaltimezone_new ();
1477 icaltimezone_set_component (zone, tz_comp);
1478 if (e_cal_backend_cache_put_timezone (priv->cache, zone) == FALSE) {
1479 icaltimezone_free (zone, 1);
1480 return GNOME_Evolution_Calendar_OtherError;
1482 icaltimezone_free (zone, 1);
1484 return GNOME_Evolution_Calendar_Success;
1487 static ECalBackendSyncStatus
1488 e_cal_backend_groupwise_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
1490 icalcomponent *tz_comp;
1491 ECalBackendGroupwise *cbgw;
1492 ECalBackendGroupwisePrivate *priv;
1495 cbgw = (ECalBackendGroupwise *) backend;
1497 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_OtherError);
1498 g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
1502 tz_comp = icalparser_parse_string (tzobj);
1504 return GNOME_Evolution_Calendar_InvalidObject;
1506 zone = icaltimezone_new ();
1507 icaltimezone_set_component (zone, tz_comp);
1509 if (priv->default_zone)
1510 icaltimezone_free (priv->default_zone, 1);
1512 /* Set the default timezone to it. */
1513 priv->default_zone = zone;
1515 return GNOME_Evolution_Calendar_Success;
1518 /* Gets the list of attachments */
1519 static ECalBackendSyncStatus
1520 e_cal_backend_groupwise_get_attachment_list (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, GSList **list)
1522 /* TODO implement the function */
1523 return GNOME_Evolution_Calendar_Success;
1526 /* Get_objects_in_range handler for the groupwise backend */
1527 static ECalBackendSyncStatus
1528 e_cal_backend_groupwise_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects)
1530 ECalBackendGroupwise *cbgw;
1531 ECalBackendGroupwisePrivate *priv;
1532 GList *components, *l;
1533 ECalBackendSExp *cbsexp;
1534 gboolean search_needed = TRUE;
1536 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1539 g_mutex_lock (priv->mutex);
1541 g_message (G_STRLOC ": Getting object list (%s)", sexp);
1543 if (!strcmp (sexp, "#t"))
1544 search_needed = FALSE;
1546 cbsexp = e_cal_backend_sexp_new (sexp);
1548 g_mutex_unlock (priv->mutex);
1549 return GNOME_Evolution_Calendar_InvalidQuery;
1553 components = e_cal_backend_cache_get_components (priv->cache);
1554 for (l = components; l != NULL; l = l->next) {
1555 ECalComponent *comp = E_CAL_COMPONENT (l->data);
1557 if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) ==
1558 icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
1559 if ((!search_needed) ||
1560 (e_cal_backend_sexp_match_comp (cbsexp, comp, E_CAL_BACKEND (backend)))) {
1561 *objects = g_list_append (*objects, e_cal_component_get_as_string (comp));
1566 g_object_unref (cbsexp);
1567 g_list_foreach (components, (GFunc) g_object_unref, NULL);
1568 g_list_free (components);
1570 g_mutex_unlock (priv->mutex);
1572 return GNOME_Evolution_Calendar_Success;
1575 /* get_query handler for the groupwise backend */
1577 e_cal_backend_groupwise_start_query (ECalBackend *backend, EDataCalView *query)
1579 ECalBackendSyncStatus status;
1580 ECalBackendGroupwise *cbgw;
1581 ECalBackendGroupwisePrivate *priv;
1582 GList *objects = NULL;
1584 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1587 g_message (G_STRLOC ": Starting query (%s)", e_data_cal_view_get_text (query));
1589 status = e_cal_backend_groupwise_get_object_list (E_CAL_BACKEND_SYNC (backend), NULL,
1590 e_data_cal_view_get_text (query), &objects);
1591 if (status != GNOME_Evolution_Calendar_Success) {
1592 e_data_cal_view_notify_done (query, status);
1596 /* notify listeners of all objects */
1598 e_data_cal_view_notify_objects_added (query, (const GList *) objects);
1601 g_list_foreach (objects, (GFunc) g_free, NULL);
1602 g_list_free (objects);
1605 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
1608 /* Get_free_busy handler for the file backend */
1609 static ECalBackendSyncStatus
1610 e_cal_backend_groupwise_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users,
1611 time_t start, time_t end, GList **freebusy)
1613 EGwConnectionStatus status;
1614 ECalBackendGroupwise *cbgw;
1617 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1618 cnc = cbgw->priv->cnc;
1620 if (cbgw->priv->mode == CAL_MODE_LOCAL) {
1622 return GNOME_Evolution_Calendar_RepositoryOffline;
1625 status = e_gw_connection_get_freebusy_info (cnc, users, start, end, freebusy, cbgw->priv->default_zone);
1627 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
1628 status = e_gw_connection_get_freebusy_info (cnc, users, start, end, freebusy, cbgw->priv->default_zone);
1630 if (status != E_GW_CONNECTION_STATUS_OK)
1631 return GNOME_Evolution_Calendar_OtherError;
1632 return GNOME_Evolution_Calendar_Success;
1636 ECalBackendGroupwise *backend;
1637 icalcomponent_kind kind;
1640 } ECalBackendGroupwiseComputeChangesData;
1643 e_cal_backend_groupwise_compute_changes_foreach_key (const char *key, const char *value, gpointer data)
1645 ECalBackendGroupwiseComputeChangesData *be_data = data;
1647 if (!e_cal_backend_cache_get_component (be_data->backend->priv->cache, key, NULL)) {
1648 ECalComponent *comp;
1650 comp = e_cal_component_new ();
1651 if (be_data->kind == ICAL_VTODO_COMPONENT)
1652 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1654 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1656 e_cal_component_set_uid (comp, key);
1657 be_data->deletes = g_list_prepend (be_data->deletes, e_cal_component_get_as_string (comp));
1659 e_xmlhash_remove (be_data->ehash, key);
1660 g_object_unref (comp);
1664 static ECalBackendSyncStatus
1665 e_cal_backend_groupwise_compute_changes (ECalBackendGroupwise *cbgw, const char *change_id,
1666 GList **adds, GList **modifies, GList **deletes)
1668 ECalBackendSyncStatus status;
1669 ECalBackendCache *cache;
1672 ECalBackendGroupwiseComputeChangesData be_data;
1673 GList *i, *list = NULL;
1674 gchar *unescaped_uri;
1676 cache = cbgw->priv->cache;
1678 /* FIXME Will this always work? */
1679 unescaped_uri = gnome_vfs_unescape_string (cbgw->priv->uri, "");
1680 filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id);
1681 ehash = e_xmlhash_new (filename);
1683 g_free (unescaped_uri);
1685 status = e_cal_backend_groupwise_get_object_list (E_CAL_BACKEND_SYNC (cbgw), NULL, NULL, &list);
1686 if (status != GNOME_Evolution_Calendar_Success)
1689 /* Calculate adds and modifies */
1690 for (i = list; i != NULL; i = g_list_next (i)) {
1694 e_cal_component_get_uid (i->data, &uid);
1695 calobj = e_cal_component_get_as_string (i->data);
1697 g_assert (calobj != NULL);
1699 /* check what type of change has occurred, if any */
1700 switch (e_xmlhash_compare (ehash, uid, calobj)) {
1701 case E_XMLHASH_STATUS_SAME:
1703 case E_XMLHASH_STATUS_NOT_FOUND:
1704 *adds = g_list_prepend (*adds, g_strdup (calobj));
1705 e_xmlhash_add (ehash, uid, calobj);
1707 case E_XMLHASH_STATUS_DIFFERENT:
1708 *modifies = g_list_prepend (*modifies, g_strdup (calobj));
1709 e_xmlhash_add (ehash, uid, calobj);
1716 /* Calculate deletions */
1717 be_data.backend = cbgw;
1718 be_data.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
1719 be_data.deletes = NULL;
1720 be_data.ehash = ehash;
1721 e_xmlhash_foreach_key (ehash, (EXmlHashFunc)e_cal_backend_groupwise_compute_changes_foreach_key, &be_data);
1723 *deletes = be_data.deletes;
1725 e_xmlhash_write (ehash);
1726 e_xmlhash_destroy (ehash);
1728 return GNOME_Evolution_Calendar_Success;
1731 /* Get_changes handler for the groupwise backend */
1732 static ECalBackendSyncStatus
1733 e_cal_backend_groupwise_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id,
1734 GList **adds, GList **modifies, GList **deletes)
1736 ECalBackendGroupwise *cbgw;
1737 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1739 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_InvalidObject);
1740 g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1742 return e_cal_backend_groupwise_compute_changes (cbgw, change_id, adds, modifies, deletes);
1746 /* Discard_alarm handler for the file backend */
1747 static ECalBackendSyncStatus
1748 e_cal_backend_groupwise_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
1750 return GNOME_Evolution_Calendar_OtherError;
1753 static icaltimezone *
1754 e_cal_backend_groupwise_internal_get_default_timezone (ECalBackend *backend)
1756 ECalBackendGroupwise *cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1758 return cbgw->priv->default_zone;
1761 static icaltimezone *
1762 e_cal_backend_groupwise_internal_get_timezone (ECalBackend *backend, const char *tzid)
1766 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1768 return icaltimezone_get_utc_timezone();
1774 sanitize_component (ECalBackendSync *backend, ECalComponent *comp, char *server_uid)
1776 ECalBackendGroupwise *cbgw;
1777 icalproperty *icalprop;
1779 GString *str = g_string_new ("");;
1781 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1784 /* the ID returned by sendItemResponse includes the container ID of the
1785 inbox folder, so we need to replace that with our container ID */
1786 for (i = 0; i < strlen (server_uid); i++) {
1787 str = g_string_append_c (str, server_uid[i]);
1788 if (server_uid[i] == ':') {
1789 str = g_string_append (str, cbgw->priv->container_id);
1794 /* add the extra property to the component */
1795 icalprop = icalproperty_new_x (str->str);
1796 icalproperty_set_x_name (icalprop, "X-GWRECORDID");
1797 icalcomponent_add_property (e_cal_component_get_icalcomponent (comp), icalprop);
1800 g_string_free (str, TRUE);
1803 static ECalBackendSyncStatus
1804 e_cal_backend_groupwise_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
1806 ECalBackendGroupwise *cbgw;
1807 ECalBackendGroupwisePrivate *priv;
1808 icalcomponent *icalcomp;
1809 ECalComponent *comp;
1810 EGwConnectionStatus status;
1811 char *server_uid = NULL;
1812 GSList *uid_list = NULL, *l;
1815 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1818 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_InvalidObject);
1819 g_return_val_if_fail (calobj != NULL && *calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
1821 if (priv->mode == CAL_MODE_LOCAL) {
1823 return GNOME_Evolution_Calendar_RepositoryOffline;
1826 /* check the component for validity */
1827 icalcomp = icalparser_parse_string (*calobj);
1829 return GNOME_Evolution_Calendar_InvalidObject;
1831 if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) != icalcomponent_isa (icalcomp)) {
1832 icalcomponent_free (icalcomp);
1833 return GNOME_Evolution_Calendar_InvalidObject;
1836 comp = e_cal_component_new ();
1837 e_cal_component_set_icalcomponent (comp, icalcomp);
1839 /* check if the object exists */
1840 switch (priv->mode) {
1842 case CAL_MODE_REMOTE :
1843 /* when online, send the item to the server */
1844 status = e_gw_connection_create_appointment (priv->cnc, priv->container_id, cbgw, comp, &uid_list);
1846 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
1847 status = e_gw_connection_create_appointment (priv->cnc, priv->container_id, cbgw, comp, &uid_list);
1849 if (status != E_GW_CONNECTION_STATUS_OK) {
1850 g_object_unref (comp);
1852 if (status == E_GW_CONNECTION_STATUS_UNKNOWN_USER)
1853 return GNOME_Evolution_Calendar_UnknownUser;
1855 return GNOME_Evolution_Calendar_OtherError;
1858 /* If delay deliver has been set, server will not send the uid */
1859 if (!uid_list || ((e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL) && e_cal_component_has_organizer (comp))) {
1861 g_object_unref (comp);
1862 return GNOME_Evolution_Calendar_Success;
1865 if (g_slist_length (uid_list) == 1) {
1866 server_uid = (char *) uid_list->data;
1867 sanitize_component (backend, comp, server_uid);
1868 g_free (server_uid);
1869 /* if successful, update the cache */
1870 e_cal_backend_cache_put_component (priv->cache, comp);
1871 *calobj = e_cal_component_get_as_string (comp);
1873 EGwConnectionStatus stat;
1874 GList *list = NULL, *tmp;
1875 GPtrArray *uid_array = g_ptr_array_new ();
1876 for (l = uid_list; l; l = g_slist_next (l)) {
1877 g_ptr_array_add (uid_array, l->data);
1880 /* convert uid_list to GPtrArray and get the items in a list */
1881 stat = e_gw_connection_get_items_from_ids (priv->cnc,
1883 "attachments recipients message recipientStatus default peek",
1886 if (stat != E_GW_CONNECTION_STATUS_OK || (list == NULL) || (g_list_length (list) == 0)) {
1887 g_ptr_array_free (uid_array, TRUE);
1888 return GNOME_Evolution_Calendar_OtherError;
1891 comp = g_object_ref ( (ECalComponent *) list->data );
1892 /* convert items into components and add them to the cache */
1893 for (i=0, tmp = list; tmp ; tmp = g_list_next (tmp), i++) {
1894 ECalComponent *e_cal_comp;
1897 item = (EGwItem *) tmp->data;
1898 e_cal_comp = e_gw_item_to_cal_component (item, cbgw);
1899 e_cal_component_commit_sequence (e_cal_comp);
1900 sanitize_component (backend, e_cal_comp, g_ptr_array_index (uid_array, i));
1901 e_cal_backend_cache_put_component (priv->cache, e_cal_comp);
1904 *calobj = e_cal_component_get_as_string (e_cal_comp);
1909 temp = e_cal_component_get_as_string (e_cal_comp);
1910 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), temp);
1914 g_object_unref (e_cal_comp);
1916 g_ptr_array_free (uid_array, TRUE);
1923 g_object_unref (comp);
1925 return GNOME_Evolution_Calendar_Success;
1929 get_retract_data (ECalComponent *comp, const char **retract_comment, gboolean *all_instances)
1931 icalcomponent *icalcomp = NULL;
1932 icalproperty *icalprop = NULL;
1933 gboolean is_instance = FALSE;
1934 const char *x_ret = NULL, *x_recur = NULL;
1936 is_instance = e_cal_component_is_instance (comp);
1937 icalcomp = e_cal_component_get_icalcomponent (comp);
1938 icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
1942 x_name = icalproperty_get_x_name (icalprop);
1943 /* This property will be set only if the user is an organizer */
1944 if (!strcmp (x_name, "X-EVOLUTION-RETRACT-COMMENT")) {
1945 x_ret = icalproperty_get_x (icalprop);
1946 if (!strcmp (x_ret, "0")) {
1947 *retract_comment = NULL;
1949 *retract_comment = x_ret;
1952 if (is_instance && !strcmp (x_name, "X-EVOLUTION-RECUR-MOD")) {
1953 x_recur = icalproperty_get_x (icalprop);
1954 if (!strcmp (x_recur, "All"))
1955 *all_instances = TRUE;
1957 *all_instances = FALSE;
1960 if (x_ret && (!is_instance || x_recur))
1962 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
1966 static ECalBackendSyncStatus
1967 e_cal_backend_groupwise_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
1968 CalObjModType mod, char **old_object, char **new_object)
1970 ECalBackendGroupwise *cbgw;
1971 ECalBackendGroupwisePrivate *priv;
1972 icalcomponent *icalcomp;
1973 ECalComponent *comp, *cache_comp = NULL;
1974 EGwConnectionStatus status;
1975 EGwItem *item, *cache_item;
1976 const char *uid = NULL, *rid = NULL;
1979 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1982 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_InvalidObject);
1983 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
1985 if (priv->mode == CAL_MODE_LOCAL) {
1987 return GNOME_Evolution_Calendar_RepositoryOffline;
1990 /* check the component for validity */
1991 icalcomp = icalparser_parse_string (calobj);
1993 return GNOME_Evolution_Calendar_InvalidObject;
1994 comp = e_cal_component_new ();
1995 e_cal_component_set_icalcomponent (comp, icalcomp);
1996 e_cal_component_get_uid (comp, &uid);
1997 rid = e_cal_component_get_recurid_as_string (comp);
1999 /* check if the object exists */
2000 switch (priv->mode) {
2002 case CAL_MODE_REMOTE :
2003 /* when online, send the item to the server */
2004 cache_comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
2006 g_message ("CRITICAL : Could not find the object in cache");
2007 return GNOME_Evolution_Calendar_ObjectNotFound;
2010 if (e_cal_component_has_attendees (comp) &&
2011 e_cal_backend_groupwise_utils_check_delegate (comp, e_gw_connection_get_user_email (priv->cnc))) {
2012 const char *id = NULL, *recur_key = NULL;
2014 item = e_gw_item_new_for_delegate_from_cal (cbgw, comp);
2016 if (mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (comp)) {
2019 id = e_gw_item_get_id (item);
2021 status = e_gw_connection_delegate_request (priv->cnc, item, id, NULL, NULL, recur_key);
2023 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2024 status = e_gw_connection_delegate_request (priv->cnc, item, id, NULL, NULL, recur_key);
2025 if (status != E_GW_CONNECTION_STATUS_OK) {
2026 g_object_unref (comp);
2027 g_object_unref (cache_comp);
2028 return GNOME_Evolution_Calendar_OtherError;
2031 e_cal_backend_cache_put_component (priv->cache, comp);
2032 *new_object = e_cal_component_get_as_string (comp);
2036 item = e_gw_item_new_from_cal_component (priv->container_id, cbgw, comp);
2037 cache_item = e_gw_item_new_from_cal_component (priv->container_id, cbgw, cache_comp);
2038 if ( e_gw_item_get_item_type (item) == E_GW_ITEM_TYPE_TASK) {
2039 gboolean completed, cache_completed;
2041 completed = e_gw_item_get_completed (item);
2042 cache_completed = e_gw_item_get_completed (cache_item);
2043 if (completed && !cache_completed) {
2044 /*FIXME return values. */
2045 status = e_gw_connection_complete_request (priv->cnc, e_gw_item_get_id (item));
2047 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2048 status = e_gw_connection_complete_request (priv->cnc, e_gw_item_get_id (item));
2050 if (status != E_GW_CONNECTION_STATUS_OK) {
2051 g_object_unref (comp);
2052 g_object_unref (cache_comp);
2053 return GNOME_Evolution_Calendar_OtherError;
2055 e_cal_backend_cache_put_component (priv->cache, comp);
2060 e_gw_item_set_changes (item, cache_item);
2062 /* the second argument is redundant */
2063 status = e_gw_connection_modify_item (priv->cnc, e_gw_item_get_id (item), item);
2065 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2066 status = e_gw_connection_modify_item (priv->cnc, e_gw_item_get_id (item), item);
2068 if (status != E_GW_CONNECTION_STATUS_OK) {
2069 g_object_unref (comp);
2070 g_object_unref (cache_comp);
2071 return GNOME_Evolution_Calendar_OtherError;
2073 /* if successful, update the cache */
2075 case CAL_MODE_LOCAL :
2076 /* in offline mode, we just update the cache */
2077 e_cal_backend_cache_put_component (priv->cache, comp);
2084 *old_object = e_cal_component_get_as_string (cache_comp);
2085 g_object_unref (cache_comp);
2086 g_object_unref (comp);
2087 return GNOME_Evolution_Calendar_Success;
2091 get_gw_item_id (icalcomponent *icalcomp)
2093 icalproperty *icalprop;
2095 /* search the component for the X-GWRECORDID property */
2096 icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
2098 const char *x_name, *x_val;
2100 x_name = icalproperty_get_x_name (icalprop);
2101 x_val = icalproperty_get_x (icalprop);
2102 if (!strcmp (x_name, "X-GWRECORDID")) {
2106 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
2111 /* Remove_object handler for the file backend */
2112 static ECalBackendSyncStatus
2113 e_cal_backend_groupwise_remove_object (ECalBackendSync *backend, EDataCal *cal,
2114 const char *uid, const char *rid,
2115 CalObjModType mod, char **old_object,
2118 ECalBackendGroupwise *cbgw;
2119 ECalBackendGroupwisePrivate *priv;
2120 char *calobj = NULL;
2122 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
2125 *old_object = *object = NULL;
2127 /* if online, remove the item from the server */
2128 if (priv->mode == CAL_MODE_REMOTE) {
2129 ECalBackendSyncStatus status;
2130 const char *id_to_remove = NULL;
2131 icalcomponent *icalcomp;
2133 status = e_cal_backend_groupwise_get_object (backend, cal, uid, rid, &calobj);
2134 if (status != GNOME_Evolution_Calendar_Success)
2136 g_message ("object found \n");
2138 icalcomp = icalparser_parse_string (calobj);
2141 return GNOME_Evolution_Calendar_InvalidObject;
2144 if (mod == CALOBJ_MOD_THIS) {
2145 id_to_remove = get_gw_item_id (icalcomp);
2146 if (!id_to_remove) {
2147 /* use the iCalId to remove the object */
2151 /* remove the object */
2152 status = e_gw_connection_remove_item (priv->cnc, priv->container_id, id_to_remove);
2154 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2155 status = e_gw_connection_remove_item (priv->cnc, priv->container_id, id_to_remove);
2157 icalcomponent_free (icalcomp);
2158 if (status == E_GW_CONNECTION_STATUS_OK) {
2159 /* remove the component from the cache */
2160 if (!e_cal_backend_cache_remove_component (priv->cache, uid, rid)) {
2162 return GNOME_Evolution_Calendar_ObjectNotFound;
2165 *old_object = strdup (calobj);
2167 return GNOME_Evolution_Calendar_Success;
2170 return GNOME_Evolution_Calendar_OtherError;
2172 } else if (mod == CALOBJ_MOD_ALL) {
2173 GSList *l, *comp_list = e_cal_backend_cache_get_components_by_uid (priv->cache, uid);
2175 if (e_cal_component_has_attendees (E_CAL_COMPONENT (comp_list->data))) {
2176 /* get recurrence key and send it to
2177 * e_gw_connection_remove_recurrence_item */
2179 id_to_remove = get_gw_item_id (e_cal_component_get_icalcomponent (comp_list->data));
2180 status = e_gw_connection_decline_request (priv->cnc, id_to_remove, NULL, uid);
2181 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2182 status = e_gw_connection_decline_request (priv->cnc, id_to_remove, NULL, uid);
2184 GList *item_ids = NULL;
2186 for (l = comp_list; l; l = l->next) {
2187 ECalComponent *comp = E_CAL_COMPONENT (l->data);
2189 id_to_remove = get_gw_item_id (e_cal_component_get_icalcomponent (comp));
2190 item_ids = g_list_append (item_ids, (char *) id_to_remove);
2192 status = e_gw_connection_remove_items (priv->cnc, priv->container_id, item_ids);
2194 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2195 status = e_gw_connection_remove_items (priv->cnc, priv->container_id, item_ids);
2198 if (status == E_GW_CONNECTION_STATUS_OK) {
2200 for (l = comp_list; l; l = l->next) {
2201 ECalComponent *comp = E_CAL_COMPONENT (l->data);
2202 ECalComponentId *id = e_cal_component_get_id (comp);
2204 e_cal_backend_cache_remove_component (priv->cache, id->uid,
2206 if (!id->rid || !g_str_equal (id->rid, rid))
2207 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), id, e_cal_component_get_as_string (comp), NULL);
2208 e_cal_component_free_id (id);
2210 g_object_unref (comp);
2213 /* Setting NULL would trigger another signal.
2214 * We do not set the *object to NULL */
2215 g_slist_free (comp_list);
2216 *old_object = strdup (calobj);
2219 return GNOME_Evolution_Calendar_Success;
2222 return GNOME_Evolution_Calendar_OtherError;
2225 return GNOME_Evolution_Calendar_UnsupportedMethod;
2226 } else if (priv->mode == CAL_MODE_LOCAL) {
2228 return GNOME_Evolution_Calendar_RepositoryOffline;
2230 return GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED;
2234 /* This function is largely duplicated in
2235 * ../file/e-cal-backend-file.c
2238 fetch_attachments (ECalBackendGroupwise *cbgw, ECalComponent *comp)
2240 GSList *attach_list = NULL, *new_attach_list = NULL;
2243 char *dest_url, *dest_file;
2247 e_cal_component_get_attachment_list (comp, &attach_list);
2248 e_cal_component_get_uid (comp, &uid);
2249 /*FIXME get the uri rather than computing the path */
2250 attach_store = g_strdup (e_cal_backend_groupwise_get_local_attachments_store (cbgw));
2252 for (l = attach_list; l ; l = l->next) {
2253 char *sfname = (char *)l->data;
2254 char *filename, *new_filename;
2255 GMappedFile *mapped_file;
2256 GError *error = NULL;
2258 mapped_file = g_mapped_file_new (sfname, FALSE, &error);
2260 g_message ("DEBUG: could not map %s: %s\n",
2261 sfname, error->message);
2262 g_error_free (error);
2265 filename = g_path_get_basename (sfname);
2266 new_filename = g_strconcat (uid, "-", filename, NULL);
2268 dest_file = g_build_filename (attach_store, new_filename, NULL);
2269 g_free (new_filename);
2270 fd = g_open (dest_file, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
2272 /* TODO handle error conditions */
2273 g_message ("DEBUG: could not open %s for writing\n",
2275 } else if (write (fd, g_mapped_file_get_contents (mapped_file),
2276 g_mapped_file_get_length (mapped_file)) == -1) {
2277 /* TODO handle error condition */
2278 g_message ("DEBUG: attachment write failed.\n");
2281 g_mapped_file_free (mapped_file);
2284 dest_url = g_filename_to_uri (dest_file, NULL, NULL);
2286 new_attach_list = g_slist_append (new_attach_list, dest_url);
2288 g_free (attach_store);
2289 e_cal_component_set_attachment_list (comp, new_attach_list);
2293 change_status (ECalComponent *comp, icalparameter_partstat status, const char *email)
2296 icalparameter *param;
2297 gboolean found = FALSE;
2298 icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
2301 for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
2303 prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
2304 const char *attendee = icalproperty_get_attendee (prop);
2306 if (!g_ascii_strncasecmp (attendee, "mailto:", 7))
2309 if (!g_ascii_strcasecmp (attendee, email)) {
2311 param = icalparameter_new_partstat (status);
2312 icalproperty_set_parameter (prop, param);
2317 /* We couldn find the attendee in the component, so add a new attendee */
2319 char *temp = g_strdup_printf ("MAILTO:%s", email);
2321 prop = icalproperty_new_attendee ((const char *) temp);
2322 icalcomponent_add_property (icalcomp, prop);
2324 param = icalparameter_new_partstat (ICAL_PARTSTAT_DELEGATED);
2325 icalproperty_add_parameter (prop, param);
2327 param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT);
2328 icalproperty_add_parameter (prop, param);
2330 param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
2331 icalproperty_add_parameter (prop, param);
2333 param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
2334 icalproperty_add_parameter (prop, param);
2340 static ECalBackendSyncStatus
2341 receive_object (ECalBackendGroupwise *cbgw, EDataCal *cal, icalcomponent *icalcomp)
2343 ECalComponent *comp, *modif_comp = NULL;
2344 ECalBackendGroupwisePrivate *priv;
2345 icalproperty_method method;
2346 EGwConnectionStatus status;
2347 gboolean all_instances = FALSE;
2348 icalparameter_partstat pstatus;
2349 icalproperty *icalprop;
2353 /* When the icalcomponent is obtained through the itip message rather
2354 * than from the SOAP protocol, the container id has to be explicitly
2355 * added to the xgwrecordid inorder to obtain the item id. */
2356 icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
2360 x_name = icalproperty_get_x_name (icalprop);
2361 if (!strcmp (x_name, "X-GW-RECUR-INSTANCES-MOD-TYPE")) {
2362 if (!strcmp (icalproperty_get_x (icalprop), "All")) {
2363 all_instances = TRUE;
2364 icalcomponent_remove_property (icalcomp, icalprop);
2369 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
2372 comp = e_cal_component_new ();
2373 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
2374 method = icalcomponent_get_method (icalcomp);
2376 /* handle attachments */
2377 if (e_cal_component_has_attachments (comp))
2378 fetch_attachments (cbgw, comp);
2380 status = e_gw_connection_send_appointment (cbgw, priv->container_id, comp, method, all_instances, &modif_comp, &pstatus);
2382 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2383 status = e_gw_connection_send_appointment (cbgw, priv->container_id, comp, method, all_instances, &modif_comp, &pstatus);
2386 modif_comp = g_object_ref (comp);
2388 /* update the cache */
2389 if (status == E_GW_CONNECTION_STATUS_OK || status == E_GW_CONNECTION_STATUS_ITEM_ALREADY_ACCEPTED) {
2390 GSList *comps = NULL, *l;
2391 gboolean found = FALSE;
2393 if (all_instances) {
2396 e_cal_component_get_uid (modif_comp, (const char **) &uid);
2397 comps = e_cal_backend_cache_get_components_by_uid (priv->cache, uid);
2400 comps = g_slist_append (comps, g_object_ref (modif_comp));
2404 ECalComponentId *id = e_cal_component_get_id (modif_comp);
2405 ECalComponent *component = NULL;
2407 component = e_cal_backend_cache_get_component (priv->cache, id->uid, id->rid);
2410 comps = g_slist_append (comps, g_object_ref (modif_comp));
2412 comps = g_slist_append (comps, component);
2416 e_cal_component_free_id (id);
2419 for (l = comps; l != NULL; l = l->next) {
2420 ECalComponent *component = E_CAL_COMPONENT (l->data);
2422 if (pstatus == ICAL_PARTSTAT_DECLINED) {
2423 ECalComponentId *id = e_cal_component_get_id (component);
2425 if (e_cal_backend_cache_remove_component (priv->cache, id->uid, id->rid)) {
2427 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), id, e_cal_component_get_as_string (component), NULL);
2428 e_cal_component_free_id (id);
2433 char *comp_str = NULL;
2435 change_status (component, pstatus, e_gw_connection_get_user_email (priv->cnc));
2436 e_cal_backend_cache_put_component (priv->cache, component);
2437 comp_str = e_cal_component_get_as_string (component);
2440 e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbgw), comp_str, comp_str);
2442 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), comp_str);
2448 g_slist_foreach (comps, (GFunc) g_object_unref, NULL);
2449 g_slist_free (comps);
2450 g_object_unref (comp);
2451 g_object_unref (modif_comp);
2452 return GNOME_Evolution_Calendar_Success;
2456 if (status == E_GW_CONNECTION_STATUS_INVALID_OBJECT) {
2457 g_object_unref (comp);
2458 return GNOME_Evolution_Calendar_InvalidObject;
2460 return GNOME_Evolution_Calendar_OtherError;
2463 /* Update_objects handler for the file backend. */
2464 static ECalBackendSyncStatus
2465 e_cal_backend_groupwise_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
2467 ECalBackendGroupwise *cbgw;
2468 ECalBackendGroupwisePrivate *priv;
2469 icalcomponent *icalcomp, *subcomp;
2470 icalcomponent_kind kind;
2471 ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success;
2473 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
2476 if (priv->mode == CAL_MODE_LOCAL) {
2478 return GNOME_Evolution_Calendar_RepositoryOffline;
2481 icalcomp = icalparser_parse_string (calobj);
2483 return GNOME_Evolution_Calendar_InvalidObject;
2485 kind = icalcomponent_isa (icalcomp);
2486 if (kind == ICAL_VCALENDAR_COMPONENT) {
2487 subcomp = icalcomponent_get_first_component (icalcomp,
2488 e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
2490 icalcomponent_set_method (subcomp, icalcomponent_get_method (icalcomp));
2491 status = receive_object (cbgw, cal, subcomp);
2492 if (status != GNOME_Evolution_Calendar_Success)
2494 subcomp = icalcomponent_get_next_component (icalcomp,
2495 e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
2497 } else if (kind == e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
2498 status = receive_object (cbgw, cal, icalcomp);
2500 status = GNOME_Evolution_Calendar_InvalidObject;
2502 icalcomponent_free (icalcomp);
2507 static ECalBackendSyncStatus
2508 send_object (ECalBackendGroupwise *cbgw, EDataCal *cal, icalcomponent *icalcomp, icalproperty_method method)
2510 ECalComponent *comp, *found_comp = NULL;
2511 ECalBackendGroupwisePrivate *priv;
2512 ECalBackendSyncStatus status = GNOME_Evolution_Calendar_OtherError;
2513 const char *uid = NULL, *rid = NULL;
2517 comp = e_cal_component_new ();
2518 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
2519 rid = e_cal_component_get_recurid_as_string (comp);
2521 e_cal_component_get_uid (comp, (const char **) &uid);
2522 found_comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
2525 g_object_unref (comp);
2526 return GNOME_Evolution_Calendar_ObjectNotFound;
2530 switch (priv->mode) {
2532 case CAL_MODE_REMOTE :
2533 if (method == ICAL_METHOD_CANCEL) {
2534 const char *retract_comment = NULL;
2535 gboolean all_instances = FALSE;
2536 const char *id = NULL;
2538 get_retract_data (comp, &retract_comment, &all_instances);
2539 id = get_gw_item_id (icalcomp);
2540 status = e_gw_connection_retract_request (priv->cnc, id, retract_comment,
2541 all_instances, FALSE);
2543 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2544 status = e_gw_connection_retract_request (priv->cnc, id, retract_comment,
2545 all_instances, FALSE);
2546 if (status == E_GW_CONNECTION_STATUS_OK) {
2547 if (all_instances) {
2548 char *old_object = NULL;
2549 GSList *l, *comp_list = e_cal_backend_cache_get_components_by_uid (priv->cache, uid);
2550 for (l = comp_list; l; l = l->next) {
2551 ECalComponent *component = E_CAL_COMPONENT (l->data);
2552 ECalComponentId *cid = e_cal_component_get_id (component);
2553 char *object = e_cal_component_get_as_string (component);
2555 if (e_cal_backend_cache_remove_component (priv->cache, cid->uid,
2557 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), cid, object, NULL);
2559 e_cal_component_free_id (cid);
2561 g_object_unref (component);
2564 g_slist_free (comp_list);
2565 g_free (old_object);
2567 ECalComponentId *cid;
2570 cid = e_cal_component_get_id (comp);
2571 icalcomp = e_cal_component_get_icalcomponent (comp);
2572 object = e_cal_component_get_as_string (comp);
2574 if (e_cal_backend_cache_remove_component (priv->cache, cid->uid,
2576 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), cid,
2581 e_cal_component_free_id (cid);
2586 case CAL_MODE_LOCAL :
2587 /* in offline mode, we just update the cache */
2588 status = GNOME_Evolution_Calendar_RepositoryOffline;
2594 g_object_unref (comp);
2595 g_object_unref (found_comp);
2600 static ECalBackendSyncStatus
2601 e_cal_backend_groupwise_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj, GList **users,
2602 char **modified_calobj)
2604 ECalBackendSyncStatus status = GNOME_Evolution_Calendar_OtherError;
2605 icalcomponent *icalcomp, *subcomp;
2606 icalcomponent_kind kind;
2607 icalproperty_method method;
2608 ECalBackendGroupwise *cbgw;
2609 ECalBackendGroupwisePrivate *priv;
2612 *modified_calobj = NULL;
2614 cbgw = E_CAL_BACKEND_GROUPWISE (backend);
2617 if (priv->mode == CAL_MODE_LOCAL) {
2619 return GNOME_Evolution_Calendar_RepositoryOffline;
2622 icalcomp = icalparser_parse_string (calobj);
2624 return GNOME_Evolution_Calendar_InvalidObject;
2626 method = icalcomponent_get_method (icalcomp);
2627 kind = icalcomponent_isa (icalcomp);
2628 if (kind == ICAL_VCALENDAR_COMPONENT) {
2629 subcomp = icalcomponent_get_first_component (icalcomp,
2630 e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
2633 status = send_object (cbgw, cal, subcomp, method);
2634 if (status != GNOME_Evolution_Calendar_Success)
2636 subcomp = icalcomponent_get_next_component (icalcomp,
2637 e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
2639 } else if (kind == e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
2640 status = send_object (cbgw, cal, icalcomp, method);
2642 status = GNOME_Evolution_Calendar_InvalidObject;
2644 if (status == GNOME_Evolution_Calendar_Success) {
2645 ECalComponent *comp;
2647 comp = e_cal_component_new ();
2649 if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) {
2650 GSList *attendee_list = NULL, *tmp;
2651 e_cal_component_get_attendee_list (comp, &attendee_list);
2652 /* convert this into GList */
2653 for (tmp = attendee_list; tmp; tmp = g_slist_next (tmp)) {
2654 const char *attendee = tmp->data;
2657 *users = g_list_append (*users, g_strdup (attendee));
2660 g_object_unref (comp);
2662 *modified_calobj = g_strdup (calobj);
2664 icalcomponent_free (icalcomp);
2670 /* Object initialization function for the file backend */
2672 e_cal_backend_groupwise_init (ECalBackendGroupwise *cbgw, ECalBackendGroupwiseClass *class)
2674 ECalBackendGroupwisePrivate *priv;
2676 priv = g_new0 (ECalBackendGroupwisePrivate, 1);
2678 priv->timeout_id = 0;
2680 priv->sendoptions_sync_timeout = 0;
2682 /* create the mutex for thread safety */
2683 priv->mutex = g_mutex_new ();
2687 e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbgw), TRUE);
2690 /* Class initialization function for the gw backend */
2692 e_cal_backend_groupwise_class_init (ECalBackendGroupwiseClass *class)
2694 GObjectClass *object_class;
2695 ECalBackendClass *backend_class;
2696 ECalBackendSyncClass *sync_class;
2698 object_class = (GObjectClass *) class;
2699 backend_class = (ECalBackendClass *) class;
2700 sync_class = (ECalBackendSyncClass *) class;
2702 parent_class = g_type_class_peek_parent (class);
2704 object_class->dispose = e_cal_backend_groupwise_dispose;
2705 object_class->finalize = e_cal_backend_groupwise_finalize;
2707 sync_class->is_read_only_sync = e_cal_backend_groupwise_is_read_only;
2708 sync_class->get_cal_address_sync = e_cal_backend_groupwise_get_cal_address;
2709 sync_class->get_alarm_email_address_sync = e_cal_backend_groupwise_get_alarm_email_address;
2710 sync_class->get_ldap_attribute_sync = e_cal_backend_groupwise_get_ldap_attribute;
2711 sync_class->get_static_capabilities_sync = e_cal_backend_groupwise_get_static_capabilities;
2712 sync_class->open_sync = e_cal_backend_groupwise_open;
2713 sync_class->remove_sync = e_cal_backend_groupwise_remove;
2714 sync_class->create_object_sync = e_cal_backend_groupwise_create_object;
2715 sync_class->modify_object_sync = e_cal_backend_groupwise_modify_object;
2716 sync_class->remove_object_sync = e_cal_backend_groupwise_remove_object;
2717 sync_class->discard_alarm_sync = e_cal_backend_groupwise_discard_alarm;
2718 sync_class->receive_objects_sync = e_cal_backend_groupwise_receive_objects;
2719 sync_class->send_objects_sync = e_cal_backend_groupwise_send_objects;
2720 sync_class->get_default_object_sync = e_cal_backend_groupwise_get_default_object;
2721 sync_class->get_object_sync = e_cal_backend_groupwise_get_object;
2722 sync_class->get_object_list_sync = e_cal_backend_groupwise_get_object_list;
2723 sync_class->get_attachment_list_sync = e_cal_backend_groupwise_get_attachment_list;
2724 sync_class->get_timezone_sync = e_cal_backend_groupwise_get_timezone;
2725 sync_class->add_timezone_sync = e_cal_backend_groupwise_add_timezone;
2726 sync_class->set_default_zone_sync = e_cal_backend_groupwise_set_default_zone;
2727 sync_class->get_freebusy_sync = e_cal_backend_groupwise_get_free_busy;
2728 sync_class->get_changes_sync = e_cal_backend_groupwise_get_changes;
2730 backend_class->is_loaded = e_cal_backend_groupwise_is_loaded;
2731 backend_class->start_query = e_cal_backend_groupwise_start_query;
2732 backend_class->get_mode = e_cal_backend_groupwise_get_mode;
2733 backend_class->set_mode = e_cal_backend_groupwise_set_mode;
2734 backend_class->internal_get_default_timezone = e_cal_backend_groupwise_internal_get_default_timezone;
2735 backend_class->internal_get_timezone = e_cal_backend_groupwise_internal_get_timezone;
2740 * e_cal_backend_groupwise_get_type:
2743 * Registers the #ECalBackendGroupwise class if necessary, and returns the type ID
2746 * Return value: The type ID of the #ECalBackendGroupwise class.
2749 e_cal_backend_groupwise_get_type (void)
2751 static GType e_cal_backend_groupwise_type = 0;
2753 if (!e_cal_backend_groupwise_type) {
2754 static GTypeInfo info = {
2755 sizeof (ECalBackendGroupwiseClass),
2756 (GBaseInitFunc) NULL,
2757 (GBaseFinalizeFunc) NULL,
2758 (GClassInitFunc) e_cal_backend_groupwise_class_init,
2760 sizeof (ECalBackendGroupwise),
2762 (GInstanceInitFunc) e_cal_backend_groupwise_init
2764 e_cal_backend_groupwise_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
2765 "ECalBackendGroupwise", &info, 0);
2768 return e_cal_backend_groupwise_type;
2772 e_cal_backend_groupwise_notify_error_code (ECalBackendGroupwise *cbgw, EGwConnectionStatus status)
2776 g_return_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw));
2778 msg = e_gw_connection_get_error_message (status);
2780 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), msg);
2784 e_cal_backend_groupwise_get_local_attachments_store (ECalBackendGroupwise *cbgw)
2786 g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
2787 return cbgw->priv->local_attachments_store;