aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/pcs/cal-backend-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/pcs/cal-backend-file.c')
-rw-r--r--calendar/pcs/cal-backend-file.c1004
1 files changed, 1004 insertions, 0 deletions
diff --git a/calendar/pcs/cal-backend-file.c b/calendar/pcs/cal-backend-file.c
new file mode 100644
index 0000000000..8c12e38e3b
--- /dev/null
+++ b/calendar/pcs/cal-backend-file.c
@@ -0,0 +1,1004 @@
+/* Evolution calendar - iCalendar file backend
+ *
+ * Copyright (C) 2000 Helix Code, Inc.
+ *
+ * Author: Federico Mena-Quintero <federico@helixcode.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gtk/gtksignal.h>
+#include "cal-util/cal-recur.h"
+#include "cal-backend-file.h"
+
+
+
+/* Private part of the CalBackendFile structure */
+struct _CalBackendFilePrivate {
+ /* URI where the calendar data is stored */
+ GnomeVFSURI *uri;
+
+ /* List of Cal objects with their listeners */
+ GList *clients;
+
+ /* Toplevel VCALENDAR component */
+ icalcomponent *icalcomp;
+
+ /* All the CalComponent objects in the calendar, hashed by UID. The
+ * hash key *is* the uid returned by cal_component_get_uid(); it is not
+ * copied, so don't free it when you remove an object from the hash
+ * table.
+ */
+ GHashTable *comp_uid_hash;
+
+ /* All event, to-do, and journal components in the calendar; they are
+ * here just for easy access (i.e. so that you don't have to iterate
+ * over the comp_uid_hash). If you need *all* the components in the
+ * calendar, iterate over the hash instead.
+ */
+ GList *events;
+ GList *todos;
+ GList *journals;
+
+ /* Idle handler for saving the calendar when it is dirty */
+ guint idle_id;
+};
+
+
+
+static void cal_backend_file_class_init (CalBackendFileClass *class);
+static void cal_backend_file_init (CalBackendFile *cbfile);
+static void cal_backend_file_destroy (GtkObject *object);
+
+static GnomeVFSURI *cal_backend_file_get_uri (CalBackend *backend);
+static void cal_backend_file_add_cal (CalBackend *backend, Cal *cal);
+static CalBackendLoadStatus cal_backend_file_load (CalBackend *backend, GnomeVFSURI *uri);
+static void cal_backend_file_create (CalBackend *backend, GnomeVFSURI *uri);
+
+static int cal_backend_file_get_n_objects (CalBackend *backend, CalObjType type);
+static char *cal_backend_file_get_object (CalBackend *backend, const char *uid);
+static GList *cal_backend_file_get_uids (CalBackend *backend, CalObjType type);
+static GList *cal_backend_file_get_events_in_range (CalBackend *backend, time_t start, time_t end);
+static GList *cal_backend_file_get_alarms_in_range (CalBackend *backend, time_t start, time_t end);
+static gboolean cal_backend_file_get_alarms_for_object (CalBackend *backend, const char *uid,
+ time_t start, time_t end,
+ GList **alarms);
+static gboolean cal_backend_file_update_object (CalBackend *backend, const char *uid,
+ const char *calobj);
+static gboolean cal_backend_file_remove_object (CalBackend *backend, const char *uid);
+static char *cal_backend_file_get_uid_by_pilot_id (CalBackend *backend, unsigned long int pilot_id);
+static void cal_backend_file_update_pilot_id (CalBackend *backend,
+ const char *uid,
+ unsigned long int pilot_id,
+ unsigned long int pilot_status);
+
+
+static CalBackendClass *parent_class;
+
+
+
+/**
+ * cal_backend_file_get_type:
+ * @void:
+ *
+ * Registers the #CalBackendFile class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #CalBackendFile class.
+ **/
+GtkType
+cal_backend_file_get_type (void)
+{
+ static GtkType cal_backend_file_type = 0;
+
+ if (!cal_backend_file_type) {
+ static const GtkTypeInfo cal_backend_file_info = {
+ "CalBackendFile",
+ sizeof (CalBackendFile),
+ sizeof (CalBackendFileClass),
+ (GtkClassInitFunc) cal_backend_file_class_init,
+ (GtkObjectInitFunc) cal_backend_file_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ cal_backend_file_type = gtk_type_unique (CAL_BACKEND_TYPE, &cal_backend_file_info);
+ }
+
+ return cal_backend_file_type;
+}
+
+/* Class initialization function for the file backend */
+static void
+cal_backend_file_class_init (CalBackendFileClass *class)
+{
+ GtkObjectClass *object_class;
+ CalBackendClass *backend_class;
+
+ object_class = (GtkObjectClass *) class;
+ backend_class = (CalBackendClass *) class;
+
+ parent_class = gtk_type_class (CAL_BACKEND_TYPE);
+
+ object_class->destroy = cal_backend_file_destroy;
+
+ backend_class->get_uri = cal_backend_file_get_uri;
+ backend_class->add_cal = cal_backend_file_add_cal;
+ backend_class->load = cal_backend_file_load;
+ backend_class->create = cal_backend_file_create;
+ backend_class->get_n_objects = cal_backend_file_get_n_objects;
+ backend_class->get_object = cal_backend_file_get_object;
+ backend_class->get_uids = cal_backend_file_get_uids;
+ backend_class->get_events_in_range = cal_backend_file_get_events_in_range;
+ backend_class->get_alarms_in_range = cal_backend_file_get_alarms_in_range;
+ backend_class->get_alarms_for_object = cal_backend_file_get_alarms_for_object;
+ backend_class->update_object = cal_backend_file_update_object;
+ backend_class->remove_object = cal_backend_file_remove_object;
+ backend_class->get_uid_by_pilot_id = cal_backend_file_get_uid_by_pilot_id;
+ backend_class->update_pilot_id = cal_backend_file_update_pilot_id;
+}
+
+/* Object initialization function for the file backend */
+static void
+cal_backend_file_init (CalBackendFile *cbfile)
+{
+ CalBackendFilePrivate *priv;
+
+ priv = g_new0 (CalBackendFilePrivate, 1);
+ cbfile->priv = priv;
+}
+
+/* g_hash_table_foreach() callback to destroy a CalComponent */
+static void
+free_cal_component (gpointer key, gpointer value, gpointer data)
+{
+ CalComponent *comp;
+
+ comp = CAL_COMPONENT (value);
+ gtk_object_unref (GTK_OBJECT (comp));
+}
+
+/* Saves the calendar data */
+static void
+save (CalBackendFile *cbfile)
+{
+ CalBackendFilePrivate *priv;
+
+ priv = cbfile->priv;
+ g_assert (priv->uri != NULL);
+ g_assert (priv->icalcomp != NULL);
+
+ /* FIXME */
+
+ /* FIXME: ensure we have the mandatory PRODID and VERSION properties, and throw
+ * in CALSCALE for good measure.
+ */
+}
+
+/* Destroy handler for the file backend */
+static void
+cal_backend_file_destroy (GtkObject *object)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_CAL_BACKEND_FILE (object));
+
+ cbfile = CAL_BACKEND_FILE (object);
+ priv = cbfile->priv;
+
+ g_assert (priv->clients == NULL);
+
+ /* Save if necessary */
+
+ if (priv->idle_id != 0) {
+ save (cbfile);
+ g_source_remove (priv->idle_id);
+ priv->idle_id = 0;
+ }
+
+ /* Clean up */
+
+ if (priv->uri) {
+ gnome_vfs_uri_unref (priv->uri);
+ priv->uri = NULL;
+ }
+
+ if (priv->comp_uid_hash) {
+ g_hash_table_foreach (priv->comp_uid_hash, free_cal_component, NULL);
+ g_hash_table_destroy (priv->comp_uid_hash);
+ priv->comp_uid_hash = NULL;
+ }
+
+ g_list_free (priv->events);
+ g_list_free (priv->todos);
+ g_list_free (priv->journals);
+
+ priv->events = NULL;
+ priv->todos = NULL;
+ priv->journals = NULL;
+
+ if (priv->icalcomp) {
+ icalcomponent_free (priv->icalcomp);
+ priv->icalcomp = NULL;
+ }
+
+ g_free (priv);
+ cbfile->priv = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+
+/* Looks up a component by its UID on the backend's component hash table */
+static CalComponent *
+lookup_component (CalBackendFile *cbfile, const char *uid)
+{
+ CalBackendFilePrivate *priv;
+ CalComponent *comp;
+
+ priv = cbfile->priv;
+ comp = g_hash_table_lookup (priv->comp_uid_hash, uid);
+
+ return comp;
+}
+
+
+
+/* Calendar backend methods */
+
+/* Get_uri handler for the file backend */
+static GnomeVFSURI *
+cal_backend_file_get_uri (CalBackend *backend)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+ g_assert (priv->uri != NULL);
+
+ return priv->uri;
+}
+
+/* Callback used when a Cal is destroyed */
+static void
+cal_destroy_cb (GtkObject *object, gpointer data)
+{
+ Cal *cal;
+ Cal *lcal;
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ GList *l;
+
+ cal = CAL (object);
+
+ cbfile = CAL_BACKEND_FILE (data);
+ priv = cbfile->priv;
+
+ /* Find the cal in the list of clients */
+
+ for (l = priv->clients; l; l = l->next) {
+ lcal = CAL (l->data);
+
+ if (lcal == cal)
+ break;
+ }
+
+ g_assert (l != NULL);
+
+ /* Disconnect */
+
+ priv->clients = g_list_remove_link (priv->clients, l);
+ g_list_free_1 (l);
+
+ /* When all clients go away, notify the parent factory about it so that
+ * it may decide whether to kill the backend or not.
+ */
+ if (!priv->clients)
+ cal_backend_last_client_gone (CAL_BACKEND (cbfile));
+}
+
+/* Add_cal handler for the file backend */
+static void
+cal_backend_file_add_cal (CalBackend *backend, Cal *cal)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_if_fail (priv->icalcomp != NULL);
+ g_return_if_fail (cal != NULL);
+ g_return_if_fail (IS_CAL (cal));
+
+ /* We do not keep a reference to the Cal since the calendar user agent
+ * owns it.
+ */
+
+ gtk_signal_connect (GTK_OBJECT (cal), "destroy",
+ GTK_SIGNAL_FUNC (cal_destroy_cb),
+ backend);
+
+ priv->clients = g_list_prepend (priv->clients, cal);
+}
+
+/* Idle handler; we save the calendar since it is dirty */
+static gboolean
+save_idle (gpointer data)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+
+ cbfile = CAL_BACKEND_FILE (data);
+ priv = cbfile->priv;
+
+ g_assert (priv->icalcomp != NULL);
+
+ save (cbfile);
+
+ priv->idle_id = 0;
+ return FALSE;
+}
+
+/* Marks the file backend as dirty and queues a save operation */
+static void
+mark_dirty (CalBackendFile *cbfile)
+{
+ CalBackendFilePrivate *priv;
+
+ priv = cbfile->priv;
+
+ if (priv->idle_id != 0)
+ return;
+
+ priv->idle_id = g_idle_add (save_idle, cbfile);
+}
+
+/* Checks if the specified component has a duplicated UID and if so changes it */
+static void
+check_dup_uid (CalBackendFile *cbfile, CalComponent *comp)
+{
+ CalBackendFilePrivate *priv;
+ CalComponent *old_comp;
+ const char *uid;
+ char *new_uid;
+
+ priv = cbfile->priv;
+
+ cal_component_get_uid (comp, &uid);
+
+ old_comp = g_hash_table_lookup (priv->comp_uid_hash, uid);
+ if (!old_comp)
+ return; /* Everything is fine */
+
+ g_message ("check_dup_uid(): Got object with duplicated UID `%s', changing it...", uid);
+
+ new_uid = cal_component_gen_uid ();
+ cal_component_set_uid (comp, new_uid);
+ g_free (new_uid);
+
+ /* FIXME: I think we need to reset the SEQUENCE property and reset the
+ * CREATED/DTSTAMP/LAST-MODIFIED.
+ */
+
+ mark_dirty (cbfile);
+}
+
+/* Tries to add an icalcomponent to the file backend. We only store the objects
+ * of the types we support; all others just remain in the toplevel component so
+ * that we don't lose them.
+ */
+static void
+add_component (CalBackendFile *cbfile, CalComponent *comp)
+{
+ CalBackendFilePrivate *priv;
+ GList **list;
+ const char *uid;
+
+ priv = cbfile->priv;
+
+ switch (cal_component_get_vtype (comp)) {
+ case CAL_COMPONENT_EVENT:
+ list = &priv->events;
+ break;
+
+ case CAL_COMPONENT_TODO:
+ list = &priv->todos;
+ break;
+
+ case CAL_COMPONENT_JOURNAL:
+ list = &priv->journals;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+
+ /* Ensure that the UID is unique; some broken implementations spit
+ * components with duplicated UIDs.
+ */
+
+ check_dup_uid (cbfile, comp);
+ cal_component_get_uid (comp, &uid);
+
+ g_hash_table_insert (priv->comp_uid_hash, (char *) uid, comp);
+ *list = g_list_prepend (*list, comp);
+}
+
+/* Removes a component from the backend's hash and lists. Does not perform
+ * notification on the clients.
+ */
+static void
+remove_component (CalBackendFile *cbfile, CalComponent *comp)
+{
+ CalBackendFilePrivate *priv;
+ const char *uid;
+ GList **list, *l;
+
+ priv = cbfile->priv;
+
+ cal_component_get_uid (comp, &uid);
+ g_hash_table_remove (priv->comp_uid_hash, uid);
+
+ switch (cal_component_get_vtype (comp)) {
+ case CAL_COMPONENT_EVENT:
+ list = &priv->events;
+ break;
+
+ case CAL_COMPONENT_TODO:
+ list = &priv->todos;
+ break;
+
+ case CAL_COMPONENT_JOURNAL:
+ list = &priv->journals;
+ break;
+
+ default:
+ /* Make the compiler shut up. */
+ list = NULL;
+ g_assert_not_reached ();
+ }
+
+ l = g_list_find (*list, comp);
+ g_assert (l != NULL);
+
+ *list = g_list_remove_link (*list, l);
+ g_list_free_1 (l);
+
+ gtk_object_unref (GTK_OBJECT (comp));
+}
+
+/* Scans the toplevel VCALENDAR component and stores the objects it finds */
+static void
+scan_vcalendar (CalBackendFile *cbfile)
+{
+ CalBackendFilePrivate *priv;
+ icalcomponent *icalcomp;
+
+ priv = cbfile->priv;
+ g_assert (priv->icalcomp != NULL);
+ g_assert (priv->comp_uid_hash != NULL);
+
+ for (icalcomp = icalcomponent_get_first_component (priv->icalcomp, ICAL_ANY_COMPONENT);
+ icalcomp;
+ icalcomp = icalcomponent_get_next_component (priv->icalcomp, ICAL_ANY_COMPONENT)) {
+ icalcomponent_kind kind;
+ CalComponent *comp;
+
+ kind = icalcomponent_isa (icalcomp);
+
+ if (!(kind == ICAL_VEVENT_COMPONENT
+ || kind == ICAL_VTODO_COMPONENT
+ || kind == ICAL_VJOURNAL_COMPONENT))
+ continue;
+
+ comp = cal_component_new ();
+
+ if (!cal_component_set_icalcomponent (comp, icalcomp))
+ continue;
+
+ add_component (cbfile, comp);
+ }
+}
+
+/* Callback used from icalparser_parse() */
+static char *
+get_line (char *s, size_t size, void *data)
+{
+ FILE *file;
+
+ file = data;
+ return fgets (s, size, file);
+}
+
+/* Load handler for the file backend */
+static CalBackendLoadStatus
+cal_backend_file_load (CalBackend *backend, GnomeVFSURI *uri)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ char *str_uri;
+ FILE *file;
+ icalparser *parser;
+ icalcomponent *icalcomp;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp == NULL, CAL_BACKEND_LOAD_ERROR);
+ g_return_val_if_fail (uri != NULL, CAL_BACKEND_LOAD_ERROR);
+
+ /* FIXME: this looks rather bad; maybe we should check for local files
+ * and fail if they are remote.
+ */
+
+ 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));
+
+ /* Load! */
+
+ file = fopen (str_uri, "r");
+ g_free (str_uri);
+
+ if (!file)
+ return CAL_BACKEND_LOAD_ERROR;
+
+ parser = icalparser_new ();
+ icalparser_set_gen_data (parser, file);
+
+ icalcomp = icalparser_parse (parser, get_line);
+ icalparser_free (parser);
+
+ if (fclose (file) != 0)
+ return CAL_BACKEND_LOAD_ERROR;
+
+ if (!icalcomp)
+ return CAL_BACKEND_LOAD_ERROR;
+
+ /* FIXME: should we try to demangle XROOT components and individual
+ * components as well?
+ */
+
+ if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT)
+ return CAL_BACKEND_LOAD_ERROR;
+
+ priv->icalcomp = icalcomp;
+
+ priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ scan_vcalendar (cbfile);
+
+ /* Clean up */
+
+ gnome_vfs_uri_ref (uri);
+ priv->uri = uri;
+
+ return CAL_BACKEND_LOAD_SUCCESS;
+}
+
+/* Create handler for the file backend */
+static void
+cal_backend_file_create (CalBackend *backend, GnomeVFSURI *uri)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ icalproperty *prop;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_if_fail (priv->icalcomp == NULL);
+ g_return_if_fail (uri != NULL);
+
+ /* Create the new calendar information */
+
+ g_assert (priv->icalcomp == NULL);
+ priv->icalcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT);
+
+ /* RFC 2445, section 4.7.1 */
+ prop = icalproperty_new_calscale ("GREGORIAN");
+ icalcomponent_add_property (priv->icalcomp, prop);
+
+ /* RFC 2445, section 4.7.3 */
+ prop = icalproperty_new_prodid ("-//Helix Code//NONSGML Evolution Calendar//EN");
+ icalcomponent_add_property (priv->icalcomp, prop);
+
+ /* RFC 2445, section 4.7.4 */
+ prop = icalproperty_new_version ("2.0");
+ icalcomponent_add_property (priv->icalcomp, prop);
+
+ /* Create our internal data */
+
+ g_assert (priv->comp_uid_hash == NULL);
+ priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* Done */
+
+ gnome_vfs_uri_ref (uri);
+
+ priv->uri = uri;
+
+ mark_dirty (cbfile);
+}
+
+/* Get_n_objects handler for the file backend */
+static int
+cal_backend_file_get_n_objects (CalBackend *backend, CalObjType type)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ int n;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, -1);
+
+ n = 0;
+
+ if (type & CALOBJ_TYPE_EVENT)
+ n += g_list_length (priv->events);
+
+ if (type & CALOBJ_TYPE_TODO)
+ n += g_list_length (priv->todos);
+
+ if (type & CALOBJ_TYPE_JOURNAL)
+ n += g_list_length (priv->journals);
+
+ return n;
+}
+
+/* Get_object handler for the file backend */
+static char *
+cal_backend_file_get_object (CalBackend *backend, const char *uid)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ CalComponent *comp;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+ g_assert (priv->comp_uid_hash != NULL);
+
+ comp = lookup_component (cbfile, uid);
+
+ if (!comp)
+ return NULL;
+
+ return cal_component_get_as_string (comp);
+}
+
+/* Builds a list of UIDs from a list of CalComponent objects */
+static void
+build_uids_list (GList **list, GList *components)
+{
+ GList *l;
+
+ for (l = components; l; l = l->next) {
+ CalComponent *comp;
+ const char *uid;
+
+ comp = CAL_COMPONENT (l->data);
+ cal_component_get_uid (comp, &uid);
+ *list = g_list_prepend (*list, g_strdup (uid));
+ }
+}
+
+/* Get_uids handler for the file backend */
+static GList *
+cal_backend_file_get_uids (CalBackend *backend, CalObjType type)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ GList *list;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+
+ list = NULL;
+
+ if (type & CALOBJ_TYPE_EVENT)
+ build_uids_list (&list, priv->events);
+
+ if (type & CALOBJ_TYPE_TODO)
+ build_uids_list (&list, priv->todos);
+
+ if (type & CALOBJ_TYPE_JOURNAL)
+ build_uids_list (&list, priv->journals);
+
+ return list;
+}
+
+/* Allocates and fills in a new CalComponentInstance structure */
+static CalObjInstance *
+build_cal_obj_instance (CalComponent *comp, time_t start, time_t end)
+{
+ CalObjInstance *icoi;
+ const char *uid;
+
+ cal_component_get_uid (comp, &uid);
+
+ icoi = g_new (CalObjInstance, 1);
+ icoi->uid = g_strdup (uid);
+ icoi->start = start;
+ icoi->end = end;
+
+ return icoi;
+}
+
+/* Builds a list of event component instances. Used as a callback from
+ * cal_recur_generate_instances().
+ */
+static gboolean
+build_event_list (CalComponent *comp, time_t start, time_t end, gpointer data)
+{
+ CalObjInstance *icoi;
+ GList **l;
+
+ l = data;
+
+ icoi = build_cal_obj_instance (comp, start, end);
+ *l = g_list_prepend (*l, icoi);
+
+ return TRUE;
+}
+
+/* Compares two CalObjInstance structures by their start times. Called from
+ * g_list_sort().
+ */
+static gint
+compare_instance_func (gconstpointer a, gconstpointer b)
+{
+ const CalObjInstance *ca, *cb;
+ time_t diff;
+
+ ca = a;
+ cb = b;
+
+ diff = ca->start - cb->start;
+ return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
+}
+
+/* Get_events_in_range handler for the file backend */
+static GList *
+cal_backend_file_get_events_in_range (CalBackend *backend, time_t start, time_t end)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ GList *l;
+ GList *event_list;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+
+ g_return_val_if_fail (start != -1 && end != -1, NULL);
+ g_return_val_if_fail (start <= end, NULL);
+
+ event_list = NULL;
+
+ for (l = priv->events; l; l = l->next) {
+ CalComponent *comp;
+
+ comp = l->data;
+ cal_recur_generate_instances (comp, start, end, build_event_list, &event_list);
+ }
+
+ event_list = g_list_sort (event_list, compare_instance_func);
+ return event_list;
+}
+
+/* Get_alarms_in_range handler for the file backend */
+static GList *
+cal_backend_file_get_alarms_in_range (CalBackend *backend, time_t start, time_t end)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, NULL);
+
+ g_return_val_if_fail (start != -1 && end != -1, NULL);
+ g_return_val_if_fail (start <= end, NULL);
+
+ /* FIXME: have to deal with an unknown number of alarms; we can't just
+ * do the same thing as in cal-backend-imc.
+ */
+ return NULL;
+}
+
+/* Get_alarms_for_object handler for the file backend */
+static gboolean
+cal_backend_file_get_alarms_for_object (CalBackend *backend, const char *uid,
+ time_t start, time_t end,
+ GList **alarms)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+ g_return_val_if_fail (uid != NULL, FALSE);
+ g_return_val_if_fail (start != -1 && end != -1, FALSE);
+ g_return_val_if_fail (start <= end, FALSE);
+ g_return_val_if_fail (alarms != NULL, FALSE);
+
+ /* FIXME */
+
+ *alarms = NULL;
+ return FALSE;
+}
+
+/* Notifies a backend's clients that an object was updated */
+static void
+notify_update (CalBackendFile *cbfile, const char *uid)
+{
+ CalBackendFilePrivate *priv;
+ GList *l;
+
+ priv = cbfile->priv;
+
+ for (l = priv->clients; l; l = l->next) {
+ Cal *cal;
+
+ cal = CAL (l->data);
+ cal_notify_update (cal, uid);
+ }
+}
+
+/* Notifies a backend's clients that an object was removed */
+static void
+notify_remove (CalBackendFile *cbfile, const char *uid)
+{
+ CalBackendFilePrivate *priv;
+ GList *l;
+
+ priv = cbfile->priv;
+
+ for (l = priv->clients; l; l = l->next) {
+ Cal *cal;
+
+ cal = CAL (l->data);
+ cal_notify_remove (cal, uid);
+ }
+}
+
+/* Update_object handler for the file backend */
+static gboolean
+cal_backend_file_update_object (CalBackend *backend, const char *uid, const char *calobj)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ icalcomponent *icalcomp;
+ icalcomponent_kind kind;
+ CalComponent *old_comp;
+ CalComponent *comp;
+ const char *comp_uid;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+ g_return_val_if_fail (uid != NULL, FALSE);
+ g_return_val_if_fail (calobj != NULL, FALSE);
+
+ /* Pull the component from the string and ensure that it is sane */
+
+ icalcomp = icalparser_parse_string ((char *) calobj);
+
+ if (!icalcomp)
+ return FALSE;
+
+ kind = icalcomponent_isa (icalcomp);
+
+ if (!(kind == ICAL_VEVENT_COMPONENT
+ || kind == ICAL_VTODO_COMPONENT
+ || kind == ICAL_VJOURNAL_COMPONENT)) {
+ /* We don't support this type of component */
+ icalcomponent_free (icalcomp);
+ return FALSE;
+ }
+
+ comp = cal_component_new ();
+ if (!cal_component_set_icalcomponent (comp, icalcomp)) {
+ gtk_object_unref (GTK_OBJECT (comp));
+ icalcomponent_free (icalcomp);
+ return FALSE;
+ }
+
+ /* Check the UID for sanity's sake */
+
+ cal_component_get_uid (comp, &comp_uid);
+
+ if (strcmp (uid, comp_uid) != 0) {
+ gtk_object_unref (GTK_OBJECT (comp));
+ return FALSE;
+ }
+
+ /* Update the component */
+
+ old_comp = lookup_component (cbfile, uid);
+
+ if (old_comp)
+ remove_component (cbfile, old_comp);
+
+ add_component (cbfile, comp);
+#if 0
+ /* FIXME */
+ new_ico->pilot_status = ICAL_PILOT_SYNC_MOD;
+#endif
+
+ mark_dirty (cbfile);
+
+ /* FIXME: do the notification asynchronously */
+ notify_update (cbfile, comp_uid);
+
+ return TRUE;
+}
+
+/* Remove_object handler for the file backend */
+static gboolean
+cal_backend_file_remove_object (CalBackend *backend, const char *uid)
+{
+ CalBackendFile *cbfile;
+ CalBackendFilePrivate *priv;
+ CalComponent *comp;
+
+ cbfile = CAL_BACKEND_FILE (backend);
+ priv = cbfile->priv;
+
+ g_return_val_if_fail (priv->icalcomp != NULL, FALSE);
+
+ g_return_val_if_fail (uid != NULL, FALSE);
+
+ comp = lookup_component (cbfile, uid);
+ if (!comp)
+ return FALSE;
+
+ remove_component (cbfile, comp);
+
+ mark_dirty (cbfile);
+
+ /* FIXME: do the notification asynchronously */
+ notify_remove (cbfile, uid);
+
+ return TRUE;
+}