1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* Evolution calendar - generic backend class
4 * Copyright (C) 2003 Novell, Inc.
6 * Authors: 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 Lesser 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 Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <libecal/e-cal-util.h>
28 #include "e-cal-backend-cache.h"
30 struct _ECalBackendCachePrivate {
32 ECalSourceType source_type;
33 GHashTable *timezones;
43 static GObjectClass *parent_class = NULL;
46 get_filename_from_uri (const char *uri, ECalSourceType source_type)
48 char *mangled_uri, *filename;
52 switch (source_type) {
53 case E_CAL_SOURCE_TYPE_EVENT :
56 case E_CAL_SOURCE_TYPE_TODO :
59 case E_CAL_SOURCE_TYPE_JOURNAL :
62 case E_CAL_SOURCE_TYPE_LAST :
67 /* mangle the URI to not contain invalid characters */
68 mangled_uri = g_strdup (uri);
69 for (i = 0; i < strlen (mangled_uri); i++) {
70 switch (mangled_uri[i]) {
77 /* generate the file name */
78 filename = g_build_filename (g_get_home_dir (), ".evolution/cache/",
79 source, mangled_uri, "cache.xml", NULL);
88 e_cal_backend_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
90 ECalBackendCache *cache;
91 ECalBackendCachePrivate *priv;
93 ECalSourceType source_type;
95 cache = E_CAL_BACKEND_CACHE (object);
98 switch (property_id) {
99 case PROP_SOURCE_TYPE :
100 source_type = g_value_get_enum (value);
101 priv->source_type = source_type;
104 /* Ensure both properties are set and then create the
105 * cache_file property */
106 cache_file = get_filename_from_uri (g_value_get_string (value), priv->source_type);
110 g_object_set (G_OBJECT (cache), "filename", cache_file, NULL);
115 priv->uri = g_value_dup_string (value);
118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
123 e_cal_backend_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
125 ECalBackendCache *cache;
126 ECalBackendCachePrivate *priv;
128 cache = E_CAL_BACKEND_CACHE (object);
131 switch (property_id) {
132 case PROP_SOURCE_TYPE:
133 g_value_set_enum (value, priv->source_type);
135 g_value_set_string (value, priv->uri);
138 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
143 free_timezone_hash (gpointer key, gpointer value, gpointer user_data)
146 icaltimezone_free (value, 1);
150 e_cal_backend_cache_finalize (GObject *object)
152 ECalBackendCache *cache;
153 ECalBackendCachePrivate *priv;
155 cache = E_CAL_BACKEND_CACHE (object);
164 if (priv->timezones) {
165 g_hash_table_foreach (priv->timezones, (GHFunc) free_timezone_hash, NULL);
166 g_hash_table_destroy (priv->timezones);
167 priv->timezones = NULL;
174 parent_class->finalize (object);
178 e_cal_backend_cache_constructor (GType type,
179 guint n_construct_properties,
180 GObjectConstructParam *construct_properties)
184 ECalSourceType source_type = E_CAL_SOURCE_TYPE_EVENT;
185 ECalBackendCacheClass *klass;
186 GObjectClass *parent_class;
188 /* Invoke parent constructor. */
189 klass = E_CAL_BACKEND_CACHE_CLASS (g_type_class_peek (E_TYPE_CAL_BACKEND_CACHE));
190 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
191 obj = parent_class->constructor (type,
192 n_construct_properties,
193 construct_properties);
195 if (!g_ascii_strcasecmp ( g_param_spec_get_name (construct_properties->pspec), "source_type"))
196 source_type = g_value_get_enum (construct_properties->value);
198 if (!g_ascii_strcasecmp ( g_param_spec_get_name (construct_properties->pspec), "uri")) {
201 uri = g_value_get_string (construct_properties->value);
202 cache_file = get_filename_from_uri (uri, source_type);
203 g_object_set (obj, "filename", cache_file, NULL);
211 e_cal_backend_cache_class_init (ECalBackendCacheClass *klass)
213 GObjectClass *object_class;
215 parent_class = g_type_class_peek_parent (klass);
217 object_class = G_OBJECT_CLASS (klass);
218 object_class->finalize = e_cal_backend_cache_finalize;
219 object_class->set_property = e_cal_backend_cache_set_property;
220 object_class->get_property = e_cal_backend_cache_get_property;
222 object_class->constructor = e_cal_backend_cache_constructor;
223 g_object_class_install_property (object_class, PROP_SOURCE_TYPE,
224 g_param_spec_enum ("source_type", NULL, NULL,
225 e_cal_source_type_enum_get_type (),
226 E_CAL_SOURCE_TYPE_EVENT,
227 G_PARAM_READABLE | G_PARAM_WRITABLE
228 | G_PARAM_CONSTRUCT_ONLY));
230 g_object_class_install_property (object_class, PROP_URI,
231 g_param_spec_string ("uri", NULL, NULL, "",
232 G_PARAM_READABLE | G_PARAM_WRITABLE
233 | G_PARAM_CONSTRUCT_ONLY));
237 e_cal_backend_cache_init (ECalBackendCache *cache)
239 ECalBackendCachePrivate *priv;
241 priv = g_new0 (ECalBackendCachePrivate, 1);
242 priv->timezones = g_hash_table_new (g_str_hash, g_str_equal);
249 * e_cal_backend_cache_get_type:
252 * Registers the #ECalBackendCache class if necessary, and returns the type ID
255 * Return value: The type ID of the #ECalBackendCache class.
258 e_cal_backend_cache_get_type (void)
260 static GType type = 0;
261 static GStaticMutex registering = G_STATIC_MUTEX_INIT;
263 g_static_mutex_lock (®istering);
265 static GTypeInfo info = {
266 sizeof (ECalBackendCacheClass),
267 (GBaseInitFunc) NULL,
268 (GBaseFinalizeFunc) NULL,
269 (GClassInitFunc) e_cal_backend_cache_class_init,
271 sizeof (ECalBackendCache),
273 (GInstanceInitFunc) e_cal_backend_cache_init,
275 /* Check if the type is already registered */
276 if (!(type = g_type_from_name ("ECalBackendCache")))
277 type = g_type_register_static (E_TYPE_FILE_CACHE, "ECalBackendCache", &info, 0);
279 g_static_mutex_unlock (®istering);
285 * e_cal_backend_cache_new
286 * @uri: URI of the backend to be cached.
288 * Creates a new #ECalBackendCache object, which implements a cache of
289 * calendar/tasks objects, very useful for remote backends.
291 * Return value: The newly created object.
294 e_cal_backend_cache_new (const char *uri, ECalSourceType source_type)
296 ECalBackendCache *cache;
298 cache = g_object_new (E_TYPE_CAL_BACKEND_CACHE, "source_type", source_type, "uri", uri, NULL);
304 get_key (const char *uid, const char *rid)
309 real_key = g_string_new (uid);
311 real_key = g_string_append (real_key, "@");
312 real_key = g_string_append (real_key, rid);
315 retval = real_key->str;
316 g_string_free (real_key, FALSE);
322 * e_cal_backend_cache_get_component:
323 * @cache: A %ECalBackendCache object.
324 * @uid: The UID of the component to retrieve.
325 * @rid: Recurrence ID of the specific detached recurrence to retrieve,
326 * or NULL if the whole object is to be retrieved.
328 * Gets a component from the %ECalBackendCache object.
330 * Return value: The %ECalComponent representing the component found,
331 * or %NULL if it was not found in the cache.
334 e_cal_backend_cache_get_component (ECalBackendCache *cache, const char *uid, const char *rid)
337 const char *comp_str;
338 icalcomponent *icalcomp;
339 ECalComponent *comp = NULL;
341 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
342 g_return_val_if_fail (uid != NULL, NULL);
344 real_key = get_key (uid, rid);
346 comp_str = e_file_cache_get_object (E_FILE_CACHE (cache), real_key);
348 icalcomp = icalparser_parse_string (comp_str);
350 comp = e_cal_component_new ();
351 if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
352 icalcomponent_free (icalcomp);
353 g_object_unref (comp);
366 * e_cal_backend_cache_put_component:
367 * @cache: An #ECalBackendCache object.
368 * @comp: Component to put on the cache.
370 * Puts the given calendar component in the given cache. This will add
371 * the component if it does not exist or replace it if there was a
372 * previous version of it.
374 * Return value: TRUE if the operation was successful, FALSE otherwise.
377 e_cal_backend_cache_put_component (ECalBackendCache *cache,
380 char *real_key, *uid, *comp_str;
383 ECalBackendCachePrivate *priv;
385 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
386 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
390 e_cal_component_get_uid (comp, (const char **) &uid);
391 if (e_cal_component_is_instance (comp)) {
392 rid = e_cal_component_get_recurid_as_string (comp);
396 comp_str = e_cal_component_get_as_string (comp);
397 real_key = get_key (uid, rid);
399 if (e_file_cache_get_object (E_FILE_CACHE (cache), real_key))
400 retval = e_file_cache_replace_object (E_FILE_CACHE (cache), real_key, comp_str);
402 retval = e_file_cache_add_object (E_FILE_CACHE (cache), real_key, comp_str);
411 * e_cal_backend_cache_remove_component:
412 * @cache: An #ECalBackendCache object.
413 * @uid: UID of the component to remove.
414 * @rid: Recurrence-ID of the component to remove. This is used when removing
415 * detached instances of a recurring appointment.
417 * Removes a component from the cache.
419 * Return value: TRUE if the component was removed, FALSE otherwise.
422 e_cal_backend_cache_remove_component (ECalBackendCache *cache,
428 ECalBackendCachePrivate *priv;
430 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
431 g_return_val_if_fail (uid != NULL, FALSE);
435 real_key = get_key (uid, rid);
436 if (!e_file_cache_get_object (E_FILE_CACHE (cache), real_key)) {
441 retval = e_file_cache_remove_object (E_FILE_CACHE (cache), real_key);
448 * e_cal_backend_cache_get_components:
449 * @cache: An #ECalBackendCache object.
451 * Retrieves a list of all the components stored in the cache.
453 * Return value: A list of all the components. Each item in the list is
454 * an #ECalComponent, which should be freed when no longer needed.
457 e_cal_backend_cache_get_components (ECalBackendCache *cache)
462 icalcomponent *icalcomp;
463 ECalComponent *comp = NULL;
465 /* return null if cache is not a valid Backend Cache. */
466 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
467 l = e_file_cache_get_objects (E_FILE_CACHE (cache));
470 for ( ; l != NULL; l = g_slist_next (l)) {
473 icalcomp = icalparser_parse_string (comp_str);
475 icalcomponent_kind kind;
477 kind = icalcomponent_isa (icalcomp);
478 if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT || kind == ICAL_VJOURNAL_COMPONENT) {
479 comp = e_cal_component_new ();
480 if (e_cal_component_set_icalcomponent (comp, icalcomp))
481 list = g_list_prepend (list, comp);
483 icalcomponent_free (icalcomp);
484 g_object_unref (comp);
487 icalcomponent_free (icalcomp);
497 * e_cal_backend_cache_get_components_by_uid:
498 * @cache: An #ECalBackendCache object.
499 * @uid: ID of the component to retrieve.
501 * Retrieves a ical components from the cache.
503 * Return value: The list of calendar components if found, or NULL otherwise.
506 e_cal_backend_cache_get_components_by_uid (ECalBackendCache *cache, const char *uid)
511 icalcomponent *icalcomp;
512 ECalComponent *comp = NULL;
514 /* return null if cache is not a valid Backend Cache. */
515 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
516 l = e_file_cache_get_objects (E_FILE_CACHE (cache));
519 for ( ; l != NULL; l = g_slist_next (l)) {
522 icalcomp = icalparser_parse_string (comp_str);
524 icalcomponent_kind kind;
526 kind = icalcomponent_isa (icalcomp);
527 if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) {
528 comp = e_cal_component_new ();
529 if ((e_cal_component_set_icalcomponent (comp, icalcomp)) &&
530 !strcmp (icalcomponent_get_uid (icalcomp), uid))
531 list = g_slist_prepend (list, comp);
533 g_object_unref (comp);
536 icalcomponent_free (icalcomp);
546 * e_cal_backend_cache_get_timezone:
547 * @cache: An #ECalBackendCache object.
548 * @tzid: ID of the timezone to retrieve.
550 * Retrieves a timezone component from the cache.
552 * Return value: The timezone if found, or NULL otherwise.
555 e_cal_backend_cache_get_timezone (ECalBackendCache *cache, const char *tzid)
558 const char *comp_str;
559 ECalBackendCachePrivate *priv;
561 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
562 g_return_val_if_fail (tzid != NULL, NULL);
566 /* we first look for the timezone in the timezones hash table */
567 zone = g_hash_table_lookup (priv->timezones, tzid);
569 return (const icaltimezone *) zone;
571 /* if not found look for the timezone in the cache */
572 comp_str = e_file_cache_get_object (E_FILE_CACHE (cache), tzid);
574 icalcomponent *icalcomp;
576 icalcomp = icalparser_parse_string (comp_str);
578 zone = icaltimezone_new ();
579 if (icaltimezone_set_component (zone, icalcomp) == 1)
580 g_hash_table_insert (priv->timezones, g_strdup (tzid), zone);
582 icalcomponent_free (icalcomp);
583 icaltimezone_free (zone, 1);
588 return (const icaltimezone *) zone;
592 * e_cal_backend_cache_put_timezone:
593 * @cache: An #ECalBackendCache object.
594 * @zone: The timezone to put on the cache.
596 * Puts the given timezone in the cache, adding it, if it did not exist, or
597 * replacing it, if there was an older version.
599 * Return value: TRUE if the timezone was put on the cache, FALSE otherwise.
602 e_cal_backend_cache_put_timezone (ECalBackendCache *cache, const icaltimezone *zone)
604 ECalBackendCachePrivate *priv;
605 gpointer orig_key, orig_value;
606 icaltimezone *new_zone;
607 icalcomponent *icalcomp;
610 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
611 g_return_val_if_fail (zone != NULL, FALSE);
615 /* add the timezone to the cache file */
616 icalcomp = icaltimezone_get_component ((icaltimezone *)zone);
620 if (e_file_cache_get_object (E_FILE_CACHE (cache), icaltimezone_get_tzid ((icaltimezone *)zone))) {
621 retval = e_file_cache_replace_object (E_FILE_CACHE (cache),
622 icaltimezone_get_tzid ((icaltimezone *)zone),
623 icalcomponent_as_ical_string (icalcomp));
625 retval = e_file_cache_add_object (E_FILE_CACHE (cache),
626 icaltimezone_get_tzid ((icaltimezone *)zone),
627 icalcomponent_as_ical_string (icalcomp));
633 /* check if the timezone already exists */
634 if (g_hash_table_lookup_extended (priv->timezones, icaltimezone_get_tzid ((icaltimezone *)zone),
635 &orig_key, &orig_value)) {
636 /* remove the previous timezone */
637 g_hash_table_remove (priv->timezones, orig_key);
639 icaltimezone_free (orig_value, 1);
642 /* add the timezone to the hash table */
643 new_zone = icaltimezone_new ();
644 icaltimezone_set_component (new_zone, icalcomponent_new_clone (icalcomp));
645 g_hash_table_insert (priv->timezones, g_strdup (icaltimezone_get_tzid (new_zone)), new_zone);
651 * e_cal_backend_cache_put_default_timezone:
652 * @cache: An #ECalBackendCache object.
653 * @default_zone: The default timezone.
655 * Sets the default timezone on the cache.
657 * Return value: TRUE if the operation was successful, FALSE otherwise.
660 e_cal_backend_cache_put_default_timezone (ECalBackendCache *cache, icaltimezone *default_zone)
662 ECalBackendCachePrivate *priv;
663 icalcomponent *icalcomp;
666 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
670 /* add the timezone to the cache file */
671 icalcomp = icaltimezone_get_component (default_zone);
675 if (e_file_cache_get_object (E_FILE_CACHE (cache), "default_zone")) {
676 retval = e_file_cache_replace_object (E_FILE_CACHE (cache), "default_zone",
677 icalcomponent_as_ical_string (icalcomp));
679 retval = e_file_cache_add_object (E_FILE_CACHE (cache),
681 icalcomponent_as_ical_string (icalcomp));
692 * e_cal_backend_cache_get_default_timezone:
693 * @cache: An #ECalBackendCache object.
695 * Retrieves the default timezone from the cache.
697 * Return value: The default timezone, or NULL if no default timezone
698 * has been set on the cache.
701 e_cal_backend_cache_get_default_timezone (ECalBackendCache *cache)
704 const char *comp_str;
705 ECalBackendCachePrivate *priv;
707 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
711 /* look for the timezone in the cache */
712 comp_str = e_file_cache_get_object (E_FILE_CACHE (cache), "default_zone");
714 icalcomponent *icalcomp;
716 icalcomp = icalparser_parse_string (comp_str);
718 zone = icaltimezone_new ();
719 if (icaltimezone_set_component (zone, icalcomp) == 1) {
722 icalcomponent_free (icalcomp);
723 icaltimezone_free (zone, 1);
732 * e_cal_backend_cache_remove_timezone:
733 * @cache: An #ECalBackendCache object.
734 * @tzid: ID of the timezone to remove.
736 * Removes a timezone component from the cache.
738 * Return value: TRUE if the timezone was removed, FALSE otherwise.
741 e_cal_backend_cache_remove_timezone (ECalBackendCache *cache, const char *tzid)
743 gpointer orig_key, orig_value;
744 ECalBackendCachePrivate *priv;
746 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
747 g_return_val_if_fail (tzid != NULL, FALSE);
751 if (g_hash_table_lookup_extended (priv->timezones, tzid, &orig_key, &orig_value)) {
752 g_hash_table_remove (priv->timezones, tzid);
754 icaltimezone_free (orig_value, 1);
757 return e_file_cache_remove_object (E_FILE_CACHE (cache), tzid);
761 * e_cal_backend_cache_get_keys:
762 * @cache: An #ECalBackendCache object.
764 * Gets the list of unique keys in the cache file.
766 * Return value: A list of all the keys. The items in the list are pointers
767 * to internal data, so should not be freed, only the list should.
770 e_cal_backend_cache_get_keys (ECalBackendCache *cache)
772 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
773 return e_file_cache_get_keys (E_FILE_CACHE (cache));
777 * e_cal_backend_cache_set_marker:
778 * @cache: An #ECalBackendCache object.
780 * Marks the cache as populated, to discriminate between an empty calendar
781 * and an unpopulated one.
784 e_cal_backend_cache_set_marker (ECalBackendCache *cache)
786 g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
787 e_file_cache_add_object (E_FILE_CACHE (cache), "populated", "TRUE");
791 * e_cal_backend_cache_get_marker:
792 * @cache: An #ECalBackendCache object.
794 * Gets the marker of the cache. If this field is present, it means the
795 * cache has been populated.
797 * Return value: The value of the marker or NULL if the cache is still empty.
800 e_cal_backend_cache_get_marker (ECalBackendCache *cache)
802 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
803 if (e_file_cache_get_object (E_FILE_CACHE (cache), "populated"))
809 * e_cal_backend_cache_put_server_utc_time:
810 * @cache: An #ECalBackendCache object.
811 * @utc_str: UTC string.
813 * Return value: TRUE if the operation was successful, FALSE otherwise.
816 e_cal_backend_cache_put_server_utc_time (ECalBackendCache *cache, const char *utc_str)
818 gboolean ret_val = FALSE;
820 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
822 if (!(ret_val = e_file_cache_add_object (E_FILE_CACHE (cache), "server_utc_time", utc_str)))
823 ret_val = e_file_cache_replace_object (E_FILE_CACHE (cache), "server_utc_time", utc_str);
829 * e_cal_backend_cache_get_server_utc_time:
830 * @cache: An #ECalBackendCache object.
832 * Return value: The server's UTC string.
835 e_cal_backend_cache_get_server_utc_time (ECalBackendCache *cache)
838 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
840 return e_file_cache_get_object (E_FILE_CACHE (cache), "server_utc_time");
844 * e_cal_backend_cache_put_key_value:
845 * @cache: An #ECalBackendCache object.
846 * @keyp: The Key parameter to identify uniquely.
847 * @valuep: The value for the keyp parameter.
849 * Return value: TRUE if the operation was successful, FALSE otherwise.
852 e_cal_backend_cache_put_key_value (ECalBackendCache *cache, const char *key, const char *value)
854 gboolean ret_val = FALSE;
856 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), FALSE);
859 e_file_cache_remove_object (E_FILE_CACHE (cache), key);
863 if (!(ret_val = e_file_cache_add_object (E_FILE_CACHE (cache), key, value)))
864 ret_val = e_file_cache_replace_object (E_FILE_CACHE (cache), key, value);
870 * e_cal_backend_cache_get_key_value:
871 * @cache: An #ECalBackendCache object.
873 * Return value: The value.
876 e_cal_backend_cache_get_key_value (ECalBackendCache *cache, const char *key)
879 g_return_val_if_fail (E_IS_CAL_BACKEND_CACHE (cache), NULL);
881 return e_file_cache_get_object (E_FILE_CACHE (cache), key);