Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / calendar / backends / caldav / e-cal-backend-caldav.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2  * ex: set ts=8: */
3 /* Evolution calendar - caldav backend
4  *
5  * Copyright (C) 2005 Novell, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * Author: Christian Kellner <gicmo@gnome.org> 
21  */
22
23 #include <config.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <gconf/gconf-client.h>
27 #include <bonobo/bonobo-exception.h>
28 #include <bonobo/bonobo-moniker-util.h>
29 #include <glib/gi18n-lib.h>
30 #include "libedataserver/e-xml-hash-utils.h"
31 #include <libecal/e-cal-recur.h>
32 #include <libecal/e-cal-util.h>
33 #include <libecal/e-cal-time-util.h>
34 #include <libedata-cal/e-cal-backend-cache.h>
35 #include <libedata-cal/e-cal-backend-util.h>
36 #include <libedata-cal/e-cal-backend-sexp.h>
37
38 /* LibXML2 includes */
39 #include <libxml/parser.h>
40 #include <libxml/tree.h>
41 #include <libxml/xpath.h>  
42 #include <libxml/xpathInternals.h>
43
44 /* LibSoup includes */
45 #include <libsoup/soup.h>
46 #include <libsoup/soup-headers.h>
47 #include <libsoup/soup-uri.h>
48 #include <libsoup/soup-soap-message.h>
49
50 #include "e-cal-backend-caldav.h"
51
52 /* in seconds */
53 #define DEFAULT_REFRESH_TIME 60
54
55 typedef enum {
56
57         SLAVE_SHOULD_SLEEP,
58         SLAVE_SHOULD_WORK,
59         SLAVE_SHOULD_DIE
60
61 } SlaveCommand;
62
63 /* Private part of the ECalBackendHttp structure */
64 struct _ECalBackendCalDAVPrivate {
65         
66         /* online/offline */
67         CalMode mode;
68
69         /* The local disk cache */
70         ECalBackendCache *cache;
71
72         /* should we sync for offline mode? */
73         gboolean do_offline;
74         
75         /* TRUE after caldav_open */
76         gboolean loaded;
77
78         /* the open status  */
79         ECalBackendSyncStatus ostatus;
80         
81         /* lock to protect cache */
82         GMutex *lock;
83
84         /* cond to synch threads */
85         GCond *cond;
86
87         /* BG synch thread */
88         GThread *synch_slave;
89         SlaveCommand slave_cmd;
90         GTimeVal refresh_time;
91         gboolean do_synch;
92         
93         /* The main soup session  */
94         SoupSession *session;
95
96         /* well, guess what */
97         gboolean read_only;
98
99         /* whehter the synch function 
100          * should report changes to the
101          * backend */
102         gboolean report_changes;
103
104         /* clandar uri */       
105         char *uri;
106
107         /* Authentication info */
108         char *username;
109         char *password;
110         gboolean need_auth;
111
112         /* object cleanup */
113         gboolean disposed;
114 };
115
116 /* ************************************************************************* */
117 /* Debugging */
118
119 #define DEBUG_MESSAGE "message"
120 #define DEBUG_MESSAGE_HEADER "message:header"
121 #define DEBUG_MESSAGE_BODY "message:body"
122
123 static gboolean caldav_debug_all = FALSE;
124 static GHashTable *caldav_debug_table = NULL;
125
126
127 static void
128 add_debug_key (const char *start, const char *end)
129 {
130         char *debug_key;
131         char *debug_value;
132
133         if (start == end) {
134                 return;
135         }
136
137         debug_key = debug_value = g_strndup (start, end - start);
138
139         debug_key = g_strchug (debug_key);
140         debug_key = g_strchomp (debug_key);
141
142         if (strlen (debug_key) == 0) {
143                 g_free (debug_value);
144                 return;
145         }
146
147         g_hash_table_insert (caldav_debug_table,
148                              debug_key,
149                              debug_value);
150                         
151         g_debug ("Adding %s to enabled debugging keys", debug_key);
152 }
153
154 static gpointer
155 caldav_debug_init_once (gpointer data)
156 {
157         const char *dbg;
158
159         dbg = g_getenv ("CALDAV_DEBUG");
160
161         if (dbg) {
162                 const char *ptr;
163
164                 g_debug ("Got debug env variable: [%s]", dbg);
165
166                 caldav_debug_table = g_hash_table_new (g_str_hash,
167                                                        g_str_equal);
168
169                 ptr = dbg;
170
171                 while (*ptr != '\0') {
172                         if (*ptr == ',' || *ptr == ':') {
173                         
174                                 add_debug_key (dbg, ptr);
175
176                                 if (*ptr == ',') {
177                                         dbg = ptr + 1;
178                                 }
179                         }
180
181                         ptr++;
182                 }
183
184                 if (ptr - dbg > 0) {
185                         add_debug_key (dbg, ptr);
186                 }
187
188                 if (g_hash_table_lookup (caldav_debug_table, "all")) {
189                         caldav_debug_all = TRUE;
190                         g_hash_table_destroy (caldav_debug_table);
191                         caldav_debug_table = NULL;
192                 }
193         }
194         
195         return NULL;
196 }
197
198 static void
199 caldav_debug_init ()
200 {
201         static GOnce debug_once = G_ONCE_INIT;
202   
203         g_once (&debug_once,
204                 caldav_debug_init_once,
205                 NULL);
206 }
207
208 static gboolean
209 caldav_debug_show (const char *component)
210 {
211         if (G_UNLIKELY (caldav_debug_all)) {
212                 return TRUE;
213         } else if (G_UNLIKELY (caldav_debug_table != NULL) &&
214                    g_hash_table_lookup (caldav_debug_table, component)) {
215                 return TRUE;
216         }
217
218         return FALSE; 
219 }
220
221 static void
222 message_debug_print_header (gpointer name, gpointer value, gpointer data)
223 {
224         g_debug ("%s: %s", (char *) name, (char *) value);
225 }
226
227 #define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
228
229 static void
230 message_response_debug_handler (SoupMessage *msg, gpointer user_data)
231 {
232
233         g_debug ("%d %s\nMessage-Debug: %p @ %lu",
234                  msg->status_code,
235                  msg->reason_phrase,
236                  msg,
237                  time (0));
238
239         if (caldav_debug_show (DEBUG_MESSAGE_HEADER)) {
240                 /* print headers */
241                 soup_message_foreach_header (msg->response_headers,
242                                              message_debug_print_header,
243                                              NULL);
244
245         }
246
247         if (caldav_debug_show (DEBUG_MESSAGE_BODY)) {
248
249                 /* print response */
250                 if (msg->response.length) {
251                         char *body;
252
253                         //needed for null terminal and truncation       
254                         body = g_strndup (msg->response.body,
255                                           MIN (msg->response.length,
256                                                DEBUG_MAX_BODY_SIZE));
257
258                         g_debug ("Response: \n[%s%s]%s", body,
259                                  msg->response.length > DEBUG_MAX_BODY_SIZE ?
260                                  " ..." : "",
261                                   msg->response.length > DEBUG_MAX_BODY_SIZE ?
262                                  " (trunkated)" : "");
263
264                         g_free (body);
265                 }
266         }
267 }
268
269 static void
270 message_setup_debug (SoupMessage *msg)
271 {
272         const SoupUri *suri;
273
274         if (G_LIKELY (! caldav_debug_show (DEBUG_MESSAGE))) {
275                 return;
276         }
277
278         suri = soup_message_get_uri (msg);
279
280         g_debug ("%s %s%s%s HTTP/1.1\nMessage-ID: %p @ %lu",
281                  SOUP_MESSAGE (msg)->method,
282                  suri->path,
283                  suri->query ? "?" : "",
284                  suri->query ? suri->query : "",
285                  msg,
286                  (unsigned long) time (0));
287
288         soup_message_add_handler (SOUP_MESSAGE (msg),
289                                   SOUP_HANDLER_POST_BODY,
290                                   message_response_debug_handler,
291                                   NULL);
292
293         if (G_LIKELY (! caldav_debug_show (DEBUG_MESSAGE_HEADER))) {
294                 return;
295         }
296
297         /* print message headers */
298         message_debug_print_header ("Host", suri->host, NULL);
299
300         soup_message_foreach_header (SOUP_MESSAGE (msg)->request_headers,
301                                      message_debug_print_header,
302                                      NULL);
303
304         if (caldav_debug_show (DEBUG_MESSAGE_BODY)) {
305
306                 /* print response */
307                 if (msg->request.length) {
308                         char *body;
309
310                         //needed for null terminal and truncation       
311                         body = g_strndup (msg->request.body,
312                                           MIN (msg->request.length,
313                                                DEBUG_MAX_BODY_SIZE));
314
315                         g_debug ("Request: \n[%s%s]%s", body,
316                                  msg->request.length > DEBUG_MAX_BODY_SIZE ?
317                                  " ..." : "",
318                                   msg->request.length > DEBUG_MAX_BODY_SIZE ?
319                                  " (trunkated)" : "");
320
321                         g_free (body);
322                 }
323         }
324 }
325
326
327 static ECalBackendSyncClass *parent_class = NULL;
328
329 /* ************************************************************************* */
330 /* Misc. utility functions */
331 #define X_E_CALDAV "X-EVOLUTION-CALDAV-" 
332
333 static void
334 icomp_x_prop_set (icalcomponent *comp, const char *key, const char *value)
335 {
336         icalproperty *xprop;
337
338         /* Find the old one first */
339         xprop = icalcomponent_get_first_property (comp, ICAL_X_PROPERTY);
340
341         while (xprop) {
342                 const char *str = icalproperty_get_x_name (xprop);
343                 
344                 if (!strcmp (str, key)) {
345                         icalcomponent_remove_property (comp, xprop);
346                         icalproperty_free (xprop);
347                         break;
348                 }
349
350                 xprop = icalcomponent_get_next_property (comp, ICAL_X_PROPERTY);
351         }
352
353         /* couldnt we be a bit smarter here and reuse the property? */
354         
355         xprop = icalproperty_new_x (value);
356         icalproperty_set_x_name (xprop, key);
357         icalcomponent_add_property (comp, xprop);
358 }
359
360
361 static const char *
362 icomp_x_prop_get (icalcomponent *comp, const char *key)
363 {
364         icalproperty *xprop;
365         
366         /* Find the old one first */
367         xprop = icalcomponent_get_first_property (comp, ICAL_X_PROPERTY);
368
369         while (xprop) {
370                 const char *str = icalproperty_get_x_name (xprop);
371                 
372                 if (!strcmp (str, key)) {
373                         break;
374                 }
375
376                 xprop = icalcomponent_get_next_property (comp, ICAL_X_PROPERTY);
377         }
378
379         if (xprop) {
380                 return icalproperty_get_value_as_string (xprop);        
381         }
382         
383         return NULL;
384 }
385
386
387 static void
388 e_cal_component_set_href (ECalComponent *comp, const char *href)
389 {
390         icalcomponent *icomp;
391
392         icomp = e_cal_component_get_icalcomponent (comp);
393         
394         icomp_x_prop_set (icomp, X_E_CALDAV "HREF", href);
395 }
396
397 static const char *
398 e_cal_component_get_href (ECalComponent *comp)
399 {
400         icalcomponent *icomp;
401         char          *str;
402         
403         str = NULL;
404         icomp = e_cal_component_get_icalcomponent (comp);
405         
406         str = (char *) icomp_x_prop_get (icomp, X_E_CALDAV "HREF");
407                 
408         return str;
409 }
410
411
412 static void
413 e_cal_component_set_etag (ECalComponent *comp, const char *etag)
414 {
415         icalcomponent *icomp;
416
417         icomp = e_cal_component_get_icalcomponent (comp);
418         
419         icomp_x_prop_set (icomp, X_E_CALDAV "ETAG", etag);
420
421
422 }
423
424 static const char *
425 e_cal_component_get_etag (ECalComponent *comp)
426 {
427         icalcomponent *icomp;
428         char          *str;
429         
430         str = NULL;
431         icomp = e_cal_component_get_icalcomponent (comp);
432         
433         str = (char *) icomp_x_prop_get (icomp, X_E_CALDAV "ETAG");
434                 
435         return str;
436 }
437
438 typedef enum {
439         
440         /* object is in synch,
441          * now isnt that ironic? :) */
442         E_CAL_COMPONENT_IN_SYNCH = 0,
443         
444         /* local changes */
445         E_CAL_COMPONENT_LOCALLY_CREATED,
446         E_CAL_COMPONENT_LOCALLY_DELETED,
447         E_CAL_COMPONENT_LOCALLY_MODIFIED,       
448
449 } ECalComponentSyncState;
450
451 /* oos = out of synch */
452 static void
453 e_cal_component_set_synch_state (ECalComponent          *comp, 
454                                  ECalComponentSyncState  state)
455 {
456         icalcomponent *icomp;
457         char          *state_string;
458         
459         icomp = e_cal_component_get_icalcomponent (comp);
460         
461         state_string = g_strdup_printf ("%d", state);
462         
463         icomp_x_prop_set (icomp, X_E_CALDAV "ETAG", state_string);
464
465         g_free (state_string);
466 }
467
468
469 /* gen uid, set it internally and report it back so we can instantly 
470  * use it 
471  * and btw FIXME!!! */
472 static char *
473 e_cal_component_gen_href (ECalComponent *comp)
474 {
475         char *href, *iso;
476
477         icalcomponent *icomp;
478
479         iso = isodate_from_time_t (time (NULL));
480
481         href = g_strconcat (iso, ".ics", NULL);
482
483         g_free (iso);   
484         
485         icomp = e_cal_component_get_icalcomponent (comp);       
486         icomp_x_prop_set (icomp, X_E_CALDAV "HREF", href);
487
488         return href;    
489 }       
490
491 /* ensure etag is quoted (to workaround potential server bugs) */
492 static char *
493 quote_etag (const char *etag)
494 {
495         char *ret;
496
497         if (etag && (strlen (etag) < 2 || etag[strlen (etag) - 1] != '\"')) {
498                 ret = g_strdup_printf ("\"%s\"", etag);
499         } else {
500                 ret = g_strdup (etag);
501         }
502         
503         return ret;
504 }
505
506 /* ************************************************************************* */
507 static char **
508 sm_join_and_split_header (SoupMessage *message, const char *header)
509 {
510         const GSList  *list;
511         char          *str;
512         char         **sa;
513         char          *tofree;
514         
515         sa   = NULL;    
516         list = soup_message_get_header_list (message->response_headers, header);
517         
518         if (list == NULL || list->data == NULL) {
519                 return NULL;
520         }
521
522         /* Only do string manipulation if really necessary */   
523         if (list->next) {
524                 GString *stmp;          
525                 stmp = g_string_new ((gchar *) list->data);
526                 
527                 while ((list = list->next)) {
528                         g_string_append_printf (stmp, ",%s", (gchar *) list->data);
529                 }
530                 
531                 str = tofree = g_string_free (stmp, FALSE);
532         } else {
533                 str = (char *) list->data;
534                 tofree = NULL;
535         }
536
537         g_assert (str != NULL);
538         sa = g_strsplit (str, ",", 20);
539         g_free (tofree);
540         
541         return sa;
542 }
543
544 static ECalBackendSyncStatus
545 status_code_to_result (guint status_code)
546 {
547         ECalBackendSyncStatus result;
548         
549         if (SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
550                 return GNOME_Evolution_Calendar_Success;
551         }
552         
553         switch (status_code) {
554
555         case 404:
556                 result = GNOME_Evolution_Calendar_NoSuchCal;
557                 break;
558
559         case 403:
560                 result = GNOME_Evolution_Calendar_AuthenticationFailed;
561                 break;
562
563         case 401:
564                 result = GNOME_Evolution_Calendar_AuthenticationRequired;
565                 break;
566                         
567         default:
568                 result = GNOME_Evolution_Calendar_OtherError;
569         }
570
571         return result;
572 }
573         
574 static gboolean
575 match_header (const char *header, const char *string)
576 {
577         g_assert (string != NULL);
578         
579         if (header == NULL || header[0] == '\0') {
580                 return FALSE;
581         }
582         
583         /* skip leading whitespaces */
584         while (g_ascii_isspace (header[0])) {
585                 header++;
586         }
587
588         return !g_ascii_strncasecmp (header, string, strlen (string));
589 }
590
591 /* !TS, call with lock held */
592 static ECalBackendSyncStatus
593 check_state (ECalBackendCalDAV *cbdav, gboolean *online)
594 {
595         ECalBackendCalDAVPrivate *priv;
596         
597         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
598
599         *online = FALSE;
600         
601         if (priv->loaded != TRUE) {
602                 return GNOME_Evolution_Calendar_OtherError;
603         }
604
605         if (priv->mode == CAL_MODE_LOCAL) {
606                 
607                 if (! priv->do_offline) {
608                         return GNOME_Evolution_Calendar_RepositoryOffline;
609                 } 
610                 
611         } else {
612                 *online = TRUE; 
613         }
614
615         return  GNOME_Evolution_Calendar_Success;
616 }
617
618 /* ************************************************************************* */
619 /* XML Parsing code */
620
621 static xmlXPathObjectPtr
622 xpath_eval (xmlXPathContextPtr ctx, char *format, ...)
623 {
624         xmlXPathObjectPtr  result;
625         va_list            args;
626         char              *expr;
627
628         if (ctx == NULL) {
629                 return NULL;    
630         }               
631
632         va_start (args, format);
633         expr = g_strdup_vprintf (format, args);
634         va_end (args);
635         
636         result = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
637         g_free (expr);
638         
639         if (result == NULL) {
640                 return NULL;    
641         }
642         
643         if (result->type == XPATH_NODESET && 
644             xmlXPathNodeSetIsEmpty (result->nodesetval)) {
645                 xmlXPathFreeObject (result);
646                 
647                 g_print ("No result\n");
648                 
649                 return NULL;
650         }
651         
652         return result;
653 }
654
655 #if 0
656 static gboolean 
657 parse_status_node (xmlNodePtr node, guint *status_code)
658 {
659         xmlChar  *content;
660         gboolean  res;
661
662         content = xmlNodeGetContent (node);
663
664         res = soup_headers_parse_status_line ((char *) content, 
665                                               NULL,
666                                               status_code,
667                                               NULL);
668         xmlFree (content);
669
670         return res;
671 }
672 #endif
673
674 static char *
675 xp_object_get_string (xmlXPathObjectPtr result)
676 {
677         char *ret;
678         
679         if (result == NULL || result->type != XPATH_STRING) {
680                 return NULL;    
681         }
682         
683         ret = g_strdup ((char *) result->stringval);
684         
685         xmlXPathFreeObject (result);
686         return ret;
687 }
688
689 /* as get_string but will normailze it (i.e. only take
690  * the last part of the href) */
691 static char *
692 xp_object_get_href (xmlXPathObjectPtr result)
693 {
694         char *ret;
695         char *val;
696
697         if (result == NULL || result->type != XPATH_STRING) {
698                 return NULL;    
699         }
700
701         val = (char *) result->stringval;
702
703         if ((ret = g_strrstr (val, "/")) == NULL) {
704                 ret = val;
705         } else {
706                 ret++; /* skip the unwanted "/" */
707         }
708
709         ret = g_strdup (ret);
710         g_debug ("found href: %s", ret);
711         
712         xmlXPathFreeObject (result);
713         return ret;
714 }
715
716 /* like get_string but will quote the etag if necessary */
717 static char *
718 xp_object_get_etag (xmlXPathObjectPtr result)
719 {
720         char *ret;
721         char *str;
722         
723         if (result == NULL || result->type != XPATH_STRING) {
724                 return NULL;    
725         }
726
727         str = (char *) result->stringval;
728
729         ret = quote_etag (str);
730                 
731         xmlXPathFreeObject (result);
732         return ret;
733 }
734
735 static guint
736 xp_object_get_status (xmlXPathObjectPtr result)
737 {
738         gboolean res;
739         guint    ret;
740         
741         
742         if (result == NULL || result->type != XPATH_STRING) {
743                 return 0;       
744         }
745         
746         res = soup_headers_parse_status_line ((char *) result->stringval, 
747                                               NULL,
748                                               &ret,
749                                               NULL);
750         
751         if (res != TRUE) {
752                 ret = 0;        
753         }
754         
755         xmlXPathFreeObject (result);
756         return ret;
757 }
758
759 #if 0
760 static int
761 xp_object_get_number (xmlXPathObjectPtr result)
762 {
763         int ret;
764         
765         if (result == NULL || result->type != XPATH_STRING) {
766                 return -1;      
767         }
768         
769         ret = result->boolval;
770         
771         xmlXPathFreeObject (result);
772         return ret;
773 }
774 #endif
775
776 /*** *** *** *** *** *** */
777 #define XPATH_HREF "string(/D:multistatus/D:response[%d]/D:href)"
778 #define XPATH_STATUS "string(/D:multistatus/D:response[%d]/D:propstat/D:status)"
779 #define XPATH_GETETAG_STATUS "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:getetag/../../D:status)"
780 #define XPATH_GETETAG "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:getetag)"
781 #define XPATH_CALENDAR_DATA "string(/D:multistatus/D:response[%d]/C:calendar-data)"
782
783
784 typedef struct _CalDAVObject CalDAVObject;
785
786 struct _CalDAVObject {
787
788         char *href;
789         char *etag;
790
791         guint status;
792
793         char *cdata;
794 };
795
796 static void
797 caldav_object_free (CalDAVObject *object, gboolean free_object_itself) 
798 {
799         g_free (object->href);
800         g_free (object->etag);
801         g_free (object->cdata);
802
803         if (free_object_itself) {
804                 g_free (object);
805         }
806 }
807
808 static gboolean
809 parse_report_response (SoupMessage *soup_message, CalDAVObject **objs, int *len)
810 {
811         xmlXPathContextPtr xpctx;
812         xmlXPathObjectPtr  result;
813         xmlDocPtr          doc;
814         int                i, n;
815         gboolean           res;
816
817         g_return_val_if_fail (soup_message != NULL, FALSE);
818         g_return_val_if_fail (objs != NULL || len != NULL, FALSE);
819
820         res = TRUE;
821         doc = xmlReadMemory (soup_message->response.body, 
822                              soup_message->response.length, 
823                              "response.xml", 
824                              NULL, 
825                              0);
826
827         if (doc == NULL) {
828                 return FALSE;
829         }
830
831         xpctx = xmlXPathNewContext (doc);
832
833         xmlXPathRegisterNs (xpctx, (xmlChar *) "D", 
834                             (xmlChar *) "DAV:");
835
836         xmlXPathRegisterNs (xpctx, (xmlChar *) "C", 
837                             (xmlChar *) "urn:ietf:params:xml:ns:caldav");
838
839         result = xpath_eval (xpctx, "/D:multistatus/D:response");       
840
841         if (result == NULL || result->type != XPATH_NODESET) {
842                 *len = 0;
843                 res = FALSE;
844                 goto out;
845         }
846
847         n = xmlXPathNodeSetGetLength (result->nodesetval);
848         *len = n;
849         
850         *objs = g_new0 (CalDAVObject, n);
851         
852         for (i = 0; i < n; i++) {
853                 CalDAVObject *object;
854                 xmlXPathObjectPtr xpres;
855                 
856                 object = *objs + i;
857                 /* see if we got a status child in the response element */
858
859                 xpres = xpath_eval (xpctx, XPATH_HREF, i + 1);
860                 object->href = xp_object_get_href (xpres);
861
862                 xpres = xpath_eval (xpctx,XPATH_STATUS , i + 1);
863                 object->status = xp_object_get_status (xpres);
864
865                 //dump_xp_object (xpres);
866                 if (object->status && object->status != 200) {
867                         continue;
868                 }
869
870                 xpres = xpath_eval (xpctx, XPATH_GETETAG_STATUS, i + 1);
871                 object->status = xp_object_get_status (xpres);
872
873                 if (object->status != 200) {
874                         continue;       
875                 }
876
877                 xpres = xpath_eval (xpctx, XPATH_GETETAG, i + 1);
878                 object->etag = xp_object_get_etag (xpres);
879
880                 xpres = xpath_eval (xpctx, XPATH_CALENDAR_DATA, i + 1);
881                 object->cdata = xp_object_get_string (xpres);
882         }
883
884 out:
885         xmlXPathFreeContext (xpctx);            
886         xmlFreeDoc (doc);
887         return res;
888 }
889
890 /* ************************************************************************* */
891 /* Authentication helpers for libsoup */
892
893 static void
894 soup_authenticate (SoupSession  *session, 
895                    SoupMessage  *msg,
896                    const char   *auth_type, 
897                    const char   *auth_realm,
898                    char        **username, 
899                    char        **password, 
900                    gpointer      data)
901 {
902         ECalBackendCalDAVPrivate *priv;
903         ECalBackendCalDAV        *cbdav;
904         
905         cbdav = E_CAL_BACKEND_CALDAV (data);    
906         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
907
908         *username = priv->username;
909         *password = priv->password;
910         
911         priv->username = NULL;
912         priv->password = NULL;
913
914 }
915
916 static void
917 soup_reauthenticate (SoupSession  *session, 
918                      SoupMessage  *msg,
919                      const char   *auth_type, 
920                      const char   *auth_realm,
921                      char        **username, 
922                      char        **password, 
923                      gpointer      data)
924 {
925         ECalBackendCalDAVPrivate *priv;
926         ECalBackendCalDAV        *cbdav;
927         
928         cbdav = E_CAL_BACKEND_CALDAV (data);    
929         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
930
931         *username = priv->username;
932         *password = priv->password;
933         
934         priv->username = NULL;
935         priv->password = NULL;
936 }
937
938 static gint 
939 caldav_ignore_host(gconstpointer a, gconstpointer b)
940 {
941         gchar *hostname = (gchar*)a, 
942               *ignore = (gchar*)b;
943  
944         if (hostname && ignore)
945           return strcmp(hostname, ignore);
946         return -1;
947 }
948
949 static void 
950 caldav_set_session_proxy(ECalBackendCalDAVPrivate *priv)
951 {
952         GConfClient *conf_client;
953         SoupUri *uri_base;
954         
955         if (priv->session == NULL)
956                 return;
957                 
958         uri_base = soup_uri_new (priv->uri);
959         if (uri_base == NULL)
960                 return;
961                 
962         /* set the outbound HTTP proxy, if configuration is set to do so */
963         conf_client = gconf_client_get_default ();
964         if (gconf_client_get_bool (conf_client, "/system/http_proxy/use_http_proxy", NULL)) {
965                 char *server, *proxy_uri;
966                 int port;
967                 GSList *ignore = gconf_client_get_list (conf_client, 
968                                                         "/system/http_proxy/ignore_hosts",
969                                                         GCONF_VALUE_STRING, NULL); 
970                 if (ignore == NULL || 
971                     g_slist_find_custom(ignore, uri_base->host, caldav_ignore_host) == NULL) {
972                         server = gconf_client_get_string (conf_client, "/system/http_proxy/host", NULL);
973                         port = gconf_client_get_int (conf_client, "/system/http_proxy/port", NULL);
974
975                         if (server && server[0]) {
976                                 SoupUri *suri;
977                                 if (gconf_client_get_bool (conf_client, "/system/http_proxy/use_authentication", NULL)) {
978                                         char *user, *password;
979                                         user = gconf_client_get_string (conf_client,
980                                                                         "/system/http_proxy/authentication_user",
981                                                                         NULL);
982                                         password = gconf_client_get_string (conf_client,
983                                                                             "/system/http_proxy/authentication_password",
984                                                                             NULL);
985
986                                         proxy_uri = g_strdup_printf("http://%s:%s@%s:%d", user, password, server, port);
987                                         g_free (user);
988                                         g_free (password);
989                                 } else
990                                         proxy_uri = g_strdup_printf ("http://%s:%d", server, port);
991
992                                 suri = soup_uri_new (proxy_uri);
993                                 g_object_set (G_OBJECT (priv->session), SOUP_SESSION_PROXY_URI, suri, NULL);
994
995                                 soup_uri_free (suri);
996                                 g_free (server);
997                                 g_free (proxy_uri);
998                         }
999                 }
1000                 g_slist_foreach(ignore, (GFunc) g_free, NULL);
1001                 g_slist_free(ignore);
1002         }
1003         soup_uri_free (uri_base);
1004 }
1005
1006
1007 /* ************************************************************************* */
1008 /* direct CalDAV server access functions */
1009
1010 static char *
1011 caldav_generate_uri (ECalBackendCalDAV *cbdav, const char *target)
1012 {
1013         ECalBackendCalDAVPrivate  *priv;
1014         char *uri;
1015
1016         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1017
1018         /* priv->uri must NOT have trailing slash */
1019         uri = g_strconcat (priv->uri, "/" , target, NULL);
1020
1021         return uri;
1022 }
1023
1024 static ECalBackendSyncStatus
1025 caldav_server_open_calendar (ECalBackendCalDAV *cbdav)
1026 {
1027         ECalBackendCalDAVPrivate  *priv;
1028         SoupMessage               *message;
1029         char                     **sa;
1030         char                     **siter;
1031         gboolean                   calendar_access;
1032         gboolean                   put_allowed;
1033         gboolean                   delete_allowed;
1034         
1035         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1036
1037         /* FIXME: setup text_uri */
1038         
1039         message = soup_message_new (SOUP_METHOD_OPTIONS, priv->uri);
1040         soup_message_add_header (message->request_headers, 
1041                                  "User-Agent", "Evolution/" VERSION);
1042
1043         message_setup_debug (message);
1044         soup_session_send_message (priv->session, message);
1045         
1046         if (! SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
1047                 g_object_unref (message);
1048
1049                 return status_code_to_result (message->status_code);
1050         }
1051         
1052         /* parse the dav header, we are intreseted in the
1053          * calendar-access bit only at the moment */
1054         sa = sm_join_and_split_header (message, "DAV");
1055         
1056         calendar_access = FALSE;
1057         for (siter = sa; siter && *siter; siter++) {
1058                 
1059                 if (match_header (*siter, "calendar-access")) {
1060                         calendar_access = TRUE;
1061                         break;
1062                 }
1063         }       
1064         
1065         g_strfreev (sa);
1066
1067         
1068         sa = sm_join_and_split_header (message, "Allow");
1069         
1070         /* parse the Allow header and look for PUT, DELETE at the 
1071          * moment (maybe we should check more here, for REPORT eg) */
1072         put_allowed = delete_allowed = FALSE;
1073         for (siter = sa; siter && *siter; siter++) {
1074                 if (match_header (*siter, "DELETE")) {
1075                         delete_allowed = TRUE;
1076                 } else if (match_header (*siter, "PUT")) {
1077                         put_allowed = TRUE;
1078                 }
1079
1080                 if (put_allowed && delete_allowed) {
1081                         break;
1082                 }
1083         }       
1084         
1085         g_strfreev (sa);
1086         
1087         g_object_unref (message);
1088
1089         if (calendar_access) {
1090                 priv->read_only = ! (put_allowed && delete_allowed);
1091                 priv->do_synch = TRUE;
1092                 return GNOME_Evolution_Calendar_Success;
1093         }
1094         
1095         return GNOME_Evolution_Calendar_NoSuchCal;      
1096 }
1097
1098
1099 static gboolean
1100 caldav_server_list_objects (ECalBackendCalDAV *cbdav, CalDAVObject **objs, int *len)
1101 {       
1102         ECalBackendCalDAVPrivate *priv;
1103         xmlOutputBufferPtr   buf;
1104         SoupMessage         *message;
1105         xmlNodePtr           node;
1106         xmlNodePtr           sn;
1107         xmlNodePtr           root;
1108         xmlDocPtr            doc;
1109         xmlNsPtr             nsdav;
1110         xmlNsPtr             nscd;
1111         gboolean             result;
1112        
1113         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1114         
1115         /* Maybe we should just do a g_strdup_printf here? */   
1116         /* Prepare request body */
1117         doc = xmlNewDoc ((xmlChar *) "1.0");
1118         root = xmlNewNode (NULL, (xmlChar *) "calendar-query");
1119         nscd = xmlNewNs (root, (xmlChar *) "urn:ietf:params:xml:ns:caldav", 
1120                          (xmlChar *) "C");
1121         xmlSetNs (root, nscd);
1122         
1123         /* Add webdav tags */
1124         nsdav = xmlNewNs (root, (xmlChar *) "DAV:", (xmlChar *) "D");
1125         node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
1126         xmlNewTextChild (node, nsdav, (xmlChar *) "getetag", NULL);
1127
1128         node = xmlNewTextChild (root, nscd, (xmlChar *) "filter", NULL);
1129         node = xmlNewTextChild (node, nscd, (xmlChar *) "comp-filter", NULL);
1130         xmlSetProp (node, (xmlChar *) "name", (xmlChar *) "VCALENDAR");
1131         
1132         sn = xmlNewTextChild (node, nscd, (xmlChar *) "comp-filter", NULL);
1133         xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VEVENT");
1134         /* ^^^ add timerange for performance?  */
1135         
1136         
1137         buf = xmlAllocOutputBuffer (NULL);
1138         xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
1139         xmlOutputBufferFlush (buf);
1140
1141         /* Prepare the soup message */
1142         message = soup_message_new ("REPORT", priv->uri);
1143         soup_message_add_header (message->request_headers, 
1144                                  "User-Agent", "Evolution/" VERSION);
1145         soup_message_add_header (message->request_headers, 
1146                                  "Depth", "1");
1147
1148         soup_message_set_request (message, 
1149                                   "application/xml",
1150                                   SOUP_BUFFER_USER_OWNED,
1151                                   (char *) buf->buffer->content,
1152                                   buf->buffer->use);
1153
1154         message_setup_debug (message);
1155
1156         /* Send the request now */
1157         soup_session_send_message (priv->session, message);
1158         
1159         /* Clean up the memory */
1160         xmlOutputBufferClose (buf);
1161         xmlFreeDoc (doc);
1162
1163         /* Check the result */
1164         if (message->status_code != 207) {
1165                 g_warning ("Sever did not response with 207\n");
1166                 return FALSE;
1167         }
1168         
1169         /* Parse the response body */
1170         result = parse_report_response (message, objs, len);
1171
1172         g_object_unref (message);
1173         return result;
1174 }
1175
1176
1177 static ECalBackendSyncStatus
1178 caldav_server_get_object (ECalBackendCalDAV *cbdav, CalDAVObject *object)
1179 {
1180         ECalBackendCalDAVPrivate *priv;
1181         ECalBackendSyncStatus     result;
1182         SoupMessage              *message;
1183         const char               *hdr;
1184         char                     *uri;
1185
1186         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);        
1187         result = GNOME_Evolution_Calendar_Success;
1188
1189         g_assert (object != NULL && object->href != NULL);
1190         
1191         uri = caldav_generate_uri (cbdav, object->href);
1192         message = soup_message_new (SOUP_METHOD_GET, uri);
1193         g_free (uri);
1194
1195         soup_message_add_header (message->request_headers, 
1196                                  "User-Agent", "Evolution/" VERSION);
1197
1198         message_setup_debug (message);
1199
1200         soup_session_send_message (priv->session, message);
1201         
1202         if (! SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
1203                 result = status_code_to_result (message->status_code);
1204                 g_object_unref (message);
1205                 g_warning ("Could not fetch object from server\n");
1206                 return result;
1207         }
1208
1209         hdr = soup_message_get_header (message->response_headers, "Content-Type");
1210
1211         if (hdr == NULL || g_ascii_strcasecmp (hdr, "text/calendar")) {
1212                 result = GNOME_Evolution_Calendar_InvalidObject;
1213                 g_object_unref (message);
1214                 g_warning ("Object to fetch not of type text/calendar");
1215                 return result;
1216         }
1217
1218         hdr = soup_message_get_header (message->response_headers, "ETag");
1219         
1220         if (hdr == NULL) {
1221                 g_warning ("UUHH no ETag, now that's bad!");
1222                 object->etag = NULL;
1223         } else {
1224                 object->etag = quote_etag (hdr);
1225         }
1226         
1227         /* Need to NULL terminate the string, do we? */
1228         object->cdata = g_malloc0 (message->response.length + 1);
1229         memcpy (object->cdata, message->response.body, message->response.length);
1230         g_object_unref (message);
1231         
1232         return result;
1233 }
1234
1235 static ECalBackendSyncStatus
1236 caldav_server_put_object (ECalBackendCalDAV *cbdav, CalDAVObject *object)
1237 {
1238         ECalBackendCalDAVPrivate *priv;
1239         ECalBackendSyncStatus     result;
1240         SoupMessage              *message;
1241         const char               *hdr;
1242         char                     *uri;
1243
1244         priv   = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);      
1245         result = GNOME_Evolution_Calendar_Success;
1246         hdr    = NULL;
1247         
1248         g_assert (object != NULL && object->cdata != NULL);
1249
1250         uri = caldav_generate_uri (cbdav, object->href);
1251         message = soup_message_new (SOUP_METHOD_PUT, uri);
1252         g_free (uri);
1253
1254         soup_message_add_header (message->request_headers, 
1255                                  "User-Agent", "Evolution/" VERSION);
1256
1257         /* For new items we use the If-None-Match so we don't
1258          * acidently override resources, for item updates we
1259          * use the If-Match header to avoid the Lost-update 
1260          * problem */
1261         if (object->etag == NULL) {
1262                 soup_message_add_header (message->request_headers, 
1263                                          "If-None-Match", "*");
1264         } else {
1265                 soup_message_add_header (message->request_headers, 
1266                                          "If-Match", object->etag);
1267         }
1268         
1269         soup_message_set_request (message, 
1270                                   "text/calendar", 
1271                                   SOUP_BUFFER_USER_OWNED, 
1272                                   object->cdata,
1273                                   strlen (object->cdata));
1274
1275         
1276         message_setup_debug (message);
1277
1278         soup_session_send_message (priv->session, message);
1279
1280         /* FIXME: sepcial case precondition errors ?*/
1281         result = status_code_to_result (message->status_code);  
1282
1283         if (result == GNOME_Evolution_Calendar_Success) {       
1284                 hdr = soup_message_get_header (message->response_headers,
1285                                                "ETag");
1286         }
1287         
1288         if (hdr != NULL) {
1289                 g_free (object->etag);
1290                 object->etag = quote_etag (hdr);
1291         } else {
1292                 g_warning ("Ups no Etag in put response");
1293         }
1294
1295         
1296         g_object_unref (message);
1297         return result;  
1298 }
1299
1300 static ECalBackendSyncStatus
1301 caldav_server_delete_object (ECalBackendCalDAV *cbdav, CalDAVObject *object)
1302 {
1303         ECalBackendCalDAVPrivate *priv;
1304         ECalBackendSyncStatus     result;
1305         SoupMessage              *message;
1306         char                     *uri;
1307
1308         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);        
1309         result = GNOME_Evolution_Calendar_Success;
1310         
1311         g_assert (object != NULL && object->href != NULL);
1312
1313         uri = caldav_generate_uri (cbdav, object->href);
1314         message = soup_message_new (SOUP_METHOD_DELETE, uri);
1315         g_free (uri);
1316         
1317         soup_message_add_header (message->request_headers, 
1318                                  "User-Agent", "Evolution/" VERSION);
1319
1320         if (object->etag != NULL) {
1321                 soup_message_add_header (message->request_headers, 
1322                                         "If-Match", object->etag);
1323         }
1324         
1325         message_setup_debug (message);
1326
1327         soup_session_send_message (priv->session, message);
1328         
1329         result = status_code_to_result (message->status_code);  
1330         
1331         g_object_unref (message);
1332
1333         return result;
1334 }
1335         
1336 /* ************************************************************************* */
1337 /* Synchronization foo */
1338
1339 static gboolean
1340 synchronize_object (ECalBackendCalDAV *cbdav, 
1341                     CalDAVObject      *object,
1342                     ECalComponent     *old_comp)
1343 {
1344         ECalBackendCalDAVPrivate *priv;
1345         ECalBackendCache         *bcache;
1346         ECalBackendSyncStatus     result;
1347         ECalBackend              *bkend;
1348         ECalComponent            *comp;
1349         icalcomponent            *icomp, *subcomp;
1350         icalcomponent_kind        kind;
1351         gboolean                  do_report;
1352         gboolean                  res;
1353
1354         comp = NULL;    
1355         res  = TRUE;
1356         result  = caldav_server_get_object (cbdav, object);
1357         
1358         if (result != GNOME_Evolution_Calendar_Success) {
1359                 g_warning ("Could not fetch object from server");
1360                 return FALSE;
1361         }
1362
1363         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);        
1364
1365         icomp = icalparser_parse_string (object->cdata);
1366         kind  = icalcomponent_isa (icomp);
1367         bkend = E_CAL_BACKEND (cbdav);
1368         
1369         if (kind == ICAL_VCALENDAR_COMPONENT) {
1370         
1371                 kind = e_cal_backend_get_kind (bkend);
1372                 subcomp = icalcomponent_get_first_component (icomp, kind);
1373
1374                 comp = e_cal_component_new ();
1375                 res = e_cal_component_set_icalcomponent (comp, 
1376                                                    icalcomponent_new_clone (subcomp));
1377                 if (res == TRUE) { 
1378                         e_cal_component_set_href (comp, object->href);
1379                         e_cal_component_set_etag (comp, object->etag);
1380                 } else {
1381                         g_object_unref (comp);
1382                         comp = NULL;
1383                 }
1384                 
1385         } else {
1386                 res = FALSE;    
1387         }
1388         
1389         icalcomponent_free (icomp);
1390
1391         if (res == FALSE) {
1392                 return res;
1393         }
1394                 
1395         bcache = priv->cache;
1396         do_report = priv->report_changes;
1397         
1398         if ((res = e_cal_backend_cache_put_component (bcache, comp)) 
1399             && do_report) {
1400                 char *new_cs = NULL;
1401                 char *old_cs = NULL;
1402
1403                 new_cs = e_cal_component_get_as_string (comp);
1404
1405                 if (old_comp == NULL) {
1406                         e_cal_backend_notify_object_created (bkend, new_cs);
1407                 } else {
1408                         old_cs = e_cal_component_get_as_string (old_comp);
1409                         e_cal_backend_notify_object_modified (bkend, old_cs, new_cs);   
1410                 }
1411                 
1412                 g_free (new_cs);
1413                 g_free (old_cs);
1414         }
1415
1416         g_object_unref (comp);
1417         
1418         return res;
1419 }
1420
1421 #define etags_match(_tag1, _tag2) ((_tag1 == _tag2) ? TRUE :                 \
1422                                    g_str_equal (_tag1 != NULL ? _tag1 : "",  \
1423                                                 _tag2 != NULL ? _tag2 : "")) 
1424
1425 static void
1426 synchronize_cache (ECalBackendCalDAV *cbdav)
1427 {
1428         ECalBackendCalDAVPrivate *priv;
1429         ECalBackendCache         *bcache;
1430         CalDAVObject             *sobjs;
1431         CalDAVObject             *object;
1432         GHashTable               *hindex;
1433         GList                    *cobjs;
1434         GList                    *citer;
1435         gboolean                  res;
1436         int                       len;
1437         int                       i;
1438         
1439         priv   = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);      
1440         bcache = priv->cache;
1441         len    = 0;
1442         sobjs  = NULL;
1443         
1444         res = caldav_server_list_objects (cbdav, &sobjs, &len);
1445         
1446         if (res == FALSE) {
1447                 /* FIXME: bloek! */
1448                 g_warning ("Could not synch server BLehh!");
1449                 return;
1450         }
1451
1452         hindex = g_hash_table_new (g_str_hash, g_str_equal);
1453         cobjs = e_cal_backend_cache_get_components (bcache);
1454
1455         /* build up a index for the href entry */       
1456         for (citer = cobjs; citer; citer = g_list_next (citer)) {
1457                 ECalComponent *ccomp = E_CAL_COMPONENT (citer->data);
1458                 const char *href;       
1459                 
1460                 href = e_cal_component_get_href (ccomp);
1461                 
1462                 if (href == NULL) {
1463                         g_warning ("href of object NULL :(");
1464                         continue;
1465                 }
1466                         
1467                 g_hash_table_insert (hindex, (gpointer) href, ccomp);
1468         }
1469         
1470         /* see if we have to upate or add some objects */
1471         for (i = 0, object = sobjs; i < len; i++, object++) {
1472                 ECalComponent *ccomp;
1473                 const char *etag = NULL;
1474
1475                 if (object->status != 200) {
1476                         /* just continue here, so that the object
1477                          * doesnt get removed from the cobjs list
1478                          * - therefore it will be removed */
1479                         continue;
1480                 }
1481
1482                 res = TRUE;
1483                 ccomp = g_hash_table_lookup (hindex, object->href);
1484                 
1485                 if (ccomp != NULL) {
1486                         etag = e_cal_component_get_etag (ccomp);        
1487                 } 
1488                 
1489                 if (!etag || !etags_match (etag, object->etag)) {
1490                         res = synchronize_object (cbdav, object, ccomp);
1491                 }
1492                 
1493                 if (res == TRUE) {
1494                         cobjs = g_list_remove (cobjs, ccomp);
1495                 }
1496
1497                 caldav_object_free (object, FALSE);
1498         }
1499
1500         /* remove old (not on server anymore) items from cache */
1501         for (citer = cobjs; citer; citer = g_list_next (citer)) {
1502                 ECalComponent *comp;
1503                 const char *uid;
1504                 
1505                 comp = E_CAL_COMPONENT (citer->data);
1506                 e_cal_component_get_uid (comp, &uid);
1507
1508                 if (e_cal_backend_cache_remove_component (bcache, uid, NULL) && 
1509                     priv->report_changes) {
1510                         char *str = e_cal_component_get_as_string (comp);
1511                         ECalComponentId *id = e_cal_component_get_id (comp);
1512                         
1513                         e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbdav), 
1514                                                              id, str, NULL);
1515                         e_cal_component_free_id (id);
1516                         g_free (str);
1517                 }
1518
1519                 g_object_unref (comp);
1520         }
1521         
1522         g_hash_table_destroy (hindex);
1523         g_list_free (cobjs);
1524         
1525 }
1526
1527 /* ************************************************************************* */
1528 static gpointer 
1529 synch_slave_loop (gpointer data)
1530 {
1531         ECalBackendCalDAVPrivate *priv;
1532         ECalBackendCalDAV        *cbdav;
1533         
1534         cbdav = E_CAL_BACKEND_CALDAV (data);    
1535         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1536
1537         g_mutex_lock (priv->lock);      
1538
1539         while (priv->slave_cmd != SLAVE_SHOULD_DIE) {
1540                 GTimeVal alarm_clock;   
1541                 if (priv->slave_cmd == SLAVE_SHOULD_SLEEP) {
1542                         /* just sleep until we get woken up again */
1543                         g_cond_wait (priv->cond, priv->lock);
1544                         
1545                         /* check if we should die, work or sleep again */
1546                         continue;
1547                 }
1548
1549                 /* Ok here we go, do some real work 
1550                  * Synch it baby one more time ...
1551                  */
1552                 //d(g_print ("Synch-Slave: Goint to work ...\n")); XXX re-enable output please
1553                 synchronize_cache (cbdav); 
1554
1555                 /* puhh that was hard, get some rest :) */
1556                 g_get_current_time (&alarm_clock);
1557                 alarm_clock.tv_sec += priv->refresh_time.tv_sec;
1558                 g_cond_timed_wait (priv->cond, 
1559                                    priv->lock, 
1560                                    &alarm_clock);
1561
1562         }
1563
1564         /* we got killed ... */ 
1565         g_mutex_unlock (priv->lock);
1566         return NULL;    
1567 }
1568
1569 /* ************************************************************************* */
1570 /* ********** ECalBackendSync virtual function implementation *************  */
1571
1572 static ECalBackendSyncStatus
1573 caldav_is_read_only (ECalBackendSync *backend, 
1574                      EDataCal        *cal, 
1575                      gboolean        *read_only)
1576 {
1577         ECalBackendCalDAV        *cbdav;
1578         ECalBackendCalDAVPrivate *priv;
1579
1580         cbdav = E_CAL_BACKEND_CALDAV (backend);
1581         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1582
1583         /* no write support in offline mode yet! */
1584         if (priv->mode == CAL_MODE_LOCAL) {
1585                 *read_only = TRUE;
1586         } else {
1587                 *read_only = priv->read_only;
1588         }
1589
1590         return GNOME_Evolution_Calendar_Success;        
1591 }
1592
1593
1594 static ECalBackendSyncStatus
1595 caldav_get_cal_address (ECalBackendSync  *backend, 
1596                         EDataCal         *cal, 
1597                         char            **address)
1598 {
1599         *address = NULL;
1600         return GNOME_Evolution_Calendar_Success;
1601 }
1602
1603
1604
1605 static ECalBackendSyncStatus
1606 caldav_get_ldap_attribute (ECalBackendSync  *backend, 
1607                            EDataCal         *cal, 
1608                            char           **attribute)
1609 {
1610         *attribute = NULL;
1611         return GNOME_Evolution_Calendar_Success;
1612 }
1613
1614 static ECalBackendSyncStatus
1615 caldav_get_alarm_email_address (ECalBackendSync  *backend, 
1616                                 EDataCal         *cal, 
1617                                 char            **address)
1618 {
1619         *address = NULL;
1620         return GNOME_Evolution_Calendar_Success;
1621 }
1622
1623 static ECalBackendSyncStatus
1624 caldav_get_static_capabilities (ECalBackendSync  *backend, 
1625                                 EDataCal         *cal, 
1626                                 char            **capabilities)
1627 {
1628         *capabilities = g_strdup (CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS ","
1629                                   CAL_STATIC_CAPABILITY_NO_THISANDFUTURE ","
1630                                   CAL_STATIC_CAPABILITY_NO_THISANDPRIOR);
1631         
1632         return GNOME_Evolution_Calendar_Success;
1633 }
1634
1635 static ECalBackendSyncStatus
1636 initialize_backend (ECalBackendCalDAV *cbdav)
1637 {       
1638         ECalBackendSyncStatus     result;
1639         ECalBackendCalDAVPrivate *priv;
1640         ESource                  *source;
1641         GThread                  *slave;
1642         const char               *os_val;
1643         const char               *uri;
1644         gsize                     len;
1645         
1646         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1647         
1648         result = GNOME_Evolution_Calendar_Success;
1649         source = e_cal_backend_get_source (E_CAL_BACKEND (cbdav));
1650
1651         os_val = e_source_get_property (source, "offline_sync");
1652
1653         if (!os_val || !g_str_equal (os_val, "1")) {
1654                 priv->do_offline = FALSE;
1655         }
1656
1657         os_val = e_source_get_property (source, "auth");
1658         
1659         if (os_val) {
1660                 priv->need_auth = TRUE;
1661         }
1662         
1663         os_val = e_source_get_property(source, "ssl");
1664         uri = e_cal_backend_get_uri (E_CAL_BACKEND (cbdav));
1665
1666
1667
1668         if (g_str_has_prefix (uri, "caldav://")) {
1669                 const char *proto;
1670
1671                 if (os_val && os_val[0] == '1') {
1672                         proto = "https://";
1673                 } else {
1674                         proto = "http://";
1675                 }
1676
1677                 priv->uri = g_strconcat (proto, uri + 9, NULL);
1678
1679         } else {
1680
1681                 priv->uri = g_strdup (uri);
1682         } 
1683
1684         /* remove trailing slashes */
1685         len = strlen (priv->uri);
1686         while (len--) {
1687                 if (priv->uri[len] == '/') {
1688                         priv->uri[len] = '\0';
1689                 } else {
1690                         break;
1691                 }
1692         }
1693
1694         if (priv->cache == NULL) {
1695                 priv->cache = e_cal_backend_cache_new (priv->uri, E_CAL_SOURCE_TYPE_EVENT);
1696
1697                 if (priv->cache == NULL) {
1698                         result = GNOME_Evolution_Calendar_OtherError;
1699                         goto out;
1700                 }
1701                 
1702         }
1703
1704         priv->slave_cmd = SLAVE_SHOULD_SLEEP;
1705         slave = g_thread_create (synch_slave_loop, cbdav, FALSE, NULL);
1706
1707         if (slave == NULL) {
1708                 g_warning ("Could not create synch slave");
1709                 result = GNOME_Evolution_Calendar_OtherError;
1710         }
1711         
1712         priv->report_changes = TRUE;    
1713         priv->synch_slave = slave;
1714         priv->loaded = TRUE;    
1715 out:
1716         return result;
1717 }
1718
1719
1720 static ECalBackendSyncStatus
1721 caldav_do_open (ECalBackendSync *backend, 
1722                 EDataCal        *cal, 
1723                 gboolean         only_if_exists,
1724                 const char      *username, 
1725                 const char      *password)
1726 {
1727         ECalBackendCalDAV        *cbdav;
1728         ECalBackendCalDAVPrivate *priv;
1729         ECalBackendSyncStatus     status;
1730         
1731         cbdav = E_CAL_BACKEND_CALDAV (backend);
1732         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1733
1734         status = GNOME_Evolution_Calendar_Success;
1735         
1736         g_mutex_lock (priv->lock);
1737         
1738         if (priv->loaded != TRUE) {
1739                 priv->ostatus = initialize_backend (cbdav);
1740         }       
1741         
1742         if (priv->ostatus != GNOME_Evolution_Calendar_Success) {
1743                 g_mutex_unlock (priv->lock);
1744                 return status;
1745         }
1746
1747
1748         if (priv->need_auth == TRUE) {
1749                 if ((username == NULL || password == NULL)) {
1750                         g_mutex_unlock (priv->lock);
1751                         return GNOME_Evolution_Calendar_AuthenticationRequired;
1752                 }
1753                 
1754                 priv->username = g_strdup (username);
1755                 priv->password = g_strdup (password);
1756                 priv->need_auth = FALSE;
1757         }
1758         
1759         if (! priv->do_offline && priv->mode == CAL_MODE_LOCAL) {
1760                 g_mutex_unlock (priv->lock);
1761                 return GNOME_Evolution_Calendar_RepositoryOffline; 
1762         }
1763
1764         if (priv->mode == CAL_MODE_REMOTE) {
1765                 /* set forward proxy */
1766                 caldav_set_session_proxy (priv);
1767         
1768                 status = caldav_server_open_calendar (cbdav);
1769
1770                 if (status == GNOME_Evolution_Calendar_Success) {
1771                         priv->slave_cmd = SLAVE_SHOULD_WORK;
1772                         g_cond_signal (priv->cond);
1773                 }
1774         } else {
1775                 priv->read_only = TRUE;
1776         }
1777
1778         g_mutex_unlock (priv->lock);
1779         
1780         return status;
1781 }
1782
1783 static ECalBackendSyncStatus
1784 caldav_remove (ECalBackendSync *backend, 
1785                EDataCal        *cal)
1786 {
1787         ECalBackendCalDAV        *cbdav;
1788         ECalBackendCalDAVPrivate *priv;
1789         ECalBackendSyncStatus     status;
1790         gboolean                  online;       
1791         
1792         cbdav = E_CAL_BACKEND_CALDAV (backend);
1793         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1794
1795         g_mutex_lock (priv->lock);
1796         
1797         if (priv->loaded != TRUE) {
1798                 g_mutex_unlock (priv->lock);
1799                 return GNOME_Evolution_Calendar_Success;
1800         }
1801         
1802         status = check_state (cbdav, &online);
1803
1804         if (status != GNOME_Evolution_Calendar_Success) {
1805                 g_mutex_unlock (priv->lock);
1806                 return status;
1807         }
1808
1809         e_file_cache_remove (E_FILE_CACHE (priv->cache));
1810         priv->cache  = NULL;
1811         priv->loaded = FALSE;   
1812         priv->slave_cmd = SLAVE_SHOULD_DIE;
1813         g_cond_signal (priv->cond);
1814         g_mutex_unlock (priv->lock);
1815         
1816         return GNOME_Evolution_Calendar_Success;
1817 }
1818
1819
1820 static char *
1821 pack_cobj (ECalBackendCalDAV *cbdav, ECalComponent *ecomp) 
1822 {
1823         ECalBackendCalDAVPrivate *priv;
1824         icalcomponent *calcomp;
1825         icalcomponent *icomp;
1826         char          *objstr;
1827         
1828         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1829
1830         icomp = e_cal_component_get_icalcomponent (ecomp);
1831
1832         if (icalcomponent_isa (icomp) != ICAL_VCALENDAR_COMPONENT) {
1833                 icalcomponent *cclone;
1834
1835                 calcomp = e_cal_util_new_top_level ();
1836                 cclone = icalcomponent_new_clone (icomp);
1837                 icalcomponent_add_component (calcomp, cclone);
1838                 e_cal_util_add_timezones_from_component(calcomp,
1839                                                         cclone);
1840         } else {
1841                 calcomp = icalcomponent_new_clone (icomp);
1842         }
1843
1844         objstr = icalcomponent_as_ical_string (calcomp);
1845         
1846         g_assert (objstr);
1847         
1848         return g_strdup (objstr);
1849                 
1850 }
1851
1852
1853 static ECalBackendSyncStatus
1854 caldav_create_object (ECalBackendSync  *backend, 
1855                       EDataCal         *cal, 
1856                       char            **calobj, 
1857                       char            **uid)
1858 {
1859         ECalBackendCalDAV        *cbdav;
1860         ECalBackendCalDAVPrivate *priv;
1861         ECalBackendSyncStatus     status;
1862         ECalComponent            *comp;
1863         gboolean                  online;
1864         char                     *href;
1865         
1866         cbdav = E_CAL_BACKEND_CALDAV (backend);
1867         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1868
1869         g_mutex_lock (priv->lock);
1870
1871         status = check_state (cbdav, &online);
1872
1873         if (status != GNOME_Evolution_Calendar_Success) {
1874                 g_mutex_unlock (priv->lock);
1875                 return status;
1876         }
1877
1878         comp = e_cal_component_new_from_string (*calobj);
1879
1880         if (comp == NULL) {
1881                 g_mutex_unlock (priv->lock);
1882                 return GNOME_Evolution_Calendar_InvalidObject;
1883         }
1884         
1885         if (online) {
1886                 CalDAVObject object;
1887         
1888                 href = e_cal_component_gen_href (comp); 
1889         
1890                 object.href  = href;
1891                 object.etag  = NULL;
1892                 object.cdata = pack_cobj (cbdav, comp);
1893
1894                 status = caldav_server_put_object (cbdav, &object);
1895
1896                 e_cal_component_set_etag (comp, object.etag);
1897                 caldav_object_free (&object, FALSE);
1898                 
1899         } else {
1900                 /* mark component as out of synch */
1901                 e_cal_component_set_synch_state (comp, 
1902                                 E_CAL_COMPONENT_LOCALLY_CREATED);
1903         }
1904
1905         if (status != GNOME_Evolution_Calendar_Success) {
1906                 g_object_unref (comp);
1907                 g_mutex_unlock (priv->lock);
1908                 return status;  
1909         }
1910         
1911         /* We should prolly check for cache errors
1912          * but when that happens we are kinda hosed anyway */
1913         e_cal_backend_cache_put_component (priv->cache, comp);
1914         *calobj = e_cal_component_get_as_string (comp);
1915         
1916         g_mutex_unlock (priv->lock);
1917         
1918         return status;  
1919 }
1920
1921 static ECalBackendSyncStatus
1922 caldav_modify_object (ECalBackendSync  *backend, 
1923                       EDataCal         *cal, 
1924                       const char       *calobj,
1925                       CalObjModType     mod, 
1926                       char            **old_object,
1927                       char            **new_object)
1928 {
1929         ECalBackendCalDAV        *cbdav;
1930         ECalBackendCalDAVPrivate *priv;
1931         ECalBackendSyncStatus     status;
1932         ECalComponent            *comp;
1933         ECalComponent            *cache_comp;
1934         gboolean                  online;
1935         const char               *uid = NULL;
1936         
1937         cbdav = E_CAL_BACKEND_CALDAV (backend);
1938         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
1939
1940         g_mutex_lock (priv->lock);
1941
1942         status = check_state (cbdav, &online);
1943
1944         if (status != GNOME_Evolution_Calendar_Success) {
1945                 g_mutex_unlock (priv->lock);
1946                 return status;
1947         }
1948
1949         comp = e_cal_component_new_from_string (calobj);
1950
1951         if (comp == NULL) {
1952                 g_mutex_unlock (priv->lock);
1953                 return GNOME_Evolution_Calendar_InvalidObject;
1954         }
1955         
1956         e_cal_component_get_uid (comp, &uid);
1957         
1958         cache_comp = e_cal_backend_cache_get_component (priv->cache, uid, NULL);
1959         if (cache_comp == NULL) {
1960                 g_mutex_unlock (priv->lock);
1961                 return GNOME_Evolution_Calendar_ObjectNotFound;
1962         }
1963
1964         if (online) {
1965                 CalDAVObject object;
1966         
1967                 object.href  = g_strdup (e_cal_component_get_href (cache_comp));
1968                 object.etag  = g_strdup (e_cal_component_get_etag (cache_comp));
1969                 object.cdata = pack_cobj (cbdav, comp);
1970
1971                 status = caldav_server_put_object (cbdav, &object);
1972
1973                 e_cal_component_set_etag (comp, object.etag);
1974                 caldav_object_free (&object, FALSE);
1975                 
1976         } else {
1977                 /* mark component as out of synch */
1978                 e_cal_component_set_synch_state (comp, 
1979                                 E_CAL_COMPONENT_LOCALLY_MODIFIED);
1980         }
1981
1982         if (status != GNOME_Evolution_Calendar_Success) {
1983                 g_object_unref (comp);
1984                 g_mutex_unlock (priv->lock);
1985                 return status;  
1986         }
1987         
1988         /* We should prolly check for cache errors
1989          * but when that happens we are kinda hosed anyway */
1990         e_cal_backend_cache_put_component (priv->cache, comp);
1991         *old_object = e_cal_component_get_as_string (cache_comp);
1992         *new_object = e_cal_component_get_as_string (comp);
1993         
1994         g_mutex_unlock (priv->lock);
1995         
1996         return status;  
1997 }
1998
1999 static ECalBackendSyncStatus
2000 caldav_remove_object (ECalBackendSync  *backend, 
2001                       EDataCal         *cal,
2002                       const char       *uid, 
2003                       const char       *rid,
2004                       CalObjModType     mod, 
2005                       char            **old_object,
2006                       char            **object)
2007 {
2008         ECalBackendCalDAV        *cbdav;
2009         ECalBackendCalDAVPrivate *priv;
2010         ECalBackendSyncStatus     status;
2011         ECalComponent            *cache_comp;
2012         gboolean                  online;
2013         
2014         cbdav = E_CAL_BACKEND_CALDAV (backend);
2015         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2016
2017         g_mutex_lock (priv->lock);
2018
2019         status = check_state (cbdav, &online);
2020
2021         if (status != GNOME_Evolution_Calendar_Success) {
2022                 g_mutex_unlock (priv->lock);
2023                 return status;
2024         }
2025
2026         cache_comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
2027         if (cache_comp == NULL) {
2028                 g_mutex_unlock (priv->lock);
2029                 return GNOME_Evolution_Calendar_ObjectNotFound;
2030         }
2031
2032         if (online) {
2033                 CalDAVObject caldav_object;
2034         
2035                 caldav_object.href  = g_strdup (e_cal_component_get_href (cache_comp));
2036                 caldav_object.etag  = g_strdup (e_cal_component_get_etag (cache_comp));
2037                 caldav_object.cdata = NULL;
2038
2039                 status = caldav_server_delete_object (cbdav, &caldav_object);
2040
2041                 caldav_object_free (&caldav_object, FALSE);
2042                 
2043         } else {
2044                 /* mark component as out of synch */
2045                 e_cal_component_set_synch_state (cache_comp, 
2046                                 E_CAL_COMPONENT_LOCALLY_DELETED);
2047         }
2048
2049         if (status != GNOME_Evolution_Calendar_Success) {
2050                 g_mutex_unlock (priv->lock);
2051                 return status;  
2052         }
2053         
2054         *old_object = e_cal_component_get_as_string (cache_comp);
2055
2056         /* We should prolly check for cache errors
2057          * but when that happens we are kinda hosed anyway */
2058         e_cal_backend_cache_remove_component (priv->cache, uid, rid);
2059
2060         /* FIXME: set new_object when removing instances of a recurring appointment */
2061         
2062         g_mutex_unlock (priv->lock);
2063         
2064         return status;  
2065 }
2066
2067 static ECalBackendSyncStatus
2068 caldav_discard_alarm (ECalBackendSync *backend,
2069                       EDataCal        *cal,
2070                       const char      *uid,
2071                       const char      *auid)
2072 {
2073         return GNOME_Evolution_Calendar_Success;
2074 }
2075
2076 /* FIXME: use list here? */
2077 static ECalBackendSyncStatus
2078 extract_objects (icalcomponent       *icomp,
2079                  icalcomponent_kind   ekind,
2080                  GList              **objects)
2081 {
2082         icalcomponent         *scomp;
2083         icalcomponent_kind     kind;
2084         
2085         g_return_val_if_fail (icomp, GNOME_Evolution_Calendar_OtherError);
2086         g_return_val_if_fail (objects, GNOME_Evolution_Calendar_OtherError);
2087         
2088         kind = icalcomponent_isa (icomp);
2089         
2090         if (kind == ekind) {
2091                 *objects = g_list_prepend (NULL, icomp);
2092                 return GNOME_Evolution_Calendar_Success;
2093         }
2094
2095         if (kind != ICAL_VCALENDAR_COMPONENT) {
2096                 return GNOME_Evolution_Calendar_InvalidObject;
2097         }
2098
2099         *objects = NULL;
2100         scomp = icalcomponent_get_first_component (icomp, 
2101                                                    ekind);
2102
2103         while (scomp) {
2104                 
2105                 /* Remove components from toplevel here */
2106                 *objects = g_list_prepend (*objects, scomp);
2107                 icalcomponent_remove_component (icomp, scomp);
2108                 
2109                 scomp = icalcomponent_get_next_component (icomp, ekind);
2110         }
2111
2112         return GNOME_Evolution_Calendar_Success;
2113 }
2114
2115 #define is_error(__status) (__status != GNOME_Evolution_Calendar_Success)
2116
2117 static ECalBackendSyncStatus
2118 process_object (ECalBackendCalDAV   *cbdav,
2119                 ECalComponent       *ecomp,
2120                 gboolean             online,
2121                 icalproperty_method  method)
2122 {
2123         ECalBackendCalDAVPrivate *priv;
2124         ECalBackendSyncStatus     status;
2125         ECalBackend              *backend;
2126         ECalComponent            *ccomp;
2127         struct icaltimetype       now;
2128         ECalComponentId          *id;
2129         const char               *uid;  
2130         const char               *rid;
2131         char                     *ostr;
2132         char                     *oostr;
2133         
2134         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2135         backend = E_CAL_BACKEND (cbdav);
2136         
2137         /* ctime, mtime */
2138         now = icaltime_from_timet (time (NULL), 0);
2139         e_cal_component_set_created (ecomp, &now);
2140         e_cal_component_set_last_modified (ecomp, &now);
2141
2142         e_cal_component_get_uid (ecomp, &uid);
2143         rid = e_cal_component_get_recurid_as_string (ecomp);
2144
2145         ccomp = e_cal_backend_cache_get_component (priv->cache, uid, NULL);
2146
2147         if (ccomp != NULL) {
2148                 oostr = e_cal_component_get_as_string (ccomp);  
2149         } else {
2150                 oostr = NULL;
2151         }
2152         
2153         ostr = e_cal_component_get_as_string (ecomp);   
2154         
2155         status = GNOME_Evolution_Calendar_Success;
2156         
2157         switch (method) {
2158         
2159                 case ICAL_METHOD_PUBLISH:
2160                 case ICAL_METHOD_REQUEST:
2161                 case ICAL_METHOD_REPLY:
2162
2163                 if (online) {
2164                         CalDAVObject object = { NULL, };
2165
2166                         if (ccomp) {
2167                                 const char *href;
2168                                 const char *etag;
2169
2170                                 href = e_cal_component_get_href (ccomp);
2171                                 etag = e_cal_component_get_etag (ccomp);
2172                                 
2173                                 object.href  = g_strdup (href);
2174                                 object.etag  = g_strdup (etag);
2175
2176                         } else {
2177                                 object.href = e_cal_component_gen_href (ecomp);
2178                         }
2179                         
2180                         object.cdata = pack_cobj (cbdav, ecomp);
2181                         status = caldav_server_put_object (cbdav, &object);
2182                         e_cal_component_set_etag (ecomp, object.etag);
2183                         caldav_object_free (&object, FALSE);
2184                 } else {
2185                         ECalComponentSyncState sstate;
2186                         
2187                         if (ccomp) {
2188                                 sstate = E_CAL_COMPONENT_LOCALLY_MODIFIED;
2189                         } else {
2190                                 sstate = E_CAL_COMPONENT_LOCALLY_CREATED;
2191                         }
2192
2193                         e_cal_component_set_synch_state (ecomp, sstate); 
2194
2195                 }
2196                 
2197                 if (status != GNOME_Evolution_Calendar_Success) {
2198                         break;
2199                 }
2200
2201                 e_cal_backend_cache_put_component (priv->cache, ecomp);
2202                         
2203                 if (ccomp) {
2204
2205                         e_cal_backend_notify_object_modified (backend,
2206                                                               ostr,
2207                                                               oostr);
2208
2209                 } else {
2210                                 
2211                         e_cal_backend_notify_object_created (backend,
2212                                                              ostr);
2213                 }
2214                 
2215                 break;
2216
2217
2218                 case ICAL_METHOD_CANCEL:
2219         
2220                         if (ccomp == NULL) {
2221                                 status = GNOME_Evolution_Calendar_ObjectNotFound;
2222                                 break;
2223                         }
2224         
2225                         /* FIXME: this is not working for instances
2226                          * of recurring appointments - yet - */ 
2227                         if (online) {
2228                                 CalDAVObject object;
2229                                 const char *href;
2230                                 const char *etag;
2231
2232                                 href = e_cal_component_get_href (ccomp);
2233                                 etag = e_cal_component_get_etag (ccomp);
2234                                 
2235                                 object.href  = g_strdup (href);
2236                                 object.etag  = g_strdup (etag);
2237                                 object.cdata = NULL;
2238
2239                                 status = caldav_server_delete_object (cbdav,
2240                                                                       &object);
2241                         
2242                                 caldav_object_free (&object, FALSE);
2243                                 
2244                         } else {
2245                                 /* mark component as out of synch */
2246                                 e_cal_component_set_synch_state (ecomp, 
2247                                 E_CAL_COMPONENT_LOCALLY_DELETED);
2248                                 
2249                         }       
2250                 
2251                         if (status != GNOME_Evolution_Calendar_Success) {
2252                                 break;
2253                         }
2254
2255                         e_cal_backend_cache_remove_component (priv->cache,
2256                                                               uid,
2257                                                               rid);
2258
2259                         id = e_cal_component_get_id (ccomp);
2260                         
2261                         e_cal_backend_notify_object_removed (E_CAL_BACKEND (backend),
2262                                                              id,
2263                                                              oostr,
2264                                                              ostr);
2265                         break;
2266
2267                 default:
2268                         /* WTF ? */
2269                         status = GNOME_Evolution_Calendar_UnsupportedMethod;
2270                         break;
2271         }
2272
2273         g_object_unref (ecomp);
2274         g_free (ostr);
2275         g_free (oostr);
2276         
2277         if (ccomp) {
2278                 g_object_unref (ccomp);
2279         }
2280
2281         return status;
2282 }
2283
2284 static ECalBackendSyncStatus
2285 caldav_receive_objects (ECalBackendSync *backend,
2286                         EDataCal        *cal, 
2287                         const char      *calobj)
2288 {
2289         ECalBackendCalDAV        *cbdav;
2290         ECalBackendCalDAVPrivate *priv;
2291         ECalBackendSyncStatus     status;
2292         icalcomponent            *icomp;
2293         icalcomponent_kind        kind;
2294         icalproperty_method       tmethod;
2295         gboolean                  online;
2296         GList                    *timezones;
2297         GList                    *objects;
2298         GList                    *iter;
2299         
2300         cbdav = E_CAL_BACKEND_CALDAV (backend);
2301         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2302         
2303         icomp = icalparser_parse_string (calobj);
2304         
2305         /* Try to parse cal object string */
2306         if (icomp == NULL) {
2307                 return GNOME_Evolution_Calendar_InvalidObject;
2308         }
2309
2310         /* FIXME: use the e_cal_backend_xxx_kind call here */
2311         kind = ICAL_VEVENT_COMPONENT;
2312         status = extract_objects (icomp, kind, &objects);
2313
2314         if (status != GNOME_Evolution_Calendar_Success) {
2315                 return status;
2316         }
2317
2318         /* Extract optional timezone compnents */
2319         kind = ICAL_VTIMEZONE_COMPONENT;
2320         status = extract_objects (icomp, kind, &timezones);
2321         
2322         if (status == GNOME_Evolution_Calendar_Success) {
2323                 /* FIXME: */
2324                 /* Do something usefull with the timezone */
2325         }
2326         
2327         /*   */
2328         g_mutex_lock (priv->lock);
2329
2330         status = check_state (cbdav, &online);
2331
2332         if (status != GNOME_Evolution_Calendar_Success) {
2333                 /* FIXME: free components here */
2334                 g_mutex_unlock (priv->lock);
2335                 return status;
2336         }
2337         
2338         tmethod = icalcomponent_get_method (icomp);
2339         
2340         for (iter = objects; iter && ! is_error (status); iter = iter->next) {
2341                 icalcomponent       *scomp;
2342                 ECalComponent       *ecomp;
2343                 icalproperty_method  method;
2344                 
2345                 scomp = (icalcomponent *) iter->data;
2346                 ecomp = e_cal_component_new ();
2347
2348                 e_cal_component_set_icalcomponent (ecomp, scomp);
2349
2350                 if (icalcomponent_get_first_property (scomp,
2351                                                       ICAL_METHOD_PROPERTY)) {
2352                         
2353                         method = icalcomponent_get_method (scomp);
2354                 } else {
2355                         method = tmethod;
2356                 }
2357                 
2358                 status = process_object (cbdav, ecomp, online, method);         
2359                 
2360                 g_object_unref (ecomp);
2361         }
2362
2363         g_list_free (objects);
2364         g_list_free (timezones);
2365         
2366         g_mutex_unlock (priv->lock);
2367
2368         return status;  
2369 }
2370
2371 static ECalBackendSyncStatus
2372 caldav_send_objects (ECalBackendSync  *backend, 
2373                      EDataCal         *cal,
2374                      const char       *calobj, 
2375                      GList           **users,
2376                      char            **modified_calobj)
2377 {       
2378         *users = NULL;
2379         *modified_calobj = g_strdup (calobj);
2380
2381         return GNOME_Evolution_Calendar_Success;
2382 }
2383
2384 static ECalBackendSyncStatus
2385 caldav_get_default_object (ECalBackendSync  *backend, 
2386                            EDataCal         *cal, 
2387                            char            **object)
2388 {
2389         ECalComponent *comp;
2390         
2391         comp = e_cal_component_new ();
2392         e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
2393         *object = e_cal_component_get_as_string (comp);
2394         g_object_unref (comp);
2395  
2396         return GNOME_Evolution_Calendar_Success;
2397 }
2398
2399 static ECalBackendSyncStatus
2400 caldav_get_object (ECalBackendSync  *backend,
2401                    EDataCal         *cal,
2402                    const char       *uid,
2403                    const char       *rid,
2404                    char           **object)
2405 {
2406         ECalBackendCalDAV        *cbdav;
2407         ECalBackendCalDAVPrivate *priv;
2408         ECalComponent            *comp;
2409         
2410         cbdav = E_CAL_BACKEND_CALDAV (backend);
2411         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2412
2413         g_mutex_lock (priv->lock);
2414         comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
2415         g_mutex_unlock (priv->lock);
2416         
2417         if (comp == NULL) {
2418                 *object = NULL;
2419                 return GNOME_Evolution_Calendar_ObjectNotFound;
2420         }
2421
2422         *object = e_cal_component_get_as_string (comp);
2423         g_object_unref (comp);
2424         
2425         return GNOME_Evolution_Calendar_Success;
2426 }
2427
2428 static ECalBackendSyncStatus
2429 caldav_get_timezone (ECalBackendSync  *backend, 
2430                      EDataCal         *cal,
2431                      const char       *tzid,
2432                      char            **object)
2433 {
2434         ECalBackendCalDAV        *cbdav;
2435         ECalBackendCalDAVPrivate *priv;
2436         const icaltimezone       *zone;
2437         icalcomponent            *icalcomp;
2438
2439         cbdav = E_CAL_BACKEND_CALDAV (backend);
2440         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2441
2442         g_return_val_if_fail (tzid, GNOME_Evolution_Calendar_ObjectNotFound);
2443
2444         /* first try to get the timezone from the cache */
2445         g_mutex_lock (priv->lock);
2446         zone = e_cal_backend_cache_get_timezone (priv->cache, tzid);
2447         g_mutex_unlock (priv->lock);
2448
2449         if (!zone) {
2450                 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
2451                 if (!zone) { 
2452                         return GNOME_Evolution_Calendar_ObjectNotFound;
2453                 }
2454         }
2455
2456         icalcomp = icaltimezone_get_component ((icaltimezone *) zone);
2457         
2458         if (!icalcomp) {
2459                 return GNOME_Evolution_Calendar_InvalidObject;
2460         }
2461                 
2462         *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
2463
2464         return GNOME_Evolution_Calendar_Success;
2465 }
2466
2467 static ECalBackendSyncStatus
2468 caldav_add_timezone (ECalBackendSync *backend, 
2469                      EDataCal        *cal,
2470                      const char      *tzobj)
2471 {
2472         /* FIXME: implement me! */
2473         g_warning ("function not implemented %s", G_STRFUNC);
2474         return GNOME_Evolution_Calendar_Success;        
2475 }
2476
2477 static ECalBackendSyncStatus
2478 caldav_set_default_zone (ECalBackendSync *backend, 
2479                              EDataCal        *cal,
2480                              const char      *tzobj)
2481 {
2482         /* FIXME: implement me! */
2483         g_warning ("function not implemented %s", G_STRFUNC);
2484         return GNOME_Evolution_Calendar_Success;        
2485 }
2486
2487 static ECalBackendSyncStatus
2488 caldav_get_object_list (ECalBackendSync  *backend, 
2489                         EDataCal         *cal,
2490                         const char       *sexp_string,
2491                         GList           **objects)
2492 {
2493         ECalBackendCalDAV        *cbdav;
2494         ECalBackendCalDAVPrivate *priv;
2495         ECalBackendSExp          *sexp;
2496         ECalBackendCache         *bcache;
2497         ECalBackend              *bkend;
2498         gboolean                  do_search;
2499         GList                    *list, *iter;
2500         
2501         cbdav = E_CAL_BACKEND_CALDAV (backend);
2502         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2503
2504         sexp = e_cal_backend_sexp_new (sexp_string);
2505
2506         if (sexp == NULL) {
2507                 return GNOME_Evolution_Calendar_InvalidQuery;
2508         }
2509         
2510         if (g_str_equal (sexp, "#t")) {
2511                 do_search = FALSE;
2512         } else {
2513                 do_search = TRUE;
2514         }
2515
2516         *objects = NULL;
2517         bcache = priv->cache;
2518
2519         g_mutex_lock (priv->lock);      
2520         
2521         list = e_cal_backend_cache_get_components (bcache);
2522         bkend = E_CAL_BACKEND (backend);
2523         
2524         for (iter = list; iter; iter = g_list_next (iter)) {
2525                 ECalComponent *comp = E_CAL_COMPONENT (iter->data);     
2526         
2527                 if (do_search == FALSE || 
2528                     e_cal_backend_sexp_match_comp (sexp, comp, bkend)) {
2529                         char *str = e_cal_component_get_as_string (comp);
2530                         *objects = g_list_prepend (*objects, str);
2531                 }
2532
2533                 g_object_unref (comp);
2534         }
2535
2536         g_object_unref (sexp);
2537         g_list_free (list);
2538
2539         g_mutex_unlock (priv->lock);
2540         
2541         return GNOME_Evolution_Calendar_Success;                
2542 }
2543
2544 static void
2545 caldav_start_query (ECalBackend  *backend, 
2546                     EDataCalView *query)
2547 {
2548         ECalBackendCalDAV        *cbdav;
2549         ECalBackendCalDAVPrivate *priv;
2550         ECalBackendSExp          *sexp;
2551         ECalBackend              *bkend;
2552         gboolean                  do_search;
2553         GList                    *list, *iter;
2554         const char               *sexp_string;
2555
2556         cbdav = E_CAL_BACKEND_CALDAV (backend);
2557         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2558         
2559         sexp_string = e_data_cal_view_get_text (query);
2560         sexp = e_cal_backend_sexp_new (sexp_string);
2561
2562         /* FIXME:check invalid sexp */
2563         
2564         if (g_str_equal (sexp, "#t")) {
2565                 do_search = FALSE;
2566         } else {
2567                 do_search = TRUE;
2568         }
2569         
2570         g_mutex_lock (priv->lock);
2571
2572         list = e_cal_backend_cache_get_components (priv->cache);
2573         bkend = E_CAL_BACKEND (backend);
2574         
2575         for (iter = list; iter; iter = g_list_next (iter)) {
2576                 ECalComponent *comp = E_CAL_COMPONENT (iter->data);     
2577         
2578                 if (do_search == FALSE || 
2579                     e_cal_backend_sexp_match_comp (sexp, comp, bkend)) {
2580                         char *str = e_cal_component_get_as_string (comp);
2581                         e_data_cal_view_notify_objects_added_1 (query, str);
2582                         g_free (str);
2583                 }
2584
2585                 g_object_unref (comp);
2586         }
2587
2588         g_object_unref (sexp);
2589         g_list_free (list);
2590
2591         
2592         e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
2593         g_mutex_unlock (priv->lock);
2594         return;
2595 }
2596
2597 static ECalBackendSyncStatus
2598 caldav_get_free_busy (ECalBackendSync  *backend, 
2599                       EDataCal         *cal, 
2600                       GList            *users,
2601                       time_t            start, 
2602                       time_t            end, 
2603                       GList           **freebusy)
2604 {
2605         /* FIXME: implement me! */
2606         g_warning ("function not implemented %s", G_STRFUNC);
2607         return GNOME_Evolution_Calendar_OtherError;     
2608 }
2609
2610 static ECalBackendSyncStatus
2611 caldav_get_changes (ECalBackendSync  *backend,
2612                     EDataCal         *cal,
2613                     const char       *change_id,
2614                     GList           **adds, 
2615                     GList           **modifies, 
2616                     GList **deletes)
2617 {
2618         /* FIXME: implement me! */
2619         g_warning ("function not implemented %s", G_STRFUNC);
2620         return GNOME_Evolution_Calendar_OtherError;     
2621 }
2622
2623 static gboolean
2624 caldav_is_loaded (ECalBackend *backend)
2625         
2626 {       ECalBackendCalDAV        *cbdav;
2627 ECalBackendCalDAVPrivate *priv;
2628         
2629 cbdav = E_CAL_BACKEND_CALDAV (backend);
2630 priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2631
2632 return priv->loaded;
2633 }
2634
2635 static CalMode
2636 caldav_get_mode (ECalBackend *backend)
2637 {
2638         ECalBackendCalDAV        *cbdav;
2639         ECalBackendCalDAVPrivate *priv;
2640
2641         cbdav = E_CAL_BACKEND_CALDAV (backend);
2642         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2643         
2644         return priv->mode;      
2645 }
2646
2647 static void
2648 caldav_set_mode (ECalBackend *backend, CalMode mode)
2649 {
2650         ECalBackendCalDAV        *cbdav;
2651         ECalBackendCalDAVPrivate *priv;
2652
2653         cbdav = E_CAL_BACKEND_CALDAV (backend);
2654         priv  = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2655
2656         g_mutex_lock (priv->lock);
2657         
2658         /* We only support online and offline 
2659          * (is there something else?) */
2660         if (mode != CAL_MODE_REMOTE &&
2661             mode != CAL_MODE_LOCAL) {
2662                 e_cal_backend_notify_mode (backend, 
2663                                            GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
2664                                            cal_mode_to_corba (mode));
2665         }
2666
2667         if (priv->mode == mode || priv->loaded == FALSE) {
2668                 priv->mode = mode;
2669                 e_cal_backend_notify_mode (backend, 
2670                                            GNOME_Evolution_Calendar_CalListener_MODE_SET,
2671                                            cal_mode_to_corba (mode));
2672                 g_mutex_unlock (priv->lock);
2673                 return;
2674         }
2675
2676         if (mode == CAL_MODE_REMOTE) {
2677                 /* Wake up the slave thread */
2678                 priv->slave_cmd = SLAVE_SHOULD_WORK;
2679                 g_cond_signal (priv->cond);
2680         } else {
2681                 soup_session_abort (priv->session);
2682                 priv->slave_cmd = SLAVE_SHOULD_SLEEP;
2683         }
2684
2685         e_cal_backend_notify_mode (backend, 
2686                                    GNOME_Evolution_Calendar_CalListener_MODE_SET,
2687                                    cal_mode_to_corba (mode));
2688
2689         g_mutex_unlock (priv->lock);
2690 }
2691
2692 static icaltimezone *
2693 caldav_internal_get_default_timezone (ECalBackend *backend)
2694 {
2695         return icaltimezone_get_utc_timezone ();
2696 }
2697
2698 static icaltimezone *
2699 caldav_internal_get_timezone (ECalBackend *backend, 
2700                               const char *tzid)
2701 {
2702         icaltimezone *zone;
2703
2704         zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
2705
2706         if (!zone) {
2707                 zone = icaltimezone_get_utc_timezone ();
2708         }
2709         
2710         return zone;
2711 }
2712
2713 /* ************************************************************************* */
2714 /* ***************************** GObject Foo ******************************* */
2715
2716 G_DEFINE_TYPE (ECalBackendCalDAV, e_cal_backend_caldav, E_TYPE_CAL_BACKEND_SYNC);
2717
2718 static void
2719 e_cal_backend_caldav_dispose (GObject *object)
2720 {
2721         ECalBackendCalDAV        *cbdav;
2722         ECalBackendCalDAVPrivate *priv;
2723         
2724         cbdav = E_CAL_BACKEND_CALDAV (object);
2725         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2726
2727         g_mutex_lock (priv->lock);
2728
2729         if (priv->disposed == TRUE) {
2730                 g_mutex_unlock (priv->lock);
2731                 return;
2732         }
2733         
2734         /* stop the slave  */
2735         priv->slave_cmd = SLAVE_SHOULD_DIE;
2736         g_cond_signal (priv->cond);
2737         g_mutex_unlock (priv->lock);
2738
2739         /* wait until the slave died */
2740         g_mutex_lock (priv->lock);
2741
2742         g_object_unref (priv->session);
2743         
2744         g_free (priv->username);
2745         g_free (priv->password);
2746         g_free (priv->uri);
2747
2748         if (priv->cache != NULL) {
2749                 g_object_unref (priv->cache);
2750         }
2751         
2752         priv->disposed = TRUE;
2753         g_mutex_unlock (priv->lock);
2754                 
2755         if (G_OBJECT_CLASS (parent_class)->dispose)
2756                 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
2757 }
2758
2759 static void
2760 e_cal_backend_caldav_finalize (GObject *object)
2761 {
2762         ECalBackendCalDAV        *cbdav;
2763         ECalBackendCalDAVPrivate *priv;
2764         
2765         cbdav = E_CAL_BACKEND_CALDAV (object);
2766         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2767
2768         g_mutex_free (priv->lock);
2769         g_cond_free (priv->cond);
2770         
2771         if (G_OBJECT_CLASS (parent_class)->finalize)
2772                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
2773 }
2774
2775
2776 static void
2777 e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav)
2778 {
2779         ECalBackendCalDAVPrivate *priv;
2780         priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
2781
2782         priv->session = soup_session_sync_new ();
2783         
2784         priv->disposed = FALSE;
2785         priv->do_synch = FALSE;
2786         priv->loaded   = FALSE;
2787         
2788         priv->cond = g_cond_new ();
2789         priv->lock = g_mutex_new ();
2790
2791         /* Slave control ... */
2792         priv->slave_cmd = SLAVE_SHOULD_SLEEP;
2793         priv->refresh_time.tv_usec = 0;
2794         priv->refresh_time.tv_sec  = DEFAULT_REFRESH_TIME;
2795         
2796         g_signal_connect (priv->session, "authenticate",
2797                           G_CALLBACK (soup_authenticate), cbdav);
2798         g_signal_connect (priv->session, "reauthenticate",
2799                           G_CALLBACK (soup_reauthenticate), cbdav);
2800         
2801         e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbdav), FALSE);
2802 }
2803
2804
2805 static void
2806 e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
2807 {
2808         GObjectClass *object_class;
2809         ECalBackendClass *backend_class;
2810         ECalBackendSyncClass *sync_class;
2811
2812         object_class = (GObjectClass *) class;
2813         backend_class = (ECalBackendClass *) class;
2814         sync_class = (ECalBackendSyncClass *) class;
2815
2816         caldav_debug_init ();
2817
2818         parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
2819         g_type_class_add_private (class, sizeof (ECalBackendCalDAVPrivate));
2820
2821         object_class->dispose  = e_cal_backend_caldav_dispose;
2822         object_class->finalize = e_cal_backend_caldav_finalize;
2823         
2824         sync_class->is_read_only_sync            = caldav_is_read_only;
2825         sync_class->get_cal_address_sync         = caldav_get_cal_address;
2826         sync_class->get_alarm_email_address_sync = caldav_get_alarm_email_address;
2827         sync_class->get_ldap_attribute_sync      = caldav_get_ldap_attribute;
2828         sync_class->get_static_capabilities_sync = caldav_get_static_capabilities;
2829         
2830         sync_class->open_sync                    = caldav_do_open;
2831         sync_class->remove_sync                  = caldav_remove;
2832         
2833         sync_class->create_object_sync = caldav_create_object;
2834         sync_class->modify_object_sync = caldav_modify_object;
2835         sync_class->remove_object_sync = caldav_remove_object;
2836         
2837         sync_class->discard_alarm_sync        = caldav_discard_alarm;
2838         sync_class->receive_objects_sync      = caldav_receive_objects;
2839         sync_class->send_objects_sync         = caldav_send_objects;
2840         sync_class->get_default_object_sync   = caldav_get_default_object;
2841         sync_class->get_object_sync           = caldav_get_object;
2842         sync_class->get_object_list_sync      = caldav_get_object_list;
2843         sync_class->get_timezone_sync         = caldav_get_timezone;
2844         sync_class->add_timezone_sync         = caldav_add_timezone;
2845         sync_class->set_default_zone_sync = caldav_set_default_zone;
2846         sync_class->get_freebusy_sync         = caldav_get_free_busy;
2847         sync_class->get_changes_sync          = caldav_get_changes;
2848
2849         backend_class->is_loaded   = caldav_is_loaded;
2850         backend_class->start_query = caldav_start_query;
2851         backend_class->get_mode    = caldav_get_mode;
2852         backend_class->set_mode    = caldav_set_mode;
2853
2854         backend_class->internal_get_default_timezone = caldav_internal_get_default_timezone;
2855         backend_class->internal_get_timezone         = caldav_internal_get_timezone;
2856 }
2857