"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 (¤t_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 }