1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - iCalendar http backend
4 * Copyright (C) 2003 Novell, Inc.
6 * Authors: Hans Petter Jansson <hpj@ximian.com>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU Lesser General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * Based in part on the file backend.
27 #include <gconf/gconf-client.h>
28 #include <bonobo/bonobo-exception.h>
29 #include <bonobo/bonobo-moniker-util.h>
30 #include <glib/gi18n-lib.h>
31 #include "libedataserver/e-xml-hash-utils.h"
32 #include <libecal/e-cal-recur.h>
33 #include <libecal/e-cal-util.h>
34 #include <libecal/e-cal-time-util.h>
35 #include <libedata-cal/e-cal-backend-cache.h>
36 #include <libedata-cal/e-cal-backend-util.h>
37 #include <libedata-cal/e-cal-backend-sexp.h>
38 #include <libsoup/soup-session-async.h>
39 #include <libsoup/soup-uri.h>
40 #include "e-cal-backend-http.h"
44 /* Private part of the ECalBackendHttp structure */
45 struct _ECalBackendHttpPrivate {
46 /* URI to get remote calendar data from */
49 /* Local/remote mode */
53 ECalBackendCache *cache;
55 /* The calendar's default timezone, used for resolving DATE and
56 floating DATE-TIME values. */
57 icaltimezone *default_zone;
59 /* The list of live queries */
62 /* Soup handles for remote file */
63 SoupSession *soup_session;
66 guint reload_timeout_id;
80 static void e_cal_backend_http_dispose (GObject *object);
81 static void e_cal_backend_http_finalize (GObject *object);
82 static gboolean begin_retrieval_cb (ECalBackendHttp *cbhttp);
83 static ECalBackendSyncStatus
84 e_cal_backend_http_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj);
86 static ECalBackendSyncClass *parent_class;
90 /* Dispose handler for the file backend */
92 e_cal_backend_http_dispose (GObject *object)
94 ECalBackendHttp *cbhttp;
95 ECalBackendHttpPrivate *priv;
97 cbhttp = E_CAL_BACKEND_HTTP (object);
100 g_free (priv->username);
101 g_free (priv->password);
103 if (G_OBJECT_CLASS (parent_class)->dispose)
104 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
107 /* Finalize handler for the file backend */
109 e_cal_backend_http_finalize (GObject *object)
111 ECalBackendHttp *cbhttp;
112 ECalBackendHttpPrivate *priv;
114 g_return_if_fail (object != NULL);
115 g_return_if_fail (E_IS_CAL_BACKEND_HTTP (object));
117 cbhttp = E_CAL_BACKEND_HTTP (object);
123 g_object_unref (priv->cache);
132 if (priv->default_zone) {
133 icaltimezone_free (priv->default_zone, 1);
134 priv->default_zone = NULL;
138 if (priv->soup_session) {
139 soup_session_abort (priv->soup_session);
140 g_object_unref (priv->soup_session);
141 priv->soup_session = NULL;
144 if (priv->reload_timeout_id) {
145 g_source_remove (priv->reload_timeout_id);
146 priv->reload_timeout_id = 0;
152 if (G_OBJECT_CLASS (parent_class)->finalize)
153 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
158 /* Calendar backend methods */
160 /* Is_read_only handler for the file backend */
161 static ECalBackendSyncStatus
162 e_cal_backend_http_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
166 return GNOME_Evolution_Calendar_Success;
169 /* Get_email_address handler for the file backend */
170 static ECalBackendSyncStatus
171 e_cal_backend_http_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
173 /* A HTTP backend has no particular email address associated
174 * with it (although that would be a useful feature some day).
178 return GNOME_Evolution_Calendar_Success;
181 static ECalBackendSyncStatus
182 e_cal_backend_http_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
186 return GNOME_Evolution_Calendar_Success;
189 static ECalBackendSyncStatus
190 e_cal_backend_http_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
192 /* A HTTP backend has no particular email address associated
193 * with it (although that would be a useful feature some day).
197 return GNOME_Evolution_Calendar_Success;
200 static ECalBackendSyncStatus
201 e_cal_backend_http_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
203 *capabilities = g_strdup (CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
205 return GNOME_Evolution_Calendar_Success;
209 webcal_to_http_method (const gchar *webcal_str, gboolean secure)
211 if (secure && (strncmp ("http://", webcal_str, sizeof ("http://") - 1) == 0))
212 return g_strconcat ("https://", webcal_str + sizeof ("http://") - 1, NULL);
214 if (strncmp ("webcal://", webcal_str, sizeof ("webcal://") - 1))
215 return g_strdup (webcal_str);
218 return g_strconcat ("https://", webcal_str + sizeof ("webcal://") - 1, NULL);
220 return g_strconcat ("http://", webcal_str + sizeof ("webcal://") - 1, NULL);
224 notify_and_remove_from_cache (gpointer key, gpointer value, gpointer user_data)
226 const char *calobj = value;
227 ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (user_data);
228 ECalComponent *comp = e_cal_component_new_from_string (calobj);
229 ECalComponentId *id = e_cal_component_get_id (comp);
231 e_cal_backend_cache_remove_component (cbhttp->priv->cache, id->uid, id->rid);
232 e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbhttp), id, calobj, NULL);
234 e_cal_component_free_id (id);
235 g_object_unref (comp);
241 retrieval_done (SoupMessage *msg, ECalBackendHttp *cbhttp)
243 ECalBackendHttpPrivate *priv;
244 icalcomponent *icalcomp, *subcomp;
245 icalcomponent_kind kind;
248 GHashTable *old_cache;
249 GList *comps_in_cache;
253 priv->is_loading = FALSE;
254 d(g_message ("Retrieval done.\n"));
256 /* Handle redirection ourselves */
257 if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
258 newuri = soup_message_get_header (msg->response_headers,
261 d(g_message ("Redirected to %s\n", newuri));
266 priv->uri = webcal_to_http_method (newuri, FALSE);
267 begin_retrieval_cb (cbhttp);
270 e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp),
271 _("Redirected to Invalid URI"));
278 /* check status code */
279 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
281 e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp),
282 soup_status_get_phrase (msg->status_code));
287 /* get the calendar from the response */
288 str = g_malloc0 (msg->response.length + 1);
289 strncpy (str, msg->response.body, msg->response.length);
290 icalcomp = icalparser_parse_string (str);
295 e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Bad file format."));
299 if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
301 e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Not a calendar."));
302 icalcomponent_free (icalcomp);
307 old_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
309 comps_in_cache = e_cal_backend_cache_get_components (priv->cache);
310 while (comps_in_cache != NULL) {
312 ECalComponent *comp = comps_in_cache->data;
314 e_cal_component_get_uid (comp, &uid);
315 g_hash_table_insert (old_cache, g_strdup (uid), e_cal_component_get_as_string (comp));
317 comps_in_cache = g_list_remove (comps_in_cache, comps_in_cache->data);
318 g_object_unref (comp);
321 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbhttp));
322 subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT);
323 e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
326 icalcomponent_kind subcomp_kind;
327 icalproperty *prop = NULL;
329 subcomp_kind = icalcomponent_isa (subcomp);
330 prop = icalcomponent_get_first_property (subcomp, ICAL_UID_PROPERTY);
332 g_warning (" The component does not have the mandatory property UID \n");
333 subcomp = icalcomponent_get_next_component (icalcomp, kind);
337 if (subcomp_kind == kind) {
338 comp = e_cal_component_new ();
339 if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp))) {
340 const char *uid, *orig_key, *orig_value;
342 e_cal_backend_cache_put_component (priv->cache, comp);
344 e_cal_component_get_uid (comp, &uid);
345 /* middle (void*) cast only because of 'dereferencing type-punned pointer will break strict-aliasing rules' */
346 if (g_hash_table_lookup_extended (old_cache, uid, (void **)(void*)&orig_key, (void **)(void*)&orig_value)) {
347 e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbhttp),
349 icalcomponent_as_ical_string (subcomp));
350 g_hash_table_remove (old_cache, uid);
352 e_cal_backend_notify_object_created (E_CAL_BACKEND (cbhttp),
353 icalcomponent_as_ical_string (subcomp));
357 g_object_unref (comp);
358 } else if (subcomp_kind == ICAL_VTIMEZONE_COMPONENT) {
361 zone = icaltimezone_new ();
362 icaltimezone_set_component (zone, icalcomponent_new_clone (subcomp));
363 e_cal_backend_cache_put_timezone (priv->cache, (const icaltimezone *) zone);
365 icaltimezone_free (zone, 1);
368 subcomp = icalcomponent_get_next_component (icalcomp, kind);
371 e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
373 /* notify the removals */
374 g_hash_table_foreach_remove (old_cache, (GHRFunc) notify_and_remove_from_cache, cbhttp);
375 g_hash_table_destroy (old_cache);
378 icalcomponent_free (icalcomp);
380 d(g_message ("Retrieval really done.\n"));
383 /* ************************************************************************* */
384 /* Authentication helpers for libsoup */
387 soup_authenticate (SoupSession *session,
389 const char *auth_type,
390 const char *auth_realm,
395 ECalBackendHttpPrivate *priv;
396 ECalBackendHttp *cbhttp;
398 cbhttp = E_CAL_BACKEND_HTTP (data);
401 *username = priv->username;
402 *password = priv->password;
404 priv->username = NULL;
405 priv->password = NULL;
410 soup_reauthenticate (SoupSession *session,
412 const char *auth_type,
413 const char *auth_realm,
418 ECalBackendHttpPrivate *priv;
419 ECalBackendHttp *cbhttp;
421 cbhttp = E_CAL_BACKEND_HTTP (data);
424 *username = priv->username;
425 *password = priv->password;
427 priv->username = NULL;
428 priv->password = NULL;
431 static gboolean reload_cb (ECalBackendHttp *cbhttp);
432 static void maybe_start_reload_timeout (ECalBackendHttp *cbhttp);
435 begin_retrieval_cb (ECalBackendHttp *cbhttp)
437 ECalBackendHttpPrivate *priv;
438 SoupMessage *soup_message;
442 if (priv->mode != CAL_MODE_REMOTE)
445 maybe_start_reload_timeout (cbhttp);
447 d(g_message ("Starting retrieval...\n"));
449 if (priv->is_loading)
452 priv->is_loading = TRUE;
454 /* create the Soup session if not already created */
455 if (!priv->soup_session) {
456 GConfClient *conf_client;
458 priv->soup_session = soup_session_async_new ();
460 g_signal_connect (priv->soup_session, "authenticate",
461 G_CALLBACK (soup_authenticate), cbhttp);
462 g_signal_connect (priv->soup_session, "reauthenticate",
463 G_CALLBACK (soup_reauthenticate), cbhttp);
465 /* set the HTTP proxy, if configuration is set to do so */
466 conf_client = gconf_client_get_default ();
467 if (gconf_client_get_bool (conf_client, "/system/http_proxy/use_http_proxy", NULL)) {
468 char *server, *proxy_uri;
471 server = gconf_client_get_string (conf_client, "/system/http_proxy/host", NULL);
472 port = gconf_client_get_int (conf_client, "/system/http_proxy/port", NULL);
474 if (server && server[0]) {
476 if (gconf_client_get_bool (conf_client, "/system/http_proxy/use_authentication", NULL)) {
477 char *user, *password;
479 user = gconf_client_get_string (conf_client,
480 "/system/http_proxy/authentication_user",
482 password = gconf_client_get_string (conf_client,
483 "/system/http_proxy/authentication_password",
486 proxy_uri = g_strdup_printf("http://%s:%s@%s:%d", user, password, server, port);
491 proxy_uri = g_strdup_printf ("http://%s:%d", server, port);
493 suri = soup_uri_new (proxy_uri);
494 g_object_set (G_OBJECT (priv->soup_session), SOUP_SESSION_PROXY_URI, suri, NULL);
496 soup_uri_free (suri);
502 g_object_unref (conf_client);
505 if (priv->uri == NULL) {
506 ESource *source = e_cal_backend_get_source (E_CAL_BACKEND (cbhttp));
507 const char *secure_prop = e_source_get_property (source, "use_ssl");
509 priv->uri = webcal_to_http_method (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp)),
510 (secure_prop && g_str_equal(secure_prop, "1")));
513 /* create message to be sent to server */
514 soup_message = soup_message_new (SOUP_METHOD_GET, priv->uri);
515 soup_message_add_header (soup_message->request_headers, "User-Agent",
516 "Evolution/" VERSION);
517 soup_message_set_flags (soup_message, SOUP_MESSAGE_NO_REDIRECT);
519 soup_session_queue_message (priv->soup_session, soup_message,
520 (SoupMessageCallbackFn) retrieval_done, cbhttp);
522 d(g_message ("Retrieval started.\n"));
527 reload_cb (ECalBackendHttp *cbhttp)
529 ECalBackendHttpPrivate *priv;
533 if (priv->is_loading)
536 d(g_message ("Reload!\n"));
538 priv->reload_timeout_id = 0;
540 begin_retrieval_cb (cbhttp);
545 maybe_start_reload_timeout (ECalBackendHttp *cbhttp)
547 ECalBackendHttpPrivate *priv;
549 const gchar *refresh_str;
553 d(g_message ("Setting reload timeout.\n"));
555 if (priv->reload_timeout_id)
558 source = e_cal_backend_get_source (E_CAL_BACKEND (cbhttp));
560 g_warning ("Could not get source for ECalBackendHttp reload.");
564 refresh_str = e_source_get_property (source, "refresh");
566 priv->reload_timeout_id = g_timeout_add ((refresh_str ? atoi (refresh_str) : 30) * 60000,
567 (GSourceFunc) reload_cb, cbhttp);
570 /* Open handler for the file backend */
571 static ECalBackendSyncStatus
572 e_cal_backend_http_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists,
573 const char *username, const char *password)
575 ECalBackendHttp *cbhttp;
576 ECalBackendHttpPrivate *priv;
579 cbhttp = E_CAL_BACKEND_HTTP (backend);
581 source = e_cal_backend_get_source (E_CAL_BACKEND (backend));
583 if (e_source_get_property (source, "auth") != NULL) {
584 if ((username == NULL || password == NULL)) {
585 return GNOME_Evolution_Calendar_AuthenticationRequired;
588 priv->username = g_strdup (username);
589 priv->password = g_strdup (password);
593 priv->cache = e_cal_backend_cache_new (e_cal_backend_get_uri (E_CAL_BACKEND (backend)), E_CAL_SOURCE_TYPE_EVENT );
597 e_cal_backend_notify_error (E_CAL_BACKEND(cbhttp), _("Could not create cache file"));
598 return GNOME_Evolution_Calendar_OtherError;
601 if (priv->default_zone) {
602 e_cal_backend_cache_put_default_timezone (priv->cache, priv->default_zone);
605 if (priv->mode == CAL_MODE_LOCAL)
606 return GNOME_Evolution_Calendar_Success;
609 g_idle_add ((GSourceFunc) begin_retrieval_cb, cbhttp);
612 return GNOME_Evolution_Calendar_Success;
615 static ECalBackendSyncStatus
616 e_cal_backend_http_remove (ECalBackendSync *backend, EDataCal *cal)
618 ECalBackendHttp *cbhttp;
619 ECalBackendHttpPrivate *priv;
621 cbhttp = E_CAL_BACKEND_HTTP (backend);
625 return GNOME_Evolution_Calendar_OtherError;
627 e_file_cache_remove (E_FILE_CACHE (priv->cache));
628 return GNOME_Evolution_Calendar_Success;
631 /* is_loaded handler for the file backend */
633 e_cal_backend_http_is_loaded (ECalBackend *backend)
635 ECalBackendHttp *cbhttp;
636 ECalBackendHttpPrivate *priv;
638 cbhttp = E_CAL_BACKEND_HTTP (backend);
647 /* is_remote handler for the http backend */
649 e_cal_backend_http_get_mode (ECalBackend *backend)
651 ECalBackendHttp *cbhttp;
652 ECalBackendHttpPrivate *priv;
654 cbhttp = E_CAL_BACKEND_HTTP (backend);
660 /* Set_mode handler for the http backend */
662 e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode)
664 ECalBackendHttp *cbhttp;
665 ECalBackendHttpPrivate *priv;
666 GNOME_Evolution_Calendar_CalMode set_mode;
668 cbhttp = E_CAL_BACKEND_HTTP (backend);
671 loaded = e_cal_backend_http_is_loaded (backend);
676 set_mode = cal_mode_to_corba (mode);
677 if (loaded && priv->reload_timeout_id) {
678 g_source_remove (priv->reload_timeout_id);
679 priv->reload_timeout_id = 0;
682 case CAL_MODE_REMOTE:
685 set_mode = cal_mode_to_corba (mode);
687 g_idle_add ((GSourceFunc) begin_retrieval_cb, backend);
690 priv->mode = CAL_MODE_REMOTE;
691 set_mode = GNOME_Evolution_Calendar_MODE_REMOTE;
694 set_mode = GNOME_Evolution_Calendar_MODE_ANY;
700 if (set_mode == GNOME_Evolution_Calendar_MODE_ANY)
701 e_cal_backend_notify_mode (backend,
702 GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
703 cal_mode_to_corba (priv->mode));
705 e_cal_backend_notify_mode (backend,
706 GNOME_Evolution_Calendar_CalListener_MODE_SET,
711 static ECalBackendSyncStatus
712 e_cal_backend_http_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
714 ECalBackendHttp *cbhttp;
715 ECalBackendHttpPrivate *priv;
716 icalcomponent *icalcomp;
717 icalcomponent_kind kind;
719 cbhttp = E_CAL_BACKEND_HTTP (backend);
722 kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
723 icalcomp = e_cal_util_new_component (kind);
724 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
725 icalcomponent_free (icalcomp);
727 return GNOME_Evolution_Calendar_Success;
730 /* Get_object_component handler for the http backend */
731 static ECalBackendSyncStatus
732 e_cal_backend_http_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
734 ECalBackendHttp *cbhttp;
735 ECalBackendHttpPrivate *priv;
736 ECalComponent *comp = NULL;
738 cbhttp = E_CAL_BACKEND_HTTP (backend);
741 g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
744 return GNOME_Evolution_Calendar_ObjectNotFound;
746 comp = e_cal_backend_cache_get_component (priv->cache, uid, rid);
748 return GNOME_Evolution_Calendar_ObjectNotFound;
750 *object = e_cal_component_get_as_string (comp);
751 g_object_unref (comp);
753 return GNOME_Evolution_Calendar_Success;
756 /* Get_timezone_object handler for the file backend */
757 static ECalBackendSyncStatus
758 e_cal_backend_http_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
760 ECalBackendHttp *cbhttp;
761 ECalBackendHttpPrivate *priv;
762 const icaltimezone *zone;
763 icalcomponent *icalcomp;
765 cbhttp = E_CAL_BACKEND_HTTP (backend);
768 g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
770 /* first try to get the timezone from the cache */
771 zone = e_cal_backend_cache_get_timezone (priv->cache, tzid);
773 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
775 return GNOME_Evolution_Calendar_ObjectNotFound;
778 icalcomp = icaltimezone_get_component ((icaltimezone *)zone);
780 return GNOME_Evolution_Calendar_InvalidObject;
782 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
784 return GNOME_Evolution_Calendar_Success;
787 /* Add_timezone handler for the file backend */
788 static ECalBackendSyncStatus
789 e_cal_backend_http_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
791 ECalBackendHttp *cbhttp;
792 ECalBackendHttpPrivate *priv;
793 icalcomponent *tz_comp;
796 cbhttp = (ECalBackendHttp *) backend;
798 g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbhttp), GNOME_Evolution_Calendar_OtherError);
799 g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
803 tz_comp = icalparser_parse_string (tzobj);
805 return GNOME_Evolution_Calendar_InvalidObject;
807 if (icalcomponent_isa (tz_comp) != ICAL_VTIMEZONE_COMPONENT) {
808 icalcomponent_free (tz_comp);
809 return GNOME_Evolution_Calendar_InvalidObject;
812 zone = icaltimezone_new ();
813 icaltimezone_set_component (zone, tz_comp);
814 e_cal_backend_cache_put_timezone (priv->cache, zone);
816 return GNOME_Evolution_Calendar_Success;
819 static ECalBackendSyncStatus
820 e_cal_backend_http_set_default_zone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
822 icalcomponent *tz_comp;
823 ECalBackendHttp *cbhttp;
824 ECalBackendHttpPrivate *priv;
827 cbhttp = (ECalBackendHttp *) backend;
829 g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbhttp), GNOME_Evolution_Calendar_OtherError);
830 g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
834 tz_comp = icalparser_parse_string (tzobj);
836 return GNOME_Evolution_Calendar_InvalidObject;
839 zone = icaltimezone_new ();
840 icaltimezone_set_component (zone, tz_comp);
842 if (priv->default_zone)
843 icaltimezone_free (priv->default_zone, 1);
845 /* Set the default timezone to it. */
846 priv->default_zone = zone;
848 return GNOME_Evolution_Calendar_Success;
851 /* Get_objects_in_range handler for the file backend */
852 static ECalBackendSyncStatus
853 e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects)
855 ECalBackendHttp *cbhttp;
856 ECalBackendHttpPrivate *priv;
857 GList *components, *l;
858 ECalBackendSExp *cbsexp;
860 cbhttp = E_CAL_BACKEND_HTTP (backend);
864 return GNOME_Evolution_Calendar_NoSuchCal;
866 /* process all components in the cache */
867 cbsexp = e_cal_backend_sexp_new (sexp);
870 components = e_cal_backend_cache_get_components (priv->cache);
871 for (l = components; l != NULL; l = l->next) {
872 if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend))) {
873 *objects = g_list_append (*objects, e_cal_component_get_as_string (l->data));
877 g_list_foreach (components, (GFunc) g_object_unref, NULL);
878 g_list_free (components);
879 g_object_unref (cbsexp);
881 return GNOME_Evolution_Calendar_Success;
884 /* get_query handler for the file backend */
886 e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query)
888 ECalBackendHttp *cbhttp;
889 ECalBackendHttpPrivate *priv;
890 GList *components, *l, *objects = NULL;
891 ECalBackendSExp *cbsexp;
893 cbhttp = E_CAL_BACKEND_HTTP (backend);
896 d(g_message (G_STRLOC ": Starting query (%s)", e_data_cal_view_get_text (query)));
899 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_NoSuchCal);
903 /* process all components in the cache */
904 cbsexp = e_cal_backend_sexp_new (e_data_cal_view_get_text (query));
907 components = e_cal_backend_cache_get_components (priv->cache);
908 for (l = components; l != NULL; l = l->next) {
909 if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), E_CAL_BACKEND (backend))) {
910 objects = g_list_append (objects, e_cal_component_get_as_string (l->data));
914 e_data_cal_view_notify_objects_added (query, (const GList *) objects);
916 g_list_foreach (components, (GFunc) g_object_unref, NULL);
917 g_list_free (components);
918 g_list_foreach (objects, (GFunc) g_free, NULL);
919 g_list_free (objects);
920 g_object_unref (cbsexp);
922 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
926 static icaltimezone *
927 resolve_tzid (const char *tzid, gpointer user_data)
929 icalcomponent *vcalendar_comp = user_data;
931 if (!tzid || !tzid[0])
933 else if (!strcmp (tzid, "UTC"))
934 return icaltimezone_get_utc_timezone ();
936 return icalcomponent_get_timezone (vcalendar_comp, tzid);
941 free_busy_instance (ECalComponent *comp,
942 time_t instance_start,
946 icalcomponent *vfb = data;
948 icalparameter *param;
949 struct icalperiodtype ipt;
950 icaltimezone *utc_zone;
952 utc_zone = icaltimezone_get_utc_timezone ();
954 ipt.start = icaltime_from_timet_with_zone (instance_start, FALSE, utc_zone);
955 ipt.end = icaltime_from_timet_with_zone (instance_end, FALSE, utc_zone);
956 ipt.duration = icaldurationtype_null_duration ();
958 /* add busy information to the vfb component */
959 prop = icalproperty_new (ICAL_FREEBUSY_PROPERTY);
960 icalproperty_set_freebusy (prop, ipt);
962 param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
963 icalproperty_add_parameter (prop, param);
965 icalcomponent_add_property (vfb, prop);
970 static icalcomponent *
971 create_user_free_busy (ECalBackendHttp *cbhttp, const char *address, const char *cn,
972 time_t start, time_t end)
974 GList *list = NULL, *l;
976 icaltimezone *utc_zone;
977 ECalBackendSExp *obj_sexp;
978 ECalBackendHttpPrivate *priv;
979 ECalBackendCache *cache;
980 char *query, *iso_start, *iso_end;
985 /* create the (unique) VFREEBUSY object that we'll return */
986 vfb = icalcomponent_new_vfreebusy ();
987 if (address != NULL) {
989 icalparameter *param;
991 prop = icalproperty_new_organizer (address);
992 if (prop != NULL && cn != NULL) {
993 param = icalparameter_new_cn (cn);
994 icalproperty_add_parameter (prop, param);
997 icalcomponent_add_property (vfb, prop);
999 utc_zone = icaltimezone_get_utc_timezone ();
1000 icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
1001 icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
1003 /* add all objects in the given interval */
1004 iso_start = isodate_from_time_t (start);
1005 iso_end = isodate_from_time_t (end);
1006 query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") (make-time \"%s\")",
1007 iso_start, iso_end);
1008 obj_sexp = e_cal_backend_sexp_new (query);
1018 list = e_cal_backend_cache_get_components(cache);
1020 for (l = list; l; l = l->next) {
1021 ECalComponent *comp = l->data;
1022 icalcomponent *icalcomp, *vcalendar_comp;
1025 icalcomp = e_cal_component_get_icalcomponent (comp);
1029 /* If the event is TRANSPARENT, skip it. */
1030 prop = icalcomponent_get_first_property (icalcomp,
1031 ICAL_TRANSP_PROPERTY);
1033 icalproperty_transp transp_val = icalproperty_get_transp (prop);
1034 if (transp_val == ICAL_TRANSP_TRANSPARENT ||
1035 transp_val == ICAL_TRANSP_TRANSPARENTNOCONFLICT)
1039 if (!e_cal_backend_sexp_match_comp (obj_sexp, l->data, E_CAL_BACKEND (cbhttp)))
1042 vcalendar_comp = icalcomponent_get_parent (icalcomp);
1043 if (!vcalendar_comp)
1044 vcalendar_comp = icalcomp;
1045 e_cal_recur_generate_instances (comp, start, end,
1050 e_cal_backend_cache_get_default_timezone (cache));
1052 g_object_unref (obj_sexp);
1059 /* Get_free_busy handler for the file backend */
1060 static ECalBackendSyncStatus
1061 e_cal_backend_http_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users,
1062 time_t start, time_t end, GList **freebusy)
1064 ECalBackendHttp *cbhttp;
1065 ECalBackendHttpPrivate *priv;
1066 gchar *address, *name;
1071 cbhttp = E_CAL_BACKEND_HTTP (backend);
1072 priv = cbhttp->priv;
1074 g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange);
1075 g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange);
1078 return GNOME_Evolution_Calendar_NoSuchCal;
1080 if (users == NULL) {
1081 if (e_cal_backend_mail_account_get_default (&address, &name)) {
1082 vfb = create_user_free_busy (cbhttp, address, name, start, end);
1083 calobj = icalcomponent_as_ical_string (vfb);
1084 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1085 icalcomponent_free (vfb);
1091 for (l = users; l != NULL; l = l->next ) {
1093 if (e_cal_backend_mail_account_is_valid (address, &name)) {
1094 vfb = create_user_free_busy (cbhttp, address, name, start, end);
1095 calobj = icalcomponent_as_ical_string (vfb);
1096 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1097 icalcomponent_free (vfb);
1103 return GNOME_Evolution_Calendar_Success;
1106 /* Get_changes handler for the file backend */
1107 static ECalBackendSyncStatus
1108 e_cal_backend_http_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id,
1109 GList **adds, GList **modifies, GList **deletes)
1111 ECalBackendHttp *cbhttp;
1112 ECalBackendHttpPrivate *priv;
1114 cbhttp = E_CAL_BACKEND_HTTP (backend);
1115 priv = cbhttp->priv;
1117 g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1120 return GNOME_Evolution_Calendar_Success;
1123 /* Discard_alarm handler for the file backend */
1124 static ECalBackendSyncStatus
1125 e_cal_backend_http_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
1127 ECalBackendHttp *cbhttp;
1128 ECalBackendHttpPrivate *priv;
1130 cbhttp = E_CAL_BACKEND_HTTP (backend);
1131 priv = cbhttp->priv;
1134 return GNOME_Evolution_Calendar_Success;
1137 static ECalBackendSyncStatus
1138 e_cal_backend_http_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
1140 ECalBackendHttp *cbhttp;
1141 ECalBackendHttpPrivate *priv;
1143 cbhttp = E_CAL_BACKEND_HTTP (backend);
1144 priv = cbhttp->priv;
1146 return GNOME_Evolution_Calendar_PermissionDenied;
1149 static ECalBackendSyncStatus
1150 e_cal_backend_http_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
1151 CalObjModType mod, char **old_object, char **new_object)
1153 ECalBackendHttp *cbhttp;
1154 ECalBackendHttpPrivate *priv;
1156 cbhttp = E_CAL_BACKEND_HTTP (backend);
1157 priv = cbhttp->priv;
1159 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1161 return GNOME_Evolution_Calendar_PermissionDenied;
1164 /* Remove_object handler for the file backend */
1165 static ECalBackendSyncStatus
1166 e_cal_backend_http_remove_object (ECalBackendSync *backend, EDataCal *cal,
1167 const char *uid, const char *rid,
1168 CalObjModType mod, char **old_object,
1171 ECalBackendHttp *cbhttp;
1172 ECalBackendHttpPrivate *priv;
1174 cbhttp = E_CAL_BACKEND_HTTP (backend);
1175 priv = cbhttp->priv;
1177 g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1179 *old_object = *object = NULL;
1181 return GNOME_Evolution_Calendar_PermissionDenied;
1184 /* Update_objects handler for the file backend. */
1185 static ECalBackendSyncStatus
1186 e_cal_backend_http_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
1188 ECalBackendHttp *cbhttp;
1189 ECalBackendHttpPrivate *priv;
1191 cbhttp = E_CAL_BACKEND_HTTP (backend);
1192 priv = cbhttp->priv;
1194 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
1196 return GNOME_Evolution_Calendar_PermissionDenied;
1199 static ECalBackendSyncStatus
1200 e_cal_backend_http_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj, GList **users,
1201 char **modified_calobj)
1203 ECalBackendHttp *cbhttp;
1204 ECalBackendHttpPrivate *priv;
1206 cbhttp = E_CAL_BACKEND_HTTP (backend);
1207 priv = cbhttp->priv;
1210 *modified_calobj = NULL;
1212 return GNOME_Evolution_Calendar_PermissionDenied;
1215 static icaltimezone *
1216 e_cal_backend_http_internal_get_default_timezone (ECalBackend *backend)
1218 ECalBackendHttp *cbhttp;
1219 ECalBackendHttpPrivate *priv;
1221 cbhttp = E_CAL_BACKEND_HTTP (backend);
1222 priv = cbhttp->priv;
1230 static icaltimezone *
1231 e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid)
1233 ECalBackendHttp *cbhttp;
1234 ECalBackendHttpPrivate *priv;
1237 cbhttp = E_CAL_BACKEND_HTTP (backend);
1238 priv = cbhttp->priv;
1240 if (!strcmp (tzid, "UTC"))
1241 zone = icaltimezone_get_utc_timezone ();
1243 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1249 /* Object initialization function for the file backend */
1251 e_cal_backend_http_init (ECalBackendHttp *cbhttp, ECalBackendHttpClass *class)
1253 ECalBackendHttpPrivate *priv;
1255 priv = g_new0 (ECalBackendHttpPrivate, 1);
1256 cbhttp->priv = priv;
1259 priv->reload_timeout_id = 0;
1260 priv->opened = FALSE;
1262 e_cal_backend_sync_set_lock (E_CAL_BACKEND_SYNC (cbhttp), TRUE);
1265 /* Class initialization function for the file backend */
1267 e_cal_backend_http_class_init (ECalBackendHttpClass *class)
1269 GObjectClass *object_class;
1270 ECalBackendClass *backend_class;
1271 ECalBackendSyncClass *sync_class;
1273 object_class = (GObjectClass *) class;
1274 backend_class = (ECalBackendClass *) class;
1275 sync_class = (ECalBackendSyncClass *) class;
1277 parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
1279 object_class->dispose = e_cal_backend_http_dispose;
1280 object_class->finalize = e_cal_backend_http_finalize;
1282 sync_class->is_read_only_sync = e_cal_backend_http_is_read_only;
1283 sync_class->get_cal_address_sync = e_cal_backend_http_get_cal_address;
1284 sync_class->get_alarm_email_address_sync = e_cal_backend_http_get_alarm_email_address;
1285 sync_class->get_ldap_attribute_sync = e_cal_backend_http_get_ldap_attribute;
1286 sync_class->get_static_capabilities_sync = e_cal_backend_http_get_static_capabilities;
1287 sync_class->open_sync = e_cal_backend_http_open;
1288 sync_class->remove_sync = e_cal_backend_http_remove;
1289 sync_class->create_object_sync = e_cal_backend_http_create_object;
1290 sync_class->modify_object_sync = e_cal_backend_http_modify_object;
1291 sync_class->remove_object_sync = e_cal_backend_http_remove_object;
1292 sync_class->discard_alarm_sync = e_cal_backend_http_discard_alarm;
1293 sync_class->receive_objects_sync = e_cal_backend_http_receive_objects;
1294 sync_class->send_objects_sync = e_cal_backend_http_send_objects;
1295 sync_class->get_default_object_sync = e_cal_backend_http_get_default_object;
1296 sync_class->get_object_sync = e_cal_backend_http_get_object;
1297 sync_class->get_object_list_sync = e_cal_backend_http_get_object_list;
1298 sync_class->get_timezone_sync = e_cal_backend_http_get_timezone;
1299 sync_class->add_timezone_sync = e_cal_backend_http_add_timezone;
1300 sync_class->set_default_zone_sync = e_cal_backend_http_set_default_zone;
1301 sync_class->get_freebusy_sync = e_cal_backend_http_get_free_busy;
1302 sync_class->get_changes_sync = e_cal_backend_http_get_changes;
1304 backend_class->is_loaded = e_cal_backend_http_is_loaded;
1305 backend_class->start_query = e_cal_backend_http_start_query;
1306 backend_class->get_mode = e_cal_backend_http_get_mode;
1307 backend_class->set_mode = e_cal_backend_http_set_mode;
1309 backend_class->internal_get_default_timezone = e_cal_backend_http_internal_get_default_timezone;
1310 backend_class->internal_get_timezone = e_cal_backend_http_internal_get_timezone;
1315 * e_cal_backend_http_get_type:
1318 * Registers the #ECalBackendHttp class if necessary, and returns the type ID
1321 * Return value: The type ID of the #ECalBackendHttp class.
1324 e_cal_backend_http_get_type (void)
1326 static GType e_cal_backend_http_type = 0;
1328 if (!e_cal_backend_http_type) {
1329 static GTypeInfo info = {
1330 sizeof (ECalBackendHttpClass),
1331 (GBaseInitFunc) NULL,
1332 (GBaseFinalizeFunc) NULL,
1333 (GClassInitFunc) e_cal_backend_http_class_init,
1335 sizeof (ECalBackendHttp),
1337 (GInstanceInitFunc) e_cal_backend_http_init
1339 e_cal_backend_http_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
1340 "ECalBackendHttp", &info, 0);
1343 return e_cal_backend_http_type;