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 <bonobo/bonobo-exception.h>
29 #include <bonobo/bonobo-moniker-util.h>
30 #include <libgnome/gnome-i18n.h>
31 #include <libgnomevfs/gnome-vfs.h>
32 #include <libedataserver/e-util.h>
33 #include <libedataserver/e-xml-hash-utils.h>
34 #include <libecal/e-cal-recur.h>
35 #include <libecal/e-cal-time-util.h>
36 #include <libecal/e-cal-util.h>
37 #include <libedata-cal/e-cal-backend-util.h>
38 #include <libedata-cal/e-cal-backend-sexp.h>
39 #include "e-cal-backend-file-events.h"
43 /* Placeholder for each component and its recurrences */
45 ECalComponent *full_object;
46 GHashTable *recurrences;
47 GList *recurrences_list;
48 } ECalBackendFileObject;
50 /* Private part of the ECalBackendFile structure */
51 struct _ECalBackendFilePrivate {
52 /* URI where the calendar data is stored */
55 /* Filename in the dir */
59 /* Toplevel VCALENDAR component */
60 icalcomponent *icalcomp;
62 /* All the objects in the calendar, hashed by UID. The
63 * hash key *is* the uid returned by cal_component_get_uid(); it is not
64 * copied, so don't free it when you remove an object from the hash
65 * table. Each item in the hash table is a ECalBackendFileObject.
67 GHashTable *comp_uid_hash;
71 /* The calendar's default timezone, used for resolving DATE and
72 floating DATE-TIME values. */
73 icaltimezone *default_zone;
75 /* The list of live queries */
83 static void e_cal_backend_file_dispose (GObject *object);
84 static void e_cal_backend_file_finalize (GObject *object);
86 static ECalBackendSyncClass *parent_class;
90 /* g_hash_table_foreach() callback to destroy recurrences in the hash table */
92 free_recurrence (gpointer key, gpointer value, gpointer data)
95 ECalComponent *comp = value;
98 g_object_unref (comp);
101 /* g_hash_table_foreach() callback to destroy a ECalBackendFileObject */
103 free_object (gpointer key, gpointer value, gpointer data)
105 ECalBackendFileObject *obj_data = value;
107 g_object_unref (obj_data->full_object);
108 g_hash_table_foreach (obj_data->recurrences, (GHFunc) free_recurrence, NULL);
109 g_hash_table_destroy (obj_data->recurrences);
110 g_list_free (obj_data->recurrences_list);
115 /* Saves the calendar data */
117 save (ECalBackendFile *cbfile)
119 ECalBackendFilePrivate *priv;
120 GnomeVFSURI *uri, *backup_uri;
121 GnomeVFSHandle *handle = NULL;
122 GnomeVFSResult result = GNOME_VFS_ERROR_BAD_FILE;
123 GnomeVFSFileSize out;
124 gchar *tmp, *backup_uristr;
128 g_assert (priv->uri != NULL);
129 g_assert (priv->icalcomp != NULL);
131 uri = gnome_vfs_uri_new (priv->uri);
133 goto error_malformed_uri;
135 /* save calendar to backup file */
136 tmp = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
138 gnome_vfs_uri_unref (uri);
139 goto error_malformed_uri;
142 backup_uristr = g_strconcat (tmp, "~", NULL);
143 backup_uri = gnome_vfs_uri_new (backup_uristr);
146 g_free (backup_uristr);
149 gnome_vfs_uri_unref (uri);
150 goto error_malformed_uri;
153 result = gnome_vfs_create_uri (&handle, backup_uri,
154 GNOME_VFS_OPEN_WRITE,
156 if (result != GNOME_VFS_OK) {
157 gnome_vfs_uri_unref (uri);
158 gnome_vfs_uri_unref (backup_uri);
162 buf = icalcomponent_as_ical_string (priv->icalcomp);
163 result = gnome_vfs_write (handle, buf, strlen (buf) * sizeof (char), &out);
164 gnome_vfs_close (handle);
165 if (result != GNOME_VFS_OK) {
166 gnome_vfs_uri_unref (uri);
167 gnome_vfs_uri_unref (backup_uri);
171 /* now copy the temporary file to the real file */
172 result = gnome_vfs_move_uri (backup_uri, uri, TRUE);
174 gnome_vfs_uri_unref (uri);
175 gnome_vfs_uri_unref (backup_uri);
176 if (result != GNOME_VFS_OK)
182 e_cal_backend_notify_error (E_CAL_BACKEND (cbfile),
183 _("Can't save calendar data: Malformed URI."));
187 e_cal_backend_notify_error (E_CAL_BACKEND (cbfile), gnome_vfs_result_to_string (result));
192 free_calendar_components (GHashTable *comp_uid_hash, icalcomponent *top_icomp)
195 g_hash_table_foreach (comp_uid_hash, (GHFunc) free_object, NULL);
196 g_hash_table_destroy (comp_uid_hash);
200 icalcomponent_free (top_icomp);
205 free_calendar_data (ECalBackendFile *cbfile)
207 ECalBackendFilePrivate *priv;
211 free_calendar_components (priv->comp_uid_hash, priv->icalcomp);
212 priv->comp_uid_hash = NULL;
213 priv->icalcomp = NULL;
215 g_list_free (priv->comp);
219 /* Dispose handler for the file backend */
221 e_cal_backend_file_dispose (GObject *object)
223 ECalBackendFile *cbfile;
224 ECalBackendFilePrivate *priv;
226 cbfile = E_CAL_BACKEND_FILE (object);
229 /* Save if necessary */
231 free_calendar_data (cbfile);
233 if (G_OBJECT_CLASS (parent_class)->dispose)
234 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
237 /* Finalize handler for the file backend */
239 e_cal_backend_file_finalize (GObject *object)
241 ECalBackendFile *cbfile;
242 ECalBackendFilePrivate *priv;
244 g_return_if_fail (object != NULL);
245 g_return_if_fail (E_IS_CAL_BACKEND_FILE (object));
247 cbfile = E_CAL_BACKEND_FILE (object);
260 if (G_OBJECT_CLASS (parent_class)->finalize)
261 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
266 /* Looks up a component by its UID on the backend's component hash table */
267 static ECalComponent *
268 lookup_component (ECalBackendFile *cbfile, const char *uid)
270 ECalBackendFilePrivate *priv;
271 ECalBackendFileObject *obj_data;
275 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
276 return obj_data ? obj_data->full_object : NULL;
281 /* Calendar backend methods */
283 /* Is_read_only handler for the file backend */
284 static ECalBackendSyncStatus
285 e_cal_backend_file_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
287 ECalBackendFile *cbfile = (ECalBackendFile *) backend;
289 *read_only = cbfile->priv->read_only;
291 return GNOME_Evolution_Calendar_Success;
294 /* Get_email_address handler for the file backend */
295 static ECalBackendSyncStatus
296 e_cal_backend_file_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
298 /* A file backend has no particular email address associated
299 * with it (although that would be a useful feature some day).
303 return GNOME_Evolution_Calendar_Success;
306 static ECalBackendSyncStatus
307 e_cal_backend_file_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
311 return GNOME_Evolution_Calendar_Success;
314 static ECalBackendSyncStatus
315 e_cal_backend_file_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
317 /* A file backend has no particular email address associated
318 * with it (although that would be a useful feature some day).
322 return GNOME_Evolution_Calendar_Success;
325 static ECalBackendSyncStatus
326 e_cal_backend_file_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
328 *capabilities = g_strdup (CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
330 return GNOME_Evolution_Calendar_Success;
333 /* function to resolve timezones */
334 static icaltimezone *
335 resolve_tzid (const char *tzid, gpointer user_data)
337 icalcomponent *vcalendar_comp = user_data;
339 if (!tzid || !tzid[0])
341 else if (!strcmp (tzid, "UTC"))
342 return icaltimezone_get_utc_timezone ();
344 return icalcomponent_get_timezone (vcalendar_comp, tzid);
347 /* Checks if the specified component has a duplicated UID and if so changes it */
349 check_dup_uid (ECalBackendFile *cbfile, ECalComponent *comp)
351 ECalBackendFilePrivate *priv;
352 ECalBackendFileObject *obj_data;
358 e_cal_component_get_uid (comp, &uid);
360 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
362 return; /* Everything is fine */
364 d(g_message (G_STRLOC ": Got object with duplicated UID `%s', changing it...", uid));
366 new_uid = e_cal_component_gen_uid ();
367 e_cal_component_set_uid (comp, new_uid);
370 /* FIXME: I think we need to reset the SEQUENCE property and reset the
371 * CREATED/DTSTAMP/LAST-MODIFIED.
377 static struct icaltimetype
378 get_rid_icaltime (ECalComponent *comp)
380 ECalComponentRange range;
381 struct icaltimetype tt;
383 e_cal_component_get_recurid (comp, &range);
384 if (!range.datetime.value)
385 return icaltime_null_time ();
386 tt = *range.datetime.value;
387 e_cal_component_free_range (&range);
392 /* Tries to add an icalcomponent to the file backend. We only store the objects
393 * of the types we support; all others just remain in the toplevel component so
394 * that we don't lose them.
397 add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_toplevel)
399 ECalBackendFilePrivate *priv;
400 ECalBackendFileObject *obj_data;
406 e_cal_component_get_uid (comp, &uid);
407 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
408 if (e_cal_component_is_instance (comp)) {
411 rid = e_cal_component_get_recurid_as_string (comp);
413 if (g_hash_table_lookup (obj_data->recurrences, rid)) {
414 g_warning (G_STRLOC ": Tried to add an already existing recurrence");
418 obj_data = g_new0 (ECalBackendFileObject, 1);
419 obj_data->full_object = NULL;
420 obj_data->recurrences = g_hash_table_new (g_str_hash, g_str_equal);
421 g_hash_table_insert (priv->comp_uid_hash, (gpointer) uid, obj_data);
424 g_hash_table_insert (obj_data->recurrences, g_strdup (rid), comp);
425 obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
427 /* Ensure that the UID is unique; some broken implementations spit
428 * components with duplicated UIDs.
430 check_dup_uid (cbfile, comp);
433 if (obj_data->full_object) {
434 g_warning (G_STRLOC ": Tried to add an already existing object");
438 obj_data->full_object = comp;
440 obj_data = g_new0 (ECalBackendFileObject, 1);
441 obj_data->full_object = comp;
442 obj_data->recurrences = g_hash_table_new (g_str_hash, g_str_equal);
444 g_hash_table_insert (priv->comp_uid_hash, (gpointer) uid, obj_data);
448 priv->comp = g_list_prepend (priv->comp, comp);
450 /* Put the object in the toplevel component if required */
452 if (add_to_toplevel) {
453 icalcomponent *icalcomp;
455 icalcomp = e_cal_component_get_icalcomponent (comp);
456 g_assert (icalcomp != NULL);
458 icalcomponent_add_component (priv->icalcomp, icalcomp);
461 /* Update the set of categories */
462 e_cal_component_get_categories_list (comp, &categories);
463 e_cal_backend_ref_categories (E_CAL_BACKEND (cbfile), categories);
464 e_cal_component_free_categories_list (categories);
467 /* g_hash_table_foreach() callback to remove recurrences from the calendar */
469 remove_recurrence_cb (gpointer key, gpointer value, gpointer data)
473 icalcomponent *icalcomp;
474 ECalBackendFilePrivate *priv;
475 ECalComponent *comp = value;
476 ECalBackendFile *cbfile = data;
480 /* remove the recurrence from the top-level calendar */
481 icalcomp = e_cal_component_get_icalcomponent (comp);
482 g_assert (icalcomp != NULL);
484 icalcomponent_remove_component (priv->icalcomp, icalcomp);
486 /* remove it from our mapping */
487 l = g_list_find (priv->comp, comp);
488 priv->comp = g_list_delete_link (priv->comp, l);
490 /* update the set of categories */
491 e_cal_component_get_categories_list (comp, &categories);
492 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
493 e_cal_component_free_categories_list (categories);
496 /* Removes a component from the backend's hash and lists. Does not perform
497 * notification on the clients. Also removes the component from the toplevel
501 remove_component (ECalBackendFile *cbfile, ECalComponent *comp)
503 ECalBackendFilePrivate *priv;
504 icalcomponent *icalcomp;
508 ECalBackendFileObject *obj_data;
512 /* Remove the icalcomp from the toplevel */
514 icalcomp = e_cal_component_get_icalcomponent (comp);
515 g_assert (icalcomp != NULL);
517 icalcomponent_remove_component (priv->icalcomp, icalcomp);
519 /* Remove it from our mapping */
521 e_cal_component_get_uid (comp, &uid);
522 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
526 g_hash_table_remove (priv->comp_uid_hash, uid);
528 l = g_list_find (priv->comp, comp);
529 g_assert (l != NULL);
530 priv->comp = g_list_delete_link (priv->comp, l);
532 /* remove the recurrences also */
533 g_hash_table_foreach (obj_data->recurrences, (GHFunc) remove_recurrence_cb, cbfile);
535 /* Update the set of categories */
536 e_cal_component_get_categories_list (comp, &categories);
537 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
538 e_cal_component_free_categories_list (categories);
540 free_object ((gpointer) uid, (gpointer) obj_data, NULL);
543 /* Scans the toplevel VCALENDAR component and stores the objects it finds */
545 scan_vcalendar (ECalBackendFile *cbfile)
547 ECalBackendFilePrivate *priv;
551 g_assert (priv->icalcomp != NULL);
552 g_assert (priv->comp_uid_hash != NULL);
554 for (iter = icalcomponent_begin_component (priv->icalcomp, ICAL_ANY_COMPONENT);
555 icalcompiter_deref (&iter) != NULL;
556 icalcompiter_next (&iter)) {
557 icalcomponent *icalcomp;
558 icalcomponent_kind kind;
561 icalcomp = icalcompiter_deref (&iter);
563 kind = icalcomponent_isa (icalcomp);
565 if (!(kind == ICAL_VEVENT_COMPONENT
566 || kind == ICAL_VTODO_COMPONENT
567 || kind == ICAL_VJOURNAL_COMPONENT))
570 comp = e_cal_component_new ();
572 if (!e_cal_component_set_icalcomponent (comp, icalcomp))
575 add_component (cbfile, comp, FALSE);
580 get_uri_string_for_gnome_vfs (ECalBackend *backend)
582 ECalBackendFile *cbfile;
583 ECalBackendFilePrivate *priv;
584 const char *master_uri;
585 char *full_uri, *str_uri;
588 cbfile = E_CAL_BACKEND_FILE (backend);
591 master_uri = e_cal_backend_get_uri (backend);
593 /* FIXME Check the error conditions a little more elegantly here */
594 if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) {
595 g_warning (G_STRLOC ": Existing file name %s", master_uri);
600 full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name);
601 uri = gnome_vfs_uri_new (full_uri);
607 str_uri = gnome_vfs_uri_to_string (uri,
608 (GNOME_VFS_URI_HIDE_USER_NAME
609 | GNOME_VFS_URI_HIDE_PASSWORD
610 | GNOME_VFS_URI_HIDE_HOST_NAME
611 | GNOME_VFS_URI_HIDE_HOST_PORT
612 | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD));
613 gnome_vfs_uri_unref (uri);
615 if (!str_uri || !strlen (str_uri)) {
624 /* Parses an open iCalendar file and loads it into the backend */
625 static ECalBackendSyncStatus
626 open_cal (ECalBackendFile *cbfile, const char *uristr)
628 ECalBackendFilePrivate *priv;
629 icalcomponent *icalcomp;
633 icalcomp = e_cal_util_parse_ics_file (uristr);
635 return GNOME_Evolution_Calendar_OtherError;
637 /* FIXME: should we try to demangle XROOT components and
638 * individual components as well?
641 if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
642 icalcomponent_free (icalcomp);
644 return GNOME_Evolution_Calendar_OtherError;
647 priv->icalcomp = icalcomp;
648 priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile));
650 priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
651 scan_vcalendar (cbfile);
653 return GNOME_Evolution_Calendar_Success;
658 ECalBackend *backend;
659 GHashTable *old_uid_hash;
660 GHashTable *new_uid_hash;
665 notify_removals_cb (gpointer key, gpointer value, gpointer data)
667 BackendDeltaContext *context = data;
668 const gchar *uid = key;
669 ECalBackendFileObject *old_obj_data = value;
671 if (!g_hash_table_lookup (context->new_uid_hash, uid)) {
672 icalcomponent *old_icomp;
675 /* Object was removed */
677 old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object);
681 old_obj_str = icalcomponent_as_ical_string (old_icomp);
685 e_cal_backend_notify_object_removed (context->backend, uid, old_obj_str);
690 notify_adds_modifies_cb (gpointer key, gpointer value, gpointer data)
692 BackendDeltaContext *context = data;
693 const gchar *uid = key;
694 ECalBackendFileObject *new_obj_data = value;
695 ECalBackendFileObject *old_obj_data;
696 icalcomponent *old_icomp, *new_icomp;
697 gchar *old_obj_str, *new_obj_str;
699 old_obj_data = g_hash_table_lookup (context->old_uid_hash, uid);
702 /* Object was added */
704 new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object);
708 new_obj_str = icalcomponent_as_ical_string (new_icomp);
712 e_cal_backend_notify_object_created (context->backend, new_obj_str);
714 old_icomp = e_cal_component_get_icalcomponent (old_obj_data->full_object);
715 new_icomp = e_cal_component_get_icalcomponent (new_obj_data->full_object);
716 if (!old_icomp || !new_icomp)
719 old_obj_str = icalcomponent_as_ical_string (old_icomp);
720 new_obj_str = icalcomponent_as_ical_string (new_icomp);
721 if (!old_obj_str || !new_obj_str)
724 if (strcmp (old_obj_str, new_obj_str)) {
725 /* Object was modified */
727 e_cal_backend_notify_object_modified (context->backend, old_obj_str, new_obj_str);
733 notify_changes (ECalBackendFile *cbfile, GHashTable *old_uid_hash, GHashTable *new_uid_hash)
735 BackendDeltaContext context;
737 context.backend = E_CAL_BACKEND (cbfile);
738 context.old_uid_hash = old_uid_hash;
739 context.new_uid_hash = new_uid_hash;
741 g_hash_table_foreach (old_uid_hash, (GHFunc) notify_removals_cb, &context);
742 g_hash_table_foreach (new_uid_hash, (GHFunc) notify_adds_modifies_cb, &context);
745 static ECalBackendSyncStatus
746 reload_cal (ECalBackendFile *cbfile, const char *uristr)
748 ECalBackendFilePrivate *priv;
749 icalcomponent *icalcomp, *icalcomp_old;
750 GHashTable *comp_uid_hash_old;
754 icalcomp = e_cal_util_parse_ics_file (uristr);
756 return GNOME_Evolution_Calendar_OtherError;
758 /* FIXME: should we try to demangle XROOT components and
759 * individual components as well?
762 if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
763 icalcomponent_free (icalcomp);
765 return GNOME_Evolution_Calendar_OtherError;
768 /* Keep old data for comparison - free later */
770 icalcomp_old = priv->icalcomp;
771 priv->icalcomp = NULL;
773 comp_uid_hash_old = priv->comp_uid_hash;
774 priv->comp_uid_hash = NULL;
776 /* Load new calendar */
778 free_calendar_data (cbfile);
780 priv->icalcomp = icalcomp;
782 priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
783 scan_vcalendar (cbfile);
785 priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile));
787 /* Compare old and new versions of calendar */
789 notify_changes (cbfile, comp_uid_hash_old, priv->comp_uid_hash);
793 free_calendar_components (comp_uid_hash_old, icalcomp_old);
794 return GNOME_Evolution_Calendar_Success;
797 static ECalBackendSyncStatus
798 create_cal (ECalBackendFile *cbfile, const char *uristr)
801 ECalBackendFilePrivate *priv;
805 /* Create the directory to contain the file */
806 dirname = g_path_get_dirname (uristr);
807 if (e_util_mkdir_hier (dirname, 0700) != 0) {
809 return GNOME_Evolution_Calendar_NoSuchCal;
814 /* Create the new calendar information */
815 priv->icalcomp = e_cal_util_new_top_level ();
817 /* Create our internal data */
818 priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
820 priv->uri = get_uri_string_for_gnome_vfs (E_CAL_BACKEND (cbfile));
824 return GNOME_Evolution_Calendar_Success;
828 get_uri_string (ECalBackend *backend)
830 gchar *str_uri, *full_uri;
832 str_uri = get_uri_string_for_gnome_vfs (backend);
833 full_uri = gnome_vfs_unescape_string (str_uri, "");
839 /* Open handler for the file backend */
840 static ECalBackendSyncStatus
841 e_cal_backend_file_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists,
842 const char *username, const char *password)
844 ECalBackendFile *cbfile;
845 ECalBackendFilePrivate *priv;
847 ECalBackendSyncStatus status;
849 cbfile = E_CAL_BACKEND_FILE (backend);
852 /* Claim a succesful open if we are already open */
853 if (priv->uri && priv->comp_uid_hash)
854 return GNOME_Evolution_Calendar_Success;
856 str_uri = get_uri_string (E_CAL_BACKEND (backend));
858 return GNOME_Evolution_Calendar_OtherError;
860 if (access (str_uri, R_OK) == 0) {
861 status = open_cal (cbfile, str_uri);
862 if (access (str_uri, W_OK) != 0)
863 priv->read_only = TRUE;
866 status = GNOME_Evolution_Calendar_NoSuchCal;
868 status = create_cal (cbfile, str_uri);
876 static ECalBackendSyncStatus
877 e_cal_backend_file_remove (ECalBackendSync *backend, EDataCal *cal)
879 ECalBackendFile *cbfile;
880 ECalBackendFilePrivate *priv;
881 char *str_uri, *dirname;
884 GError *error = NULL;
887 cbfile = E_CAL_BACKEND_FILE (backend);
890 str_uri = get_uri_string (E_CAL_BACKEND (backend));
892 return GNOME_Evolution_Calendar_OtherError;
894 if (access (str_uri, W_OK) != 0) {
897 return GNOME_Evolution_Calendar_PermissionDenied;
900 /* remove all files in the directory */
901 dirname = g_path_get_dirname (str_uri);
902 dir = g_dir_open (dirname, 0, &error);
907 return GNOME_Evolution_Calendar_PermissionDenied;
910 while ((fname = g_dir_read_name (dir))) {
913 full_path = g_build_filename (dirname, fname, NULL);
914 if (unlink (full_path) != 0) {
920 return GNOME_Evolution_Calendar_OtherError;
926 /* remove the directory itself */
927 success = rmdir (dirname) == 0;
933 return success ? GNOME_Evolution_Calendar_Success : GNOME_Evolution_Calendar_OtherError;
936 /* is_loaded handler for the file backend */
938 e_cal_backend_file_is_loaded (ECalBackend *backend)
940 ECalBackendFile *cbfile;
941 ECalBackendFilePrivate *priv;
943 cbfile = E_CAL_BACKEND_FILE (backend);
946 return (priv->icalcomp != NULL);
949 /* is_remote handler for the file backend */
951 e_cal_backend_file_get_mode (ECalBackend *backend)
953 ECalBackendFile *cbfile;
954 ECalBackendFilePrivate *priv;
956 cbfile = E_CAL_BACKEND_FILE (backend);
959 return CAL_MODE_LOCAL;
962 /* Set_mode handler for the file backend */
964 e_cal_backend_file_set_mode (ECalBackend *backend, CalMode mode)
966 e_cal_backend_notify_mode (backend,
967 GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
968 GNOME_Evolution_Calendar_MODE_LOCAL);
972 static ECalBackendSyncStatus
973 e_cal_backend_file_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
977 comp = e_cal_component_new ();
979 switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
980 case ICAL_VEVENT_COMPONENT:
981 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
983 case ICAL_VTODO_COMPONENT:
984 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
986 case ICAL_VJOURNAL_COMPONENT:
987 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
990 g_object_unref (comp);
991 return GNOME_Evolution_Calendar_ObjectNotFound;
994 *object = e_cal_component_get_as_string (comp);
995 g_object_unref (comp);
997 return GNOME_Evolution_Calendar_Success;
1001 add_detached_recur_to_vcalendar (gpointer key, gpointer value, gpointer user_data)
1003 ECalComponent *recurrence = value;
1004 icalcomponent *vcalendar = user_data;
1006 icalcomponent_add_component (
1008 icalcomponent_new_clone (e_cal_component_get_icalcomponent (recurrence)));
1011 /* Get_object_component handler for the file backend */
1012 static ECalBackendSyncStatus
1013 e_cal_backend_file_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
1015 ECalBackendFile *cbfile;
1016 ECalBackendFilePrivate *priv;
1017 ECalBackendFileObject *obj_data;
1019 cbfile = E_CAL_BACKEND_FILE (backend);
1020 priv = cbfile->priv;
1022 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_InvalidObject);
1023 g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1024 g_assert (priv->comp_uid_hash != NULL);
1026 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
1028 return GNOME_Evolution_Calendar_ObjectNotFound;
1031 ECalComponent *comp;
1033 comp = g_hash_table_lookup (obj_data->recurrences, rid);
1035 *object = e_cal_component_get_as_string (comp);
1037 icalcomponent *icalcomp;
1038 struct icaltimetype itt;
1040 itt = icaltime_from_string (rid);
1041 icalcomp = e_cal_util_construct_instance (
1042 e_cal_component_get_icalcomponent (obj_data->full_object),
1045 return GNOME_Evolution_Calendar_ObjectNotFound;
1047 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1049 icalcomponent_free (icalcomp);
1052 if (g_hash_table_size (obj_data->recurrences) > 0) {
1053 icalcomponent *icalcomp;
1055 /* if we have detached recurrences, return a VCALENDAR */
1056 icalcomp = e_cal_util_new_top_level ();
1057 icalcomponent_add_component (
1059 icalcomponent_new_clone (e_cal_component_get_icalcomponent (obj_data->full_object)));
1061 /* add all detached recurrences */
1062 g_hash_table_foreach (obj_data->recurrences, (GHFunc) add_detached_recur_to_vcalendar, icalcomp);
1064 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1066 icalcomponent_free (icalcomp);
1068 *object = e_cal_component_get_as_string (obj_data->full_object);
1071 return GNOME_Evolution_Calendar_Success;
1074 /* Get_timezone_object handler for the file backend */
1075 static ECalBackendSyncStatus
1076 e_cal_backend_file_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
1078 ECalBackendFile *cbfile;
1079 ECalBackendFilePrivate *priv;
1081 icalcomponent *icalcomp;
1083 cbfile = E_CAL_BACKEND_FILE (backend);
1084 priv = cbfile->priv;
1086 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1087 g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1089 if (!strcmp (tzid, "UTC")) {
1090 zone = icaltimezone_get_utc_timezone ();
1092 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1094 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1096 return GNOME_Evolution_Calendar_ObjectNotFound;
1100 icalcomp = icaltimezone_get_component (zone);
1102 return GNOME_Evolution_Calendar_InvalidObject;
1104 *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
1106 return GNOME_Evolution_Calendar_Success;
1109 /* Add_timezone handler for the file backend */
1110 static ECalBackendSyncStatus
1111 e_cal_backend_file_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
1113 icalcomponent *tz_comp;
1114 ECalBackendFile *cbfile;
1115 ECalBackendFilePrivate *priv;
1117 cbfile = (ECalBackendFile *) backend;
1119 g_return_val_if_fail (E_IS_CAL_BACKEND_FILE (cbfile), GNOME_Evolution_Calendar_OtherError);
1120 g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
1122 priv = cbfile->priv;
1124 tz_comp = icalparser_parse_string (tzobj);
1126 return GNOME_Evolution_Calendar_InvalidObject;
1128 if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
1131 zone = icaltimezone_new ();
1132 icaltimezone_set_component (zone, tz_comp);
1133 if (!icalcomponent_get_timezone (priv->icalcomp,
1134 icaltimezone_get_tzid (zone))) {
1135 icalcomponent_add_component (priv->icalcomp, tz_comp);
1139 icaltimezone_free (zone, 1);
1142 return GNOME_Evolution_Calendar_Success;
1146 static ECalBackendSyncStatus
1147 e_cal_backend_file_set_default_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid)
1149 ECalBackendFile *cbfile;
1150 ECalBackendFilePrivate *priv;
1153 cbfile = E_CAL_BACKEND_FILE (backend);
1154 priv = cbfile->priv;
1156 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1158 /* Look up the VTIMEZONE in our icalcomponent. */
1159 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1161 return GNOME_Evolution_Calendar_ObjectNotFound;
1163 /* Set the default timezone to it. */
1164 priv->default_zone = zone;
1166 return GNOME_Evolution_Calendar_Success;
1171 gboolean search_needed;
1173 ECalBackendSExp *obj_sexp;
1174 ECalBackend *backend;
1175 icaltimezone *default_zone;
1179 match_recurrence_sexp (gpointer key, gpointer value, gpointer data)
1181 ECalComponent *comp = value;
1182 MatchObjectData *match_data = data;
1184 if ((!match_data->search_needed) ||
1185 (e_cal_backend_sexp_match_comp (match_data->obj_sexp, comp, match_data->backend))) {
1186 match_data->obj_list = g_list_append (match_data->obj_list,
1187 e_cal_component_get_as_string (comp));
1192 match_object_sexp (gpointer key, gpointer value, gpointer data)
1194 ECalBackendFileObject *obj_data = value;
1195 MatchObjectData *match_data = data;
1197 if (obj_data->full_object) {
1198 if ((!match_data->search_needed) ||
1199 (e_cal_backend_sexp_match_comp (match_data->obj_sexp, obj_data->full_object, match_data->backend))) {
1200 match_data->obj_list = g_list_append (match_data->obj_list,
1201 e_cal_component_get_as_string (obj_data->full_object));
1205 /* match also recurrences */
1206 g_hash_table_foreach (obj_data->recurrences,
1207 (GHFunc) match_recurrence_sexp,
1211 /* Get_objects_in_range handler for the file backend */
1212 static ECalBackendSyncStatus
1213 e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects)
1215 ECalBackendFile *cbfile;
1216 ECalBackendFilePrivate *priv;
1217 MatchObjectData match_data;
1219 cbfile = E_CAL_BACKEND_FILE (backend);
1220 priv = cbfile->priv;
1222 d(g_message (G_STRLOC ": Getting object list (%s)", sexp));
1224 match_data.search_needed = TRUE;
1225 match_data.query = sexp;
1226 match_data.obj_list = NULL;
1227 match_data.backend = E_CAL_BACKEND (backend);
1228 match_data.default_zone = priv->default_zone;
1230 if (!strcmp (sexp, "#t"))
1231 match_data.search_needed = FALSE;
1233 match_data.obj_sexp = e_cal_backend_sexp_new (sexp);
1234 if (!match_data.obj_sexp)
1235 return GNOME_Evolution_Calendar_InvalidQuery;
1237 g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
1239 *objects = match_data.obj_list;
1241 return GNOME_Evolution_Calendar_Success;
1244 /* get_query handler for the file backend */
1246 e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
1248 ECalBackendFile *cbfile;
1249 ECalBackendFilePrivate *priv;
1250 MatchObjectData match_data;
1252 cbfile = E_CAL_BACKEND_FILE (backend);
1253 priv = cbfile->priv;
1255 d(g_message (G_STRLOC ": Starting query (%s)", e_data_cal_view_get_text (query)));
1257 /* try to match all currently existing objects */
1258 match_data.search_needed = TRUE;
1259 match_data.query = e_data_cal_view_get_text (query);
1260 match_data.obj_list = NULL;
1261 match_data.backend = backend;
1262 match_data.default_zone = priv->default_zone;
1264 if (!strcmp (match_data.query, "#t"))
1265 match_data.search_needed = FALSE;
1267 match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
1268 if (!match_data.obj_sexp) {
1269 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_InvalidQuery);
1273 g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
1275 /* notify listeners of all objects */
1276 if (match_data.obj_list) {
1277 e_data_cal_view_notify_objects_added (query, (const GList *) match_data.obj_list);
1280 g_list_foreach (match_data.obj_list, (GFunc) g_free, NULL);
1281 g_list_free (match_data.obj_list);
1284 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
1288 free_busy_instance (ECalComponent *comp,
1289 time_t instance_start,
1290 time_t instance_end,
1293 icalcomponent *vfb = data;
1295 icalparameter *param;
1296 struct icalperiodtype ipt;
1297 icaltimezone *utc_zone;
1299 utc_zone = icaltimezone_get_utc_timezone ();
1301 ipt.start = icaltime_from_timet_with_zone (instance_start, FALSE, utc_zone);
1302 ipt.end = icaltime_from_timet_with_zone (instance_end, FALSE, utc_zone);
1303 ipt.duration = icaldurationtype_null_duration ();
1305 /* add busy information to the vfb component */
1306 prop = icalproperty_new (ICAL_FREEBUSY_PROPERTY);
1307 icalproperty_set_freebusy (prop, ipt);
1309 param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
1310 icalproperty_add_parameter (prop, param);
1312 icalcomponent_add_property (vfb, prop);
1317 static icalcomponent *
1318 create_user_free_busy (ECalBackendFile *cbfile, const char *address, const char *cn,
1319 time_t start, time_t end)
1321 ECalBackendFilePrivate *priv;
1324 icaltimezone *utc_zone;
1325 ECalBackendSExp *obj_sexp;
1326 char *query, *iso_start, *iso_end;
1328 priv = cbfile->priv;
1330 /* create the (unique) VFREEBUSY object that we'll return */
1331 vfb = icalcomponent_new_vfreebusy ();
1332 if (address != NULL) {
1334 icalparameter *param;
1336 prop = icalproperty_new_organizer (address);
1337 if (prop != NULL && cn != NULL) {
1338 param = icalparameter_new_cn (cn);
1339 icalproperty_add_parameter (prop, param);
1342 icalcomponent_add_property (vfb, prop);
1344 utc_zone = icaltimezone_get_utc_timezone ();
1345 icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
1346 icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
1348 /* add all objects in the given interval */
1349 iso_start = isodate_from_time_t (start);
1350 iso_end = isodate_from_time_t (end);
1351 query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") (make-time \"%s\")",
1352 iso_start, iso_end);
1353 obj_sexp = e_cal_backend_sexp_new (query);
1361 for (l = priv->comp; l; l = l->next) {
1362 ECalComponent *comp = l->data;
1363 icalcomponent *icalcomp, *vcalendar_comp;
1366 icalcomp = e_cal_component_get_icalcomponent (comp);
1370 /* If the event is TRANSPARENT, skip it. */
1371 prop = icalcomponent_get_first_property (icalcomp,
1372 ICAL_TRANSP_PROPERTY);
1374 icalproperty_transp transp_val = icalproperty_get_transp (prop);
1375 if (transp_val == ICAL_TRANSP_TRANSPARENT ||
1376 transp_val == ICAL_TRANSP_TRANSPARENTNOCONFLICT)
1380 if (!e_cal_backend_sexp_match_comp (obj_sexp, l->data, E_CAL_BACKEND (cbfile)))
1383 vcalendar_comp = icalcomponent_get_parent (icalcomp);
1384 e_cal_recur_generate_instances (comp, start, end,
1389 priv->default_zone);
1395 /* Get_free_busy handler for the file backend */
1396 static ECalBackendSyncStatus
1397 e_cal_backend_file_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users,
1398 time_t start, time_t end, GList **freebusy)
1400 ECalBackendFile *cbfile;
1401 ECalBackendFilePrivate *priv;
1402 gchar *address, *name;
1407 cbfile = E_CAL_BACKEND_FILE (backend);
1408 priv = cbfile->priv;
1410 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1411 g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange);
1412 g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange);
1416 if (users == NULL) {
1417 if (e_cal_backend_mail_account_get_default (&address, &name)) {
1418 vfb = create_user_free_busy (cbfile, address, name, start, end);
1419 calobj = icalcomponent_as_ical_string (vfb);
1420 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1421 icalcomponent_free (vfb);
1426 for (l = users; l != NULL; l = l->next ) {
1428 if (e_cal_backend_mail_account_is_valid (address, &name)) {
1429 vfb = create_user_free_busy (cbfile, address, name, start, end);
1430 calobj = icalcomponent_as_ical_string (vfb);
1431 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1432 icalcomponent_free (vfb);
1438 return GNOME_Evolution_Calendar_Success;
1443 ECalBackendFile *backend;
1444 icalcomponent_kind kind;
1447 } ECalBackendFileComputeChangesData;
1450 e_cal_backend_file_compute_changes_foreach_key (const char *key, gpointer value, gpointer data)
1452 ECalBackendFileComputeChangesData *be_data = data;
1454 if (!lookup_component (be_data->backend, key)) {
1455 ECalComponent *comp;
1457 comp = e_cal_component_new ();
1458 if (be_data->kind == ICAL_VTODO_COMPONENT)
1459 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1461 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1463 e_cal_component_set_uid (comp, key);
1464 be_data->deletes = g_list_prepend (be_data->deletes, e_cal_component_get_as_string (comp));
1466 e_xmlhash_remove (be_data->ehash, key);
1470 static ECalBackendSyncStatus
1471 e_cal_backend_file_compute_changes (ECalBackendFile *cbfile, const char *change_id,
1472 GList **adds, GList **modifies, GList **deletes)
1474 ECalBackendFilePrivate *priv;
1477 ECalBackendFileComputeChangesData be_data;
1479 gchar *unescaped_uri;
1481 priv = cbfile->priv;
1483 /* FIXME Will this always work? */
1484 unescaped_uri = gnome_vfs_unescape_string (priv->uri, "");
1485 filename = g_strdup_printf ("%s-%s.db", unescaped_uri, change_id);
1486 g_free (unescaped_uri);
1487 if (!(ehash = e_xmlhash_new (filename))) {
1489 return GNOME_Evolution_Calendar_OtherError;
1494 /* Calculate adds and modifies */
1495 for (i = priv->comp; i != NULL; i = i->next) {
1499 e_cal_component_get_uid (i->data, &uid);
1500 calobj = e_cal_component_get_as_string (i->data);
1502 g_assert (calobj != NULL);
1504 /* check what type of change has occurred, if any */
1505 switch (e_xmlhash_compare (ehash, uid, calobj)) {
1506 case E_XMLHASH_STATUS_SAME:
1508 case E_XMLHASH_STATUS_NOT_FOUND:
1509 *adds = g_list_prepend (*adds, g_strdup (calobj));
1510 e_xmlhash_add (ehash, uid, calobj);
1512 case E_XMLHASH_STATUS_DIFFERENT:
1513 *modifies = g_list_prepend (*modifies, g_strdup (calobj));
1514 e_xmlhash_add (ehash, uid, calobj);
1521 /* Calculate deletions */
1522 be_data.backend = cbfile;
1523 be_data.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbfile));
1524 be_data.deletes = NULL;
1525 be_data.ehash = ehash;
1527 e_xmlhash_foreach_key (ehash, (EXmlHashFunc)e_cal_backend_file_compute_changes_foreach_key, &be_data);
1529 *deletes = be_data.deletes;
1531 e_xmlhash_write (ehash);
1532 e_xmlhash_destroy (ehash);
1534 return GNOME_Evolution_Calendar_Success;
1537 /* Get_changes handler for the file backend */
1538 static ECalBackendSyncStatus
1539 e_cal_backend_file_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id,
1540 GList **adds, GList **modifies, GList **deletes)
1542 ECalBackendFile *cbfile;
1543 ECalBackendFilePrivate *priv;
1545 cbfile = E_CAL_BACKEND_FILE (backend);
1546 priv = cbfile->priv;
1548 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1549 g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1551 return e_cal_backend_file_compute_changes (cbfile, change_id, adds, modifies, deletes);
1554 /* Discard_alarm handler for the file backend */
1555 static ECalBackendSyncStatus
1556 e_cal_backend_file_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
1558 /* we just do nothing with the alarm */
1559 return GNOME_Evolution_Calendar_Success;
1562 static icaltimezone *
1563 e_cal_backend_file_internal_get_default_timezone (ECalBackend *backend)
1565 ECalBackendFile *cbfile;
1566 ECalBackendFilePrivate *priv;
1568 cbfile = E_CAL_BACKEND_FILE (backend);
1569 priv = cbfile->priv;
1571 g_return_val_if_fail (priv->icalcomp != NULL, NULL);
1573 return priv->default_zone;
1576 static icaltimezone *
1577 e_cal_backend_file_internal_get_timezone (ECalBackend *backend, const char *tzid)
1579 ECalBackendFile *cbfile;
1580 ECalBackendFilePrivate *priv;
1583 cbfile = E_CAL_BACKEND_FILE (backend);
1584 priv = cbfile->priv;
1586 g_return_val_if_fail (priv->icalcomp != NULL, NULL);
1588 if (!strcmp (tzid, "UTC"))
1589 zone = icaltimezone_get_utc_timezone ();
1591 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1593 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1600 sanitize_component (ECalBackendFile *cbfile, ECalComponent *comp)
1602 ECalComponentDateTime dt;
1603 icaltimezone *zone, *default_zone;
1605 /* Check dtstart, dtend and due's timezone, and convert it to local
1606 * default timezone if the timezone is not in our builtin timezone
1608 e_cal_component_get_dtstart (comp, &dt);
1609 if (dt.value && dt.tzid) {
1610 zone = e_cal_backend_file_internal_get_timezone ((ECalBackend *)cbfile, dt.tzid);
1612 default_zone = e_cal_backend_file_internal_get_default_timezone ((ECalBackend *)cbfile);
1613 g_free ((char *)dt.tzid);
1614 dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
1615 e_cal_component_set_dtstart (comp, &dt);
1618 e_cal_component_free_datetime (&dt);
1620 e_cal_component_get_dtend (comp, &dt);
1621 if (dt.value && dt.tzid) {
1622 zone = e_cal_backend_file_internal_get_timezone ((ECalBackend *)cbfile, dt.tzid);
1624 default_zone = e_cal_backend_file_internal_get_default_timezone ((ECalBackend *)cbfile);
1625 g_free ((char *)dt.tzid);
1626 dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
1627 e_cal_component_set_dtend (comp, &dt);
1630 e_cal_component_free_datetime (&dt);
1632 e_cal_component_get_due (comp, &dt);
1633 if (dt.value && dt.tzid) {
1634 zone = e_cal_backend_file_internal_get_timezone ((ECalBackend *)cbfile, dt.tzid);
1636 default_zone = e_cal_backend_file_internal_get_default_timezone ((ECalBackend *)cbfile);
1637 g_free ((char *)dt.tzid);
1638 dt.tzid = g_strdup (icaltimezone_get_tzid (default_zone));
1639 e_cal_component_set_due (comp, &dt);
1642 e_cal_component_free_datetime (&dt);
1643 e_cal_component_abort_sequence (comp);
1648 static ECalBackendSyncStatus
1649 e_cal_backend_file_create_object (ECalBackendSync *backend, EDataCal *cal, char **calobj, char **uid)
1651 ECalBackendFile *cbfile;
1652 ECalBackendFilePrivate *priv;
1653 icalcomponent *icalcomp;
1654 ECalComponent *comp;
1655 const char *comp_uid;
1656 struct icaltimetype current;
1658 cbfile = E_CAL_BACKEND_FILE (backend);
1659 priv = cbfile->priv;
1661 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1662 g_return_val_if_fail (*calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1664 /* Parse the icalendar text */
1665 icalcomp = icalparser_parse_string (*calobj);
1667 return GNOME_Evolution_Calendar_InvalidObject;
1669 /* Check kind with the parent */
1670 if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
1671 icalcomponent_free (icalcomp);
1672 return GNOME_Evolution_Calendar_InvalidObject;
1676 comp_uid = icalcomponent_get_uid (icalcomp);
1680 new_uid = e_cal_component_gen_uid ();
1682 icalcomponent_free (icalcomp);
1683 return GNOME_Evolution_Calendar_InvalidObject;
1686 icalcomponent_set_uid (icalcomp, new_uid);
1687 comp_uid = icalcomponent_get_uid (icalcomp);
1692 /* check the object is not in our cache */
1693 if (lookup_component (cbfile, comp_uid)) {
1694 icalcomponent_free (icalcomp);
1695 return GNOME_Evolution_Calendar_ObjectIdAlreadyExists;
1698 /* Create the cal component */
1699 comp = e_cal_component_new ();
1700 e_cal_component_set_icalcomponent (comp, icalcomp);
1702 /* Set the created and last modified times on the component */
1703 current = icaltime_from_timet (time (NULL), 0);
1704 e_cal_component_set_created (comp, ¤t);
1705 e_cal_component_set_last_modified (comp, ¤t);
1707 /* sanitize the component*/
1708 sanitize_component (cbfile, comp);
1710 /* Add the object */
1711 add_component (cbfile, comp, TRUE);
1716 /* Return the UID and the modified component */
1718 *uid = g_strdup (comp_uid);
1719 *calobj = e_cal_component_get_as_string (comp);
1721 return GNOME_Evolution_Calendar_Success;
1724 static ECalBackendSyncStatus
1725 e_cal_backend_file_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj,
1726 CalObjModType mod, char **old_object)
1728 ECalBackendFile *cbfile;
1729 ECalBackendFilePrivate *priv;
1730 icalcomponent *icalcomp;
1731 const char *comp_uid, *rid;
1733 ECalComponent *comp, *recurrence;
1734 ECalBackendFileObject *obj_data;
1735 struct icaltimetype current;
1737 cbfile = E_CAL_BACKEND_FILE (backend);
1738 priv = cbfile->priv;
1740 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1741 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1743 /* Parse the icalendar text */
1744 icalcomp = icalparser_parse_string ((char *) calobj);
1746 return GNOME_Evolution_Calendar_InvalidObject;
1748 /* Check kind with the parent */
1749 if (icalcomponent_isa (icalcomp) != e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
1750 icalcomponent_free (icalcomp);
1751 return GNOME_Evolution_Calendar_InvalidObject;
1755 comp_uid = icalcomponent_get_uid (icalcomp);
1757 /* Get the object from our cache */
1758 if (!(obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid))) {
1759 icalcomponent_free (icalcomp);
1760 return GNOME_Evolution_Calendar_ObjectNotFound;
1763 /* Create the cal component */
1764 comp = e_cal_component_new ();
1765 e_cal_component_set_icalcomponent (comp, icalcomp);
1767 /* Set the last modified time on the component */
1768 current = icaltime_from_timet (time (NULL), 0);
1769 e_cal_component_set_last_modified (comp, ¤t);
1771 /* sanitize the component*/
1772 sanitize_component (cbfile, comp);
1774 /* handle mod_type */
1776 case CALOBJ_MOD_THIS :
1777 rid = e_cal_component_get_recurid_as_string (comp);
1778 if (!rid || !*rid) {
1780 *old_object = e_cal_component_get_as_string (obj_data->full_object);
1782 /* replace only the full object */
1783 icalcomponent_remove_component (priv->icalcomp,
1784 e_cal_component_get_icalcomponent (obj_data->full_object));
1785 priv->comp = g_list_remove (priv->comp, obj_data->full_object);
1787 /* add the new object */
1788 g_object_unref (obj_data->full_object);
1789 obj_data->full_object = comp;
1791 icalcomponent_add_component (priv->icalcomp,
1792 e_cal_component_get_icalcomponent (obj_data->full_object));
1793 priv->comp = g_list_prepend (priv->comp, obj_data->full_object);
1797 return GNOME_Evolution_Calendar_Success;
1800 if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
1801 (void **) &real_rid, (void **) &recurrence)) {
1803 *old_object = e_cal_component_get_as_string (recurrence);
1805 /* remove the component from our data */
1806 icalcomponent_remove_component (priv->icalcomp,
1807 e_cal_component_get_icalcomponent (recurrence));
1808 priv->comp = g_list_remove (priv->comp, recurrence);
1809 g_hash_table_remove (obj_data->recurrences, rid);
1810 obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, recurrence);
1814 g_object_unref (recurrence);
1818 old = e_cal_component_get_as_string (obj_data->full_object);
1820 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
1821 get_rid_icaltime (comp),
1824 new = e_cal_component_get_as_string (obj_data->full_object);
1826 e_cal_backend_notify_object_modified (E_CAL_BACKEND (backend), old, new);
1835 /* add the detached instance */
1836 g_hash_table_insert (obj_data->recurrences,
1837 g_strdup (e_cal_component_get_recurid_as_string (comp)),
1839 obj_data->recurrences_list = g_list_append (obj_data->recurrences_list, comp);
1841 case CALOBJ_MOD_THISANDPRIOR :
1843 case CALOBJ_MOD_THISANDFUTURE :
1845 case CALOBJ_MOD_ALL :
1846 /* in this case, we blow away all recurrences, and start over
1847 with a clean component */
1848 /* Remove the old version */
1850 *old_object = e_cal_component_get_as_string (obj_data->full_object);
1852 remove_component (cbfile, obj_data->full_object);
1854 /* Add the new object */
1855 add_component (cbfile, comp, TRUE);
1861 return GNOME_Evolution_Calendar_Success;
1865 remove_instance (ECalBackendFile *cbfile, ECalBackendFileObject *obj_data, const char *rid)
1868 ECalComponent *comp;
1874 if (g_hash_table_lookup_extended (obj_data->recurrences, rid, (void **) &hash_rid, (void **) &comp)) {
1875 /* remove the component from our data */
1876 icalcomponent_remove_component (cbfile->priv->icalcomp,
1877 e_cal_component_get_icalcomponent (comp));
1878 cbfile->priv->comp = g_list_remove (cbfile->priv->comp, comp);
1879 g_hash_table_remove (obj_data->recurrences, rid);
1880 obj_data->recurrences_list = g_list_remove (obj_data->recurrences_list, comp);
1882 /* update the set of categories */
1883 e_cal_component_get_categories_list (comp, &categories);
1884 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
1885 e_cal_component_free_categories_list (categories);
1889 g_object_unref (comp);
1894 /* remove the component from our data, temporarily */
1895 icalcomponent_remove_component (cbfile->priv->icalcomp,
1896 e_cal_component_get_icalcomponent (obj_data->full_object));
1897 cbfile->priv->comp = g_list_remove (cbfile->priv->comp, obj_data->full_object);
1899 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
1900 icaltime_from_string (rid), CALOBJ_MOD_THIS);
1902 /* add the modified object to the beginning of the list,
1903 so that it's always before any detached instance we
1905 icalcomponent_add_component (cbfile->priv->icalcomp,
1906 e_cal_component_get_icalcomponent (obj_data->full_object));
1907 cbfile->priv->comp = g_list_prepend (cbfile->priv->comp, obj_data->full_object);
1911 ECalBackendFile *cbfile;
1912 ECalBackendFileObject *obj_data;
1915 } RemoveRecurrenceData;
1918 remove_object_instance_cb (gpointer key, gpointer value, gpointer user_data)
1920 time_t fromtt, instancett;
1923 ECalComponent *instance = value;
1924 RemoveRecurrenceData *rrdata = user_data;
1926 fromtt = icaltime_as_timet (icaltime_from_string (rrdata->rid));
1927 instancett = icaltime_as_timet (get_rid_icaltime (instance));
1929 if (fromtt > 0 && instancett > 0) {
1930 if ((rrdata->mod == CALOBJ_MOD_THISANDPRIOR && instancett <= fromtt) ||
1931 (rrdata->mod == CALOBJ_MOD_THISANDFUTURE && instancett >= fromtt)) {
1932 /* remove the component from our data */
1933 icalcomponent_remove_component (rrdata->cbfile->priv->icalcomp,
1934 e_cal_component_get_icalcomponent (instance));
1935 rrdata->cbfile->priv->comp = g_list_remove (rrdata->cbfile->priv->comp, instance);
1937 rrdata->obj_data->recurrences_list = g_list_remove (rrdata->obj_data->recurrences_list, instance);
1939 /* update the set of categories */
1940 e_cal_component_get_categories_list (instance, &categories);
1941 e_cal_backend_unref_categories (E_CAL_BACKEND (rrdata->cbfile), categories);
1942 e_cal_component_free_categories_list (categories);
1946 g_object_unref (instance);
1955 /* Remove_object handler for the file backend */
1956 static ECalBackendSyncStatus
1957 e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal,
1958 const char *uid, const char *rid,
1959 CalObjModType mod, char **object)
1961 ECalBackendFile *cbfile;
1962 ECalBackendFilePrivate *priv;
1963 ECalBackendFileObject *obj_data;
1964 ECalComponent *comp;
1966 RemoveRecurrenceData rrdata;
1968 cbfile = E_CAL_BACKEND_FILE (backend);
1969 priv = cbfile->priv;
1971 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1972 g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1974 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
1976 return GNOME_Evolution_Calendar_ObjectNotFound;
1978 comp = obj_data->full_object;
1981 case CALOBJ_MOD_ALL :
1982 *object = e_cal_component_get_as_string (comp);
1983 remove_component (cbfile, comp);
1985 case CALOBJ_MOD_THIS :
1986 *object = e_cal_component_get_as_string (comp);
1988 remove_component (cbfile, comp);
1990 remove_instance (cbfile, obj_data, rid);
1992 case CALOBJ_MOD_THISANDPRIOR :
1993 case CALOBJ_MOD_THISANDFUTURE :
1995 return GNOME_Evolution_Calendar_ObjectNotFound;
1997 *object = e_cal_component_get_as_string (comp);
1999 /* remove the component from our data, temporarily */
2000 icalcomponent_remove_component (priv->icalcomp,
2001 e_cal_component_get_icalcomponent (comp));
2002 priv->comp = g_list_remove (priv->comp, comp);
2004 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (comp),
2005 icaltime_from_string (rid), mod);
2007 /* now remove all detached instances */
2008 rrdata.cbfile = cbfile;
2009 rrdata.obj_data = obj_data;
2012 g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
2014 /* add the modified object to the beginning of the list,
2015 so that it's always before any detached instance we
2017 priv->comp = g_list_prepend (priv->comp, comp);
2023 return GNOME_Evolution_Calendar_Success;
2027 cancel_received_object (ECalBackendFile *cbfile, icalcomponent *icalcomp)
2029 ECalComponent *old_comp;
2031 /* Find the old version of the component. */
2032 old_comp = lookup_component (cbfile, icalcomponent_get_uid (icalcomp));
2037 remove_component (cbfile, old_comp);
2046 } ECalBackendFileTzidData;
2049 check_tzids (icalparameter *param, void *data)
2051 ECalBackendFileTzidData *tzdata = data;
2054 tzid = icalparameter_get_tzid (param);
2055 if (!tzid || g_hash_table_lookup (tzdata->zones, tzid))
2056 tzdata->found = FALSE;
2059 /* Update_objects handler for the file backend. */
2060 static ECalBackendSyncStatus
2061 e_cal_backend_file_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
2063 ECalBackendFile *cbfile;
2064 ECalBackendFilePrivate *priv;
2065 icalcomponent *toplevel_comp, *icalcomp = NULL;
2066 icalcomponent_kind kind;
2067 icalproperty_method method;
2068 icalcomponent *subcomp;
2069 GList *comps, *del_comps, *l;
2070 ECalComponent *comp;
2071 struct icaltimetype current;
2072 ECalBackendFileTzidData tzdata;
2073 ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success;
2075 cbfile = E_CAL_BACKEND_FILE (backend);
2076 priv = cbfile->priv;
2078 g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_InvalidObject);
2079 g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
2081 /* Pull the component from the string and ensure that it is sane */
2082 toplevel_comp = icalparser_parse_string ((char *) calobj);
2084 return GNOME_Evolution_Calendar_InvalidObject;
2086 kind = icalcomponent_isa (toplevel_comp);
2087 if (kind != ICAL_VCALENDAR_COMPONENT) {
2088 /* If its not a VCALENDAR, make it one to simplify below */
2089 icalcomp = toplevel_comp;
2090 toplevel_comp = e_cal_util_new_top_level ();
2091 icalcomponent_add_component (toplevel_comp, icalcomp);
2094 method = icalcomponent_get_method (toplevel_comp);
2096 /* Build a list of timezones so we can make sure all the objects have valid info */
2097 tzdata.zones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2099 subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_VTIMEZONE_COMPONENT);
2103 zone = icaltimezone_new ();
2104 if (icaltimezone_set_component (zone, subcomp))
2105 g_hash_table_insert (tzdata.zones, g_strdup (icaltimezone_get_tzid (zone)), NULL);
2107 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_VTIMEZONE_COMPONENT);
2110 /* First we make sure all the components are usuable */
2111 comps = del_comps = NULL;
2112 kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
2114 subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_ANY_COMPONENT);
2116 icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
2118 if (child_kind != kind) {
2119 /* remove the component from the toplevel VCALENDAR */
2120 if (child_kind != ICAL_VTIMEZONE_COMPONENT)
2121 del_comps = g_list_prepend (del_comps, subcomp);
2123 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_ANY_COMPONENT);
2127 tzdata.found = TRUE;
2128 icalcomponent_foreach_tzid (subcomp, check_tzids, &tzdata);
2130 if (!tzdata.found) {
2131 status = GNOME_Evolution_Calendar_InvalidObject;
2135 if (!icalcomponent_get_uid (subcomp)) {
2136 status = GNOME_Evolution_Calendar_InvalidObject;
2140 comps = g_list_prepend (comps, subcomp);
2141 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_ANY_COMPONENT);
2144 /* Now we manipulate the components we care about */
2145 for (l = comps; l; l = l->next) {
2146 const char *uid, *rid;
2147 char *object, *old_object;
2148 ECalComponent *old_comp;
2152 /* Create the cal component */
2153 comp = e_cal_component_new ();
2154 e_cal_component_set_icalcomponent (comp, subcomp);
2156 /* Set the created and last modified times on the component */
2157 current = icaltime_from_timet (time (NULL), 0);
2158 e_cal_component_set_created (comp, ¤t);
2159 e_cal_component_set_last_modified (comp, ¤t);
2161 e_cal_component_get_uid (comp, &uid);
2162 rid = e_cal_component_get_recurid_as_string (comp);
2165 case ICAL_METHOD_PUBLISH:
2166 case ICAL_METHOD_REQUEST:
2167 case ICAL_METHOD_REPLY:
2168 old_comp = lookup_component (cbfile, uid);
2170 old_object = e_cal_component_get_as_string (old_comp);
2171 remove_component (cbfile, old_comp);
2172 add_component (cbfile, comp, FALSE);
2174 object = e_cal_component_get_as_string (comp);
2175 e_cal_backend_notify_object_modified (E_CAL_BACKEND (backend), old_object, object);
2177 g_free (old_object);
2179 add_component (cbfile, comp, FALSE);
2181 object = e_cal_component_get_as_string (comp);
2182 e_cal_backend_notify_object_created (E_CAL_BACKEND (backend), object);
2186 case ICAL_METHOD_ADD:
2187 /* FIXME This should be doable once all the recurid stuff is done */
2188 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2191 case ICAL_METHOD_COUNTER:
2192 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2195 case ICAL_METHOD_DECLINECOUNTER:
2196 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2199 case ICAL_METHOD_CANCEL:
2200 if (cancel_received_object (cbfile, subcomp)) {
2201 object = (char *) icalcomponent_as_ical_string (subcomp);
2202 e_cal_backend_notify_object_removed (E_CAL_BACKEND (backend), uid, object);
2204 /* remove the component from the toplevel VCALENDAR */
2205 icalcomponent_remove_component (toplevel_comp, subcomp);
2206 icalcomponent_free (subcomp);
2210 status = GNOME_Evolution_Calendar_UnsupportedMethod;
2215 g_list_free (comps);
2217 /* Now we remove the components we don't care about */
2218 for (l = del_comps; l; l = l->next) {
2221 icalcomponent_remove_component (toplevel_comp, subcomp);
2222 icalcomponent_free (subcomp);
2225 g_list_free (del_comps);
2227 /* Merge the iCalendar components with our existing VCALENDAR,
2228 resolving any conflicting TZIDs. */
2229 icalcomponent_merge_component (priv->icalcomp, toplevel_comp);
2234 g_hash_table_destroy (tzdata.zones);
2239 static ECalBackendSyncStatus
2240 e_cal_backend_file_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj, GList **users,
2241 char **modified_calobj)
2244 *modified_calobj = g_strdup (calobj);
2246 return GNOME_Evolution_Calendar_Success;
2249 /* Object initialization function for the file backend */
2251 e_cal_backend_file_init (ECalBackendFile *cbfile, ECalBackendFileClass *class)
2253 ECalBackendFilePrivate *priv;
2255 priv = g_new0 (ECalBackendFilePrivate, 1);
2256 cbfile->priv = priv;
2259 priv->file_name = g_strdup ("calendar.ics");
2260 priv->read_only = FALSE;
2261 priv->icalcomp = NULL;
2262 priv->comp_uid_hash = NULL;
2265 /* The timezone defaults to UTC. */
2266 priv->default_zone = icaltimezone_get_utc_timezone ();
2269 /* Class initialization function for the file backend */
2271 e_cal_backend_file_class_init (ECalBackendFileClass *class)
2273 GObjectClass *object_class;
2274 ECalBackendClass *backend_class;
2275 ECalBackendSyncClass *sync_class;
2277 object_class = (GObjectClass *) class;
2278 backend_class = (ECalBackendClass *) class;
2279 sync_class = (ECalBackendSyncClass *) class;
2281 parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
2283 object_class->dispose = e_cal_backend_file_dispose;
2284 object_class->finalize = e_cal_backend_file_finalize;
2286 sync_class->is_read_only_sync = e_cal_backend_file_is_read_only;
2287 sync_class->get_cal_address_sync = e_cal_backend_file_get_cal_address;
2288 sync_class->get_alarm_email_address_sync = e_cal_backend_file_get_alarm_email_address;
2289 sync_class->get_ldap_attribute_sync = e_cal_backend_file_get_ldap_attribute;
2290 sync_class->get_static_capabilities_sync = e_cal_backend_file_get_static_capabilities;
2291 sync_class->open_sync = e_cal_backend_file_open;
2292 sync_class->remove_sync = e_cal_backend_file_remove;
2293 sync_class->create_object_sync = e_cal_backend_file_create_object;
2294 sync_class->modify_object_sync = e_cal_backend_file_modify_object;
2295 sync_class->remove_object_sync = e_cal_backend_file_remove_object;
2296 sync_class->discard_alarm_sync = e_cal_backend_file_discard_alarm;
2297 sync_class->receive_objects_sync = e_cal_backend_file_receive_objects;
2298 sync_class->send_objects_sync = e_cal_backend_file_send_objects;
2299 sync_class->get_default_object_sync = e_cal_backend_file_get_default_object;
2300 sync_class->get_object_sync = e_cal_backend_file_get_object;
2301 sync_class->get_object_list_sync = e_cal_backend_file_get_object_list;
2302 sync_class->get_timezone_sync = e_cal_backend_file_get_timezone;
2303 sync_class->add_timezone_sync = e_cal_backend_file_add_timezone;
2304 sync_class->set_default_timezone_sync = e_cal_backend_file_set_default_timezone;
2305 sync_class->get_freebusy_sync = e_cal_backend_file_get_free_busy;
2306 sync_class->get_changes_sync = e_cal_backend_file_get_changes;
2308 backend_class->is_loaded = e_cal_backend_file_is_loaded;
2309 backend_class->start_query = e_cal_backend_file_start_query;
2310 backend_class->get_mode = e_cal_backend_file_get_mode;
2311 backend_class->set_mode = e_cal_backend_file_set_mode;
2313 backend_class->internal_get_default_timezone = e_cal_backend_file_internal_get_default_timezone;
2314 backend_class->internal_get_timezone = e_cal_backend_file_internal_get_timezone;
2319 * e_cal_backend_file_get_type:
2322 * Registers the #ECalBackendFile class if necessary, and returns the type ID
2325 * Return value: The type ID of the #ECalBackendFile class.
2328 e_cal_backend_file_get_type (void)
2330 static GType e_cal_backend_file_type = 0;
2332 if (!e_cal_backend_file_type) {
2333 static GTypeInfo info = {
2334 sizeof (ECalBackendFileClass),
2335 (GBaseInitFunc) NULL,
2336 (GBaseFinalizeFunc) NULL,
2337 (GClassInitFunc) e_cal_backend_file_class_init,
2339 sizeof (ECalBackendFile),
2341 (GInstanceInitFunc) e_cal_backend_file_init
2343 e_cal_backend_file_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
2344 "ECalBackendFile", &info, 0);
2347 return e_cal_backend_file_type;
2351 e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, const char *file_name)
2353 ECalBackendFilePrivate *priv;
2355 g_return_if_fail (cbfile != NULL);
2356 g_return_if_fail (E_IS_CAL_BACKEND_FILE (cbfile));
2357 g_return_if_fail (file_name != NULL);
2359 priv = cbfile->priv;
2361 if (priv->file_name)
2362 g_free (priv->file_name);
2364 priv->file_name = g_strdup (file_name);
2368 e_cal_backend_file_get_file_name (ECalBackendFile *cbfile)
2370 ECalBackendFilePrivate *priv;
2372 g_return_val_if_fail (cbfile != NULL, NULL);
2373 g_return_val_if_fail (E_IS_CAL_BACKEND_FILE (cbfile), NULL);
2375 priv = cbfile->priv;
2377 return priv->file_name;
2380 ECalBackendSyncStatus
2381 e_cal_backend_file_reload (ECalBackendFile *cbfile)
2383 ECalBackendFilePrivate *priv;
2385 ECalBackendSyncStatus status;
2387 priv = cbfile->priv;
2389 str_uri = get_uri_string (E_CAL_BACKEND (cbfile));
2391 return GNOME_Evolution_Calendar_OtherError;
2393 if (access (str_uri, R_OK) == 0) {
2394 status = reload_cal (cbfile, str_uri);
2395 if (access (str_uri, W_OK) != 0)
2396 priv->read_only = TRUE;
2398 status = GNOME_Evolution_Calendar_NoSuchCal;