bug #52890: improved time zone support - merged 9022:9026 from gnome-2-22 branch
authorPatrick Ohly <pohly@src.gnome.org>
Sun, 22 Jun 2008 13:12:20 +0000 (13:12 +0000)
committerPatrick Ohly <pohly@src.gnome.org>
Sun, 22 Jun 2008 13:12:20 +0000 (13:12 +0000)
svn path=/trunk/; revision=9028

ChangeLog
calendar/backends/file/e-cal-backend-file.c
calendar/libecal/Makefile.am
calendar/libecal/e-cal-check-timezones.c [new file with mode: 0644]
calendar/libecal/e-cal-check-timezones.h [new file with mode: 0644]
calendar/libecal/e-cal.c
configure.in

index b708ebf..bd45992 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2008-06-22  Patrick Ohly  <patrick.ohly@gmx.de>
+
+       * configure.in, calendar/libecal/Makefile.am,
+       calendar/libecal/e-cal-check-timezones.c,
+       calendar/libecal/e-cal-check-timezones.h: added
+       e_cal_check_timezones() which matches time zone definitions to
+       system time zones and resolves conflicting definitions; bumped
+       version and age to matched the extended libecal API (bug #52890)
+
+       * calendar/backends/file/e-cal-backend-file.c: use
+       e_cal_check_timezones() to improve time zone handling (bug #52890)
+
+       * calendar/libecal/e-cal.c: use current system time zone
+       definitions instead of possibly out-dated custom definitions when
+       exporting events or retrieving the time zone information for an
+       event (bug #52890)
 2008-06-17  Johnny Jacob  <jjohnny@novell.com>
 
        * configure.in (eds_micro_version): Bumped to 2.23.5.
index ec83eb4..f1ae222 100644 (file)
@@ -38,6 +38,7 @@
 #include <libecal/e-cal-recur.h>
 #include <libecal/e-cal-time-util.h>
 #include <libecal/e-cal-util.h>
+#include <libecal/e-cal-check-timezones.h>
 #include <libedata-cal/e-cal-backend-util.h>
 #include <libedata-cal/e-cal-backend-sexp.h>
 #include "e-cal-backend-file-events.h"
@@ -2626,6 +2627,27 @@ e_cal_backend_file_receive_objects (ECalBackendSync *backend, EDataCal *cal, con
 
        g_list_free (del_comps);
 
+        /* check and patch timezones */
+        {
+            GError *error = NULL;
+            if (!e_cal_check_timezones(toplevel_comp,
+                                       NULL,
+                                       e_cal_tzlookup_icomp,
+                                       priv->icalcomp,
+                                       &error)) {
+                /*
+                 * This makes assumptions about what kind of
+                 * errors can occur inside e_cal_check_timezones().
+                 * We control it, so that should be safe, but
+                 * is the code really identical with the calendar
+                 * status?
+                 */
+                status = error->code;
+                g_clear_error(&error);
+                goto error;
+            }
+        }
+
        /* Merge the iCalendar components with our existing VCALENDAR,
           resolving any conflicting TZIDs. */
        icalcomponent_merge_component (priv->icalcomp, toplevel_comp);
index 428426d..82d5ef4 100644 (file)
@@ -48,6 +48,7 @@ libecal_1_2_la_SOURCES =      \
        e-cal-listener.h        \
        e-cal-recur.c           \
        e-cal-time-util.c       \
+        e-cal-check-timezones.c \
        e-cal-util.c            \
        e-cal-view.c            \
        e-cal-view-listener.c   \
@@ -71,6 +72,7 @@ libecalinclude_HEADERS =      \
        e-cal-component.h       \
        e-cal-recur.h           \
        e-cal-time-util.h       \
+        e-cal-check-timezones.h \
        e-cal-types.h           \
        e-cal-util.h            \
        e-cal-view.h
diff --git a/calendar/libecal/e-cal-check-timezones.c b/calendar/libecal/e-cal-check-timezones.c
new file mode 100644 (file)
index 0000000..02791ab
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * Authors: Patrick Ohly <patrick.ohly@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef HANDLE_LIBICAL_MEMORY
+# define HANDLE_LIBICAL_MEMORY 1
+#endif
+#include <libical/ical.h>
+
+#ifdef LIBICAL_MEMFIXES
+/* avoid dependency on icalstrdup.h, works when compiling as part of EDS >= 2.22 */
+# define ical_strdup(_x) (_x)
+#else
+/* use icalstrdup.h to get runtime detection of memory fix patch */
+# include "libical/icalstrdup.h"
+#endif
+
+#include "e-cal-check-timezones.h"
+#include <libecal/e-cal.h>
+#include <string.h>
+#include <ctype.h>
+
+/**
+ * Matches a location to a system timezone definition via a fuzzy
+ * search and returns the matching TZID, or NULL if none found.
+ *
+ * Currently simply strips a suffix introduced by a hyphen,
+ * as in "America/Denver-(Standard)".
+ */
+static const char *e_cal_match_location(const char *location)
+{
+    icaltimezone *icomp;
+    const char *tail;
+    size_t len;
+    char *buffer;
+
+    icomp = icaltimezone_get_builtin_timezone (location);
+    if (icomp) {
+        return icaltimezone_get_tzid(icomp);
+    }
+
+    /* try a bit harder by stripping trailing suffix */
+    tail = strrchr(location, '-');
+    len = tail ? (tail - location) : strlen(location);
+    buffer = g_malloc(len + 1);
+
+    if (buffer) {
+        memcpy(buffer, location, len);
+        buffer[len] = 0;
+        icomp = icaltimezone_get_builtin_timezone (buffer);
+        g_free(buffer);
+        if (icomp) {
+            return icaltimezone_get_tzid(icomp);
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * e_cal_match_tzid:
+ * matches a TZID against the system timezone definitions
+ * and returns the matching TZID, or NULL if none found
+ */
+const char *e_cal_match_tzid(const char *tzid)
+{
+    const char *location;
+    const char *systzid;
+    size_t len = strlen(tzid);
+    ssize_t eostr;
+
+    /*
+     * Try without any trailing spaces/digits: they might have been added
+     * by e_cal_check_timezones() in order to distinguish between
+     * different incompatible definitions. At that time mapping
+     * to system time zones must have failed, but perhaps now
+     * we have better code and it succeeds...
+     */
+    eostr = len - 1;
+    while (eostr >= 0 &&
+           isdigit(tzid[eostr])) {
+        eostr--;
+    }
+    while (eostr >= 0 &&
+           isspace(tzid[eostr])) {
+        eostr--;
+    }
+    if (eostr + 1 < len) {
+        char *strippedtzid = g_strndup(tzid, eostr + 1);
+        if (strippedtzid) {
+            systzid = e_cal_match_tzid(strippedtzid);
+            g_free(strippedtzid);
+            if (systzid) {
+                return systzid;
+            }
+        }
+    }
+
+    /*
+     * old-style Evolution: /softwarestudio.org/Olson_20011030_5/America/Denver
+     *
+     * jump from one slash to the next and check whether the remainder
+     * is a known location; start with the whole string (just in case)
+     */
+    for (location = tzid;
+         location && location[0];
+         location = strchr(location + 1, '/')) {
+        systzid = e_cal_match_location(location[0] == '/' ?
+                                       location + 1 :
+                                       location);
+        if (systzid) {
+            return systzid;
+        }
+    }
+
+    /* TODO: lookup table for Exchange TZIDs */
+
+    return NULL;
+}
+
+static void patch_tzids(icalcomponent *subcomp,
+                        GHashTable *mapping)
+{
+    char *tzid = NULL;
+
+    if (icalcomponent_isa(subcomp) != ICAL_VTIMEZONE_COMPONENT) {
+        icalproperty *prop = icalcomponent_get_first_property(subcomp,
+                                                              ICAL_ANY_PROPERTY);
+        while (prop) {
+            icalparameter *param = icalproperty_get_first_parameter(prop,
+                                                                    ICAL_TZID_PARAMETER);
+            while (param) {
+                const char *oldtzid;
+                const char *newtzid;
+
+                g_free(tzid);
+                tzid = g_strdup(icalparameter_get_tzid(param));
+
+                if (!g_hash_table_lookup_extended(mapping,
+                                                  tzid,
+                                                  (gpointer *)&oldtzid,
+                                                  (gpointer *)&newtzid)) {
+                    /* Corresponding VTIMEZONE not seen before! */
+                    newtzid = e_cal_match_tzid(tzid);
+                }
+                if (newtzid) {
+                    icalparameter_set_tzid(param, newtzid);
+                }
+                param = icalproperty_get_next_parameter(prop,
+                                                        ICAL_TZID_PARAMETER);
+            }
+            prop = icalcomponent_get_next_property(subcomp,
+                                                   ICAL_ANY_PROPERTY);
+        }
+    }
+
+    g_free(tzid);
+}
+
+static void addsystemtz(gpointer key,
+                        gpointer value,
+                        gpointer user_data)
+{
+    const char *tzid = key;
+    icalcomponent *comp = user_data;
+    icaltimezone *zone;
+
+    zone = icaltimezone_get_builtin_timezone_from_tzid(tzid);
+    if (zone) {
+        icalcomponent_add_component(comp,
+                                    icalcomponent_new_clone(icaltimezone_get_component(zone)));
+    }
+}
+
+/**
+ * e_cal_check_timezones:
+ * @comp:     a VCALENDAR containing a list of
+ *            VTIMEZONE and arbitrary other components, in
+ *            arbitrary order: these other components are
+ *            modified by this call
+ * @comps:    a list of icalcomponent instances which
+ *            also have to be patched; may be NULL
+ * @tzlookup: a callback function which is called to retrieve
+ *            a calendar's VTIMEZONE definition; the returned
+ *            definition is *not* freed by e_cal_check_timezones()
+ *            (to be compatible with e_cal_get_timezone());
+ *            NULL indicates that no such timezone exists
+ *            or an error occurred
+ * @custom:   an arbitrary pointer which is passed through to
+ *            the tzlookup function
+ * @error:    an error description in case of a failure
+ *
+ * This function cleans up VEVENT, VJOURNAL, VTODO and VTIMEZONE
+ * items which are to be imported into Evolution.
+ *
+ * Using VTIMEZONE definitions is problematic because they cannot be
+ * updated properly when timezone definitions change. They are also
+ * incomplete (for compatibility reason only one set of rules for
+ * summer saving changes can be included, even if different rules
+ * apply in different years). This function looks for matches of the
+ * used TZIDs against system timezones and replaces such TZIDs with
+ * the corresponding system timezone. This works for TZIDs containing
+ * a location (found via a fuzzy string search) and for Outlook TZIDs
+ * (via a hard-coded lookup table).
+ *
+ * Some programs generate broken meeting invitations with TZID, but
+ * without including the corresponding VTIMEZONE. Importing such
+ * invitations unchanged causes problems later on (meeting displayed
+ * incorrectly, #e_cal_get_component_as_string fails). The situation
+ * where this occurred in the past (found by a SyncEvolution user) is
+ * now handled via the location based mapping.
+ *
+ * If this mapping fails, this function also deals with VTIMEZONE
+ * conflicts: such conflicts occur when the calendar already contains
+ * an old VTIMEZONE definition with the same TZID, but different
+ * summer saving rules. Replacing the VTIMEZONE potentially breaks
+ * displaying of old events, whereas not replacing it breaks the new
+ * events (the behavior in Evolution <= 2.22.1).
+ *
+ * The way this problem is resolved is by renaming the new VTIMEZONE
+ * definition until the TZID is unique. A running count is appended to
+ * the TZID. All items referencing the renamed TZID are adapted
+ * accordingly.
+ *
+ * Return value: TRUE if successful, FALSE otherwise.
+ */
+gboolean e_cal_check_timezones(icalcomponent *comp,
+                               GList *comps,
+                               icaltimezone *(*tzlookup)(const char *tzid,
+                                                         const void *custom,
+                                                         GError **error),
+                               const void *custom,
+                               GError **error)
+{
+    gboolean success = TRUE;
+    icalcomponent *subcomp = NULL;
+    icaltimezone *zone = icaltimezone_new();
+    char *key = NULL, *value = NULL;
+    char *buffer = NULL;
+    char *zonestr = NULL;
+    char *tzid = NULL;
+    GList *l;
+
+    /** a hash from old to new tzid; strings dynamically allocated */
+    GHashTable *mapping = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+    /** a hash of all system time zone IDs which have to be added; strings are shared with mapping hash */
+    GHashTable *systemtzids = g_hash_table_new(g_str_hash, g_str_equal);
+
+    *error = NULL;
+
+    if (!mapping || !zone) {
+        goto nomem;
+    }
+
+    /* iterate over all VTIMEZONE definitions */
+    subcomp = icalcomponent_get_first_component(comp,
+                                                ICAL_VTIMEZONE_COMPONENT);
+    while (subcomp) {
+        if (icaltimezone_set_component(zone, subcomp)) {
+            g_free(tzid);
+            tzid = g_strdup(icaltimezone_get_tzid(zone));
+            if (tzid) {
+                const char *newtzid = e_cal_match_tzid(tzid);
+                if (newtzid) {
+                    /* matched against system time zone */
+                    g_free(key);
+                    key = g_strdup(tzid);
+                    if (!key) {
+                        goto nomem;
+                    }
+
+                    g_free(value);
+                    value = g_strdup(newtzid);
+                    if (!value) {
+                        goto nomem;
+                    }
+
+                    g_hash_table_insert(mapping, key, value);
+                    g_hash_table_insert(systemtzids, value, NULL);
+                    key =
+                        value = NULL;
+                } else {
+                    zonestr = ical_strdup(icalcomponent_as_ical_string(subcomp));
+
+                    /* check for collisions with existing timezones */
+                    int counter;
+                    for (counter = 0;
+                         counter < 100 /* sanity limit */;
+                         counter++) {
+                        icaltimezone *existing_zone;
+
+                        if (counter) {
+                            g_free(value);
+                            value = g_strdup_printf("%s %d", tzid, counter);
+                        }
+                        existing_zone = tzlookup(counter ? value : tzid,
+                                                 custom,
+                                                 error);
+                        if (!existing_zone) {
+                            if (*error) {
+                                goto failed;
+                            } else {
+                                break;
+                            }
+                        }
+                        g_free(buffer);
+                        buffer = ical_strdup(icalcomponent_as_ical_string(icaltimezone_get_component(existing_zone)));
+
+                        if (counter) {
+                            char *fulltzid = g_strdup_printf("TZID:%s", value);
+                            size_t baselen = strlen("TZID:") + strlen(tzid);
+                            size_t fulllen = strlen(fulltzid);
+                            char *tzidprop;
+                            /*
+                             * Map TZID with counter suffix back to basename.
+                             */
+                            tzidprop = strstr(buffer, fulltzid);
+                            if (tzidprop) {
+                                memmove(tzidprop + baselen,
+                                        tzidprop + fulllen,
+                                        strlen(tzidprop + fulllen) + 1);
+                            }
+                            g_free(fulltzid);
+                        }
+                            
+
+                        /*
+                         * If the strings are identical, then the
+                         * VTIMEZONE definitions are identical.  If
+                         * they are not identical, then VTIMEZONE
+                         * definitions might still be semantically
+                         * correct and we waste some space by
+                         * needlesly duplicating the VTIMEZONE. This
+                         * is expected to occur rarely (if at all) in
+                         * practice.
+                         */
+                        if (!strcmp(zonestr, buffer)) {
+                            break;
+                        }
+                    }
+
+                    if (!counter) {
+                        /* does not exist, nothing to do */
+                    } else {
+                        /* timezone renamed */
+                        icalproperty *prop = icalcomponent_get_first_property(subcomp,
+                                                                              ICAL_TZID_PROPERTY);
+                        while (prop) {
+                            icalproperty_set_value_from_string(prop, value, "NO");
+                            prop = icalcomponent_get_next_property(subcomp,
+                                                                   ICAL_ANY_PROPERTY);
+                        }
+                        g_free(key);
+                        key = g_strdup(tzid);
+                        g_hash_table_insert(mapping, key, value);
+                        key =
+                            value = NULL;
+                    }
+                }
+            }
+        }
+
+        subcomp = icalcomponent_get_next_component(comp,
+                                                   ICAL_VTIMEZONE_COMPONENT);
+    }
+
+    /*
+     * now replace all TZID parameters in place
+     */
+    subcomp = icalcomponent_get_first_component(comp,
+                                                ICAL_ANY_COMPONENT);
+    while (subcomp) {
+        /*
+         * Leave VTIMEZONE unchanged, iterate over properties of
+         * everything else.
+         *
+         * Note that no attempt is made to remove unused VTIMEZONE
+         * definitions. That would just make the code more complex for
+         * little additional gain. However, newly used time zones are
+         * added below.
+         */
+        patch_tzids (subcomp, mapping);
+        subcomp = icalcomponent_get_next_component(comp,
+                                                   ICAL_ANY_COMPONENT);
+    }
+
+    for (l = comps; l; l = l->next) {
+        patch_tzids (l->data, mapping);
+    }
+
+    /*
+     * add system time zones that we mapped to: adding them ensures
+     * that the VCALENDAR remains consistent
+     */
+    g_hash_table_foreach(systemtzids, addsystemtz, comp);
+    
+    goto done;
+ nomem:
+    /* set gerror for "out of memory" if possible, otherwise abort via g_error() */
+    *error = g_error_new(E_CALENDAR_ERROR, E_CALENDAR_STATUS_OTHER_ERROR, "out of memory");
+    if (!*error) {
+        g_error("e_cal_check_timezones(): out of memory, cannot proceed - sorry!");
+    }
+ failed:
+    /* gerror should have been set already */
+    success = FALSE;
+ done:
+    if (mapping) {
+        g_hash_table_destroy(mapping);
+    }
+    if (systemtzids) {
+        g_hash_table_destroy(systemtzids);
+    }
+    if (zone) {
+        icaltimezone_free(zone, 1);
+    }
+    g_free(tzid);
+    g_free(zonestr);
+    g_free(buffer);
+    g_free(key);
+    g_free(value);
+    
+    return success;
+}
+
+/**
+ * e_cal_tzlookup_ecal:
+ * @custom: must be a valid ECal pointer
+ *
+ * An implementation of the tzlookup callback which clients
+ * can use. Calls #e_cal_get_timezone.
+ */
+icaltimezone *e_cal_tzlookup_ecal(const char *tzid,
+                                  const void *custom,
+                                  GError **error)
+{
+    ECal *ecal = (ECal *)custom;
+    icaltimezone *zone = NULL;
+
+    if (e_cal_get_timezone(ecal, tzid, &zone, error)) {
+        g_assert(*error == NULL);
+        return zone;
+    } else {
+        g_assert(*error);
+        if ((*error)->domain == E_CALENDAR_ERROR &&
+            (*error)->code == E_CALENDAR_STATUS_OBJECT_NOT_FOUND) {
+            /*
+             * we had to trigger this error to check for the timezone existance,
+             * clear it and return NULL
+             */
+            g_clear_error(error);
+        }
+        return NULL;
+    }
+}
+
+/**
+ * e_cal_tzlookup_icomp:
+ * @custom: must be a icalcomponent pointer which contains
+ *          either a VCALENDAR with VTIMEZONEs or VTIMEZONES
+ *          directly
+ *
+ * An implementation of the tzlookup callback which backends
+ * like the file backend can use. Searches for the timezone
+ * in the component list.
+ */
+icaltimezone *e_cal_tzlookup_icomp(const char *tzid,
+                                   const void *custom,
+                                   GError **error)
+{
+    icalcomponent *icomp = (icalcomponent *)custom;
+
+    return icalcomponent_get_timezone(icomp, (char *)tzid);
+}
diff --git a/calendar/libecal/e-cal-check-timezones.h b/calendar/libecal/e-cal-check-timezones.h
new file mode 100644 (file)
index 0000000..a19f644
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * Authors: Patrick Ohly <patrick.ohly@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef E_CAL_CHECK_TIMEZONES_H
+#define E_CAL_CHECK_TIMEZONES_H
+
+#include <libical/ical.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gboolean e_cal_check_timezones(icalcomponent *comp,
+                               GList *comps,
+                               icaltimezone *(*tzlookup)(const char *tzid,
+                                                         const void *custom,
+                                                         GError **error),
+                               const void *custom,
+                               GError **error);
+
+icaltimezone *e_cal_tzlookup_ecal(const char *tzid,
+                                  const void *custom,
+                                  GError **error);
+
+icaltimezone *e_cal_tzlookup_icomp(const char *tzid,
+                                   const void *custom,
+                                   GError **error);
+
+const char *e_cal_match_tzid(const char *tzid);
+
+G_END_DECLS
+
+#endif /* E_CAL_CHECK_TIMEZONES_H */
index ab8db26..389006e 100644 (file)
@@ -31,6 +31,7 @@
 #include <bonobo/bonobo-exception.h>
 #include <bonobo/bonobo-main.h>
 
+#include "libecal/e-cal-check-timezones.h"
 #include "libedataserver/e-component-listener.h"
 #include "libedataserver/e-flag.h"
 #include "libedataserver/e-url.h"
@@ -4634,9 +4635,10 @@ e_cal_get_timezone (ECal *ecal, const char *tzid, icaltimezone **zone, GError **
 {
        ECalPrivate *priv;
        CORBA_Environment ev;
-       ECalendarStatus status;
+       ECalendarStatus status = E_CALENDAR_STATUS_OK;
        ECalendarOp *our_op;
        icalcomponent *icalcomp;
+       const char *systzid;
 
        e_return_error_if_fail (ecal && E_IS_CAL (ecal), E_CALENDAR_STATUS_INVALID_ARG);
        e_return_error_if_fail (zone, E_CALENDAR_STATUS_INVALID_ARG);
@@ -4687,34 +4689,74 @@ e_cal_get_timezone (ECal *ecal, const char *tzid, icaltimezone **zone, GError **
                E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_OK, error);
        }
 
-       /* call the backend */
-       CORBA_exception_init (&ev);
+       /*
+        * Try to replace the original time zone with a more complete
+        * and/or potentially updated system time zone. Note that this
+        * also applies to TZIDs which match system time zones exactly:
+        * they are extracted via icaltimezone_get_builtin_timezone_from_tzid()
+        * below without a roundtrip to the backend.
+        */
+       systzid = e_cal_match_tzid (tzid);
+       if (!systzid) {
+               /* call the backend */
+               CORBA_exception_init (&ev);
 
-       GNOME_Evolution_Calendar_Cal_getTimezone (priv->cal, tzid, &ev);
-       if (BONOBO_EX (&ev)) {
-               e_calendar_remove_op (ecal, our_op);
-               e_calendar_free_op (our_op);
+               GNOME_Evolution_Calendar_Cal_getTimezone (priv->cal, tzid, &ev);
+               if (BONOBO_EX (&ev)) {
+                       e_calendar_remove_op (ecal, our_op);
+                       e_calendar_free_op (our_op);
 
-               CORBA_exception_free (&ev);
+                       CORBA_exception_free (&ev);
 
-               g_warning (G_STRLOC ": Unable to contact backend");
+                       g_warning (G_STRLOC ": Unable to contact backend");
 
-               E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
-       }
+                       E_CALENDAR_CHECK_STATUS (E_CALENDAR_STATUS_CORBA_EXCEPTION, error);
+               }
 
-       CORBA_exception_free (&ev);
+               CORBA_exception_free (&ev);
 
-       e_flag_wait (our_op->done);
+               e_flag_wait (our_op->done);
 
-       status = our_op->status;
-        if (status != E_CALENDAR_STATUS_OK){
-                icalcomp = NULL;
-        } else {
-                icalcomp = icalparser_parse_string (our_op->string);
-               if (!icalcomp)
+               status = our_op->status;
+               if (status != E_CALENDAR_STATUS_OK){
+                       icalcomp = NULL;
+               } else {
+                       icalcomp = icalparser_parse_string (our_op->string);
+                       if (!icalcomp)
+                               status = E_CALENDAR_STATUS_INVALID_OBJECT;
+               }
+               g_free (our_op->string);
+       } else {
+               /*
+                * Use built-in time zone *and* rename it:
+                * if the caller is asking for a TZID=FOO,
+                * then likely because it has an event with
+                * such a TZID. Returning a different TZID
+                * would lead to broken VCALENDARs in the
+                * caller.
+                */
+               icaltimezone *syszone = icaltimezone_get_builtin_timezone_from_tzid (systzid);
+               g_assert (syszone);
+               if (syszone) {
+                       gboolean found = FALSE;
+                       icalproperty *prop;
+
+                       icalcomp = icalcomponent_new_clone (icaltimezone_get_component (syszone));
+                       prop = icalcomponent_get_first_property(icalcomp,
+                                                               ICAL_ANY_PROPERTY);
+                       while (!found && prop) {
+                               if (icalproperty_isa(prop) == ICAL_TZID_PROPERTY) {
+                                       icalproperty_set_value_from_string(prop, tzid, "NO");
+                                       found = TRUE;
+                               }
+                               prop = icalcomponent_get_next_property(icalcomp,
+                                                                      ICAL_ANY_PROPERTY);
+                       }
+                       g_assert (found);
+               } else {
                        status = E_CALENDAR_STATUS_INVALID_OBJECT;
+               }
        }
-       g_free (our_op->string);
 
        if (!icalcomp) {
                e_calendar_remove_op (ecal, our_op);
index 7307533..180364b 100644 (file)
@@ -59,9 +59,9 @@ LIBEDATASERVERUI_CURRENT=9
 LIBEDATASERVERUI_REVISION=0
 LIBEDATASERVERUI_AGE=1
 
-LIBECAL_CURRENT=8
+LIBECAL_CURRENT=9
 LIBECAL_REVISION=1
-LIBECAL_AGE=1
+LIBECAL_AGE=2
 
 LIBEDATACAL_CURRENT=6
 LIBEDATACAL_REVISION=2