Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / backends / groupwise / e-cal-backend-groupwise.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* 
3  * Authors : 
4  *  JP Rosevear <jpr@ximian.com>
5  *  Rodrigo Moya <rodrigo@ximian.com>
6  *  Harish Krishnaswamy <kharish@novell.com>
7  *
8  * Copyright 2003, Novell, Inc.
9  *
10  * This program is free software; you can redistribute it and/or 
11  * modify it under the terms of version 2 of the GNU Lesser General Public 
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.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"
47
48 #ifndef O_BINARY
49 #define O_BINARY 0
50 #endif
51
52 #ifdef G_OS_WIN32
53 /* Undef the similar macro from pthread.h, it doesn't check if
54  * gmtime() returns NULL.
55  */
56 #undef gmtime_r
57
58 /* The gmtime() in Microsoft's C library is MT-safe */
59 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
60 #endif
61
62 /* Private part of the CalBackendGroupwise structure */
63 struct _ECalBackendGroupwisePrivate {
64         /* A mutex to control access to the private structure */
65         GMutex *mutex;
66         EGwConnection *cnc;
67         ECalBackendCache *cache;
68         gboolean read_only;
69         char *uri;
70         char *username;
71         char *password;
72         char *container_id;
73         int timeout_id;
74         CalMode mode;
75         gboolean mode_changed;
76         icaltimezone *default_zone;
77         GHashTable *categories_by_id;
78         GHashTable *categories_by_name;
79
80         /* number of calendar items in the folder */
81         guint32 total_count;
82
83         /* timeout handler for syncing sendoptions */
84         guint sendoptions_sync_timeout;
85         
86         /* fields for storing info while offline */
87         char *user_email;
88         char *local_attachments_store;
89 };
90
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);
99
100 #define PARENT_TYPE E_TYPE_CAL_BACKEND_SYNC
101 static ECalBackendClass *parent_class = NULL;
102
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
107
108 EGwConnection *
109 e_cal_backend_groupwise_get_connection (ECalBackendGroupwise *cbgw) {
110
111         g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
112
113         return cbgw->priv->cnc;
114 }
115
116 GHashTable *
117 e_cal_backend_groupwise_get_categories_by_id (ECalBackendGroupwise *cbgw) {
118         
119         g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
120         
121         return cbgw->priv->categories_by_id;
122 }
123
124 GHashTable *
125 e_cal_backend_groupwise_get_categories_by_name (ECalBackendGroupwise *cbgw) {
126
127         g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
128
129         return cbgw->priv->categories_by_name;
130 }
131
132 icaltimezone *
133 e_cal_backend_groupwise_get_default_zone (ECalBackendGroupwise *cbgw) {
134
135         g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
136
137         return cbgw->priv->default_zone;
138 }
139
140 static GMutex *mutex = NULL;
141
142 static const char * 
143 get_element_type (icalcomponent_kind kind) 
144 {
145         
146         const char *type;
147
148         if (kind == ICAL_VEVENT_COMPONENT)
149                 type = "Appointment";
150         else if (kind == ICAL_VTODO_COMPONENT)
151                 type = "Task";
152         else
153                 type = "Note";
154
155         return type;
156
157 }
158
159 /* Initialy populate the cache from the server */
160 static EGwConnectionStatus
161 populate_cache (ECalBackendGroupwise *cbgw)
162 {
163         ECalBackendGroupwisePrivate *priv;
164         EGwConnectionStatus status;
165         ECalComponent *comp;
166         GList *list = NULL, *l;
167         gboolean done = FALSE,  forward = FALSE;
168         int cursor = 0;
169         guint32 total, num = 0;
170         int percent = 0, i;
171         const char *position = E_GW_CURSOR_POSITION_END; 
172         icalcomponent_kind kind;
173         const char *type;
174         EGwFilter* filter[3];
175         char l_str[26]; 
176         char h_str[26];
177         icaltimetype temp;
178         struct tm tm;
179         time_t h_time, l_time;
180
181         priv = cbgw->priv;
182         kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
183         total = priv->total_count;
184
185         if (!mutex) {
186                 mutex = g_mutex_new ();
187         }
188
189         g_mutex_lock (mutex);
190
191         type = get_element_type (kind); 
192         
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);
196         temp.day -= i;
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);
201         temp.day += (2*i);
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);
206
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);
220
221         for (i = 0; i < 3; i++) {
222                 status = e_gw_connection_create_cursor (priv->cnc,
223                                 priv->container_id, 
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);
228                         return status;
229                 }
230                 done = FALSE;
231                 if (i == 1) {
232                         position = E_GW_CURSOR_POSITION_START;
233                         forward = TRUE;
234
235                 } else {
236                         position = E_GW_CURSOR_POSITION_END;
237                         forward = FALSE;
238                 }
239                         
240                 while (!done) {
241                         
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);
246                                 return status;
247                         }
248                         for (l = list; l != NULL; l = g_list_next(l)) {
249                                 EGwItem *item;
250                                 char *progress_string = NULL;
251                                 
252                                 item = E_GW_ITEM (l->data);
253                                 comp = e_gw_item_to_cal_component (item, cbgw);
254                                 g_object_unref (item);
255                                 
256                                 /* Show the progress information */
257                                 num++;
258                                 percent = ((float) num/total) * 100;
259                         
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 */
262          
263                                 if (percent > 100)
264                                         percent = 99; 
265
266                                 progress_string = g_strdup_printf (_("Loading %s items"), type);
267                                 e_cal_backend_notify_view_progress (E_CAL_BACKEND (cbgw), progress_string, percent);
268                                 
269                                 if (E_IS_CAL_COMPONENT (comp)) {
270                                         char *comp_str;
271                                         
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);
275                                         g_free (comp_str);
276                                         e_cal_backend_cache_put_component (priv->cache, comp);
277                                         g_object_unref (comp);
278                                 }
279                                 g_free (progress_string);
280                         }
281                         
282                         if (!list  || g_list_length (list) == 0)
283                                 done = TRUE;
284                         g_list_free (list);
285                         list = NULL;
286                         position = E_GW_CURSOR_POSITION_CURRENT;
287                 }
288                 e_gw_connection_destroy_cursor (priv->cnc, priv->container_id, cursor);
289                 g_object_unref (filter[i]);
290         }
291         e_cal_backend_notify_view_done (E_CAL_BACKEND (cbgw), GNOME_Evolution_Calendar_Success);
292
293         g_mutex_unlock (mutex);
294         return E_GW_CONNECTION_STATUS_OK;
295 }
296
297 static gboolean
298 compare_prefix (gconstpointer a, gconstpointer b)
299 {
300         return !(g_str_has_prefix ((const char *)a, (const char *)b));
301 }
302
303 static gboolean
304 get_deltas (gpointer handle)
305 {
306         ECalBackendGroupwise *cbgw;
307         ECalBackendGroupwisePrivate *priv;
308         EGwConnection *cnc; 
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;
316         char t_str [26]; 
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 ;
323         
324
325         EGwFilter *filter;
326         int time_interval;
327         icaltimetype temp;
328         gboolean done = FALSE;
329         int cursor = 0;
330         struct tm tm;
331         time_t current_time;
332         gboolean needs_to_get = FALSE;
333
334         if (!handle)
335                 return FALSE;
336         cbgw = (ECalBackendGroupwise *) handle;
337         priv= cbgw->priv;
338         kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
339         cnc = priv->cnc; 
340         cache = priv->cache; 
341         item_list = NULL;
342
343         if (priv->mode == CAL_MODE_LOCAL)
344                 return FALSE;
345
346         attempts = e_cal_backend_cache_get_key_value (cache, key);
347
348         g_static_mutex_lock (&connecting);
349
350         serv_time = e_cal_backend_cache_get_server_utc_time (cache);
351         if (serv_time) {
352                 icaltimetype tmp;
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 (&current_time, &tm);
360                         strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
361                 }
362         } else {
363                 icaltimetype tmp;
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 (&current_time, &tm);
369                 strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
370         }
371         time_string = g_strdup (t_str);
372
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);
378
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);
383
384         if (status != E_GW_CONNECTION_STATUS_OK) {
385
386                 const char *msg = NULL;
387
388                 if (!attempts) {
389                         e_cal_backend_cache_put_key_value (cache, key, "2");
390                 } else {
391                         int failures;
392                         failures = g_ascii_strtod(attempts, NULL) + 1;
393                         e_cal_backend_cache_put_key_value (cache, key, GINT_TO_POINTER (failures));
394                 }
395
396                 if (status == E_GW_CONNECTION_STATUS_NO_RESPONSE) { 
397                         g_static_mutex_unlock (&connecting);
398                         return TRUE;
399                 }
400
401                 msg = e_gw_connection_get_error_message (status);
402
403                 g_static_mutex_unlock (&connecting);
404                 return TRUE;
405         }
406
407         e_file_cache_freeze_changes (E_FILE_CACHE (cache));
408
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;
414                 int r_key;
415
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");
420                         continue;
421                 }
422                 if ((r_key = e_gw_item_get_recurrence_key (item)) != 0)
423                         rid = e_cal_component_get_recurid_as_string (modified_comp);
424                 
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);
429
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);
435
436                 g_object_unref (item);
437                 g_object_unref (modified_comp);
438         }
439         e_file_cache_thaw_changes (E_FILE_CACHE (cache));
440
441         temp = icaltime_from_string (time_string);
442         current_time = icaltime_as_timet_with_zone (temp, icaltimezone_get_utc_timezone ());
443         gmtime_r (&current_time, &tm);
444
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);
449         } 
450         if (attempts) {
451                 tm.tm_min += (time_interval * g_ascii_strtod (attempts, NULL));
452                 e_cal_backend_cache_put_key_value (cache, key, NULL);
453         } else {
454                 tm.tm_min += time_interval;
455         }
456         strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
457         time_string = g_strdup (t_str);
458
459         e_cal_backend_cache_put_server_utc_time (cache, time_string);
460
461         g_free (time_string);
462         time_string = NULL;
463
464         if (item_list) {
465                 g_list_free (item_list);
466                 item_list = NULL;
467         }
468         
469         /* handle deleted items here by going over the entire cache and
470          * checking for deleted items.*/
471         position = E_GW_CURSOR_POSITION_END;
472         cursor = 0;
473         filter = e_gw_filter_new ();
474         e_gw_filter_add_filter_component (filter, E_GW_FILTER_OP_EQUAL, "@type", get_element_type (kind));
475
476         status = e_gw_connection_create_cursor (cnc, cbgw->priv->container_id, "id iCalId recurrenceKey startDate", filter, &cursor);
477         g_object_unref (filter);
478
479         if (status != E_GW_CONNECTION_STATUS_OK) {
480                 if (status == E_GW_CONNECTION_STATUS_NO_RESPONSE) {
481                         g_static_mutex_unlock (&connecting);
482                         return TRUE;
483                 }
484
485                 e_cal_backend_groupwise_notify_error_code (cbgw, status);
486                 g_static_mutex_unlock (&connecting);
487                 return TRUE;
488         }
489
490         cache_keys = e_cal_backend_cache_get_keys (cache);
491         
492         done = FALSE;
493         while (!done) {
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);
498                                 return TRUE;
499                         }
500                         e_cal_backend_groupwise_notify_error_code (cbgw, status);
501                         g_static_mutex_unlock (&connecting);
502                         return TRUE;
503                 }
504                 
505                 if (!item_list  || g_list_length (item_list) == 0)
506                         done = TRUE;
507                 else {
508                         if (!total_list)
509                                 total_list = item_list;
510                         else
511                                 total_list = g_list_concat (total_list, item_list);
512                 }
513
514                 item_list = NULL;
515
516                 position = E_GW_CURSOR_POSITION_CURRENT;
517
518         }
519         e_gw_connection_destroy_cursor (cnc, cbgw->priv->container_id, cursor);
520         e_file_cache_freeze_changes (E_FILE_CACHE (cache));
521         
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;
529
530                 if (calid->recur_key && calid->ical_id) {
531                         const char *rid = NULL;
532                         icaltimetype tt = icaltime_from_string (calid->ical_id);
533                         if (!tt.is_date) {
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);
537                         } else
538                                 rid = calid->ical_id;
539                         real_key = g_strconcat (calid->recur_key, "@", rid, NULL);
540                 }
541                 
542                 if (!calid->recur_key || real_key) {
543                         recur_key = real_key;
544                         func = (GCompareFunc) strcmp;
545                 } else {
546                         recur_key = calid->recur_key;
547                         func = (GCompareFunc) compare_prefix;
548                 }
549
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));
553                         needs_to_get = TRUE;
554                 } else  {
555                         cache_keys = g_slist_remove_link (cache_keys, remove);
556                 }
557         
558                 g_free (real_key);
559         }
560
561         for (ls = cache_keys; ls ; ls = g_slist_next (ls)) {
562                 ECalComponent *comp = NULL;
563                 icalcomponent *icalcomp = NULL;
564                 
565
566                 comp = e_cal_backend_cache_get_component (cache, (const char *) ls->data, NULL);        
567
568                 if (!comp)
569                         continue;
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);
574                         
575                         comp_str = e_cal_component_get_as_string (comp);
576                         e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), 
577                                         id, comp_str, NULL);
578                         e_cal_backend_cache_remove_component (cache, (const char *) id->uid, id->rid);
579
580                         e_cal_component_free_id (id);
581                         g_free (comp_str);
582                 }
583                 g_object_unref (comp);
584         }
585
586         if (needs_to_get) {
587                 e_gw_connection_get_items_from_ids (priv->cnc,
588                                 priv->container_id, 
589                                 "attachments recipients message recipientStatus default peek",
590                                 uid_array, &item_list);
591
592                 for (l = item_list; l != NULL; l = l->next) {
593                         ECalComponent *comp = NULL;
594                         EGwItem *item = NULL;
595                         char *tmp = NULL;
596
597                         item = (EGwItem *) l->data;
598                         comp = e_gw_item_to_cal_component (item, cbgw); 
599                         if (comp) {
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);
603
604
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);
608                                         g_free (tmp);
609                                 }
610                                 g_object_unref (comp);
611                         }
612
613                         g_object_unref (item);
614                 }
615         } 
616
617         e_file_cache_thaw_changes (E_FILE_CACHE (cache));
618
619         g_ptr_array_free (uid_array, TRUE);
620         
621         if (item_list) {
622                 g_list_free (item_list);
623                 item_list = NULL;
624         }
625
626         if (total_list) {
627                 g_list_foreach (total_list, (GFunc) e_gw_item_free_cal_id, NULL);
628                 g_list_free (total_list);
629         }
630         
631         if (cache_keys) {
632                 /*FIXME this is a memory leak, but free'ing it causes crash in gslice */
633 //              g_slist_free (cache_keys);
634         }
635         
636         g_static_mutex_unlock (&connecting);
637         return TRUE;        
638 }
639
640 static gboolean
641 get_deltas_timeout (gpointer cbgw)
642 {
643         GThread *thread;
644         GError *error = NULL;
645
646         if (!cbgw)
647                 return FALSE;
648
649         thread = g_thread_create ((GThreadFunc) get_deltas, cbgw, FALSE, &error);
650         if (!thread) {
651                 g_warning (G_STRLOC ": %s", error->message);
652                 g_error_free (error);
653         }
654
655         return TRUE;
656 }
657
658
659 static char* 
660 form_uri (ESource *source)
661 {
662         char *uri;
663         const char *port;
664         char *formed_uri;
665         const char *use_ssl;
666         
667         EUri *parsed_uri;
668
669         uri = e_source_get_uri (source);
670         if (uri == NULL)
671                 return NULL;
672
673         parsed_uri = e_uri_new (uri);
674         if (parsed_uri == NULL)
675                 return NULL;
676
677         port = e_source_get_property (source, "port");
678         if (port == NULL)
679                 port = "7191";
680         use_ssl = e_source_get_property (source, "use_ssl");
681
682         if (use_ssl && !g_str_equal (use_ssl, "never"))
683                 formed_uri = g_strconcat ("https://", parsed_uri->host,":", port, "/soap", NULL );
684         else 
685                 formed_uri = g_strconcat ("http://", parsed_uri->host,":", port, "/soap", NULL );
686         
687         g_free (uri);
688         e_uri_free (parsed_uri);
689         return formed_uri;
690
691 }
692                                 
693 static ECalBackendSyncStatus
694 cache_init (ECalBackendGroupwise *cbgw)
695 {
696         ECalBackendGroupwisePrivate *priv = cbgw->priv;
697         EGwConnectionStatus cnc_status;
698         icalcomponent_kind kind;
699         EGwSendOptions *opts;
700         const char *time_interval_string;
701         int time_interval;
702
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); 
709                 
710         }
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);
714
715                 hold->cbgw = cbgw;
716                 hold->opts = opts;
717                 
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);
721         } else 
722                 g_warning (G_STRLOC ": Could not get the settings from the server");
723         
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");
728         }
729
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;
741                 } else {
742                         char *utc_str;
743                         
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);
747
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;
751
752                         return GNOME_Evolution_Calendar_Success;
753                 }
754
755         } else {
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;
761                 } else {
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;       
765                 }
766         }
767 }
768
769 static ECalBackendSyncStatus
770 set_container_id_with_count (ECalBackendGroupwise *cbgw) 
771 {
772         ECalBackendGroupwisePrivate *priv;
773         GList *container_list = NULL, *l;
774         EGwConnectionStatus status;
775         icalcomponent_kind kind;
776         ECalBackendSyncStatus res;
777
778         priv = cbgw->priv;
779
780         kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
781         
782         switch (kind) {
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"));
787                 break;
788         default:
789                 priv->container_id = NULL;
790                 return GNOME_Evolution_Calendar_UnsupportedMethod;
791         }
792         
793         status = e_gw_connection_get_container_list (priv->cnc, "folders", &container_list);
794         
795         if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
796                 status = e_gw_connection_get_container_list (priv->cnc, "folders", &container_list);
797         
798         if (status != E_GW_CONNECTION_STATUS_OK)
799                 return GNOME_Evolution_Calendar_OtherError;
800
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);
805                 
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;
810                         break;
811                 }
812         }
813
814         e_gw_connection_free_container_list (container_list);
815
816         return res;
817 }
818         
819 static ECalBackendSyncStatus
820 connect_to_server (ECalBackendGroupwise *cbgw)
821 {
822         char *real_uri;
823         ECalBackendGroupwisePrivate *priv;
824         ESource *source;
825         ECalSourceType source_type;
826         const char *use_ssl;
827         char *http_uri;
828         int permissions;
829         GThread *thread;
830         GError *error = NULL;
831         char *parent_user = NULL;
832         icalcomponent_kind kind;
833         
834         priv = cbgw->priv;
835
836         source = e_cal_backend_get_source (E_CAL_BACKEND (cbgw));
837         real_uri = NULL;
838         if (source)
839                 real_uri = form_uri (source);
840         use_ssl = e_source_get_property (source, "use_ssl");
841
842         if (!real_uri) {
843                 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Invalid server URI"));
844                 return GNOME_Evolution_Calendar_NoSuchCal;
845         } 
846
847         kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbgw));
848
849         parent_user = (char *) e_source_get_property (source, "parent_id_name");
850         /* create connection to server */
851         if (parent_user) {
852                 EGwConnection *cnc;
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);
858                         g_free (http_uri);
859                 }
860
861                 if (!cnc) {
862                         e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Authentication failed"));
863                         return GNOME_Evolution_Calendar_AuthenticationFailed;
864                 }
865                         
866                 priv->cnc = e_gw_connection_get_proxy_connection (cnc, parent_user, priv->password, priv->username, &permissions);
867
868                 g_object_unref(cnc);
869         
870                 if (!priv->cnc) {
871                         e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Authentication failed"));
872                         return GNOME_Evolution_Calendar_AuthenticationFailed;
873                 }
874
875
876                 cbgw->priv->read_only = TRUE;
877         
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;
884
885         } else {
886
887                 priv->cnc = e_gw_connection_new (
888                                 real_uri,
889                                 priv->username,
890                                 priv->password);
891
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);
895                         g_free (http_uri);
896                 }
897                 cbgw->priv->read_only = FALSE;
898         }
899         g_free (real_uri);
900                 
901         if (priv->cnc && priv->cache && priv->container_id) {
902                 char *utc_str;
903                 priv->mode = CAL_MODE_REMOTE;
904                 if (priv->mode_changed && !priv->timeout_id ) {
905                         GThread *thread1;
906                         priv->mode_changed = FALSE;
907
908                         thread1 = g_thread_create ((GThreadFunc) get_deltas, cbgw, FALSE, &error);
909                         if (!thread1) {
910                                 g_warning (G_STRLOC ": %s", error->message);
911                                 g_error_free (error);
912
913                                 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create thread for getting deltas"));
914                                 return GNOME_Evolution_Calendar_OtherError;
915                         }
916                         priv->timeout_id = g_timeout_add (CACHE_REFRESH_INTERVAL, (GSourceFunc) get_deltas_timeout, (gpointer)cbgw);
917                 }
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);
920
921                 return GNOME_Evolution_Calendar_Success;
922         }
923         priv->mode_changed = FALSE;
924
925         switch (kind) {
926         case ICAL_VEVENT_COMPONENT:
927                 source_type = E_CAL_SOURCE_TYPE_EVENT;
928                 break;
929         case ICAL_VTODO_COMPONENT:
930                 source_type = E_CAL_SOURCE_TYPE_TODO;
931                 break;
932         case ICAL_VJOURNAL_COMPONENT:
933                 source_type = E_CAL_SOURCE_TYPE_JOURNAL;
934                 break;
935         default:
936                 source_type = E_CAL_SOURCE_TYPE_EVENT;
937
938         }
939
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);
945                 
946                 if ((status = set_container_id_with_count (cbgw)) != GNOME_Evolution_Calendar_Success) {
947                         return status;
948                 }
949         
950                 priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbgw)), source_type);
951                 if (!priv->cache) {
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;
955                 }
956
957                 e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
958
959                 /* spawn a new thread for opening the calendar */
960                 thread = g_thread_create ((GThreadFunc) cache_init, cbgw, FALSE, &error);
961                 if (!thread) {
962                         g_warning (G_STRLOC ": %s", error->message);
963                         g_error_free (error);
964
965                         e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create thread for populating cache"));
966                         return GNOME_Evolution_Calendar_OtherError;
967                 } 
968
969
970         } else {
971                 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Authentication failed"));
972                 return GNOME_Evolution_Calendar_AuthenticationFailed;
973         }
974
975         if (!e_gw_connection_get_version (priv->cnc)) 
976                 return GNOME_Evolution_Calendar_InvalidServerVersion;
977
978         return GNOME_Evolution_Calendar_Success;
979 }
980
981 /* Dispose handler for the file backend */
982 static void
983 e_cal_backend_groupwise_dispose (GObject *object)
984 {
985         ECalBackendGroupwise *cbgw;
986         ECalBackendGroupwisePrivate *priv;
987
988         cbgw = E_CAL_BACKEND_GROUPWISE (object);
989         priv = cbgw->priv;
990
991         if (G_OBJECT_CLASS (parent_class)->dispose)
992                 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
993 }
994
995 /* Finalize handler for the file backend */
996 static void
997 e_cal_backend_groupwise_finalize (GObject *object)
998 {
999         ECalBackendGroupwise *cbgw;
1000         ECalBackendGroupwisePrivate *priv;
1001
1002         g_return_if_fail (object != NULL);
1003         g_return_if_fail (E_IS_CAL_BACKEND_GROUPWISE (object));
1004
1005         cbgw = E_CAL_BACKEND_GROUPWISE (object);
1006         priv = cbgw->priv;
1007
1008         /* Clean up */
1009         if (priv->mutex) {
1010                 g_mutex_free (priv->mutex);
1011                 priv->mutex = NULL;
1012         }
1013
1014         if (priv->cnc) {
1015                 g_object_unref (priv->cnc);
1016                 priv->cnc = NULL;
1017         }
1018
1019         if (priv->cache) {
1020                 g_object_unref (priv->cache);
1021                 priv->cache = NULL;
1022         }
1023
1024         if (priv->username) {
1025                 g_free (priv->username);
1026                 priv->username = NULL;
1027         }
1028
1029         if (priv->password) {
1030                 g_free (priv->password);
1031                 priv->password = NULL;
1032         }
1033
1034         if (priv->container_id) {
1035                 g_free (priv->container_id);
1036                 priv->container_id = NULL;
1037         }
1038
1039         if (priv->user_email) {
1040                 g_free (priv->user_email);
1041                 priv->user_email = NULL;
1042         }
1043
1044         if (priv->local_attachments_store) {
1045                 g_free (priv->local_attachments_store);
1046                 priv->local_attachments_store = NULL;
1047         }
1048
1049         if (priv->timeout_id) {
1050                 g_source_remove (priv->timeout_id);
1051                 priv->timeout_id = 0;
1052         }
1053
1054         if (priv->sendoptions_sync_timeout) {
1055                 g_source_remove (priv->sendoptions_sync_timeout);
1056                 priv->sendoptions_sync_timeout = 0;
1057         }
1058
1059         if (priv->default_zone) {
1060                 icaltimezone_free (priv->default_zone, 1);
1061                 priv->default_zone = NULL;
1062         }
1063
1064         g_free (priv);
1065         cbgw->priv = NULL;
1066
1067         if (G_OBJECT_CLASS (parent_class)->finalize)
1068                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
1069 }
1070
1071 /* Calendar backend methods */
1072
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)
1076 {
1077         ECalBackendGroupwise *cbgw;
1078         
1079         cbgw = E_CAL_BACKEND_GROUPWISE(backend);
1080         *read_only = cbgw->priv->read_only;
1081         
1082         return GNOME_Evolution_Calendar_Success;
1083 }
1084
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)
1088 {
1089         ECalBackendGroupwise *cbgw;
1090         ECalBackendGroupwisePrivate *priv;
1091         
1092         cbgw = E_CAL_BACKEND_GROUPWISE(backend);
1093         priv = cbgw->priv;
1094
1095         if (priv->mode == CAL_MODE_REMOTE) {
1096                 if (priv->user_email)
1097                         g_free (priv->user_email);
1098
1099                 priv->user_email = g_strdup (e_gw_connection_get_user_email (cbgw->priv->cnc));
1100         }
1101
1102         *address = g_strdup (priv->user_email);
1103         
1104         return GNOME_Evolution_Calendar_Success;
1105 }
1106
1107 static ECalBackendSyncStatus
1108 e_cal_backend_groupwise_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
1109 {
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 */
1112         
1113         *attribute = NULL;
1114         
1115         return GNOME_Evolution_Calendar_Success;
1116 }
1117
1118 static ECalBackendSyncStatus
1119 e_cal_backend_groupwise_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
1120 {
1121         /*group wise does not support email based alarms */
1122         
1123         *address = NULL;
1124         
1125         return GNOME_Evolution_Calendar_Success;
1126 }
1127
1128 static ECalBackendSyncStatus
1129 e_cal_backend_groupwise_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
1130 {
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);
1147
1148         return GNOME_Evolution_Calendar_Success;
1149 }
1150
1151
1152 static void
1153 in_offline (ECalBackendGroupwise *cbgw) {
1154         ECalBackendGroupwisePrivate *priv;
1155
1156         priv= cbgw->priv;
1157         priv->read_only = TRUE;
1158
1159         if (priv->timeout_id) { 
1160                 g_source_remove (priv->timeout_id);
1161                 priv->timeout_id =0;
1162         }       
1163         
1164         if (priv->cnc) {
1165                 g_object_unref (priv->cnc);
1166                 priv->cnc = NULL;
1167         }
1168
1169
1170 }
1171
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)
1176 {
1177         ECalBackendGroupwise *cbgw;
1178         ECalBackendGroupwisePrivate *priv;
1179         ECalBackendSyncStatus status;
1180         ECalSourceType source_type;
1181         char *source = NULL;
1182         char *filename;
1183         char *mangled_uri;
1184         int i;
1185         
1186         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1187         priv = cbgw->priv;
1188
1189         g_mutex_lock (priv->mutex);
1190
1191         cbgw->priv->read_only = FALSE;
1192
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";
1197                 break;
1198         case ICAL_VTODO_COMPONENT:
1199                 source_type = E_CAL_SOURCE_TYPE_TODO;
1200                 source = "tasks";
1201                 break;
1202         case ICAL_VJOURNAL_COMPONENT:
1203                 source_type = E_CAL_SOURCE_TYPE_JOURNAL;
1204                 source = "journal";
1205                 break;
1206         default:
1207                 source_type = E_CAL_SOURCE_TYPE_EVENT;
1208         }
1209
1210         if (priv->mode == CAL_MODE_LOCAL) {
1211                 ESource *esource;
1212                 const char *display_contents = NULL;
1213                         
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");
1217                 
1218                 if (!display_contents || !g_str_equal (display_contents, "1")) {
1219                         g_mutex_unlock (priv->mutex);   
1220                         return GNOME_Evolution_Calendar_RepositoryOffline;
1221                 }
1222
1223                 if (!priv->cache) {
1224                         priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (cbgw)), source_type);
1225                         if (!priv->cache) {
1226                                 g_mutex_unlock (priv->mutex);
1227                                 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), _("Could not create cache file"));
1228                                 
1229                                 return GNOME_Evolution_Calendar_OtherError;
1230                         }
1231                 }
1232                 
1233                 e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
1234
1235                 g_mutex_unlock (priv->mutex);   
1236                 return GNOME_Evolution_Calendar_Success;
1237         }
1238
1239         priv->username = g_strdup (username);
1240         priv->password = g_strdup (password);
1241
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]) {
1247                 case ':' :
1248                 case '/' :
1249                         mangled_uri[i] = '_';
1250                 }
1251         }
1252
1253         filename = g_build_filename (g_get_home_dir (),
1254                                      ".evolution/cache/", source,
1255                                      mangled_uri,
1256                                      NULL);
1257         g_free (mangled_uri);
1258         priv->local_attachments_store = 
1259                 g_filename_to_uri (filename, NULL, NULL);
1260         g_free (filename);
1261
1262         /* FIXME: no need to set it online here when we implement the online/offline stuff correctly */
1263         status = connect_to_server (cbgw);
1264
1265         g_mutex_unlock (priv->mutex);
1266         return status;
1267 }
1268
1269 static ECalBackendSyncStatus
1270 e_cal_backend_groupwise_remove (ECalBackendSync *backend, EDataCal *cal)
1271 {
1272         ECalBackendGroupwise *cbgw;
1273         ECalBackendGroupwisePrivate *priv;
1274         
1275         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1276         priv = cbgw->priv;
1277
1278         g_mutex_lock (priv->mutex);
1279
1280         /* remove the cache */
1281         if (priv->cache)
1282                 e_file_cache_remove (E_FILE_CACHE (priv->cache));
1283
1284         g_mutex_unlock (priv->mutex);
1285
1286         return GNOME_Evolution_Calendar_Success;
1287 }
1288
1289 /* is_loaded handler for the file backend */
1290 static gboolean
1291 e_cal_backend_groupwise_is_loaded (ECalBackend *backend)
1292 {
1293         ECalBackendGroupwise *cbgw;
1294         ECalBackendGroupwisePrivate *priv;
1295
1296         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1297         priv = cbgw->priv;
1298
1299         return priv->cache ? TRUE : FALSE;
1300 }
1301
1302 /* is_remote handler for the file backend */
1303 static CalMode
1304 e_cal_backend_groupwise_get_mode (ECalBackend *backend)
1305 {
1306         ECalBackendGroupwise *cbgw;
1307         ECalBackendGroupwisePrivate *priv;
1308
1309         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1310         priv = cbgw->priv;
1311
1312         return priv->mode;
1313 }
1314
1315 /* Set_mode handler for the file backend */
1316 static void
1317 e_cal_backend_groupwise_set_mode (ECalBackend *backend, CalMode mode)
1318 {
1319         ECalBackendGroupwise *cbgw;
1320         ECalBackendGroupwisePrivate *priv;
1321
1322         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1323         priv = cbgw->priv;
1324
1325         if (priv->mode == mode) {
1326                 e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_SET,
1327                                            cal_mode_to_corba (mode));
1328                 return;
1329         }
1330
1331         g_mutex_lock (priv->mutex);
1332         
1333         priv->mode_changed = TRUE;
1334         switch (mode) {
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);      
1343                 break;
1344
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;
1348                 in_offline (cbgw);
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);
1352
1353                 break;
1354         default :
1355                 e_cal_backend_notify_mode (backend, GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
1356                                            cal_mode_to_corba (mode));
1357         }
1358
1359         g_mutex_unlock (priv->mutex);
1360 }
1361
1362 static ECalBackendSyncStatus
1363 e_cal_backend_groupwise_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
1364 {
1365         
1366         ECalComponent *comp;
1367         
1368         comp = e_cal_component_new ();
1369
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);
1373                 break;
1374         case ICAL_VTODO_COMPONENT:
1375                 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1376                 break;
1377         default:
1378                 g_object_unref (comp);
1379                 return GNOME_Evolution_Calendar_ObjectNotFound;
1380         }
1381
1382         *object = e_cal_component_get_as_string (comp);
1383         g_object_unref (comp);
1384
1385         return GNOME_Evolution_Calendar_Success;
1386 }
1387
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)
1391 {
1392         ECalComponent *comp;
1393         ECalBackendGroupwisePrivate *priv;
1394         ECalBackendGroupwise *cbgw = (ECalBackendGroupwise *) backend;
1395
1396         g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), GNOME_Evolution_Calendar_OtherError);
1397
1398         priv = cbgw->priv;
1399
1400         g_mutex_lock (priv->mutex);
1401
1402         /* search the object in the cache */
1403         comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
1404         if (comp) {
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);
1409                 else
1410                         *object = NULL;
1411
1412                 g_object_unref (comp);
1413
1414                 return *object ? GNOME_Evolution_Calendar_Success : GNOME_Evolution_Calendar_ObjectNotFound;
1415         }
1416
1417         g_mutex_unlock (priv->mutex);
1418
1419         /* callers will never have a uuid that is in server but not in cache */
1420         return GNOME_Evolution_Calendar_ObjectNotFound;
1421 }
1422
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)
1426 {
1427         ECalBackendGroupwise *cbgw;
1428         ECalBackendGroupwisePrivate *priv;
1429         icaltimezone *zone;
1430         icalcomponent *icalcomp;
1431
1432         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1433         priv = cbgw->priv;
1434
1435         g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1436
1437         if (!strcmp (tzid, "UTC")) {
1438                 zone = icaltimezone_get_utc_timezone ();
1439         } else {
1440                 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1441                 if (!zone)
1442                         return GNOME_Evolution_Calendar_ObjectNotFound;
1443         }
1444
1445         icalcomp = icaltimezone_get_component (zone);
1446         if (!icalcomp)
1447                 return GNOME_Evolution_Calendar_InvalidObject;
1448                                                       
1449         *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1450
1451         return GNOME_Evolution_Calendar_Success;
1452 }
1453
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)
1457 {
1458         icalcomponent *tz_comp;
1459         ECalBackendGroupwise *cbgw;
1460         ECalBackendGroupwisePrivate *priv;
1461
1462         cbgw = (ECalBackendGroupwise *) backend;
1463
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);
1466
1467         priv = cbgw->priv;
1468
1469         tz_comp = icalparser_parse_string (tzobj);
1470         if (!tz_comp)
1471                 return GNOME_Evolution_Calendar_InvalidObject;
1472
1473         if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
1474                 icaltimezone *zone;
1475
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;
1481                 }
1482                 icaltimezone_free (zone, 1);
1483         }
1484         return GNOME_Evolution_Calendar_Success;
1485 }
1486
1487 static ECalBackendSyncStatus
1488 e_cal_backend_groupwise_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
1489 {
1490         icalcomponent *tz_comp;
1491         ECalBackendGroupwise *cbgw;
1492         ECalBackendGroupwisePrivate *priv;
1493         icaltimezone *zone;
1494
1495         cbgw = (ECalBackendGroupwise *) backend;
1496
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);
1499
1500         priv = cbgw->priv;
1501
1502         tz_comp = icalparser_parse_string (tzobj);
1503         if (!tz_comp)
1504                 return GNOME_Evolution_Calendar_InvalidObject;
1505
1506         zone = icaltimezone_new ();
1507         icaltimezone_set_component (zone, tz_comp);
1508
1509         if (priv->default_zone)
1510                 icaltimezone_free (priv->default_zone, 1);
1511
1512         /* Set the default timezone to it. */
1513         priv->default_zone = zone;
1514
1515         return GNOME_Evolution_Calendar_Success;
1516 }
1517
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)
1521 {
1522         /* TODO implement the function */
1523         return GNOME_Evolution_Calendar_Success;
1524 }
1525
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)
1529 {
1530         ECalBackendGroupwise *cbgw;
1531         ECalBackendGroupwisePrivate *priv;
1532         GList *components, *l;
1533         ECalBackendSExp *cbsexp;
1534         gboolean search_needed = TRUE;
1535         
1536         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1537         priv = cbgw->priv;
1538
1539         g_mutex_lock (priv->mutex);
1540
1541         g_message (G_STRLOC ": Getting object list (%s)", sexp);
1542
1543         if (!strcmp (sexp, "#t"))
1544                 search_needed = FALSE;
1545
1546         cbsexp = e_cal_backend_sexp_new (sexp);
1547         if (!cbsexp) {
1548                 g_mutex_unlock (priv->mutex);
1549                 return GNOME_Evolution_Calendar_InvalidQuery;
1550         }
1551
1552         *objects = NULL;
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);
1556
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));
1562                         }
1563                 }
1564         }
1565
1566         g_object_unref (cbsexp);
1567         g_list_foreach (components, (GFunc) g_object_unref, NULL);
1568         g_list_free (components);
1569
1570         g_mutex_unlock (priv->mutex);
1571
1572         return GNOME_Evolution_Calendar_Success;
1573 }
1574
1575 /* get_query handler for the groupwise backend */
1576 static void
1577 e_cal_backend_groupwise_start_query (ECalBackend *backend, EDataCalView *query)
1578 {
1579         ECalBackendSyncStatus status;
1580         ECalBackendGroupwise *cbgw;
1581         ECalBackendGroupwisePrivate *priv;
1582         GList *objects = NULL;
1583
1584         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1585         priv = cbgw->priv;
1586
1587         g_message (G_STRLOC ": Starting query (%s)", e_data_cal_view_get_text (query));
1588
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);
1593                 return;
1594         }
1595
1596         /* notify listeners of all objects */
1597         if (objects) {
1598                 e_data_cal_view_notify_objects_added (query, (const GList *) objects);
1599
1600                 /* free memory */
1601                 g_list_foreach (objects, (GFunc) g_free, NULL);
1602                 g_list_free (objects);
1603         }
1604
1605         e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
1606 }
1607
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)
1612 {
1613        EGwConnectionStatus status;
1614        ECalBackendGroupwise *cbgw;
1615        EGwConnection *cnc;
1616
1617        cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1618        cnc = cbgw->priv->cnc;
1619
1620        if (cbgw->priv->mode == CAL_MODE_LOCAL) {
1621                in_offline (cbgw);
1622                return GNOME_Evolution_Calendar_RepositoryOffline;
1623        }
1624
1625        status = e_gw_connection_get_freebusy_info (cnc, users, start, end, freebusy, cbgw->priv->default_zone);
1626
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);
1629
1630        if (status != E_GW_CONNECTION_STATUS_OK)
1631                return GNOME_Evolution_Calendar_OtherError;
1632        return GNOME_Evolution_Calendar_Success; 
1633 }
1634
1635 typedef struct {
1636         ECalBackendGroupwise *backend;
1637         icalcomponent_kind kind;
1638         GList *deletes;
1639         EXmlHash *ehash;
1640 } ECalBackendGroupwiseComputeChangesData;
1641
1642 static void
1643 e_cal_backend_groupwise_compute_changes_foreach_key (const char *key, const char *value, gpointer data)
1644 {
1645         ECalBackendGroupwiseComputeChangesData *be_data = data;
1646                 
1647         if (!e_cal_backend_cache_get_component (be_data->backend->priv->cache, key, NULL)) {
1648                 ECalComponent *comp;
1649
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);
1653                 else
1654                         e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1655
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));
1658
1659                 e_xmlhash_remove (be_data->ehash, key);
1660                 g_object_unref (comp);
1661         }
1662 }
1663
1664 static ECalBackendSyncStatus
1665 e_cal_backend_groupwise_compute_changes (ECalBackendGroupwise *cbgw, const char *change_id,
1666                                          GList **adds, GList **modifies, GList **deletes)
1667 {
1668         ECalBackendSyncStatus status;
1669         ECalBackendCache *cache;
1670         char    *filename;
1671         EXmlHash *ehash;
1672         ECalBackendGroupwiseComputeChangesData be_data;
1673         GList *i, *list = NULL;
1674         gchar *unescaped_uri;
1675
1676         cache = cbgw->priv->cache;
1677
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);
1682         g_free (filename);
1683         g_free (unescaped_uri);
1684
1685         status = e_cal_backend_groupwise_get_object_list (E_CAL_BACKEND_SYNC (cbgw), NULL, NULL, &list);
1686         if (status != GNOME_Evolution_Calendar_Success)
1687                 return status;
1688         
1689         /* Calculate adds and modifies */
1690         for (i = list; i != NULL; i = g_list_next (i)) {
1691                 const char *uid;
1692                 char *calobj;
1693
1694                 e_cal_component_get_uid (i->data, &uid);
1695                 calobj = e_cal_component_get_as_string (i->data);
1696
1697                 g_assert (calobj != NULL);
1698
1699                 /* check what type of change has occurred, if any */
1700                 switch (e_xmlhash_compare (ehash, uid, calobj)) {
1701                 case E_XMLHASH_STATUS_SAME:
1702                         break;
1703                 case E_XMLHASH_STATUS_NOT_FOUND:
1704                         *adds = g_list_prepend (*adds, g_strdup (calobj));
1705                         e_xmlhash_add (ehash, uid, calobj);
1706                         break;
1707                 case E_XMLHASH_STATUS_DIFFERENT:
1708                         *modifies = g_list_prepend (*modifies, g_strdup (calobj));
1709                         e_xmlhash_add (ehash, uid, calobj);
1710                         break;
1711                 }
1712
1713                 g_free (calobj);
1714         }
1715
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);
1722
1723         *deletes = be_data.deletes;
1724
1725         e_xmlhash_write (ehash);
1726         e_xmlhash_destroy (ehash);
1727         
1728         return GNOME_Evolution_Calendar_Success;
1729 }
1730
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)
1735 {
1736         ECalBackendGroupwise *cbgw;
1737         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1738
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);
1741
1742         return e_cal_backend_groupwise_compute_changes (cbgw, change_id, adds, modifies, deletes);
1743
1744 }
1745
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)
1749 {
1750         return GNOME_Evolution_Calendar_OtherError;
1751 }
1752
1753 static icaltimezone *
1754 e_cal_backend_groupwise_internal_get_default_timezone (ECalBackend *backend)
1755 {       
1756         ECalBackendGroupwise *cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1757         
1758         return cbgw->priv->default_zone;
1759 }
1760
1761 static icaltimezone *
1762 e_cal_backend_groupwise_internal_get_timezone (ECalBackend *backend, const char *tzid)
1763 {
1764         icaltimezone *zone;
1765
1766         zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1767         if (!zone)
1768                 return icaltimezone_get_utc_timezone();
1769                 
1770         return zone;
1771 }
1772
1773 static void
1774 sanitize_component (ECalBackendSync *backend, ECalComponent *comp, char *server_uid)
1775 {
1776         ECalBackendGroupwise *cbgw;
1777         icalproperty *icalprop;
1778         int i;
1779         GString *str = g_string_new ("");;      
1780         
1781         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1782         if (server_uid) {
1783
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);
1790                                 break;
1791                         }
1792                 }
1793                 
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);
1798
1799         }
1800         g_string_free (str, TRUE);
1801 }
1802
1803 static ECalBackendSyncStatus
1804 e_cal_backend_groupwise_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
1805 {
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;
1813         int i;
1814
1815         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1816         priv = cbgw->priv;
1817
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);
1820
1821         if (priv->mode == CAL_MODE_LOCAL) {
1822                 in_offline(cbgw);
1823                 return GNOME_Evolution_Calendar_RepositoryOffline;
1824         }
1825
1826         /* check the component for validity */
1827         icalcomp = icalparser_parse_string (*calobj);
1828         if (!icalcomp)
1829                 return GNOME_Evolution_Calendar_InvalidObject;
1830
1831         if (e_cal_backend_get_kind (E_CAL_BACKEND (backend)) != icalcomponent_isa (icalcomp)) {
1832                 icalcomponent_free (icalcomp);
1833                 return GNOME_Evolution_Calendar_InvalidObject;
1834         }
1835
1836         comp = e_cal_component_new ();
1837         e_cal_component_set_icalcomponent (comp, icalcomp);
1838
1839         /* check if the object exists */
1840         switch (priv->mode) {
1841         case CAL_MODE_ANY :
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);
1845                 
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);
1848
1849                 if (status != E_GW_CONNECTION_STATUS_OK) {
1850                         g_object_unref (comp);
1851
1852                         if (status == E_GW_CONNECTION_STATUS_UNKNOWN_USER)
1853                                 return GNOME_Evolution_Calendar_UnknownUser;
1854                         else
1855                                 return GNOME_Evolution_Calendar_OtherError;
1856                 }
1857         
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))) {
1860                         *calobj = NULL;
1861                         g_object_unref (comp);
1862                         return GNOME_Evolution_Calendar_Success;
1863                 }
1864                 
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);
1872                 } else {
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);
1878                         }
1879                         
1880                         /* convert uid_list to GPtrArray and get the items in a list */
1881                         stat = e_gw_connection_get_items_from_ids (priv->cnc,
1882                                         priv->container_id, 
1883                                         "attachments recipients message recipientStatus default peek",
1884                                         uid_array, &list);
1885
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;
1889                         }
1890                         
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;
1895                                 EGwItem *item;
1896
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);
1902                                 
1903                                 if (i == 0) {
1904                                         *calobj = e_cal_component_get_as_string (e_cal_comp);   
1905                                 }
1906
1907                                 if (i != 0) {
1908                                         char *temp;
1909                                         temp = e_cal_component_get_as_string (e_cal_comp);
1910                                         e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), temp);
1911                                         g_free (temp);
1912                                 }
1913
1914                                 g_object_unref (e_cal_comp);
1915                         }
1916                         g_ptr_array_free (uid_array, TRUE);
1917                 }
1918                 break;
1919         default :
1920                 break;
1921         }
1922
1923         g_object_unref (comp);
1924
1925         return GNOME_Evolution_Calendar_Success;
1926 }
1927
1928 static void 
1929 get_retract_data (ECalComponent *comp, const char **retract_comment, gboolean *all_instances)
1930 {
1931         icalcomponent *icalcomp = NULL;
1932         icalproperty *icalprop = NULL;
1933         gboolean is_instance = FALSE;
1934         const char *x_ret = NULL, *x_recur = NULL;
1935
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);
1939         while (icalprop) {
1940                 const char *x_name;
1941
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;
1948                         } else
1949                                 *retract_comment = x_ret;
1950                 }
1951
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;
1956                         else
1957                                 *all_instances = FALSE;
1958                 }
1959
1960                 if (x_ret && (!is_instance || x_recur))
1961                         break;
1962                 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
1963         }
1964 }
1965
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)
1969 {
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;
1977
1978         *old_object = NULL;
1979         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
1980         priv = cbgw->priv;
1981
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);
1984
1985         if (priv->mode == CAL_MODE_LOCAL) {
1986                 in_offline (cbgw);
1987                 return GNOME_Evolution_Calendar_RepositoryOffline;
1988         }
1989         
1990         /* check the component for validity */
1991         icalcomp = icalparser_parse_string (calobj);
1992         if (!icalcomp)
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);
1998
1999         /* check if the object exists */
2000         switch (priv->mode) {
2001         case CAL_MODE_ANY :
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);
2005                 if (!cache_comp) {
2006                         g_message ("CRITICAL : Could not find the object in cache");
2007                         return GNOME_Evolution_Calendar_ObjectNotFound;
2008                 }
2009
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;
2013
2014                         item = e_gw_item_new_for_delegate_from_cal (cbgw, comp);
2015
2016                         if (mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (comp)) {
2017                                 recur_key = uid;
2018                         } 
2019                         id = e_gw_item_get_id (item);
2020
2021                         status = e_gw_connection_delegate_request (priv->cnc, item, id, NULL, NULL, recur_key); 
2022
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;
2029                         }
2030
2031                         e_cal_backend_cache_put_component (priv->cache, comp);
2032                         *new_object = e_cal_component_get_as_string (comp);
2033                         break;
2034                 } 
2035
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;
2040                         
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));
2046                         
2047                                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2048                                         status = e_gw_connection_complete_request (priv->cnc, e_gw_item_get_id (item));
2049                                 
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;
2054                                 }
2055                                 e_cal_backend_cache_put_component (priv->cache, comp);
2056                                 break;
2057                         }
2058                 }
2059                 
2060                 e_gw_item_set_changes (item, cache_item); 
2061
2062                 /* the second argument is redundant */
2063                 status = e_gw_connection_modify_item (priv->cnc, e_gw_item_get_id (item), item);
2064                 
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);
2067
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;
2072                 }
2073                 /* if successful, update the cache */
2074
2075         case CAL_MODE_LOCAL :
2076                 /* in offline mode, we just update the cache */
2077                 e_cal_backend_cache_put_component (priv->cache, comp);
2078                 break;
2079         default :
2080                 break;
2081         }
2082
2083
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;
2088 }
2089
2090 static const char *
2091 get_gw_item_id (icalcomponent *icalcomp) 
2092 {
2093         icalproperty *icalprop; 
2094
2095         /* search the component for the X-GWRECORDID property */
2096         icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
2097         while (icalprop) {
2098                 const char *x_name, *x_val;
2099
2100                 x_name = icalproperty_get_x_name (icalprop);
2101                 x_val = icalproperty_get_x (icalprop);
2102                 if (!strcmp (x_name, "X-GWRECORDID")) {
2103                         return x_val;
2104                 }
2105
2106                 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
2107         }
2108         return NULL;
2109 }
2110
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,
2116                                        char **object)
2117 {
2118         ECalBackendGroupwise *cbgw;
2119         ECalBackendGroupwisePrivate *priv;
2120         char *calobj = NULL;
2121
2122         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
2123         priv = cbgw->priv;
2124
2125         *old_object = *object = NULL;
2126
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;
2132         
2133                 status = e_cal_backend_groupwise_get_object (backend, cal, uid, rid, &calobj);
2134                 if (status != GNOME_Evolution_Calendar_Success)
2135                         return status;
2136                 g_message ("object found \n");
2137
2138                 icalcomp = icalparser_parse_string (calobj);
2139                 if (!icalcomp) {
2140                         g_free (calobj);
2141                         return GNOME_Evolution_Calendar_InvalidObject;
2142                 }
2143
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 */
2148                                 id_to_remove = uid;
2149                         }
2150
2151                         /* remove the object */
2152                         status = e_gw_connection_remove_item (priv->cnc, priv->container_id, id_to_remove);
2153
2154                         if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2155                                 status = e_gw_connection_remove_item (priv->cnc, priv->container_id, id_to_remove);
2156
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)) {
2161                                         g_free (calobj);
2162                                         return GNOME_Evolution_Calendar_ObjectNotFound;
2163                                 }
2164                                 *object = NULL;
2165                                 *old_object = strdup (calobj);
2166                                 g_free (calobj);
2167                                 return GNOME_Evolution_Calendar_Success;
2168                         } else {
2169                                 g_free (calobj);
2170                                 return GNOME_Evolution_Calendar_OtherError;
2171                         }
2172                 } else if (mod == CALOBJ_MOD_ALL) {
2173                         GSList *l, *comp_list = e_cal_backend_cache_get_components_by_uid (priv->cache, uid);
2174
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 */
2178                                 
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);
2183                         } else {
2184                                 GList *item_ids = NULL; 
2185                                 
2186                                 for (l = comp_list; l; l = l->next) {
2187                                         ECalComponent *comp = E_CAL_COMPONENT (l->data);
2188
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);
2191                                 }
2192                                 status = e_gw_connection_remove_items (priv->cnc, priv->container_id, item_ids);
2193                                 
2194                                 if (status == E_GW_CONNECTION_STATUS_INVALID_CONNECTION)
2195                                         status = e_gw_connection_remove_items (priv->cnc, priv->container_id, item_ids);
2196                         }
2197
2198                         if (status == E_GW_CONNECTION_STATUS_OK) {
2199
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);
2203
2204                                         e_cal_backend_cache_remove_component (priv->cache, id->uid, 
2205                                                         id->rid);
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);
2209                                         
2210                                         g_object_unref (comp);
2211                                 
2212                                 }
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);
2217                                 *object = NULL;
2218                                 g_free (calobj);
2219                                 return GNOME_Evolution_Calendar_Success;
2220                         } else {
2221                                 g_free (calobj);
2222                                 return GNOME_Evolution_Calendar_OtherError;
2223                         }
2224                 } else
2225                         return GNOME_Evolution_Calendar_UnsupportedMethod;
2226         } else if (priv->mode == CAL_MODE_LOCAL) {
2227                 in_offline (cbgw);
2228                 return GNOME_Evolution_Calendar_RepositoryOffline;
2229         } else
2230                 return GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED;
2231         
2232 }
2233
2234 /* This function is largely duplicated in
2235  * ../file/e-cal-backend-file.c
2236  */
2237 static void
2238 fetch_attachments (ECalBackendGroupwise *cbgw, ECalComponent *comp)
2239 {
2240         GSList *attach_list = NULL, *new_attach_list = NULL;
2241         GSList *l;
2242         char  *attach_store;
2243         char *dest_url, *dest_file;
2244         int fd;
2245         const char *uid;
2246
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));
2251         
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;
2257
2258                 mapped_file = g_mapped_file_new (sfname, FALSE, &error);
2259                 if (!mapped_file) {
2260                         g_message ("DEBUG: could not map %s: %s\n",
2261                                    sfname, error->message);
2262                         g_error_free (error);
2263                         continue;
2264                 }
2265                 filename = g_path_get_basename (sfname);
2266                 new_filename = g_strconcat (uid, "-", filename, NULL);
2267                 g_free (filename);
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);
2271                 if (fd == -1) {
2272                         /* TODO handle error conditions */
2273                         g_message ("DEBUG: could not open %s for writing\n",
2274                                    dest_file);
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");
2279                 }
2280
2281                 g_mapped_file_free (mapped_file);
2282                 if (fd != -1)
2283                         close (fd);
2284                 dest_url = g_filename_to_uri (dest_file, NULL, NULL);
2285                 g_free (dest_file);
2286                 new_attach_list = g_slist_append (new_attach_list, dest_url);
2287         }
2288         g_free (attach_store);
2289         e_cal_component_set_attachment_list (comp, new_attach_list);
2290 }
2291
2292 static void 
2293 change_status (ECalComponent *comp, icalparameter_partstat status, const char *email)
2294 {       
2295         icalproperty *prop;     
2296         icalparameter *param;
2297         gboolean found = FALSE;
2298         icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
2299
2300
2301         for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
2302                         prop;
2303                         prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
2304                 const char *attendee = icalproperty_get_attendee (prop);
2305
2306                 if (!g_ascii_strncasecmp (attendee, "mailto:", 7))
2307                         attendee += 7;
2308
2309                 if (!g_ascii_strcasecmp (attendee, email)) {
2310                         found = TRUE;
2311                         param = icalparameter_new_partstat (status);
2312                         icalproperty_set_parameter (prop, param);
2313                         break;
2314                 }
2315         }
2316
2317         /* We couldn find the attendee in the component, so add a new attendee */
2318         if (!found) {
2319                 char *temp = g_strdup_printf ("MAILTO:%s", email);
2320
2321                 prop = icalproperty_new_attendee ((const char *) temp);
2322                 icalcomponent_add_property (icalcomp, prop);
2323
2324                 param = icalparameter_new_partstat (ICAL_PARTSTAT_DELEGATED);
2325                 icalproperty_add_parameter (prop, param);
2326
2327                 param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT);
2328                 icalproperty_add_parameter (prop, param);
2329
2330                 param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
2331                 icalproperty_add_parameter (prop, param);
2332
2333                 param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
2334                 icalproperty_add_parameter (prop, param);
2335
2336                 g_free (temp);
2337         }
2338 }
2339
2340 static ECalBackendSyncStatus
2341 receive_object (ECalBackendGroupwise *cbgw, EDataCal *cal, icalcomponent *icalcomp)
2342 {
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;
2350
2351         priv = cbgw->priv;
2352
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);
2357         while (icalprop) {
2358                 const char *x_name;
2359
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);
2365                                 break;
2366                         }
2367                 }
2368
2369                 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
2370         }
2371
2372         comp = e_cal_component_new ();
2373         e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
2374         method = icalcomponent_get_method (icalcomp);
2375         
2376         /* handle attachments */
2377         if (e_cal_component_has_attachments (comp))
2378                 fetch_attachments (cbgw, comp);
2379
2380         status = e_gw_connection_send_appointment (cbgw, priv->container_id, comp, method, all_instances, &modif_comp, &pstatus);
2381
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);
2384
2385         if (!modif_comp)
2386                 modif_comp = g_object_ref (comp);
2387
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;
2392
2393                 if (all_instances) {
2394                         const char *uid;
2395                         
2396                         e_cal_component_get_uid (modif_comp, (const char **) &uid);
2397                         comps = e_cal_backend_cache_get_components_by_uid (priv->cache, uid);
2398
2399                         if (!comps)
2400                                 comps = g_slist_append (comps, g_object_ref (modif_comp));
2401                         else
2402                                 found = TRUE;
2403                 } else {
2404                         ECalComponentId *id = e_cal_component_get_id (modif_comp);
2405                         ECalComponent *component = NULL;
2406
2407                         component = e_cal_backend_cache_get_component (priv->cache, id->uid, id->rid);
2408
2409                         if (!component)
2410                                 comps = g_slist_append (comps, g_object_ref (modif_comp));
2411                         else {
2412                                 comps = g_slist_append (comps, component);
2413                                 found = TRUE;
2414                         }
2415  
2416                         e_cal_component_free_id (id);
2417                 }
2418  
2419                 for (l = comps; l != NULL; l = l->next) {
2420                         ECalComponent *component = E_CAL_COMPONENT (l->data);
2421
2422                         if (pstatus == ICAL_PARTSTAT_DECLINED) {
2423                                 ECalComponentId *id = e_cal_component_get_id (component);
2424
2425                                 if (e_cal_backend_cache_remove_component (priv->cache, id->uid, id->rid)) {
2426
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);
2429
2430                                 }
2431
2432                         } else {
2433                                 char *comp_str = NULL;
2434
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);
2438                                 
2439                                 if (found)
2440                                         e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbgw), comp_str, comp_str);
2441                                 else
2442                                         e_cal_backend_notify_object_created (E_CAL_BACKEND (cbgw), comp_str);
2443
2444                                 g_free (comp_str);
2445                         }
2446                 }
2447                 
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;
2453
2454         }
2455
2456         if (status == E_GW_CONNECTION_STATUS_INVALID_OBJECT) {
2457                 g_object_unref (comp);
2458                 return  GNOME_Evolution_Calendar_InvalidObject;
2459         }
2460         return GNOME_Evolution_Calendar_OtherError;
2461 }
2462
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)
2466 {
2467         ECalBackendGroupwise *cbgw;
2468         ECalBackendGroupwisePrivate *priv;
2469         icalcomponent *icalcomp, *subcomp;
2470         icalcomponent_kind kind;
2471         ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success;
2472
2473         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
2474         priv = cbgw->priv;
2475
2476         if (priv->mode == CAL_MODE_LOCAL) {
2477                 in_offline (cbgw);
2478                 return GNOME_Evolution_Calendar_RepositoryOffline;
2479         }
2480
2481         icalcomp = icalparser_parse_string (calobj);
2482         if (!icalcomp)
2483                 return GNOME_Evolution_Calendar_InvalidObject;
2484
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)));
2489                 while (subcomp) {
2490                         icalcomponent_set_method (subcomp, icalcomponent_get_method (icalcomp));
2491                         status = receive_object (cbgw, cal, subcomp);
2492                         if (status != GNOME_Evolution_Calendar_Success)
2493                                 break;
2494                         subcomp = icalcomponent_get_next_component (icalcomp,
2495                                                                     e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
2496                 }
2497         } else if (kind == e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
2498                 status = receive_object (cbgw, cal, icalcomp);
2499         } else
2500                 status = GNOME_Evolution_Calendar_InvalidObject;
2501
2502         icalcomponent_free (icalcomp);
2503
2504         return status;
2505 }
2506
2507 static ECalBackendSyncStatus
2508 send_object (ECalBackendGroupwise *cbgw, EDataCal *cal, icalcomponent *icalcomp, icalproperty_method method)
2509 {
2510         ECalComponent *comp, *found_comp = NULL;
2511         ECalBackendGroupwisePrivate *priv;
2512         ECalBackendSyncStatus status = GNOME_Evolution_Calendar_OtherError;
2513         const char *uid = NULL, *rid = NULL;
2514
2515         priv = cbgw->priv;
2516
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);
2520
2521         e_cal_component_get_uid (comp, (const char **) &uid);
2522         found_comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
2523
2524         if (!found_comp) {
2525                 g_object_unref (comp);
2526                 return GNOME_Evolution_Calendar_ObjectNotFound;
2527         }
2528                 
2529
2530         switch (priv->mode) {
2531         case CAL_MODE_ANY :
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;
2537
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);
2542
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);
2554
2555                                                 if (e_cal_backend_cache_remove_component (priv->cache, cid->uid, 
2556                                                                 cid->rid))
2557                                                 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), cid, object, NULL);
2558                                                 
2559                                                 e_cal_component_free_id (cid);
2560                                                 g_free (object);
2561                                                 g_object_unref (component);
2562                                         }
2563
2564                                         g_slist_free (comp_list);
2565                                         g_free (old_object);
2566                                 } else {
2567                                         ECalComponentId *cid;
2568                                         char * object;
2569
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);
2573                                 
2574                                         if (e_cal_backend_cache_remove_component (priv->cache, cid->uid, 
2575                                                                 cid->rid)) {
2576                                                 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbgw), cid, 
2577                                                         object, NULL);
2578                                         }
2579
2580                                         g_free (object);
2581                                         e_cal_component_free_id (cid);
2582                                 }
2583                         }
2584                 }
2585                 break;
2586         case CAL_MODE_LOCAL :
2587                 /* in offline mode, we just update the cache */
2588                 status = GNOME_Evolution_Calendar_RepositoryOffline;
2589                 break;
2590         default:
2591                 break;  
2592         }
2593
2594         g_object_unref (comp);
2595         g_object_unref (found_comp);
2596
2597         return status;
2598 }
2599
2600 static ECalBackendSyncStatus
2601 e_cal_backend_groupwise_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj, GList **users,
2602                                       char **modified_calobj)
2603 {
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;
2610
2611         *users = NULL;
2612         *modified_calobj = NULL;
2613
2614         cbgw = E_CAL_BACKEND_GROUPWISE (backend);
2615         priv = cbgw->priv;
2616
2617         if (priv->mode == CAL_MODE_LOCAL) {
2618                 in_offline (cbgw);
2619                 return GNOME_Evolution_Calendar_RepositoryOffline;
2620         }
2621
2622         icalcomp = icalparser_parse_string (calobj);
2623         if (!icalcomp)
2624                 return GNOME_Evolution_Calendar_InvalidObject;
2625
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)));
2631                 while (subcomp) {
2632
2633                         status = send_object (cbgw, cal, subcomp, method);
2634                         if (status != GNOME_Evolution_Calendar_Success)
2635                                 break;
2636                         subcomp = icalcomponent_get_next_component (icalcomp,
2637                                                                     e_cal_backend_get_kind (E_CAL_BACKEND (backend)));
2638                 }
2639         } else if (kind == e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
2640                 status = send_object (cbgw, cal, icalcomp, method);
2641         } else
2642                 status = GNOME_Evolution_Calendar_InvalidObject;
2643         
2644         if (status == GNOME_Evolution_Calendar_Success) {
2645                 ECalComponent *comp;
2646
2647                 comp = e_cal_component_new ();
2648                 
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;
2655         
2656                                 if (attendee)
2657                                         *users = g_list_append (*users, g_strdup (attendee));
2658                         }
2659                         
2660                         g_object_unref (comp);  
2661                 }
2662                 *modified_calobj = g_strdup (calobj);
2663         }
2664         icalcomponent_free (icalcomp);
2665
2666         return status;
2667 }
2668
2669
2670 /* Object initialization function for the file backend */
2671 static void
2672 e_cal_backend_groupwise_init (ECalBackendGroupwise *cbgw, ECalBackendGroupwiseClass *class)
2673 {
2674         ECalBackendGroupwisePrivate *priv;
2675
2676         priv = g_new0 (ECalBackendGroupwisePrivate, 1);
2677
2678         priv->timeout_id = 0;
2679         priv->cnc = NULL;
2680         priv->sendoptions_sync_timeout = 0;
2681
2682         /* create the mutex for thread safety */
2683         priv->mutex = g_mutex_new ();
2684
2685         cbgw->priv = priv;
2686
2687         e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbgw), TRUE);
2688 }
2689
2690 /* Class initialization function for the gw backend */
2691 static void
2692 e_cal_backend_groupwise_class_init (ECalBackendGroupwiseClass *class)
2693 {
2694         GObjectClass *object_class;
2695         ECalBackendClass *backend_class;
2696         ECalBackendSyncClass *sync_class;
2697
2698         object_class = (GObjectClass *) class;
2699         backend_class = (ECalBackendClass *) class;
2700         sync_class = (ECalBackendSyncClass *) class;
2701
2702         parent_class = g_type_class_peek_parent (class);
2703
2704         object_class->dispose = e_cal_backend_groupwise_dispose;
2705         object_class->finalize = e_cal_backend_groupwise_finalize;
2706
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;
2729
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;
2736 }
2737
2738
2739 /**
2740  * e_cal_backend_groupwise_get_type:
2741  * @void: 
2742  * 
2743  * Registers the #ECalBackendGroupwise class if necessary, and returns the type ID
2744  * associated to it.
2745  * 
2746  * Return value: The type ID of the #ECalBackendGroupwise class.
2747  **/
2748 GType
2749 e_cal_backend_groupwise_get_type (void)
2750 {
2751         static GType e_cal_backend_groupwise_type = 0;
2752
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,
2759                         NULL, NULL,
2760                         sizeof (ECalBackendGroupwise),
2761                         0,
2762                         (GInstanceInitFunc) e_cal_backend_groupwise_init
2763                 };
2764                 e_cal_backend_groupwise_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
2765                                                                   "ECalBackendGroupwise", &info, 0);
2766         }
2767
2768         return e_cal_backend_groupwise_type;
2769 }
2770
2771 void
2772 e_cal_backend_groupwise_notify_error_code (ECalBackendGroupwise *cbgw, EGwConnectionStatus status)
2773 {
2774         const char *msg;
2775
2776         g_return_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw));
2777
2778         msg = e_gw_connection_get_error_message (status);
2779         if (msg)
2780                 e_cal_backend_notify_error (E_CAL_BACKEND (cbgw), msg);
2781 }
2782
2783 const char *
2784 e_cal_backend_groupwise_get_local_attachments_store (ECalBackendGroupwise *cbgw)
2785 {
2786         g_return_val_if_fail (E_IS_CAL_BACKEND_GROUPWISE (cbgw), NULL);
2787         return cbgw->priv->local_attachments_store;
2788 }