Initial revision
[platform/upstream/evolution-data-server.git] / calendar / backends / file / e-cal-backend-file.c
1 /* Evolution calendar - iCalendar file backend
2  *
3  * Copyright (C) 2000-2003 Ximian, Inc.
4  *
5  * Authors: Federico Mena-Quintero <federico@ximian.com>
6  *          Rodrigo Moya <rodrigo@ximian.com>
7  *
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.
11  *
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.
16  *
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.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <unistd.h>
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-xml-hash-utils.h>
33 #include <libecal/e-cal-recur.h>
34 #include <libecal/e-cal-util.h>
35 #include "e-cal-backend-file-events.h"
36 #include "e-cal-backend-util.h"
37 #include "e-cal-backend-sexp.h"
38
39 \f
40
41 /* Placeholder for each component and its recurrences */
42 typedef struct {
43         ECalComponent *full_object;
44         GHashTable *recurrences;
45 } ECalBackendFileObject;
46
47 /* Private part of the ECalBackendFile structure */
48 struct _ECalBackendFilePrivate {
49         /* URI where the calendar data is stored */
50         char *uri;
51
52         /* Filename in the dir */
53         char *file_name;        
54         gboolean read_only;
55
56         /* Toplevel VCALENDAR component */
57         icalcomponent *icalcomp;
58
59         /* All the objects in the calendar, hashed by UID.  The
60          * hash key *is* the uid returned by cal_component_get_uid(); it is not
61          * copied, so don't free it when you remove an object from the hash
62          * table. Each item in the hash table is a ECalBackendFileObject.
63          */
64         GHashTable *comp_uid_hash;
65
66         GList *comp;
67         
68         /* The calendar's default timezone, used for resolving DATE and
69            floating DATE-TIME values. */
70         icaltimezone *default_zone;
71
72         /* The list of live queries */
73         GList *queries;
74 };
75
76 \f
77
78 static void e_cal_backend_file_dispose (GObject *object);
79 static void e_cal_backend_file_finalize (GObject *object);
80
81 static ECalBackendSyncClass *parent_class;
82
83 \f
84
85 /* g_hash_table_foreach() callback to destroy recurrences in the hash table */
86 static void
87 free_recurrence (gpointer key, gpointer value, gpointer data)
88 {
89         char *rid = key;
90         ECalComponent *comp = value;
91
92         g_free (rid);
93         g_object_unref (comp);
94 }
95
96 /* g_hash_table_foreach() callback to destroy a ECalBackendFileObject */
97 static void
98 free_object (gpointer key, gpointer value, gpointer data)
99 {
100         ECalBackendFileObject *obj_data = value;
101
102         g_object_unref (obj_data->full_object);
103         g_hash_table_foreach (obj_data->recurrences, (GHFunc) free_recurrence, NULL);
104         g_hash_table_destroy (obj_data->recurrences);
105 }
106
107 /* Saves the calendar data */
108 static void
109 save (ECalBackendFile *cbfile)
110 {
111         ECalBackendFilePrivate *priv;
112         GnomeVFSURI *uri, *backup_uri;
113         GnomeVFSHandle *handle = NULL;
114         GnomeVFSResult result = GNOME_VFS_ERROR_BAD_FILE;
115         GnomeVFSFileSize out;
116         gchar *tmp, *backup_uristr;
117         char *buf;
118         
119         priv = cbfile->priv;
120         g_assert (priv->uri != NULL);
121         g_assert (priv->icalcomp != NULL);
122
123         uri = gnome_vfs_uri_new (priv->uri);
124         if (!uri)
125                 goto error_malformed_uri;
126
127         /* save calendar to backup file */
128         tmp = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE);
129         if (!tmp) {
130                 gnome_vfs_uri_unref (uri);
131                 goto error_malformed_uri;
132         }
133                 
134         backup_uristr = g_strconcat (tmp, "~", NULL);
135         backup_uri = gnome_vfs_uri_new (backup_uristr);
136
137         g_free (tmp);
138         g_free (backup_uristr);
139
140         if (!backup_uri) {
141                 gnome_vfs_uri_unref (uri);
142                 goto error_malformed_uri;
143         }
144         
145         result = gnome_vfs_create_uri (&handle, backup_uri,
146                                        GNOME_VFS_OPEN_WRITE,
147                                        FALSE, 0666);
148         if (result != GNOME_VFS_OK) {
149                 gnome_vfs_uri_unref (uri);
150                 gnome_vfs_uri_unref (backup_uri);
151                 goto error;
152         }
153
154         buf = icalcomponent_as_ical_string (priv->icalcomp);
155         result = gnome_vfs_write (handle, buf, strlen (buf) * sizeof (char), &out);
156         gnome_vfs_close (handle);
157         if (result != GNOME_VFS_OK) {
158                 gnome_vfs_uri_unref (uri);
159                 gnome_vfs_uri_unref (backup_uri);
160                 goto error;
161         }
162
163         /* now copy the temporary file to the real file */
164         result = gnome_vfs_move_uri (backup_uri, uri, TRUE);
165
166         gnome_vfs_uri_unref (uri);
167         gnome_vfs_uri_unref (backup_uri);
168         if (result != GNOME_VFS_OK)
169                 goto error;
170
171         return;
172
173  error_malformed_uri:
174         e_cal_backend_notify_error (E_CAL_BACKEND (cbfile),
175                                   _("Can't save calendar data: Malformed URI."));
176         return;
177
178  error:
179         e_cal_backend_notify_error (E_CAL_BACKEND (cbfile), gnome_vfs_result_to_string (result));
180         return;
181 }
182
183 /* Dispose handler for the file backend */
184 static void
185 e_cal_backend_file_dispose (GObject *object)
186 {
187         ECalBackendFile *cbfile;
188         ECalBackendFilePrivate *priv;
189
190         cbfile = E_CAL_BACKEND_FILE (object);
191         priv = cbfile->priv;
192
193         /* Save if necessary */
194
195         if (priv->comp_uid_hash) {
196                 g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) free_object, NULL);
197                 g_hash_table_destroy (priv->comp_uid_hash);
198                 priv->comp_uid_hash = NULL;
199         }
200
201         g_list_free (priv->comp);
202         priv->comp = NULL;
203
204         if (priv->icalcomp) {
205                 icalcomponent_free (priv->icalcomp);
206                 priv->icalcomp = NULL;
207         }
208
209         if (G_OBJECT_CLASS (parent_class)->dispose)
210                 (* G_OBJECT_CLASS (parent_class)->dispose) (object);
211 }
212
213 /* Finalize handler for the file backend */
214 static void
215 e_cal_backend_file_finalize (GObject *object)
216 {
217         ECalBackendFile *cbfile;
218         ECalBackendFilePrivate *priv;
219
220         g_return_if_fail (object != NULL);
221         g_return_if_fail (E_IS_CAL_BACKEND_FILE (object));
222
223         cbfile = E_CAL_BACKEND_FILE (object);
224         priv = cbfile->priv;
225
226         /* Clean up */
227
228         if (priv->uri) {
229                 g_free (priv->uri);
230                 priv->uri = NULL;
231         }
232
233         g_free (priv);
234         cbfile->priv = NULL;
235
236         if (G_OBJECT_CLASS (parent_class)->finalize)
237                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
238 }
239
240 \f
241
242 /* Looks up a component by its UID on the backend's component hash table */
243 static ECalComponent *
244 lookup_component (ECalBackendFile *cbfile, const char *uid)
245 {
246         ECalBackendFilePrivate *priv;
247         ECalBackendFileObject *obj_data;
248
249         priv = cbfile->priv;
250
251         obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
252         return obj_data ? obj_data->full_object : NULL;
253 }
254
255 \f
256
257 /* Calendar backend methods */
258
259 /* Is_read_only handler for the file backend */
260 static ECalBackendSyncStatus
261 e_cal_backend_file_is_read_only (ECalBackendSync *backend, EDataCal *cal, gboolean *read_only)
262 {
263         ECalBackendFile *cbfile = (ECalBackendFile *) backend;
264
265         *read_only = cbfile->priv->read_only;
266         
267         return GNOME_Evolution_Calendar_Success;
268 }
269
270 /* Get_email_address handler for the file backend */
271 static ECalBackendSyncStatus
272 e_cal_backend_file_get_cal_address (ECalBackendSync *backend, EDataCal *cal, char **address)
273 {
274         /* A file backend has no particular email address associated
275          * with it (although that would be a useful feature some day).
276          */
277         *address = NULL;
278
279         return GNOME_Evolution_Calendar_Success;
280 }
281
282 static ECalBackendSyncStatus
283 e_cal_backend_file_get_ldap_attribute (ECalBackendSync *backend, EDataCal *cal, char **attribute)
284 {
285         *attribute = NULL;
286         
287         return GNOME_Evolution_Calendar_Success;
288 }
289
290 static ECalBackendSyncStatus
291 e_cal_backend_file_get_alarm_email_address (ECalBackendSync *backend, EDataCal *cal, char **address)
292 {
293         /* A file backend has no particular email address associated
294          * with it (although that would be a useful feature some day).
295          */
296         *address = NULL;
297         
298         return GNOME_Evolution_Calendar_Success;
299 }
300
301 static ECalBackendSyncStatus
302 e_cal_backend_file_get_static_capabilities (ECalBackendSync *backend, EDataCal *cal, char **capabilities)
303 {
304         *capabilities = CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS;
305         
306         return GNOME_Evolution_Calendar_Success;
307 }
308
309 /* function to resolve timezones */
310 static icaltimezone *
311 resolve_tzid (const char *tzid, gpointer user_data)
312 {
313         icalcomponent *vcalendar_comp = user_data;
314
315         if (!tzid || !tzid[0])
316                 return NULL;
317         else if (!strcmp (tzid, "UTC"))
318                 return icaltimezone_get_utc_timezone ();
319
320         return icalcomponent_get_timezone (vcalendar_comp, tzid);
321 }
322
323 /* Checks if the specified component has a duplicated UID and if so changes it */
324 static void
325 check_dup_uid (ECalBackendFile *cbfile, ECalComponent *comp)
326 {
327         ECalBackendFilePrivate *priv;
328         ECalBackendFileObject *obj_data;
329         const char *uid;
330         char *new_uid;
331
332         priv = cbfile->priv;
333
334         e_cal_component_get_uid (comp, &uid);
335
336         obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
337         if (!obj_data)
338                 return; /* Everything is fine */
339
340         g_message ("check_dup_uid(): Got object with duplicated UID `%s', changing it...", uid);
341
342         new_uid = e_cal_component_gen_uid ();
343         e_cal_component_set_uid (comp, new_uid);
344         g_free (new_uid);
345
346         /* FIXME: I think we need to reset the SEQUENCE property and reset the
347          * CREATED/DTSTAMP/LAST-MODIFIED.
348          */
349
350         save (cbfile);
351 }
352
353 static const char *
354 get_rid_string (ECalComponent *comp)
355 {
356         ECalComponentRange range;
357         struct icaltimetype tt;
358                                                                                    
359         e_cal_component_get_recurid (comp, &range);
360         if (!range.datetime.value)
361                 return "0";
362         tt = *range.datetime.value;
363         e_cal_component_free_range (&range);
364                                                                                    
365         return icaltime_is_valid_time (tt) && !icaltime_is_null_time (tt) ?
366                 icaltime_as_ical_string (tt) : "0";
367 }
368
369 static struct icaltimetype
370 get_rid_icaltime (ECalComponent *comp)
371 {
372         ECalComponentRange range;
373         struct icaltimetype tt;
374                                                                                    
375         e_cal_component_get_recurid (comp, &range);
376         if (!range.datetime.value)
377                 return icaltime_null_time ();
378         tt = *range.datetime.value;
379         e_cal_component_free_range (&range);
380                                                                                    
381         return tt;
382 }
383
384 /* Tries to add an icalcomponent to the file backend.  We only store the objects
385  * of the types we support; all others just remain in the toplevel component so
386  * that we don't lose them.
387  */
388 static void
389 add_component (ECalBackendFile *cbfile, ECalComponent *comp, gboolean add_to_toplevel)
390 {
391         ECalBackendFilePrivate *priv;
392         ECalBackendFileObject *obj_data;
393         const char *uid;
394         GSList *categories;
395
396         priv = cbfile->priv;
397
398         if (e_cal_component_is_instance (comp)) { /* FIXME: more checks needed, to detect detached instances */
399                 const char *rid;
400
401                 e_cal_component_get_uid (comp, &uid);
402
403                 obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
404                 if (!obj_data) {
405                         g_warning (G_STRLOC ": Got an instance of a non-existing component");
406                         return;
407                 }
408
409                 rid = get_rid_string (comp);
410                 if (g_hash_table_lookup (obj_data->recurrences, rid)) {
411                         g_warning (G_STRLOC ": Tried to adding an already existing recurrence");
412                         return;
413                 }
414
415                 g_hash_table_insert (obj_data->recurrences, g_strdup (rid), comp);
416         } else {
417                 /* Ensure that the UID is unique; some broken implementations spit
418                  * components with duplicated UIDs.
419                  */
420                 check_dup_uid (cbfile, comp);
421                 e_cal_component_get_uid (comp, &uid);
422
423                 obj_data = g_new0 (ECalBackendFileObject, 1);
424                 obj_data->full_object = comp;
425                 obj_data->recurrences = g_hash_table_new (g_str_hash, g_str_equal);
426
427                 g_hash_table_insert (priv->comp_uid_hash, (gpointer) uid, obj_data);
428         }
429
430         priv->comp = g_list_prepend (priv->comp, comp);
431
432         /* Put the object in the toplevel component if required */
433
434         if (add_to_toplevel) {
435                 icalcomponent *icalcomp;
436
437                 icalcomp = e_cal_component_get_icalcomponent (comp);
438                 g_assert (icalcomp != NULL);
439
440                 icalcomponent_add_component (priv->icalcomp, icalcomp);
441         }
442
443         /* Update the set of categories */
444         e_cal_component_get_categories_list (comp, &categories);
445         e_cal_backend_ref_categories (E_CAL_BACKEND (cbfile), categories);
446         e_cal_component_free_categories_list (categories);
447 }
448
449 /* g_hash_table_foreach() callback to remove recurrences from the calendar */
450 static void
451 remove_recurrence_cb (gpointer key, gpointer value, gpointer data)
452 {
453         GList *l;
454         GSList *categories;
455         icalcomponent *icalcomp;
456         ECalBackendFilePrivate *priv;
457         ECalComponent *comp = value;
458         ECalBackendFile *cbfile = data;
459
460         priv = cbfile->priv;
461
462         /* remove the recurrence from the top-level calendar */
463         icalcomp = e_cal_component_get_icalcomponent (comp);
464         g_assert (icalcomp != NULL);
465
466         icalcomponent_remove_component (priv->icalcomp, icalcomp);
467
468         /* remove it from our mapping */
469         l = g_list_find (priv->comp, comp);
470         priv->comp = g_list_delete_link (priv->comp, l);
471
472         /* update the set of categories */
473         e_cal_component_get_categories_list (comp, &categories);
474         e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
475         e_cal_component_free_categories_list (categories);
476 }
477
478 /* Removes a component from the backend's hash and lists.  Does not perform
479  * notification on the clients.  Also removes the component from the toplevel
480  * icalcomponent.
481  */
482 static void
483 remove_component (ECalBackendFile *cbfile, ECalComponent *comp)
484 {
485         ECalBackendFilePrivate *priv;
486         icalcomponent *icalcomp;
487         const char *uid;
488         GList *l;
489         GSList *categories;
490         ECalBackendFileObject *obj_data;
491
492         priv = cbfile->priv;
493
494         /* Remove the icalcomp from the toplevel */
495
496         icalcomp = e_cal_component_get_icalcomponent (comp);
497         g_assert (icalcomp != NULL);
498
499         icalcomponent_remove_component (priv->icalcomp, icalcomp);
500
501         /* Remove it from our mapping */
502
503         e_cal_component_get_uid (comp, &uid);
504         obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
505         if (!obj_data)
506                 return;
507
508         g_hash_table_remove (priv->comp_uid_hash, uid);
509
510         l = g_list_find (priv->comp, comp);
511         g_assert (l != NULL);
512         priv->comp = g_list_delete_link (priv->comp, l);
513
514         /* remove the recurrences also */
515         g_hash_table_foreach (obj_data->recurrences, (GHFunc) remove_recurrence_cb, cbfile);
516
517         /* Update the set of categories */
518         e_cal_component_get_categories_list (comp, &categories);
519         e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
520         e_cal_component_free_categories_list (categories);
521
522         free_object ((gpointer) uid, (gpointer) obj_data, NULL);
523 }
524
525 /* Scans the toplevel VCALENDAR component and stores the objects it finds */
526 static void
527 scan_vcalendar (ECalBackendFile *cbfile)
528 {
529         ECalBackendFilePrivate *priv;
530         icalcompiter iter;
531
532         priv = cbfile->priv;
533         g_assert (priv->icalcomp != NULL);
534         g_assert (priv->comp_uid_hash != NULL);
535
536         for (iter = icalcomponent_begin_component (priv->icalcomp, ICAL_ANY_COMPONENT);
537              icalcompiter_deref (&iter) != NULL;
538              icalcompiter_next (&iter)) {
539                 icalcomponent *icalcomp;
540                 icalcomponent_kind kind;
541                 ECalComponent *comp;
542
543                 icalcomp = icalcompiter_deref (&iter);
544                 
545                 kind = icalcomponent_isa (icalcomp);
546
547                 if (!(kind == ICAL_VEVENT_COMPONENT
548                       || kind == ICAL_VTODO_COMPONENT
549                       || kind == ICAL_VJOURNAL_COMPONENT))
550                         continue;
551
552                 comp = e_cal_component_new ();
553
554                 if (!e_cal_component_set_icalcomponent (comp, icalcomp))
555                         continue;
556
557                 add_component (cbfile, comp, FALSE);
558         }
559 }
560
561 /* Parses an open iCalendar file and loads it into the backend */
562 static ECalBackendSyncStatus
563 open_cal (ECalBackendFile *cbfile, const char *uristr)
564 {
565         ECalBackendFilePrivate *priv;
566         icalcomponent *icalcomp;
567
568         priv = cbfile->priv;
569
570         icalcomp = e_cal_util_parse_ics_file (uristr);
571         if (!icalcomp)
572                 return GNOME_Evolution_Calendar_OtherError;
573
574         /* FIXME: should we try to demangle XROOT components and
575          * individual components as well?
576          */
577
578         if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
579                 icalcomponent_free (icalcomp);
580
581                 return GNOME_Evolution_Calendar_OtherError;
582         }
583
584         priv->icalcomp = icalcomp;
585
586         priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
587         scan_vcalendar (cbfile);
588
589         priv->uri = g_strdup (uristr);
590
591         return GNOME_Evolution_Calendar_Success;
592 }
593
594 static ECalBackendSyncStatus
595 create_cal (ECalBackendFile *cbfile, const char *uristr)
596 {
597         ECalBackendFilePrivate *priv;
598
599         priv = cbfile->priv;
600
601         /* Create the new calendar information */
602         priv->icalcomp = e_cal_util_new_top_level ();
603
604         /* Create our internal data */
605         priv->comp_uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
606
607         priv->uri = g_strdup (uristr);
608
609         save (cbfile);
610
611         return GNOME_Evolution_Calendar_Success;
612 }
613
614 static char *
615 get_uri_string (ECalBackend *backend)
616 {
617         ECalBackendFile *cbfile;
618         ECalBackendFilePrivate *priv;
619         const char *master_uri;
620         char *full_uri, *str_uri;
621         GnomeVFSURI *uri;
622
623         cbfile = E_CAL_BACKEND_FILE (backend);
624         priv = cbfile->priv;
625         
626         master_uri = e_cal_backend_get_uri (backend);
627         g_message (G_STRLOC ": Trying to open %s", master_uri);
628         
629         /* FIXME Check the error conditions a little more elegantly here */
630         if (g_strrstr ("tasks.ics", master_uri) || g_strrstr ("calendar.ics", master_uri)) {
631                 g_warning (G_STRLOC ": Existing file name %s", master_uri);
632
633                 return NULL;
634         }
635         
636         full_uri = g_strdup_printf ("%s%s%s", master_uri, G_DIR_SEPARATOR_S, priv->file_name);
637         uri = gnome_vfs_uri_new (full_uri);
638         g_free (full_uri);
639         
640         if (!uri)
641                 return NULL;
642
643         str_uri = gnome_vfs_uri_to_string (uri,
644                                            (GNOME_VFS_URI_HIDE_USER_NAME
645                                             | GNOME_VFS_URI_HIDE_PASSWORD
646                                             | GNOME_VFS_URI_HIDE_HOST_NAME
647                                             | GNOME_VFS_URI_HIDE_HOST_PORT
648                                             | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD));
649         gnome_vfs_uri_unref (uri);
650
651         if (!str_uri || !strlen (str_uri)) {
652                 g_free (str_uri);
653
654                 return NULL;
655         }       
656
657         return str_uri;
658 }
659
660 /* Open handler for the file backend */
661 static ECalBackendSyncStatus
662 e_cal_backend_file_open (ECalBackendSync *backend, EDataCal *cal, gboolean only_if_exists)
663 {
664         ECalBackendFile *cbfile;
665         ECalBackendFilePrivate *priv;
666         char *str_uri;
667         ECalBackendSyncStatus status;
668         
669         cbfile = E_CAL_BACKEND_FILE (backend);
670         priv = cbfile->priv;
671
672         /* Claim a succesful open if we are already open */
673         if (priv->uri && priv->comp_uid_hash)
674                 return GNOME_Evolution_Calendar_Success;
675         
676         str_uri = get_uri_string (E_CAL_BACKEND (backend));
677         if (!str_uri)
678                 return GNOME_Evolution_Calendar_OtherError;
679         
680         if (access (str_uri, R_OK) == 0) {
681                 status = open_cal (cbfile, str_uri);
682                 if (access (str_uri, W_OK) != 0)
683                         priv->read_only = TRUE;
684         } else {
685                 if (only_if_exists)
686                         status = GNOME_Evolution_Calendar_NoSuchCal;
687                 else
688                         status = create_cal (cbfile, str_uri);
689         }
690
691         g_free (str_uri);
692
693         return status;
694 }
695
696 static ECalBackendSyncStatus
697 e_cal_backend_file_remove (ECalBackendSync *backend, EDataCal *cal)
698 {
699         ECalBackendFile *cbfile;
700         ECalBackendFilePrivate *priv;
701         char *str_uri;
702         
703         cbfile = E_CAL_BACKEND_FILE (backend);
704         priv = cbfile->priv;
705
706         str_uri = get_uri_string (E_CAL_BACKEND (backend));
707         if (!str_uri)
708                 return GNOME_Evolution_Calendar_OtherError;
709
710         if (access (str_uri, W_OK) != 0) {
711                 g_free (str_uri);
712
713                 return GNOME_Evolution_Calendar_PermissionDenied;
714         }
715
716         /* FIXME Remove backup file and whole directory too? */
717         if (unlink (str_uri) != 0) {
718                 g_free (str_uri);
719
720                 return GNOME_Evolution_Calendar_OtherError;
721         }
722         
723         g_free (str_uri);
724         
725         return GNOME_Evolution_Calendar_Success;
726 }
727
728 /* is_loaded handler for the file backend */
729 static gboolean
730 e_cal_backend_file_is_loaded (ECalBackend *backend)
731 {
732         ECalBackendFile *cbfile;
733         ECalBackendFilePrivate *priv;
734
735         cbfile = E_CAL_BACKEND_FILE (backend);
736         priv = cbfile->priv;
737
738         return (priv->icalcomp != NULL);
739 }
740
741 /* is_remote handler for the file backend */
742 static CalMode
743 e_cal_backend_file_get_mode (ECalBackend *backend)
744 {
745         ECalBackendFile *cbfile;
746         ECalBackendFilePrivate *priv;
747
748         cbfile = E_CAL_BACKEND_FILE (backend);
749         priv = cbfile->priv;
750
751         return CAL_MODE_LOCAL;  
752 }
753
754 /* Set_mode handler for the file backend */
755 static void
756 e_cal_backend_file_set_mode (ECalBackend *backend, CalMode mode)
757 {
758         e_cal_backend_notify_mode (backend,
759                                  GNOME_Evolution_Calendar_CalListener_MODE_NOT_SUPPORTED,
760                                  GNOME_Evolution_Calendar_MODE_LOCAL);
761         
762 }
763
764 static ECalBackendSyncStatus
765 e_cal_backend_file_get_default_object (ECalBackendSync *backend, EDataCal *cal, char **object)
766 {
767         ECalComponent *comp;
768         
769         comp = e_cal_component_new ();
770
771         switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
772         case ICAL_VEVENT_COMPONENT:
773                 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
774                 break;
775         case ICAL_VTODO_COMPONENT:
776                 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
777                 break;
778         case ICAL_VJOURNAL_COMPONENT:
779                 e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
780                 break;
781         default:
782                 g_object_unref (comp);
783                 return GNOME_Evolution_Calendar_ObjectNotFound;
784         }
785         
786         *object = e_cal_component_get_as_string (comp);
787         g_object_unref (comp);
788  
789         return GNOME_Evolution_Calendar_Success;
790 }
791
792 /* Get_object_component handler for the file backend */
793 static ECalBackendSyncStatus
794 e_cal_backend_file_get_object (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *rid, char **object)
795 {
796         ECalBackendFile *cbfile;
797         ECalBackendFilePrivate *priv;
798         ECalBackendFileObject *obj_data;
799         ECalComponent *comp = NULL;
800         gboolean free_comp = FALSE;
801
802         cbfile = E_CAL_BACKEND_FILE (backend);
803         priv = cbfile->priv;
804
805         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_InvalidObject);
806         g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
807         g_assert (priv->comp_uid_hash != NULL);
808
809         obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
810         if (!obj_data)
811                 return GNOME_Evolution_Calendar_ObjectNotFound;
812
813         if (rid && *rid) {
814                 comp = g_hash_table_lookup (obj_data->recurrences, rid);
815                 if (!comp) {
816                         icalcomponent *icalcomp;
817                         struct icaltimetype itt;
818
819                         itt = icaltime_from_string (rid);
820                         icalcomp = e_cal_util_construct_instance (
821                                 e_cal_component_get_icalcomponent (obj_data->full_object),
822                                 itt);
823                         if (!icalcomp)
824                                 return GNOME_Evolution_Calendar_ObjectNotFound;
825
826                         comp = e_cal_component_new ();
827                         free_comp = TRUE;
828                         e_cal_component_set_icalcomponent (comp, icalcomp);
829                 }
830         } else
831                 comp = obj_data->full_object;
832         
833         if (!comp)
834                 return GNOME_Evolution_Calendar_ObjectNotFound;
835
836         *object = e_cal_component_get_as_string (comp);
837
838         if (free_comp)
839                 g_object_unref (comp);
840
841         return GNOME_Evolution_Calendar_Success;
842 }
843
844 /* Get_timezone_object handler for the file backend */
845 static ECalBackendSyncStatus
846 e_cal_backend_file_get_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid, char **object)
847 {
848         ECalBackendFile *cbfile;
849         ECalBackendFilePrivate *priv;
850         icaltimezone *zone;
851         icalcomponent *icalcomp;
852
853         cbfile = E_CAL_BACKEND_FILE (backend);
854         priv = cbfile->priv;
855
856         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
857         g_return_val_if_fail (tzid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
858
859         if (!strcmp (tzid, "UTC")) {
860                 zone = icaltimezone_get_utc_timezone ();
861         } else {
862                 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
863                 if (!zone) {
864                         zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
865                         if (!zone)
866                                 return GNOME_Evolution_Calendar_ObjectNotFound;
867                 }
868         }
869         
870         icalcomp = icaltimezone_get_component (zone);
871         if (!icalcomp)
872                 return GNOME_Evolution_Calendar_InvalidObject;
873
874         *object = g_strdup (icalcomponent_as_ical_string (icalcomp));
875
876         return GNOME_Evolution_Calendar_Success;
877 }
878
879 /* Add_timezone handler for the file backend */
880 static ECalBackendSyncStatus
881 e_cal_backend_file_add_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzobj)
882 {
883         icalcomponent *tz_comp;
884         ECalBackendFile *cbfile;
885         ECalBackendFilePrivate *priv;
886
887         cbfile = (ECalBackendFile *) backend;
888
889         g_return_val_if_fail (E_IS_CAL_BACKEND_FILE (cbfile), GNOME_Evolution_Calendar_OtherError);
890         g_return_val_if_fail (tzobj != NULL, GNOME_Evolution_Calendar_OtherError);
891
892         priv = cbfile->priv;
893
894         tz_comp = icalparser_parse_string (tzobj);
895         if (!tz_comp)
896                 return GNOME_Evolution_Calendar_InvalidObject;
897
898         if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
899                 icaltimezone *zone;
900
901                 zone = icaltimezone_new ();
902                 icaltimezone_set_component (zone, tz_comp);
903                 if (!icalcomponent_get_timezone (priv->icalcomp,
904                                                  icaltimezone_get_tzid (zone))) {
905                         icalcomponent_add_component (priv->icalcomp, tz_comp);
906                         save (cbfile);
907                 }
908
909                 icaltimezone_free (zone, 1);
910         }
911
912         return GNOME_Evolution_Calendar_Success;
913 }
914
915
916 static ECalBackendSyncStatus
917 e_cal_backend_file_set_default_timezone (ECalBackendSync *backend, EDataCal *cal, const char *tzid)
918 {
919         ECalBackendFile *cbfile;
920         ECalBackendFilePrivate *priv;
921         icaltimezone *zone;
922
923         cbfile = E_CAL_BACKEND_FILE (backend);
924         priv = cbfile->priv;
925
926         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
927
928         /* Look up the VTIMEZONE in our icalcomponent. */
929         zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
930         if (!zone)
931                 return GNOME_Evolution_Calendar_ObjectNotFound;
932
933         /* Set the default timezone to it. */
934         priv->default_zone = zone;
935
936         return GNOME_Evolution_Calendar_Success;
937 }
938
939 typedef struct {
940         GList *obj_list;
941         gboolean search_needed;
942         const char *query;
943         ECalBackendSExp *obj_sexp;
944         ECalBackend *backend;
945         icaltimezone *default_zone;
946 } MatchObjectData;
947
948 static void
949 match_recurrence_sexp (gpointer key, gpointer value, gpointer data)
950 {
951         ECalComponent *comp = value;
952         MatchObjectData *match_data = data;
953
954         if ((!match_data->search_needed) ||
955             (e_cal_backend_sexp_match_comp (match_data->obj_sexp, comp, match_data->backend))) {
956                 match_data->obj_list = g_list_append (match_data->obj_list,
957                                                       e_cal_component_get_as_string (comp));
958         }
959 }
960
961 static void
962 match_object_sexp (gpointer key, gpointer value, gpointer data)
963 {
964         ECalBackendFileObject *obj_data = value;
965         MatchObjectData *match_data = data;
966
967         if ((!match_data->search_needed) ||
968             (e_cal_backend_sexp_match_comp (match_data->obj_sexp, obj_data->full_object, match_data->backend))) {
969                 match_data->obj_list = g_list_append (match_data->obj_list,
970                                                       e_cal_component_get_as_string (obj_data->full_object));
971
972                 /* match also recurrences */
973                 g_hash_table_foreach (obj_data->recurrences,
974                                       (GHFunc) match_recurrence_sexp,
975                                       match_data);
976         }
977 }
978
979 /* Get_objects_in_range handler for the file backend */
980 static ECalBackendSyncStatus
981 e_cal_backend_file_get_object_list (ECalBackendSync *backend, EDataCal *cal, const char *sexp, GList **objects)
982 {
983         ECalBackendFile *cbfile;
984         ECalBackendFilePrivate *priv;
985         MatchObjectData match_data;
986
987         cbfile = E_CAL_BACKEND_FILE (backend);
988         priv = cbfile->priv;
989
990         g_message (G_STRLOC ": Getting object list (%s)", sexp);
991
992         match_data.search_needed = TRUE;
993         match_data.query = sexp;
994         match_data.obj_list = NULL;
995         match_data.backend = E_CAL_BACKEND (backend);
996         match_data.default_zone = priv->default_zone;
997
998         if (!strcmp (sexp, "#t"))
999                 match_data.search_needed = FALSE;
1000
1001         match_data.obj_sexp = e_cal_backend_sexp_new (sexp);
1002         if (!match_data.obj_sexp)
1003                 return GNOME_Evolution_Calendar_InvalidQuery;
1004
1005         g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
1006
1007         *objects = match_data.obj_list;
1008         
1009         return GNOME_Evolution_Calendar_Success;        
1010 }
1011
1012 /* get_query handler for the file backend */
1013 static void
1014 e_cal_backend_file_start_query (ECalBackend *backend, EDataCalView *query)
1015 {
1016         ECalBackendFile *cbfile;
1017         ECalBackendFilePrivate *priv;
1018         MatchObjectData match_data;
1019
1020         cbfile = E_CAL_BACKEND_FILE (backend);
1021         priv = cbfile->priv;
1022
1023         g_message (G_STRLOC ": Starting query (%s)", e_data_cal_view_get_text (query));
1024
1025         /* try to match all currently existing objects */
1026         match_data.search_needed = TRUE;
1027         match_data.query = e_data_cal_view_get_text (query);
1028         match_data.obj_list = NULL;
1029         match_data.backend = backend;
1030         match_data.default_zone = priv->default_zone;
1031
1032         if (!strcmp (match_data.query, "#t"))
1033                 match_data.search_needed = FALSE;
1034
1035         match_data.obj_sexp = e_data_cal_view_get_object_sexp (query);
1036         if (!match_data.obj_sexp) {
1037                 e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_InvalidQuery);
1038                 return;
1039         }
1040
1041         g_hash_table_foreach (priv->comp_uid_hash, (GHFunc) match_object_sexp, &match_data);
1042
1043         /* notify listeners of all objects */
1044         if (match_data.obj_list) {
1045                 e_data_cal_view_notify_objects_added (query, (const GList *) match_data.obj_list);
1046
1047                 /* free memory */
1048                 g_list_foreach (match_data.obj_list, (GFunc) g_free, NULL);
1049                 g_list_free (match_data.obj_list);
1050         }
1051
1052         e_data_cal_view_notify_done (query, GNOME_Evolution_Calendar_Success);
1053 }
1054
1055 static gboolean
1056 free_busy_instance (ECalComponent *comp,
1057                     time_t        instance_start,
1058                     time_t        instance_end,
1059                     gpointer      data)
1060 {
1061         icalcomponent *vfb = data;
1062         icalproperty *prop;
1063         icalparameter *param;
1064         struct icalperiodtype ipt;
1065         icaltimezone *utc_zone;
1066
1067         utc_zone = icaltimezone_get_utc_timezone ();
1068
1069         ipt.start = icaltime_from_timet_with_zone (instance_start, FALSE, utc_zone);
1070         ipt.end = icaltime_from_timet_with_zone (instance_end, FALSE, utc_zone);
1071         ipt.duration = icaldurationtype_null_duration ();
1072         
1073         /* add busy information to the vfb component */
1074         prop = icalproperty_new (ICAL_FREEBUSY_PROPERTY);
1075         icalproperty_set_freebusy (prop, ipt);
1076         
1077         param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
1078         icalproperty_add_parameter (prop, param);
1079         
1080         icalcomponent_add_property (vfb, prop);
1081
1082         return TRUE;
1083 }
1084
1085 static icalcomponent *
1086 create_user_free_busy (ECalBackendFile *cbfile, const char *address, const char *cn,
1087                        time_t start, time_t end)
1088 {       
1089         ECalBackendFilePrivate *priv;
1090         GList *l;
1091         icalcomponent *vfb;
1092         icaltimezone *utc_zone;
1093         ECalBackendSExp *obj_sexp;
1094         char *query;
1095         
1096         priv = cbfile->priv;
1097
1098         /* create the (unique) VFREEBUSY object that we'll return */
1099         vfb = icalcomponent_new_vfreebusy ();
1100         if (address != NULL) {
1101                 icalproperty *prop;
1102                 icalparameter *param;
1103                 
1104                 prop = icalproperty_new_organizer (address);
1105                 if (prop != NULL && cn != NULL) {
1106                         param = icalparameter_new_cn (cn);
1107                         icalproperty_add_parameter (prop, param);                       
1108                 }
1109                 if (prop != NULL)
1110                         icalcomponent_add_property (vfb, prop);         
1111         }
1112         utc_zone = icaltimezone_get_utc_timezone ();
1113         icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
1114         icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));
1115
1116         /* add all objects in the given interval */
1117         query = g_strdup_printf ("occur-in-time-range? %lu %lu", start, end);
1118         obj_sexp = e_cal_backend_sexp_new (query);
1119         g_free (query);
1120
1121         if (!obj_sexp)
1122                 return vfb;
1123
1124         for (l = priv->comp; l; l = l->next) {
1125                 ECalComponent *comp = l->data;
1126                 icalcomponent *icalcomp, *vcalendar_comp;
1127                 icalproperty *prop;
1128                 
1129                 icalcomp = e_cal_component_get_icalcomponent (comp);
1130                 if (!icalcomp)
1131                         continue;
1132
1133                 /* If the event is TRANSPARENT, skip it. */
1134                 prop = icalcomponent_get_first_property (icalcomp,
1135                                                          ICAL_TRANSP_PROPERTY);
1136                 if (prop) {
1137                         icalproperty_transp transp_val = icalproperty_get_transp (prop);
1138                         if (transp_val == ICAL_TRANSP_TRANSPARENT ||
1139                             transp_val == ICAL_TRANSP_TRANSPARENTNOCONFLICT)
1140                                 continue;
1141                 }
1142         
1143                 if (!e_cal_backend_sexp_match_comp (obj_sexp, l->data, E_CAL_BACKEND (cbfile)))
1144                         continue;
1145                 
1146                 vcalendar_comp = icalcomponent_get_parent (icalcomp);
1147                 e_cal_recur_generate_instances (comp, start, end,
1148                                                 free_busy_instance,
1149                                                 vfb,
1150                                                 resolve_tzid,
1151                                                 vcalendar_comp,
1152                                                 priv->default_zone);
1153         }
1154
1155         return vfb;     
1156 }
1157
1158 /* Get_free_busy handler for the file backend */
1159 static ECalBackendSyncStatus
1160 e_cal_backend_file_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GList *users,
1161                                 time_t start, time_t end, GList **freebusy)
1162 {
1163         ECalBackendFile *cbfile;
1164         ECalBackendFilePrivate *priv;
1165         gchar *address, *name;  
1166         icalcomponent *vfb;
1167         char *calobj;
1168         GList *l;
1169
1170         cbfile = E_CAL_BACKEND_FILE (backend);
1171         priv = cbfile->priv;
1172
1173         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1174         g_return_val_if_fail (start != -1 && end != -1, GNOME_Evolution_Calendar_InvalidRange);
1175         g_return_val_if_fail (start <= end, GNOME_Evolution_Calendar_InvalidRange);
1176
1177         *freebusy = NULL;
1178         
1179         if (users == NULL) {
1180                 if (e_cal_backend_mail_account_get_default (&address, &name)) {
1181                         vfb = create_user_free_busy (cbfile, address, name, start, end);
1182                         calobj = icalcomponent_as_ical_string (vfb);
1183                         *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1184                         icalcomponent_free (vfb);
1185                         g_free (address);
1186                         g_free (name);
1187                 }               
1188         } else {
1189                 for (l = users; l != NULL; l = l->next ) {
1190                         address = l->data;                      
1191                         if (e_cal_backend_mail_account_is_valid (address, &name)) {
1192                                 vfb = create_user_free_busy (cbfile, address, name, start, end);
1193                                 calobj = icalcomponent_as_ical_string (vfb);
1194                                 *freebusy = g_list_append (*freebusy, g_strdup (calobj));
1195                                 icalcomponent_free (vfb);
1196                                 g_free (name);
1197                         }
1198                 }               
1199         }
1200
1201         return GNOME_Evolution_Calendar_Success;
1202 }
1203
1204 typedef struct 
1205 {
1206         ECalBackendFile *backend;
1207         icalcomponent_kind kind;
1208         GList *deletes;
1209         EXmlHash *ehash;
1210 } ECalBackendFileComputeChangesData;
1211
1212 static void
1213 e_cal_backend_file_compute_changes_foreach_key (const char *key, gpointer data)
1214 {
1215         ECalBackendFileComputeChangesData *be_data = data;
1216         
1217         if (!lookup_component (be_data->backend, key)) {
1218                 ECalComponent *comp;
1219
1220                 comp = e_cal_component_new ();
1221                 if (be_data->kind == ICAL_VTODO_COMPONENT)
1222                         e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
1223                 else
1224                         e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
1225
1226                 e_cal_component_set_uid (comp, key);
1227                 be_data->deletes = g_list_prepend (be_data->deletes, e_cal_component_get_as_string (comp));
1228
1229                 e_xmlhash_remove (be_data->ehash, key);
1230         }
1231 }
1232
1233 static ECalBackendSyncStatus
1234 e_cal_backend_file_compute_changes (ECalBackendFile *cbfile, const char *change_id,
1235                                   GList **adds, GList **modifies, GList **deletes)
1236 {
1237         ECalBackendFilePrivate *priv;
1238         char    *filename;
1239         EXmlHash *ehash;
1240         ECalBackendFileComputeChangesData be_data;
1241         GList *i;
1242
1243         priv = cbfile->priv;
1244
1245         /* FIXME Will this always work? */
1246         filename = g_strdup_printf ("%s/%s.db", priv->uri, change_id);
1247         ehash = e_xmlhash_new (filename);
1248         g_free (filename);
1249         
1250         /* Calculate adds and modifies */
1251         for (i = priv->comp; i != NULL; i = i->next) {
1252                 const char *uid;
1253                 char *calobj;
1254
1255                 e_cal_component_get_uid (i->data, &uid);
1256                 calobj = e_cal_component_get_as_string (i->data);
1257
1258                 g_assert (calobj != NULL);
1259
1260                 /* check what type of change has occurred, if any */
1261                 switch (e_xmlhash_compare (ehash, uid, calobj)) {
1262                 case E_XMLHASH_STATUS_SAME:
1263                         break;
1264                 case E_XMLHASH_STATUS_NOT_FOUND:
1265                         *adds = g_list_prepend (*adds, g_strdup (calobj));
1266                         e_xmlhash_add (ehash, uid, calobj);
1267                         break;
1268                 case E_XMLHASH_STATUS_DIFFERENT:
1269                         *modifies = g_list_prepend (*modifies, g_strdup (calobj));
1270                         e_xmlhash_add (ehash, uid, calobj);
1271                         break;
1272                 }
1273
1274                 g_free (calobj);
1275         }
1276
1277         /* Calculate deletions */
1278         be_data.backend = cbfile;
1279         be_data.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbfile));
1280         be_data.deletes = NULL;
1281         be_data.ehash = ehash;
1282         e_xmlhash_foreach_key (ehash, (EXmlHashFunc)e_cal_backend_file_compute_changes_foreach_key, &be_data);
1283
1284         *deletes = be_data.deletes;
1285
1286         e_xmlhash_write (ehash);
1287         e_xmlhash_destroy (ehash);
1288         
1289         return GNOME_Evolution_Calendar_Success;
1290 }
1291
1292 /* Get_changes handler for the file backend */
1293 static ECalBackendSyncStatus
1294 e_cal_backend_file_get_changes (ECalBackendSync *backend, EDataCal *cal, const char *change_id,
1295                               GList **adds, GList **modifies, GList **deletes)
1296 {
1297         ECalBackendFile *cbfile;
1298         ECalBackendFilePrivate *priv;
1299
1300         cbfile = E_CAL_BACKEND_FILE (backend);
1301         priv = cbfile->priv;
1302
1303         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1304         g_return_val_if_fail (change_id != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1305
1306         return e_cal_backend_file_compute_changes (cbfile, change_id, adds, modifies, deletes);
1307 }
1308
1309 /* Discard_alarm handler for the file backend */
1310 static ECalBackendSyncStatus
1311 e_cal_backend_file_discard_alarm (ECalBackendSync *backend, EDataCal *cal, const char *uid, const char *auid)
1312 {
1313         /* we just do nothing with the alarm */
1314         return GNOME_Evolution_Calendar_Success;
1315 }
1316
1317 static ECalBackendSyncStatus
1318 e_cal_backend_file_create_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, char **uid)
1319 {
1320         ECalBackendFile *cbfile;
1321         ECalBackendFilePrivate *priv;
1322         icalcomponent *icalcomp;
1323         icalcomponent_kind kind;
1324         ECalComponent *comp;
1325         const char *comp_uid;
1326         struct icaltimetype current;
1327         
1328         cbfile = E_CAL_BACKEND_FILE (backend);
1329         priv = cbfile->priv;
1330
1331         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1332         g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1333
1334         icalcomp = icalparser_parse_string ((char *) calobj);
1335         if (!icalcomp)
1336                 return GNOME_Evolution_Calendar_InvalidObject;
1337
1338         /* FIXME Check kind with the parent */
1339         kind = icalcomponent_isa (icalcomp);
1340         if (kind != ICAL_VEVENT_COMPONENT && kind != ICAL_VTODO_COMPONENT) {
1341                 icalcomponent_free (icalcomp);
1342                 return GNOME_Evolution_Calendar_InvalidObject;
1343         }
1344
1345         /* Get the UID */
1346         comp_uid = icalcomponent_get_uid (icalcomp);
1347         
1348         /* check the object is not in our cache */
1349         if (lookup_component (cbfile, comp_uid)) {
1350                 icalcomponent_free (icalcomp);
1351                 return GNOME_Evolution_Calendar_CardIdAlreadyExists;
1352         }
1353
1354         /* Create the cal component */
1355         comp = e_cal_component_new ();
1356         e_cal_component_set_icalcomponent (comp, icalcomp);
1357
1358         /* Set the created and last modified times on the component */
1359         current = icaltime_from_timet (time (NULL), 0);
1360         e_cal_component_set_created (comp, &current);
1361         e_cal_component_set_last_modified (comp, &current);
1362
1363         /* Add the object */
1364         add_component (cbfile, comp, TRUE);
1365
1366         /* Save the file */
1367         save (cbfile);
1368
1369         /* Return the UID */
1370         if (uid)
1371                 *uid = g_strdup (comp_uid);
1372
1373         return GNOME_Evolution_Calendar_Success;
1374 }
1375
1376 static ECalBackendSyncStatus
1377 e_cal_backend_file_modify_object (ECalBackendSync *backend, EDataCal *cal, const char *calobj, 
1378                                 CalObjModType mod, char **old_object)
1379 {
1380         ECalBackendFile *cbfile;
1381         ECalBackendFilePrivate *priv;
1382         icalcomponent *icalcomp;
1383         icalcomponent_kind kind;
1384         const char *comp_uid, *rid;
1385         char *real_rid;
1386         ECalComponent *comp, *recurrence;
1387         ECalBackendFileObject *obj_data;
1388         struct icaltimetype current;
1389
1390         cbfile = E_CAL_BACKEND_FILE (backend);
1391         priv = cbfile->priv;
1392                 
1393         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1394         g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1395
1396         icalcomp = icalparser_parse_string ((char *) calobj);
1397         if (!icalcomp)
1398                 return GNOME_Evolution_Calendar_InvalidObject;
1399
1400         /* check kind with the parent */
1401         kind = icalcomponent_isa (icalcomp);
1402         if (kind != ICAL_VEVENT_COMPONENT && kind != ICAL_VTODO_COMPONENT) {
1403                 icalcomponent_free (icalcomp);
1404                 return GNOME_Evolution_Calendar_InvalidObject;
1405         }
1406
1407         /* Get the uid */
1408         comp_uid = icalcomponent_get_uid (icalcomp);
1409
1410         /* Get the object from our cache */
1411         if (!(obj_data = g_hash_table_lookup (priv->comp_uid_hash, comp_uid))) {
1412                 icalcomponent_free (icalcomp);
1413                 return GNOME_Evolution_Calendar_ObjectNotFound;
1414         }
1415
1416         /* Create the cal component */
1417         comp = e_cal_component_new ();
1418         e_cal_component_set_icalcomponent (comp, icalcomp);
1419         
1420         /* Set the last modified time on the component */
1421         current = icaltime_from_timet (time (NULL), 0);
1422         e_cal_component_set_last_modified (comp, &current);
1423
1424         /* handle mod_type */
1425         switch (mod) {
1426         case CALOBJ_MOD_THIS :
1427                 rid = get_rid_string (comp);
1428                 if (!rid || !*rid) {
1429                         g_object_unref (comp);
1430                         return GNOME_Evolution_Calendar_ObjectNotFound;
1431                 }
1432
1433                 if (g_hash_table_lookup_extended (obj_data->recurrences, rid,
1434                                                   &real_rid, &recurrence)) {
1435                         /* remove the component from our data */
1436                         icalcomponent_remove_component (priv->icalcomp,
1437                                                         e_cal_component_get_icalcomponent (recurrence));
1438                         priv->comp = g_list_remove (priv->comp, recurrence);
1439                         g_hash_table_remove (obj_data->recurrences, rid);
1440
1441                         /* free memory */
1442                         g_free (real_rid);
1443                         g_object_unref (recurrence);
1444                 } else {
1445                         char *old, *new;
1446
1447                         old = e_cal_component_get_as_string (obj_data->full_object);
1448
1449                         e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
1450                                                      get_rid_icaltime (comp),
1451                                                      mod);
1452
1453                         new = e_cal_component_get_as_string (obj_data->full_object);
1454
1455                         e_cal_backend_notify_object_modified (E_CAL_BACKEND (backend), old, new);
1456
1457                         g_free (old);
1458                         g_free (new);
1459                 }
1460
1461                 /* add the detached instance */
1462                 g_hash_table_insert (obj_data->recurrences, g_strdup (get_rid_string (comp)), comp);
1463                 break;
1464         case CALOBJ_MOD_THISANDPRIOR :
1465                 break;
1466         case CALOBJ_MOD_THISANDFUTURE :
1467                 break;
1468         case CALOBJ_MOD_ALL :
1469                 /* in this case, we blow away all recurrences, and start over
1470                    with a clean component */
1471                 /* Remove the old version */
1472                 remove_component (cbfile, obj_data->full_object);
1473
1474                 /* Add the new object */
1475                 add_component (cbfile, comp, TRUE);
1476                 break;
1477         }
1478
1479         save (cbfile);
1480
1481         if (old_object)
1482                 *old_object = e_cal_component_get_as_string (comp);
1483
1484         return GNOME_Evolution_Calendar_Success;
1485 }
1486
1487 static void
1488 remove_instance (ECalBackendFile *cbfile, ECalBackendFileObject *obj_data, const char *rid)
1489 {
1490         char *hash_rid;
1491         ECalComponent *comp;
1492         GSList *categories;
1493
1494         if (!rid || !*rid)
1495                 return;
1496
1497         if (g_hash_table_lookup_extended (obj_data->recurrences, rid, &hash_rid, &comp)) {
1498                 /* remove the component from our data */
1499                 icalcomponent_remove_component (cbfile->priv->icalcomp,
1500                                                 e_cal_component_get_icalcomponent (comp));
1501                 cbfile->priv->comp = g_list_remove (cbfile->priv->comp, comp);
1502                 g_hash_table_remove (obj_data->recurrences, rid);
1503
1504                 /* update the set of categories */
1505                 e_cal_component_get_categories_list (comp, &categories);
1506                 e_cal_backend_unref_categories (E_CAL_BACKEND (cbfile), categories);
1507                 e_cal_component_free_categories_list (categories);
1508
1509                 /* free memory */
1510                 g_free (hash_rid);
1511                 g_object_unref (comp);
1512
1513                 return;
1514         }
1515
1516         /* remove the component from our data, temporarily */
1517         icalcomponent_remove_component (cbfile->priv->icalcomp,
1518                                         e_cal_component_get_icalcomponent (obj_data->full_object));
1519         cbfile->priv->comp = g_list_remove (cbfile->priv->comp, obj_data->full_object);
1520
1521         e_cal_util_remove_instances (e_cal_component_get_icalcomponent (obj_data->full_object),
1522                                      icaltime_from_string (rid), CALOBJ_MOD_THIS);
1523
1524         /* add the modified object to the beginning of the list, 
1525            so that it's always before any detached instance we
1526            might have */
1527         cbfile->priv->comp = g_list_prepend (cbfile->priv->comp, obj_data->full_object);
1528 }
1529
1530 typedef struct {
1531         ECalBackendFile *cbfile;
1532         ECalBackendFileObject *obj_data;
1533         const char *rid;
1534         CalObjModType mod;
1535 } RemoveRecurrenceData;
1536
1537 static gboolean
1538 remove_object_instance_cb (gpointer key, gpointer value, gpointer user_data)
1539 {
1540         time_t fromtt, instancett;
1541         GSList *categories;
1542         char *rid = key;
1543         ECalComponent *instance = value;
1544         RemoveRecurrenceData *rrdata = user_data;
1545
1546         fromtt = icaltime_as_timet (icaltime_from_string (rrdata->rid));
1547         instancett = icaltime_as_timet (get_rid_icaltime (instance));
1548
1549         if (fromtt > 0 && instancett > 0) {
1550                 if ((rrdata->mod == CALOBJ_MOD_THISANDPRIOR && instancett <= fromtt) ||
1551                     (rrdata->mod == CALOBJ_MOD_THISANDFUTURE && instancett >= fromtt)) {
1552                         /* remove the component from our data */
1553                         icalcomponent_remove_component (rrdata->cbfile->priv->icalcomp,
1554                                                         e_cal_component_get_icalcomponent (instance));
1555                         rrdata->cbfile->priv->comp = g_list_remove (rrdata->cbfile->priv->comp, instance);
1556
1557                         /* update the set of categories */
1558                         e_cal_component_get_categories_list (instance, &categories);
1559                         e_cal_backend_unref_categories (E_CAL_BACKEND (rrdata->cbfile), categories);
1560                         e_cal_component_free_categories_list (categories);
1561
1562                         /* free memory */
1563                         g_free (rid);
1564                         g_object_unref (instance);
1565
1566                         return TRUE;
1567                 }
1568         }
1569
1570         return FALSE;
1571 }
1572
1573 /* Remove_object handler for the file backend */
1574 static ECalBackendSyncStatus
1575 e_cal_backend_file_remove_object (ECalBackendSync *backend, EDataCal *cal,
1576                                 const char *uid, const char *rid,
1577                                 CalObjModType mod, char **object)
1578 {
1579         ECalBackendFile *cbfile;
1580         ECalBackendFilePrivate *priv;
1581         ECalBackendFileObject *obj_data;
1582         ECalComponent *comp;
1583         GSList *categories;
1584         RemoveRecurrenceData rrdata;
1585
1586         cbfile = E_CAL_BACKEND_FILE (backend);
1587         priv = cbfile->priv;
1588
1589         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_NoSuchCal);
1590         g_return_val_if_fail (uid != NULL, GNOME_Evolution_Calendar_ObjectNotFound);
1591
1592         obj_data = g_hash_table_lookup (priv->comp_uid_hash, uid);
1593         if (!obj_data)
1594                 return GNOME_Evolution_Calendar_ObjectNotFound;
1595
1596         comp = obj_data->full_object;
1597
1598         switch (mod) {
1599         case CALOBJ_MOD_ALL :
1600                 *object = e_cal_component_get_as_string (comp);
1601                 remove_component (cbfile, comp);
1602                 break;
1603         case CALOBJ_MOD_THIS :
1604                 if (!rid || !*rid)
1605                         return GNOME_Evolution_Calendar_ObjectNotFound;
1606
1607                 remove_instance (cbfile, obj_data, rid);
1608                 break;
1609         case CALOBJ_MOD_THISANDPRIOR :
1610         case CALOBJ_MOD_THISANDFUTURE :
1611                 if (!rid || !*rid)
1612                         return GNOME_Evolution_Calendar_ObjectNotFound;
1613
1614                 /* remove the component from our data, temporarily */
1615                 icalcomponent_remove_component (priv->icalcomp,
1616                                                 e_cal_component_get_icalcomponent (comp));
1617                 priv->comp = g_list_remove (priv->comp, comp);
1618
1619                 e_cal_util_remove_instances (e_cal_component_get_icalcomponent (comp),
1620                                            icaltime_from_string (rid), mod);
1621
1622                 /* now remove all detached instances */
1623                 rrdata.cbfile = cbfile;
1624                 rrdata.obj_data = obj_data;
1625                 rrdata.rid = rid;
1626                 rrdata.mod = mod;
1627                 g_hash_table_foreach_remove (obj_data->recurrences, (GHRFunc) remove_object_instance_cb, &rrdata);
1628
1629                 /* add the modified object to the beginning of the list, 
1630                    so that it's always before any detached instance we
1631                    might have */
1632                 priv->comp = g_list_prepend (priv->comp, comp);
1633                 break;
1634         }
1635
1636         save (cbfile);
1637
1638         return GNOME_Evolution_Calendar_Success;
1639 }
1640
1641 static gboolean
1642 cancel_received_object (ECalBackendFile *cbfile, icalcomponent *icalcomp)
1643 {
1644         ECalComponent *old_comp;
1645
1646         /* Find the old version of the component. */
1647         old_comp = lookup_component (cbfile, icalcomponent_get_uid (icalcomp));
1648         if (!old_comp)
1649                 return FALSE;
1650
1651         /* And remove it */
1652         remove_component (cbfile, old_comp);
1653
1654         return TRUE;
1655 }
1656
1657 typedef struct {
1658         GHashTable *zones;
1659         
1660         gboolean found;
1661 } ECalBackendFileTzidData;
1662
1663 static void
1664 check_tzids (icalparameter *param, void *data)
1665 {
1666         ECalBackendFileTzidData *tzdata = data;
1667         const char *tzid;
1668         
1669         tzid = icalparameter_get_tzid (param);
1670         if (!tzid || g_hash_table_lookup (tzdata->zones, tzid))
1671                 tzdata->found = FALSE;
1672 }
1673
1674 /* Update_objects handler for the file backend. */
1675 static ECalBackendSyncStatus
1676 e_cal_backend_file_receive_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
1677 {
1678         ECalBackendFile *cbfile;
1679         ECalBackendFilePrivate *priv;
1680         icalcomponent *toplevel_comp, *icalcomp = NULL;
1681         icalcomponent_kind kind;
1682         icalproperty_method method;
1683         icalcomponent *subcomp;
1684         GList *comps, *l;
1685         ECalBackendFileTzidData tzdata;
1686         ECalBackendSyncStatus status = GNOME_Evolution_Calendar_Success;
1687
1688         cbfile = E_CAL_BACKEND_FILE (backend);
1689         priv = cbfile->priv;
1690
1691         g_return_val_if_fail (priv->icalcomp != NULL, GNOME_Evolution_Calendar_InvalidObject);
1692         g_return_val_if_fail (calobj != NULL, GNOME_Evolution_Calendar_InvalidObject);
1693
1694         /* Pull the component from the string and ensure that it is sane */
1695         toplevel_comp = icalparser_parse_string ((char *) calobj);
1696         if (!toplevel_comp)
1697                 return GNOME_Evolution_Calendar_InvalidObject;
1698
1699         kind = icalcomponent_isa (toplevel_comp);
1700         if (kind != ICAL_VCALENDAR_COMPONENT) {
1701                 /* If its not a VCALENDAR, make it one to simplify below */
1702                 icalcomp = toplevel_comp;
1703                 toplevel_comp = e_cal_util_new_top_level ();
1704                 icalcomponent_add_component (toplevel_comp, icalcomp);  
1705         }
1706
1707         method = icalcomponent_get_method (toplevel_comp);
1708
1709         /* Build a list of timezones so we can make sure all the objects have valid info */
1710         tzdata.zones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1711
1712         subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_VTIMEZONE_COMPONENT);
1713         while (subcomp) {
1714                 icaltimezone *zone;
1715                 
1716                 zone = icaltimezone_new ();
1717                 if (icaltimezone_set_component (zone, subcomp))
1718                         g_hash_table_insert (tzdata.zones, g_strdup (icaltimezone_get_tzid (zone)), NULL);
1719                 
1720                 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_VTIMEZONE_COMPONENT);
1721         }       
1722
1723         /* First we make sure all the components are usuable */
1724         comps = NULL;
1725         subcomp = icalcomponent_get_first_component (toplevel_comp, ICAL_ANY_COMPONENT);
1726         while (subcomp) {
1727                 /* We ignore anything except VEVENT, VTODO and VJOURNAL
1728                    components. */
1729                 icalcomponent_kind child_kind = icalcomponent_isa (subcomp);
1730
1731                 switch (child_kind) {
1732                 case ICAL_VEVENT_COMPONENT:
1733                 case ICAL_VTODO_COMPONENT:
1734                 case ICAL_VJOURNAL_COMPONENT:
1735                         tzdata.found = TRUE;
1736                         icalcomponent_foreach_tzid (subcomp, check_tzids, &tzdata);
1737                         
1738                         if (!tzdata.found) {
1739                                 status = GNOME_Evolution_Calendar_InvalidObject;
1740                                 goto error;
1741                         }
1742
1743                         if (!icalcomponent_get_uid (subcomp)) {
1744                                 status = GNOME_Evolution_Calendar_InvalidObject;
1745                                 goto error;
1746                         }
1747                 
1748                         comps = g_list_prepend (comps, subcomp);
1749                         break;
1750                 default:
1751                         /* Ignore it */
1752                         break;
1753                 }
1754
1755                 subcomp = icalcomponent_get_next_component (toplevel_comp, ICAL_ANY_COMPONENT);
1756         }
1757
1758         /* Now we manipulate the components we care about */
1759         for (l = comps; l; l = l->next) {
1760                 subcomp = l->data;
1761                 
1762                 switch (method) {
1763                 case ICAL_METHOD_PUBLISH:
1764                 case ICAL_METHOD_REQUEST:
1765                         /* FIXME Need to see the new create/modify stuff before we set this up */
1766                         break;                  
1767                 case ICAL_METHOD_REPLY:
1768                         /* FIXME Update the status of the user, if we are the organizer */
1769                         break;
1770                 case ICAL_METHOD_ADD:
1771                         /* FIXME This should be doable once all the recurid stuff is done */
1772                         break;
1773                 case ICAL_METHOD_COUNTER:
1774                         status = GNOME_Evolution_Calendar_UnsupportedMethod;
1775                         goto error;
1776                         break;                  
1777                 case ICAL_METHOD_DECLINECOUNTER:                        
1778                         status = GNOME_Evolution_Calendar_UnsupportedMethod;
1779                         goto error;
1780                         break;
1781                 case ICAL_METHOD_CANCEL:
1782                         /* FIXME Do we need to remove the subcomp so it isn't merged? */
1783                         if (cancel_received_object (cbfile, subcomp)) {
1784                                 const char *calobj = icalcomponent_as_ical_string (subcomp);
1785                                 e_cal_backend_notify_object_removed (E_CAL_BACKEND (backend), icalcomponent_get_uid (subcomp), calobj);
1786                         }
1787                         break;
1788                 default:
1789                         status = GNOME_Evolution_Calendar_UnsupportedMethod;
1790                         goto error;
1791                 }
1792         }
1793         g_list_free (comps);
1794         
1795         /* Merge the iCalendar components with our existing VCALENDAR,
1796            resolving any conflicting TZIDs. */
1797         icalcomponent_merge_component (priv->icalcomp, toplevel_comp);
1798
1799         save (cbfile);
1800
1801  error:
1802         g_hash_table_destroy (tzdata.zones);
1803         
1804         return status;
1805 }
1806
1807 static ECalBackendSyncStatus
1808 e_cal_backend_file_send_objects (ECalBackendSync *backend, EDataCal *cal, const char *calobj)
1809 {
1810         /* FIXME Put in a util routine to send stuff via email */
1811         
1812         return GNOME_Evolution_Calendar_Success;
1813 }
1814
1815 static icaltimezone *
1816 e_cal_backend_file_internal_get_default_timezone (ECalBackend *backend)
1817 {
1818         ECalBackendFile *cbfile;
1819         ECalBackendFilePrivate *priv;
1820
1821         cbfile = E_CAL_BACKEND_FILE (backend);
1822         priv = cbfile->priv;
1823
1824         g_return_val_if_fail (priv->icalcomp != NULL, NULL);
1825
1826         return priv->default_zone;
1827 }
1828
1829 static icaltimezone *
1830 e_cal_backend_file_internal_get_timezone (ECalBackend *backend, const char *tzid)
1831 {
1832         ECalBackendFile *cbfile;
1833         ECalBackendFilePrivate *priv;
1834         icaltimezone *zone;
1835
1836         cbfile = E_CAL_BACKEND_FILE (backend);
1837         priv = cbfile->priv;
1838
1839         g_return_val_if_fail (priv->icalcomp != NULL, NULL);
1840
1841         if (!strcmp (tzid, "UTC"))
1842                 zone = icaltimezone_get_utc_timezone ();
1843         else {
1844                 zone = icalcomponent_get_timezone (priv->icalcomp, tzid);
1845                 if (!zone)
1846                         zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
1847         }
1848
1849         return zone;
1850 }
1851
1852 /* Object initialization function for the file backend */
1853 static void
1854 e_cal_backend_file_init (ECalBackendFile *cbfile, ECalBackendFileClass *class)
1855 {
1856         ECalBackendFilePrivate *priv;
1857
1858         priv = g_new0 (ECalBackendFilePrivate, 1);
1859         cbfile->priv = priv;
1860
1861         priv->uri = NULL;
1862         priv->file_name = g_strdup ("calendar.ics");
1863         priv->read_only = FALSE;
1864         priv->icalcomp = NULL;
1865         priv->comp_uid_hash = NULL;
1866         priv->comp = NULL;
1867
1868         /* The timezone defaults to UTC. */
1869         priv->default_zone = icaltimezone_get_utc_timezone ();
1870 }
1871
1872 /* Class initialization function for the file backend */
1873 static void
1874 e_cal_backend_file_class_init (ECalBackendFileClass *class)
1875 {
1876         GObjectClass *object_class;
1877         ECalBackendClass *backend_class;
1878         ECalBackendSyncClass *sync_class;
1879
1880         object_class = (GObjectClass *) class;
1881         backend_class = (ECalBackendClass *) class;
1882         sync_class = (ECalBackendSyncClass *) class;
1883
1884         parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
1885
1886         object_class->dispose = e_cal_backend_file_dispose;
1887         object_class->finalize = e_cal_backend_file_finalize;
1888
1889         sync_class->is_read_only_sync = e_cal_backend_file_is_read_only;
1890         sync_class->get_cal_address_sync = e_cal_backend_file_get_cal_address;
1891         sync_class->get_alarm_email_address_sync = e_cal_backend_file_get_alarm_email_address;
1892         sync_class->get_ldap_attribute_sync = e_cal_backend_file_get_ldap_attribute;
1893         sync_class->get_static_capabilities_sync = e_cal_backend_file_get_static_capabilities;
1894         sync_class->open_sync = e_cal_backend_file_open;
1895         sync_class->remove_sync = e_cal_backend_file_remove;
1896         sync_class->create_object_sync = e_cal_backend_file_create_object;
1897         sync_class->modify_object_sync = e_cal_backend_file_modify_object;
1898         sync_class->remove_object_sync = e_cal_backend_file_remove_object;
1899         sync_class->discard_alarm_sync = e_cal_backend_file_discard_alarm;
1900         sync_class->receive_objects_sync = e_cal_backend_file_receive_objects;
1901         sync_class->send_objects_sync = e_cal_backend_file_send_objects;
1902         sync_class->get_default_object_sync = e_cal_backend_file_get_default_object;
1903         sync_class->get_object_sync = e_cal_backend_file_get_object;
1904         sync_class->get_object_list_sync = e_cal_backend_file_get_object_list;
1905         sync_class->get_timezone_sync = e_cal_backend_file_get_timezone;
1906         sync_class->add_timezone_sync = e_cal_backend_file_add_timezone;
1907         sync_class->set_default_timezone_sync = e_cal_backend_file_set_default_timezone;
1908         sync_class->get_freebusy_sync = e_cal_backend_file_get_free_busy;
1909         sync_class->get_changes_sync = e_cal_backend_file_get_changes;
1910
1911         backend_class->is_loaded = e_cal_backend_file_is_loaded;
1912         backend_class->start_query = e_cal_backend_file_start_query;
1913         backend_class->get_mode = e_cal_backend_file_get_mode;
1914         backend_class->set_mode = e_cal_backend_file_set_mode;
1915
1916         backend_class->internal_get_default_timezone = e_cal_backend_file_internal_get_default_timezone;
1917         backend_class->internal_get_timezone = e_cal_backend_file_internal_get_timezone;
1918 }
1919
1920
1921 /**
1922  * e_cal_backend_file_get_type:
1923  * @void: 
1924  * 
1925  * Registers the #ECalBackendFile class if necessary, and returns the type ID
1926  * associated to it.
1927  * 
1928  * Return value: The type ID of the #ECalBackendFile class.
1929  **/
1930 GType
1931 e_cal_backend_file_get_type (void)
1932 {
1933         static GType e_cal_backend_file_type = 0;
1934
1935         if (!e_cal_backend_file_type) {
1936                 static GTypeInfo info = {
1937                         sizeof (ECalBackendFileClass),
1938                         (GBaseInitFunc) NULL,
1939                         (GBaseFinalizeFunc) NULL,
1940                         (GClassInitFunc) e_cal_backend_file_class_init,
1941                         NULL, NULL,
1942                         sizeof (ECalBackendFile),
1943                         0,
1944                         (GInstanceInitFunc) e_cal_backend_file_init
1945                 };
1946                 e_cal_backend_file_type = g_type_register_static (E_TYPE_CAL_BACKEND_SYNC,
1947                                                                 "ECalBackendFile", &info, 0);
1948         }
1949
1950         return e_cal_backend_file_type;
1951 }
1952
1953 void
1954 e_cal_backend_file_set_file_name (ECalBackendFile *cbfile, const char *file_name)
1955 {
1956         ECalBackendFilePrivate *priv;
1957         
1958         g_return_if_fail (cbfile != NULL);
1959         g_return_if_fail (E_IS_CAL_BACKEND_FILE (cbfile));
1960         g_return_if_fail (file_name != NULL);
1961
1962         priv = cbfile->priv;
1963         
1964         if (priv->file_name)
1965                 g_free (priv->file_name);
1966         
1967         priv->file_name = g_strdup (file_name);
1968 }
1969
1970 const char *
1971 e_cal_backend_file_get_file_name (ECalBackendFile *cbfile)
1972 {
1973         ECalBackendFilePrivate *priv;
1974
1975         g_return_val_if_fail (cbfile != NULL, NULL);
1976         g_return_val_if_fail (E_IS_CAL_BACKEND_FILE (cbfile), NULL);
1977
1978         priv = cbfile->priv;    
1979
1980         return priv->file_name;
1981 }