diff options
Diffstat (limited to 'src/libical/icaltimezone.c')
-rw-r--r-- | src/libical/icaltimezone.c | 286 |
1 files changed, 192 insertions, 94 deletions
diff --git a/src/libical/icaltimezone.c b/src/libical/icaltimezone.c index dd0e511..aa9c1d9 100644 --- a/src/libical/icaltimezone.c +++ b/src/libical/icaltimezone.c @@ -40,12 +40,20 @@ #include "icalerror.h" #include "icalparser.h" #include "icaltimezone.h" +#include "icaltimezoneimpl.h" #include "icaltz-util.h" #include <sys/stat.h> +#ifdef HAVE_PTHREAD +#include <pthread.h> +static pthread_mutex_t builtin_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + #ifdef WIN32 +#ifndef _WIN32_WCE #include <mbstring.h> +#endif #include <windows.h> /* Undef the similar macro from pthread.h, it doesn't check if * gmtime() returns NULL. @@ -54,6 +62,16 @@ /* The gmtime() in Microsoft's C library is MT-safe */ #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) + +// MSVC lacks the POSIX macro S_ISDIR, however it's a trivial one: +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +#endif +#endif + +#if defined(_MSC_VER) +#define snprintf _snprintf +#define strcasecmp stricmp #endif /** This is the toplevel directory where the timezone data is installed in. */ @@ -76,54 +94,6 @@ const char *ical_tzid_prefix = "/freeassociation.sourceforge.net/"; somewhere around 2037. */ #define ICALTIMEZONE_MAX_YEAR 2035 -struct _icaltimezone { - char *tzid; - /**< The unique ID of this timezone, - e.g. "/citadel.org/Olson_20010601_1/Africa/Banjul". - This should only be used to identify a VTIMEZONE. It is not - meant to be displayed to the user in any form. */ - - char *location; - /**< The location for the timezone, e.g. "Africa/Accra" for the - Olson database. We look for this in the "LOCATION" or - "X-LIC-LOCATION" properties of the VTIMEZONE component. It - isn't a standard property yet. This will be NULL if no location - is found in the VTIMEZONE. */ - - char *tznames; - /**< This will be set to a combination of the TZNAME properties - from the last STANDARD and DAYLIGHT components in the - VTIMEZONE, e.g. "EST/EDT". If they both use the same TZNAME, - or only one type of component is found, then only one TZNAME - will appear, e.g. "AZOT". If no TZNAME is found this will be - NULL. */ - - double latitude; - double longitude; - /**< The coordinates of the city, in degrees. */ - - icalcomponent *component; - /**< The toplevel VTIMEZONE component loaded from the .ics file for this - timezone. If we need to regenerate the changes data we need this. */ - - icaltimezone *builtin_timezone; - /**< If this is not NULL it points to the builtin icaltimezone - that the above TZID refers to. This icaltimezone should be used - instead when accessing the timezone changes data, so that the - expanded timezone changes data is shared between calendar - components. */ - - int end_year; - /**< This is the last year for which we have expanded the data to. - If we need to calculate a date past this we need to expand the - timezone component data from scratch. */ - - icalarray *changes; - /**< A dynamically-allocated array of time zone changes, sorted by the - time of the change in local time. So we can do fast binary-searches - to convert from local time to UTC. */ -}; - typedef struct _icaltimezonechange icaltimezonechange; struct _icaltimezonechange { @@ -234,10 +204,18 @@ icaltimezone_copy (icaltimezone *originalzone) } memcpy (zone, originalzone, sizeof (icaltimezone)); + if (zone->tzid != NULL) + zone->tzid = strdup (zone->tzid); if (zone->location != NULL) zone->location = strdup (zone->location); if (zone->tznames != NULL) zone->tznames = strdup (zone->tznames); + if (zone->changes != NULL) + zone->changes = icalarray_copy(zone->changes); + + /* Let the caller set the component because then they will + know to be careful not to free this reference twice. */ + zone->component = NULL; return zone; } @@ -298,7 +276,7 @@ icaltimezone_get_vtimezone_properties (icaltimezone *zone, icalcomponent *component) { icalproperty *prop; - const char *tzid, *tzname; + const char *tzid; prop = icalcomponent_get_first_property (component, ICAL_TZID_PROPERTY); if (!prop) @@ -309,17 +287,16 @@ icaltimezone_get_vtimezone_properties (icaltimezone *zone, if (!tzid) return 0; - prop = icalcomponent_get_first_property (component, ICAL_TZNAME_PROPERTY); - if (prop) { - tzname = icalproperty_get_tzname (prop); - zone->tznames = strdup(tzname); - } else - zone->tznames = NULL; - + if (zone->tzid) free(zone->tzid); zone->tzid = strdup (tzid); + + if (zone->component) icalcomponent_free(zone->component); zone->component = component; - if ( zone->location != 0 ) free ( zone->location ); + + if (zone->location) free(zone->location); zone->location = icaltimezone_get_location_from_vtimezone (component); + + if (zone->tznames) free(zone->tznames); zone->tznames = icaltimezone_get_tznames_from_vtimezone (component); return 1; @@ -449,7 +426,7 @@ icaltimezone_get_tznames_from_vtimezone (icalcomponent *component) /* If both standard and daylight TZNAMEs were found, if they are the same we return just one, else we format them like "EST/EDT". */ if (standard_tzname && daylight_tzname) { - unsigned int standard_len, daylight_len; + size_t standard_len, daylight_len; char *tznames; if (!strcmp (standard_tzname, daylight_tzname)) @@ -482,8 +459,7 @@ icaltimezone_ensure_coverage (icaltimezone *zone, int changes_end_year; - if (!zone->component) - icaltimezone_load_builtin_timezone (zone); + icaltimezone_load_builtin_timezone (zone); if (icaltimezone_minimum_expansion_year == -1) { struct icaltimetype today = icaltime_today(); @@ -552,7 +528,7 @@ icaltimezone_expand_vtimezone (icalcomponent *comp, icalrecur_iterator* rrule_iterator; struct icaldatetimeperiodtype rdate; int found_dtstart = 0, found_tzoffsetto = 0, found_tzoffsetfrom = 0; - int has_recurrence = 0; + int has_rdate = 0, has_rrule = 0; /* First we check if it is a STANDARD or DAYLIGHT component, and just return if it isn't. */ @@ -584,8 +560,10 @@ icaltimezone_expand_vtimezone (icalcomponent *comp, found_tzoffsetfrom = 1; break; case ICAL_RDATE_PROPERTY: + has_rdate = 1; + break; case ICAL_RRULE_PROPERTY: - has_recurrence = 1; + has_rrule = 1; break; default: /* Just ignore any other properties. */ @@ -595,6 +573,14 @@ icaltimezone_expand_vtimezone (icalcomponent *comp, prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY); } + /* Microsoft Outlook for Mac (and possibly other versions) will create + timezones without a tzoffsetfrom property if it's a timezone that + doesn't change for DST. */ + if (found_tzoffsetto && !found_tzoffsetfrom) { + change.prev_utc_offset = change.utc_offset; + found_tzoffsetfrom = 1; + } + /* If we didn't find a DTSTART, TZOFFSETTO and TZOFFSETFROM we have to ignore the component. FIXME: Add an error property? */ if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom) @@ -606,9 +592,9 @@ icaltimezone_expand_vtimezone (icalcomponent *comp, dtstart.hour, dtstart.minute, dtstart.second); #endif - /* If the STANDARD/DAYLIGHT component has no recurrence data, we just add + /* If the STANDARD/DAYLIGHT component has no recurrence rule, we add a single change for the DTSTART. */ - if (!has_recurrence) { + if (!has_rrule) { change.year = dtstart.year; change.month = dtstart.month; change.day = dtstart.day; @@ -627,12 +613,11 @@ icaltimezone_expand_vtimezone (icalcomponent *comp, /* Add the change to the array. */ icalarray_append (changes, &change); - return; } /* The component has recurrence data, so we expand that now. */ prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY); - while (prop) { + while (prop && (has_rdate || has_rrule)) { #if 0 printf ("Expanding property...\n"); #endif @@ -688,9 +673,32 @@ icaltimezone_expand_vtimezone (icalcomponent *comp, rrule.until.is_utc = 0; } + /* Add the dtstart to changes, otherwise some oddly-defined VTIMEZONE + components can cause the first year to get skipped. */ + change.year = dtstart.year; + change.month = dtstart.month; + change.day = dtstart.day; + change.hour = dtstart.hour; + change.minute = dtstart.minute; + change.second = dtstart.second; + +#if 0 + printf (" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n", + change.year, change.month, change.day, + change.hour, change.minute, change.second); +#endif + + icaltimezone_adjust_change (&change, 0, 0, 0, + -change.prev_utc_offset); + + icalarray_append (changes, &change); + rrule_iterator = icalrecur_iterator_new (rrule, dtstart); - for (;;) { + for (;rrule_iterator;) { occ = icalrecur_iterator_next (rrule_iterator); + /* Skip dtstart since we just added it */ + if (icaltime_compare(dtstart, occ) == 0) + continue; if (occ.year > end_year || icaltime_is_null_time (occ)) break; @@ -899,9 +907,12 @@ icaltimezone_get_utc_offset (icaltimezone *zone, change_num += step; /* If we go past the start of the changes array, then we have no data - for this time so we return a UTC offset of 0. */ - if (change_num < 0) - return 0; + for this time so we return the prev UTC offset. */ + if (change_num < 0) { + if (is_daylight) + *is_daylight = ! tmp_change.is_daylight; + return tmp_change.prev_utc_offset; + } if ((unsigned int)change_num >= zone->changes->num_elements) break; @@ -1169,8 +1180,7 @@ icaltimezone_get_tzid (icaltimezone *zone) if (!zone) return NULL; - if (!zone->tzid) - icaltimezone_load_builtin_timezone (zone); + icaltimezone_load_builtin_timezone (zone); return zone->tzid; } @@ -1196,8 +1206,7 @@ icaltimezone_get_tznames (icaltimezone *zone) if (!zone) return NULL; - if (!zone->component) - icaltimezone_load_builtin_timezone (zone); + icaltimezone_load_builtin_timezone (zone); return zone->tznames; } @@ -1239,8 +1248,7 @@ icaltimezone_get_component (icaltimezone *zone) if (!zone) return NULL; - if (!zone->component) - icaltimezone_load_builtin_timezone (zone); + icaltimezone_load_builtin_timezone (zone); return zone->component; } @@ -1354,6 +1362,7 @@ void icaltimezone_free_builtin_timezones(void) { icaltimezone_array_free(builtin_timezones); + builtin_timezones = 0; } @@ -1361,8 +1370,9 @@ icaltimezone_free_builtin_timezones(void) icaltimezone* icaltimezone_get_builtin_timezone (const char *location) { + icalcomponent *comp; icaltimezone *zone; - int lower; + unsigned int lower; const char *zone_location; if (!location || !location[0]) @@ -1371,7 +1381,7 @@ icaltimezone_get_builtin_timezone (const char *location) if (!builtin_timezones) icaltimezone_init_builtin_timezones (); - if (!strcmp (location, "UTC")) + if (strcmp (location, "UTC") == 0 || strcmp (location, "GMT") == 0) return &utc_timezone; #if 0 @@ -1402,6 +1412,20 @@ icaltimezone_get_builtin_timezone (const char *location) return zone; } + /* Check whether file exists, but is not mentioned in zone.tab. + It means it's a deprecated timezone, but still available. */ + comp = icaltzutil_fetch_timezone (location); + if (comp) { + icaltimezone tz; + icaltimezone_init (&tz); + if (icaltimezone_set_component (&tz, comp)) { + icalarray_append (builtin_timezones, &tz); + return icalarray_element_at (builtin_timezones, builtin_timezones->num_elements - 1); + } else { + icalcomponent_free (comp); + } + } + return NULL; } @@ -1462,8 +1486,7 @@ icaltimezone_get_builtin_timezone_from_offset (int offset, const char *tzname) for (i=0; i<count; i++) { int z_offset; zone = icalarray_element_at (builtin_timezones, i); - if (!zone->component) - icaltimezone_load_builtin_timezone (zone); + icaltimezone_load_builtin_timezone (zone); z_offset = get_offset(zone); @@ -1485,12 +1508,15 @@ icaltimezone_get_builtin_timezone_from_tzid (const char *tzid) if (!tzid || !tzid[0]) return NULL; + if (strcmp (tzid, "UTC") == 0 || strcmp (tzid, "GMT") == 0) { + return icaltimezone_get_builtin_timezone(tzid); + } + /* Check that the TZID starts with our unique prefix. */ if (strncmp (tzid, ical_tzid_prefix, strlen(ical_tzid_prefix))) return NULL; /* Get the location, which is after the 3rd '/' character. */ - p = tzid; for (p = tzid; *p; p++) { if (*p == '/') { num_slashes++; @@ -1541,7 +1567,14 @@ icaltimezone_init_builtin_timezones (void) /* Initialize the special UTC timezone. */ utc_timezone.tzid = (char *)"UTC"; - icaltimezone_parse_zone_tab (); +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&builtin_mutex); +#endif + if (!builtin_timezones) + icaltimezone_parse_zone_tab (); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&builtin_mutex); +#endif } static int @@ -1570,7 +1603,7 @@ parse_coord (char *coord, } static int fetch_lat_long_from_string (const char *str, int *latitude_degrees, int *latitude_minutes, int *latitude_seconds, - int *longitude_degrees, int *longitude_minutes, int *longitude_seconds, char *location) + int *longitude_degrees, int *longitude_minutes, int *longitude_seconds, char *location) { size_t len; char *sptr, *lat, *lon, *loc, *temp; @@ -1614,10 +1647,17 @@ fetch_lat_long_from_string (const char *str, int *latitude_degrees, int *latitu while (*lon != '+' && *lon != '-') lon++; - if (parse_coord (lat, lon - lat, latitude_degrees, latitude_minutes, latitude_seconds) == 1 || - parse_coord (lon, strlen (lon), longitude_degrees, longitude_minutes, longitude_seconds) - == 1) - return 1; + if (parse_coord (lat, (int)(lon - lat), + latitude_degrees, + latitude_minutes, + latitude_seconds) == 1 || + parse_coord (lon, (int)strlen(lon), + longitude_degrees, + longitude_minutes, + longitude_seconds) == 1) { + free(lat); + return 1; + } free (lat); @@ -1638,7 +1678,7 @@ icaltimezone_parse_zone_tab (void) FILE *fp; char buf[1024]; /* Used to store each line of zones.tab as it is read. */ char location[1024]; /* Stores the city name when parsing buf. */ - unsigned int filename_len; + size_t filename_len; int latitude_degrees = 0, latitude_minutes = 0, latitude_seconds = 0; int longitude_degrees = 0, longitude_minutes = 0, longitude_seconds = 0; icaltimezone zone; @@ -1735,7 +1775,7 @@ icaltimezone_parse_zone_tab (void) void icaltimezone_release_zone_tab (void) { - int i; + unsigned int i; icalarray *mybuiltin_timezones = builtin_timezones; if (builtin_timezones == NULL) @@ -1756,6 +1796,17 @@ icaltimezone_load_builtin_timezone (icaltimezone *zone) if (!zone->location || !zone->location[0]) return; +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&builtin_mutex); + if (zone->component) + goto out; +#else +#ifdef USE_BUILTIN_TZDATA + if (zone->component) + return; +#endif +#endif + #ifdef USE_BUILTIN_TZDATA { char *filename; @@ -1769,7 +1820,7 @@ icaltimezone_load_builtin_timezone (icaltimezone *zone) filename = (char*) malloc (filename_len); if (!filename) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); - return; + goto out; } snprintf (filename, filename_len, "%s/%s.ics", get_zone_directory(), @@ -1779,7 +1830,7 @@ icaltimezone_load_builtin_timezone (icaltimezone *zone) free (filename); if (!fp) { icalerror_set_errno(ICAL_FILE_ERROR); - return; + goto out; } @@ -1803,7 +1854,7 @@ icaltimezone_load_builtin_timezone (icaltimezone *zone) if (!subcomp) { icalerror_set_errno(ICAL_PARSE_ERROR); - return; + goto out; } icaltimezone_get_vtimezone_properties (zone, subcomp); @@ -1812,8 +1863,13 @@ icaltimezone_load_builtin_timezone (icaltimezone *zone) icalcomponent_remove_component(comp,subcomp); icalcomponent_free(comp); } -#endif +#endif + out: +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&builtin_mutex); +#endif + return; } @@ -1867,7 +1923,6 @@ icaltimezone_dump_changes (icaltimezone *zone, printf ("Num changes: %i\n", zone->changes->num_elements); #endif - change_num = 0; for (change_num = 0; (unsigned int)change_num < zone->changes->num_elements; change_num++) { zone_change = icalarray_element_at (zone->changes, change_num); @@ -1929,10 +1984,18 @@ static const char* get_zone_directory(void) return zone_files_directory == NULL ? ZONEINFO_DIRECTORY : zone_files_directory; #else wchar_t wbuffer[1000]; +#ifndef _WIN32_WCE char buffer[1000], zoneinfodir[1000], dirname[1000]; +#else + wchar_t zoneinfodir[1000], dirname[1000]; +#endif int used_default; static char *cache = NULL; +#ifndef _WIN32_WCE char *dirslash, *zislash; +#else + wchar_t *dirslash, *zislash; +#endif struct stat st; if (zone_files_directory) @@ -1945,6 +2008,8 @@ static const char* get_zone_directory(void) if (!GetModuleFileNameW (NULL, wbuffer, sizeof (wbuffer) / sizeof (wbuffer[0]))) return ZONEINFO_DIRECTORY; +/*wince supports only unicode*/ +#ifndef _WIN32_WCE /* Convert to system codepage */ if (!WideCharToMultiByte (CP_ACP, 0, wbuffer, -1, buffer, sizeof (buffer), NULL, &used_default) || @@ -1957,6 +2022,7 @@ static const char* get_zone_directory(void) used_default) return ZONEINFO_DIRECTORY; } +#endif /* Look for the zoneinfo directory somewhere in the path where * the app is installed. If the path to the app is * @@ -1992,10 +2058,38 @@ static const char* get_zone_directory(void) */ /* Strip away basename of app .exe first */ +#ifndef _WIN32_WCE dirslash = _mbsrchr (buffer, '\\'); +#else + dirslash = wcsrchr (wbuffer, L'\\'); +#endif if (dirslash) +#ifndef _WIN32_WCE *dirslash = '\0'; +#else + *dirslash = L'\0'; +#endif +#ifdef _WIN32_WCE + while ((dirslash = wcsrchr (wbuffer, '\\'))) { + /* Strip one more directory from app .exe location */ + *dirslash = L'\0'; + + MultiByteToWideChar(CP_ACP,0,ZONEINFO_DIRECTORY,-1,zoneinfodir,1000); + + while ((zislash = wcschr (zoneinfodir, L'/'))) { + *zislash = L'.'; + wcscpy (dirname, wbuffer); + wcscat (dirname, "/"); + wcscat (dirname, zislash + 1); + if (stat (dirname, &st) == 0 && + S_ISDIR (st.st_mode)) { + cache = wce_wctomb (dirname); + return cache; + } + } + } +#else while ((dirslash = _mbsrchr (buffer, '\\'))) { /* Strip one more directory from app .exe location */ *dirslash = '\0'; @@ -2013,12 +2107,15 @@ static const char* get_zone_directory(void) } } } +#endif return ZONEINFO_DIRECTORY; #endif } void set_zone_directory(char *path) { + if (zone_files_directory) + free_zone_directory(); zone_files_directory = malloc(strlen(path)+1); if ( zone_files_directory != NULL ) { @@ -2031,6 +2128,7 @@ void free_zone_directory(void) if ( zone_files_directory != NULL ) { free(zone_files_directory); + zone_files_directory = NULL; } } |