2 * Copyright (C) 2008 Patrick Ohly
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY, TITLE, NONINFRINGEMENT or FITNESS FOR A PARTICULAR
12 * PURPOSE. See the GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 #include "e_cal_check_timezones.h"
21 #include <libecal/e-cal.h>
25 * Matches a location to a system timezone definition via a fuzzy
26 * search and returns the matching TZID, or NULL if none found.
28 * Currently simply strips a suffix introduced by a hyphen,
29 * as in "America/Denver-(Standard)".
31 static const char *e_cal_match_location(const char *location)
38 icomp = icaltimezone_get_builtin_timezone (location);
40 return icaltimezone_get_tzid(icomp);
43 /* try a bit harder by stripping trailing suffix */
44 tail = strrchr(location, '-');
45 len = tail ? (tail - location) : strlen(location);
46 buffer = g_malloc(len + 1);
49 memcpy(buffer, location, len);
51 icomp = icaltimezone_get_builtin_timezone (buffer);
54 return icaltimezone_get_tzid(icomp);
62 * matches a TZID against the system timezone definitions
63 * and returns the matching TZID, or NULL if none found
65 static const char *e_cal_match_tzid(const char *tzid)
71 * old-style Evolution: /softwarestudio.org/Olson_20011030_5/America/Denver
73 * jump from one slash to the next and check whether the remainder
74 * is a known location; start with the whole string (just in case)
77 location && location[0];
78 location = strchr(location + 1, '/')) {
79 systzid = e_cal_match_location(location[0] == '/' ?
87 /* TODO: lookup table for Exchange TZIDs */
94 gboolean e_cal_check_timezones(icalcomponent *comp,
95 icaltimezone *(*tzlookup)(const char *tzid,
101 gboolean success = TRUE;
102 icalcomponent *subcomp = NULL;
103 icaltimezone *zone = icaltimezone_new();
104 char *key = NULL, *value = NULL;
106 char *zonestr = NULL;
109 /** a hash from old to new tzid; strings dynamically allocated */
110 GHashTable *mapping = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
114 if (!mapping || !zone) {
118 /* iterate over all VTIMEZONE definitions */
119 subcomp = icalcomponent_get_first_component(comp,
120 ICAL_VTIMEZONE_COMPONENT);
122 if (icaltimezone_set_component(zone, subcomp)) {
124 tzid = g_strdup(icaltimezone_get_tzid(zone));
126 const char *newtzid = e_cal_match_tzid(tzid);
128 /* matched against system time zone */
130 key = g_strdup(tzid);
136 value = g_strdup(newtzid);
141 g_hash_table_insert(mapping, key, value);
145 zonestr = g_strdup(icalcomponent_as_ical_string(subcomp));
147 /* check for collisions with existing timezones */
150 counter < 100 /* sanity limit */;
152 icaltimezone *existing_zone;
156 value = g_strdup_printf("%s %d", tzid, counter);
158 existing_zone = tzlookup(counter ? value : tzid,
161 if (!existing_zone) {
169 buffer = g_strdup(icalcomponent_as_ical_string(icaltimezone_get_component(existing_zone)));
172 char *fulltzid = g_strdup_printf("TZID:%s", value);
173 size_t baselen = strlen("TZID:") + strlen(tzid);
174 size_t fulllen = strlen(fulltzid);
177 * Map TZID with counter suffix back to basename.
179 tzidprop = strstr(buffer, fulltzid);
182 g_assert(baselen < fulllen);
183 memmove(tzidprop + baselen,
185 strlen(tzidprop + fulllen) + 1);
192 * If the strings are identical, then the
193 * VTIMEZONE definitions are identical. If
194 * they are not identical, then VTIMEZONE
195 * definitions might still be semantically
196 * correct and we waste some space by
197 * needlesly duplicating the VTIMEZONE. This
198 * is expected to occur rarely (if at all) in
201 if (!strcmp(zonestr, buffer)) {
207 /* does not exist, nothing to do */
209 /* timezone renamed */
210 icalproperty *prop = icalcomponent_get_first_property(subcomp,
213 icalproperty_set_value_from_string(prop, value, "NO");
214 prop = icalcomponent_get_next_property(subcomp,
218 key = g_strdup(tzid);
219 g_hash_table_insert(mapping, key, value);
227 subcomp = icalcomponent_get_next_component(comp,
228 ICAL_VTIMEZONE_COMPONENT);
232 * now replace all TZID parameters in place
234 subcomp = icalcomponent_get_first_component(comp,
238 * Leave VTIMEZONE unchanged, iterate over properties of
241 * Note that no attempt is made to remove unused VTIMEZONE
242 * definitions. That would just make the code more complex for
243 * little additional gain.
245 if (icalcomponent_isa(subcomp) != ICAL_VTIMEZONE_COMPONENT) {
246 icalproperty *prop = icalcomponent_get_first_property(subcomp,
249 icalparameter *param = icalproperty_get_first_parameter(prop,
252 if (icalparameter_isa(param) == ICAL_TZID_PARAMETER) {
257 tzid = g_strdup(icalparameter_get_tzid(param));
259 if (!g_hash_table_lookup_extended(mapping,
261 (gpointer *)&oldtzid,
262 (gpointer *)&newtzid)) {
263 /* Corresponding VTIMEZONE not seen before! */
264 newtzid = e_cal_match_tzid(tzid);
267 icalparameter_set_tzid(param, newtzid);
270 param = icalproperty_get_next_parameter(prop,
273 prop = icalcomponent_get_next_property(subcomp,
277 subcomp = icalcomponent_get_next_component(comp,
283 /* TODO: set gerror for "out of memory" */
285 /* gerror should have been set already */
290 g_hash_table_destroy(mapping);
293 icaltimezone_free(zone, 1);
304 icaltimezone *e_cal_tzlookup_ecal(const char *tzid,
308 ECal *ecal = (ECal *)custom;
309 icaltimezone *zone = NULL;
311 if (e_cal_get_timezone(ecal, tzid, &zone, error)) {
312 g_assert(*error == NULL);
316 if ((*error)->domain == E_CALENDAR_ERROR &&
317 (*error)->code == E_CALENDAR_STATUS_OBJECT_NOT_FOUND) {
319 * we had to trigger this error to check for the timezone existance,
320 * clear it and return NULL
322 g_clear_error(error);
328 icaltimezone *e_cal_tzlookup_icomp(const char *tzid,
332 const icalcomponent *icomp = custom;