Coverage Report

Created: 2024-09-30 06:41

/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
}