From 83cbc862e6981bd06b406a0ae01e50d15a3502e7 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Wed, 2 Aug 2000 22:15:22 +0000 Subject: New files for the iCalendar file backend. 2000-08-02 Federico Mena Quintero * pcs/cal-backend-file.[ch]: New files for the iCalendar file backend. * pcs/Makefile.am (libpcs_a_SOURCES): Added cal-backend-file.[ch]. * cal-util/cal-component.c (cal_component_set_icalcomponent): Return an operation success code for if we are passed a component of a type we don't support. svn path=/trunk/; revision=4479 --- calendar/ChangeLog | 11 + calendar/cal-util/cal-component.c | 33 +- calendar/cal-util/cal-component.h | 2 +- calendar/pcs/Makefile.am | 2 + calendar/pcs/cal-backend-file.c | 1004 +++++++++++++++++++++++++++++++++++++ calendar/pcs/cal-backend-file.h | 62 +++ calendar/pcs/cal-backend.c | 3 +- 7 files changed, 1108 insertions(+), 9 deletions(-) create mode 100644 calendar/pcs/cal-backend-file.c create mode 100644 calendar/pcs/cal-backend-file.h (limited to 'calendar') diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 280f75e57d..0e156caa66 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,14 @@ +2000-08-02 Federico Mena Quintero + + * pcs/cal-backend-file.[ch]: New files for the iCalendar file + backend. + + * pcs/Makefile.am (libpcs_a_SOURCES): Added cal-backend-file.[ch]. + + * cal-util/cal-component.c (cal_component_set_icalcomponent): + Return an operation success code for if we are passed a component + of a type we don't support. + 2000-07-31 Federico Mena Quintero * cal-util/cal-recur.c (*): Use CalComponent and the new property diff --git a/calendar/cal-util/cal-component.c b/calendar/cal-util/cal-component.c index e8b1adb96c..b697b60389 100644 --- a/calendar/cal-util/cal-component.c +++ b/calendar/cal-util/cal-component.c @@ -652,29 +652,48 @@ cal_component_set_new_vtype (CalComponent *comp, CalComponentVType type) * structure. If the @comp already had an #icalcomponent set into it, it will * will be freed automatically if the #icalcomponent does not have a parent * component itself. + * + * Supported component types are VEVENT, VTODO, VJOURNAL, VFREEBUSY, and VTIMEZONE. + * + * Return value: TRUE on success, FALSE if @icalcomp is an unsupported component + * type. **/ -void +gboolean cal_component_set_icalcomponent (CalComponent *comp, icalcomponent *icalcomp) { CalComponentPrivate *priv; + icalcomponent_kind kind; - g_return_if_fail (comp != NULL); - g_return_if_fail (IS_CAL_COMPONENT (comp)); + g_return_val_if_fail (comp != NULL, FALSE); + g_return_val_if_fail (IS_CAL_COMPONENT (comp), FALSE); priv = comp->priv; if (priv->icalcomp == icalcomp) - return; + return TRUE; free_icalcomponent (comp); - priv->icalcomp = icalcomp; + if (!icalcomp) { + priv->icalcomp = NULL; + return TRUE; + } - if (!icalcomp) - return; + kind = icalcomponent_isa (icalcomp); + + if (!(kind == ICAL_VEVENT_COMPONENT + || kind == ICAL_VTODO_COMPONENT + || kind == ICAL_VJOURNAL_COMPONENT + || kind == ICAL_VFREEBUSY_COMPONENT + || kind == ICAL_VTIMEZONE_COMPONENT)) + return FALSE; + + priv->icalcomp = icalcomp; scan_icalcomponent (comp); ensure_mandatory_properties (comp); + + return TRUE; } /** diff --git a/calendar/cal-util/cal-component.h b/calendar/cal-util/cal-component.h index c09c73d5bb..38b9e60aed 100644 --- a/calendar/cal-util/cal-component.h +++ b/calendar/cal-util/cal-component.h @@ -128,7 +128,7 @@ CalComponent *cal_component_new (void); void cal_component_set_new_vtype (CalComponent *comp, CalComponentVType type); -void cal_component_set_icalcomponent (CalComponent *comp, icalcomponent *icalcomp); +gboolean cal_component_set_icalcomponent (CalComponent *comp, icalcomponent *icalcomp); icalcomponent *cal_component_get_icalcomponent (CalComponent *comp); CalComponentVType cal_component_get_vtype (CalComponent *comp); diff --git a/calendar/pcs/Makefile.am b/calendar/pcs/Makefile.am index 64aef701c9..320e3f0a14 100644 --- a/calendar/pcs/Makefile.am +++ b/calendar/pcs/Makefile.am @@ -29,6 +29,8 @@ libpcs_a_SOURCES = \ cal.h \ cal-backend.c \ cal-backend.h \ + cal-backend-file.c \ + cal-backend-file.h \ cal-backend-imc.c \ cal-backend-imc.h \ cal-common.h \ 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 + * + * 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 +#include +#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; +} diff --git a/calendar/pcs/cal-backend-file.h b/calendar/pcs/cal-backend-file.h new file mode 100644 index 0000000000..376790751c --- /dev/null +++ b/calendar/pcs/cal-backend-file.h @@ -0,0 +1,62 @@ +/* Evolution calendar - iCalendar file backend + * + * Copyright (C) 2000 Helix Code, Inc. + * + * Author: Federico Mena-Quintero + * + * 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. + */ + +#ifndef CAL_BACKEND_FILE_H +#define CAL_BACKEND_FILE_H + +#include +#include "cal-backend.h" + +BEGIN_GNOME_DECLS + + + +#define CAL_BACKEND_FILE_TYPE (cal_backend_file_get_type ()) +#define CAL_BACKEND_FILE(obj) (GTK_CHECK_CAST ((obj), CAL_BACKEND_FILE_TYPE, \ + CalBackendFile)) +#define CAL_BACKEND_FILE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), CAL_BACKEND_FILE_TYPE, \ + CalBackendFileClass)) +#define IS_CAL_BACKEND_FILE(obj) (GTK_CHECK_TYPE ((obj), CAL_BACKEND_FILE_TYPE)) +#define IS_CAL_BACKEND_FILE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), CAL_BACKEND_FILE_TYPE)) + +typedef struct _CalBackendFile CalBackendFile; +typedef struct _CalBackendFileClass CalBackendFileClass; + +typedef struct _CalBackendFilePrivate CalBackendFilePrivate; + +struct _CalBackendFile { + CalBackend backend; + + /* Private data */ + CalBackendFilePrivate *priv; +}; + +struct _CalBackendFileClass { + CalBackendClass parent_class; +}; + +GtkType cal_backend_file_get_type (void); + + + +END_GNOME_DECLS + +#endif diff --git a/calendar/pcs/cal-backend.c b/calendar/pcs/cal-backend.c index 658f55012b..97d09fac24 100644 --- a/calendar/pcs/cal-backend.c +++ b/calendar/pcs/cal-backend.c @@ -347,7 +347,8 @@ void cal_backend_update_pilot_id (CalBackend *backend, const char *uid, * object that has the same UID as the specified one. The backend will in * turn notify all of its clients about the change. * - * Return value: TRUE on success, FALSE on being passed an invalid object. + * Return value: TRUE on success, FALSE on being passed an invalid object or one + * with an unsupported type. **/ gboolean cal_backend_update_object (CalBackend *backend, const char *uid, const char *calobj) -- cgit