source: src/router/php7/ext/date/lib/parse_tz.c @ 31879

Last change on this file since 31879 was 31879, checked in by brainslayer, 5 weeks ago

update php

File size: 27.2 KB
Line 
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2015 Derick Rethans
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "timelib.h"
26
27#ifdef HAVE_SYSTEM_TZDATA
28#include <sys/mman.h>
29#include <sys/stat.h>
30#include <limits.h>
31#include <fcntl.h>
32#include <unistd.h>
33
34#include "php_scandir.h"
35#endif
36
37#include <stdio.h>
38
39#ifdef HAVE_LOCALE_H
40#include <locale.h>
41#endif
42
43#ifdef HAVE_STRING_H
44#include <string.h>
45#else
46#include <strings.h>
47#endif
48
49#ifndef HAVE_SYSTEM_TZDATA
50#define TIMELIB_SUPPORTS_V2DATA
51#include "timezonedb.h"
52#endif
53
54#include <ctype.h>
55
56#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
57# if defined(__LITTLE_ENDIAN__)
58#  undef WORDS_BIGENDIAN
59# else
60#  if defined(__BIG_ENDIAN__)
61#   define WORDS_BIGENDIAN
62#  endif
63# endif
64#endif
65
66#ifdef WORDS_BIGENDIAN
67#define timelib_conv_int(l) (l)
68#else
69#define timelib_conv_int(l) ((l & 0x000000ff) << 24) + ((l & 0x0000ff00) << 8) + ((l & 0x00ff0000) >> 8) + ((l & 0xff000000) >> 24)
70#endif
71
72static int read_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
73{
74        uint32_t version;
75
76        if (memcmp(*tzf, "TZif", 4) == 0) {
77                *tzf += 20;
78                return 0;
79        }
80
81        /* read ID */
82        version = (*tzf)[3] - '0';
83        *tzf += 4;
84
85        /* read BC flag */
86        tz->bc = (**tzf == '\1');
87        *tzf += 1;
88
89        /* read country code */
90        memcpy(tz->location.country_code, *tzf, 2);
91        tz->location.country_code[2] = '\0';
92        *tzf += 2;
93
94        /* skip rest of preamble */
95        *tzf += 13;
96
97        return version;
98}
99
100static void read_header(const unsigned char **tzf, timelib_tzinfo *tz)
101{
102        uint32_t buffer[6];
103
104        memcpy(&buffer, *tzf, sizeof(buffer));
105        tz->bit32.ttisgmtcnt = timelib_conv_int(buffer[0]);
106        tz->bit32.ttisstdcnt = timelib_conv_int(buffer[1]);
107        tz->bit32.leapcnt    = timelib_conv_int(buffer[2]);
108        tz->bit32.timecnt    = timelib_conv_int(buffer[3]);
109        tz->bit32.typecnt    = timelib_conv_int(buffer[4]);
110        tz->bit32.charcnt    = timelib_conv_int(buffer[5]);
111        *tzf += sizeof(buffer);
112}
113
114static void skip_64bit_transistions(const unsigned char **tzf, timelib_tzinfo *tz)
115{
116        if (tz->bit64.timecnt) {
117                *tzf += (sizeof(int64_t) * tz->bit64.timecnt);
118                *tzf += (sizeof(unsigned char) * tz->bit64.timecnt);
119        }
120}
121
122static void read_transistions(const unsigned char **tzf, timelib_tzinfo *tz)
123{
124        int32_t *buffer = NULL;
125        uint32_t i;
126        unsigned char *cbuffer = NULL;
127
128        if (tz->bit32.timecnt) {
129                buffer = (int32_t*) timelib_malloc(tz->bit32.timecnt * sizeof(int32_t));
130                if (!buffer) {
131                        return;
132                }
133                memcpy(buffer, *tzf, sizeof(int32_t) * tz->bit32.timecnt);
134                *tzf += (sizeof(int32_t) * tz->bit32.timecnt);
135                for (i = 0; i < tz->bit32.timecnt; i++) {
136                        buffer[i] = timelib_conv_int(buffer[i]);
137                }
138
139                cbuffer = (unsigned char*) timelib_malloc(tz->bit32.timecnt * sizeof(unsigned char));
140                if (!cbuffer) {
141                        timelib_free(buffer);
142                        return;
143                }
144                memcpy(cbuffer, *tzf, sizeof(unsigned char) * tz->bit32.timecnt);
145                *tzf += sizeof(unsigned char) * tz->bit32.timecnt;
146        }
147
148        tz->trans = buffer;
149        tz->trans_idx = cbuffer;
150}
151
152static void skip_64bit_types(const unsigned char **tzf, timelib_tzinfo *tz)
153{
154        *tzf += sizeof(unsigned char) * 6 * tz->bit64.typecnt;
155        *tzf += sizeof(char) * tz->bit64.charcnt;
156        if (tz->bit64.leapcnt) {
157                *tzf += sizeof(int64_t) * tz->bit64.leapcnt * 2;
158        }
159        if (tz->bit64.ttisstdcnt) {
160                *tzf += sizeof(unsigned char) * tz->bit64.ttisstdcnt;
161        }
162        if (tz->bit64.ttisgmtcnt) {
163                *tzf += sizeof(unsigned char) * tz->bit64.ttisgmtcnt;
164        }
165}
166
167static void read_types(const unsigned char **tzf, timelib_tzinfo *tz)
168{
169        unsigned char *buffer;
170        int32_t *leap_buffer;
171        unsigned int i, j;
172
173        buffer = (unsigned char*) timelib_malloc(tz->bit32.typecnt * sizeof(unsigned char) * 6);
174        if (!buffer) {
175                return;
176        }
177        memcpy(buffer, *tzf, sizeof(unsigned char) * 6 * tz->bit32.typecnt);
178        *tzf += sizeof(unsigned char) * 6 * tz->bit32.typecnt;
179
180        tz->type = (ttinfo*) timelib_malloc(tz->bit32.typecnt * sizeof(struct ttinfo));
181        if (!tz->type) {
182                timelib_free(buffer);
183                return;
184        }
185
186        for (i = 0; i < tz->bit32.typecnt; i++) {
187                j = i * 6;
188                tz->type[i].offset = (buffer[j] * 16777216) + (buffer[j + 1] * 65536) + (buffer[j + 2] * 256) + buffer[j + 3];
189                tz->type[i].isdst = buffer[j + 4];
190                tz->type[i].abbr_idx = buffer[j + 5];
191        }
192        timelib_free(buffer);
193
194        tz->timezone_abbr = (char*) timelib_malloc(tz->bit32.charcnt);
195        if (!tz->timezone_abbr) {
196                return;
197        }
198        memcpy(tz->timezone_abbr, *tzf, sizeof(char) * tz->bit32.charcnt);
199        *tzf += sizeof(char) * tz->bit32.charcnt;
200
201        if (tz->bit32.leapcnt) {
202                leap_buffer = (int32_t *) timelib_malloc(tz->bit32.leapcnt * 2 * sizeof(int32_t));
203                if (!leap_buffer) {
204                        return;
205                }
206                memcpy(leap_buffer, *tzf, sizeof(int32_t) * tz->bit32.leapcnt * 2);
207                *tzf += sizeof(int32_t) * tz->bit32.leapcnt * 2;
208
209                tz->leap_times = (tlinfo*) timelib_malloc(tz->bit32.leapcnt * sizeof(tlinfo));
210                if (!tz->leap_times) {
211                        timelib_free(leap_buffer);
212                        return;
213                }
214                for (i = 0; i < tz->bit32.leapcnt; i++) {
215                        tz->leap_times[i].trans = timelib_conv_int(leap_buffer[i * 2]);
216                        tz->leap_times[i].offset = timelib_conv_int(leap_buffer[i * 2 + 1]);
217                }
218                timelib_free(leap_buffer);
219        }
220
221        if (tz->bit32.ttisstdcnt) {
222                buffer = (unsigned char*) timelib_malloc(tz->bit32.ttisstdcnt * sizeof(unsigned char));
223                if (!buffer) {
224                        return;
225                }
226                memcpy(buffer, *tzf, sizeof(unsigned char) * tz->bit32.ttisstdcnt);
227                *tzf += sizeof(unsigned char) * tz->bit32.ttisstdcnt;
228
229                for (i = 0; i < tz->bit32.ttisstdcnt; i++) {
230                        tz->type[i].isstdcnt = buffer[i];
231                }
232                timelib_free(buffer);
233        }
234
235        if (tz->bit32.ttisgmtcnt) {
236                buffer = (unsigned char*) timelib_malloc(tz->bit32.ttisgmtcnt * sizeof(unsigned char));
237                if (!buffer) {
238                        return;
239                }
240                memcpy(buffer, *tzf, sizeof(unsigned char) * tz->bit32.ttisgmtcnt);
241                *tzf += sizeof(unsigned char) * tz->bit32.ttisgmtcnt;
242
243                for (i = 0; i < tz->bit32.ttisgmtcnt; i++) {
244                        tz->type[i].isgmtcnt = buffer[i];
245                }
246                timelib_free(buffer);
247        }
248}
249
250static void skip_posix_string(const unsigned char **tzf, timelib_tzinfo *tz)
251{
252        int n_count = 0;
253
254        do {
255                if (*tzf[0] == '\n') {
256                        n_count++;
257                }
258                (*tzf)++;
259        } while (n_count < 2);
260}
261
262static void read_location(const unsigned char **tzf, timelib_tzinfo *tz)
263{
264        uint32_t buffer[3];
265        uint32_t comments_len;
266
267        memcpy(&buffer, *tzf, sizeof(buffer));
268        tz->location.latitude = timelib_conv_int(buffer[0]);
269        tz->location.latitude = (tz->location.latitude / 100000) - 90;
270        tz->location.longitude = timelib_conv_int(buffer[1]);
271        tz->location.longitude = (tz->location.longitude / 100000) - 180;
272        comments_len = timelib_conv_int(buffer[2]);
273        *tzf += sizeof(buffer);
274
275        tz->location.comments = timelib_malloc(comments_len + 1);
276        memcpy(tz->location.comments, *tzf, comments_len);
277        tz->location.comments[comments_len] = '\0';
278        *tzf += comments_len;
279}
280
281void timelib_dump_tzinfo(timelib_tzinfo *tz)
282{
283        uint32_t i;
284
285        printf("Country Code:      %s\n", tz->location.country_code);
286        printf("Geo Location:      %f,%f\n", tz->location.latitude, tz->location.longitude);
287        printf("Comments:\n%s\n",          tz->location.comments);
288        printf("BC:                %s\n",  tz->bc ? "" : "yes");
289        printf("UTC/Local count:   " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit32.ttisgmtcnt);
290        printf("Std/Wall count:    " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit32.ttisstdcnt);
291        printf("Leap.sec. count:   " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit32.leapcnt);
292        printf("Trans. count:      " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit32.timecnt);
293        printf("Local types count: " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit32.typecnt);
294        printf("Zone Abbr. count:  " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit32.charcnt);
295
296        printf ("%8s (%12s) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n",
297                "", "", 0,
298                (long int) tz->type[0].offset,
299                tz->type[0].isdst,
300                tz->type[0].abbr_idx,
301                &tz->timezone_abbr[tz->type[0].abbr_idx],
302                tz->type[0].isstdcnt,
303                tz->type[0].isgmtcnt
304                );
305        for (i = 0; i < tz->bit32.timecnt; i++) {
306                printf ("%08X (%12d) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n",
307                        tz->trans[i], tz->trans[i], tz->trans_idx[i],
308                        (long int) tz->type[tz->trans_idx[i]].offset,
309                        tz->type[tz->trans_idx[i]].isdst,
310                        tz->type[tz->trans_idx[i]].abbr_idx,
311                        &tz->timezone_abbr[tz->type[tz->trans_idx[i]].abbr_idx],
312                        tz->type[tz->trans_idx[i]].isstdcnt,
313                        tz->type[tz->trans_idx[i]].isgmtcnt
314                        );
315        }
316        for (i = 0; i < tz->bit32.leapcnt; i++) {
317                printf ("%08X (%12ld) = %d\n",
318                        tz->leap_times[i].trans,
319                        (long) tz->leap_times[i].trans,
320                        tz->leap_times[i].offset);
321        }
322}
323
324#ifdef HAVE_SYSTEM_TZDATA
325
326#ifdef HAVE_SYSTEM_TZDATA_PREFIX
327#define ZONEINFO_PREFIX HAVE_SYSTEM_TZDATA_PREFIX
328#else
329#define ZONEINFO_PREFIX "/usr/share/zoneinfo"
330#endif
331
332/* System timezone database pointer. */
333static const timelib_tzdb *timezonedb_system;
334
335/* Hash table entry for the cache of the zone.tab mapping table. */
336struct location_info {
337        char code[2];
338        double latitude, longitude;
339        char name[64];
340        char *comment;
341        struct location_info *next;
342};
343
344/* Cache of zone.tab. */
345static struct location_info **system_location_table;
346
347/* Size of the zone.tab hash table; a random-ish prime big enough to
348 * prevent too many collisions. */
349#define LOCINFO_HASH_SIZE (1021)
350
351/* Compute a case insensitive hash of str */
352static uint32_t tz_hash(const char *str)
353{
354    const unsigned char *p = (const unsigned char *)str;
355    uint32_t hash = 5381;
356    int c;
357
358    while ((c = tolower(*p++)) != '\0') {
359        hash = (hash << 5) ^ hash ^ c;
360    }
361
362    return hash % LOCINFO_HASH_SIZE;
363}
364
365/* Parse an ISO-6709 date as used in zone.tab. Returns end of the
366 * parsed string on success, or NULL on parse error.  On success,
367 * writes the parsed number to *result. */
368static char *parse_iso6709(char *p, double *result)
369{
370    double v, sign;
371    char *pend;
372    size_t len;
373
374    if (*p == '+')
375        sign = 1.0;
376    else if (*p == '-')
377        sign = -1.0;
378    else
379        return NULL;
380
381    p++;
382    for (pend = p; *pend >= '0' && *pend <= '9'; pend++)
383        ;;
384
385    /* Annoying encoding used by zone.tab has no decimal point, so use
386     * the length to determine the format:
387     *
388     * 4 = DDMM
389     * 5 = DDDMM
390     * 6 = DDMMSS
391     * 7 = DDDMMSS
392     */
393    len = pend - p;
394    if (len < 4 || len > 7) {
395        return NULL;
396    }
397
398    /* p => [D]DD */
399    v = (p[0] - '0') * 10.0 + (p[1] - '0');
400    p += 2;
401    if (len == 5 || len == 7)
402        v = v * 10.0 + (*p++ - '0');
403    /* p => MM[SS] */
404    v += (10.0 * (p[0] - '0')
405          + p[1] - '0') / 60.0;
406    p += 2;
407    /* p => [SS] */
408    if (len > 5) {
409        v += (10.0 * (p[0] - '0')
410              + p[1] - '0') / 3600.0;
411        p += 2;
412    }
413
414    /* Round to five decimal place, not because it's a good idea,
415     * but, because the builtin data uses rounded data, so, match
416     * that. */
417    *result = trunc(v * sign * 100000.0) / 100000.0;
418
419    return p;
420}
421
422/* This function parses the zone.tab file to build up the mapping of
423 * timezone to country code and geographic location, and returns a
424 * hash table.  The hash table is indexed by the function:
425 *
426 *   tz_hash(timezone-name)
427 */
428static struct location_info **create_location_table(void)
429{
430    struct location_info **li, *i;
431    char zone_tab[PATH_MAX];
432    char line[512];
433    FILE *fp;
434
435    strncpy(zone_tab, ZONEINFO_PREFIX "/zone.tab", sizeof zone_tab);
436
437    fp = fopen(zone_tab, "r");
438    if (!fp) {
439        return NULL;
440    }
441
442    li = calloc(LOCINFO_HASH_SIZE, sizeof *li);
443
444    while (fgets(line, sizeof line, fp)) {
445        char *p = line, *code, *name, *comment;
446        uint32_t hash;
447        double latitude, longitude;
448
449        while (isspace(*p))
450            p++;
451
452        if (*p == '#' || *p == '\0' || *p == '\n')
453            continue;
454       
455        if (!isalpha(p[0]) || !isalpha(p[1]) || p[2] != '\t')
456            continue;
457       
458        /* code => AA */
459        code = p;
460        p[2] = 0;
461        p += 3;
462
463        /* coords => [+-][D]DDMM[SS][+-][D]DDMM[SS] */
464        p = parse_iso6709(p, &latitude);
465        if (!p) {
466            continue;
467        }
468        p = parse_iso6709(p, &longitude);
469        if (!p) {
470            continue;
471        }
472
473        if (!p || *p != '\t') {
474            continue;
475        }
476
477        /* name = string */
478        name = ++p;
479        while (*p != '\t' && *p && *p != '\n')
480            p++;
481
482        *p++ = '\0';
483
484        /* comment = string */
485        comment = p;
486        while (*p != '\t' && *p && *p != '\n')
487            p++;
488
489        if (*p == '\n' || *p == '\t')
490            *p = '\0';
491       
492        hash = tz_hash(name);
493        i = malloc(sizeof *i);
494        memcpy(i->code, code, 2);
495        strncpy(i->name, name, sizeof i->name);
496        i->comment = strdup(comment);
497        i->longitude = longitude;
498        i->latitude = latitude;
499        i->next = li[hash];
500        li[hash] = i;
501        /* printf("%s [%u, %f, %f]\n", name, hash, latitude, longitude); */
502    }
503
504    fclose(fp);
505
506    return li;
507}
508
509/* Return location info from hash table, using given timezone name.
510 * Returns NULL if the name could not be found. */
511const struct location_info *find_zone_info(struct location_info **li,
512                                           const char *name)
513{
514    uint32_t hash = tz_hash(name);
515    const struct location_info *l;
516
517    if (!li) {
518        return NULL;
519    }
520
521    for (l = li[hash]; l; l = l->next) {
522        if (strcasecmp(l->name, name) == 0)
523            return l;
524    }
525
526    return NULL;
527}   
528
529/* Filter out some non-tzdata files and the posix/right databases, if
530 * present. */
531static int index_filter(const struct dirent *ent)
532{
533        return strcmp(ent->d_name, ".") != 0
534                && strcmp(ent->d_name, "..") != 0
535                && strcmp(ent->d_name, "posix") != 0
536                && strcmp(ent->d_name, "posixrules") != 0
537                && strcmp(ent->d_name, "right") != 0
538                && strstr(ent->d_name, ".list") == NULL
539                && strstr(ent->d_name, ".tab") == NULL;
540}
541
542static int sysdbcmp(const void *first, const void *second)
543{
544        const timelib_tzdb_index_entry *alpha = first, *beta = second;
545
546        return strcasecmp(alpha->id, beta->id);
547}
548
549
550/* Create the zone identifier index by trawling the filesystem. */
551static void create_zone_index(timelib_tzdb *db)
552{
553        size_t dirstack_size,  dirstack_top;
554        size_t index_size, index_next;
555        timelib_tzdb_index_entry *db_index;
556        char **dirstack;
557
558        /* LIFO stack to hold directory entries to scan; each slot is a
559         * directory name relative to the zoneinfo prefix. */
560        dirstack_size = 32;
561        dirstack = malloc(dirstack_size * sizeof *dirstack);
562        dirstack_top = 1;
563        dirstack[0] = strdup("");
564       
565        /* Index array. */
566        index_size = 64;
567        db_index = malloc(index_size * sizeof *db_index);
568        index_next = 0;
569
570        do {
571                struct dirent **ents;
572                char name[PATH_MAX], *top;
573                int count;
574
575                /* Pop the top stack entry, and iterate through its contents. */
576                top = dirstack[--dirstack_top];
577                snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s", top);
578
579                count = php_scandir(name, &ents, index_filter, php_alphasort);
580
581                while (count > 0) {
582                        struct stat st;
583                        const char *leaf = ents[count - 1]->d_name;
584
585                        snprintf(name, sizeof name, ZONEINFO_PREFIX "/%s/%s",
586                                 top, leaf);
587                       
588                        if (strlen(name) && stat(name, &st) == 0) {
589                                /* Name, relative to the zoneinfo prefix. */
590                                const char *root = top;
591
592                                if (root[0] == '/') root++;
593
594                                snprintf(name, sizeof name, "%s%s%s", root,
595                                         *root ? "/": "", leaf);
596
597                                if (S_ISDIR(st.st_mode)) {
598                                        if (dirstack_top == dirstack_size) {
599                                                dirstack_size *= 2;
600                                                dirstack = realloc(dirstack,
601                                                                   dirstack_size * sizeof *dirstack);
602                                        }
603                                        dirstack[dirstack_top++] = strdup(name);
604                                }
605                                else {
606                                        if (index_next == index_size) {
607                                                index_size *= 2;
608                                                db_index = realloc(db_index,
609                                                                   index_size * sizeof *db_index);
610                                        }
611
612                                        db_index[index_next++].id = strdup(name);
613                                }
614                        }
615
616                        free(ents[--count]);
617                }
618               
619                if (count != -1) free(ents);
620                free(top);
621        } while (dirstack_top);
622
623        qsort(db_index, index_next, sizeof *db_index, sysdbcmp);
624
625        db->index = db_index;
626        db->index_size = index_next;
627
628        free(dirstack);
629}
630
631#define FAKE_HEADER "1234\0??\1??"
632#define FAKE_UTC_POS (7 - 4)
633
634/* Create a fake data segment for database 'sysdb'. */
635static void fake_data_segment(timelib_tzdb *sysdb,
636                              struct location_info **info)
637{
638        size_t n;
639        char *data, *p;
640       
641        data = malloc(3 * sysdb->index_size + 7);
642
643        p = mempcpy(data, FAKE_HEADER, sizeof(FAKE_HEADER) - 1);
644
645        for (n = 0; n < sysdb->index_size; n++) {
646                const struct location_info *li;
647                timelib_tzdb_index_entry *ent;
648
649                ent = (timelib_tzdb_index_entry *)&sysdb->index[n];
650
651                /* Lookup the timezone name in the hash table. */
652                if (strcmp(ent->id, "UTC") == 0) {
653                        ent->pos = FAKE_UTC_POS;
654                        continue;
655                }
656
657                li = find_zone_info(info, ent->id);
658                if (li) {
659                        /* If found, append the BC byte and the
660                         * country code; set the position for this
661                         * section of timezone data.  */
662                        ent->pos = (p - data) - 4;
663                        *p++ = '\1';
664                        *p++ = li->code[0];
665                        *p++ = li->code[1];
666                }
667                else {
668                        /* If not found, the timezone data can
669                         * point at the header. */
670                        ent->pos = 0;
671                }
672        }
673       
674        sysdb->data = (unsigned char *)data;
675}
676
677/* Returns true if the passed-in stat structure describes a
678 * probably-valid timezone file. */
679static int is_valid_tzfile(const struct stat *st, int fd)
680{
681        if (fd) {
682                char buf[20];
683                if (read(fd, buf, 20)!=20) {
684                        return 0;
685                }
686                lseek(fd, SEEK_SET, 0);
687                if (memcmp(buf, "TZif", 4)) {
688                        return 0;
689                }
690        }
691        return S_ISREG(st->st_mode) && st->st_size > 20;
692}
693
694/* To allow timezone names to be used case-insensitively, find the
695 * canonical name for this timezone, if possible. */
696static const char *canonical_tzname(const char *timezone)
697{
698    if (timezonedb_system) {
699        timelib_tzdb_index_entry *ent, lookup;
700
701        lookup.id = (char *)timezone;
702
703        ent = bsearch(&lookup, timezonedb_system->index,
704                      timezonedb_system->index_size, sizeof lookup,
705                      sysdbcmp);
706        if (ent) {
707            return ent->id;
708        }
709    }
710
711    return timezone;
712}
713
714/* Return the mmap()ed tzfile if found, else NULL.  On success, the
715 * length of the mapped data is placed in *length. */
716static char *map_tzfile(const char *timezone, size_t *length)
717{
718        char fname[PATH_MAX];
719        struct stat st;
720        char *p;
721        int fd;
722       
723        if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
724                return NULL;
725        }
726
727        snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", canonical_tzname(timezone));
728
729        fd = open(fname, O_RDONLY);
730        if (fd == -1) {
731                return NULL;
732        } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st, fd)) {
733                close(fd);
734                return NULL;
735        }
736
737        *length = st.st_size;
738        p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
739        close(fd);
740       
741        return p != MAP_FAILED ? p : NULL;
742}
743
744#endif
745
746static int inmem_seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
747{
748        int left = 0, right = tzdb->index_size - 1;
749#ifdef HAVE_SETLOCALE
750        char *cur_locale = NULL, *tmp;
751
752        tmp = setlocale(LC_CTYPE, NULL);
753        if (tmp) {
754                cur_locale = timelib_strdup(tmp);
755        }
756        setlocale(LC_CTYPE, "C");
757#endif
758
759        do {
760                int mid = ((unsigned)left + right) >> 1;
761                int cmp = strcasecmp(timezone, tzdb->index[mid].id);
762
763                if (cmp < 0) {
764                        right = mid - 1;
765                } else if (cmp > 0) {
766                        left = mid + 1;
767                } else { /* (cmp == 0) */
768                        (*tzf) = &(tzdb->data[tzdb->index[mid].pos]);
769#ifdef HAVE_SETLOCALE
770                        setlocale(LC_CTYPE, cur_locale);
771                        if (cur_locale) timelib_free(cur_locale);
772#endif
773                        return 1;
774                }
775
776        } while (left <= right);
777
778#ifdef HAVE_SETLOCALE
779        setlocale(LC_CTYPE, cur_locale);
780        if (cur_locale) timelib_free(cur_locale);
781#endif
782        return 0;
783}
784
785static int seek_to_tz_position(const unsigned char **tzf, char *timezone,
786                               char **map, size_t *maplen,
787                               const timelib_tzdb *tzdb)
788{
789#ifdef HAVE_SYSTEM_TZDATA
790        if (tzdb == timezonedb_system) {
791                char *orig;
792
793                orig = map_tzfile(timezone, maplen);
794                if (orig == NULL) {
795                        return 0;
796                }
797
798                (*tzf) = (unsigned char *)orig;
799                *map = orig;
800        return 1;
801        }
802        else
803#endif
804        {
805                return inmem_seek_to_tz_position(tzf, timezone, tzdb);
806        }
807}
808
809const timelib_tzdb *timelib_builtin_db(void)
810{
811#ifdef HAVE_SYSTEM_TZDATA
812        if (timezonedb_system == NULL) {
813                timelib_tzdb *tmp = malloc(sizeof *tmp);
814
815                tmp->version = "0.system";
816                tmp->data = NULL;
817                create_zone_index(tmp);
818                system_location_table = create_location_table();
819                fake_data_segment(tmp, system_location_table);
820                timezonedb_system = tmp;
821        }
822
823        return timezonedb_system;
824#else
825        return &timezonedb_builtin;
826#endif
827}
828
829const timelib_tzdb_index_entry *timelib_timezone_builtin_identifiers_list(int *count)
830{
831#ifdef HAVE_SYSTEM_TZDATA
832        *count = timezonedb_system->index_size;
833        return timezonedb_system->index;
834#else
835        *count = sizeof(timezonedb_idx_builtin) / sizeof(*timezonedb_idx_builtin);
836        return timezonedb_idx_builtin;
837#endif
838}
839
840int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb)
841{
842        const unsigned char *tzf;
843
844#ifdef HAVE_SYSTEM_TZDATA
845        if (tzdb == timezonedb_system) {
846                char fname[PATH_MAX];
847                struct stat st;
848
849                if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
850                        return 0;
851                }
852
853                if (system_location_table) {
854                        if (find_zone_info(system_location_table, timezone) != NULL) {
855                                /* found in cache */
856                                return 1;
857                        }
858                }
859
860                snprintf(fname, sizeof fname, ZONEINFO_PREFIX "/%s", canonical_tzname(timezone));
861
862                return stat(fname, &st) == 0 && is_valid_tzfile(&st, 0);
863        }
864#endif
865
866        return (inmem_seek_to_tz_position(&tzf, timezone, tzdb));
867}
868
869static void skip_64bit_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
870{
871        *tzf += 20;
872}
873
874static void read_64bit_header(const unsigned char **tzf, timelib_tzinfo *tz)
875{
876        uint32_t buffer[6];
877
878        memcpy(&buffer, *tzf, sizeof(buffer));
879        tz->bit64.ttisgmtcnt = timelib_conv_int(buffer[0]);
880        tz->bit64.ttisstdcnt = timelib_conv_int(buffer[1]);
881        tz->bit64.leapcnt    = timelib_conv_int(buffer[2]);
882        tz->bit64.timecnt    = timelib_conv_int(buffer[3]);
883        tz->bit64.typecnt    = timelib_conv_int(buffer[4]);
884        tz->bit64.charcnt    = timelib_conv_int(buffer[5]);
885        *tzf += sizeof(buffer);
886}
887
888timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb)
889{
890        const unsigned char *tzf;
891        char *memmap = NULL;
892        size_t maplen;
893        timelib_tzinfo *tmp;
894        int version;
895
896        if (seek_to_tz_position(&tzf, timezone, &memmap, &maplen, tzdb)) {
897                tmp = timelib_tzinfo_ctor(timezone);
898
899                version = read_preamble(&tzf, tmp);
900                read_header(&tzf, tmp);
901                read_transistions(&tzf, tmp);
902                read_types(&tzf, tmp);
903
904#ifdef HAVE_SYSTEM_TZDATA
905                if (memmap) {
906                        const struct location_info *li;
907
908                        /* TZif-style - grok the location info from the system database,
909                         * if possible. */
910
911                        if ((li = find_zone_info(system_location_table, timezone)) != NULL) {
912                                tmp->location.comments = timelib_strdup(li->comment);
913                                strncpy(tmp->location.country_code, li->code, 2);
914                                tmp->location.longitude = li->longitude;
915                                tmp->location.latitude = li->latitude;
916                                tmp->bc = 1;
917                        }
918                        else {
919                                strcpy(tmp->location.country_code, "??");
920                                tmp->bc = 0;
921                                tmp->location.comments = timelib_strdup("");
922                        }
923
924                        /* Now done with the mmap segment - discard it. */
925                        munmap(memmap, maplen);
926                } else
927#endif
928                {
929                        /* PHP-style - use the embedded info. */
930                        if (version == 2) {
931                                skip_64bit_preamble(&tzf, tmp);
932                                read_64bit_header(&tzf, tmp);
933                                skip_64bit_transistions(&tzf, tmp);
934                                skip_64bit_types(&tzf, tmp);
935                                skip_posix_string(&tzf, tmp);
936                        }
937                        read_location(&tzf, tmp);
938                }
939        } else {
940                tmp = NULL;
941        }
942
943        return tmp;
944}
945
946static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
947{
948        uint32_t i;
949
950        /* If there is no transition time, we pick the first one, if that doesn't
951         * exist we return NULL */
952        if (!tz->bit32.timecnt || !tz->trans) {
953                *transition_time = 0;
954                if (tz->bit32.typecnt == 1) {
955                        return &(tz->type[0]);
956                }
957                return NULL;
958        }
959
960        /* If the TS is lower than the first transition time, then we scan over
961         * all the transition times to find the first non-DST one, or the first
962         * one in case there are only DST entries. Not sure which smartass came up
963         * with this idea in the first though :) */
964        if (ts < tz->trans[0]) {
965                uint32_t j;
966
967                *transition_time = 0;
968                j = 0;
969                while (j < tz->bit32.timecnt && tz->type[tz->trans_idx[j]].isdst) {
970                        ++j;
971                }
972                if (j == tz->bit32.timecnt) {
973                        j = 0;
974                }
975                return &(tz->type[tz->trans_idx[j]]);
976        }
977
978        /* In all other cases we loop through the available transtion times to find
979         * the correct entry */
980        for (i = 0; i < tz->bit32.timecnt; i++) {
981                if (ts < tz->trans[i]) {
982                        *transition_time = tz->trans[i - 1];
983                        return &(tz->type[tz->trans_idx[i - 1]]);
984                }
985        }
986        *transition_time = tz->trans[tz->bit32.timecnt - 1];
987        return &(tz->type[tz->trans_idx[tz->bit32.timecnt - 1]]);
988}
989
990static tlinfo* fetch_leaptime_offset(timelib_tzinfo *tz, timelib_sll ts)
991{
992        int i;
993
994        if (!tz->bit32.leapcnt || !tz->leap_times) {
995                return NULL;
996        }
997
998        for (i = tz->bit32.leapcnt - 1; i > 0; i--) {
999                if (ts > tz->leap_times[i].trans) {
1000                        return &(tz->leap_times[i]);
1001                }
1002        }
1003        return NULL;
1004}
1005
1006int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz)
1007{
1008        ttinfo *to;
1009        timelib_sll dummy;
1010
1011        if ((to = fetch_timezone_offset(tz, ts, &dummy))) {
1012                return to->isdst;
1013        }
1014        return -1;
1015}
1016
1017timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz)
1018{
1019        ttinfo *to;
1020        tlinfo *tl;
1021        int32_t offset = 0, leap_secs = 0;
1022        char *abbr;
1023        timelib_time_offset *tmp = timelib_time_offset_ctor();
1024        timelib_sll                transistion_time;
1025
1026        if ((to = fetch_timezone_offset(tz, ts, &transistion_time))) {
1027                offset = to->offset;
1028                abbr = &(tz->timezone_abbr[to->abbr_idx]);
1029                tmp->is_dst = to->isdst;
1030                tmp->transistion_time = transistion_time;
1031        } else {
1032                offset = 0;
1033                abbr = tz->timezone_abbr;
1034                tmp->is_dst = 0;
1035                tmp->transistion_time = 0;
1036        }
1037
1038        if ((tl = fetch_leaptime_offset(tz, ts))) {
1039                leap_secs = -tl->offset;
1040        }
1041
1042        tmp->offset = offset;
1043        tmp->leap_secs = leap_secs;
1044        tmp->abbr = abbr ? timelib_strdup(abbr) : timelib_strdup("GMT");
1045
1046        return tmp;
1047}
1048
1049timelib_sll timelib_get_current_offset(timelib_time *t)
1050{
1051        timelib_time_offset *gmt_offset;
1052        timelib_sll retval;
1053
1054        switch (t->zone_type) {
1055                case TIMELIB_ZONETYPE_ABBR:
1056                case TIMELIB_ZONETYPE_OFFSET:
1057                        return (t->z + t->dst) * -60;
1058
1059                case TIMELIB_ZONETYPE_ID:
1060                        gmt_offset = timelib_get_time_zone_info(t->sse, t->tz_info);
1061                        retval = gmt_offset->offset;
1062                        timelib_time_offset_dtor(gmt_offset);
1063                        return retval;
1064
1065                default:
1066                        return 0;
1067        }
1068}
Note: See TracBrowser for help on using the repository browser.