From 325c9408cc69bc60a176b264e7df9d7abcb348e4 Mon Sep 17 00:00:00 2001 From: Hans Petter Jansson Date: Tue, 4 Nov 2003 03:28:32 +0000 Subject: [PATCH] Implement. (e_cal_backend_file_dispose): Use above freers. 2003-11-03 Hans Petter Jansson * calendar/libedatacal/e-cal-backend-file.c (free_calendar_components) (free_calendar_data): Implement. (e_cal_backend_file_dispose): Use above freers. (get_uri_string_for_gnome_vfs): Implement. (open_cal): Set priv->uri to be escaped, GnomeVFS-friendly. (notify_removals_cb) (notify_adds_modifies_cb) (notify_changes) (reload_cal): Implement. (create_cal): Set priv->uri to be escaped, GnomeVFS-friendly. (get_uri_string): Implement - unescapes once. (e_cal_backend_file_compute_changes): Unescape DB URI once, don't make like it's in a subdirectory. (e_cal_backend_file_reload): Implement. * calendar/libedatacal/e-cal-backend-http.c (e_cal_backend_http_finalize): Cancel potential retrieval and free slave backend. (e_cal_backend_http_is_read_only): Clean up. (webcal_to_http_method) (uri_to_cache_dir) (ensure_cache_dir) (retrieval_done) (retrieval_progress_cb) (begin_retrieval_cb): Implement. (e_cal_backend_http_open): Set up slave backend. (e_cal_backend_http_remove) (e_cal_backend_http_is_loaded) (e_cal_backend_http_get_default_object) (e_cal_backend_http_get_object) (e_cal_backend_http_get_timezone) (e_cal_backend_http_add_timezone) (e_cal_backend_http_set_default_timezone) (e_cal_backend_http_get_object_list) (e_cal_backend_http_get_free_busy) (e_cal_backend_http_get_changes) (e_cal_backend_http_discard_alarm) (e_cal_backend_http_modify_object) (e_cal_backend_http_remove_object) (e_cal_backend_http_receive_objects) (e_cal_backend_http_send_objects) (e_cal_backend_http_internal_get_default_timezone) (e_cal_backend_http_internal_get_timezone): Implement with fallthrough to slave backend. (e_cal_backend_http_init): Remove cruft. * calendar/libedatacal/e-cal-backend-sync.c (e_cal_backend_sync_open): Use per-instance mutex. (e_cal_backend_sync_init): Set up per-instance mutex. (e_cal_backend_sync_dispose): Free per-instance mutex. * calendar/libedatacal/e-cal-backend.c (e_cal_backend_set_notification_proxy): Implement. (e_cal_backend_notify_object_created) (e_cal_backend_notify_object_modified) (e_cal_backend_notify_object_removed) (e_cal_backend_notify_mode) (e_cal_backend_notify_error): Use notification proxy if set. --- calendar/ChangeLog | 60 ++++ calendar/backends/file/e-cal-backend-file.c | 306 +++++++++++++--- calendar/backends/file/e-cal-backend-file.h | 10 +- calendar/backends/http/e-cal-backend-http.c | 539 +++++++++++++++++++++------- calendar/libedata-cal/e-cal-backend-file.c | 306 +++++++++++++--- calendar/libedata-cal/e-cal-backend-file.h | 10 +- calendar/libedata-cal/e-cal-backend-http.c | 539 +++++++++++++++++++++------- calendar/libedata-cal/e-cal-backend-sync.c | 20 +- calendar/libedata-cal/e-cal-backend.c | 49 +++ calendar/libedata-cal/e-cal-backend.h | 1 + calendar/libedatacal/e-cal-backend-file.c | 306 +++++++++++++--- calendar/libedatacal/e-cal-backend-file.h | 10 +- calendar/libedatacal/e-cal-backend-http.c | 539 +++++++++++++++++++++------- calendar/libedatacal/e-cal-backend-sync.c | 20 +- calendar/libedatacal/e-cal-backend.c | 49 +++ calendar/libedatacal/e-cal-backend.h | 1 + 16 files changed, 2157 insertions(+), 608 deletions(-) diff --git a/calendar/ChangeLog b/calendar/ChangeLog index e69de29..044527e 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -0,0 +1,60 @@ +2003-11-03 Hans Petter Jansson + + * calendar/libedatacal/e-cal-backend-file.c (free_calendar_components) + (free_calendar_data): Implement. + (e_cal_backend_file_dispose): Use above freers. + (get_uri_string_for_gnome_vfs): Implement. + (open_cal): Set priv->uri to be escaped, GnomeVFS-friendly. + (notify_removals_cb) + (notify_adds_modifies_cb) + (notify_changes) + (reload_cal): Implement. + (create_cal): Set priv->uri to be escaped, GnomeVFS-friendly. + (get_uri_string): Implement - unescapes once. + (e_cal_backend_file_compute_changes): Unescape DB URI once, don't make + like it's in a subdirectory. + (e_cal_backend_file_reload): Implement. + + * calendar/libedatacal/e-cal-backend-http.c + (e_cal_backend_http_finalize): Cancel potential retrieval and free + slave backend. + (e_cal_backend_http_is_read_only): Clean up. + (webcal_to_http_method) + (uri_to_cache_dir) + (ensure_cache_dir) + (retrieval_done) + (retrieval_progress_cb) + (begin_retrieval_cb): Implement. + (e_cal_backend_http_open): Set up slave backend. + (e_cal_backend_http_remove) + (e_cal_backend_http_is_loaded) + (e_cal_backend_http_get_default_object) + (e_cal_backend_http_get_object) + (e_cal_backend_http_get_timezone) + (e_cal_backend_http_add_timezone) + (e_cal_backend_http_set_default_timezone) + (e_cal_backend_http_get_object_list) + (e_cal_backend_http_get_free_busy) + (e_cal_backend_http_get_changes) + (e_cal_backend_http_discard_alarm) + (e_cal_backend_http_modify_object) + (e_cal_backend_http_remove_object) + (e_cal_backend_http_receive_objects) + (e_cal_backend_http_send_objects) + (e_cal_backend_http_internal_get_default_timezone) + (e_cal_backend_http_internal_get_timezone): Implement with fallthrough + to slave backend. + (e_cal_backend_http_init): Remove cruft. + + * calendar/libedatacal/e-cal-backend-sync.c + (e_cal_backend_sync_open): Use per-instance mutex. + (e_cal_backend_sync_init): Set up per-instance mutex. + (e_cal_backend_sync_dispose): Free per-instance mutex. + + * calendar/libedatacal/e-cal-backend.c + (e_cal_backend_set_notification_proxy): Implement. + (e_cal_backend_notify_object_created) + (e_cal_backend_notify_object_modified) + (e_cal_backend_notify_object_removed) + (e_cal_backend_notify_mode) + (e_cal_backend_notify_error): Use notification proxy if set. diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c index 8529a50..f12944d 100644 --- a/calendar/backends/file/e-cal-backend-file.c +++ b/calendar/backends/file/e-cal-backend-file.c @@ -180,6 +180,34 @@ save (ECalBackendFile *cbfile) return; } +static void +free_calendar_components (GHashTable *comp_uid_hash, icalcomponent *top_icomp) +{ + if (comp_uid_hash) { + g_hash_table_foreach (comp_uid_hash, (GHFunc) free_object, NULL); + g_hash_table_destroy (comp_uid_hash); + } + + if (top_icomp) { + icalcomponent_free (top_icomp); + } +} + +static void +free_calendar_data (ECalBackendFile *cbfile) +{ + ECalBackendFilePrivate *priv; + + priv = cbfile->priv; + + free_calendar_components (priv->comp_uid_hash, priv->icalcomp); + priv->comp_uid_hash = NULL; + priv->icalcomp = NULL; + + g_list_free (priv->comp); + priv->comp = NULL; +} + /* Dispose handler for the file backend */ static void e_cal_backend_file_dispose (GObject *object) @@ -192,19 +220,7 @@ e_cal_backend_file_dispose (GObject *object) /* Save if necessary */ - if (priv->comp_uid_hash) { - g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) free_object, NULL); - g_hash_table_destroy (priv->comp_uid_hash); - priv->comp_uid_hash = NULL; - } - - g_list_free (priv->comp); - priv->comp = NULL; - - if (priv->icalcomp) { - icalcomponent_free (priv->icalcomp); - priv->icalcomp = NULL; - } + free_calendar_data (cbfile); if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); @@ -558,6 +574,51 @@ scan_vcalendar (ECalBackendFile *cbfile) } } +static char * +get_uri_string_for_gnome_vfs (ECalBackend *backend) +{ + ECalBackendFile *cbfile; + ECalBackendFilePrivate *priv; + const char *master_uri; + char *full_uri, *str_uri; + GnomeVFSURI *uri; + + cbfile = E_CAL_BACKEND_FILE (backend); + priv = cbfile->priv; + + master_uri = e_cal_backend_get_uri (backend); + + /* FIXME Check the error conditions a little more elegantly here */ + if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) { + g_warning (G_STRLOC ": Existing file name %s", master_uri); + + return NULL; + } + + full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name); + uri = gnome_vfs_uri_new (full_uri); + g_free (full_uri); + + if (!uri) + return NULL; + + str_uri = gnome_vfs_uri_to_string (uri, + (GNOME_VFS_URI_HIDE_USER_NAME + | GNOME_VFS_URI_HIDE_PASSWORD + | GNOME_VFS_URI_HIDE_HOST_NAME + | GNOME_VFS_URI_HIDE_HOST_PORT + | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); + gnome_vfs_uri_unref (uri); + + if (!str_uri || !strlen (str_uri)) { + g_free (str_uri); + + return NULL; + } + + return str_uri; +} + /* Parses an open iCalendar file and loads it into the backend */ static ECalBackendSyncStatus open_cal (ECalBackendFile *cbfile, const char *uristr) @@ -586,11 +647,152 @@ open_cal (ECalBackendFile *cbfile, const char *uristr) priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); scan_vcalendar (cbfile); - priv->uri = g_strdup (uristr); + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); return GNOME_Evolution_Calendar_Success; } +typedef struct +{ + ECalBackend *backend; + GHashTable *old_uid_hash; + GHashTable *new_uid_hash; +} +BackendDeltaContext; + +static void +notify_removals_cb (gpointer key, gpointer value, gpointer data) +{ + BackendDeltaContext *context = data; + const gchar *uid = key; + ECalBackendFileObject *old_obj_data = value; + + if (!g_hash_table_lookup (context->new_uid_hash, uid)) { + icalcomponent *old_icomp; + gchar *old_obj_str; + + /* Object was removed */ + + old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object); + if (!old_icomp) + return; + + old_obj_str = icalcomponent_as_ical_string (old_icomp); + if (!old_obj_str) + return; + + e_cal_backend_notify_object_removed (context->backend, uid, old_obj_str); + } +} + +static void +notify_adds_modifies_cb (gpointer key, gpointer value, gpointer data) +{ + BackendDeltaContext *context = data; + const gchar *uid = key; + ECalBackendFileObject *new_obj_data = value; + ECalBackendFileObject *old_obj_data; + icalcomponent *old_icomp, *new_icomp; + gchar *old_obj_str, *new_obj_str; + + old_obj_data = g_hash_table_lookup (context->old_uid_hash, uid); + + if (!old_obj_data) { + /* Object was added */ + + new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object); + if (!new_icomp) + return; + + new_obj_str = icalcomponent_as_ical_string (new_icomp); + if (!new_obj_str) + return; + + e_cal_backend_notify_object_created (context->backend, new_obj_str); + } else { + old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object); + new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object); + if (!old_icomp || !new_icomp) + return; + + old_obj_str = icalcomponent_as_ical_string (old_icomp); + new_obj_str = icalcomponent_as_ical_string (new_icomp); + if (!old_obj_str || !new_obj_str) + return; + + if (strcmp (old_obj_str, new_obj_str)) { + /* Object was modified */ + + e_cal_backend_notify_object_modified (context->backend, old_obj_str, new_obj_str); + } + } +} + +static void +notify_changes (ECalBackendFile *cbfile, GHashTable *old_uid_hash, GHashTable *new_uid_hash) +{ + BackendDeltaContext context; + + context.backend = E_CAL_BACKEND (cbfile); + context.old_uid_hash = old_uid_hash; + context.new_uid_hash = new_uid_hash; + + g_hash_table_foreach (old_uid_hash, (GHFunc) notify_removals_cb, &context); + g_hash_table_foreach (new_uid_hash, (GHFunc) notify_adds_modifies_cb, &context); +} + +static ECalBackendSyncStatus +reload_cal (ECalBackendFile *cbfile, const char *uristr) +{ + ECalBackendFilePrivate *priv; + icalcomponent *icalcomp, *icalcomp_old; + GHashTable *comp_uid_hash_old; + + priv = cbfile->priv; + + icalcomp = e_cal_util_parse_ics_file (uristr); + if (!icalcomp) + return GNOME_Evolution_Calendar_OtherError; + + /* FIXME: should we try to demangle XROOT components and + * individual components as well? + */ + + if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { + icalcomponent_free (icalcomp); + + return GNOME_Evolution_Calendar_OtherError; + } + + /* Keep old data for comparison - free later */ + + icalcomp_old = priv->icalcomp; + priv->icalcomp = NULL; + + comp_uid_hash_old = priv->comp_uid_hash; + priv->comp_uid_hash = NULL; + + /* Load new calendar */ + + free_calendar_data (cbfile); + + priv->icalcomp = icalcomp; + + priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); + scan_vcalendar (cbfile); + + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); + + /* Compare old and new versions of calendar */ + + notify_changes (cbfile, comp_uid_hash_old, priv->comp_uid_hash); + + /* Free old data */ + + free_calendar_components (comp_uid_hash_old, icalcomp_old); + return GNOME_Evolution_Calendar_Success; +} + static ECalBackendSyncStatus create_cal (ECalBackendFile *cbfile, const char *uristr) { @@ -604,7 +806,7 @@ create_cal (ECalBackendFile *cbfile, const char *uristr) /* Create our internal data */ priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); - priv->uri = g_strdup (uristr); + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); save (cbfile); @@ -614,47 +816,13 @@ create_cal (ECalBackendFile *cbfile, const char *uristr) static char * get_uri_string (ECalBackend *backend) { - ECalBackendFile *cbfile; - ECalBackendFilePrivate *priv; - const char *master_uri; - char *full_uri, *str_uri; - GnomeVFSURI *uri; - - cbfile = E_CAL_BACKEND_FILE (backend); - priv = cbfile->priv; - - master_uri = e_cal_backend_get_uri (backend); - g_message (G_STRLOC ": Trying to open %s", master_uri); - - /* FIXME Check the error conditions a little more elegantly here */ - if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) { - g_warning (G_STRLOC ": Existing file name %s", master_uri); - - return NULL; - } - - full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name); - uri = gnome_vfs_uri_new (full_uri); - g_free (full_uri); - - if (!uri) - return NULL; - - str_uri = gnome_vfs_uri_to_string (uri, - (GNOME_VFS_URI_HIDE_USER_NAME - | GNOME_VFS_URI_HIDE_PASSWORD - | GNOME_VFS_URI_HIDE_HOST_NAME - | GNOME_VFS_URI_HIDE_HOST_PORT - | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); - gnome_vfs_uri_unref (uri); - - if (!str_uri || !strlen (str_uri)) { - g_free (str_uri); + gchar *str_uri, *full_uri; - return NULL; - } + str_uri = get_uri_string_for_gnome_vfs (backend); + full_uri = gnome_vfs_unescape_string (str_uri, ""); + g_free (str_uri); - return str_uri; + return full_uri; } /* Open handler for the file backend */ @@ -1239,13 +1407,16 @@ e_cal_backend_file_compute_changes (ECalBackendFile *cbfile, const char *change_ EXmlHash *ehash; ECalBackendFileComputeChangesData be_data; GList *i; + gchar *unescaped_uri; priv = cbfile->priv; /* FIXME Will this always work? */ - filename = g_strdup_printf ("%s/%s.db", priv->uri, change_id); + unescaped_uri = gnome_vfs_unescape_string (priv->uri, ""); + filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id); ehash = e_xmlhash_new (filename); g_free (filename); + g_free (unescaped_uri); /* Calculate adds and modifies */ for (i = priv->comp; i != NULL; i = i->next) { @@ -1979,3 +2150,28 @@ e_cal_backend_file_get_file_name (ECalBackendFile *cbfile) return priv->file_name; } + +ECalBackendSyncStatus +e_cal_backend_file_reload (ECalBackendFile *cbfile) +{ + ECalBackendFilePrivate *priv; + char *str_uri; + ECalBackendSyncStatus status; + + priv = cbfile->priv; + + str_uri = get_uri_string (E_CAL_BACKEND (cbfile)); + if (!str_uri) + return GNOME_Evolution_Calendar_OtherError; + + if (access (str_uri, R_OK) == 0) { + status = reload_cal (cbfile, str_uri); + if (access (str_uri, W_OK) != 0) + priv->read_only = TRUE; + } else { + status = GNOME_Evolution_Calendar_NoSuchCal; + } + + g_free (str_uri); + return status; +} diff --git a/calendar/backends/file/e-cal-backend-file.h b/calendar/backends/file/e-cal-backend-file.h index f1da45d..88d6ea4 100644 --- a/calendar/backends/file/e-cal-backend-file.h +++ b/calendar/backends/file/e-cal-backend-file.h @@ -52,11 +52,13 @@ struct _ECalBackendFileClass { ECalBackendSyncClass parent_class; }; -GType e_cal_backend_file_get_type (void); +GType e_cal_backend_file_get_type (void); -void e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, - const char *file_name); -const char *e_cal_backend_file_get_file_name (ECalBackendFile *cbfile); +void e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, + const char *file_name); +const char *e_cal_backend_file_get_file_name (ECalBackendFile *cbfile); + +ECalBackendSyncStatus e_cal_backend_file_reload (ECalBackendFile *cbfile); diff --git a/calendar/backends/http/e-cal-backend-http.c b/calendar/backends/http/e-cal-backend-http.c index 4a40538..099823d 100644 --- a/calendar/backends/http/e-cal-backend-http.c +++ b/calendar/backends/http/e-cal-backend-http.c @@ -30,8 +30,8 @@ #include #include #include +#include "e-cal-backend-file-events.h" #include "e-cal-backend-http.h" -#include "e-cal-backend-file.h" #include "e-cal-backend-util.h" #include "e-cal-backend-sexp.h" @@ -46,7 +46,7 @@ struct _ECalBackendHttpPrivate { CalMode mode; /* Cached-file backend */ - ECalBackendFile file_backend; + ECalBackendSync *file_backend; /* The calendar's default timezone, used for resolving DATE and floating DATE-TIME values. */ @@ -54,6 +54,9 @@ struct _ECalBackendHttpPrivate { /* The list of live queries */ GList *queries; + + /* GnomeVFS handle for remote retrieval */ + GnomeVFSAsyncHandle *retrieval_handle; }; @@ -69,11 +72,11 @@ static ECalBackendSyncClass *parent_class; static void e_cal_backend_http_dispose (GObject *object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (object); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (object); + priv = cbhttp->priv; if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); @@ -83,24 +86,34 @@ e_cal_backend_http_dispose (GObject *object) static void e_cal_backend_http_finalize (GObject *object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; g_return_if_fail (object != NULL); g_return_if_fail (E_IS_CAL_BACKEND_HTTP (object)); - cbfile = E_CAL_BACKEND_HTTP (object); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (object); + priv = cbhttp->priv; /* Clean up */ + if (priv->retrieval_handle) { + gnome_vfs_async_cancel (priv->retrieval_handle); + priv->retrieval_handle = NULL; + } + + if (priv->file_backend) { + g_object_unref (priv->file_backend); + e_cal_backend_set_notification_proxy (E_CAL_BACKEND (priv->file_backend), NULL); + } + if (priv->uri) { g_free (priv->uri); priv->uri = NULL; } g_free (priv); - cbfile->priv = NULL; + cbhttp->priv = NULL; if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (object); @@ -114,8 +127,6 @@ e_cal_backend_http_finalize (GObject *object) static ECalBackendSyncStatus e_cal_backend_http_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only) { - ECalBackendHttp *cbfile = backend; - *read_only = TRUE; return GNOME_Evolution_Calendar_Success; @@ -160,58 +171,272 @@ e_cal_backend_http_get_static_capabilities (ECalBackendSync *backend, EDataCal * return GNOME_Evolution_Calendar_Success; } +static gchar * +webcal_to_http_method (const gchar *webcal_str) +{ + if (strncmp ("webcal://", webcal_str, sizeof ("webcal://") - 1)) + return NULL; + + return g_strconcat ("http://", webcal_str + sizeof ("webcal://") - 1, NULL); +} + +static gchar * +uri_to_cache_dir (const gchar *uri_str) +{ + gchar *http_uri_str, *dir_str, *escaped_dir_str; + GnomeVFSURI *uri; + + http_uri_str = webcal_to_http_method (uri_str); + if (!http_uri_str) + http_uri_str = g_strdup (uri_str); + + uri = gnome_vfs_uri_new (http_uri_str); + g_free (http_uri_str); + + if (!uri) { + g_warning ("No GnomeVFSURI."); + return NULL; + } + + dir_str = gnome_vfs_uri_to_string (uri, + GNOME_VFS_URI_HIDE_PASSWORD | + GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD); + gnome_vfs_uri_unref (uri); + + if (!dir_str || !strlen (dir_str)) { + g_warning ("No dir_str."); + return NULL; + } + + /* GnomeVFS unescapes paths. We need to escape twice. */ + escaped_dir_str = gnome_vfs_escape_slashes (dir_str); + g_free (dir_str); + dir_str = gnome_vfs_escape_slashes (escaped_dir_str); + g_free (escaped_dir_str); + escaped_dir_str = dir_str; + + if (!escaped_dir_str || !strlen (escaped_dir_str)) { + g_warning ("No escaped_dir_str."); + return NULL; + } + + return g_build_filename (g_get_home_dir (), + "/.evolution/calendar/webcal/", + escaped_dir_str, NULL); +} + +static gboolean +ensure_cache_dir (const gchar *cache_dir_str) +{ + GnomeVFSResult result; + gchar *webcal_dir; + + /* Make sure we have the webcal base dir */ + webcal_dir = g_build_filename (g_get_home_dir (), + "/.evolution/calendar/webcal", NULL); + gnome_vfs_make_directory (webcal_dir, GNOME_VFS_PERM_USER_ALL); + g_free (webcal_dir); + + /* Create cache subdirectory */ + result = gnome_vfs_make_directory (cache_dir_str, GNOME_VFS_PERM_USER_ALL); + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) + return FALSE; + + return TRUE; +} + +static void +retrieval_done (ECalBackendHttp *cbhttp) +{ + ECalBackendHttpPrivate *priv; + icalcomponent *icalcomp; + gchar *cache_dir; + gchar *temp_file, *cal_file; + gchar *temp_file_unescaped; + + priv = cbhttp->priv; + + priv->retrieval_handle = NULL; + + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return; + + temp_file = g_build_filename (cache_dir, "/calendar.ics.tmp", NULL); + cal_file = g_build_filename (cache_dir, "/calendar.ics", NULL); + + temp_file_unescaped = gnome_vfs_unescape_string (temp_file, ""); + icalcomp = e_cal_util_parse_ics_file (temp_file_unescaped); + g_free (temp_file_unescaped); + + if (!icalcomp) { + e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Bad file format.")); + goto out; + } + + if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { + e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Not a calendar.")); + icalcomponent_free (icalcomp); + goto out; + } + + /* Update calendar file and tell file backend to reload */ + + gnome_vfs_move (temp_file, cal_file, TRUE); + e_cal_backend_file_reload (E_CAL_BACKEND_FILE (priv->file_backend)); + +out: + g_free (temp_file); + g_free (cal_file); +} + +static gint +retrieval_progress_cb (GnomeVFSAsyncHandle *handle, GnomeVFSXferProgressInfo *info, gpointer data) +{ + ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (data); + ECalBackendHttpPrivate *priv; + + priv = cbhttp->priv; + + /* TODO: Handle errors */ + /* TODO: Report progress */ + + g_message ("GnomeVFS async progress: %s", + info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OK ? "ok" : + info->status == GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR ? "error" : "other"); + + g_message ("(%d) %s -> %s", info->phase, info->source_name, info->target_name); + + if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) + retrieval_done (cbhttp); + + return TRUE; +} + +static gboolean +begin_retrieval_cb (ECalBackendHttp *cbhttp) +{ + GnomeVFSURI *source_uri, *dest_uri; + gchar *source_uri_str, *cache_dir, *temp_file; + GList *source_uri_list = NULL, *dest_uri_list = NULL; + ECalBackendHttpPrivate *priv; + + priv = cbhttp->priv; + + if (priv->retrieval_handle != NULL) + return FALSE; + + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return FALSE; + + temp_file = g_build_filename (cache_dir, "/calendar.ics.tmp", NULL); + + source_uri_str = webcal_to_http_method (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!source_uri_str) + source_uri_str = g_strdup (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + + source_uri = gnome_vfs_uri_new (source_uri_str); + dest_uri = gnome_vfs_uri_new (temp_file); + + g_free (source_uri_str); + + source_uri_list = g_list_append (source_uri_list, source_uri); + dest_uri_list = g_list_append (dest_uri_list, dest_uri); + + gnome_vfs_async_xfer (&priv->retrieval_handle, + source_uri_list, dest_uri_list, + GNOME_VFS_XFER_DEFAULT, + GNOME_VFS_XFER_ERROR_MODE_QUERY, + GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, + GNOME_VFS_PRIORITY_DEFAULT, + retrieval_progress_cb, cbhttp, + NULL, NULL); + + g_list_free (source_uri_list); + g_list_free (dest_uri_list); + + gnome_vfs_uri_unref (source_uri); + gnome_vfs_uri_unref (dest_uri); + g_free (temp_file); + g_free (cache_dir); + return FALSE; +} + /* Open handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - char *str_uri; - ECalBackendSyncStatus status = GNOME_Evolution_Calendar_NoSuchCal; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) { + gchar *cache_dir; - g_message ("Open URI '%s'.", e_cal_backend_get_uri (E_CAL_BACKEND (cbfile))); + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return GNOME_Evolution_Calendar_NoSuchCal; - return status; + if (!ensure_cache_dir (cache_dir)) { + g_free (cache_dir); + return GNOME_Evolution_Calendar_NoSuchCal; + } + + priv->file_backend = g_object_new (E_TYPE_CAL_BACKEND_FILE_EVENTS, "uri", cache_dir, NULL); + e_cal_backend_set_notification_proxy (E_CAL_BACKEND (priv->file_backend), + E_CAL_BACKEND (backend)); + g_free (cache_dir); + + g_idle_add ((GSourceFunc) begin_retrieval_cb, cbhttp); + } + + e_cal_backend_sync_open (priv->file_backend, cal, FALSE); + return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus e_cal_backend_http_remove (ECalBackendSync *backend, EDataCal *cal) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - char *str_uri; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_OtherError; - return GNOME_Evolution_Calendar_OtherError; + return e_cal_backend_sync_remove (priv->file_backend, cal); } /* is_loaded handler for the file backend */ static gboolean e_cal_backend_http_is_loaded (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - return FALSE; + if (!priv->file_backend) + return FALSE; + + return e_cal_backend_is_loaded (E_CAL_BACKEND (priv->file_backend)); } /* is_remote handler for the file backend */ static CalMode e_cal_backend_http_get_mode (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; return priv->mode; } @@ -225,12 +450,12 @@ e_cal_backend_http_get_mode (ECalBackend *backend) static void e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; GNOME_Evolution_Calendar_CalMode set_mode; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; switch (mode) { case CAL_MODE_LOCAL: @@ -260,98 +485,132 @@ e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode) static ECalBackendSyncStatus e_cal_backend_http_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object) { - ECalComponent *comp; - - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_default_object (priv->file_backend, cal, object); } /* Get_object_component handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - ECalComponent *comp = NULL; - gboolean free_comp = FALSE; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_object (priv->file_backend, cal, uid, rid, object); } /* Get_timezone_object handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icaltimezone *zone; - icalcomponent *icalcomp; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) { + icaltimezone *zone; + icalcomponent *icalcomp; + + zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); + if (!zone) + return GNOME_Evolution_Calendar_ObjectNotFound; + + icalcomp = icaltimezone_get_component (zone); + if (!icalcomp) + return GNOME_Evolution_Calendar_InvalidObject; + + *object = g_strdup (icalcomponent_as_ical_string (icalcomp)); + return GNOME_Evolution_Calendar_Success; + } + + return e_cal_backend_sync_get_timezone (priv->file_backend, cal, tzid, object); } /* Add_timezone handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj) { - icalcomponent *tz_comp; - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = (ECalBackendHttp *) backend; + cbhttp = (ECalBackendHttp *) backend; - g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbfile), GNOME_Evolution_Calendar_OtherError); + g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbhttp), GNOME_Evolution_Calendar_OtherError); g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError); - priv = cbfile->priv; + priv = cbhttp->priv; - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_InvalidObject; + + return e_cal_backend_sync_add_timezone (priv->file_backend, cal, tzobj); } static ECalBackendSyncStatus e_cal_backend_http_set_default_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icaltimezone *zone; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_set_default_timezone (priv->file_backend, cal, tzid); } /* Get_objects_in_range handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; - return GNOME_Evolution_Calendar_Success; + return e_cal_backend_sync_get_object_list (priv->file_backend, cal, sexp, objects); } /* get_query handler for the file backend */ static void e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return; + + e_cal_backend_start_query (E_CAL_BACKEND (priv->file_backend), query); } /* Get_free_busy handler for the file backend */ @@ -359,20 +618,19 @@ static ECalBackendSyncStatus e_cal_backend_http_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users, time_t start, time_t end, GList **freebusy) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - gchar *address, *name; - icalcomponent *vfb; - char *calobj; - GList *l; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange); g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_get_free_busy (priv->file_backend, cal, users, start, end, freebusy); } /* Get_changes handler for the file backend */ @@ -380,62 +638,69 @@ static ECalBackendSyncStatus e_cal_backend_http_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id, GList **adds, GList **modifies, GList **deletes) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_changes (priv->file_backend, cal, change_id, adds, modifies, deletes); } /* Discard_alarm handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid) { - /* we just do nothing with the alarm */ - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_Success; + + return e_cal_backend_sync_discard_alarm (priv->file_backend, cal, uid, auid); } static ECalBackendSyncStatus e_cal_backend_http_create_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, char **uid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *icalcomp; - icalcomponent_kind kind; - ECalComponent *comp; - const char *comp_uid; - struct icaltimetype current; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_create_object (priv->file_backend, cal, calobj, uid); } static ECalBackendSyncStatus e_cal_backend_http_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, CalObjModType mod, char **old_object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *icalcomp; - icalcomponent_kind kind; - const char *comp_uid; - ECalComponent *comp; - struct icaltimetype current; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_modify_object (priv->file_backend, cal, calobj, mod, old_object); } /* Remove_object handler for the file backend */ @@ -444,73 +709,81 @@ e_cal_backend_http_remove_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - ECalComponent *comp; - char *hash_rid; - GSList *categories; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_remove_object (priv->file_backend, cal, uid, rid, mod, object); } /* Update_objects handler for the file backend. */ static ECalBackendSyncStatus e_cal_backend_http_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *toplevel_comp, *icalcomp = NULL; - icalcomponent_kind kind; - icalproperty_method method; - icalcomponent *subcomp; - GList *comps, *l; - ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject); - return status; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_InvalidObject; + + return e_cal_backend_sync_receive_objects (priv->file_backend, cal, calobj); } static ECalBackendSyncStatus e_cal_backend_http_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { - /* FIXME Put in a util routine to send stuff via email */ - - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_Success; + + return e_cal_backend_sync_send_objects (priv->file_backend, cal, calobj); } static icaltimezone * e_cal_backend_http_internal_get_default_timezone (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + if (!priv->file_backend) + return NULL; - return priv->default_zone; + return e_cal_backend_internal_get_default_timezone (E_CAL_BACKEND (priv->file_backend)); } static icaltimezone * e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; icaltimezone *zone; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - if (!strcmp (tzid, "UTC")) + if (priv->file_backend) + zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (priv->file_backend), tzid); + else if (!strcmp (tzid, "UTC")) zone = icaltimezone_get_utc_timezone (); else { zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); @@ -521,20 +794,14 @@ e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid /* Object initialization function for the file backend */ static void -e_cal_backend_http_init (ECalBackendHttp *cbfile, ECalBackendHttpClass *class) +e_cal_backend_http_init (ECalBackendHttp *cbhttp, ECalBackendHttpClass *class) { ECalBackendHttpPrivate *priv; - g_message ("Webcal backend init."); - priv = g_new0 (ECalBackendHttpPrivate, 1); - cbfile->priv = priv; + cbhttp->priv = priv; priv->uri = NULL; - -#if 0 - priv->config_listener = e_config_listener_new (); -#endif } /* Class initialization function for the file backend */ @@ -600,8 +867,6 @@ e_cal_backend_http_get_type (void) { static GType e_cal_backend_http_type = 0; - g_message (G_STRLOC); - if (!e_cal_backend_http_type) { static GTypeInfo info = { sizeof (ECalBackendHttpClass), @@ -614,7 +879,7 @@ e_cal_backend_http_get_type (void) (GInstanceInitFunc) e_cal_backend_http_init }; e_cal_backend_http_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC, - "ECalBackendHttp", &info, 0); + "ECalBackendHttp", &info, 0); } return e_cal_backend_http_type; diff --git a/calendar/libedata-cal/e-cal-backend-file.c b/calendar/libedata-cal/e-cal-backend-file.c index 8529a50..f12944d 100644 --- a/calendar/libedata-cal/e-cal-backend-file.c +++ b/calendar/libedata-cal/e-cal-backend-file.c @@ -180,6 +180,34 @@ save (ECalBackendFile *cbfile) return; } +static void +free_calendar_components (GHashTable *comp_uid_hash, icalcomponent *top_icomp) +{ + if (comp_uid_hash) { + g_hash_table_foreach (comp_uid_hash, (GHFunc) free_object, NULL); + g_hash_table_destroy (comp_uid_hash); + } + + if (top_icomp) { + icalcomponent_free (top_icomp); + } +} + +static void +free_calendar_data (ECalBackendFile *cbfile) +{ + ECalBackendFilePrivate *priv; + + priv = cbfile->priv; + + free_calendar_components (priv->comp_uid_hash, priv->icalcomp); + priv->comp_uid_hash = NULL; + priv->icalcomp = NULL; + + g_list_free (priv->comp); + priv->comp = NULL; +} + /* Dispose handler for the file backend */ static void e_cal_backend_file_dispose (GObject *object) @@ -192,19 +220,7 @@ e_cal_backend_file_dispose (GObject *object) /* Save if necessary */ - if (priv->comp_uid_hash) { - g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) free_object, NULL); - g_hash_table_destroy (priv->comp_uid_hash); - priv->comp_uid_hash = NULL; - } - - g_list_free (priv->comp); - priv->comp = NULL; - - if (priv->icalcomp) { - icalcomponent_free (priv->icalcomp); - priv->icalcomp = NULL; - } + free_calendar_data (cbfile); if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); @@ -558,6 +574,51 @@ scan_vcalendar (ECalBackendFile *cbfile) } } +static char * +get_uri_string_for_gnome_vfs (ECalBackend *backend) +{ + ECalBackendFile *cbfile; + ECalBackendFilePrivate *priv; + const char *master_uri; + char *full_uri, *str_uri; + GnomeVFSURI *uri; + + cbfile = E_CAL_BACKEND_FILE (backend); + priv = cbfile->priv; + + master_uri = e_cal_backend_get_uri (backend); + + /* FIXME Check the error conditions a little more elegantly here */ + if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) { + g_warning (G_STRLOC ": Existing file name %s", master_uri); + + return NULL; + } + + full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name); + uri = gnome_vfs_uri_new (full_uri); + g_free (full_uri); + + if (!uri) + return NULL; + + str_uri = gnome_vfs_uri_to_string (uri, + (GNOME_VFS_URI_HIDE_USER_NAME + | GNOME_VFS_URI_HIDE_PASSWORD + | GNOME_VFS_URI_HIDE_HOST_NAME + | GNOME_VFS_URI_HIDE_HOST_PORT + | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); + gnome_vfs_uri_unref (uri); + + if (!str_uri || !strlen (str_uri)) { + g_free (str_uri); + + return NULL; + } + + return str_uri; +} + /* Parses an open iCalendar file and loads it into the backend */ static ECalBackendSyncStatus open_cal (ECalBackendFile *cbfile, const char *uristr) @@ -586,11 +647,152 @@ open_cal (ECalBackendFile *cbfile, const char *uristr) priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); scan_vcalendar (cbfile); - priv->uri = g_strdup (uristr); + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); return GNOME_Evolution_Calendar_Success; } +typedef struct +{ + ECalBackend *backend; + GHashTable *old_uid_hash; + GHashTable *new_uid_hash; +} +BackendDeltaContext; + +static void +notify_removals_cb (gpointer key, gpointer value, gpointer data) +{ + BackendDeltaContext *context = data; + const gchar *uid = key; + ECalBackendFileObject *old_obj_data = value; + + if (!g_hash_table_lookup (context->new_uid_hash, uid)) { + icalcomponent *old_icomp; + gchar *old_obj_str; + + /* Object was removed */ + + old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object); + if (!old_icomp) + return; + + old_obj_str = icalcomponent_as_ical_string (old_icomp); + if (!old_obj_str) + return; + + e_cal_backend_notify_object_removed (context->backend, uid, old_obj_str); + } +} + +static void +notify_adds_modifies_cb (gpointer key, gpointer value, gpointer data) +{ + BackendDeltaContext *context = data; + const gchar *uid = key; + ECalBackendFileObject *new_obj_data = value; + ECalBackendFileObject *old_obj_data; + icalcomponent *old_icomp, *new_icomp; + gchar *old_obj_str, *new_obj_str; + + old_obj_data = g_hash_table_lookup (context->old_uid_hash, uid); + + if (!old_obj_data) { + /* Object was added */ + + new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object); + if (!new_icomp) + return; + + new_obj_str = icalcomponent_as_ical_string (new_icomp); + if (!new_obj_str) + return; + + e_cal_backend_notify_object_created (context->backend, new_obj_str); + } else { + old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object); + new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object); + if (!old_icomp || !new_icomp) + return; + + old_obj_str = icalcomponent_as_ical_string (old_icomp); + new_obj_str = icalcomponent_as_ical_string (new_icomp); + if (!old_obj_str || !new_obj_str) + return; + + if (strcmp (old_obj_str, new_obj_str)) { + /* Object was modified */ + + e_cal_backend_notify_object_modified (context->backend, old_obj_str, new_obj_str); + } + } +} + +static void +notify_changes (ECalBackendFile *cbfile, GHashTable *old_uid_hash, GHashTable *new_uid_hash) +{ + BackendDeltaContext context; + + context.backend = E_CAL_BACKEND (cbfile); + context.old_uid_hash = old_uid_hash; + context.new_uid_hash = new_uid_hash; + + g_hash_table_foreach (old_uid_hash, (GHFunc) notify_removals_cb, &context); + g_hash_table_foreach (new_uid_hash, (GHFunc) notify_adds_modifies_cb, &context); +} + +static ECalBackendSyncStatus +reload_cal (ECalBackendFile *cbfile, const char *uristr) +{ + ECalBackendFilePrivate *priv; + icalcomponent *icalcomp, *icalcomp_old; + GHashTable *comp_uid_hash_old; + + priv = cbfile->priv; + + icalcomp = e_cal_util_parse_ics_file (uristr); + if (!icalcomp) + return GNOME_Evolution_Calendar_OtherError; + + /* FIXME: should we try to demangle XROOT components and + * individual components as well? + */ + + if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { + icalcomponent_free (icalcomp); + + return GNOME_Evolution_Calendar_OtherError; + } + + /* Keep old data for comparison - free later */ + + icalcomp_old = priv->icalcomp; + priv->icalcomp = NULL; + + comp_uid_hash_old = priv->comp_uid_hash; + priv->comp_uid_hash = NULL; + + /* Load new calendar */ + + free_calendar_data (cbfile); + + priv->icalcomp = icalcomp; + + priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); + scan_vcalendar (cbfile); + + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); + + /* Compare old and new versions of calendar */ + + notify_changes (cbfile, comp_uid_hash_old, priv->comp_uid_hash); + + /* Free old data */ + + free_calendar_components (comp_uid_hash_old, icalcomp_old); + return GNOME_Evolution_Calendar_Success; +} + static ECalBackendSyncStatus create_cal (ECalBackendFile *cbfile, const char *uristr) { @@ -604,7 +806,7 @@ create_cal (ECalBackendFile *cbfile, const char *uristr) /* Create our internal data */ priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); - priv->uri = g_strdup (uristr); + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); save (cbfile); @@ -614,47 +816,13 @@ create_cal (ECalBackendFile *cbfile, const char *uristr) static char * get_uri_string (ECalBackend *backend) { - ECalBackendFile *cbfile; - ECalBackendFilePrivate *priv; - const char *master_uri; - char *full_uri, *str_uri; - GnomeVFSURI *uri; - - cbfile = E_CAL_BACKEND_FILE (backend); - priv = cbfile->priv; - - master_uri = e_cal_backend_get_uri (backend); - g_message (G_STRLOC ": Trying to open %s", master_uri); - - /* FIXME Check the error conditions a little more elegantly here */ - if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) { - g_warning (G_STRLOC ": Existing file name %s", master_uri); - - return NULL; - } - - full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name); - uri = gnome_vfs_uri_new (full_uri); - g_free (full_uri); - - if (!uri) - return NULL; - - str_uri = gnome_vfs_uri_to_string (uri, - (GNOME_VFS_URI_HIDE_USER_NAME - | GNOME_VFS_URI_HIDE_PASSWORD - | GNOME_VFS_URI_HIDE_HOST_NAME - | GNOME_VFS_URI_HIDE_HOST_PORT - | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); - gnome_vfs_uri_unref (uri); - - if (!str_uri || !strlen (str_uri)) { - g_free (str_uri); + gchar *str_uri, *full_uri; - return NULL; - } + str_uri = get_uri_string_for_gnome_vfs (backend); + full_uri = gnome_vfs_unescape_string (str_uri, ""); + g_free (str_uri); - return str_uri; + return full_uri; } /* Open handler for the file backend */ @@ -1239,13 +1407,16 @@ e_cal_backend_file_compute_changes (ECalBackendFile *cbfile, const char *change_ EXmlHash *ehash; ECalBackendFileComputeChangesData be_data; GList *i; + gchar *unescaped_uri; priv = cbfile->priv; /* FIXME Will this always work? */ - filename = g_strdup_printf ("%s/%s.db", priv->uri, change_id); + unescaped_uri = gnome_vfs_unescape_string (priv->uri, ""); + filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id); ehash = e_xmlhash_new (filename); g_free (filename); + g_free (unescaped_uri); /* Calculate adds and modifies */ for (i = priv->comp; i != NULL; i = i->next) { @@ -1979,3 +2150,28 @@ e_cal_backend_file_get_file_name (ECalBackendFile *cbfile) return priv->file_name; } + +ECalBackendSyncStatus +e_cal_backend_file_reload (ECalBackendFile *cbfile) +{ + ECalBackendFilePrivate *priv; + char *str_uri; + ECalBackendSyncStatus status; + + priv = cbfile->priv; + + str_uri = get_uri_string (E_CAL_BACKEND (cbfile)); + if (!str_uri) + return GNOME_Evolution_Calendar_OtherError; + + if (access (str_uri, R_OK) == 0) { + status = reload_cal (cbfile, str_uri); + if (access (str_uri, W_OK) != 0) + priv->read_only = TRUE; + } else { + status = GNOME_Evolution_Calendar_NoSuchCal; + } + + g_free (str_uri); + return status; +} diff --git a/calendar/libedata-cal/e-cal-backend-file.h b/calendar/libedata-cal/e-cal-backend-file.h index f1da45d..88d6ea4 100644 --- a/calendar/libedata-cal/e-cal-backend-file.h +++ b/calendar/libedata-cal/e-cal-backend-file.h @@ -52,11 +52,13 @@ struct _ECalBackendFileClass { ECalBackendSyncClass parent_class; }; -GType e_cal_backend_file_get_type (void); +GType e_cal_backend_file_get_type (void); -void e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, - const char *file_name); -const char *e_cal_backend_file_get_file_name (ECalBackendFile *cbfile); +void e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, + const char *file_name); +const char *e_cal_backend_file_get_file_name (ECalBackendFile *cbfile); + +ECalBackendSyncStatus e_cal_backend_file_reload (ECalBackendFile *cbfile); diff --git a/calendar/libedata-cal/e-cal-backend-http.c b/calendar/libedata-cal/e-cal-backend-http.c index 4a40538..099823d 100644 --- a/calendar/libedata-cal/e-cal-backend-http.c +++ b/calendar/libedata-cal/e-cal-backend-http.c @@ -30,8 +30,8 @@ #include #include #include +#include "e-cal-backend-file-events.h" #include "e-cal-backend-http.h" -#include "e-cal-backend-file.h" #include "e-cal-backend-util.h" #include "e-cal-backend-sexp.h" @@ -46,7 +46,7 @@ struct _ECalBackendHttpPrivate { CalMode mode; /* Cached-file backend */ - ECalBackendFile file_backend; + ECalBackendSync *file_backend; /* The calendar's default timezone, used for resolving DATE and floating DATE-TIME values. */ @@ -54,6 +54,9 @@ struct _ECalBackendHttpPrivate { /* The list of live queries */ GList *queries; + + /* GnomeVFS handle for remote retrieval */ + GnomeVFSAsyncHandle *retrieval_handle; }; @@ -69,11 +72,11 @@ static ECalBackendSyncClass *parent_class; static void e_cal_backend_http_dispose (GObject *object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (object); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (object); + priv = cbhttp->priv; if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); @@ -83,24 +86,34 @@ e_cal_backend_http_dispose (GObject *object) static void e_cal_backend_http_finalize (GObject *object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; g_return_if_fail (object != NULL); g_return_if_fail (E_IS_CAL_BACKEND_HTTP (object)); - cbfile = E_CAL_BACKEND_HTTP (object); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (object); + priv = cbhttp->priv; /* Clean up */ + if (priv->retrieval_handle) { + gnome_vfs_async_cancel (priv->retrieval_handle); + priv->retrieval_handle = NULL; + } + + if (priv->file_backend) { + g_object_unref (priv->file_backend); + e_cal_backend_set_notification_proxy (E_CAL_BACKEND (priv->file_backend), NULL); + } + if (priv->uri) { g_free (priv->uri); priv->uri = NULL; } g_free (priv); - cbfile->priv = NULL; + cbhttp->priv = NULL; if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (object); @@ -114,8 +127,6 @@ e_cal_backend_http_finalize (GObject *object) static ECalBackendSyncStatus e_cal_backend_http_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only) { - ECalBackendHttp *cbfile = backend; - *read_only = TRUE; return GNOME_Evolution_Calendar_Success; @@ -160,58 +171,272 @@ e_cal_backend_http_get_static_capabilities (ECalBackendSync *backend, EDataCal * return GNOME_Evolution_Calendar_Success; } +static gchar * +webcal_to_http_method (const gchar *webcal_str) +{ + if (strncmp ("webcal://", webcal_str, sizeof ("webcal://") - 1)) + return NULL; + + return g_strconcat ("http://", webcal_str + sizeof ("webcal://") - 1, NULL); +} + +static gchar * +uri_to_cache_dir (const gchar *uri_str) +{ + gchar *http_uri_str, *dir_str, *escaped_dir_str; + GnomeVFSURI *uri; + + http_uri_str = webcal_to_http_method (uri_str); + if (!http_uri_str) + http_uri_str = g_strdup (uri_str); + + uri = gnome_vfs_uri_new (http_uri_str); + g_free (http_uri_str); + + if (!uri) { + g_warning ("No GnomeVFSURI."); + return NULL; + } + + dir_str = gnome_vfs_uri_to_string (uri, + GNOME_VFS_URI_HIDE_PASSWORD | + GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD); + gnome_vfs_uri_unref (uri); + + if (!dir_str || !strlen (dir_str)) { + g_warning ("No dir_str."); + return NULL; + } + + /* GnomeVFS unescapes paths. We need to escape twice. */ + escaped_dir_str = gnome_vfs_escape_slashes (dir_str); + g_free (dir_str); + dir_str = gnome_vfs_escape_slashes (escaped_dir_str); + g_free (escaped_dir_str); + escaped_dir_str = dir_str; + + if (!escaped_dir_str || !strlen (escaped_dir_str)) { + g_warning ("No escaped_dir_str."); + return NULL; + } + + return g_build_filename (g_get_home_dir (), + "/.evolution/calendar/webcal/", + escaped_dir_str, NULL); +} + +static gboolean +ensure_cache_dir (const gchar *cache_dir_str) +{ + GnomeVFSResult result; + gchar *webcal_dir; + + /* Make sure we have the webcal base dir */ + webcal_dir = g_build_filename (g_get_home_dir (), + "/.evolution/calendar/webcal", NULL); + gnome_vfs_make_directory (webcal_dir, GNOME_VFS_PERM_USER_ALL); + g_free (webcal_dir); + + /* Create cache subdirectory */ + result = gnome_vfs_make_directory (cache_dir_str, GNOME_VFS_PERM_USER_ALL); + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) + return FALSE; + + return TRUE; +} + +static void +retrieval_done (ECalBackendHttp *cbhttp) +{ + ECalBackendHttpPrivate *priv; + icalcomponent *icalcomp; + gchar *cache_dir; + gchar *temp_file, *cal_file; + gchar *temp_file_unescaped; + + priv = cbhttp->priv; + + priv->retrieval_handle = NULL; + + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return; + + temp_file = g_build_filename (cache_dir, "/calendar.ics.tmp", NULL); + cal_file = g_build_filename (cache_dir, "/calendar.ics", NULL); + + temp_file_unescaped = gnome_vfs_unescape_string (temp_file, ""); + icalcomp = e_cal_util_parse_ics_file (temp_file_unescaped); + g_free (temp_file_unescaped); + + if (!icalcomp) { + e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Bad file format.")); + goto out; + } + + if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { + e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Not a calendar.")); + icalcomponent_free (icalcomp); + goto out; + } + + /* Update calendar file and tell file backend to reload */ + + gnome_vfs_move (temp_file, cal_file, TRUE); + e_cal_backend_file_reload (E_CAL_BACKEND_FILE (priv->file_backend)); + +out: + g_free (temp_file); + g_free (cal_file); +} + +static gint +retrieval_progress_cb (GnomeVFSAsyncHandle *handle, GnomeVFSXferProgressInfo *info, gpointer data) +{ + ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (data); + ECalBackendHttpPrivate *priv; + + priv = cbhttp->priv; + + /* TODO: Handle errors */ + /* TODO: Report progress */ + + g_message ("GnomeVFS async progress: %s", + info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OK ? "ok" : + info->status == GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR ? "error" : "other"); + + g_message ("(%d) %s -> %s", info->phase, info->source_name, info->target_name); + + if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) + retrieval_done (cbhttp); + + return TRUE; +} + +static gboolean +begin_retrieval_cb (ECalBackendHttp *cbhttp) +{ + GnomeVFSURI *source_uri, *dest_uri; + gchar *source_uri_str, *cache_dir, *temp_file; + GList *source_uri_list = NULL, *dest_uri_list = NULL; + ECalBackendHttpPrivate *priv; + + priv = cbhttp->priv; + + if (priv->retrieval_handle != NULL) + return FALSE; + + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return FALSE; + + temp_file = g_build_filename (cache_dir, "/calendar.ics.tmp", NULL); + + source_uri_str = webcal_to_http_method (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!source_uri_str) + source_uri_str = g_strdup (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + + source_uri = gnome_vfs_uri_new (source_uri_str); + dest_uri = gnome_vfs_uri_new (temp_file); + + g_free (source_uri_str); + + source_uri_list = g_list_append (source_uri_list, source_uri); + dest_uri_list = g_list_append (dest_uri_list, dest_uri); + + gnome_vfs_async_xfer (&priv->retrieval_handle, + source_uri_list, dest_uri_list, + GNOME_VFS_XFER_DEFAULT, + GNOME_VFS_XFER_ERROR_MODE_QUERY, + GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, + GNOME_VFS_PRIORITY_DEFAULT, + retrieval_progress_cb, cbhttp, + NULL, NULL); + + g_list_free (source_uri_list); + g_list_free (dest_uri_list); + + gnome_vfs_uri_unref (source_uri); + gnome_vfs_uri_unref (dest_uri); + g_free (temp_file); + g_free (cache_dir); + return FALSE; +} + /* Open handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - char *str_uri; - ECalBackendSyncStatus status = GNOME_Evolution_Calendar_NoSuchCal; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) { + gchar *cache_dir; - g_message ("Open URI '%s'.", e_cal_backend_get_uri (E_CAL_BACKEND (cbfile))); + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return GNOME_Evolution_Calendar_NoSuchCal; - return status; + if (!ensure_cache_dir (cache_dir)) { + g_free (cache_dir); + return GNOME_Evolution_Calendar_NoSuchCal; + } + + priv->file_backend = g_object_new (E_TYPE_CAL_BACKEND_FILE_EVENTS, "uri", cache_dir, NULL); + e_cal_backend_set_notification_proxy (E_CAL_BACKEND (priv->file_backend), + E_CAL_BACKEND (backend)); + g_free (cache_dir); + + g_idle_add ((GSourceFunc) begin_retrieval_cb, cbhttp); + } + + e_cal_backend_sync_open (priv->file_backend, cal, FALSE); + return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus e_cal_backend_http_remove (ECalBackendSync *backend, EDataCal *cal) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - char *str_uri; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_OtherError; - return GNOME_Evolution_Calendar_OtherError; + return e_cal_backend_sync_remove (priv->file_backend, cal); } /* is_loaded handler for the file backend */ static gboolean e_cal_backend_http_is_loaded (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - return FALSE; + if (!priv->file_backend) + return FALSE; + + return e_cal_backend_is_loaded (E_CAL_BACKEND (priv->file_backend)); } /* is_remote handler for the file backend */ static CalMode e_cal_backend_http_get_mode (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; return priv->mode; } @@ -225,12 +450,12 @@ e_cal_backend_http_get_mode (ECalBackend *backend) static void e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; GNOME_Evolution_Calendar_CalMode set_mode; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; switch (mode) { case CAL_MODE_LOCAL: @@ -260,98 +485,132 @@ e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode) static ECalBackendSyncStatus e_cal_backend_http_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object) { - ECalComponent *comp; - - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_default_object (priv->file_backend, cal, object); } /* Get_object_component handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - ECalComponent *comp = NULL; - gboolean free_comp = FALSE; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_object (priv->file_backend, cal, uid, rid, object); } /* Get_timezone_object handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icaltimezone *zone; - icalcomponent *icalcomp; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) { + icaltimezone *zone; + icalcomponent *icalcomp; + + zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); + if (!zone) + return GNOME_Evolution_Calendar_ObjectNotFound; + + icalcomp = icaltimezone_get_component (zone); + if (!icalcomp) + return GNOME_Evolution_Calendar_InvalidObject; + + *object = g_strdup (icalcomponent_as_ical_string (icalcomp)); + return GNOME_Evolution_Calendar_Success; + } + + return e_cal_backend_sync_get_timezone (priv->file_backend, cal, tzid, object); } /* Add_timezone handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj) { - icalcomponent *tz_comp; - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = (ECalBackendHttp *) backend; + cbhttp = (ECalBackendHttp *) backend; - g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbfile), GNOME_Evolution_Calendar_OtherError); + g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbhttp), GNOME_Evolution_Calendar_OtherError); g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError); - priv = cbfile->priv; + priv = cbhttp->priv; - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_InvalidObject; + + return e_cal_backend_sync_add_timezone (priv->file_backend, cal, tzobj); } static ECalBackendSyncStatus e_cal_backend_http_set_default_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icaltimezone *zone; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_set_default_timezone (priv->file_backend, cal, tzid); } /* Get_objects_in_range handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; - return GNOME_Evolution_Calendar_Success; + return e_cal_backend_sync_get_object_list (priv->file_backend, cal, sexp, objects); } /* get_query handler for the file backend */ static void e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return; + + e_cal_backend_start_query (E_CAL_BACKEND (priv->file_backend), query); } /* Get_free_busy handler for the file backend */ @@ -359,20 +618,19 @@ static ECalBackendSyncStatus e_cal_backend_http_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users, time_t start, time_t end, GList **freebusy) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - gchar *address, *name; - icalcomponent *vfb; - char *calobj; - GList *l; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange); g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_get_free_busy (priv->file_backend, cal, users, start, end, freebusy); } /* Get_changes handler for the file backend */ @@ -380,62 +638,69 @@ static ECalBackendSyncStatus e_cal_backend_http_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id, GList **adds, GList **modifies, GList **deletes) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_changes (priv->file_backend, cal, change_id, adds, modifies, deletes); } /* Discard_alarm handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid) { - /* we just do nothing with the alarm */ - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_Success; + + return e_cal_backend_sync_discard_alarm (priv->file_backend, cal, uid, auid); } static ECalBackendSyncStatus e_cal_backend_http_create_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, char **uid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *icalcomp; - icalcomponent_kind kind; - ECalComponent *comp; - const char *comp_uid; - struct icaltimetype current; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_create_object (priv->file_backend, cal, calobj, uid); } static ECalBackendSyncStatus e_cal_backend_http_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, CalObjModType mod, char **old_object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *icalcomp; - icalcomponent_kind kind; - const char *comp_uid; - ECalComponent *comp; - struct icaltimetype current; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_modify_object (priv->file_backend, cal, calobj, mod, old_object); } /* Remove_object handler for the file backend */ @@ -444,73 +709,81 @@ e_cal_backend_http_remove_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - ECalComponent *comp; - char *hash_rid; - GSList *categories; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_remove_object (priv->file_backend, cal, uid, rid, mod, object); } /* Update_objects handler for the file backend. */ static ECalBackendSyncStatus e_cal_backend_http_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *toplevel_comp, *icalcomp = NULL; - icalcomponent_kind kind; - icalproperty_method method; - icalcomponent *subcomp; - GList *comps, *l; - ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject); - return status; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_InvalidObject; + + return e_cal_backend_sync_receive_objects (priv->file_backend, cal, calobj); } static ECalBackendSyncStatus e_cal_backend_http_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { - /* FIXME Put in a util routine to send stuff via email */ - - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_Success; + + return e_cal_backend_sync_send_objects (priv->file_backend, cal, calobj); } static icaltimezone * e_cal_backend_http_internal_get_default_timezone (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + if (!priv->file_backend) + return NULL; - return priv->default_zone; + return e_cal_backend_internal_get_default_timezone (E_CAL_BACKEND (priv->file_backend)); } static icaltimezone * e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; icaltimezone *zone; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - if (!strcmp (tzid, "UTC")) + if (priv->file_backend) + zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (priv->file_backend), tzid); + else if (!strcmp (tzid, "UTC")) zone = icaltimezone_get_utc_timezone (); else { zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); @@ -521,20 +794,14 @@ e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid /* Object initialization function for the file backend */ static void -e_cal_backend_http_init (ECalBackendHttp *cbfile, ECalBackendHttpClass *class) +e_cal_backend_http_init (ECalBackendHttp *cbhttp, ECalBackendHttpClass *class) { ECalBackendHttpPrivate *priv; - g_message ("Webcal backend init."); - priv = g_new0 (ECalBackendHttpPrivate, 1); - cbfile->priv = priv; + cbhttp->priv = priv; priv->uri = NULL; - -#if 0 - priv->config_listener = e_config_listener_new (); -#endif } /* Class initialization function for the file backend */ @@ -600,8 +867,6 @@ e_cal_backend_http_get_type (void) { static GType e_cal_backend_http_type = 0; - g_message (G_STRLOC); - if (!e_cal_backend_http_type) { static GTypeInfo info = { sizeof (ECalBackendHttpClass), @@ -614,7 +879,7 @@ e_cal_backend_http_get_type (void) (GInstanceInitFunc) e_cal_backend_http_init }; e_cal_backend_http_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC, - "ECalBackendHttp", &info, 0); + "ECalBackendHttp", &info, 0); } return e_cal_backend_http_type; diff --git a/calendar/libedata-cal/e-cal-backend-sync.c b/calendar/libedata-cal/e-cal-backend-sync.c index 7ec298f..1e31ede 100644 --- a/calendar/libedata-cal/e-cal-backend-sync.c +++ b/calendar/libedata-cal/e-cal-backend-sync.c @@ -13,15 +13,12 @@ #include "e-cal-backend-sync.h" struct _ECalBackendSyncPrivate { - int mumble; + int mumble; + GMutex *sync_mutex; }; static GObjectClass *parent_class; -G_LOCK_DEFINE_STATIC (cal_sync_mutex); -#define SYNC_LOCK() G_LOCK (cal_sync_mutex) -#define SYNC_UNLOCK() G_UNLOCK (cal_sync_mutex) - ECalBackendSyncStatus e_cal_backend_sync_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only) { @@ -80,17 +77,18 @@ e_cal_backend_sync_get_static_capabilities (ECalBackendSync *backend, EDataCal ECalBackendSyncStatus e_cal_backend_sync_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists) { + ECalBackendSyncPrivate *priv; ECalBackendSyncStatus status; g_return_val_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), GNOME_Evolution_Calendar_OtherError); - g_assert (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->open_sync); + priv = backend->priv; + + g_mutex_lock (priv->sync_mutex); - SYNC_LOCK (); - status = (* E_CAL_BACKEND_SYNC_GET_CLASS (backend)->open_sync) (backend, cal, only_if_exists); - SYNC_UNLOCK (); + g_mutex_unlock (priv->sync_mutex); return status; } @@ -523,7 +521,8 @@ e_cal_backend_sync_init (ECalBackendSync *backend) { ECalBackendSyncPrivate *priv; - priv = g_new0 (ECalBackendSyncPrivate, 1); + priv = g_new0 (ECalBackendSyncPrivate, 1); + priv->sync_mutex = g_mutex_new (); backend->priv = priv; } @@ -536,6 +535,7 @@ e_cal_backend_sync_dispose (GObject *object) backend = E_CAL_BACKEND_SYNC (object); if (backend->priv) { + g_mutex_free (backend->priv->sync_mutex); g_free (backend->priv); backend->priv = NULL; diff --git a/calendar/libedata-cal/e-cal-backend.c b/calendar/libedata-cal/e-cal-backend.c index 0b89fc9..6886aa6 100644 --- a/calendar/libedata-cal/e-cal-backend.c +++ b/calendar/libedata-cal/e-cal-backend.c @@ -62,6 +62,9 @@ struct _ECalBackendPrivate { GHashTable *categories; GHashTable *changed_categories; guint category_idle_id; + + /* ECalBackend to pass notifications on to */ + ECalBackend *notification_proxy; }; /* Property IDs */ @@ -895,6 +898,18 @@ e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid) return (* CLASS (backend)->internal_get_timezone) (backend, tzid); } +void +e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy) +{ + ECalBackendPrivate *priv; + + g_return_if_fail (E_IS_CAL_BACKEND (backend)); + + priv = backend->priv; + + priv->notification_proxy = proxy; +} + /** * e_cal_backend_notify_object_created: * @backend: A calendar backend. @@ -909,10 +924,18 @@ e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid) void e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj) { + ECalBackendPrivate *priv; EList *queries; EIterator *iter; EDataCalView *query; + priv = backend->priv; + + if (priv->notification_proxy) { + e_cal_backend_notify_object_created (priv->notification_proxy, calobj); + return; + } + queries = e_cal_backend_get_queries (backend); iter = e_list_get_iterator (queries); @@ -946,11 +969,19 @@ void e_cal_backend_notify_object_modified (ECalBackend *backend, const char *old_object, const char *object) { + ECalBackendPrivate *priv; EList *queries; EIterator *iter; EDataCalView *query; gboolean old_match, new_match; + priv = backend->priv; + + if (priv->notification_proxy) { + e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object); + return; + } + queries = e_cal_backend_get_queries (backend); iter = e_list_get_iterator (queries); @@ -997,10 +1028,18 @@ void e_cal_backend_notify_object_removed (ECalBackend *backend, const char *uid, const char *old_object) { + ECalBackendPrivate *priv; EList *queries; EIterator *iter; EDataCalView *query; + priv = backend->priv; + + if (priv->notification_proxy) { + e_cal_backend_notify_object_removed (priv->notification_proxy, uid, old_object); + return; + } + queries = e_cal_backend_get_queries (backend); iter = e_list_get_iterator (queries); @@ -1035,6 +1074,11 @@ e_cal_backend_notify_mode (ECalBackend *backend, ECalBackendPrivate *priv = backend->priv; GList *l; + if (priv->notification_proxy) { + e_cal_backend_notify_mode (priv->notification_proxy, status, mode); + return; + } + for (l = priv->clients; l; l = l->next) e_data_cal_notify_mode (l->data, status, mode); } @@ -1052,6 +1096,11 @@ e_cal_backend_notify_error (ECalBackend *backend, const char *message) ECalBackendPrivate *priv = backend->priv; GList *l; + if (priv->notification_proxy) { + e_cal_backend_notify_error (priv->notification_proxy, message); + return; + } + for (l = priv->clients; l; l = l->next) e_data_cal_notify_error (l->data, message); } diff --git a/calendar/libedata-cal/e-cal-backend.h b/calendar/libedata-cal/e-cal-backend.h index 00676c7..0383813 100644 --- a/calendar/libedata-cal/e-cal-backend.h +++ b/calendar/libedata-cal/e-cal-backend.h @@ -160,6 +160,7 @@ icaltimezone* e_cal_backend_internal_get_timezone (ECalBackend *backend, const c void e_cal_backend_last_client_gone (ECalBackend *backend); +void e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy); void e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj); void e_cal_backend_notify_object_modified (ECalBackend *backend, const char *old_object, const char *object); void e_cal_backend_notify_object_removed (ECalBackend *backend, const char *uid, const char *old_object); diff --git a/calendar/libedatacal/e-cal-backend-file.c b/calendar/libedatacal/e-cal-backend-file.c index 8529a50..f12944d 100644 --- a/calendar/libedatacal/e-cal-backend-file.c +++ b/calendar/libedatacal/e-cal-backend-file.c @@ -180,6 +180,34 @@ save (ECalBackendFile *cbfile) return; } +static void +free_calendar_components (GHashTable *comp_uid_hash, icalcomponent *top_icomp) +{ + if (comp_uid_hash) { + g_hash_table_foreach (comp_uid_hash, (GHFunc) free_object, NULL); + g_hash_table_destroy (comp_uid_hash); + } + + if (top_icomp) { + icalcomponent_free (top_icomp); + } +} + +static void +free_calendar_data (ECalBackendFile *cbfile) +{ + ECalBackendFilePrivate *priv; + + priv = cbfile->priv; + + free_calendar_components (priv->comp_uid_hash, priv->icalcomp); + priv->comp_uid_hash = NULL; + priv->icalcomp = NULL; + + g_list_free (priv->comp); + priv->comp = NULL; +} + /* Dispose handler for the file backend */ static void e_cal_backend_file_dispose (GObject *object) @@ -192,19 +220,7 @@ e_cal_backend_file_dispose (GObject *object) /* Save if necessary */ - if (priv->comp_uid_hash) { - g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) free_object, NULL); - g_hash_table_destroy (priv->comp_uid_hash); - priv->comp_uid_hash = NULL; - } - - g_list_free (priv->comp); - priv->comp = NULL; - - if (priv->icalcomp) { - icalcomponent_free (priv->icalcomp); - priv->icalcomp = NULL; - } + free_calendar_data (cbfile); if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); @@ -558,6 +574,51 @@ scan_vcalendar (ECalBackendFile *cbfile) } } +static char * +get_uri_string_for_gnome_vfs (ECalBackend *backend) +{ + ECalBackendFile *cbfile; + ECalBackendFilePrivate *priv; + const char *master_uri; + char *full_uri, *str_uri; + GnomeVFSURI *uri; + + cbfile = E_CAL_BACKEND_FILE (backend); + priv = cbfile->priv; + + master_uri = e_cal_backend_get_uri (backend); + + /* FIXME Check the error conditions a little more elegantly here */ + if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) { + g_warning (G_STRLOC ": Existing file name %s", master_uri); + + return NULL; + } + + full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name); + uri = gnome_vfs_uri_new (full_uri); + g_free (full_uri); + + if (!uri) + return NULL; + + str_uri = gnome_vfs_uri_to_string (uri, + (GNOME_VFS_URI_HIDE_USER_NAME + | GNOME_VFS_URI_HIDE_PASSWORD + | GNOME_VFS_URI_HIDE_HOST_NAME + | GNOME_VFS_URI_HIDE_HOST_PORT + | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); + gnome_vfs_uri_unref (uri); + + if (!str_uri || !strlen (str_uri)) { + g_free (str_uri); + + return NULL; + } + + return str_uri; +} + /* Parses an open iCalendar file and loads it into the backend */ static ECalBackendSyncStatus open_cal (ECalBackendFile *cbfile, const char *uristr) @@ -586,11 +647,152 @@ open_cal (ECalBackendFile *cbfile, const char *uristr) priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); scan_vcalendar (cbfile); - priv->uri = g_strdup (uristr); + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); return GNOME_Evolution_Calendar_Success; } +typedef struct +{ + ECalBackend *backend; + GHashTable *old_uid_hash; + GHashTable *new_uid_hash; +} +BackendDeltaContext; + +static void +notify_removals_cb (gpointer key, gpointer value, gpointer data) +{ + BackendDeltaContext *context = data; + const gchar *uid = key; + ECalBackendFileObject *old_obj_data = value; + + if (!g_hash_table_lookup (context->new_uid_hash, uid)) { + icalcomponent *old_icomp; + gchar *old_obj_str; + + /* Object was removed */ + + old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object); + if (!old_icomp) + return; + + old_obj_str = icalcomponent_as_ical_string (old_icomp); + if (!old_obj_str) + return; + + e_cal_backend_notify_object_removed (context->backend, uid, old_obj_str); + } +} + +static void +notify_adds_modifies_cb (gpointer key, gpointer value, gpointer data) +{ + BackendDeltaContext *context = data; + const gchar *uid = key; + ECalBackendFileObject *new_obj_data = value; + ECalBackendFileObject *old_obj_data; + icalcomponent *old_icomp, *new_icomp; + gchar *old_obj_str, *new_obj_str; + + old_obj_data = g_hash_table_lookup (context->old_uid_hash, uid); + + if (!old_obj_data) { + /* Object was added */ + + new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object); + if (!new_icomp) + return; + + new_obj_str = icalcomponent_as_ical_string (new_icomp); + if (!new_obj_str) + return; + + e_cal_backend_notify_object_created (context->backend, new_obj_str); + } else { + old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object); + new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object); + if (!old_icomp || !new_icomp) + return; + + old_obj_str = icalcomponent_as_ical_string (old_icomp); + new_obj_str = icalcomponent_as_ical_string (new_icomp); + if (!old_obj_str || !new_obj_str) + return; + + if (strcmp (old_obj_str, new_obj_str)) { + /* Object was modified */ + + e_cal_backend_notify_object_modified (context->backend, old_obj_str, new_obj_str); + } + } +} + +static void +notify_changes (ECalBackendFile *cbfile, GHashTable *old_uid_hash, GHashTable *new_uid_hash) +{ + BackendDeltaContext context; + + context.backend = E_CAL_BACKEND (cbfile); + context.old_uid_hash = old_uid_hash; + context.new_uid_hash = new_uid_hash; + + g_hash_table_foreach (old_uid_hash, (GHFunc) notify_removals_cb, &context); + g_hash_table_foreach (new_uid_hash, (GHFunc) notify_adds_modifies_cb, &context); +} + +static ECalBackendSyncStatus +reload_cal (ECalBackendFile *cbfile, const char *uristr) +{ + ECalBackendFilePrivate *priv; + icalcomponent *icalcomp, *icalcomp_old; + GHashTable *comp_uid_hash_old; + + priv = cbfile->priv; + + icalcomp = e_cal_util_parse_ics_file (uristr); + if (!icalcomp) + return GNOME_Evolution_Calendar_OtherError; + + /* FIXME: should we try to demangle XROOT components and + * individual components as well? + */ + + if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { + icalcomponent_free (icalcomp); + + return GNOME_Evolution_Calendar_OtherError; + } + + /* Keep old data for comparison - free later */ + + icalcomp_old = priv->icalcomp; + priv->icalcomp = NULL; + + comp_uid_hash_old = priv->comp_uid_hash; + priv->comp_uid_hash = NULL; + + /* Load new calendar */ + + free_calendar_data (cbfile); + + priv->icalcomp = icalcomp; + + priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); + scan_vcalendar (cbfile); + + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); + + /* Compare old and new versions of calendar */ + + notify_changes (cbfile, comp_uid_hash_old, priv->comp_uid_hash); + + /* Free old data */ + + free_calendar_components (comp_uid_hash_old, icalcomp_old); + return GNOME_Evolution_Calendar_Success; +} + static ECalBackendSyncStatus create_cal (ECalBackendFile *cbfile, const char *uristr) { @@ -604,7 +806,7 @@ create_cal (ECalBackendFile *cbfile, const char *uristr) /* Create our internal data */ priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal); - priv->uri = g_strdup (uristr); + priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile)); save (cbfile); @@ -614,47 +816,13 @@ create_cal (ECalBackendFile *cbfile, const char *uristr) static char * get_uri_string (ECalBackend *backend) { - ECalBackendFile *cbfile; - ECalBackendFilePrivate *priv; - const char *master_uri; - char *full_uri, *str_uri; - GnomeVFSURI *uri; - - cbfile = E_CAL_BACKEND_FILE (backend); - priv = cbfile->priv; - - master_uri = e_cal_backend_get_uri (backend); - g_message (G_STRLOC ": Trying to open %s", master_uri); - - /* FIXME Check the error conditions a little more elegantly here */ - if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) { - g_warning (G_STRLOC ": Existing file name %s", master_uri); - - return NULL; - } - - full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name); - uri = gnome_vfs_uri_new (full_uri); - g_free (full_uri); - - if (!uri) - return NULL; - - str_uri = gnome_vfs_uri_to_string (uri, - (GNOME_VFS_URI_HIDE_USER_NAME - | GNOME_VFS_URI_HIDE_PASSWORD - | GNOME_VFS_URI_HIDE_HOST_NAME - | GNOME_VFS_URI_HIDE_HOST_PORT - | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD)); - gnome_vfs_uri_unref (uri); - - if (!str_uri || !strlen (str_uri)) { - g_free (str_uri); + gchar *str_uri, *full_uri; - return NULL; - } + str_uri = get_uri_string_for_gnome_vfs (backend); + full_uri = gnome_vfs_unescape_string (str_uri, ""); + g_free (str_uri); - return str_uri; + return full_uri; } /* Open handler for the file backend */ @@ -1239,13 +1407,16 @@ e_cal_backend_file_compute_changes (ECalBackendFile *cbfile, const char *change_ EXmlHash *ehash; ECalBackendFileComputeChangesData be_data; GList *i; + gchar *unescaped_uri; priv = cbfile->priv; /* FIXME Will this always work? */ - filename = g_strdup_printf ("%s/%s.db", priv->uri, change_id); + unescaped_uri = gnome_vfs_unescape_string (priv->uri, ""); + filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id); ehash = e_xmlhash_new (filename); g_free (filename); + g_free (unescaped_uri); /* Calculate adds and modifies */ for (i = priv->comp; i != NULL; i = i->next) { @@ -1979,3 +2150,28 @@ e_cal_backend_file_get_file_name (ECalBackendFile *cbfile) return priv->file_name; } + +ECalBackendSyncStatus +e_cal_backend_file_reload (ECalBackendFile *cbfile) +{ + ECalBackendFilePrivate *priv; + char *str_uri; + ECalBackendSyncStatus status; + + priv = cbfile->priv; + + str_uri = get_uri_string (E_CAL_BACKEND (cbfile)); + if (!str_uri) + return GNOME_Evolution_Calendar_OtherError; + + if (access (str_uri, R_OK) == 0) { + status = reload_cal (cbfile, str_uri); + if (access (str_uri, W_OK) != 0) + priv->read_only = TRUE; + } else { + status = GNOME_Evolution_Calendar_NoSuchCal; + } + + g_free (str_uri); + return status; +} diff --git a/calendar/libedatacal/e-cal-backend-file.h b/calendar/libedatacal/e-cal-backend-file.h index f1da45d..88d6ea4 100644 --- a/calendar/libedatacal/e-cal-backend-file.h +++ b/calendar/libedatacal/e-cal-backend-file.h @@ -52,11 +52,13 @@ struct _ECalBackendFileClass { ECalBackendSyncClass parent_class; }; -GType e_cal_backend_file_get_type (void); +GType e_cal_backend_file_get_type (void); -void e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, - const char *file_name); -const char *e_cal_backend_file_get_file_name (ECalBackendFile *cbfile); +void e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, + const char *file_name); +const char *e_cal_backend_file_get_file_name (ECalBackendFile *cbfile); + +ECalBackendSyncStatus e_cal_backend_file_reload (ECalBackendFile *cbfile); diff --git a/calendar/libedatacal/e-cal-backend-http.c b/calendar/libedatacal/e-cal-backend-http.c index 4a40538..099823d 100644 --- a/calendar/libedatacal/e-cal-backend-http.c +++ b/calendar/libedatacal/e-cal-backend-http.c @@ -30,8 +30,8 @@ #include #include #include +#include "e-cal-backend-file-events.h" #include "e-cal-backend-http.h" -#include "e-cal-backend-file.h" #include "e-cal-backend-util.h" #include "e-cal-backend-sexp.h" @@ -46,7 +46,7 @@ struct _ECalBackendHttpPrivate { CalMode mode; /* Cached-file backend */ - ECalBackendFile file_backend; + ECalBackendSync *file_backend; /* The calendar's default timezone, used for resolving DATE and floating DATE-TIME values. */ @@ -54,6 +54,9 @@ struct _ECalBackendHttpPrivate { /* The list of live queries */ GList *queries; + + /* GnomeVFS handle for remote retrieval */ + GnomeVFSAsyncHandle *retrieval_handle; }; @@ -69,11 +72,11 @@ static ECalBackendSyncClass *parent_class; static void e_cal_backend_http_dispose (GObject *object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (object); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (object); + priv = cbhttp->priv; if (G_OBJECT_CLASS (parent_class)->dispose) (* G_OBJECT_CLASS (parent_class)->dispose) (object); @@ -83,24 +86,34 @@ e_cal_backend_http_dispose (GObject *object) static void e_cal_backend_http_finalize (GObject *object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; g_return_if_fail (object != NULL); g_return_if_fail (E_IS_CAL_BACKEND_HTTP (object)); - cbfile = E_CAL_BACKEND_HTTP (object); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (object); + priv = cbhttp->priv; /* Clean up */ + if (priv->retrieval_handle) { + gnome_vfs_async_cancel (priv->retrieval_handle); + priv->retrieval_handle = NULL; + } + + if (priv->file_backend) { + g_object_unref (priv->file_backend); + e_cal_backend_set_notification_proxy (E_CAL_BACKEND (priv->file_backend), NULL); + } + if (priv->uri) { g_free (priv->uri); priv->uri = NULL; } g_free (priv); - cbfile->priv = NULL; + cbhttp->priv = NULL; if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (object); @@ -114,8 +127,6 @@ e_cal_backend_http_finalize (GObject *object) static ECalBackendSyncStatus e_cal_backend_http_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only) { - ECalBackendHttp *cbfile = backend; - *read_only = TRUE; return GNOME_Evolution_Calendar_Success; @@ -160,58 +171,272 @@ e_cal_backend_http_get_static_capabilities (ECalBackendSync *backend, EDataCal * return GNOME_Evolution_Calendar_Success; } +static gchar * +webcal_to_http_method (const gchar *webcal_str) +{ + if (strncmp ("webcal://", webcal_str, sizeof ("webcal://") - 1)) + return NULL; + + return g_strconcat ("http://", webcal_str + sizeof ("webcal://") - 1, NULL); +} + +static gchar * +uri_to_cache_dir (const gchar *uri_str) +{ + gchar *http_uri_str, *dir_str, *escaped_dir_str; + GnomeVFSURI *uri; + + http_uri_str = webcal_to_http_method (uri_str); + if (!http_uri_str) + http_uri_str = g_strdup (uri_str); + + uri = gnome_vfs_uri_new (http_uri_str); + g_free (http_uri_str); + + if (!uri) { + g_warning ("No GnomeVFSURI."); + return NULL; + } + + dir_str = gnome_vfs_uri_to_string (uri, + GNOME_VFS_URI_HIDE_PASSWORD | + GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD); + gnome_vfs_uri_unref (uri); + + if (!dir_str || !strlen (dir_str)) { + g_warning ("No dir_str."); + return NULL; + } + + /* GnomeVFS unescapes paths. We need to escape twice. */ + escaped_dir_str = gnome_vfs_escape_slashes (dir_str); + g_free (dir_str); + dir_str = gnome_vfs_escape_slashes (escaped_dir_str); + g_free (escaped_dir_str); + escaped_dir_str = dir_str; + + if (!escaped_dir_str || !strlen (escaped_dir_str)) { + g_warning ("No escaped_dir_str."); + return NULL; + } + + return g_build_filename (g_get_home_dir (), + "/.evolution/calendar/webcal/", + escaped_dir_str, NULL); +} + +static gboolean +ensure_cache_dir (const gchar *cache_dir_str) +{ + GnomeVFSResult result; + gchar *webcal_dir; + + /* Make sure we have the webcal base dir */ + webcal_dir = g_build_filename (g_get_home_dir (), + "/.evolution/calendar/webcal", NULL); + gnome_vfs_make_directory (webcal_dir, GNOME_VFS_PERM_USER_ALL); + g_free (webcal_dir); + + /* Create cache subdirectory */ + result = gnome_vfs_make_directory (cache_dir_str, GNOME_VFS_PERM_USER_ALL); + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) + return FALSE; + + return TRUE; +} + +static void +retrieval_done (ECalBackendHttp *cbhttp) +{ + ECalBackendHttpPrivate *priv; + icalcomponent *icalcomp; + gchar *cache_dir; + gchar *temp_file, *cal_file; + gchar *temp_file_unescaped; + + priv = cbhttp->priv; + + priv->retrieval_handle = NULL; + + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return; + + temp_file = g_build_filename (cache_dir, "/calendar.ics.tmp", NULL); + cal_file = g_build_filename (cache_dir, "/calendar.ics", NULL); + + temp_file_unescaped = gnome_vfs_unescape_string (temp_file, ""); + icalcomp = e_cal_util_parse_ics_file (temp_file_unescaped); + g_free (temp_file_unescaped); + + if (!icalcomp) { + e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Bad file format.")); + goto out; + } + + if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { + e_cal_backend_notify_error (E_CAL_BACKEND (cbhttp), _("Not a calendar.")); + icalcomponent_free (icalcomp); + goto out; + } + + /* Update calendar file and tell file backend to reload */ + + gnome_vfs_move (temp_file, cal_file, TRUE); + e_cal_backend_file_reload (E_CAL_BACKEND_FILE (priv->file_backend)); + +out: + g_free (temp_file); + g_free (cal_file); +} + +static gint +retrieval_progress_cb (GnomeVFSAsyncHandle *handle, GnomeVFSXferProgressInfo *info, gpointer data) +{ + ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (data); + ECalBackendHttpPrivate *priv; + + priv = cbhttp->priv; + + /* TODO: Handle errors */ + /* TODO: Report progress */ + + g_message ("GnomeVFS async progress: %s", + info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OK ? "ok" : + info->status == GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR ? "error" : "other"); + + g_message ("(%d) %s -> %s", info->phase, info->source_name, info->target_name); + + if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) + retrieval_done (cbhttp); + + return TRUE; +} + +static gboolean +begin_retrieval_cb (ECalBackendHttp *cbhttp) +{ + GnomeVFSURI *source_uri, *dest_uri; + gchar *source_uri_str, *cache_dir, *temp_file; + GList *source_uri_list = NULL, *dest_uri_list = NULL; + ECalBackendHttpPrivate *priv; + + priv = cbhttp->priv; + + if (priv->retrieval_handle != NULL) + return FALSE; + + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return FALSE; + + temp_file = g_build_filename (cache_dir, "/calendar.ics.tmp", NULL); + + source_uri_str = webcal_to_http_method (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!source_uri_str) + source_uri_str = g_strdup (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + + source_uri = gnome_vfs_uri_new (source_uri_str); + dest_uri = gnome_vfs_uri_new (temp_file); + + g_free (source_uri_str); + + source_uri_list = g_list_append (source_uri_list, source_uri); + dest_uri_list = g_list_append (dest_uri_list, dest_uri); + + gnome_vfs_async_xfer (&priv->retrieval_handle, + source_uri_list, dest_uri_list, + GNOME_VFS_XFER_DEFAULT, + GNOME_VFS_XFER_ERROR_MODE_QUERY, + GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, + GNOME_VFS_PRIORITY_DEFAULT, + retrieval_progress_cb, cbhttp, + NULL, NULL); + + g_list_free (source_uri_list); + g_list_free (dest_uri_list); + + gnome_vfs_uri_unref (source_uri); + gnome_vfs_uri_unref (dest_uri); + g_free (temp_file); + g_free (cache_dir); + return FALSE; +} + /* Open handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - char *str_uri; - ECalBackendSyncStatus status = GNOME_Evolution_Calendar_NoSuchCal; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) { + gchar *cache_dir; - g_message ("Open URI '%s'.", e_cal_backend_get_uri (E_CAL_BACKEND (cbfile))); + cache_dir = uri_to_cache_dir (e_cal_backend_get_uri (E_CAL_BACKEND (cbhttp))); + if (!cache_dir) + return GNOME_Evolution_Calendar_NoSuchCal; - return status; + if (!ensure_cache_dir (cache_dir)) { + g_free (cache_dir); + return GNOME_Evolution_Calendar_NoSuchCal; + } + + priv->file_backend = g_object_new (E_TYPE_CAL_BACKEND_FILE_EVENTS, "uri", cache_dir, NULL); + e_cal_backend_set_notification_proxy (E_CAL_BACKEND (priv->file_backend), + E_CAL_BACKEND (backend)); + g_free (cache_dir); + + g_idle_add ((GSourceFunc) begin_retrieval_cb, cbhttp); + } + + e_cal_backend_sync_open (priv->file_backend, cal, FALSE); + return GNOME_Evolution_Calendar_Success; } static ECalBackendSyncStatus e_cal_backend_http_remove (ECalBackendSync *backend, EDataCal *cal) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - char *str_uri; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_OtherError; - return GNOME_Evolution_Calendar_OtherError; + return e_cal_backend_sync_remove (priv->file_backend, cal); } /* is_loaded handler for the file backend */ static gboolean e_cal_backend_http_is_loaded (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - return FALSE; + if (!priv->file_backend) + return FALSE; + + return e_cal_backend_is_loaded (E_CAL_BACKEND (priv->file_backend)); } /* is_remote handler for the file backend */ static CalMode e_cal_backend_http_get_mode (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; return priv->mode; } @@ -225,12 +450,12 @@ e_cal_backend_http_get_mode (ECalBackend *backend) static void e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; GNOME_Evolution_Calendar_CalMode set_mode; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; switch (mode) { case CAL_MODE_LOCAL: @@ -260,98 +485,132 @@ e_cal_backend_http_set_mode (ECalBackend *backend, CalMode mode) static ECalBackendSyncStatus e_cal_backend_http_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object) { - ECalComponent *comp; - - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_default_object (priv->file_backend, cal, object); } /* Get_object_component handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - ECalComponent *comp = NULL; - gboolean free_comp = FALSE; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_object (priv->file_backend, cal, uid, rid, object); } /* Get_timezone_object handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icaltimezone *zone; - icalcomponent *icalcomp; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) { + icaltimezone *zone; + icalcomponent *icalcomp; + + zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); + if (!zone) + return GNOME_Evolution_Calendar_ObjectNotFound; + + icalcomp = icaltimezone_get_component (zone); + if (!icalcomp) + return GNOME_Evolution_Calendar_InvalidObject; + + *object = g_strdup (icalcomponent_as_ical_string (icalcomp)); + return GNOME_Evolution_Calendar_Success; + } + + return e_cal_backend_sync_get_timezone (priv->file_backend, cal, tzid, object); } /* Add_timezone handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj) { - icalcomponent *tz_comp; - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = (ECalBackendHttp *) backend; + cbhttp = (ECalBackendHttp *) backend; - g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbfile), GNOME_Evolution_Calendar_OtherError); + g_return_val_if_fail (E_IS_CAL_BACKEND_HTTP (cbhttp), GNOME_Evolution_Calendar_OtherError); g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError); - priv = cbfile->priv; + priv = cbhttp->priv; - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_InvalidObject; + + return e_cal_backend_sync_add_timezone (priv->file_backend, cal, tzobj); } static ECalBackendSyncStatus e_cal_backend_http_set_default_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icaltimezone *zone; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_set_default_timezone (priv->file_backend, cal, tzid); } /* Get_objects_in_range handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; - return GNOME_Evolution_Calendar_Success; + return e_cal_backend_sync_get_object_list (priv->file_backend, cal, sexp, objects); } /* get_query handler for the file backend */ static void e_cal_backend_http_start_query (ECalBackend *backend, EDataCalView *query) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return; + + e_cal_backend_start_query (E_CAL_BACKEND (priv->file_backend), query); } /* Get_free_busy handler for the file backend */ @@ -359,20 +618,19 @@ static ECalBackendSyncStatus e_cal_backend_http_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users, time_t start, time_t end, GList **freebusy) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - gchar *address, *name; - icalcomponent *vfb; - char *calobj; - GList *l; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange); g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_get_free_busy (priv->file_backend, cal, users, start, end, freebusy); } /* Get_changes handler for the file backend */ @@ -380,62 +638,69 @@ static ECalBackendSyncStatus e_cal_backend_http_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id, GList **adds, GList **modifies, GList **deletes) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_ObjectNotFound; + + return e_cal_backend_sync_get_changes (priv->file_backend, cal, change_id, adds, modifies, deletes); } /* Discard_alarm handler for the file backend */ static ECalBackendSyncStatus e_cal_backend_http_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid) { - /* we just do nothing with the alarm */ - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_Success; + + return e_cal_backend_sync_discard_alarm (priv->file_backend, cal, uid, auid); } static ECalBackendSyncStatus e_cal_backend_http_create_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, char **uid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *icalcomp; - icalcomponent_kind kind; - ECalComponent *comp; - const char *comp_uid; - struct icaltimetype current; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_create_object (priv->file_backend, cal, calobj, uid); } static ECalBackendSyncStatus e_cal_backend_http_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, CalObjModType mod, char **old_object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *icalcomp; - icalcomponent_kind kind; - const char *comp_uid; - ECalComponent *comp; - struct icaltimetype current; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_modify_object (priv->file_backend, cal, calobj, mod, old_object); } /* Remove_object handler for the file backend */ @@ -444,73 +709,81 @@ e_cal_backend_http_remove_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod, char **object) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - ECalComponent *comp; - char *hash_rid; - GSList *categories; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound); - return GNOME_Evolution_Calendar_Success; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_NoSuchCal; + + return e_cal_backend_sync_remove_object (priv->file_backend, cal, uid, rid, mod, object); } /* Update_objects handler for the file backend. */ static ECalBackendSyncStatus e_cal_backend_http_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - icalcomponent *toplevel_comp, *icalcomp = NULL; - icalcomponent_kind kind; - icalproperty_method method; - icalcomponent *subcomp; - GList *comps, *l; - ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject); - return status; + if (!priv->file_backend) + return GNOME_Evolution_Calendar_InvalidObject; + + return e_cal_backend_sync_receive_objects (priv->file_backend, cal, calobj); } static ECalBackendSyncStatus e_cal_backend_http_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj) { - /* FIXME Put in a util routine to send stuff via email */ - - return GNOME_Evolution_Calendar_Success; + ECalBackendHttp *cbhttp; + ECalBackendHttpPrivate *priv; + + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + + if (!priv->file_backend) + return GNOME_Evolution_Calendar_Success; + + return e_cal_backend_sync_send_objects (priv->file_backend, cal, calobj); } static icaltimezone * e_cal_backend_http_internal_get_default_timezone (ECalBackend *backend) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; + if (!priv->file_backend) + return NULL; - return priv->default_zone; + return e_cal_backend_internal_get_default_timezone (E_CAL_BACKEND (priv->file_backend)); } static icaltimezone * e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid) { - ECalBackendHttp *cbfile; + ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; icaltimezone *zone; - cbfile = E_CAL_BACKEND_HTTP (backend); - priv = cbfile->priv; + cbhttp = E_CAL_BACKEND_HTTP (backend); + priv = cbhttp->priv; - if (!strcmp (tzid, "UTC")) + if (priv->file_backend) + zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (priv->file_backend), tzid); + else if (!strcmp (tzid, "UTC")) zone = icaltimezone_get_utc_timezone (); else { zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); @@ -521,20 +794,14 @@ e_cal_backend_http_internal_get_timezone (ECalBackend *backend, const char *tzid /* Object initialization function for the file backend */ static void -e_cal_backend_http_init (ECalBackendHttp *cbfile, ECalBackendHttpClass *class) +e_cal_backend_http_init (ECalBackendHttp *cbhttp, ECalBackendHttpClass *class) { ECalBackendHttpPrivate *priv; - g_message ("Webcal backend init."); - priv = g_new0 (ECalBackendHttpPrivate, 1); - cbfile->priv = priv; + cbhttp->priv = priv; priv->uri = NULL; - -#if 0 - priv->config_listener = e_config_listener_new (); -#endif } /* Class initialization function for the file backend */ @@ -600,8 +867,6 @@ e_cal_backend_http_get_type (void) { static GType e_cal_backend_http_type = 0; - g_message (G_STRLOC); - if (!e_cal_backend_http_type) { static GTypeInfo info = { sizeof (ECalBackendHttpClass), @@ -614,7 +879,7 @@ e_cal_backend_http_get_type (void) (GInstanceInitFunc) e_cal_backend_http_init }; e_cal_backend_http_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC, - "ECalBackendHttp", &info, 0); + "ECalBackendHttp", &info, 0); } return e_cal_backend_http_type; diff --git a/calendar/libedatacal/e-cal-backend-sync.c b/calendar/libedatacal/e-cal-backend-sync.c index 7ec298f..1e31ede 100644 --- a/calendar/libedatacal/e-cal-backend-sync.c +++ b/calendar/libedatacal/e-cal-backend-sync.c @@ -13,15 +13,12 @@ #include "e-cal-backend-sync.h" struct _ECalBackendSyncPrivate { - int mumble; + int mumble; + GMutex *sync_mutex; }; static GObjectClass *parent_class; -G_LOCK_DEFINE_STATIC (cal_sync_mutex); -#define SYNC_LOCK() G_LOCK (cal_sync_mutex) -#define SYNC_UNLOCK() G_UNLOCK (cal_sync_mutex) - ECalBackendSyncStatus e_cal_backend_sync_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only) { @@ -80,17 +77,18 @@ e_cal_backend_sync_get_static_capabilities (ECalBackendSync *backend, EDataCal ECalBackendSyncStatus e_cal_backend_sync_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists) { + ECalBackendSyncPrivate *priv; ECalBackendSyncStatus status; g_return_val_if_fail (backend && E_IS_CAL_BACKEND_SYNC (backend), GNOME_Evolution_Calendar_OtherError); - g_assert (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->open_sync); + priv = backend->priv; + + g_mutex_lock (priv->sync_mutex); - SYNC_LOCK (); - status = (* E_CAL_BACKEND_SYNC_GET_CLASS (backend)->open_sync) (backend, cal, only_if_exists); - SYNC_UNLOCK (); + g_mutex_unlock (priv->sync_mutex); return status; } @@ -523,7 +521,8 @@ e_cal_backend_sync_init (ECalBackendSync *backend) { ECalBackendSyncPrivate *priv; - priv = g_new0 (ECalBackendSyncPrivate, 1); + priv = g_new0 (ECalBackendSyncPrivate, 1); + priv->sync_mutex = g_mutex_new (); backend->priv = priv; } @@ -536,6 +535,7 @@ e_cal_backend_sync_dispose (GObject *object) backend = E_CAL_BACKEND_SYNC (object); if (backend->priv) { + g_mutex_free (backend->priv->sync_mutex); g_free (backend->priv); backend->priv = NULL; diff --git a/calendar/libedatacal/e-cal-backend.c b/calendar/libedatacal/e-cal-backend.c index 0b89fc9..6886aa6 100644 --- a/calendar/libedatacal/e-cal-backend.c +++ b/calendar/libedatacal/e-cal-backend.c @@ -62,6 +62,9 @@ struct _ECalBackendPrivate { GHashTable *categories; GHashTable *changed_categories; guint category_idle_id; + + /* ECalBackend to pass notifications on to */ + ECalBackend *notification_proxy; }; /* Property IDs */ @@ -895,6 +898,18 @@ e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid) return (* CLASS (backend)->internal_get_timezone) (backend, tzid); } +void +e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy) +{ + ECalBackendPrivate *priv; + + g_return_if_fail (E_IS_CAL_BACKEND (backend)); + + priv = backend->priv; + + priv->notification_proxy = proxy; +} + /** * e_cal_backend_notify_object_created: * @backend: A calendar backend. @@ -909,10 +924,18 @@ e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid) void e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj) { + ECalBackendPrivate *priv; EList *queries; EIterator *iter; EDataCalView *query; + priv = backend->priv; + + if (priv->notification_proxy) { + e_cal_backend_notify_object_created (priv->notification_proxy, calobj); + return; + } + queries = e_cal_backend_get_queries (backend); iter = e_list_get_iterator (queries); @@ -946,11 +969,19 @@ void e_cal_backend_notify_object_modified (ECalBackend *backend, const char *old_object, const char *object) { + ECalBackendPrivate *priv; EList *queries; EIterator *iter; EDataCalView *query; gboolean old_match, new_match; + priv = backend->priv; + + if (priv->notification_proxy) { + e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object); + return; + } + queries = e_cal_backend_get_queries (backend); iter = e_list_get_iterator (queries); @@ -997,10 +1028,18 @@ void e_cal_backend_notify_object_removed (ECalBackend *backend, const char *uid, const char *old_object) { + ECalBackendPrivate *priv; EList *queries; EIterator *iter; EDataCalView *query; + priv = backend->priv; + + if (priv->notification_proxy) { + e_cal_backend_notify_object_removed (priv->notification_proxy, uid, old_object); + return; + } + queries = e_cal_backend_get_queries (backend); iter = e_list_get_iterator (queries); @@ -1035,6 +1074,11 @@ e_cal_backend_notify_mode (ECalBackend *backend, ECalBackendPrivate *priv = backend->priv; GList *l; + if (priv->notification_proxy) { + e_cal_backend_notify_mode (priv->notification_proxy, status, mode); + return; + } + for (l = priv->clients; l; l = l->next) e_data_cal_notify_mode (l->data, status, mode); } @@ -1052,6 +1096,11 @@ e_cal_backend_notify_error (ECalBackend *backend, const char *message) ECalBackendPrivate *priv = backend->priv; GList *l; + if (priv->notification_proxy) { + e_cal_backend_notify_error (priv->notification_proxy, message); + return; + } + for (l = priv->clients; l; l = l->next) e_data_cal_notify_error (l->data, message); } diff --git a/calendar/libedatacal/e-cal-backend.h b/calendar/libedatacal/e-cal-backend.h index 00676c7..0383813 100644 --- a/calendar/libedatacal/e-cal-backend.h +++ b/calendar/libedatacal/e-cal-backend.h @@ -160,6 +160,7 @@ icaltimezone* e_cal_backend_internal_get_timezone (ECalBackend *backend, const c void e_cal_backend_last_client_gone (ECalBackend *backend); +void e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy); void e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj); void e_cal_backend_notify_object_modified (ECalBackend *backend, const char *old_object, const char *object); void e_cal_backend_notify_object_removed (ECalBackend *backend, const char *uid, const char *old_object); -- 2.7.4