From c7e767bfe1d218aaf845686f9811195cecc7be2a Mon Sep 17 00:00:00 2001
From: Ken Murchison <murch@fastmail.com>
Date: Wed, 11 Nov 2020 08:50:54 -0500
Subject: [PATCH] icaltzutil_fetch_timezone() should read v2/v3 data when
 available

Rebased by Mike Gorse <mgorse@suse.com>
---
diff -urp libical-2.0.0.orig/src/libical/icaltz-util.c libical-2.0.0/src/libical/icaltz-util.c
--- libical-2.0.0.orig/src/libical/icaltz-util.c	2015-12-28 15:44:53.000000000 -0600
+++ libical-2.0.0/src/libical/icaltz-util.c	2020-11-30 14:35:43.179227567 -0600
@@ -85,6 +85,9 @@
 
 typedef struct
 {
+    char magic[4];
+    char version;
+    char unused[15];
     char ttisgmtcnt[4];
     char ttisstdcnt[4];
     char leapcnt[4];
@@ -153,6 +156,23 @@ static int decode(const void *ptr)
     }
 }
 
+static long long int decode64(const void *ptr)
+{
+#if defined(sun) && defined(__SVR4)
+#if defined(_BIG_ENDIAN)
+    return *(const long long int *)ptr;
+#else
+    return BSWAP_64(*(const long long int *)ptr);
+#endif
+#else
+    if ((BYTE_ORDER == BIG_ENDIAN)) {
+        return *(const long long int *)ptr;
+    } else {
+        return (int)bswap_64(*(const long long int *)ptr);
+    }
+#endif
+}
+
 static char *zname_from_stridx(char *str, long idx)
 {
     long i;
@@ -294,13 +314,14 @@ static void adjust_dtstart_day_to_rrule(
 
 icalcomponent *icaltzutil_fetch_timezone(const char *location)
 {
-    tzinfo type_cnts;
+    tzinfo header;
     size_t i, num_trans, num_chars, num_leaps, num_isstd, num_isgmt;
     size_t num_types = 0;
     size_t size;
     time_t trans;
     int dstidx = -1, stdidx = -1, pos, sign, zidx, zp_idx;
     icalcomponent *std_comp = NULL;
+    int trans_size = 4;
 
     const char *zonedir;
     FILE *f = NULL;
@@ -342,32 +363,74 @@ icalcomponent *icaltzutil_fetch_timezone
         goto error;
     }
 
-    if (fseek(f, 20, SEEK_SET) != 0) {
-        icalerror_set_errno(ICAL_FILE_ERROR);
+    /* read version 1 header */
+    EFREAD(&header, 44, 1, f);
+    if (memcmp(header.magic, "TZif", 4)) {
+        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+        goto error;
+    }
+    switch (header.version) {
+    case 0:
+        break;
+    case '2':
+    case '3':
+        if (sizeof(time_t) == 8)
+            trans_size = 8;
+        break;
+    default:
+        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
         goto error;
     }
 
-    EFREAD(&type_cnts, 24, 1, f);
-
-    num_isgmt = (size_t)decode(type_cnts.ttisgmtcnt);
-    num_leaps = (size_t)decode(type_cnts.leapcnt);
-    num_chars = (size_t)decode(type_cnts.charcnt);
-    num_trans = (size_t)decode(type_cnts.timecnt);
-    num_isstd = (size_t)decode(type_cnts.ttisstdcnt);
-    num_types = (size_t)decode(type_cnts.typecnt);
+    num_isgmt = (size_t)decode(header.ttisgmtcnt);
+    num_leaps = (size_t)decode(header.leapcnt);
+    num_chars = (size_t)decode(header.charcnt);
+    num_trans = (size_t)decode(header.timecnt);
+    num_isstd = (size_t)decode(header.ttisstdcnt);
+    num_types = (size_t)decode(header.typecnt);
+
+    if (trans_size == 8) {
+        long skip = num_trans * 5 + num_types * 6 +
+            num_chars + num_leaps * 8 + num_isstd + num_isgmt;
+            
+        /* skip version 1 data block */
+        if (fseek(f, skip, SEEK_CUR) != 0) {
+            icalerror_set_errno(ICAL_FILE_ERROR);
+            goto error;
+        }
 
-    transitions = calloc(num_trans, sizeof(time_t));
-    if (transitions == NULL) {
-        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
-        goto error;
+        /* read version 2+ header */
+        EFREAD(&header, 44, 1, f);
+        if (memcmp(header.magic, "TZif", 4)) {
+            icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
+            goto error;
+        }
+ 
+        num_isgmt = (size_t)decode(header.ttisgmtcnt);
+        num_leaps = (size_t)decode(header.leapcnt);
+        num_chars = (size_t)decode(header.charcnt);
+        num_trans = (size_t)decode(header.timecnt);
+        num_isstd = (size_t)decode(header.ttisstdcnt);
+        num_types = (size_t)decode(header.typecnt);
     }
-    r_trans = calloc(num_trans, 4);
-    if (r_trans == NULL) {
-        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+
+    /* read data block */
+    if (num_trans > 0) {
+        transitions = calloc(num_trans, sizeof(time_t));
+        if (transitions == NULL) {
+            icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+            goto error;
+        }
+        r_trans = calloc(num_trans, trans_size);
+        if (r_trans == NULL) {
+            icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+            goto error;
+        }
+    } else {
+        icalerror_set_errno(ICAL_FILE_ERROR);
         goto error;
     }
-
-    EFREAD(r_trans, 4, num_trans, f);
+    EFREAD(r_trans, trans_size, num_trans, f);
     temp = r_trans;
     if (num_trans) {
         trans_idx = calloc(num_trans, sizeof(int));
@@ -377,8 +440,11 @@ icalcomponent *icaltzutil_fetch_timezone
         }
         for (i = 0; i < num_trans; i++) {
             trans_idx[i] = fgetc(f);
-            transitions[i] = (time_t) decode(r_trans);
-            r_trans += 4;
+            if (trans_size == 8)
+                transitions[i] = (time_t) decode64(r_trans);
+            else
+                transitions[i] = (time_t) decode(r_trans);
+            r_trans += trans_size;
         }
     }
     r_trans = temp;
@@ -417,10 +483,13 @@ icalcomponent *icaltzutil_fetch_timezone
         goto error;
     }
     for (i = 0; i < num_leaps; i++) {
-        char c[4];
+        char c[8];
 
-        EFREAD(c, 4, 1, f);
-        leaps[i].transition = (time_t)decode(c);
+        EFREAD(c, trans_size, 1, f);
+        if (trans_size == 8)
+            leaps[i].transition = (time_t)decode64(c);
+        else
+            leaps[i].transition = (time_t)decode(c);
 
         EFREAD(c, 4, 1, f);
         leaps[i].change = decode(c);
@@ -445,6 +514,10 @@ icalcomponent *icaltzutil_fetch_timezone
         types[i++].isgmt = 0;
     }
 
+    if (trans_size == 8) {
+        /* XXX  Do we need/want to read and use the footer? */
+    }
+
     /* Read all the contents now */
 
     for (i = 0; i < num_types; i++) {
