1 /* Evolution calendar - iCalendar file backend
3 * Copyright (C) 2000-2003 Ximian, Inc.
5 * Authors: Federico Mena-Quintero <federico@ximian.com>
6 * Rodrigo Moya <rodrigo@ximian.com>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
28 #include <sys/types.h>
31 #include <bonobo/bonobo-exception.h>
32 #include <bonobo/bonobo-moniker-util.h>
33 #include <libgnome/gnome-i18n.h>
34 #include <libgnomevfs/gnome-vfs.h>
35 #include <libedataserver/e-util.h>
36 #include <libedataserver/e-xml-hash-utils.h>
37 #include <libecal/e-cal-recur.h>
38 #include <libecal/e-cal-time-util.h>
39 #include <libecal/e-cal-util.h>
40 #include <libedata-cal/e-cal-backend-util.h>
41 #include <libedata-cal/e-cal-backend-sexp.h>
42 #include "e-cal-backend-file-events.h"
46 /* Placeholder for each component and its recurrences */
48 ECalComponent *full_object;
49 GHashTable *recurrences;
50 GList *recurrences_list;
51 } ECalBackendFileObject;
53 /* Private part of the ECalBackendFile structure */
54 struct _ECalBackendFilePrivate {
55 /* URI where the calendar data is stored */
58 /* Filename in the dir */
64 /* Toplevel VCALENDAR component */
65 icalcomponent *icalcomp;
67 /* All the objects in the calendar, hashed by UID. The
68 * hash key *is* the uid returned by cal_component_get_uid(); it is not
69 * copied, so don't free it when you remove an object from the hash
70 * table. Each item in the hash table is a ECalBackendFileObject.
72 GHashTable *comp_uid_hash;
76 /* The calendar's default timezone, used for resolving DATE and
77 floating DATE-TIME values. */
78 icaltimezone *default_zone;
80 /* The list of live queries */
88 static void e_cal_backend_file_dispose (GObject *object);
89 static void e_cal_backend_file_finalize (GObject *object);
91 static ECalBackendSyncClass *parent_class;
95 /* g_hash_table_foreach() callback to destroy a ECalBackendFileObject */
97 free_object (gpointer key, gpointer value, gpointer data)
99 ECalBackendFileObject *obj_data = value;
101 g_object_unref (obj_data->full_object);
102 g_hash_table_destroy (obj_data->recurrences);
103 g_list_free (obj_data->recurrences_list);
108 /* Saves the calendar data */
110 save_file_when_idle (gpointer user_data)
112 ECalBackendFilePrivate *priv;
113 GnomeVFSURI *uri, *backup_uri;
114 GnomeVFSHandle *handle = NULL;
115 GnomeVFSResult result = GNOME_VFS_ERROR_BAD_FILE;
116 GnomeVFSFileSize out;
117 gchar *tmp, *backup_uristr;
119 ECalBackendFile *cbfile = user_data;
122 g_assert (priv->uri != NULL);
123 g_assert (priv->icalcomp != NULL);
128 uri = gnome_vfs_uri_new (priv->uri);
130 goto error_malformed_uri;
132 /* save calendar to backup file */
133 tmp = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
135 gnome_vfs_uri_unref (uri);
136 goto error_malformed_uri;
139 backup_uristr = g_strconcat (tmp, "~", NULL);
140 backup_uri = gnome_vfs_uri_new (backup_uristr);
143 g_free (backup_uristr);
146 gnome_vfs_uri_unref (uri);
147 goto error_malformed_uri;
150 result = gnome_vfs_create_uri (&handle, backup_uri,
151 GNOME_VFS_OPEN_WRITE,
153 if (result != GNOME_VFS_OK) {
154 gnome_vfs_uri_unref (uri);
155 gnome_vfs_uri_unref (backup_uri);
159 buf = icalcomponent_as_ical_string (priv->icalcomp);
160 result = gnome_vfs_write (handle, buf, strlen (buf) * sizeof (char), &out);
161 gnome_vfs_close (handle);
162 if (result != GNOME_VFS_OK) {
163 gnome_vfs_uri_unref (uri);
164 gnome_vfs_uri_unref (backup_uri);
168 /* now copy the temporary file to the real file */
169 result = gnome_vfs_move_uri (backup_uri, uri, TRUE);
171 gnome_vfs_uri_unref (uri);
172 gnome_vfs_uri_unref (backup_uri);
173 if (result != GNOME_VFS_OK)
176 priv->is_dirty = FALSE;
181 e_cal_backend_notify_error (E_CAL_BACKEND (cbfile),
182 _("Can't save calendar data: Malformed URI."));
186 e_cal_backend_notify_error (E_CAL_BACKEND (cbfile), gnome_vfs_result_to_string (result));
191 save (ECalBackendFile *cbfile)
193 ECalBackendFilePrivate *priv;
197 if (!priv->dirty_idle_id) {
198 priv->dirty_idle_id = g_idle_add ((GSourceFunc) save_file_when_idle, cbfile);
201 priv->is_dirty = TRUE;
205 free_calendar_components (GHashTable *comp_uid_hash, icalcomponent *top_icomp)
208 g_hash_table_foreach (comp_uid_hash, (GHFunc) free_object, NULL);
209 g_hash_table_destroy (comp_uid_hash);
213 icalcomponent_free (top_icomp);
218 free_calendar_data (ECalBackendFile *cbfile)
220 ECalBackendFilePrivate *priv;
224 free_calendar_components (priv->comp_uid_hash, priv->icalcomp);
225 priv->comp_uid_hash = NULL;
226 priv->icalcomp = NULL;
228 g_list_free (priv->comp);
232 /* Dispose handler for the file backend */
234 e_cal_backend_file_dispose (GObject *object)
236 ECalBackendFile *cbfile;
237 ECalBackendFilePrivate *priv;
239 cbfile = E_CAL_BACKEND_FILE (object);
242 /* Save if necessary */
244 save_file_when_idle (cbfile);
246 free_calendar_data (cbfile);
248 if (G_OBJECT_CLASS (parent_class)->dispose)
249 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
252 /* Finalize handler for the file backend */
254 e_cal_backend_file_finalize (GObject *object)
256 ECalBackendFile *cbfile;
257 ECalBackendFilePrivate *priv;
259 g_return_if_fail (object != NULL);
260 g_return_if_fail (E_IS_CAL_BACKEND_FILE (object));
262 cbfile = E_CAL_BACKEND_FILE (object);
267 if (priv->dirty_idle_id) {
268 g_source_remove (priv->dirty_idle_id);
269 priv->dirty_idle_id = 0;
280 if (G_OBJECT_CLASS (parent_class)->finalize)
281 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
286 /* Looks up a component by its UID on the backend's component hash table */
287 static ECalComponent *
288 lookup_component (ECalBackendFile *cbfile, const char *uid)
290 ECalBackendFilePrivate *priv;
291 ECalBackendFileObject *obj_data;
295 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
296 return obj_data ? obj_data->full_object : NULL;
301 /* Calendar backend methods */
303 /* Is_read_only handler for the file backend */
304 static ECalBackendSyncStatus
305 e_cal_backend_file_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
307 ECalBackendFile *cbfile = (ECalBackendFile *) backend;
309 *read_only = cbfile->priv->read_only;
311 return GNOME_Evolution_Calendar_Success;
314 /* Get_email_address handler for the file backend */
315 static ECalBackendSyncStatus
316 e_cal_backend_file_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
318 /* A file backend has no particular email address associated
319 * with it (although that would be a useful feature some day).
323 return GNOME_Evolution_Calendar_Success;
326 static ECalBackendSyncStatus
327 e_cal_backend_file_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
331 return GNOME_Evolution_Calendar_Success;
334 static ECalBackendSyncStatus
335 e_cal_backend_file_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
337 /* A file backend has no particular email address associated
338 * with it (although that would be a useful feature some day).
342 return GNOME_Evolution_Calendar_Success;
345 static ECalBackendSyncStatus
346 e_cal_backend_file_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
348 *capabilities = g_strdup (CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
350 return GNOME_Evolution_Calendar_Success;
353 /* function to resolve timezones */
354 static icaltimezone *
355 resolve_tzid (const char *tzid, gpointer user_data)
357 icalcomponent *vcalendar_comp = user_data;
359 if (!tzid || !tzid[0])
361 else if (!strcmp (tzid, "UTC"))
362 return icaltimezone_get_utc_timezone ();
364 return icalcomponent_get_timezone (vcalendar_comp, tzid);
367 /* Checks if the specified component has a duplicated UID and if so changes it */
369 check_dup_uid (ECalBackendFile *cbfile, ECalComponent *comp)
371 ECalBackendFilePrivate *priv;
372 ECalBackendFileObject *obj_data;
378 e_cal_component_get_uid (comp, &uid);
380 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
382 return; /* Everything is fine */
384 d(g_message (G_STRLOC ": Got object with duplicated UID `%s', changing it...", uid));
386 new_uid = e_cal_component_gen_uid ();
387 e_cal_component_set_uid (comp, new_uid);
390 /* FIXME: I think we need to reset the SEQUENCE property and reset the
391 * CREATED/DTSTAMP/LAST-MODIFIED.
397 static struct icaltimetype
398 get_rid_icaltime (ECalComponent *comp)
400 ECalComponentRange range;
401 struct icaltimetype tt;
403 e_cal_component_get_recurid (comp, &range);
404 if (!range.datetime.value)
405 return icaltime_null_time ();
406 tt = *range.datetime.value;
407 e_cal_component_free_range (&range);
412 /* Tries to add an icalcomponent to the file backend. We only store the objects
413 * of the types we support; all others just remain in the toplevel component so
414 * that we don't lose them.
417 add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_toplevel)
419 ECalBackendFilePrivate *priv;
420 ECalBackendFileObject *obj_data;
426 e_cal_component_get_uid (comp, &uid);
427 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
428 if (e_cal_component_is_instance (comp)) {
431 rid = e_cal_component_get_recurid_as_string (comp);
433 if (g_hash_table_lookup (obj_data->recurrences, rid)) {
434 g_warning (G_STRLOC ": Tried to add an already existing recurrence");
438 obj_data = g_new0 (ECalBackendFileObject, 1);
439 obj_data->full_object = NULL;
440 obj_data->recurrences = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
441 g_hash_table_insert (priv->comp_uid_hash, (gpointer) uid, obj_data);
444 g_hash_table_insert (obj_data->recurrences, g_strdup (rid), comp);
445 obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
447 /* Ensure that the UID is unique; some broken implementations spit
448 * components with duplicated UIDs.
450 check_dup_uid (cbfile, comp);
453 if (obj_data->full_object) {
454 g_warning (G_STRLOC ": Tried to add an already existing object");
458 obj_data->full_object = comp;
460 obj_data = g_new0 (ECalBackendFileObject, 1);
461 obj_data->full_object = comp;
462 obj_data->recurrences = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
464 g_hash_table_insert (priv->comp_uid_hash, (gpointer) uid, obj_data);
468 priv->comp = g_list_prepend (priv->comp, comp);
470 /* Put the object in the toplevel component if required */
472 if (add_to_toplevel) {
473 icalcomponent *icalcomp;
475 icalcomp = e_cal_component_get_icalcomponent (comp);
476 g_assert (icalcomp != NULL);
478 icalcomponent_add_component (priv->icalcomp, icalcomp);
481 /* Update the set of categories */
482 e_cal_component_get_categories_list (comp, &categories);
483 e_cal_backend_ref_categories (E_CAL_BACKEND (cbfile), categories);
484 e_cal_component_free_categories_list (categories);
487 /* g_hash_table_foreach_remove() callback to remove recurrences from the calendar */
489 remove_recurrence_cb (gpointer key, gpointer value, gpointer data)
493 icalcomponent *icalcomp;
494 ECalBackendFilePrivate *priv;
495 ECalComponent *comp = value;
496 ECalBackendFile *cbfile = data;
500 /* remove the recurrence from the top-level calendar */
501 icalcomp = e_cal_component_get_icalcomponent (comp);
502 g_assert (icalcomp != NULL);
504 icalcomponent_remove_component (priv->icalcomp, icalcomp);
506 /* remove it from our mapping */
507 l = g_list_find (priv->comp, comp);
508 priv->comp = g_list_delete_link (priv->comp, l);
510 /* Update the set of categories */
511 e_cal_component_get_categories_list (comp, &categories);
512 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
513 e_cal_component_free_categories_list (categories);
518 /* Removes a component from the backend's hash and lists. Does not perform
519 * notification on the clients. Also removes the component from the toplevel
523 remove_component (ECalBackendFile *cbfile, const char *uid, ECalBackendFileObject *obj_data)
525 ECalBackendFilePrivate *priv;
526 icalcomponent *icalcomp;
532 /* Remove the icalcomp from the toplevel */
533 if (obj_data->full_object) {
534 icalcomp = e_cal_component_get_icalcomponent (obj_data->full_object);
535 g_assert (icalcomp != NULL);
537 icalcomponent_remove_component (priv->icalcomp, icalcomp);
539 /* Remove it from our mapping */
540 l = g_list_find (priv->comp, obj_data->full_object);
541 g_assert (l != NULL);
542 priv->comp = g_list_delete_link (priv->comp, l);
544 /* Update the set of categories */
545 e_cal_component_get_categories_list (obj_data->full_object, &categories);
546 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
547 e_cal_component_free_categories_list (categories);
550 /* remove the recurrences also */
551 g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_recurrence_cb, cbfile);
553 g_hash_table_remove (priv->comp_uid_hash, uid);
554 free_object ((gpointer) uid, (gpointer) obj_data, NULL);
557 /* Scans the toplevel VCALENDAR component and stores the objects it finds */
559 scan_vcalendar (ECalBackendFile *cbfile)
561 ECalBackendFilePrivate *priv;
565 g_assert (priv->icalcomp != NULL);
566 g_assert (priv->comp_uid_hash != NULL);
568 for (iter = icalcomponent_begin_component (priv->icalcomp, ICAL_ANY_COMPONENT);
569 icalcompiter_deref (&iter) != NULL;
570 icalcompiter_next (&iter)) {
571 icalcomponent *icalcomp;
572 icalcomponent_kind kind;
575 icalcomp = icalcompiter_deref (&iter);
577 kind = icalcomponent_isa (icalcomp);
579 if (!(kind == ICAL_VEVENT_COMPONENT
580 || kind == ICAL_VTODO_COMPONENT
581 || kind == ICAL_VJOURNAL_COMPONENT))
584 comp = e_cal_component_new ();
586 if (!e_cal_component_set_icalcomponent (comp, icalcomp))
589 add_component (cbfile, comp, FALSE);
594 get_uri_string_for_gnome_vfs (ECalBackend *backend)
596 ECalBackendFile *cbfile;
597 ECalBackendFilePrivate *priv;
598 const char *master_uri;
599 char *full_uri, *str_uri;
602 cbfile = E_CAL_BACKEND_FILE (backend);
605 master_uri = e_cal_backend_get_uri (backend);
607 /* FIXME Check the error conditions a little more elegantly here */
608 if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) {
609 g_warning (G_STRLOC ": Existing file name %s", master_uri);
614 full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name);
615 uri = gnome_vfs_uri_new (full_uri);
621 str_uri = gnome_vfs_uri_to_string (uri,
622 (GNOME_VFS_URI_HIDE_USER_NAME
623 | GNOME_VFS_URI_HIDE_PASSWORD
624 | GNOME_VFS_URI_HIDE_HOST_NAME
625 | GNOME_VFS_URI_HIDE_HOST_PORT
626 | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD));
627 gnome_vfs_uri_unref (uri);
629 if (!str_uri || !strlen (str_uri)) {
638 /* Parses an open iCalendar file and loads it into the backend */
639 static ECalBackendSyncStatus
640 open_cal (ECalBackendFile *cbfile, const char *uristr)
642 ECalBackendFilePrivate *priv;
643 icalcomponent *icalcomp;
647 icalcomp = e_cal_util_parse_ics_file (uristr);
649 return GNOME_Evolution_Calendar_OtherError;
651 /* FIXME: should we try to demangle XROOT components and
652 * individual components as well?
655 if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
656 icalcomponent_free (icalcomp);
658 return GNOME_Evolution_Calendar_OtherError;
661 priv->icalcomp = icalcomp;
662 priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile));
664 priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
665 scan_vcalendar (cbfile);
667 return GNOME_Evolution_Calendar_Success;
672 ECalBackend *backend;
673 GHashTable *old_uid_hash;
674 GHashTable *new_uid_hash;
679 notify_removals_cb (gpointer key, gpointer value, gpointer data)
681 BackendDeltaContext *context = data;
682 const gchar *uid = key;
683 ECalBackendFileObject *old_obj_data = value;
685 if (!g_hash_table_lookup (context->new_uid_hash, uid)) {
686 icalcomponent *old_icomp;
689 /* Object was removed */
691 old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object);
695 old_obj_str = icalcomponent_as_ical_string (old_icomp);
699 e_cal_backend_notify_object_removed (context->backend, uid, old_obj_str, NULL);
704 notify_adds_modifies_cb (gpointer key, gpointer value, gpointer data)
706 BackendDeltaContext *context = data;
707 const gchar *uid = key;
708 ECalBackendFileObject *new_obj_data = value;
709 ECalBackendFileObject *old_obj_data;
710 icalcomponent *old_icomp, *new_icomp;
711 gchar *old_obj_str, *new_obj_str;
713 old_obj_data = g_hash_table_lookup (context->old_uid_hash, uid);
716 /* Object was added */
718 new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object);
722 new_obj_str = icalcomponent_as_ical_string (new_icomp);
726 e_cal_backend_notify_object_created (context->backend, new_obj_str);
728 old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object);
729 new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object);
730 if (!old_icomp || !new_icomp)
733 old_obj_str = icalcomponent_as_ical_string (old_icomp);
734 new_obj_str = icalcomponent_as_ical_string (new_icomp);
735 if (!old_obj_str || !new_obj_str)
738 if (strcmp (old_obj_str, new_obj_str)) {
739 /* Object was modified */
741 e_cal_backend_notify_object_modified (context->backend, old_obj_str, new_obj_str);
747 notify_changes (ECalBackendFile *cbfile, GHashTable *old_uid_hash, GHashTable *new_uid_hash)
749 BackendDeltaContext context;
751 context.backend = E_CAL_BACKEND (cbfile);
752 context.old_uid_hash = old_uid_hash;
753 context.new_uid_hash = new_uid_hash;
755 g_hash_table_foreach (old_uid_hash, (GHFunc) notify_removals_cb, &context);
756 g_hash_table_foreach (new_uid_hash, (GHFunc) notify_adds_modifies_cb, &context);
759 static ECalBackendSyncStatus
760 reload_cal (ECalBackendFile *cbfile, const char *uristr)
762 ECalBackendFilePrivate *priv;
763 icalcomponent *icalcomp, *icalcomp_old;
764 GHashTable *comp_uid_hash_old;
768 icalcomp = e_cal_util_parse_ics_file (uristr);
770 return GNOME_Evolution_Calendar_OtherError;
772 /* FIXME: should we try to demangle XROOT components and
773 * individual components as well?
776 if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
777 icalcomponent_free (icalcomp);
779 return GNOME_Evolution_Calendar_OtherError;
782 /* Keep old data for comparison - free later */
784 icalcomp_old = priv->icalcomp;
785 priv->icalcomp = NULL;
787 comp_uid_hash_old = priv->comp_uid_hash;
788 priv->comp_uid_hash = NULL;
790 /* Load new calendar */
792 free_calendar_data (cbfile);
794 priv->icalcomp = icalcomp;
796 priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
797 scan_vcalendar (cbfile);
799 priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile));
801 /* Compare old and new versions of calendar */
803 notify_changes (cbfile, comp_uid_hash_old, priv->comp_uid_hash);
807 free_calendar_components (comp_uid_hash_old, icalcomp_old);
808 return GNOME_Evolution_Calendar_Success;
811 static ECalBackendSyncStatus
812 create_cal (ECalBackendFile *cbfile, const char *uristr)
815 ECalBackendFilePrivate *priv;
819 /* Create the directory to contain the file */
820 dirname = g_path_get_dirname (uristr);
821 if (e_util_mkdir_hier (dirname, 0700) != 0) {
823 return GNOME_Evolution_Calendar_NoSuchCal;
828 /* Create the new calendar information */
829 priv->icalcomp = e_cal_util_new_top_level ();
831 /* Create our internal data */
832 priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
834 priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile));
838 return GNOME_Evolution_Calendar_Success;
842 get_uri_string (ECalBackend *backend)
844 gchar *str_uri, *full_uri;
846 str_uri = get_uri_string_for_gnome_vfs (backend);
847 full_uri = gnome_vfs_unescape_string (str_uri, "");
853 /* Open handler for the file backend */
854 static ECalBackendSyncStatus
855 e_cal_backend_file_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists,
856 const char *username, const char *password)
858 ECalBackendFile *cbfile;
859 ECalBackendFilePrivate *priv;
861 ECalBackendSyncStatus status;
863 cbfile = E_CAL_BACKEND_FILE (backend);
866 /* Claim a succesful open if we are already open */
867 if (priv->uri && priv->comp_uid_hash)
868 return GNOME_Evolution_Calendar_Success;
870 str_uri = get_uri_string (E_CAL_BACKEND (backend));
872 return GNOME_Evolution_Calendar_OtherError;
874 if (access (str_uri, R_OK) == 0) {
875 status = open_cal (cbfile, str_uri);
876 if (access (str_uri, W_OK) != 0)
877 priv->read_only = TRUE;
880 status = GNOME_Evolution_Calendar_NoSuchCal;
882 status = create_cal (cbfile, str_uri);
890 static ECalBackendSyncStatus
891 e_cal_backend_file_remove (ECalBackendSync *backend, EDataCal *cal)
893 ECalBackendFile *cbfile;
894 ECalBackendFilePrivate *priv;
895 char *str_uri, *dirname;
898 GError *error = NULL;
901 cbfile = E_CAL_BACKEND_FILE (backend);
904 str_uri = get_uri_string (E_CAL_BACKEND (backend));
906 return GNOME_Evolution_Calendar_OtherError;
908 if (access (str_uri, W_OK) != 0) {
911 return GNOME_Evolution_Calendar_PermissionDenied;
914 /* remove all files in the directory */
915 dirname = g_path_get_dirname (str_uri);
916 dir = g_dir_open (dirname, 0, &error);
921 return GNOME_Evolution_Calendar_PermissionDenied;
924 while ((fname = g_dir_read_name (dir))) {
927 full_path = g_build_filename (dirname, fname, NULL);
928 if (unlink (full_path) != 0) {
934 return GNOME_Evolution_Calendar_OtherError;
940 /* remove the directory itself */
941 success = rmdir (dirname) == 0;
947 return success ? GNOME_Evolution_Calendar_Success : GNOME_Evolution_Calendar_OtherError;
950 /* is_loaded handler for the file backend */
952 e_cal_backend_file_is_loaded (ECalBackend *backend)
954 ECalBackendFile *cbfile;
955 ECalBackendFilePrivate *priv;
957 cbfile = E_CAL_BACKEND_FILE (backend);
960 return (priv->icalcomp != NULL);
963 /* is_remote handler for the file backend */
965 e_cal_backend_file_get_mode (ECalBackend *backend)
967 ECalBackendFile *cbfile;
968 ECalBackendFilePrivate *priv;
970 cbfile = E_CAL_BACKEND_FILE (backend);
973 return CAL_MODE_LOCAL;
976 /* Set_mode handler for the file backend */
978 e_cal_backend_file_set_mode (ECalBackend *backend, CalMode mode)
980 e_cal_backend_notify_mode (backend,
981 GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
982 GNOME_Evolution_Calendar_MODE_LOCAL);
986 static ECalBackendSyncStatus
987 e_cal_backend_file_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
991 comp = e_cal_component_new ();
993 switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
994 case ICAL_VEVENT_COMPONENT:
995 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
997 case ICAL_VTODO_COMPONENT:
998 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1000 case ICAL_VJOURNAL_COMPONENT:
1001 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
1004 g_object_unref (comp);
1005 return GNOME_Evolution_Calendar_ObjectNotFound;
1008 *object = e_cal_component_get_as_string (comp);
1009 g_object_unref (comp);
1011 return GNOME_Evolution_Calendar_Success;
1015 add_detached_recur_to_vcalendar (gpointer key, gpointer value, gpointer user_data)
1017 ECalComponent *recurrence = value;
1018 icalcomponent *vcalendar = user_data;
1020 icalcomponent_add_component (
1022 icalcomponent_new_clone (e_cal_component_get_icalcomponent (recurrence)));
1025 /* Get_object_component handler for the file backend */
1026 static ECalBackendSyncStatus
1027 e_cal_backend_file_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
1029 ECalBackendFile *cbfile;
1030 ECalBackendFilePrivate *priv;
1031 ECalBackendFileObject *obj_data;
1033 cbfile = E_CAL_BACKEND_FILE (backend);
1034 priv = cbfile->priv;
1036 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_InvalidObject);
1037 g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1038 g_assert (priv->comp_uid_hash != NULL);
1040 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
1042 return GNOME_Evolution_Calendar_ObjectNotFound;
1045 ECalComponent *comp;
1047 comp = g_hash_table_lookup (obj_data->recurrences, rid);
1049 *object = e_cal_component_get_as_string (comp);
1051 icalcomponent *icalcomp;
1052 struct icaltimetype itt;
1054 itt = icaltime_from_string (rid);
1055 icalcomp = e_cal_util_construct_instance (
1056 e_cal_component_get_icalcomponent (obj_data->full_object),
1059 return GNOME_Evolution_Calendar_ObjectNotFound;
1061 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1063 icalcomponent_free (icalcomp);
1066 if (g_hash_table_size (obj_data->recurrences) > 0) {
1067 icalcomponent *icalcomp;
1069 /* if we have detached recurrences, return a VCALENDAR */
1070 icalcomp = e_cal_util_new_top_level ();
1071 icalcomponent_add_component (
1073 icalcomponent_new_clone (e_cal_component_get_icalcomponent (obj_data->full_object)));
1075 /* add all detached recurrences */
1076 g_hash_table_foreach (obj_data->recurrences, (GHFunc) add_detached_recur_to_vcalendar, icalcomp);
1078 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1080 icalcomponent_free (icalcomp);
1082 *object = e_cal_component_get_as_string (obj_data->full_object);
1085 return GNOME_Evolution_Calendar_Success;
1088 /* Get_timezone_object handler for the file backend */
1089 static ECalBackendSyncStatus
1090 e_cal_backend_file_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
1092 ECalBackendFile *cbfile;
1093 ECalBackendFilePrivate *priv;
1095 icalcomponent *icalcomp;
1097 cbfile = E_CAL_BACKEND_FILE (backend);
1098 priv = cbfile->priv;
1100 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1101 g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1103 if (!strcmp (tzid, "UTC")) {
1104 zone = icaltimezone_get_utc_timezone ();
1106 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1108 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1110 return GNOME_Evolution_Calendar_ObjectNotFound;
1114 icalcomp = icaltimezone_get_component (zone);
1116 return GNOME_Evolution_Calendar_InvalidObject;
1118 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1120 return GNOME_Evolution_Calendar_Success;
1123 /* Add_timezone handler for the file backend */
1124 static ECalBackendSyncStatus
1125 e_cal_backend_file_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
1127 icalcomponent *tz_comp;
1128 ECalBackendFile *cbfile;
1129 ECalBackendFilePrivate *priv;
1131 cbfile = (ECalBackendFile *) backend;
1133 g_return_val_if_fail (E_IS_CAL_BACKEND_FILE (cbfile), GNOME_Evolution_Calendar_OtherError);
1134 g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
1136 priv = cbfile->priv;
1138 tz_comp = icalparser_parse_string (tzobj);
1140 return GNOME_Evolution_Calendar_InvalidObject;
1142 if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
1145 zone = icaltimezone_new ();
1146 icaltimezone_set_component (zone, tz_comp);
1147 if (!icalcomponent_get_timezone (priv->icalcomp,
1148 icaltimezone_get_tzid (zone))) {
1149 icalcomponent_add_component (priv->icalcomp, tz_comp);
1153 icaltimezone_free (zone, 1);
1156 return GNOME_Evolution_Calendar_Success;
1160 static ECalBackendSyncStatus
1161 e_cal_backend_file_set_default_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid)
1163 ECalBackendFile *cbfile;
1164 ECalBackendFilePrivate *priv;
1167 cbfile = E_CAL_BACKEND_FILE (backend);
1168 priv = cbfile->priv;
1170 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1172 /* Look up the VTIMEZONE in our icalcomponent. */
1173 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1175 return GNOME_Evolution_Calendar_ObjectNotFound;
1177 /* Set the default timezone to it. */
1178 priv->default_zone = zone;
1180 return GNOME_Evolution_Calendar_Success;
1185 gboolean search_needed;
1187 ECalBackendSExp *obj_sexp;
1188 ECalBackend *backend;
1189 icaltimezone *default_zone;
1193 match_recurrence_sexp (gpointer key, gpointer value, gpointer data)
1195 ECalComponent *comp = value;
1196 MatchObjectData *match_data = data;
1198 if ((!match_data->search_needed) ||
1199 (e_cal_backend_sexp_match_comp (match_data->obj_sexp, comp, match_data->backend))) {
1200 match_data->obj_list = g_list_append (match_data->obj_list,
1201 e_cal_component_get_as_string (comp));
1206 match_object_sexp (gpointer key, gpointer value, gpointer data)
1208 ECalBackendFileObject *obj_data = value;
1209 MatchObjectData *match_data = data;
1211 if (obj_data->full_object) {
1212 if ((!match_data->search_needed) ||
1213 (e_cal_backend_sexp_match_comp (match_data->obj_sexp, obj_data->full_object, match_data->backend))) {
1214 match_data->obj_list = g_list_append (match_data->obj_list,
1215 e_cal_component_get_as_string (obj_data->full_object));
1219 /* match also recurrences */
1220 g_hash_table_foreach (obj_data->recurrences,
1221 (GHFunc) match_recurrence_sexp,
1225 /* Get_objects_in_range handler for the file backend */
1226 static ECalBackendSyncStatus
1227 e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects)
1229 ECalBackendFile *cbfile;
1230 ECalBackendFilePrivate *priv;
1231 MatchObjectData match_data;
1233 cbfile = E_CAL_BACKEND_FILE (backend);
1234 priv = cbfile->priv;
1236 d(g_message (G_STRLOC ": Getting object list (%s)", sexp));
1238 match_data.search_needed = TRUE;
1239 match_data.query = sexp;
1240 match_data.obj_list = NULL;
1241 match_data.backend = E_CAL_BACKEND (backend);
1242 match_data.default_zone = priv->default_zone;
1244 if (!strcmp (sexp, "#t"))
1245 match_data.search_needed = FALSE;
1247 match_data.obj_sexp = e_cal_backend_sexp_new (sexp);
1248 if (!match_data.obj_sexp)
1249 return GNOME_Evolution_Calendar_InvalidQuery;
1251 g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
1253 *objects = match_data.obj_list;
1255 return GNOME_Evolution_Calendar_Success;
1258 /* get_query handler for the file backend */
1260 e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
1262 ECalBackendFile *cbfile;
1263 ECalBackendFilePrivate *priv;
1264 MatchObjectData match_data;
1266 cbfile = E_CAL_BACKEND_FILE (backend);
1267 priv = cbfile->priv;
1269 d(g_message (G_STRLOC ": Starting query (%s)", e_data_cal_view_get_text (query)));
1271 /* try to match all currently existing objects */
1272 match_data.search_needed = TRUE;
1273 match_data.query = e_data_cal_view_get_text (query);
1274 match_data.obj_list = NULL;
1275 match_data.backend = backend;
1276 match_data.default_zone = priv->default_zone;
1278 if (!strcmp (match_data.query, "#t"))
1279 match_data.search_needed = FALSE;
1281 match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
1282 if (!match_data.obj_sexp) {
1283 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_InvalidQuery);
1287 g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
1289 /* notify listeners of all objects */
1290 if (match_data.obj_list) {
1291 e_data_cal_view_notify_objects_added (query, (const GList *) match_data.obj_list);
1294 g_list_foreach (match_data.obj_list, (GFunc) g_free, NULL);
1295 g_list_free (match_data.obj_list);
1298 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
1302 free_busy_instance (ECalComponent *comp,
1303 time_t instance_start,
1304 time_t instance_end,
1307 icalcomponent *vfb = data;
1309 icalparameter *param;
1310 struct icalperiodtype ipt;
1311 icaltimezone *utc_zone;
1313 utc_zone = icaltimezone_get_utc_timezone ();
1315 ipt.start = icaltime_from_timet_with_zone (instance_start, FALSE, utc_zone);
1316 ipt.end = icaltime_from_timet_with_zone (instance_end, FALSE, utc_zone);
1317 ipt.duration = icaldurationtype_null_duration ();
1319 /* add busy information to the vfb component */
1320 prop = icalproperty_new (ICAL_FREEBUSY_PROPERTY);
1321 icalproperty_set_freebusy (prop, ipt);
1323 param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
1324 icalproperty_add_parameter (prop, param);
1326 icalcomponent_add_property (vfb, prop);
1331 static icalcomponent *
1332 create_user_free_busy (ECalBackendFile *cbfile, const char *address, const char *cn,
1333 time_t start, time_t end)
1335 ECalBackendFilePrivate *priv;
1338 icaltimezone *utc_zone;
1339 ECalBackendSExp *obj_sexp;
1340 char *query, *iso_start, *iso_end;
1342 priv = cbfile->priv;
1344 /* create the (unique) VFREEBUSY object that we'll return */
1345 vfb = icalcomponent_new_vfreebusy ();
1346 if (address != NULL) {
1348 icalparameter *param;
1350 prop = icalproperty_new_organizer (address);
1351 if (prop != NULL && cn != NULL) {
1352 param = icalparameter_new_cn (cn);
1353 icalproperty_add_parameter (prop, param);
1356 icalcomponent_add_property (vfb, prop);
1358 utc_zone = icaltimezone_get_utc_timezone ();
1359 icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
1360 icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
1362 /* add all objects in the given interval */
1363 iso_start = isodate_from_time_t (start);
1364 iso_end = isodate_from_time_t (end);
1365 query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") (make-time \"%s\")",
1366 iso_start, iso_end);
1367 obj_sexp = e_cal_backend_sexp_new (query);
1375 for (l = priv->comp; l; l = l->next) {
1376 ECalComponent *comp = l->data;
1377 icalcomponent *icalcomp, *vcalendar_comp;
1380 icalcomp = e_cal_component_get_icalcomponent (comp);
1384 /* If the event is TRANSPARENT, skip it. */
1385 prop = icalcomponent_get_first_property (icalcomp,
1386 ICAL_TRANSP_PROPERTY);
1388 icalproperty_transp transp_val = icalproperty_get_transp (prop);
1389 if (transp_val == ICAL_TRANSP_TRANSPARENT ||
1390 transp_val == ICAL_TRANSP_TRANSPARENTNOCONFLICT)
1394 if (!e_cal_backend_sexp_match_comp (obj_sexp, l->data, E_CAL_BACKEND (cbfile)))
1397 vcalendar_comp = icalcomponent_get_parent (icalcomp);
1398 e_cal_recur_generate_instances (comp, start, end,
1403 priv->default_zone);
1409 /* Get_free_busy handler for the file backend */
1410 static ECalBackendSyncStatus
1411 e_cal_backend_file_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users,
1412 time_t start, time_t end, GList **freebusy)
1414 ECalBackendFile *cbfile;
1415 ECalBackendFilePrivate *priv;
1416 gchar *address, *name;
1421 cbfile = E_CAL_BACKEND_FILE (backend);
1422 priv = cbfile->priv;
1424 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1425 g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange);
1426 g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange);
1430 if (users == NULL) {
1431 if (e_cal_backend_mail_account_get_default (&address, &name)) {
1432 vfb = create_user_free_busy (cbfile, address, name, start, end);
1433 calobj = icalcomponent_as_ical_string (vfb);
1434 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1435 icalcomponent_free (vfb);
1440 for (l = users; l != NULL; l = l->next ) {
1442 if (e_cal_backend_mail_account_is_valid (address, &name)) {
1443 vfb = create_user_free_busy (cbfile, address, name, start, end);
1444 calobj = icalcomponent_as_ical_string (vfb);
1445 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1446 icalcomponent_free (vfb);
1452 return GNOME_Evolution_Calendar_Success;
1457 ECalBackendFile *backend;
1458 icalcomponent_kind kind;
1461 } ECalBackendFileComputeChangesData;
1464 e_cal_backend_file_compute_changes_foreach_key (const char *key, gpointer value, gpointer data)
1466 ECalBackendFileComputeChangesData *be_data = data;
1468 if (!lookup_component (be_data->backend, key)) {
1469 ECalComponent *comp;
1471 comp = e_cal_component_new ();
1472 if (be_data->kind == ICAL_VTODO_COMPONENT)
1473 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1475 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1477 e_cal_component_set_uid (comp, key);
1478 be_data->deletes = g_list_prepend (be_data->deletes, e_cal_component_get_as_string (comp));
1480 e_xmlhash_remove (be_data->ehash, key);
1484 static ECalBackendSyncStatus
1485 e_cal_backend_file_compute_changes (ECalBackendFile *cbfile, const char *change_id,
1486 GList **adds, GList **modifies, GList **deletes)
1488 ECalBackendFilePrivate *priv;
1491 ECalBackendFileComputeChangesData be_data;
1493 gchar *unescaped_uri;
1495 priv = cbfile->priv;
1497 /* FIXME Will this always work? */
1498 unescaped_uri = gnome_vfs_unescape_string (priv->uri, "");
1499 filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id);
1500 g_free (unescaped_uri);
1501 if (!(ehash = e_xmlhash_new (filename))) {
1503 return GNOME_Evolution_Calendar_OtherError;
1508 /* Calculate adds and modifies */
1509 for (i = priv->comp; i != NULL; i = i->next) {
1513 e_cal_component_get_uid (i->data, &uid);
1514 calobj = e_cal_component_get_as_string (i->data);
1516 g_assert (calobj != NULL);
1518 /* check what type of change has occurred, if any */
1519 switch (e_xmlhash_compare (ehash, uid, calobj)) {
1520 case E_XMLHASH_STATUS_SAME:
1522 case E_XMLHASH_STATUS_NOT_FOUND:
1523 *adds = g_list_prepend (*adds, g_strdup (calobj));
1524 e_xmlhash_add (ehash, uid, calobj);
1526 case E_XMLHASH_STATUS_DIFFERENT:
1527 *modifies = g_list_prepend (*modifies, g_strdup (calobj));
1528 e_xmlhash_add (ehash, uid, calobj);
1535 /* Calculate deletions */
1536 be_data.backend = cbfile;
1537 be_data.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbfile));
1538 be_data.deletes = NULL;
1539 be_data.ehash = ehash;
1541 e_xmlhash_foreach_key (ehash, (EXmlHashFunc)e_cal_backend_file_compute_changes_foreach_key, &be_data);
1543 *deletes = be_data.deletes;
1545 e_xmlhash_write (ehash);
1546 e_xmlhash_destroy (ehash);
1548 return GNOME_Evolution_Calendar_Success;
1551 /* Get_changes handler for the file backend */
1552 static ECalBackendSyncStatus
1553 e_cal_backend_file_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id,
1554 GList **adds, GList **modifies, GList **deletes)
1556 ECalBackendFile *cbfile;
1557 ECalBackendFilePrivate *priv;
1559 cbfile = E_CAL_BACKEND_FILE (backend);
1560 priv = cbfile->priv;
1562 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1563 g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1565 return e_cal_backend_file_compute_changes (cbfile, change_id, adds, modifies, deletes);
1568 /* Discard_alarm handler for the file backend */
1569 static ECalBackendSyncStatus
1570 e_cal_backend_file_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
1572 /* we just do nothing with the alarm */
1573 return GNOME_Evolution_Calendar_Success;
1576 static icaltimezone *
1577 e_cal_backend_file_internal_get_default_timezone (ECalBackend *backend)
1579 ECalBackendFile *cbfile;
1580 ECalBackendFilePrivate *priv;
1582 cbfile = E_CAL_BACKEND_FILE (backend);
1583 priv = cbfile->priv;
1585 g_return_val_if_fail (priv->icalcomp != NULL, NULL);
1587 return priv->default_zone;
1590 static icaltimezone *
1591 e_cal_backend_file_internal_get_timezone (ECalBackend *backend, const char *tzid)
1593 ECalBackendFile *cbfile;
1594 ECalBackendFilePrivate *priv;
1597 cbfile = E_CAL_BACKEND_FILE (backend);
1598 priv = cbfile->priv;
1600 g_return_val_if_fail (priv->icalcomp != NULL, NULL);
1602 if (!strcmp (tzid, "UTC"))
1603 zone = icaltimezone_get_utc_timezone ();
1605 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1607 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1614 sanitize_component (ECalBackendFile *cbfile, ECalComponent *comp)
1616 ECalComponentDateTime dt;
1617 icaltimezone *zone, *default_zone;
1619 /* Check dtstart, dtend and due's timezone, and convert it to local
1620 * default timezone if the timezone is not in our builtin timezone
1622 e_cal_component_get_dtstart (comp, &dt);
1623 if (dt.value && dt.tzid) {
1624 zone = e_cal_backend_file_internal_get_timezone ((ECalBackend *)cbfile, dt.tzid);
1626 default_zone = e_cal_backend_file_internal_get_default_timezone ((ECalBackend *)cbfile);
1627 g_free ((char *)dt.tzid);
1628 dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
1629 e_cal_component_set_dtstart (comp, &dt);
1632 e_cal_component_free_datetime (&dt);
1634 e_cal_component_get_dtend (comp, &dt);
1635 if (dt.value && dt.tzid) {
1636 zone = e_cal_backend_file_internal_get_timezone ((ECalBackend *)cbfile, dt.tzid);
1638 default_zone = e_cal_backend_file_internal_get_default_timezone ((ECalBackend *)cbfile);
1639 g_free ((char *)dt.tzid);
1640 dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
1641 e_cal_component_set_dtend (comp, &dt);
1644 e_cal_component_free_datetime (&dt);
1646 e_cal_component_get_due (comp, &dt);
1647 if (dt.value && dt.tzid) {
1648 zone = e_cal_backend_file_internal_get_timezone ((ECalBackend *)cbfile, dt.tzid);
1650 default_zone = e_cal_backend_file_internal_get_default_timezone ((ECalBackend *)cbfile);
1651 g_free ((char *)dt.tzid);
1652 dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
1653 e_cal_component_set_due (comp, &dt);
1656 e_cal_component_free_datetime (&dt);
1657 e_cal_component_abort_sequence (comp);
1662 static ECalBackendSyncStatus
1663 e_cal_backend_file_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
1665 ECalBackendFile *cbfile;
1666 ECalBackendFilePrivate *priv;
1667 icalcomponent *icalcomp;
1668 ECalComponent *comp;
1669 const char *comp_uid;
1670 struct icaltimetype current;
1672 cbfile = E_CAL_BACKEND_FILE (backend);
1673 priv = cbfile->priv;
1675 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1676 g_return_val_if_fail (*calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1678 /* Parse the icalendar text */
1679 icalcomp = icalparser_parse_string (*calobj);
1681 return GNOME_Evolution_Calendar_InvalidObject;
1683 /* Check kind with the parent */
1684 if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
1685 icalcomponent_free (icalcomp);
1686 return GNOME_Evolution_Calendar_InvalidObject;
1690 comp_uid = icalcomponent_get_uid (icalcomp);
1694 new_uid = e_cal_component_gen_uid ();
1696 icalcomponent_free (icalcomp);
1697 return GNOME_Evolution_Calendar_InvalidObject;
1700 icalcomponent_set_uid (icalcomp, new_uid);
1701 comp_uid = icalcomponent_get_uid (icalcomp);
1706 /* check the object is not in our cache */
1707 if (lookup_component (cbfile, comp_uid)) {
1708 icalcomponent_free (icalcomp);
1709 return GNOME_Evolution_Calendar_ObjectIdAlreadyExists;
1712 /* Create the cal component */
1713 comp = e_cal_component_new ();
1714 e_cal_component_set_icalcomponent (comp, icalcomp);
1716 /* Set the created and last modified times on the component */
1717 current = icaltime_from_timet (time (NULL), 0);
1718 e_cal_component_set_created (comp, ¤t);
1719 e_cal_component_set_last_modified (comp, ¤t);
1721 /* sanitize the component*/
1722 sanitize_component (cbfile, comp);
1724 /* Add the object */
1725 add_component (cbfile, comp, TRUE);
1730 /* Return the UID and the modified component */
1732 *uid = g_strdup (comp_uid);
1733 *calobj = e_cal_component_get_as_string (comp);
1735 return GNOME_Evolution_Calendar_Success;
1739 ECalBackendFile *cbfile;
1740 ECalBackendFileObject *obj_data;
1743 } RemoveRecurrenceData;
1746 remove_object_instance_cb (gpointer key, gpointer value, gpointer user_data)
1748 time_t fromtt, instancett;
1750 ECalComponent *instance = value;
1751 RemoveRecurrenceData *rrdata = user_data;
1753 fromtt = icaltime_as_timet (icaltime_from_string (rrdata->rid));
1754 instancett = icaltime_as_timet (get_rid_icaltime (instance));
1756 if (fromtt > 0 && instancett > 0) {
1757 if ((rrdata->mod == CALOBJ_MOD_THISANDPRIOR && instancett <= fromtt) ||
1758 (rrdata->mod == CALOBJ_MOD_THISANDFUTURE && instancett >= fromtt)) {
1759 /* remove the component from our data */
1760 icalcomponent_remove_component (rrdata->cbfile->priv->icalcomp,
1761 e_cal_component_get_icalcomponent (instance));
1762 rrdata->cbfile->priv->comp = g_list_remove (rrdata->cbfile->priv->comp, instance);
1764 rrdata->obj_data->recurrences_list = g_list_remove (rrdata->obj_data->recurrences_list, instance);
1766 /* update the set of categories */
1767 e_cal_component_get_categories_list (instance, &categories);
1768 e_cal_backend_unref_categories (E_CAL_BACKEND (rrdata->cbfile), categories);
1769 e_cal_component_free_categories_list (categories);
1778 static ECalBackendSyncStatus
1779 e_cal_backend_file_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
1780 CalObjModType mod, char **old_object)
1782 RemoveRecurrenceData rrdata;
1783 ECalBackendFile *cbfile;
1784 ECalBackendFilePrivate *priv;
1785 icalcomponent *icalcomp;
1786 const char *comp_uid, *rid;
1788 ECalComponent *comp, *recurrence;
1789 ECalBackendFileObject *obj_data;
1790 struct icaltimetype current;
1792 cbfile = E_CAL_BACKEND_FILE (backend);
1793 priv = cbfile->priv;
1795 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1796 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1798 /* Parse the icalendar text */
1799 icalcomp = icalparser_parse_string ((char *) calobj);
1801 return GNOME_Evolution_Calendar_InvalidObject;
1803 /* Check kind with the parent */
1804 if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
1805 icalcomponent_free (icalcomp);
1806 return GNOME_Evolution_Calendar_InvalidObject;
1810 comp_uid = icalcomponent_get_uid (icalcomp);
1812 /* Get the object from our cache */
1813 if (!(obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid))) {
1814 icalcomponent_free (icalcomp);
1815 return GNOME_Evolution_Calendar_ObjectNotFound;
1818 /* Create the cal component */
1819 comp = e_cal_component_new ();
1820 e_cal_component_set_icalcomponent (comp, icalcomp);
1822 /* Set the last modified time on the component */
1823 current = icaltime_from_timet (time (NULL), 0);
1824 e_cal_component_set_last_modified (comp, ¤t);
1826 /* sanitize the component*/
1827 sanitize_component (cbfile, comp);
1829 /* handle mod_type */
1831 case CALOBJ_MOD_THIS :
1832 rid = e_cal_component_get_recurid_as_string (comp);
1833 if (!rid || !*rid) {
1835 *old_object = e_cal_component_get_as_string (obj_data->full_object);
1837 /* replace only the full object */
1838 icalcomponent_remove_component (priv->icalcomp,
1839 e_cal_component_get_icalcomponent (obj_data->full_object));
1840 priv->comp = g_list_remove (priv->comp, obj_data->full_object);
1842 /* add the new object */
1843 g_object_unref (obj_data->full_object);
1844 obj_data->full_object = comp;
1846 icalcomponent_add_component (priv->icalcomp,
1847 e_cal_component_get_icalcomponent (obj_data->full_object));
1848 priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
1852 return GNOME_Evolution_Calendar_Success;
1855 if (g_hash_table_lookup_extended (obj_data->recurrences, rid, &real_rid, &recurrence)) {
1857 *old_object = e_cal_component_get_as_string (recurrence);
1859 /* remove the component from our data */
1860 icalcomponent_remove_component (priv->icalcomp,
1861 e_cal_component_get_icalcomponent (recurrence));
1862 priv->comp = g_list_remove (priv->comp, recurrence);
1863 obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
1864 g_hash_table_remove (obj_data->recurrences, rid);
1868 old = e_cal_component_get_as_string (obj_data->full_object);
1870 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
1871 get_rid_icaltime (comp),
1873 new = e_cal_component_get_as_string (comp);
1874 e_cal_backend_notify_object_modified (E_CAL_BACKEND (backend), old, new);
1883 /* add the detached instance */
1884 icalcomponent_set_recurrenceid (e_cal_component_get_icalcomponent (comp),
1885 icaltime_from_string (rid));
1886 g_hash_table_insert (obj_data->recurrences,
1887 g_strdup (e_cal_component_get_recurid_as_string (comp)),
1889 icalcomponent_add_component (priv->icalcomp,
1890 e_cal_component_get_icalcomponent (comp));
1891 priv->comp = g_list_append (priv->comp, comp);
1892 obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
1894 case CALOBJ_MOD_THISANDPRIOR :
1895 case CALOBJ_MOD_THISANDFUTURE :
1896 rid = e_cal_component_get_recurid_as_string (comp);
1897 if (!rid || !*rid) {
1899 *old_object = e_cal_component_get_as_string (obj_data->full_object);
1901 remove_component (cbfile, comp_uid, obj_data);
1903 /* Add the new object */
1904 add_component (cbfile, comp, TRUE);
1908 /* remove the component from our data, temporarily */
1909 icalcomponent_remove_component (priv->icalcomp,
1910 e_cal_component_get_icalcomponent (obj_data->full_object));
1911 priv->comp = g_list_remove (priv->comp, obj_data->full_object);
1913 /* now deal with the detached recurrence */
1914 if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
1915 &real_rid, &recurrence)) {
1917 *old_object = e_cal_component_get_as_string (recurrence);
1919 /* remove the component from our data */
1920 icalcomponent_remove_component (priv->icalcomp,
1921 e_cal_component_get_icalcomponent (recurrence));
1922 priv->comp = g_list_remove (priv->comp, recurrence);
1923 obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
1924 g_hash_table_remove (obj_data->recurrences, rid);
1927 *old_object = e_cal_component_get_as_string (obj_data->full_object);
1930 /* remove all affected recurrences */
1931 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
1932 icaltime_from_string (rid), mod);
1934 rrdata.cbfile = cbfile;
1935 rrdata.obj_data = obj_data;
1938 g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
1940 /* add the modified object to the beginning of the list,
1941 so that it's always before any detached instance we
1943 icalcomponent_add_component (priv->icalcomp,
1944 e_cal_component_get_icalcomponent (obj_data->full_object));
1945 priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
1947 /* add the new detached recurrence */
1948 g_hash_table_insert (obj_data->recurrences,
1949 g_strdup (e_cal_component_get_recurid_as_string (comp)),
1951 icalcomponent_add_component (priv->icalcomp,
1952 e_cal_component_get_icalcomponent (comp));
1953 priv->comp = g_list_append (priv->comp, comp);
1954 obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
1956 case CALOBJ_MOD_ALL :
1957 /* in this case, we blow away all recurrences, and start over
1958 with a clean component */
1959 /* Remove the old version */
1961 *old_object = e_cal_component_get_as_string (obj_data->full_object);
1963 remove_component (cbfile, comp_uid, obj_data);
1965 /* Add the new object */
1966 add_component (cbfile, comp, TRUE);
1972 return GNOME_Evolution_Calendar_Success;
1976 remove_instance (ECalBackendFile *cbfile, ECalBackendFileObject *obj_data, const char *rid)
1979 ECalComponent *comp;
1985 if (g_hash_table_lookup_extended (obj_data->recurrences, rid, &hash_rid, &comp)) {
1986 /* update the set of categories */
1987 e_cal_component_get_categories_list (comp, &categories);
1988 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
1989 e_cal_component_free_categories_list (categories);
1991 /* remove the component from our data */
1992 icalcomponent_remove_component (cbfile->priv->icalcomp,
1993 e_cal_component_get_icalcomponent (comp));
1994 cbfile->priv->comp = g_list_remove (cbfile->priv->comp, comp);
1995 obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, comp);
1996 g_hash_table_remove (obj_data->recurrences, rid);
2001 /* remove the component from our data, temporarily */
2002 icalcomponent_remove_component (cbfile->priv->icalcomp,
2003 e_cal_component_get_icalcomponent (obj_data->full_object));
2004 cbfile->priv->comp = g_list_remove (cbfile->priv->comp, obj_data->full_object);
2006 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
2007 icaltime_from_string (rid), CALOBJ_MOD_THIS);
2009 /* add the modified object to the beginning of the list,
2010 so that it's always before any detached instance we
2012 icalcomponent_add_component (cbfile->priv->icalcomp,
2013 e_cal_component_get_icalcomponent (obj_data->full_object));
2014 cbfile->priv->comp = g_list_prepend (cbfile->priv->comp, obj_data->full_object);
2017 /* Remove_object handler for the file backend */
2018 static ECalBackendSyncStatus
2019 e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal,
2020 const char *uid, const char *rid,
2021 CalObjModType mod, char **old_object,
2024 ECalBackendFile *cbfile;
2025 ECalBackendFilePrivate *priv;
2026 ECalBackendFileObject *obj_data;
2027 ECalComponent *comp;
2028 RemoveRecurrenceData rrdata;
2030 cbfile = E_CAL_BACKEND_FILE (backend);
2031 priv = cbfile->priv;
2033 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
2034 g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
2036 *old_object = *object = NULL;
2038 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
2040 return GNOME_Evolution_Calendar_ObjectNotFound;
2042 comp = obj_data->full_object;
2045 case CALOBJ_MOD_ALL :
2047 *old_object = e_cal_component_get_as_string (comp);
2051 if (g_hash_table_lookup_extended (obj_data->recurrences, rid, &real_rid, &comp))
2052 *old_object = e_cal_component_get_as_string (comp);
2055 remove_component (cbfile, uid, obj_data);
2059 case CALOBJ_MOD_THIS :
2060 *old_object = e_cal_component_get_as_string (comp);
2061 if (!rid || !*rid) {
2062 remove_component (cbfile, uid, obj_data);
2065 remove_instance (cbfile, obj_data, rid);
2066 *object = e_cal_component_get_as_string (obj_data->full_object);
2069 case CALOBJ_MOD_THISANDPRIOR :
2070 case CALOBJ_MOD_THISANDFUTURE :
2072 return GNOME_Evolution_Calendar_ObjectNotFound;
2074 *old_object = e_cal_component_get_as_string (comp);
2076 /* remove the component from our data, temporarily */
2077 icalcomponent_remove_component (priv->icalcomp,
2078 e_cal_component_get_icalcomponent (comp));
2079 priv->comp = g_list_remove (priv->comp, comp);
2081 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (comp),
2082 icaltime_from_string (rid), mod);
2084 /* now remove all detached instances */
2085 rrdata.cbfile = cbfile;
2086 rrdata.obj_data = obj_data;
2089 g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
2091 /* add the modified object to the beginning of the list,
2092 so that it's always before any detached instance we
2094 priv->comp = g_list_prepend (priv->comp, comp);
2096 *object = e_cal_component_get_as_string (obj_data->full_object);
2102 return GNOME_Evolution_Calendar_Success;
2106 cancel_received_object (ECalBackendFile *cbfile, icalcomponent *icalcomp)
2108 ECalBackendFileObject *obj_data;
2109 ECalBackendFilePrivate *priv;
2111 priv = cbfile->priv;
2113 /* Find the old version of the component. */
2114 obj_data = g_hash_table_lookup (priv->comp_uid_hash, icalcomponent_get_uid (icalcomp));
2119 remove_component (cbfile, icalcomponent_get_uid (icalcomp), obj_data);
2128 } ECalBackendFileTzidData;
2131 check_tzids (icalparameter *param, void *data)
2133 ECalBackendFileTzidData *tzdata = data;
2136 tzid = icalparameter_get_tzid (param);
2137 if (!tzid || g_hash_table_lookup (tzdata->zones, tzid))
2138 tzdata->found = FALSE;
2142 fetch_attachments (ECalBackendSync *backend, ECalComponent *comp)
2144 GSList *attach_list = NULL, *new_attach_list = NULL;
2146 char *attach_store, *filename, *file_contents;
2147 char *dest_url, *dest_file;
2155 e_cal_component_get_attachment_list (comp, &attach_list);
2156 e_cal_component_get_uid (comp, &uid);
2157 /*FIXME get the uri rather than computing the path */
2158 attach_store = g_strconcat (g_get_home_dir (), "/",
2159 ".evolution/calendar/local/system", NULL);
2161 for (l = attach_list; l ; l = l->next) {
2162 char *sfname = (char *)l->data;
2164 filename = g_strrstr (sfname, "/") + 1;
2166 // open the file using the data
2167 fd = open (sfname, O_RDONLY);
2169 /* TODO handle error conditions */
2170 g_message ("DEBUG: could not open the file descriptor\n");
2173 if (fstat (fd, &sb) == -1) {
2174 /* TODO handle error conditions */
2175 g_message ("DEBUG: could not fstat the attachment file\n");
2180 file_contents = g_malloc (len + 1);
2182 while (len_read < len) {
2183 int c = read (fd, buf, sizeof (buf));
2188 memcpy (&file_contents[len_read], buf, c);
2191 file_contents [len_read] = 0;
2194 dest_file = g_strconcat (attach_store, "/", uid, "-",
2196 fd = open (dest_file, O_RDWR|O_CREAT|O_TRUNC, 0600);
2198 /* TODO handle error conditions */
2199 g_message ("DEBUG: could not serialize attachments\n");
2202 if (write (fd, file_contents, len_read) == -1) {
2203 /* TODO handle error condition */
2204 g_message ("DEBUG: attachment write failed.\n");
2207 dest_url = g_strconcat ("file:///", dest_file, NULL);
2208 new_attach_list = g_slist_append (new_attach_list, dest_url);
2211 g_free (attach_store);
2212 e_cal_component_set_attachment_list (comp, new_attach_list);
2215 /* Update_objects handler for the file backend. */
2216 static ECalBackendSyncStatus
2217 e_cal_backend_file_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
2219 ECalBackendFile *cbfile;
2220 ECalBackendFilePrivate *priv;
2221 icalcomponent *toplevel_comp, *icalcomp = NULL;
2222 icalcomponent_kind kind;
2223 icalproperty_method toplevel_method, method;
2224 icalcomponent *subcomp;
2225 GList *comps, *del_comps, *l;
2226 ECalComponent *comp;
2227 struct icaltimetype current;
2228 ECalBackendFileTzidData tzdata;
2229 ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success;
2231 cbfile = E_CAL_BACKEND_FILE (backend);
2232 priv = cbfile->priv;
2234 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_InvalidObject);
2235 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
2237 /* Pull the component from the string and ensure that it is sane */
2238 toplevel_comp = icalparser_parse_string ((char *) calobj);
2240 return GNOME_Evolution_Calendar_InvalidObject;
2242 kind = icalcomponent_isa (toplevel_comp);
2243 if (kind != ICAL_VCALENDAR_COMPONENT) {
2244 /* If its not a VCALENDAR, make it one to simplify below */
2245 icalcomp = toplevel_comp;
2246 toplevel_comp = e_cal_util_new_top_level ();
2247 if (icalcomponent_get_method (icalcomp) == ICAL_METHOD_CANCEL)
2248 icalcomponent_set_method (toplevel_comp, ICAL_METHOD_CANCEL);
2250 icalcomponent_set_method (toplevel_comp, ICAL_METHOD_PUBLISH);
2251 icalcomponent_add_component (toplevel_comp, icalcomp);
2253 if (!icalcomponent_get_first_property (toplevel_comp, ICAL_METHOD_PROPERTY))
2254 icalcomponent_set_method (toplevel_comp, ICAL_METHOD_PUBLISH);
2257 toplevel_method = icalcomponent_get_method (toplevel_comp);
2259 /* Build a list of timezones so we can make sure all the objects have valid info */
2260 tzdata.zones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2262 subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_VTIMEZONE_COMPONENT);
2266 zone = icaltimezone_new ();
2267 if (icaltimezone_set_component (zone, subcomp))
2268 g_hash_table_insert (tzdata.zones, g_strdup (icaltimezone_get_tzid (zone)), NULL);
2270 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_VTIMEZONE_COMPONENT);
2273 /* First we make sure all the components are usuable */
2274 comps = del_comps = NULL;
2275 kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
2277 subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_ANY_COMPONENT);
2279 icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
2281 if (child_kind != kind) {
2282 /* remove the component from the toplevel VCALENDAR */
2283 if (child_kind != ICAL_VTIMEZONE_COMPONENT)
2284 del_comps = g_list_prepend (del_comps, subcomp);
2286 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_ANY_COMPONENT);
2290 tzdata.found = TRUE;
2291 icalcomponent_foreach_tzid (subcomp, check_tzids, &tzdata);
2293 if (!tzdata.found) {
2294 status = GNOME_Evolution_Calendar_InvalidObject;
2298 if (!icalcomponent_get_uid (subcomp)) {
2299 status = GNOME_Evolution_Calendar_InvalidObject;
2303 comps = g_list_prepend (comps, subcomp);
2304 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_ANY_COMPONENT);
2307 /* Now we manipulate the components we care about */
2308 for (l = comps; l; l = l->next) {
2309 const char *uid, *rid;
2310 char *object, *old_object;
2311 ECalBackendFileObject *obj_data;
2315 /* Create the cal component */
2316 comp = e_cal_component_new ();
2317 e_cal_component_set_icalcomponent (comp, subcomp);
2319 /* Set the created and last modified times on the component */
2320 current = icaltime_from_timet (time (NULL), 0);
2321 e_cal_component_set_created (comp, ¤t);
2322 e_cal_component_set_last_modified (comp, ¤t);
2324 e_cal_component_get_uid (comp, &uid);
2325 rid = e_cal_component_get_recurid_as_string (comp);
2327 if (icalcomponent_get_first_property (subcomp, ICAL_METHOD_PROPERTY))
2328 method = icalcomponent_get_method (subcomp);
2330 method = toplevel_method;
2333 case ICAL_METHOD_PUBLISH:
2334 case ICAL_METHOD_REQUEST:
2335 case ICAL_METHOD_REPLY:
2336 /* handle attachments */
2337 if (e_cal_component_has_attachments (comp))
2338 fetch_attachments (backend, comp);
2339 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
2341 old_object = e_cal_component_get_as_string (obj_data->full_object);
2343 remove_instance (cbfile, obj_data, rid);
2345 remove_component (cbfile, uid, obj_data);
2346 add_component (cbfile, comp, FALSE);
2348 object = e_cal_component_get_as_string (comp);
2349 e_cal_backend_notify_object_modified (E_CAL_BACKEND (backend), old_object, object);
2351 g_free (old_object);
2353 add_component (cbfile, comp, FALSE);
2355 object = e_cal_component_get_as_string (comp);
2356 e_cal_backend_notify_object_created (E_CAL_BACKEND (backend), object);
2360 case ICAL_METHOD_ADD:
2361 /* FIXME This should be doable once all the recurid stuff is done */
2362 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2365 case ICAL_METHOD_COUNTER:
2366 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2369 case ICAL_METHOD_DECLINECOUNTER:
2370 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2373 case ICAL_METHOD_CANCEL:
2374 if (cancel_received_object (cbfile, subcomp)) {
2375 object = (char *) icalcomponent_as_ical_string (subcomp);
2376 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
2378 old_object = e_cal_component_get_as_string (obj_data->full_object);
2382 e_cal_backend_notify_object_removed (E_CAL_BACKEND (backend), uid, old_object, object);
2384 /* remove the component from the toplevel VCALENDAR */
2385 icalcomponent_remove_component (toplevel_comp, subcomp);
2386 icalcomponent_free (subcomp);
2388 g_free (old_object);
2392 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2397 g_list_free (comps);
2399 /* Now we remove the components we don't care about */
2400 for (l = del_comps; l; l = l->next) {
2403 icalcomponent_remove_component (toplevel_comp, subcomp);
2404 icalcomponent_free (subcomp);
2407 g_list_free (del_comps);
2409 /* Merge the iCalendar components with our existing VCALENDAR,
2410 resolving any conflicting TZIDs. */
2411 icalcomponent_merge_component (priv->icalcomp, toplevel_comp);
2416 g_hash_table_destroy (tzdata.zones);
2421 static ECalBackendSyncStatus
2422 e_cal_backend_file_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj, GList **users,
2423 char **modified_calobj)
2426 *modified_calobj = g_strdup (calobj);
2428 return GNOME_Evolution_Calendar_Success;
2431 /* Object initialization function for the file backend */
2433 e_cal_backend_file_init (ECalBackendFile *cbfile, ECalBackendFileClass *class)
2435 ECalBackendFilePrivate *priv;
2437 priv = g_new0 (ECalBackendFilePrivate, 1);
2438 cbfile->priv = priv;
2441 priv->file_name = g_strdup ("calendar.ics");
2442 priv->read_only = FALSE;
2443 priv->is_dirty = FALSE;
2444 priv->dirty_idle_id = 0;
2445 priv->icalcomp = NULL;
2446 priv->comp_uid_hash = NULL;
2449 /* The timezone defaults to UTC. */
2450 priv->default_zone = icaltimezone_get_utc_timezone ();
2453 /* Class initialization function for the file backend */
2455 e_cal_backend_file_class_init (ECalBackendFileClass *class)
2457 GObjectClass *object_class;
2458 ECalBackendClass *backend_class;
2459 ECalBackendSyncClass *sync_class;
2461 object_class = (GObjectClass *) class;
2462 backend_class = (ECalBackendClass *) class;
2463 sync_class = (ECalBackendSyncClass *) class;
2465 parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
2467 object_class->dispose = e_cal_backend_file_dispose;
2468 object_class->finalize = e_cal_backend_file_finalize;
2470 sync_class->is_read_only_sync = e_cal_backend_file_is_read_only;
2471 sync_class->get_cal_address_sync = e_cal_backend_file_get_cal_address;
2472 sync_class->get_alarm_email_address_sync = e_cal_backend_file_get_alarm_email_address;
2473 sync_class->get_ldap_attribute_sync = e_cal_backend_file_get_ldap_attribute;
2474 sync_class->get_static_capabilities_sync = e_cal_backend_file_get_static_capabilities;
2475 sync_class->open_sync = e_cal_backend_file_open;
2476 sync_class->remove_sync = e_cal_backend_file_remove;
2477 sync_class->create_object_sync = e_cal_backend_file_create_object;
2478 sync_class->modify_object_sync = e_cal_backend_file_modify_object;
2479 sync_class->remove_object_sync = e_cal_backend_file_remove_object;
2480 sync_class->discard_alarm_sync = e_cal_backend_file_discard_alarm;
2481 sync_class->receive_objects_sync = e_cal_backend_file_receive_objects;
2482 sync_class->send_objects_sync = e_cal_backend_file_send_objects;
2483 sync_class->get_default_object_sync = e_cal_backend_file_get_default_object;
2484 sync_class->get_object_sync = e_cal_backend_file_get_object;
2485 sync_class->get_object_list_sync = e_cal_backend_file_get_object_list;
2486 sync_class->get_timezone_sync = e_cal_backend_file_get_timezone;
2487 sync_class->add_timezone_sync = e_cal_backend_file_add_timezone;
2488 sync_class->set_default_timezone_sync = e_cal_backend_file_set_default_timezone;
2489 sync_class->get_freebusy_sync = e_cal_backend_file_get_free_busy;
2490 sync_class->get_changes_sync = e_cal_backend_file_get_changes;
2492 backend_class->is_loaded = e_cal_backend_file_is_loaded;
2493 backend_class->start_query = e_cal_backend_file_start_query;
2494 backend_class->get_mode = e_cal_backend_file_get_mode;
2495 backend_class->set_mode = e_cal_backend_file_set_mode;
2497 backend_class->internal_get_default_timezone = e_cal_backend_file_internal_get_default_timezone;
2498 backend_class->internal_get_timezone = e_cal_backend_file_internal_get_timezone;
2503 * e_cal_backend_file_get_type:
2506 * Registers the #ECalBackendFile class if necessary, and returns the type ID
2509 * Return value: The type ID of the #ECalBackendFile class.
2512 e_cal_backend_file_get_type (void)
2514 static GType e_cal_backend_file_type = 0;
2516 if (!e_cal_backend_file_type) {
2517 static GTypeInfo info = {
2518 sizeof (ECalBackendFileClass),
2519 (GBaseInitFunc) NULL,
2520 (GBaseFinalizeFunc) NULL,
2521 (GClassInitFunc) e_cal_backend_file_class_init,
2523 sizeof (ECalBackendFile),
2525 (GInstanceInitFunc) e_cal_backend_file_init
2527 e_cal_backend_file_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
2528 "ECalBackendFile", &info, 0);
2531 return e_cal_backend_file_type;
2535 e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, const char *file_name)
2537 ECalBackendFilePrivate *priv;
2539 g_return_if_fail (cbfile != NULL);
2540 g_return_if_fail (E_IS_CAL_BACKEND_FILE (cbfile));
2541 g_return_if_fail (file_name != NULL);
2543 priv = cbfile->priv;
2545 if (priv->file_name)
2546 g_free (priv->file_name);
2548 priv->file_name = g_strdup (file_name);
2552 e_cal_backend_file_get_file_name (ECalBackendFile *cbfile)
2554 ECalBackendFilePrivate *priv;
2556 g_return_val_if_fail (cbfile != NULL, NULL);
2557 g_return_val_if_fail (E_IS_CAL_BACKEND_FILE (cbfile), NULL);
2559 priv = cbfile->priv;
2561 return priv->file_name;
2564 ECalBackendSyncStatus
2565 e_cal_backend_file_reload (ECalBackendFile *cbfile)
2567 ECalBackendFilePrivate *priv;
2569 ECalBackendSyncStatus status;
2571 priv = cbfile->priv;
2573 str_uri = get_uri_string (E_CAL_BACKEND (cbfile));
2575 return GNOME_Evolution_Calendar_OtherError;
2577 if (access (str_uri, R_OK) == 0) {
2578 status = reload_cal (cbfile, str_uri);
2579 if (access (str_uri, W_OK) != 0)
2580 priv->read_only = TRUE;
2582 status = GNOME_Evolution_Calendar_NoSuchCal;