"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/libexchangemapi/e-mapi-cal-tz-utils.c" (2 Dec 2022, 25383 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "e-mapi-cal-tz-utils.c" see the Fossies "Dox" file reference documentation.

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * This program is free software; you can redistribute it and/or
    4  * modify it under the terms of the GNU Lesser General Public
    5  * License as published by the Free Software Foundation; either
    6  * version 2 of the License, or (at your option) version 3.
    7  *
    8  * This program is distributed in the hope that it will be useful,
    9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11  * Lesser General Public License for more details.
   12  *
   13  * You should have received a copy of the GNU Lesser General Public
   14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   15  *
   16  *
   17  * Authors:
   18  *    Suman Manjunath <msuman@novell.com>
   19  *
   20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
   21  *
   22  */
   23 
   24 #include "evolution-mapi-config.h"
   25 
   26 #include "e-mapi-cal-utils.h"
   27 #include "e-mapi-cal-tz-utils.h"
   28 
   29 #define d(x) 
   30 
   31 #define MAPPING_SEPARATOR "~~~"
   32 
   33 static GRecMutex mutex;
   34 
   35 static GHashTable *mapi_to_ical = NULL;
   36 static GHashTable *ical_to_mapi = NULL;
   37 
   38 const gchar *
   39 e_mapi_cal_tz_util_get_mapi_equivalent (const gchar *ical_tz_location)
   40 {
   41     const gchar *retval = NULL;
   42 
   43     g_return_val_if_fail ((ical_tz_location && *ical_tz_location), NULL);
   44 
   45     g_rec_mutex_lock(&mutex);
   46     if (!e_mapi_cal_tz_util_populate()) {
   47         g_rec_mutex_unlock(&mutex);
   48         return NULL;
   49     }
   50 
   51     d(g_message("%s: %s of '%s' ", G_STRLOC, G_STRFUNC, ical_tz_location));
   52 
   53     retval = g_hash_table_lookup (ical_to_mapi, ical_tz_location);
   54 
   55     g_rec_mutex_unlock(&mutex);
   56 
   57     return retval;
   58 }
   59 
   60 const gchar *
   61 e_mapi_cal_tz_util_get_ical_equivalent (const gchar *mapi_tz_location)
   62 {
   63     const gchar *retval = NULL;
   64 
   65     g_return_val_if_fail ((mapi_tz_location && *mapi_tz_location), NULL);
   66 
   67     g_rec_mutex_lock(&mutex);
   68     if (!e_mapi_cal_tz_util_populate()) {
   69         g_rec_mutex_unlock(&mutex);
   70         return NULL;
   71     }
   72 
   73     d(g_message("%s: %s of '%s' ", G_STRLOC, G_STRFUNC, mapi_tz_location));
   74 
   75     retval = g_hash_table_lookup (mapi_to_ical, mapi_tz_location);
   76 
   77     g_rec_mutex_unlock(&mutex);
   78 
   79     return retval;
   80 }
   81 
   82 static ICalTime *
   83 e_mapi_tm_to_icaltime (struct tm *tm,
   84                gboolean dst)
   85 {
   86     ICalTime *itt;
   87 
   88     itt = i_cal_time_new_null_time ();
   89     i_cal_time_set_time (itt, 0, 0, 0);
   90     i_cal_time_set_date (itt, tm->tm_year + 1900, dst ? 6 : 1, 1);
   91     i_cal_time_set_timezone (itt, NULL);
   92     i_cal_time_set_is_date (itt, FALSE);
   93 
   94     return itt;
   95 }
   96 
   97 static gint
   98 get_offset (ICalTimezone *zone,
   99         gboolean dst)
  100 {
  101     struct tm local;
  102     ICalTime *itt;
  103     gint offset;
  104     gint is_daylight = 0; /* Its value is ignored, but libical-glib 3.0.5 API requires it */
  105     time_t now = time (NULL);
  106 
  107     gmtime_r (&now, &local);
  108     itt = e_mapi_tm_to_icaltime (&local, dst);
  109     offset = i_cal_timezone_get_utc_offset (zone, itt, &is_daylight);
  110     g_clear_object (&itt);
  111 
  112     return offset / -60;
  113 }
  114 
  115 const gchar *
  116 e_mapi_cal_tz_util_ical_from_zone_struct (const guint8 *lpb,
  117                       guint32 cb)
  118 {
  119     GHashTableIter iter;
  120     gpointer key, value;
  121     guint32 utcBias, stdBias, dstBias;
  122     const gchar *res = NULL;
  123 
  124     g_return_val_if_fail (lpb != NULL, NULL);
  125 
  126     /* get the timezone by biases, which are the first 3*4 bytes */
  127     if (cb < 12)
  128         return NULL;
  129 
  130     memcpy (&utcBias, lpb, 4); lpb += 4;
  131     memcpy (&stdBias, lpb, 4); lpb += 4;
  132     memcpy (&dstBias, lpb, 4); lpb += 4;
  133 
  134     g_rec_mutex_lock (&mutex);
  135     if (!e_mapi_cal_tz_util_populate ()) {
  136         g_rec_mutex_unlock (&mutex);
  137         return NULL;
  138     }
  139 
  140     g_hash_table_iter_init (&iter, mapi_to_ical);
  141     while (g_hash_table_iter_next (&iter, &key, &value)) {
  142         const gchar *location = value;
  143         ICalTimezone *zone;
  144         gint offset;
  145 
  146         zone = i_cal_timezone_get_builtin_timezone (location);
  147         if (!zone)
  148             continue;
  149 
  150         offset = get_offset (zone, FALSE);
  151         if (offset != utcBias || offset != utcBias + stdBias)
  152             continue;
  153 
  154         offset = get_offset (zone, TRUE);
  155         if (offset != utcBias + dstBias)
  156             continue;
  157 
  158         /* pick shortest and alphabetically first timezone */
  159         if (!res ||
  160             strlen (res) > strlen (location) ||
  161             (strlen (res) == strlen (location) &&
  162             strcmp (location, res) < 0))
  163             res = location;
  164     }
  165 
  166     g_rec_mutex_unlock (&mutex);
  167 
  168     return res;
  169 }
  170 
  171 void
  172 e_mapi_cal_tz_util_destroy (void)
  173 {
  174     g_rec_mutex_lock(&mutex);
  175     if (!(mapi_to_ical && ical_to_mapi)) {
  176         g_rec_mutex_unlock(&mutex);
  177         return;
  178     }
  179 
  180     g_hash_table_destroy (mapi_to_ical);
  181     g_hash_table_destroy (ical_to_mapi);
  182 
  183     /* Reset all the values */
  184     mapi_to_ical = NULL;
  185     ical_to_mapi = NULL;
  186 
  187     g_rec_mutex_unlock(&mutex);
  188 }
  189 
  190 static void
  191 file_contents_to_hashtable (const gchar *contents, GHashTable *table)
  192 {
  193     gchar **array = NULL;
  194     guint len = 0, i;
  195 
  196     array = g_strsplit (contents, "\n", -1);
  197     len = g_strv_length (array);
  198 
  199     for (i=0; i < len-1; ++i) {
  200         gchar **mapping = g_strsplit (array[i], MAPPING_SEPARATOR, -1);
  201         if (g_strv_length (mapping) == 2)
  202             g_hash_table_insert (table, g_strdup (mapping[0]), g_strdup (mapping[1]));
  203         g_strfreev (mapping);
  204     }
  205 
  206     g_strfreev (array);
  207 }
  208 
  209 gboolean
  210 e_mapi_cal_tz_util_populate (void)
  211 {
  212     gchar *mtoi_fn = NULL, *itom_fn = NULL;
  213     GMappedFile *mtoi_mf = NULL, *itom_mf = NULL;
  214 
  215     g_rec_mutex_lock(&mutex);
  216     if (mapi_to_ical && ical_to_mapi) {
  217         g_rec_mutex_unlock(&mutex);
  218         return TRUE;
  219     }
  220 
  221     mtoi_fn = g_build_filename (MAPI_DATADIR, "tz-mapi-to-ical", NULL);
  222     itom_fn = g_build_filename (MAPI_DATADIR, "tz-ical-to-mapi", NULL);
  223 
  224     mtoi_mf = g_mapped_file_new (mtoi_fn, FALSE, NULL);
  225     itom_mf = g_mapped_file_new (itom_fn, FALSE, NULL);
  226 
  227     g_free (mtoi_fn);
  228     g_free (itom_fn);
  229 
  230     if (!(mtoi_mf && itom_mf)) {
  231         g_warning ("Could not map Exchange MAPI timezone files.");
  232 
  233         if (mtoi_mf)
  234 #if GLIB_CHECK_VERSION(2,21,3)
  235             g_mapped_file_unref (mtoi_mf);
  236 #else
  237             g_mapped_file_free (mtoi_mf);
  238 #endif
  239         if (itom_mf)
  240 #if GLIB_CHECK_VERSION(2,21,3)
  241             g_mapped_file_unref (itom_mf);
  242 #else
  243             g_mapped_file_free (itom_mf);
  244 #endif
  245 
  246         g_rec_mutex_unlock(&mutex);
  247         return FALSE;
  248     }
  249 
  250     mapi_to_ical = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  251 
  252     file_contents_to_hashtable (g_mapped_file_get_contents (mtoi_mf), mapi_to_ical);
  253 
  254     ical_to_mapi = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  255 
  256     file_contents_to_hashtable (g_mapped_file_get_contents (itom_mf), ical_to_mapi);
  257 
  258     if (!(g_hash_table_size (mapi_to_ical) && g_hash_table_size (ical_to_mapi))) {
  259         g_warning ("Exchange MAPI timezone files are not valid.");
  260 
  261         e_mapi_cal_tz_util_destroy ();
  262 
  263 #if GLIB_CHECK_VERSION(2,21,3)
  264         g_mapped_file_unref (mtoi_mf);
  265         g_mapped_file_unref (itom_mf);
  266 #else
  267         g_mapped_file_free (mtoi_mf);
  268         g_mapped_file_free (itom_mf);
  269 #endif
  270 
  271         g_rec_mutex_unlock(&mutex);
  272         return FALSE;
  273     }
  274 
  275 #if GLIB_CHECK_VERSION(2,21,3)
  276     g_mapped_file_unref (mtoi_mf);
  277     g_mapped_file_unref (itom_mf);
  278 #else
  279     g_mapped_file_free (mtoi_mf);
  280     g_mapped_file_free (itom_mf);
  281 #endif
  282 
  283     d(e_mapi_cal_tz_util_dump ());
  284 
  285     g_rec_mutex_unlock(&mutex);
  286 
  287     return TRUE;
  288 }
  289 
  290 static void
  291 e_mapi_cal_tz_util_dump_ical_tzs (void)
  292 {
  293     gint ii, nelems;
  294     ICalArray *zones;
  295     GList *l, *list_items = NULL;
  296 
  297     /* Get the array of builtin timezones. */
  298     zones = i_cal_timezone_get_builtin_timezones ();
  299     nelems = i_cal_array_size (zones);
  300 
  301     g_message("%s: %s: ", G_STRLOC, G_STRFUNC);
  302 
  303     for (ii = 0; ii < nelems; ii++) {
  304         ICalTimezone *zone;
  305         const gchar *tzid = NULL;
  306 
  307         zone = i_cal_timezone_array_element_at (zones, ii);
  308         if (!zone)
  309             continue;
  310 
  311         tzid = i_cal_timezone_get_tzid (zone);
  312         if (tzid)
  313             list_items = g_list_prepend (list_items, g_strdup (tzid));
  314 
  315         g_object_unref (zone);
  316     }
  317 
  318     list_items = g_list_sort (list_items, (GCompareFunc) g_ascii_strcasecmp);
  319 
  320     /* Put the "UTC" entry at the top of the combo's list. */
  321     list_items = g_list_prepend (list_items, g_strdup ("UTC"));
  322 
  323     for (l = list_items, ii = 0; l != NULL; l = l->next, ++ii) {
  324         g_print ("[%3d]\t%s\n", (ii + 1), (gchar *)(l->data));
  325     }
  326 
  327     /* i_cal_timezone_free_builtin_timezones (); */
  328 
  329     g_list_free_full (list_items, g_free);
  330 }
  331 
  332 void
  333 e_mapi_cal_tz_util_dump (void)
  334 {
  335     guint i;
  336     GList *keys, *values, *l, *m;
  337 
  338     g_rec_mutex_lock(&mutex);
  339 
  340     e_mapi_cal_tz_util_dump_ical_tzs ();
  341 
  342     if (!(mapi_to_ical && ical_to_mapi)) {
  343         g_rec_mutex_unlock(&mutex);
  344         return;
  345     }
  346 
  347     g_message("%s: %s: ", G_STRLOC, G_STRFUNC);
  348 
  349     g_message ("Dumping #table mapi_to_ical");
  350     keys = g_hash_table_get_keys (mapi_to_ical);
  351     values = g_hash_table_get_values (mapi_to_ical);
  352     l = g_list_first (keys);
  353     m = g_list_first (values);
  354     for (i=0; l && m; ++i, l=l->next, m=m->next)
  355         g_print ("[%3d]\t%s\t%s\t%s\n", (i+1), (gchar *)(l->data), MAPPING_SEPARATOR, (gchar *)(m->data));
  356     g_message ("Dumping differences in #tables");
  357     l = g_list_first (keys);
  358     m = g_list_first (values);
  359     for (i=0; l && m; ++i, l=l->next, m=m->next)
  360         if (g_ascii_strcasecmp ((gchar *)(l->data), (gchar *) g_hash_table_lookup (ical_to_mapi, (m->data))))
  361             g_print ("[%3d] Possible mis-match for %s\n", (i+1), (gchar *)(l->data));
  362     g_list_free (keys);
  363     g_list_free (values);
  364 
  365     g_message ("Dumping #table ical_to_mapi");
  366     keys = g_hash_table_get_keys (ical_to_mapi);
  367     values = g_hash_table_get_values (ical_to_mapi);
  368     l = g_list_first (keys);
  369     m = g_list_first (values);
  370     for (i=0; l && m; ++i, l=l->next, m=m->next)
  371         g_print ("[%3d]\t%s\t%s\t%s\n", (i+1), (gchar *)(l->data), MAPPING_SEPARATOR, (gchar *)(m->data));
  372     g_list_free (keys);
  373     g_list_free (values);
  374 
  375     g_rec_mutex_unlock(&mutex);
  376 }
  377 
  378 static void
  379 write_icaltime_as_systemtime (GByteArray *ba,
  380                   ICalTime *itt)
  381 {
  382     guint16 flag16;
  383 
  384     /* wYear */
  385     flag16 = i_cal_time_get_year (itt);
  386     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  387 
  388     /* wMonth */
  389     flag16 = i_cal_time_get_month (itt);
  390     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  391 
  392     /* wDayOfWeek */
  393     flag16 = i_cal_time_get_year (itt) == 0 ? 0 : i_cal_time_day_of_week (itt);
  394     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  395 
  396     /* wDay */
  397     flag16 = i_cal_time_get_day (itt);
  398     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  399 
  400     /* wHour */
  401     flag16 = i_cal_time_get_hour (itt);
  402     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  403 
  404     /* wMinute */
  405     flag16 = i_cal_time_get_minute (itt);
  406     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  407 
  408     /* wSecond */
  409     flag16 = i_cal_time_get_second (itt);
  410     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  411 
  412     /* wMilliseconds */
  413     flag16 = 0;
  414     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  415 }
  416 
  417 static void
  418 write_tz_rule (GByteArray *ba,
  419            gboolean is_recur,
  420            guint32 bias,
  421            guint32 standard_bias,
  422            guint32 daylight_bias,
  423            ICalTime *standard_date,
  424            ICalTime *daylight_date)
  425 {
  426     guint8 flag8;
  427     guint16 flag16;
  428 
  429     g_return_if_fail (ba != NULL);
  430 
  431     /* Major version */
  432     flag8 = 0x02;
  433     g_byte_array_append (ba, (const guint8 *) &flag8, sizeof (guint8));
  434     
  435     /* Minor version */
  436     flag8 = 0x01;
  437     g_byte_array_append (ba, (const guint8 *) &flag8, sizeof (guint8));
  438 
  439     /* Reserved */
  440     flag16 = 0x003e;
  441     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  442 
  443     /* TZRule flags */
  444     flag16 = 0;
  445     if (is_recur)
  446         flag16 |= 1;
  447     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  448 
  449     /* wYear */
  450     flag16 = i_cal_time_get_year (standard_date);
  451     g_byte_array_append (ba, (const guint8 *) &flag16, sizeof (guint16));
  452 
  453     /* X - 14 times 0x00 */
  454     flag8 = 0x00;
  455     for (flag16 = 0; flag16 < 14; flag16++) {
  456         g_byte_array_append (ba, (const guint8 *) &flag8, sizeof (guint8));
  457     }
  458 
  459     /* lBias */
  460     g_byte_array_append (ba, (const guint8 *) &bias, sizeof (guint32));
  461 
  462     /* lStandardBias */
  463     g_byte_array_append (ba, (const guint8 *) &standard_bias, sizeof (guint32));
  464 
  465     /* lDaylightBias */
  466     g_byte_array_append (ba, (const guint8 *) &daylight_bias, sizeof (guint32));
  467 
  468     /* stStandardDate */
  469     write_icaltime_as_systemtime (ba, standard_date);
  470 
  471     /* stDaylightDate */
  472     write_icaltime_as_systemtime (ba, daylight_date);
  473 }
  474 
  475 static void
  476 extract_bias_and_date (ICalComponent *icomp,
  477                guint32 *bias,
  478                ICalTime **start)
  479 {
  480     ICalProperty *prop;
  481     gint tzoffset;
  482 
  483     g_return_if_fail (icomp != NULL);
  484     g_return_if_fail (bias != NULL);
  485     g_return_if_fail (start != NULL);
  486 
  487     prop = i_cal_component_get_first_property (icomp, I_CAL_TZOFFSETTO_PROPERTY);
  488     if (prop)
  489         tzoffset = i_cal_property_get_tzoffsetto (prop);
  490     else
  491         tzoffset = 0;
  492 
  493     *bias = tzoffset / 60;
  494     *start = i_cal_component_get_dtstart (icomp);
  495 
  496     g_clear_object (&prop);
  497 }
  498 
  499 static void
  500 write_tz_rule_comps (GByteArray *ba,
  501              gboolean is_recur,
  502              ICalComponent *standardcomp,
  503              ICalComponent *daylightcomp,
  504              ICalTimezone *zone)
  505 {
  506     ICalTime *standard_date, *daylight_date, *current_time;
  507     guint32 bias, standard_bias, daylight_bias;
  508 
  509     g_return_if_fail (ba != NULL);
  510     g_return_if_fail (standardcomp != NULL);
  511     g_return_if_fail (daylightcomp != NULL);
  512 
  513     extract_bias_and_date (standardcomp, &standard_bias, &standard_date);
  514     extract_bias_and_date (daylightcomp, &daylight_bias, &daylight_date);
  515 
  516     current_time = i_cal_time_new_current_with_zone (zone);
  517     bias = i_cal_time_is_daylight (current_time) ? daylight_bias : standard_bias;
  518 
  519     write_tz_rule (ba, is_recur, bias, standard_bias, daylight_bias, standard_date, daylight_date);
  520 
  521     g_clear_object (&standard_date);
  522     g_clear_object (&daylight_date);
  523     g_clear_object (&current_time);
  524 }
  525 
  526 static void
  527 add_timezone_rules (GByteArray *ba,
  528             gboolean is_recur,
  529             ICalComponent *vtimezone,
  530             ICalTimezone *zone)
  531 {
  532     gboolean any_added = FALSE;
  533 
  534     g_return_if_fail (ba != NULL);
  535 
  536     if (vtimezone) {
  537         ICalComponent *subcomp, *standardcomp = NULL, *daylightcomp = NULL;
  538 
  539         for (subcomp = i_cal_component_get_first_component (vtimezone, I_CAL_ANY_COMPONENT);
  540              subcomp;
  541              g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (vtimezone, I_CAL_ANY_COMPONENT)) {
  542             if (i_cal_component_isa (subcomp) == I_CAL_XSTANDARD_COMPONENT)
  543                 standardcomp = g_object_ref (subcomp);
  544             if (i_cal_component_isa (subcomp) == I_CAL_XDAYLIGHT_COMPONENT)
  545                 daylightcomp = g_object_ref (subcomp);
  546             if (standardcomp && daylightcomp) {
  547                 write_tz_rule_comps (ba, is_recur, standardcomp, daylightcomp, zone);
  548 
  549                 any_added = TRUE;
  550                 g_clear_object (&standardcomp);
  551                 g_clear_object (&daylightcomp);
  552             }
  553         }
  554 
  555         if (standardcomp || daylightcomp) {
  556             if (!standardcomp)
  557                 standardcomp = g_object_ref (daylightcomp);
  558             write_tz_rule_comps (ba, is_recur, standardcomp, daylightcomp, zone);
  559             any_added = TRUE;
  560         }
  561 
  562         g_clear_object (&standardcomp);
  563         g_clear_object (&daylightcomp);
  564     }
  565 
  566     /* at least one should be always added, make it UTC */
  567     if (!any_added) {
  568         ICalTime *fake_utc;
  569 
  570         fake_utc = i_cal_time_new_null_time ();
  571 
  572         write_tz_rule (ba, is_recur, 0, 0, 0, fake_utc, fake_utc);
  573 
  574         g_object_unref (fake_utc);
  575     }
  576 }
  577 
  578 #define TZDEFINITION_FLAG_VALID_GUID     0x0001 // the guid is valid
  579 #define TZDEFINITION_FLAG_VALID_KEYNAME  0x0002 // the keyname is valid
  580 #define TZ_MAX_RULES          1024 
  581 #define TZ_BIN_VERSION_MAJOR  0x02 
  582 #define TZ_BIN_VERSION_MINOR  0x01 
  583 
  584 void
  585 e_mapi_cal_util_mapi_tz_to_bin (const gchar *mapi_tzid,
  586                 struct SBinary_short *bin,
  587                 TALLOC_CTX *mem_ctx,
  588                 gboolean is_recur)
  589 {
  590     GByteArray *ba;
  591     guint8 flag8;
  592     guint16 flag16;
  593     gunichar2 *buf;
  594     glong items_written;
  595     ICalTimezone *zone = NULL;
  596     ICalComponent *vtimezone;
  597     gint rules = 0;
  598     const gchar *ical_location = e_mapi_cal_tz_util_get_ical_equivalent (mapi_tzid);
  599 
  600     if (ical_location && *ical_location)
  601         zone = i_cal_timezone_get_builtin_timezone (ical_location);
  602     if (!zone)
  603         zone = i_cal_timezone_get_utc_timezone ();
  604     vtimezone = i_cal_timezone_get_component (zone);
  605     if (vtimezone)
  606         rules = (i_cal_component_count_components (vtimezone, I_CAL_XSTANDARD_COMPONENT) +
  607              i_cal_component_count_components (vtimezone, I_CAL_XDAYLIGHT_COMPONENT)) / 2;
  608     if (!rules)
  609         rules = 1;
  610 
  611     ba = g_byte_array_new ();
  612 
  613     /* UTF-8 length of the keyname */
  614     flag16 = g_utf8_strlen (mapi_tzid, -1);
  615     ba = g_byte_array_append (ba, (const guint8 *)&flag16, sizeof (guint16));
  616     /* Keyname */
  617     buf = g_utf8_to_utf16 (mapi_tzid, flag16, NULL, &items_written, NULL);
  618     ba = g_byte_array_append (ba, (const guint8 *)buf, (sizeof (gunichar2) * items_written));
  619     g_free (buf);
  620 
  621     /* number of rules */
  622     flag16 = rules;
  623     ba = g_byte_array_append (ba, (const guint8 *)&flag16, sizeof (guint16));
  624 
  625     /* wFlags: we know only keyname based names */
  626     flag16 = TZDEFINITION_FLAG_VALID_KEYNAME;
  627     ba = g_byte_array_prepend (ba, (const guint8 *)&flag16, sizeof (guint16));
  628 
  629     /* Length in bytes until rules info */
  630     flag16 = (guint16) (ba->len);
  631     ba = g_byte_array_prepend (ba, (const guint8 *)&flag16, sizeof (guint16));
  632 
  633     /* Minor version */
  634     flag8 = TZ_BIN_VERSION_MINOR;
  635     ba = g_byte_array_prepend (ba, (const guint8 *)&flag8, sizeof (guint8));
  636 
  637     /* Major version */
  638     flag8 = TZ_BIN_VERSION_MAJOR;
  639     ba = g_byte_array_prepend (ba, (const guint8 *)&flag8, sizeof (guint8));
  640 
  641     /* Rules */
  642     add_timezone_rules (ba, is_recur, vtimezone, zone);
  643 
  644     bin->cb = ba->len;
  645     bin->lpb = talloc_memdup (mem_ctx, ba->data, ba->len);
  646 
  647     d(g_message ("New timezone stream.. Length: %d bytes.. Hex-data follows:", ba->len));
  648     d(for (i = 0; i < ba->len; i++)
  649         g_print("0x%.2X ", ba->data[i]));
  650 
  651     g_byte_array_free (ba, TRUE);
  652     g_clear_object (&vtimezone);
  653 }
  654 
  655 gchar *
  656 e_mapi_cal_util_bin_to_mapi_tz (const guint8 *lpb,
  657                 guint32 cb)
  658 {
  659     guint8 flag8;
  660     guint16 flag16, cbHeader = 0;
  661     const guint8 *ptr = lpb;
  662     gchar *buf = NULL;
  663 
  664     d(g_message ("New timezone stream.. Length: %d bytes.. Info follows:", ba->len));
  665 
  666     /* Major version */
  667     flag8 = *((guint8 *)ptr);
  668     ptr += sizeof (guint8);
  669     d(g_print ("Major version: %d\n", flag8));
  670     if (TZ_BIN_VERSION_MAJOR != flag8)
  671         return NULL;
  672 
  673     /* Minor version */
  674     flag8 = *((guint8 *)ptr);
  675     ptr += sizeof (guint8);
  676     d(g_print ("Minor version: %d\n", flag8));
  677     if (TZ_BIN_VERSION_MINOR > flag8)
  678         return NULL;
  679 
  680     /* Length in bytes until rules info */
  681     flag16 = *((guint16 *)ptr);
  682     ptr += sizeof (guint16);
  683     d(g_print ("Length in bytes until rules: %d\n", flag16));
  684     cbHeader = flag16;
  685 
  686     /* wFlags: we don't yet understand GUID based names */
  687     flag16 = *((guint16 *)ptr);
  688     ptr += sizeof (guint16);
  689     d(g_print ("wFlags: %d\n", flag16));
  690     cbHeader -= sizeof (guint16);
  691     if (TZDEFINITION_FLAG_VALID_KEYNAME != flag16)
  692         return NULL;
  693 
  694     /* UTF-8 length of the keyname */
  695     flag16 = *((guint16 *)ptr);
  696     ptr += sizeof (guint16);
  697     d(g_print ("UTF8 length of keyname: %d\n", flag16));
  698     cbHeader -= sizeof (guint16);
  699 
  700     /* number of rules is at the end of the header.. we'll parse and use later */
  701     cbHeader -= sizeof (guint16);
  702 
  703     /* Keyname */
  704     buf = g_utf16_to_utf8 ((const gunichar2 *)ptr, cbHeader/sizeof (gunichar2), NULL, NULL, NULL);
  705     ptr += cbHeader;
  706     d(g_print ("Keyname: %s\n", buf));
  707 
  708     /* number of rules */
  709     flag16 = *((guint16 *)ptr);
  710     ptr += sizeof (guint16);
  711     d(g_print ("Number of rules: %d\n", flag16));
  712 
  713     /* FIXME: Need to support rules */
  714 
  715     return buf;
  716 }
  717 
  718 /* Corresponds to the first table in OXOCAL 2.2.5.6.
  719    - has_dst is signifies whether the SDT/DST entries are relevant or "N/A"
  720    - utc_offset is in minutes east of UTC
  721  */
  722 struct pltz_mapentry {
  723     gboolean has_dst;
  724     int utc_offset;
  725     int standard_wMonth;
  726     int standard_wDayOfWeek;
  727     int standard_wDay;
  728     int standard_wHour;
  729     int daylight_wMonth;
  730     int daylight_wDayOfWeek;
  731     int daylight_wDay;
  732     int daylight_wHour;
  733 };
  734 
  735 /* Table contents, current as of [MS-OXOCAL] - v20110315 */
  736 static const struct pltz_mapentry pltz_table[] = {
  737     { FALSE,  720,  0, 0, 0, 0,   0, 0, 0, 0 },
  738     { TRUE,     0, 10, 0, 5, 2,   3, 0, 5, 1 },
  739     { TRUE,    60,  9, 0, 5, 2,   3, 0, 5, 1 },
  740     { TRUE,    60, 10, 0, 5, 3,   3, 0, 5, 2 },
  741     { TRUE,    60, 10, 0, 5, 3,   3, 0, 5, 2 },
  742     { TRUE,   120,  9, 0, 5, 1,   3, 0, 5, 0 },
  743     { TRUE,    60,  9, 0, 5, 1,   3, 0, 5, 0 },
  744     { TRUE,   120, 10, 0, 5, 4,   3, 0, 5, 3 },
  745     { TRUE,  -180,  2, 0, 2, 2,  10, 0, 3, 2 },
  746     { TRUE,  -240, 11, 0, 1, 2,   3, 0, 2, 2 },
  747     { TRUE,  -300, 11, 0, 1, 2,   3, 0, 2, 2 },
  748     { TRUE,  -360, 11, 0, 1, 2,   3, 0, 2, 2 },
  749     { TRUE,  -420, 11, 0, 1, 2,   3, 0, 2, 2 },
  750     { TRUE,  -480, 11, 0, 1, 2,   3, 0, 2, 2 },
  751     { TRUE,  -540, 11, 0, 1, 2,   3, 0, 2, 2 },
  752     { FALSE, -600,  0, 0, 0, 0,   0, 0, 0, 0 },
  753     { FALSE, -660,  0, 0, 0, 0,   0, 0, 0, 0 },
  754     { TRUE,   720,  4, 0, 1, 3,   9, 0, 5, 2 },
  755     { TRUE,   600,  3, 0, 5, 3,  10, 0, 5, 2 },
  756     { TRUE,   570,  3, 0, 5, 3,  10, 0, 5, 2 },
  757     { FALSE,  540,  0, 0, 0, 0,   0, 0, 0, 0 },
  758     { FALSE,  480,  0, 0, 0, 0,   0, 0, 0, 0 },
  759     { FALSE,  420,  0, 0, 0, 0,   0, 0, 0, 0 },
  760     { FALSE,  330,  0, 0, 0, 0,   0, 0, 0, 0 },
  761     { FALSE,  240,  0, 0, 0, 0,   0, 0, 0, 0 },
  762     { TRUE,   210,  9, 2, 4, 2,   3, 0, 1, 2 },
  763     { FALSE,  180,  0, 0, 0, 0,   0, 0, 0, 0 },
  764     { TRUE,   120,  9, 0, 3, 2,   3, 5, 5, 2 },
  765     { TRUE,  -210, 11, 0, 1, 0,   3, 0, 2, 0 },
  766     { TRUE,   -60, 10, 0, 5, 1,   3, 0, 5, 0 },
  767     { TRUE,  -120, 10, 0, 5, 1,   3, 0, 5, 0 },
  768     { FALSE,    0,  0, 0, 0, 0,   0, 0, 0, 0 },
  769     { FALSE, -180,  0, 0, 0, 0,   0, 0, 0, 0 },
  770     { FALSE, -240,  0, 0, 0, 0,   0, 0, 0, 0 },
  771     { FALSE, -300,  0, 0, 0, 0,   0, 0, 0, 0 },
  772     { FALSE, -300,  0, 0, 0, 0,   0, 0, 0, 0 },
  773     { FALSE, -360,  0, 0, 0, 0,   0, 0, 0, 0 },
  774     { TRUE,  -360, 10, 0, 5, 2,   4, 0, 1, 2 },
  775     { FALSE, -420,  0, 0, 0, 0,   0, 0, 0, 0 },
  776     { FALSE, -720,  0, 0, 0, 0,   0, 0, 0, 0 },
  777     { FALSE,  720,  0, 0, 0, 0,   0, 0, 0, 0 },
  778     { FALSE,  660,  0, 0, 0, 0,   0, 0, 0, 0 },
  779     { TRUE,   600,  3, 0, 5, 2,  10, 0, 1, 2 },
  780     { FALSE,  600,  0, 0, 0, 0,   0, 0, 0, 0 },
  781     { FALSE,  570,  0, 0, 0, 0,   0, 0, 0, 0 },
  782     { TRUE,   480,  9, 0, 2, 2,   4, 0, 2, 2 },
  783     { FALSE,  360,  0, 0, 0, 0,   0, 0, 0, 0 },
  784     { FALSE,  300,  0, 0, 0, 0,   0, 0, 0, 0 },
  785     { FALSE,  270,  0, 0, 0, 0,   0, 0, 0, 0 },
  786     { TRUE,   120,  9, 4, 5, 2,   5, 5, 1, 2 },
  787     { FALSE,  120,  0, 0, 0, 0,   0, 0, 0, 0 },
  788     { TRUE,   180, 10, 0, 5, 1,   3, 0, 5, 0 },
  789     { TRUE,   600,  3, 0, 5, 2,   8, 0, 5, 2 },
  790     { TRUE,   600,  4, 0, 1, 3,  10, 0, 5, 2 },
  791     { TRUE,   570,  4, 0, 1, 3,  10, 0, 5, 2 },
  792     { TRUE,   600,  4, 0, 1, 3,  10, 0, 1, 2 },
  793     { TRUE,  -240,  3, 6, 2, 23, 10, 6, 2, 23 },
  794     { TRUE,   480,  3, 0, 5, 3,  10, 0, 5, 2 },
  795     { TRUE,  -420, 10, 0, 5, 2,   4, 0, 1, 2 },
  796     { TRUE,  -480, 10, 0, 5, 2,   4, 0, 1, 2 }
  797 };
  798 
  799 /* Return the ordinal-th wday day in month as a time_t in the local time.
  800     @year: year in decimal form
  801     @month: month (1 == Jan)
  802     @wday: weekday (0 == Sunday)
  803     @ordinal: nth occurence of wday, or last occurrence if out of bounds
  804  */
  805 static time_t
  806 nth_day_of_month (int year, int month, int wday, int ordinal)
  807 {
  808     struct tm stm = {0};
  809     time_t ts;
  810 
  811     /* first day of month */
  812     stm.tm_year = year - 1900;
  813     stm.tm_mon = month - 1;
  814     stm.tm_mday = 1;
  815 
  816     ts = mktime (&stm);
  817     /* go to first instance of wday in month */
  818     ts += (60 * 60 * 24) * (wday - stm.tm_wday + 7 * (wday < stm.tm_wday));
  819     /* go to the n weeks in the future */
  820     ts += (60 * 60 * 24 * 7) * (ordinal - 1);
  821     localtime_r (&ts, &stm);
  822     /* the MS spec says that the 5th such weekday of the month always
  823        refers to the last such day, even if it is the 4th.  So, check to
  824        see if we're in the same month, and if not, rewind a week. */
  825     if (stm.tm_mon != month - 1)
  826         ts -= (60 * 60 * 24 * 7);
  827 
  828     return ts;
  829 }
  830 
  831 /* return the most-correct PidLidTimeZone value w.r.t. OXOCAL 2.2.5.6. */
  832 int
  833 e_mapi_cal_util_mapi_tz_pidlidtimezone (ICalTimezone *ictz)
  834 {
  835     gboolean tz_dst_now = FALSE, tz_has_dst = FALSE;
  836     int i, utc_offset = 0, best_index = 0, best_score = -1;
  837     const gchar *tznames;
  838     ICalTime *tt;
  839 
  840     if (ictz == NULL)
  841         return 0;
  842 
  843     /* Simple hack to determine if our TZ has DST */
  844     tznames = i_cal_timezone_get_tznames (ictz);
  845     if (tznames && strchr (tznames, '/'))
  846         tz_has_dst = TRUE;
  847 
  848     /* Calculate minutes east of UTC, what MS uses in this spec */
  849     tt = i_cal_time_new_current_with_zone (ictz);
  850     utc_offset = i_cal_timezone_get_utc_offset (ictz, tt, &tz_dst_now) / 60;
  851     if (tz_dst_now)
  852         utc_offset -= 60;
  853 
  854     /* Find the PidLidTimeZone entry that matches the most closely to
  855        the SDT/DST rules for the given timezone */
  856     for (i = 0; i < sizeof (pltz_table) / sizeof (struct pltz_mapentry); ++i) {
  857         const struct pltz_mapentry *pme = &pltz_table[i];
  858         time_t pre_sdt, sdt, post_sdt, pre_dst, dst, post_dst;
  859         struct tm pre_stm, stm, post_stm;
  860         int score = 0;
  861 
  862         if (pme->utc_offset == utc_offset && tz_has_dst == pme->has_dst)
  863             score = 1;
  864 
  865         if (score && tz_has_dst) {
  866             sdt = nth_day_of_month (i_cal_time_get_year (tt), pme->standard_wMonth,
  867                                     pme->standard_wDayOfWeek,
  868                                     pme->standard_wDay);
  869             /* add the transition hour and a second */
  870             sdt += (pme->standard_wHour * 60 * 60) + 1;
  871             pre_sdt = sdt - 2 * 60 * 60;
  872             post_sdt = sdt + 2 * 60 * 60;
  873 
  874             dst = nth_day_of_month (i_cal_time_get_year (tt), pme->daylight_wMonth,
  875                                     pme->daylight_wDayOfWeek,
  876                                     pme->daylight_wDay);
  877             dst += (pme->daylight_wHour * 60 * 60) + 1;
  878             pre_dst = dst - 2 * 60 * 60;
  879             post_dst = dst + 2 * 60 * 60;
  880 
  881             localtime_r (&sdt, &stm);
  882             localtime_r (&pre_sdt, &pre_stm);
  883             localtime_r (&post_sdt, &post_stm);
  884 
  885             if (!stm.tm_isdst)
  886                 score++;
  887             if (pre_stm.tm_isdst)
  888                 score++;
  889             if (!post_stm.tm_isdst)
  890                 score++;
  891 
  892             localtime_r (&dst, &stm);
  893             localtime_r (&pre_dst, &pre_stm);
  894             localtime_r (&post_dst, &post_stm);
  895 
  896             if (stm.tm_isdst)
  897                 score++;
  898             if (!pre_stm.tm_isdst)
  899                 score++;
  900             if (post_stm.tm_isdst)
  901                 score++;
  902 
  903             if (score > best_score) {
  904                 best_score = score;
  905                 best_index = i;
  906             }
  907         }
  908     }
  909 
  910     g_clear_object (&tt);
  911 
  912     return best_index;
  913 }