/src/libical/src/libical/icaltz-util.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Authors : |
3 | | * Chenthill Palanisamy <pchenthill@novell.com> |
4 | | * |
5 | | * SPDX-FileCopyrightText: 2007, Novell, Inc. |
6 | | * |
7 | | * SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0 |
8 | | */ |
9 | | //krazy:excludeall=cpp |
10 | | |
11 | | #ifdef HAVE_CONFIG_H |
12 | | #include <config.h> |
13 | | #endif |
14 | | |
15 | | #include "icaltz-util.h" |
16 | | #include "icalerror.h" |
17 | | #include "icaltimezone.h" |
18 | | #include "icalmemory.h" |
19 | | |
20 | | #include <stdlib.h> |
21 | | #include <limits.h> |
22 | | |
23 | | #if defined(HAVE_BYTESWAP_H) |
24 | | #include <byteswap.h> |
25 | | #endif |
26 | | #if defined(HAVE_ENDIAN_H) |
27 | | #include <endian.h> |
28 | | #else |
29 | | #if defined(HAVE_SYS_ENDIAN_H) |
30 | | #include <sys/endian.h> |
31 | | #if defined(bswap32) |
32 | | #define bswap_32 bswap32 |
33 | | #else |
34 | | #define bswap_32 swap32 |
35 | | #endif |
36 | | #endif |
37 | | #endif |
38 | | |
39 | | #if defined(__OpenBSD__) && !defined(bswap_32) |
40 | | #define bswap_32 swap32 |
41 | | #endif |
42 | | |
43 | | #if defined(_MSC_VER) |
44 | | #if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_ENDIAN_H) && !defined(HAVE_ENDIAN_H) |
45 | | #define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8) & 0xff) |
46 | | |
47 | | #define bswap_32(x) \ |
48 | | (((x) << 24) & 0xff000000) | \ |
49 | | (((x) << 8) & 0xff0000) | \ |
50 | | (((x) >> 8) & 0xff00) | \ |
51 | | (((x) >> 24) & 0xff) |
52 | | |
53 | | #define bswap_64(x) \ |
54 | | ((((x) & 0xff00000000000000ull) >> 56) | \ |
55 | | (((x) & 0x00ff000000000000ull) >> 40) | \ |
56 | | (((x) & 0x0000ff0000000000ull) >> 24) | \ |
57 | | (((x) & 0x000000ff00000000ull) >> 8) | \ |
58 | | (((x) & 0x00000000ff000000ull) << 8) | \ |
59 | | (((x) & 0x0000000000ff0000ull) << 24) | \ |
60 | | (((x) & 0x000000000000ff00ull) << 40) | \ |
61 | | (((x) & 0x00000000000000ffull) << 56)) |
62 | | #endif |
63 | | #include <io.h> |
64 | | #endif |
65 | | |
66 | | #if defined(__APPLE__) || defined(__MINGW32__) |
67 | | #define bswap_16(x) (((x) << 8) & 0xff00) | (((x) >> 8) & 0xff) |
68 | | #define bswap_32 __builtin_bswap32 |
69 | | #define bswap_64 __builtin_bswap64 |
70 | | #endif |
71 | | |
72 | | //@cond PRIVATE |
73 | | typedef struct |
74 | | { |
75 | | char magic[4]; |
76 | | char version; |
77 | | char unused[15]; |
78 | | char ttisgmtcnt[4]; |
79 | | char ttisstdcnt[4]; |
80 | | char leapcnt[4]; |
81 | | char timecnt[4]; |
82 | | char typecnt[4]; |
83 | | char charcnt[4]; |
84 | | } tzinfo; |
85 | | |
86 | | /* fullpath to the system zoneinfo directory (where zone.tab lives) */ |
87 | | static ICAL_GLOBAL_VAR char s_zoneinfopath[MAXPATHLEN] = {0}; |
88 | | |
89 | | /* A few well-known locations for system zoneinfo; can be overridden with TZDIR environment */ |
90 | | static const char *s_zoneinfo_search_paths[] = { |
91 | | "/usr/share/zoneinfo", |
92 | | "/usr/lib/zoneinfo", |
93 | | "/etc/zoneinfo", |
94 | | "/usr/share/lib/zoneinfo"}; |
95 | | |
96 | | #define EFREAD(buf, size, num, fs) \ |
97 | 0 | if (fread(buf, size, num, fs) < num && ferror(fs)) { \ |
98 | 0 | icalerror_set_errno(ICAL_FILE_ERROR); \ |
99 | 0 | goto error; \ |
100 | 0 | } |
101 | | |
102 | | typedef struct |
103 | | { |
104 | | long int gmtoff; |
105 | | unsigned char isdst; |
106 | | unsigned int abbr; |
107 | | unsigned char isstd; |
108 | | unsigned char isgmt; |
109 | | char *zname; |
110 | | |
111 | | } ttinfo; |
112 | | |
113 | | typedef struct |
114 | | { |
115 | | icaltime_t transition; |
116 | | long int change; |
117 | | } leap; |
118 | | //@endcond |
119 | | |
120 | | static int decode(const void *ptr) |
121 | 0 | { |
122 | 0 | if ((BYTE_ORDER == BIG_ENDIAN) && sizeof(int) == 4) { |
123 | 0 | return *(const int *)ptr; |
124 | 0 | } else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof(int) == 4) { |
125 | 0 | return (int)bswap_32(*(const unsigned int *)ptr); |
126 | 0 | } else { |
127 | 0 | const unsigned char *p = ptr; |
128 | 0 | int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0; |
129 | | |
130 | | /* cppcheck-suppress shiftNegativeLHS */ |
131 | 0 | result = (result << 8) | *p++; |
132 | 0 | result = (result << 8) | *p++; |
133 | 0 | result = (result << 8) | *p++; |
134 | 0 | result = (result << 8) | *p++; |
135 | |
|
136 | 0 | return result; |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | static long long int decode64(const void *ptr) |
141 | 0 | { |
142 | 0 | if ((BYTE_ORDER == BIG_ENDIAN)) { |
143 | 0 | return *(const long long int *)ptr; |
144 | 0 | } else { |
145 | 0 | return (const long long int)bswap_64(*(const unsigned long long int *)ptr); |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | | static char *zname_from_stridx(char *str, size_t idx) |
150 | 0 | { |
151 | 0 | size_t i; |
152 | 0 | size_t size; |
153 | 0 | char *ret; |
154 | |
|
155 | 0 | i = idx; |
156 | 0 | while (str[i] != '\0') { |
157 | 0 | i++; |
158 | 0 | } |
159 | |
|
160 | 0 | size = i - idx; |
161 | 0 | str += idx; |
162 | 0 | ret = (char *)icalmemory_new_buffer(size + 1); |
163 | 0 | ret = strncpy(ret, str, size); |
164 | 0 | ret[size] = '\0'; |
165 | |
|
166 | 0 | return ret; |
167 | 0 | } |
168 | | |
169 | | static void set_zoneinfopath(void) |
170 | 2 | { |
171 | 2 | char file_path[MAXPATHLEN]; |
172 | 2 | const char *fname = ZONES_TAB_SYSTEM_FILENAME; |
173 | 2 | size_t i, num_zi_search_paths; |
174 | | |
175 | | /* Search for the zone.tab file in the dir specified by the TZDIR environment */ |
176 | 2 | const char *env_tzdir = getenv("TZDIR"); |
177 | 2 | if (env_tzdir != NULL) { |
178 | 0 | snprintf(file_path, MAXPATHLEN, "%s/%s", env_tzdir, fname); |
179 | 0 | if (!access(file_path, F_OK | R_OK)) { |
180 | 0 | strncpy(s_zoneinfopath, env_tzdir, MAXPATHLEN - 1); |
181 | 0 | return; |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | /* Else, search for zone.tab in a list of well-known locations */ |
186 | 2 | num_zi_search_paths = sizeof(s_zoneinfo_search_paths) / sizeof(s_zoneinfo_search_paths[0]); |
187 | 2 | for (i = 0; i < num_zi_search_paths; i++) { |
188 | 2 | snprintf(file_path, MAXPATHLEN, "%s/%s", s_zoneinfo_search_paths[i], fname); |
189 | 2 | if (!access(file_path, F_OK | R_OK)) { |
190 | 2 | strncpy(s_zoneinfopath, s_zoneinfo_search_paths[i], MAXPATHLEN - 1); |
191 | 2 | break; |
192 | 2 | } |
193 | 2 | } |
194 | 2 | } |
195 | | |
196 | | void icaltzutil_set_zone_directory(const char *zonepath) |
197 | 0 | { |
198 | 0 | if ((zonepath == NULL) || (zonepath[0] == '\0')) { |
199 | 0 | memset(s_zoneinfopath, 0, MAXPATHLEN); |
200 | 0 | } else { |
201 | 0 | strncpy(s_zoneinfopath, zonepath, MAXPATHLEN - 1); |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | const char *icaltzutil_get_zone_directory(void) |
206 | 2 | { |
207 | 2 | if (s_zoneinfopath[0] == '\0') { |
208 | 2 | set_zoneinfopath(); |
209 | 2 | } |
210 | | |
211 | 2 | return s_zoneinfopath; |
212 | 2 | } |
213 | | |
214 | | static int calculate_pos(icaltimetype icaltime) |
215 | 0 | { |
216 | 0 | static const int r_pos[] = {1, 2, 3, -2, -1}; |
217 | 0 | int pos; |
218 | |
|
219 | 0 | pos = (icaltime.day - 1) / 7; |
220 | | |
221 | | /* Check if pos 3 is the last occurrence of the week day in the month */ |
222 | 0 | if (pos == 3 && ((icaltime.day + 7) > icaltime_days_in_month(icaltime.month, icaltime.year))) { |
223 | 0 | pos = 4; |
224 | 0 | } |
225 | |
|
226 | 0 | return r_pos[pos]; |
227 | 0 | } |
228 | | |
229 | | static char *parse_posix_zone(char *p, ttinfo *type) |
230 | 0 | { |
231 | 0 | size_t size; |
232 | | |
233 | | /* Zone name */ |
234 | 0 | if (*p == '<') { |
235 | | /* Alphanumeric, '-', or '+' */ |
236 | 0 | size = strcspn(++p, ">"); |
237 | 0 | } else { |
238 | | /* Alpha ONLY */ |
239 | 0 | size = strcspn(p, "-+0123456789,\n"); |
240 | 0 | } |
241 | |
|
242 | 0 | type->zname = (char *)icalmemory_new_buffer(size + 1); |
243 | 0 | strncpy(type->zname, p, size); |
244 | 0 | type->zname[size] = '\0'; |
245 | 0 | p += size; |
246 | |
|
247 | 0 | if (*p == '>') { |
248 | 0 | p++; |
249 | 0 | } |
250 | |
|
251 | 0 | if (*p == ',') { |
252 | 0 | return p; |
253 | 0 | } |
254 | | |
255 | | /* Zone offset: hh[:mm[:ss]] */ |
256 | 0 | type->gmtoff = strtol(p, &p, 10) * -3600; /* sign of offset is reversed */ |
257 | 0 | if (*p == ':') { |
258 | 0 | type->gmtoff += strtol(++p, &p, 10) * 60; |
259 | 0 | } |
260 | 0 | if (*p == ':') { |
261 | 0 | type->gmtoff += strtol(++p, &p, 10); |
262 | 0 | } |
263 | 0 | return p; |
264 | 0 | } |
265 | | |
266 | 0 | #define nth_weekday(week, day) (icalrecurrencetype_encode_day(day, week)) |
267 | | |
268 | | static char *parse_posix_rule(char *p, |
269 | | struct icalrecurrencetype *recur, icaltimetype *t) |
270 | 0 | { |
271 | 0 | int month = 0, monthday = 0, week = 0, day; |
272 | | |
273 | | /* Parse date */ |
274 | 0 | if (*p == 'J') { |
275 | | /* The Julian day n (1 <= n <= 365). |
276 | | Leap days shall not be counted. That is, in all years, |
277 | | including leap years, February 28 is day 59 and March 1 is day 60. |
278 | | It is impossible to refer explicitly to the occasional February 29. |
279 | | */ |
280 | 0 | day = strtol(++p, &p, 10); |
281 | 0 | } else if (*p == 'M') { |
282 | | /* The d'th day (0 <= d <= 6) |
283 | | of week n of month m of the year (1 <= n <= 5, 1 <= m <= 12, |
284 | | where week 5 means "the last d day in month m" |
285 | | which may occur in either the fourth or the fifth week). |
286 | | Week 1 is the first week in which the d'th day occurs. |
287 | | Day zero is Sunday. |
288 | | */ |
289 | 0 | month = strtol(++p, &p, 10); |
290 | 0 | week = strtol(++p, &p, 10); |
291 | 0 | day = strtol(++p, &p, 10); |
292 | 0 | if (week == 5) { |
293 | 0 | week = -1; |
294 | 0 | } |
295 | 0 | } else { |
296 | | /* The zero-based Julian day (0 <= n <= 365). |
297 | | Leap days shall be counted, and it is possible to refer to February 29. |
298 | | |
299 | | Flag this by adding 1001 to the day. |
300 | | */ |
301 | 0 | day = strtol(++p, &p, 10) + 1001; |
302 | 0 | } |
303 | | |
304 | | /* Parse time */ |
305 | 0 | *t = icaltime_null_time(); |
306 | 0 | t->hour = 2; /* default is 02:00 */ |
307 | |
|
308 | 0 | if (*p == '/') { |
309 | 0 | t->hour = strtol(++p, &p, 10); |
310 | 0 | if (*p == ':') |
311 | 0 | t->minute = strtol(++p, &p, 10); |
312 | 0 | if (*p == ':') |
313 | 0 | t->second = strtol(++p, &p, 10); |
314 | 0 | } |
315 | | |
316 | | /* Do adjustments for extended TZ strings */ |
317 | 0 | if (t->hour < 0 || t->hour > 23) { |
318 | 0 | int days_adjust = t->hour / 24; |
319 | |
|
320 | 0 | t->hour %= 24; |
321 | 0 | day += days_adjust; |
322 | |
|
323 | 0 | if (t->hour < 0) { |
324 | 0 | t->hour += 24; |
325 | 0 | day += 6; |
326 | 0 | } |
327 | 0 | if (month) { |
328 | 0 | if (week == -1) { |
329 | 0 | int days_in_month = icaltime_days_in_month(month, 1 /* non-leap */); |
330 | |
|
331 | 0 | monthday = days_in_month + days_adjust - 7; |
332 | 0 | } else { |
333 | 0 | monthday = 1 + (week - 1) * 7 + days_adjust; |
334 | 0 | } |
335 | 0 | week = 0; |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | | /* Create rule */ |
340 | 0 | icalrecurrencetype_clear(recur); |
341 | 0 | recur->freq = ICAL_YEARLY_RECURRENCE; |
342 | |
|
343 | 0 | if (month) { |
344 | 0 | recur->by_day[0] = nth_weekday(week, (day % 7) + 1); |
345 | 0 | recur->by_month[0] = month; |
346 | |
|
347 | 0 | if (monthday) { |
348 | 0 | unsigned i; |
349 | 0 | for (i = 0; i < 7; i++) { |
350 | 0 | recur->by_month_day[i] = monthday++; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | } else if (day > 1000) { |
354 | 0 | recur->by_year_day[0] = day - 1000; |
355 | 0 | } else { |
356 | | /* Convert day-of-non-leap-year into month/day */ |
357 | 0 | icaltimetype t = icaltime_from_day_of_year(day, 1 /* non-leap */); |
358 | |
|
359 | 0 | recur->by_month[0] = t.month; |
360 | 0 | recur->by_month_day[0] = t.day; |
361 | 0 | } |
362 | |
|
363 | 0 | return p; |
364 | 0 | } |
365 | | |
366 | | struct zone_context { |
367 | | enum icalcomponent_kind kind; |
368 | | const char *name; |
369 | | long gmtoff_from; |
370 | | long gmtoff_to; |
371 | | |
372 | | icaltimetype time; |
373 | | icaltimetype prev_time; |
374 | | |
375 | | icalcomponent *rdate_comp; |
376 | | icalcomponent *rrule_comp; |
377 | | icalproperty *rrule_prop; |
378 | | short num_monthdays; |
379 | | struct icalrecurrencetype recur; |
380 | | struct icalrecurrencetype final_recur; |
381 | | }; |
382 | | |
383 | | static void terminate_rrule(struct zone_context *zone) |
384 | 0 | { |
385 | 0 | if (icaltime_compare(zone->time, zone->prev_time)) { |
386 | | // Multiple instances |
387 | | // Set UNTIL of the component's recurrence |
388 | 0 | zone->recur.until = zone->time; |
389 | 0 | icaltime_adjust(&zone->recur.until, 0, 0, 0, -zone->gmtoff_from); |
390 | 0 | zone->recur.until.zone = icaltimezone_get_utc_timezone(); |
391 | | |
392 | | // Remove BYMONTHDAY if BYDAY week != 0 |
393 | 0 | if (icalrecurrencetype_day_position(zone->recur.by_day[0])) { |
394 | 0 | zone->recur.by_month_day[0] = ICAL_RECURRENCE_ARRAY_MAX; |
395 | 0 | } |
396 | |
|
397 | 0 | icalproperty_set_rrule(zone->rrule_prop, zone->recur); |
398 | |
|
399 | 0 | zone->rdate_comp = zone->rrule_comp = NULL; |
400 | 0 | } else { |
401 | | // Remove the RRULE from the component |
402 | 0 | icalcomponent_remove_property(zone->rrule_comp, zone->rrule_prop); |
403 | 0 | icalproperty_free(zone->rrule_prop); |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | icalcomponent *icaltzutil_fetch_timezone(const char *location) |
408 | 0 | { |
409 | 0 | tzinfo header; |
410 | 0 | size_t i, num_trans, num_chars, num_leaps, num_isstd, num_isgmt; |
411 | 0 | size_t num_types = 0; |
412 | 0 | size_t size; |
413 | 0 | int trans_size = 4; |
414 | |
|
415 | 0 | const char *zonedir; |
416 | 0 | FILE *f = NULL; |
417 | 0 | char *full_path = NULL; |
418 | 0 | icaltime_t *transitions = NULL; |
419 | 0 | char *r_trans = NULL, *temp; |
420 | 0 | int *trans_idx = NULL; |
421 | 0 | ttinfo *types = NULL; |
422 | 0 | char *znames = NULL; |
423 | 0 | leap *leaps = NULL; |
424 | 0 | char *tzid = NULL; |
425 | |
|
426 | 0 | char footer[100], *tzstr = NULL; |
427 | |
|
428 | 0 | int idx, prev_idx; |
429 | 0 | icalcomponent *tz_comp = NULL; |
430 | 0 | icalproperty *icalprop; |
431 | 0 | icaltimetype icaltime; |
432 | |
|
433 | 0 | struct zone_context standard = |
434 | 0 | {ICAL_XSTANDARD_COMPONENT, NULL, LONG_MIN, LONG_MIN, |
435 | 0 | ICALTIMETYPE_INITIALIZER, ICALTIMETYPE_INITIALIZER, |
436 | 0 | NULL, NULL, NULL, 0, |
437 | 0 | ICALRECURRENCETYPE_INITIALIZER, ICALRECURRENCETYPE_INITIALIZER}; |
438 | 0 | struct zone_context daylight = |
439 | 0 | {ICAL_XDAYLIGHT_COMPONENT, NULL, LONG_MIN, LONG_MIN, |
440 | 0 | ICALTIMETYPE_INITIALIZER, ICALTIMETYPE_INITIALIZER, |
441 | 0 | NULL, NULL, NULL, 0, |
442 | 0 | ICALRECURRENCETYPE_INITIALIZER, ICALRECURRENCETYPE_INITIALIZER}; |
443 | 0 | struct zone_context *zone; |
444 | |
|
445 | 0 | if (icaltimezone_get_builtin_tzdata()) { |
446 | 0 | goto error; |
447 | 0 | } |
448 | | |
449 | 0 | zonedir = icaltzutil_get_zone_directory(); |
450 | 0 | if (!zonedir) { |
451 | 0 | icalerror_set_errno(ICAL_FILE_ERROR); |
452 | 0 | goto error; |
453 | 0 | } |
454 | | |
455 | 0 | size = strlen(zonedir) + strlen(location) + 2; |
456 | 0 | full_path = (char *)icalmemory_new_buffer(size); |
457 | 0 | if (full_path == NULL) { |
458 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
459 | 0 | goto error; |
460 | 0 | } |
461 | 0 | snprintf(full_path, size, "%s/%s", zonedir, location); |
462 | 0 | if ((f = fopen(full_path, "rb")) == 0) { |
463 | 0 | icalerror_set_errno(ICAL_FILE_ERROR); |
464 | 0 | goto error; |
465 | 0 | } |
466 | | |
467 | | /* read version 1 header */ |
468 | 0 | EFREAD(&header, 44, 1, f); |
469 | 0 | if (memcmp(header.magic, "TZif", 4)) { |
470 | 0 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); |
471 | 0 | goto error; |
472 | 0 | } |
473 | 0 | switch (header.version) { |
474 | 0 | case 0: |
475 | 0 | break; |
476 | 0 | case '2': |
477 | 0 | case '3': |
478 | 0 | if (sizeof(icaltime_t) == 8) { |
479 | 0 | trans_size = 8; |
480 | 0 | } |
481 | 0 | break; |
482 | 0 | default: |
483 | 0 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); |
484 | 0 | goto error; |
485 | 0 | } |
486 | | |
487 | 0 | num_isgmt = (size_t)decode(header.ttisgmtcnt); |
488 | 0 | num_leaps = (size_t)decode(header.leapcnt); |
489 | 0 | num_chars = (size_t)decode(header.charcnt); |
490 | 0 | num_trans = (size_t)decode(header.timecnt); |
491 | 0 | num_isstd = (size_t)decode(header.ttisstdcnt); |
492 | 0 | num_types = (size_t)decode(header.typecnt); |
493 | |
|
494 | 0 | if (trans_size == 8) { |
495 | 0 | size_t skip = num_trans * 5 + num_types * 6 + |
496 | 0 | num_chars + num_leaps * 8 + num_isstd + num_isgmt; |
497 | | |
498 | | /* skip version 1 data block */ |
499 | 0 | if (fseek(f, (long)skip, SEEK_CUR) != 0) { |
500 | 0 | icalerror_set_errno(ICAL_FILE_ERROR); |
501 | 0 | goto error; |
502 | 0 | } |
503 | | |
504 | | /* read version 2+ header */ |
505 | 0 | EFREAD(&header, 44, 1, f); |
506 | 0 | if (memcmp(header.magic, "TZif", 4)) { |
507 | 0 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); |
508 | 0 | goto error; |
509 | 0 | } |
510 | | |
511 | 0 | num_isgmt = (size_t)decode(header.ttisgmtcnt); |
512 | 0 | num_leaps = (size_t)decode(header.leapcnt); |
513 | 0 | num_chars = (size_t)decode(header.charcnt); |
514 | 0 | num_trans = (size_t)decode(header.timecnt); |
515 | 0 | num_isstd = (size_t)decode(header.ttisstdcnt); |
516 | 0 | num_types = (size_t)decode(header.typecnt); |
517 | 0 | } |
518 | | |
519 | | /* read data block */ |
520 | 0 | transitions = icalmemory_new_buffer((num_trans + 1) * sizeof(icaltime_t)); // +1 for TZ string |
521 | 0 | if (transitions == NULL) { |
522 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
523 | 0 | goto error; |
524 | 0 | } |
525 | 0 | r_trans = icalmemory_new_buffer(num_trans * (size_t)trans_size); |
526 | 0 | if (r_trans == NULL) { |
527 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
528 | 0 | goto error; |
529 | 0 | } |
530 | 0 | trans_idx = icalmemory_new_buffer((num_trans + 1) * sizeof(int)); // +1 for TZ string |
531 | 0 | if (trans_idx == NULL) { |
532 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
533 | 0 | goto error; |
534 | 0 | } |
535 | 0 | if (num_trans == 0) { |
536 | | // Add one transition using time type 0 at 19011213T204552Z |
537 | 0 | transitions[0] = (icaltime_t)INT_MIN; |
538 | 0 | trans_idx[0] = 0; |
539 | 0 | num_trans = 1; |
540 | 0 | } else { |
541 | 0 | EFREAD(r_trans, (size_t)trans_size, num_trans, f); |
542 | 0 | temp = r_trans; |
543 | 0 | for (i = 0; i < num_trans; i++) { |
544 | 0 | trans_idx[i] = fgetc(f); |
545 | 0 | if (trans_size == 8) { |
546 | 0 | transitions[i] = (icaltime_t)decode64(r_trans); |
547 | 0 | } else { |
548 | 0 | transitions[i] = (icaltime_t)decode(r_trans); |
549 | 0 | } |
550 | 0 | r_trans += trans_size; |
551 | 0 | } |
552 | 0 | r_trans = temp; |
553 | 0 | } |
554 | | |
555 | 0 | types = icalmemory_new_buffer((num_types + 2) * sizeof(ttinfo)); // +2 for TZ string |
556 | 0 | if (types == NULL) { |
557 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
558 | 0 | goto error; |
559 | 0 | } |
560 | 0 | for (i = 0; i < num_types; i++) { |
561 | 0 | unsigned char a[4]; |
562 | 0 | int c; |
563 | |
|
564 | 0 | EFREAD(a, 4, 1, f); |
565 | 0 | c = fgetc(f); |
566 | 0 | types[i].isdst = (unsigned char)c; |
567 | 0 | if ((c = fgetc(f)) < 0) { |
568 | 0 | break; |
569 | 0 | } |
570 | 0 | types[i].abbr = (unsigned int)c; |
571 | 0 | types[i].gmtoff = decode(a); |
572 | 0 | } |
573 | | |
574 | 0 | znames = (char *)icalmemory_new_buffer(num_chars); |
575 | 0 | if (znames == NULL) { |
576 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
577 | 0 | goto error; |
578 | 0 | } |
579 | 0 | EFREAD(znames, num_chars, 1, f); |
580 | | |
581 | | /* We got all the information which we need */ |
582 | |
|
583 | 0 | leaps = icalmemory_new_buffer(num_leaps * sizeof(leap)); |
584 | 0 | if (leaps == NULL) { |
585 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
586 | 0 | goto error; |
587 | 0 | } |
588 | 0 | for (i = 0; i < num_leaps; i++) { |
589 | 0 | char c[8]; |
590 | |
|
591 | 0 | EFREAD(c, (size_t)trans_size, 1, f); |
592 | 0 | if (trans_size == 8) { |
593 | 0 | leaps[i].transition = (icaltime_t)decode64(c); |
594 | 0 | } else { |
595 | 0 | leaps[i].transition = (icaltime_t)decode(c); |
596 | 0 | } |
597 | |
|
598 | 0 | EFREAD(c, 4, 1, f); |
599 | 0 | leaps[i].change = decode(c); |
600 | 0 | } |
601 | | |
602 | 0 | for (i = 0; i < num_isstd; ++i) { |
603 | 0 | int c = getc(f); |
604 | 0 | types[i].isstd = c != 0; |
605 | 0 | } |
606 | |
|
607 | 0 | while (i < num_types) { |
608 | 0 | types[i++].isstd = 0; |
609 | 0 | } |
610 | |
|
611 | 0 | for (i = 0; i < num_isgmt; ++i) { |
612 | 0 | int c = getc(f); |
613 | |
|
614 | 0 | types[i].isgmt = c != 0; |
615 | 0 | } |
616 | |
|
617 | 0 | while (i < num_types) { |
618 | 0 | types[i++].isgmt = 0; |
619 | 0 | } |
620 | |
|
621 | 0 | for (i = 0; i < num_types; i++) { |
622 | | /* coverity[tainted_data] */ |
623 | 0 | types[i].zname = zname_from_stridx(znames, types[i].abbr); |
624 | 0 | } |
625 | | |
626 | | /* Read the footer */ |
627 | 0 | if (trans_size == 8 && |
628 | 0 | (footer[0] = (char)fgetc(f)) == '\n' && |
629 | 0 | fgets(footer + 1, (int)sizeof(footer) - 1, f) && |
630 | 0 | footer[strlen(footer) - 1] == '\n') { |
631 | 0 | tzstr = footer + 1; |
632 | 0 | } |
633 | 0 | if (tzstr) { |
634 | | /* Parse the TZ string: |
635 | | stdoffset[dst[offset][,start[/time],end[/time]]] |
636 | | */ |
637 | 0 | ttinfo *std_type = &types[num_types++]; |
638 | 0 | ttinfo *dst_type = &types[num_types++]; |
639 | 0 | char *p = tzstr; |
640 | | |
641 | | /* Parse standard zone */ |
642 | 0 | p = parse_posix_zone(p, std_type); |
643 | 0 | if (*p == '\n') { |
644 | | /* No DST, so ignore the TZ string */ |
645 | 0 | tzstr = NULL; |
646 | 0 | } else { |
647 | | /* Parse DST zone */ |
648 | 0 | dst_type->isdst = 1; |
649 | 0 | dst_type->gmtoff = std_type->gmtoff + 3600; /* default is +1hr */ |
650 | 0 | p = parse_posix_zone(p, dst_type); |
651 | |
|
652 | 0 | if (*p != ',') { |
653 | | /* No rule, so ignore the TZ string */ |
654 | 0 | tzstr = NULL; |
655 | 0 | } else { |
656 | 0 | struct icaltimetype std_trans, dst_trans; |
657 | | |
658 | | /* Parse std->dst rule */ |
659 | 0 | p = parse_posix_rule(++p, /* skip ',' */ |
660 | 0 | &daylight.final_recur, &dst_trans); |
661 | | |
662 | | /* Parse dst->std rule */ |
663 | 0 | p = parse_posix_rule(++p, /* skip ',' */ |
664 | 0 | &standard.final_recur, &std_trans); |
665 | |
|
666 | 0 | if (*p != '\n') { |
667 | | /* Trailing junk, so ignore the TZ string */ |
668 | 0 | tzstr = NULL; |
669 | 0 | } else { |
670 | 0 | struct icaltimetype last_trans = |
671 | 0 | icaltime_from_timet_with_zone(transitions[num_trans - 1], |
672 | 0 | 0, NULL); |
673 | 0 | icalrecur_iterator *iter; |
674 | |
|
675 | 0 | if (types[trans_idx[num_trans - 1]].isdst) { |
676 | | /* Add next dst->std transition */ |
677 | 0 | std_trans.year = last_trans.year; |
678 | 0 | std_trans.month = last_trans.month; |
679 | 0 | std_trans.day = last_trans.day; |
680 | 0 | iter = icalrecur_iterator_new(standard.final_recur, |
681 | 0 | std_trans); |
682 | 0 | std_trans = icalrecur_iterator_next(iter); |
683 | 0 | icaltime_adjust(&std_trans, 0, 0, 0, -dst_type->gmtoff); |
684 | 0 | transitions[num_trans] = icaltime_as_timet(std_trans); |
685 | 0 | trans_idx[num_trans++] = (int)num_types - 2; |
686 | 0 | icalrecur_iterator_free(iter); |
687 | 0 | } else { |
688 | | /* Add next std->dst transition */ |
689 | 0 | dst_trans.year = last_trans.year; |
690 | 0 | dst_trans.month = last_trans.month; |
691 | 0 | dst_trans.day = last_trans.day; |
692 | 0 | iter = icalrecur_iterator_new(daylight.final_recur, |
693 | 0 | dst_trans); |
694 | 0 | dst_trans = icalrecur_iterator_next(iter); |
695 | 0 | icaltime_adjust(&dst_trans, 0, 0, 0, -std_type->gmtoff); |
696 | 0 | transitions[num_trans] = icaltime_as_timet(dst_trans); |
697 | 0 | trans_idx[num_trans++] = (int)num_types - 1; |
698 | 0 | icalrecur_iterator_free(iter); |
699 | 0 | } |
700 | 0 | } |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | | /* Build the VTIMEZONE now */ |
706 | |
|
707 | 0 | tz_comp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT); |
708 | | |
709 | | /* Add tzid property */ |
710 | 0 | size = strlen(icaltimezone_tzid_prefix()) + strlen(location) + 1; |
711 | 0 | tzid = (char *)icalmemory_new_buffer(size); |
712 | 0 | if (tzid == NULL) { |
713 | 0 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); |
714 | 0 | goto error; |
715 | 0 | } |
716 | 0 | snprintf(tzid, size, "%s%s", icaltimezone_tzid_prefix(), location); |
717 | 0 | icalprop = icalproperty_new_tzid(tzid); |
718 | 0 | icalcomponent_add_property(tz_comp, icalprop); |
719 | |
|
720 | 0 | icalprop = icalproperty_new_x(location); |
721 | 0 | icalproperty_set_x_name(icalprop, "X-LIC-LOCATION"); |
722 | 0 | icalcomponent_add_property(tz_comp, icalprop); |
723 | |
|
724 | 0 | idx = 0; // time type 0 is always time prior to first transition |
725 | |
|
726 | 0 | for (i = 0; i < num_trans; i++) { |
727 | 0 | int by_day = 0; |
728 | 0 | icaltime_t start; |
729 | 0 | enum icalrecurrencetype_weekday dow = ICAL_NO_WEEKDAY; |
730 | |
|
731 | 0 | prev_idx = idx; |
732 | 0 | idx = trans_idx[i]; |
733 | 0 | start = transitions[i] + types[prev_idx].gmtoff; |
734 | 0 | icaltime = icaltime_from_timet_with_zone(start, 0, NULL); |
735 | |
|
736 | 0 | if (types[idx].isdst) { |
737 | 0 | zone = &daylight; |
738 | 0 | } else { |
739 | 0 | zone = &standard; |
740 | 0 | } |
741 | | |
742 | | // The last two transition times are DTSTART for the TZ string RRULEs |
743 | 0 | if (tzstr && (i >= num_trans - 2)) { |
744 | 0 | terminate_rrule(zone); |
745 | 0 | zone->rrule_comp = NULL; |
746 | 0 | } else { |
747 | 0 | dow = icaltime_day_of_week(icaltime); |
748 | 0 | by_day = nth_weekday(calculate_pos(icaltime), dow); |
749 | 0 | } |
750 | |
|
751 | 0 | if (zone->rrule_comp) { |
752 | 0 | int terminate = 0; |
753 | 0 | int rdate = 0; |
754 | | |
755 | | // Check if the zone name or either of the offsets have changed |
756 | 0 | if (types[prev_idx].gmtoff != zone->gmtoff_from || |
757 | 0 | types[idx].gmtoff != zone->gmtoff_to || |
758 | 0 | (types[idx].zname != NULL && |
759 | 0 | strcmp(types[idx].zname, zone->name))) { |
760 | 0 | zone->rdate_comp = NULL; |
761 | 0 | terminate = 1; |
762 | 0 | } |
763 | | // Check if most of the recurrence pattern is the same |
764 | 0 | else if (icaltime.year == zone->time.year + 1 && |
765 | 0 | icaltime.month == zone->time.month && |
766 | 0 | icaltime.hour == zone->time.hour && |
767 | 0 | icaltime.minute == zone->time.minute && |
768 | 0 | icaltime.second == zone->time.second) { |
769 | 0 | if (by_day == zone->recur.by_day[0]) { |
770 | | // Same nth weekday of the month - continue |
771 | 0 | } else if (dow == icalrecurrencetype_day_day_of_week(zone->recur.by_day[0])) { |
772 | | // Same weekday in the month |
773 | 0 | if (icaltime.day >= zone->recur.by_month_day[0] + 7 || |
774 | 0 | icaltime.day + 7 <= zone->recur.by_month_day[zone->num_monthdays - 1]) { |
775 | | // Don't allow two month days with the same weekday - |
776 | | // possible RDATE |
777 | 0 | rdate = terminate = 1; |
778 | 0 | } else { |
779 | | // Insert day of month into the array |
780 | 0 | int j; |
781 | 0 | for (j = 0; j < zone->num_monthdays; j++) { |
782 | 0 | if (icaltime.day <= zone->recur.by_month_day[j]) { |
783 | 0 | break; |
784 | 0 | } |
785 | 0 | } |
786 | 0 | if (icaltime.day < zone->recur.by_month_day[j]) { |
787 | 0 | memmove(&zone->recur.by_month_day[j + 1], |
788 | 0 | &zone->recur.by_month_day[j], |
789 | 0 | (zone->num_monthdays - j) * |
790 | 0 | sizeof(zone->recur.by_month_day[0])); |
791 | 0 | zone->recur.by_month_day[j] = icaltime.day; |
792 | 0 | zone->num_monthdays++; |
793 | 0 | } |
794 | | |
795 | | // Remove week number from BYDAY |
796 | 0 | zone->recur.by_day[0] = nth_weekday(0, dow); |
797 | 0 | } |
798 | 0 | } else if (icaltime.day == zone->recur.by_month_day[0]) { |
799 | | // Same day of the month - remove BYDAY |
800 | 0 | zone->recur.by_day[0] = ICAL_RECURRENCE_ARRAY_MAX; |
801 | 0 | } else { |
802 | | // Different BYDAY and BYMONTHDAY - possible RDATE |
803 | 0 | rdate = terminate = 1; |
804 | 0 | } |
805 | 0 | } else { |
806 | | // Different recurrence pattern entirely - possible RDATE |
807 | 0 | rdate = terminate = 1; |
808 | 0 | } |
809 | |
|
810 | 0 | if (terminate) { |
811 | | // Terminate the current RRULE |
812 | 0 | terminate_rrule(zone); |
813 | |
|
814 | 0 | if (rdate) { |
815 | 0 | if (zone->rdate_comp) { |
816 | | // Add an RDATE to the previous component |
817 | | // Remove the current RRULE component |
818 | 0 | struct icaldatetimeperiodtype dtp = |
819 | 0 | {zone->time, |
820 | 0 | ICALPERIODTYPE_INITIALIZER}; |
821 | |
|
822 | 0 | icalprop = icalproperty_new_rdate(dtp); |
823 | 0 | icalcomponent_add_property(zone->rdate_comp, icalprop); |
824 | |
|
825 | 0 | icalcomponent_remove_component(tz_comp, zone->rrule_comp); |
826 | 0 | icalcomponent_free(zone->rrule_comp); |
827 | 0 | } else { |
828 | 0 | zone->rdate_comp = zone->rrule_comp; |
829 | 0 | } |
830 | 0 | } |
831 | 0 | zone->rrule_comp = NULL; |
832 | 0 | } |
833 | 0 | } |
834 | |
|
835 | 0 | zone->prev_time = zone->time; |
836 | 0 | zone->time = icaltime; |
837 | 0 | zone->gmtoff_from = types[prev_idx].gmtoff; |
838 | 0 | zone->gmtoff_to = types[idx].gmtoff; |
839 | |
|
840 | 0 | if (!zone->rrule_comp) { |
841 | 0 | zone->name = types[idx].zname; |
842 | 0 | zone->prev_time = icaltime; |
843 | | |
844 | | // Create a recurrence rule for the current set of changes |
845 | 0 | icalrecurrencetype_clear(&zone->recur); |
846 | 0 | zone->recur.freq = ICAL_YEARLY_RECURRENCE; |
847 | 0 | zone->recur.by_day[0] = by_day; |
848 | 0 | zone->recur.by_month[0] = icaltime.month; |
849 | 0 | zone->recur.by_month_day[0] = icaltime.day; |
850 | 0 | zone->num_monthdays = 1; |
851 | 0 | zone->rrule_prop = icalproperty_new_rrule(zone->recur); |
852 | |
|
853 | 0 | zone->rrule_comp = |
854 | 0 | icalcomponent_vanew(zone->kind, |
855 | 0 | icalproperty_new_tzname(zone->name), |
856 | 0 | icalproperty_new_tzoffsetfrom(zone->gmtoff_from), |
857 | 0 | icalproperty_new_tzoffsetto(zone->gmtoff_to), |
858 | 0 | icalproperty_new_dtstart(zone->time), |
859 | 0 | zone->rrule_prop, |
860 | 0 | (void *)0); |
861 | 0 | icalcomponent_add_component(tz_comp, zone->rrule_comp); |
862 | 0 | } |
863 | 0 | } |
864 | |
|
865 | 0 | if (tzstr) { |
866 | | // Replace the last recurrence rules with those from the TZ string |
867 | 0 | icalproperty_set_rrule(standard.rrule_prop, standard.final_recur); |
868 | 0 | icalproperty_set_rrule(daylight.rrule_prop, daylight.final_recur); |
869 | 0 | } else { |
870 | | // Terminate the last recurrence rules |
871 | 0 | if (standard.rrule_comp) { |
872 | 0 | terminate_rrule(&standard); |
873 | 0 | } |
874 | 0 | if (daylight.rrule_comp) { |
875 | 0 | terminate_rrule(&daylight); |
876 | 0 | } |
877 | 0 | } |
878 | |
|
879 | 0 | error: |
880 | 0 | if (f) |
881 | 0 | fclose(f); |
882 | |
|
883 | 0 | if (full_path) |
884 | 0 | icalmemory_free_buffer(full_path); |
885 | |
|
886 | 0 | if (transitions) |
887 | 0 | icalmemory_free_buffer(transitions); |
888 | |
|
889 | 0 | if (r_trans) |
890 | 0 | icalmemory_free_buffer(r_trans); |
891 | |
|
892 | 0 | if (trans_idx) |
893 | 0 | icalmemory_free_buffer(trans_idx); |
894 | |
|
895 | 0 | if (types) { |
896 | 0 | for (i = 0; i < num_types; i++) { |
897 | 0 | if (types[i].zname) { |
898 | 0 | icalmemory_free_buffer(types[i].zname); |
899 | 0 | } |
900 | 0 | } |
901 | 0 | icalmemory_free_buffer(types); |
902 | 0 | } |
903 | |
|
904 | 0 | if (znames) |
905 | 0 | icalmemory_free_buffer(znames); |
906 | |
|
907 | 0 | if (leaps) |
908 | 0 | icalmemory_free_buffer(leaps); |
909 | |
|
910 | 0 | if (tzid) |
911 | 0 | icalmemory_free_buffer(tzid); |
912 | |
|
913 | 0 | return tz_comp; |
914 | 0 | } |