Coverage Report

Created: 2025-07-11 06:46

/src/FreeRDP/winpr/libwinpr/timezone/timezone.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Time Zone
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2024 Armin Novak <anovak@thincast.com>
7
 * Copyright 2024 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <winpr/config.h>
23
24
#include <winpr/environment.h>
25
#include <winpr/wtypes.h>
26
#include <winpr/timezone.h>
27
#include <winpr/crt.h>
28
#include <winpr/assert.h>
29
#include <winpr/file.h>
30
#include "../log.h"
31
#include "timezone.h"
32
33
105k
#define TAG WINPR_TAG("timezone")
34
35
#include "TimeZoneNameMap.h"
36
#include "TimeZoneIanaAbbrevMap.h"
37
38
#ifndef _WIN32
39
40
#include <time.h>
41
#include <unistd.h>
42
43
#endif
44
45
#if !defined(_WIN32)
46
static char* winpr_read_unix_timezone_identifier_from_file(FILE* fp)
47
105k
{
48
105k
  const INT CHUNK_SIZE = 32;
49
105k
  size_t rc = 0;
50
105k
  size_t read = 0;
51
105k
  size_t length = CHUNK_SIZE;
52
53
105k
  char* tzid = malloc(length);
54
105k
  if (!tzid)
55
0
    return NULL;
56
57
105k
  do
58
105k
  {
59
105k
    rc = fread(tzid + read, 1, length - read - 1UL, fp);
60
105k
    if (rc > 0)
61
105k
      read += rc;
62
63
105k
    if (read < (length - 1UL))
64
105k
      break;
65
66
0
    if (read > length - 1UL)
67
0
    {
68
0
      free(tzid);
69
0
      return NULL;
70
0
    }
71
72
0
    length += CHUNK_SIZE;
73
0
    char* tmp = (char*)realloc(tzid, length);
74
0
    if (!tmp)
75
0
    {
76
0
      free(tzid);
77
0
      return NULL;
78
0
    }
79
80
0
    tzid = tmp;
81
0
  } while (rc > 0);
82
83
105k
  if (ferror(fp))
84
0
  {
85
0
    free(tzid);
86
0
    return NULL;
87
0
  }
88
89
105k
  tzid[read] = '\0';
90
105k
  if (read > 0)
91
105k
  {
92
105k
    if (tzid[read - 1] == '\n')
93
105k
      tzid[read - 1] = '\0';
94
105k
  }
95
96
105k
  return tzid;
97
105k
}
98
99
static char* winpr_get_timezone_from_link(const char* links[], size_t count)
100
105k
{
101
105k
  const char* _links[] = { "/etc/localtime", "/etc/TZ" };
102
103
105k
  if (links == NULL)
104
0
  {
105
0
    links = _links;
106
0
    count = ARRAYSIZE(_links);
107
0
  }
108
109
  /*
110
   * On linux distros such as Redhat or Archlinux, a symlink at /etc/localtime
111
   * will point to /usr/share/zoneinfo/region/place where region/place could be
112
   * America/Montreal for example.
113
   * Some distributions do have to symlink at /etc/TZ.
114
   */
115
116
105k
  for (size_t x = 0; x < count; x++)
117
105k
  {
118
105k
    char* tzid = NULL;
119
105k
    const char* link = links[x];
120
105k
    char* buf = realpath(link, NULL);
121
122
105k
    if (buf)
123
105k
    {
124
105k
      size_t sep = 0;
125
105k
      size_t alloc = 0;
126
105k
      size_t pos = 0;
127
105k
      size_t len = pos = strlen(buf);
128
129
      /* find the position of the 2nd to last "/" */
130
844k
      for (size_t i = 1; i <= len; i++)
131
844k
      {
132
844k
        const size_t curpos = len - i;
133
844k
        const char cur = buf[curpos];
134
135
844k
        if (cur == '/')
136
211k
          sep++;
137
844k
        if (sep >= 2)
138
105k
        {
139
105k
          alloc = i;
140
105k
          pos = len - i + 1;
141
105k
          break;
142
105k
        }
143
844k
      }
144
145
105k
      if ((len == 0) || (sep != 2))
146
0
        goto end;
147
148
105k
      tzid = (char*)calloc(alloc + 1, sizeof(char));
149
150
105k
      if (!tzid)
151
0
        goto end;
152
153
105k
      strncpy(tzid, &buf[pos], alloc);
154
105k
      WLog_DBG(TAG, "tzid: %s", tzid);
155
105k
      goto end;
156
105k
    }
157
158
105k
  end:
159
105k
    free(buf);
160
105k
    if (tzid)
161
105k
      return tzid;
162
105k
  }
163
164
0
  return NULL;
165
105k
}
166
167
#if defined(ANDROID)
168
#include "../utils/android.h"
169
170
static char* winpr_get_android_timezone_identifier(void)
171
{
172
  char* tzid = NULL;
173
  JNIEnv* jniEnv;
174
175
  /* Preferred: Try to get identifier from java TimeZone class */
176
  if (jniVm && ((*jniVm)->GetEnv(jniVm, (void**)&jniEnv, JNI_VERSION_1_6) == JNI_OK))
177
  {
178
    const char* raw;
179
    jclass jObjClass;
180
    jobject jObj;
181
    jmethodID jDefaultTimezone;
182
    jmethodID jTimezoneIdentifier;
183
    jstring tzJId;
184
    jboolean attached = (*jniVm)->AttachCurrentThread(jniVm, &jniEnv, NULL);
185
    jObjClass = (*jniEnv)->FindClass(jniEnv, "java/util/TimeZone");
186
187
    if (!jObjClass)
188
      goto fail;
189
190
    jDefaultTimezone =
191
        (*jniEnv)->GetStaticMethodID(jniEnv, jObjClass, "getDefault", "()Ljava/util/TimeZone;");
192
193
    if (!jDefaultTimezone)
194
      goto fail;
195
196
    jObj = (*jniEnv)->CallStaticObjectMethod(jniEnv, jObjClass, jDefaultTimezone);
197
198
    if (!jObj)
199
      goto fail;
200
201
    jTimezoneIdentifier =
202
        (*jniEnv)->GetMethodID(jniEnv, jObjClass, "getID", "()Ljava/lang/String;");
203
204
    if (!jTimezoneIdentifier)
205
      goto fail;
206
207
    tzJId = (*jniEnv)->CallObjectMethod(jniEnv, jObj, jTimezoneIdentifier);
208
209
    if (!tzJId)
210
      goto fail;
211
212
    raw = (*jniEnv)->GetStringUTFChars(jniEnv, tzJId, 0);
213
214
    if (raw)
215
      tzid = _strdup(raw);
216
217
    (*jniEnv)->ReleaseStringUTFChars(jniEnv, tzJId, raw);
218
  fail:
219
220
    if (attached)
221
      (*jniVm)->DetachCurrentThread(jniVm);
222
  }
223
224
  /* Fall back to property, might not be available. */
225
  if (!tzid)
226
  {
227
    FILE* fp = popen("getprop persist.sys.timezone", "r");
228
229
    if (fp)
230
    {
231
      tzid = winpr_read_unix_timezone_identifier_from_file(fp);
232
      pclose(fp);
233
    }
234
  }
235
236
  return tzid;
237
}
238
#endif
239
240
static char* winpr_get_unix_timezone_identifier_from_file(void)
241
105k
{
242
#if defined(ANDROID)
243
  return winpr_get_android_timezone_identifier();
244
#else
245
105k
  FILE* fp = NULL;
246
105k
  char* tzid = NULL;
247
#if !defined(WINPR_TIMEZONE_FILE)
248
#error \
249
    "Please define WINPR_TIMEZONE_FILE with the path to your timezone file (e.g. /etc/timezone or similar)"
250
#else
251
105k
  fp = winpr_fopen(WINPR_TIMEZONE_FILE, "r");
252
105k
#endif
253
254
105k
  if (NULL == fp)
255
0
    return NULL;
256
257
105k
  tzid = winpr_read_unix_timezone_identifier_from_file(fp);
258
105k
  (void)fclose(fp);
259
105k
  if (tzid != NULL)
260
105k
    WLog_DBG(TAG, "tzid: %s", tzid);
261
105k
  return tzid;
262
105k
#endif
263
105k
}
264
265
static char* winpr_time_zone_from_env(void)
266
105k
{
267
105k
  LPCSTR tz = "TZ";
268
105k
  char* tzid = NULL;
269
270
105k
  DWORD nSize = GetEnvironmentVariableA(tz, NULL, 0);
271
105k
  if (nSize > 0)
272
0
  {
273
0
    tzid = (char*)calloc(nSize, sizeof(char));
274
0
    if (!tzid)
275
0
      goto fail;
276
0
    if (!GetEnvironmentVariableA(tz, tzid, nSize))
277
0
      goto fail;
278
0
    else if (tzid[0] == ':')
279
0
    {
280
      /* Remove leading colon, see tzset(3) */
281
0
      memmove(tzid, tzid + 1, nSize - sizeof(char));
282
0
    }
283
0
  }
284
285
105k
  return tzid;
286
287
0
fail:
288
0
  free(tzid);
289
0
  return NULL;
290
105k
}
291
292
static char* winpr_translate_time_zone(const char* tzid)
293
105k
{
294
105k
  const char* zipath = "/usr/share/zoneinfo/";
295
105k
  char* buf = NULL;
296
105k
  const char* links[] = { buf };
297
298
105k
  if (!tzid)
299
0
    return NULL;
300
301
105k
  if (tzid[0] == '/')
302
0
  {
303
    /* Full path given in TZ */
304
0
    links[0] = tzid;
305
0
  }
306
105k
  else
307
105k
  {
308
105k
    size_t bsize = 0;
309
105k
    winpr_asprintf(&buf, &bsize, "%s%s", zipath, tzid);
310
105k
    links[0] = buf;
311
105k
  }
312
313
105k
  char* ntzid = winpr_get_timezone_from_link(links, 1);
314
105k
  free(buf);
315
105k
  return ntzid;
316
105k
}
317
318
static char* winpr_guess_time_zone(void)
319
105k
{
320
105k
  char* tzid = winpr_time_zone_from_env();
321
105k
  if (tzid)
322
0
    goto end;
323
105k
  tzid = winpr_get_unix_timezone_identifier_from_file();
324
105k
  if (tzid)
325
105k
    goto end;
326
0
  tzid = winpr_get_timezone_from_link(NULL, 0);
327
0
  if (tzid)
328
0
    goto end;
329
330
105k
end:
331
105k
{
332
105k
  char* ntzid = winpr_translate_time_zone(tzid);
333
105k
  if (ntzid)
334
105k
  {
335
105k
    free(tzid);
336
105k
    return ntzid;
337
105k
  }
338
0
  return tzid;
339
105k
}
340
105k
}
341
342
static SYSTEMTIME tm2systemtime(const struct tm* t)
343
211k
{
344
211k
  SYSTEMTIME st = { 0 };
345
346
211k
  if (t)
347
0
  {
348
0
    st.wYear = (WORD)(1900 + t->tm_year);
349
0
    st.wMonth = (WORD)t->tm_mon + 1;
350
0
    st.wDay = (WORD)t->tm_mday;
351
0
    st.wDayOfWeek = (WORD)t->tm_wday;
352
0
    st.wHour = (WORD)t->tm_hour;
353
0
    st.wMinute = (WORD)t->tm_min;
354
0
    st.wSecond = (WORD)t->tm_sec;
355
0
    st.wMilliseconds = 0;
356
0
  }
357
211k
  return st;
358
211k
}
359
360
static struct tm systemtime2tm(const SYSTEMTIME* st)
361
0
{
362
0
  struct tm t = { 0 };
363
0
  if (st)
364
0
  {
365
0
    if (st->wYear >= 1900)
366
0
      t.tm_year = st->wYear - 1900;
367
0
    if (st->wMonth > 0)
368
0
      t.tm_mon = st->wMonth - 1;
369
0
    t.tm_mday = st->wDay;
370
0
    t.tm_wday = st->wDayOfWeek;
371
0
    t.tm_hour = st->wHour;
372
0
    t.tm_min = st->wMinute;
373
0
    t.tm_sec = st->wSecond;
374
0
  }
375
0
  return t;
376
0
}
377
378
static LONG get_gmtoff_min(const struct tm* t)
379
211k
{
380
211k
  WINPR_ASSERT(t);
381
211k
  return -(LONG)(t->tm_gmtoff / 60l);
382
211k
}
383
384
static struct tm next_day(const struct tm* start)
385
115M
{
386
115M
  struct tm cur = *start;
387
115M
  cur.tm_hour = 0;
388
115M
  cur.tm_min = 0;
389
115M
  cur.tm_sec = 0;
390
115M
  cur.tm_isdst = -1;
391
115M
  cur.tm_mday++;
392
115M
  const time_t t = mktime(&cur);
393
115M
  (void)localtime_r(&t, &cur);
394
115M
  return cur;
395
115M
}
396
397
static struct tm adjust_time(const struct tm* start, int hour, int minute)
398
0
{
399
0
  struct tm cur = *start;
400
0
  cur.tm_hour = hour;
401
0
  cur.tm_min = minute;
402
0
  cur.tm_sec = 0;
403
0
  cur.tm_isdst = -1;
404
0
  const time_t t = mktime(&cur);
405
0
  (void)localtime_r(&t, &cur);
406
0
  return cur;
407
0
}
408
409
/* [MS-RDPBCGR] 2.2.1.11.1.1.1.1.1  System Time (TS_SYSTEMTIME) */
410
static WORD get_transition_weekday_occurrence(const SYSTEMTIME* st)
411
0
{
412
0
  WORD count = 0;
413
0
  struct tm start = systemtime2tm(st);
414
415
0
  WORD last = 0;
416
0
  struct tm next = start;
417
0
  next.tm_mday = 1;
418
0
  next.tm_isdst = -1;
419
0
  do
420
0
  {
421
422
0
    const time_t t = mktime(&next);
423
0
    next.tm_mday++;
424
425
0
    struct tm cur = { 0 };
426
0
    (void)localtime_r(&t, &cur);
427
428
0
    if (cur.tm_mon + 1 != st->wMonth)
429
0
      break;
430
431
0
    if (cur.tm_wday == st->wDayOfWeek)
432
0
    {
433
0
      if (cur.tm_mday <= st->wDay)
434
0
        count++;
435
0
      last++;
436
0
    }
437
438
0
  } while (TRUE);
439
440
0
  if (count == last)
441
0
    count = 5;
442
443
0
  return count;
444
0
}
445
446
static SYSTEMTIME tm2transitiontime(const struct tm* cur)
447
211k
{
448
211k
  SYSTEMTIME t = tm2systemtime(cur);
449
211k
  if (cur)
450
0
  {
451
0
    t.wDay = get_transition_weekday_occurrence(&t);
452
0
    t.wYear = 0;
453
0
  }
454
455
211k
  return t;
456
211k
}
457
458
static SYSTEMTIME get_transition_time(const struct tm* start, BOOL toDst)
459
0
{
460
0
  BOOL toggled = FALSE;
461
0
  struct tm first = adjust_time(start, 0, 0);
462
0
  for (int hour = 0; hour < 24; hour++)
463
0
  {
464
0
    struct tm cur = adjust_time(start, hour, 0);
465
0
    if (cur.tm_isdst != first.tm_isdst)
466
0
      toggled = TRUE;
467
468
0
    if (toggled)
469
0
    {
470
0
      if (toDst && (cur.tm_isdst > 0))
471
0
        return tm2transitiontime(&cur);
472
0
      else if (!toDst && (cur.tm_isdst == 0))
473
0
        return tm2transitiontime(&cur);
474
0
    }
475
0
  }
476
0
  return tm2transitiontime(start);
477
0
}
478
479
static BOOL get_transition_date(const struct tm* start, BOOL toDst, SYSTEMTIME* pdate)
480
211k
{
481
211k
  WINPR_ASSERT(start);
482
211k
  WINPR_ASSERT(pdate);
483
484
211k
  *pdate = tm2transitiontime(NULL);
485
486
211k
  if (start->tm_isdst < 0)
487
0
    return FALSE;
488
489
211k
  BOOL val = start->tm_isdst > 0; // the year starts with DST or not
490
211k
  BOOL toggled = FALSE;
491
211k
  struct tm cur = *start;
492
211k
  struct tm last = cur;
493
77.2M
  for (int day = 1; day <= 365; day++)
494
77.0M
  {
495
77.0M
    last = cur;
496
77.0M
    cur = next_day(&cur);
497
77.0M
    const BOOL curDst = (cur.tm_isdst > 0);
498
77.0M
    if ((val != curDst) && !toggled)
499
0
      toggled = TRUE;
500
501
77.0M
    if (toggled)
502
0
    {
503
0
      if (toDst && curDst)
504
0
      {
505
0
        *pdate = get_transition_time(&last, toDst);
506
0
        return TRUE;
507
0
      }
508
0
      if (!toDst && !curDst)
509
0
      {
510
0
        *pdate = get_transition_time(&last, toDst);
511
0
        return TRUE;
512
0
      }
513
0
    }
514
77.0M
  }
515
211k
  return FALSE;
516
211k
}
517
518
static LONG get_bias(const struct tm* start, BOOL dstBias)
519
316k
{
520
316k
  if ((start->tm_isdst > 0) && dstBias)
521
0
    return get_gmtoff_min(start);
522
316k
  if ((start->tm_isdst == 0) && !dstBias)
523
211k
    return get_gmtoff_min(start);
524
105k
  if (start->tm_isdst < 0)
525
0
    return get_gmtoff_min(start);
526
527
105k
  struct tm cur = *start;
528
38.6M
  for (int day = 1; day <= 365; day++)
529
38.5M
  {
530
38.5M
    cur = next_day(&cur);
531
38.5M
    if ((cur.tm_isdst > 0) && dstBias)
532
0
      return get_gmtoff_min(&cur);
533
38.5M
    else if ((cur.tm_isdst == 0) && !dstBias)
534
0
      return get_gmtoff_min(&cur);
535
38.5M
  }
536
105k
  return 0;
537
105k
}
538
539
static BOOL map_iana_id(const char* iana, LPDYNAMIC_TIME_ZONE_INFORMATION tz)
540
105k
{
541
105k
  const char* winId = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_ID);
542
105k
  const char* winStd = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_STANDARD);
543
105k
  const char* winDst = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_DAYLIGHT);
544
545
105k
  if (winStd)
546
105k
    (void)ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName));
547
105k
  if (winDst)
548
105k
    (void)ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName));
549
105k
  if (winId)
550
105k
    (void)ConvertUtf8ToWChar(winId, tz->TimeZoneKeyName, ARRAYSIZE(tz->TimeZoneKeyName));
551
552
105k
  return winId != NULL;
553
105k
}
554
555
static const char* weekday2str(WORD wDayOfWeek)
556
0
{
557
0
  switch (wDayOfWeek)
558
0
  {
559
0
    case 0:
560
0
      return "SUNDAY";
561
0
    case 1:
562
0
      return "MONDAY";
563
0
    case 2:
564
0
      return "TUESDAY";
565
0
    case 3:
566
0
      return "WEDNESDAY";
567
0
    case 4:
568
0
      return "THURSDAY";
569
0
    case 5:
570
0
      return "FRIDAY";
571
0
    case 6:
572
0
      return "SATURDAY";
573
0
    default:
574
0
      return "DAY-OF-MAGIC";
575
0
  }
576
0
}
577
578
static char* systemtime2str(const SYSTEMTIME* t, char* buffer, size_t len)
579
211k
{
580
211k
  const SYSTEMTIME empty = { 0 };
581
582
211k
  if (memcmp(t, &empty, sizeof(SYSTEMTIME)) == 0)
583
211k
    (void)_snprintf(buffer, len, "{ not set }");
584
0
  else
585
0
  {
586
0
    (void)_snprintf(buffer, len,
587
0
                    "{ %" PRIu16 "-%" PRIu16 "-%" PRIu16 " [%s] %" PRIu16 ":%" PRIu16
588
0
                    ":%" PRIu16 ".%" PRIu16 "}",
589
0
                    t->wYear, t->wMonth, t->wDay, weekday2str(t->wDayOfWeek), t->wHour,
590
0
                    t->wMinute, t->wSecond, t->wMilliseconds);
591
0
  }
592
211k
  return buffer;
593
211k
}
594
595
static void log_print(wLog* log, DWORD level, const char* file, const char* fkt, size_t line, ...)
596
1.26M
{
597
1.26M
  if (!WLog_IsLevelActive(log, level))
598
1.26M
    return;
599
600
0
  va_list ap = { 0 };
601
0
  va_start(ap, line);
602
0
  WLog_PrintMessageVA(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, ap);
603
0
  va_end(ap);
604
0
}
605
606
105k
#define log_timezone(tzif, result) log_timezone_((tzif), (result), __FILE__, __func__, __LINE__)
607
static void log_timezone_(const DYNAMIC_TIME_ZONE_INFORMATION* tzif, DWORD result, const char* file,
608
                          const char* fkt, size_t line)
609
105k
{
610
105k
  WINPR_ASSERT(tzif);
611
612
105k
  char buffer[130] = { 0 };
613
105k
  DWORD level = WLOG_TRACE;
614
105k
  wLog* log = WLog_Get(TAG);
615
105k
  log_print(log, level, file, fkt, line, "DYNAMIC_TIME_ZONE_INFORMATION {");
616
617
105k
  log_print(log, level, file, fkt, line, "  Bias=%" PRIu32, tzif->Bias);
618
105k
  (void)ConvertWCharNToUtf8(tzif->StandardName, ARRAYSIZE(tzif->StandardName), buffer,
619
105k
                            ARRAYSIZE(buffer));
620
105k
  log_print(log, level, file, fkt, line, "  StandardName=%s", buffer);
621
105k
  log_print(log, level, file, fkt, line, "  StandardDate=%s",
622
105k
            systemtime2str(&tzif->StandardDate, buffer, sizeof(buffer)));
623
105k
  log_print(log, level, file, fkt, line, "  StandardBias=%" PRIu32, tzif->StandardBias);
624
625
105k
  (void)ConvertWCharNToUtf8(tzif->DaylightName, ARRAYSIZE(tzif->DaylightName), buffer,
626
105k
                            ARRAYSIZE(buffer));
627
105k
  log_print(log, level, file, fkt, line, "  DaylightName=%s", buffer);
628
105k
  log_print(log, level, file, fkt, line, "  DaylightDate=%s",
629
105k
            systemtime2str(&tzif->DaylightDate, buffer, sizeof(buffer)));
630
105k
  log_print(log, level, file, fkt, line, "  DaylightBias=%" PRIu32, tzif->DaylightBias);
631
105k
  (void)ConvertWCharNToUtf8(tzif->TimeZoneKeyName, ARRAYSIZE(tzif->TimeZoneKeyName), buffer,
632
105k
                            ARRAYSIZE(buffer));
633
105k
  log_print(log, level, file, fkt, line, "  TimeZoneKeyName=%s", buffer);
634
105k
  log_print(log, level, file, fkt, line, "  DynamicDaylightTimeDisabled=DST-%s",
635
105k
            tzif->DynamicDaylightTimeDisabled ? "disabled" : "enabled");
636
105k
  switch (result)
637
105k
  {
638
0
    case TIME_ZONE_ID_DAYLIGHT:
639
0
      log_print(log, level, file, fkt, line, "  DaylightDate in use");
640
0
      break;
641
105k
    case TIME_ZONE_ID_STANDARD:
642
105k
      log_print(log, level, file, fkt, line, "  StandardDate in use");
643
105k
      break;
644
0
    default:
645
0
      log_print(log, level, file, fkt, line, "  UnknownDate in use");
646
0
      break;
647
105k
  }
648
105k
  log_print(log, level, file, fkt, line, "}");
649
105k
}
650
651
DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
652
52.7k
{
653
52.7k
  DYNAMIC_TIME_ZONE_INFORMATION dyn = { 0 };
654
52.7k
  DWORD rc = GetDynamicTimeZoneInformation(&dyn);
655
52.7k
  lpTimeZoneInformation->Bias = dyn.Bias;
656
52.7k
  lpTimeZoneInformation->DaylightBias = dyn.DaylightBias;
657
52.7k
  lpTimeZoneInformation->DaylightDate = dyn.DaylightDate;
658
52.7k
  lpTimeZoneInformation->StandardBias = dyn.StandardBias;
659
52.7k
  lpTimeZoneInformation->StandardDate = dyn.StandardDate;
660
52.7k
  memcpy(lpTimeZoneInformation->StandardName, dyn.StandardName,
661
52.7k
         sizeof(lpTimeZoneInformation->StandardName));
662
52.7k
  memcpy(lpTimeZoneInformation->DaylightName, dyn.DaylightName,
663
52.7k
         sizeof(lpTimeZoneInformation->DaylightName));
664
52.7k
  return rc;
665
52.7k
}
666
667
BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation)
668
0
{
669
0
  WINPR_UNUSED(lpTimeZoneInformation);
670
0
  return FALSE;
671
0
}
672
673
BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime)
674
0
{
675
0
  WINPR_UNUSED(lpSystemTime);
676
0
  WINPR_UNUSED(lpFileTime);
677
0
  return FALSE;
678
0
}
679
680
BOOL FileTimeToSystemTime(const FILETIME* lpFileTime, LPSYSTEMTIME lpSystemTime)
681
0
{
682
0
  WINPR_UNUSED(lpFileTime);
683
0
  WINPR_UNUSED(lpSystemTime);
684
0
  return FALSE;
685
0
}
686
687
BOOL SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTimeZone,
688
                                     LPSYSTEMTIME lpUniversalTime, LPSYSTEMTIME lpLocalTime)
689
0
{
690
0
  WINPR_UNUSED(lpTimeZone);
691
0
  WINPR_UNUSED(lpUniversalTime);
692
0
  WINPR_UNUSED(lpLocalTime);
693
0
  return FALSE;
694
0
}
695
696
BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
697
                                     LPSYSTEMTIME lpLocalTime, LPSYSTEMTIME lpUniversalTime)
698
0
{
699
0
  WINPR_UNUSED(lpTimeZoneInformation);
700
0
  WINPR_UNUSED(lpLocalTime);
701
0
  WINPR_UNUSED(lpUniversalTime);
702
0
  return FALSE;
703
0
}
704
705
#endif
706
707
/*
708
 * GetDynamicTimeZoneInformation is provided by the SDK if _WIN32_WINNT >= 0x0600 in SDKs above 7.1A
709
 * and incorrectly if _WIN32_WINNT >= 0x0501 in older SDKs
710
 */
711
#if !defined(_WIN32) ||                                                  \
712
    (defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \
713
                         !defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */
714
715
typedef enum
716
{
717
  HAVE_TRANSITION_DATES = 0,
718
  HAVE_NO_STANDARD_TRANSITION_DATE = 1,
719
  HAVE_NO_DAYLIGHT_TRANSITION_DATE = 2
720
} dyn_transition_result;
721
722
static int dynamic_time_zone_from_localtime(const struct tm* local_time,
723
                                            PDYNAMIC_TIME_ZONE_INFORMATION tz)
724
105k
{
725
105k
  WINPR_ASSERT(local_time);
726
105k
  WINPR_ASSERT(tz);
727
105k
  int rc = HAVE_TRANSITION_DATES;
728
729
105k
  tz->Bias = get_bias(local_time, FALSE);
730
731
  /* If the current time has (or had) DST */
732
105k
  if (local_time->tm_isdst >= 0)
733
105k
  {
734
    /* DST bias is the difference between standard time and DST in minutes */
735
105k
    const LONG d = get_bias(local_time, TRUE);
736
105k
    tz->DaylightBias = -1 * (tz->Bias - d);
737
105k
    if (!get_transition_date(local_time, FALSE, &tz->StandardDate))
738
105k
    {
739
105k
      rc |= HAVE_NO_STANDARD_TRANSITION_DATE;
740
105k
      tz->StandardBias = 0;
741
105k
    }
742
105k
    if (!get_transition_date(local_time, TRUE, &tz->DaylightDate))
743
105k
    {
744
105k
      rc |= HAVE_NO_DAYLIGHT_TRANSITION_DATE;
745
105k
      tz->DaylightBias = 0;
746
105k
    }
747
105k
  }
748
105k
  return rc;
749
105k
}
750
751
DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation)
752
105k
{
753
105k
  const char** list = NULL;
754
105k
  char* tzid = NULL;
755
105k
  const char* defaultName = "Client Local Time";
756
105k
  DWORD res = TIME_ZONE_ID_UNKNOWN;
757
105k
  const DYNAMIC_TIME_ZONE_INFORMATION empty = { 0 };
758
759
105k
  WINPR_ASSERT(pTimeZoneInformation);
760
761
105k
  *pTimeZoneInformation = empty;
762
105k
  (void)ConvertUtf8ToWChar(defaultName, pTimeZoneInformation->StandardName,
763
105k
                           ARRAYSIZE(pTimeZoneInformation->StandardName));
764
765
105k
  const time_t t = time(NULL);
766
105k
  struct tm tres = { 0 };
767
105k
  struct tm* local_time = localtime_r(&t, &tres);
768
105k
  if (!local_time)
769
0
    goto out_error;
770
771
105k
  pTimeZoneInformation->Bias = get_bias(local_time, FALSE);
772
105k
  if (local_time->tm_isdst >= 0)
773
105k
    dynamic_time_zone_from_localtime(local_time, pTimeZoneInformation);
774
775
105k
  tzid = winpr_guess_time_zone();
776
105k
  if (!map_iana_id(tzid, pTimeZoneInformation))
777
0
  {
778
0
    const size_t len = TimeZoneIanaAbbrevGet(local_time->tm_zone, NULL, 0);
779
0
    list = (const char**)calloc(len, sizeof(const char*));
780
0
    if (!list)
781
0
      goto out_error;
782
0
    const size_t size = TimeZoneIanaAbbrevGet(local_time->tm_zone, list, len);
783
0
    for (size_t x = 0; x < size; x++)
784
0
    {
785
0
      const char* id = list[x];
786
0
      if (map_iana_id(id, pTimeZoneInformation))
787
0
      {
788
0
        res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
789
0
        break;
790
0
      }
791
0
    }
792
0
  }
793
105k
  else
794
105k
    res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
795
796
105k
out_error:
797
105k
  free(tzid);
798
105k
  free((void*)list);
799
800
105k
  log_timezone(pTimeZoneInformation, res);
801
105k
  return res;
802
105k
}
803
804
BOOL SetDynamicTimeZoneInformation(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation)
805
0
{
806
0
  WINPR_UNUSED(lpTimeZoneInformation);
807
0
  return FALSE;
808
0
}
809
810
BOOL GetTimeZoneInformationForYear(USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi,
811
                                   LPTIME_ZONE_INFORMATION ptzi)
812
0
{
813
0
  WINPR_UNUSED(wYear);
814
0
  WINPR_UNUSED(pdtzi);
815
0
  WINPR_UNUSED(ptzi);
816
0
  return FALSE;
817
0
}
818
819
#endif
820
821
#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0601)) /* Windows 7 */
822
823
BOOL SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation,
824
                                       const SYSTEMTIME* lpUniversalTime, LPSYSTEMTIME lpLocalTime)
825
0
{
826
0
  WINPR_UNUSED(lpTimeZoneInformation);
827
0
  WINPR_UNUSED(lpUniversalTime);
828
0
  WINPR_UNUSED(lpLocalTime);
829
0
  return FALSE;
830
0
}
831
832
BOOL TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation,
833
                                       const SYSTEMTIME* lpLocalTime, LPSYSTEMTIME lpUniversalTime)
834
0
{
835
0
  WINPR_UNUSED(lpTimeZoneInformation);
836
0
  WINPR_UNUSED(lpLocalTime);
837
0
  WINPR_UNUSED(lpUniversalTime);
838
0
  return FALSE;
839
0
}
840
841
#endif
842
843
#if !defined(_WIN32)
844
845
DWORD EnumDynamicTimeZoneInformation(DWORD dwIndex,
846
                                     PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation)
847
0
{
848
0
  if (!lpTimeZoneInformation)
849
0
    return ERROR_INVALID_PARAMETER;
850
0
  const DYNAMIC_TIME_ZONE_INFORMATION empty = { 0 };
851
0
  *lpTimeZoneInformation = empty;
852
853
0
  const TimeZoneNameMapEntry* entry = TimeZoneGetAt(dwIndex);
854
0
  if (!entry)
855
0
    return ERROR_NO_MORE_ITEMS;
856
857
0
  if (entry->DaylightName)
858
0
    (void)ConvertUtf8ToWChar(entry->DaylightName, lpTimeZoneInformation->DaylightName,
859
0
                             ARRAYSIZE(lpTimeZoneInformation->DaylightName));
860
0
  if (entry->StandardName)
861
0
    (void)ConvertUtf8ToWChar(entry->StandardName, lpTimeZoneInformation->StandardName,
862
0
                             ARRAYSIZE(lpTimeZoneInformation->StandardName));
863
0
  if (entry->Id)
864
0
    (void)ConvertUtf8ToWChar(entry->Id, lpTimeZoneInformation->TimeZoneKeyName,
865
0
                             ARRAYSIZE(lpTimeZoneInformation->TimeZoneKeyName));
866
867
0
  const time_t t = time(NULL);
868
0
  struct tm tres = { 0 };
869
870
0
  char* tzcopy = entry->Iana ? setNewAndSaveOldTZ(entry->Iana) : NULL;
871
872
0
  struct tm* local_time = localtime_r(&t, &tres);
873
874
0
  if (local_time)
875
0
    dynamic_time_zone_from_localtime(local_time, lpTimeZoneInformation);
876
877
0
  if (entry->Iana)
878
0
    restoreSavedTZ(tzcopy);
879
880
0
  return ERROR_SUCCESS;
881
0
}
882
883
// NOLINTBEGIN(readability-non-const-parameter)
884
DWORD GetDynamicTimeZoneInformationEffectiveYears(
885
    const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear)
886
// NOLINTEND(readability-non-const-parameter)
887
0
{
888
0
  WINPR_UNUSED(lpTimeZoneInformation);
889
0
  WINPR_UNUSED(FirstYear);
890
0
  WINPR_UNUSED(LastYear);
891
0
  return ERROR_FILE_NOT_FOUND;
892
0
}
893
894
#elif _WIN32_WINNT < 0x0602 /* Windows 8 */
895
DWORD EnumDynamicTimeZoneInformation(DWORD dwIndex,
896
                                     PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation)
897
{
898
  WINPR_UNUSED(dwIndex);
899
  WINPR_UNUSED(lpTimeZoneInformation);
900
  return ERROR_NO_MORE_ITEMS;
901
}
902
903
DWORD GetDynamicTimeZoneInformationEffectiveYears(
904
    const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear)
905
{
906
  WINPR_UNUSED(lpTimeZoneInformation);
907
  WINPR_UNUSED(FirstYear);
908
  WINPR_UNUSED(LastYear);
909
  return ERROR_FILE_NOT_FOUND;
910
}
911
#endif
912
913
#if !defined(_WIN32)
914
char* setNewAndSaveOldTZ(const char* val)
915
0
{
916
  // NOLINTBEGIN(concurrency-mt-unsafe)
917
0
  const char* otz = getenv("TZ");
918
0
  char* oldtz = NULL;
919
0
  if (otz)
920
0
    oldtz = _strdup(otz);
921
0
  setenv("TZ", val, 1);
922
0
  tzset();
923
  // NOLINTEND(concurrency-mt-unsafe)
924
0
  return oldtz;
925
0
}
926
927
void restoreSavedTZ(char* saved)
928
0
{
929
  // NOLINTBEGIN(concurrency-mt-unsafe)
930
0
  if (saved)
931
0
  {
932
0
    setenv("TZ", saved, 1);
933
0
    free(saved);
934
0
  }
935
0
  else
936
0
    unsetenv("TZ");
937
0
  tzset();
938
  // NOLINTEND(concurrency-mt-unsafe)
939
0
}
940
#endif