Coverage Report

Created: 2025-08-26 06:31

/src/FreeRDP/winpr/libwinpr/timezone/TimeZoneIanaAbbrevMap.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Time Zone
4
 *
5
 * Copyright 2024 Armin Novak <anovak@thincast.com>
6
 * Copyright 2024 Thincast Technologies GmbH
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include "TimeZoneIanaAbbrevMap.h"
22
23
#include <stdlib.h>
24
#include <dirent.h>
25
#include <time.h>
26
#include <sys/stat.h>
27
#include <unistd.h>
28
29
#include <winpr/string.h>
30
#include <winpr/synch.h>
31
#include "timezone.h"
32
33
typedef struct
34
{
35
  char* Iana;
36
  char* Abbrev;
37
} TimeZoneInanaAbbrevMapEntry;
38
39
const static char* zonepath = "/usr/share/zoneinfo";
40
41
static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = NULL;
42
static size_t TimeZoneIanaAbbrevMapSize = 0;
43
44
static void append(const char* iana, const char* sname)
45
0
{
46
0
  const size_t size = TimeZoneIanaAbbrevMapSize + 1;
47
48
0
  TimeZoneInanaAbbrevMapEntry* tmp =
49
0
      realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
50
0
  if (!tmp)
51
0
    return;
52
0
  TimeZoneIanaAbbrevMap = tmp;
53
0
  TimeZoneIanaAbbrevMapSize = size;
54
55
0
  TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
56
0
  cur->Abbrev = _strdup(sname);
57
0
  cur->Iana = _strdup(iana);
58
0
}
59
60
static void append_timezone(const char* dir, const char* name)
61
0
{
62
0
  char* tz = NULL;
63
0
  if (!dir && !name)
64
0
    return;
65
0
  if (!dir)
66
0
  {
67
0
    size_t len = 0;
68
0
    winpr_asprintf(&tz, &len, "%s", name);
69
0
  }
70
0
  else
71
0
  {
72
0
    size_t len = 0;
73
0
    winpr_asprintf(&tz, &len, "%s/%s", dir, name);
74
0
  }
75
0
  if (!tz)
76
0
    return;
77
78
0
  char* oldtz = setNewAndSaveOldTZ(tz);
79
80
0
  const time_t t = time(NULL);
81
0
  struct tm lt = { 0 };
82
0
  (void)localtime_r(&t, &lt);
83
0
  append(tz, lt.tm_zone);
84
0
  restoreSavedTZ(oldtz);
85
0
  free(tz);
86
0
}
87
88
static void handle_link(const char* base, const char* dir, const char* name);
89
90
static char* topath(const char* base, const char* bname, const char* name)
91
0
{
92
0
  size_t plen = 0;
93
0
  char* path = NULL;
94
95
0
  if (!base && !bname && !name)
96
0
    return NULL;
97
98
0
  if (!base && !name)
99
0
    return _strdup(bname);
100
101
0
  if (!bname && !name)
102
0
    return _strdup(base);
103
104
0
  if (!base && !bname)
105
0
    return _strdup(name);
106
107
0
  if (!base)
108
0
    winpr_asprintf(&path, &plen, "%s/%s", bname, name);
109
0
  else if (!bname)
110
0
    winpr_asprintf(&path, &plen, "%s/%s", base, name);
111
0
  else if (!name)
112
0
    winpr_asprintf(&path, &plen, "%s/%s", base, bname);
113
0
  else
114
0
    winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
115
0
  return path;
116
0
}
117
118
static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
119
0
{
120
0
  char* path = topath(base, bname, name);
121
0
  if (!path)
122
0
    return;
123
124
0
  DIR* d = opendir(path);
125
0
  if (d)
126
0
  {
127
0
    struct dirent* dp = NULL;
128
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
129
0
    while ((dp = readdir(d)) != NULL)
130
0
    {
131
0
      switch (dp->d_type)
132
0
      {
133
0
        case DT_DIR:
134
0
        {
135
0
          if (strcmp(dp->d_name, ".") == 0)
136
0
            continue;
137
0
          if (strcmp(dp->d_name, "..") == 0)
138
0
            continue;
139
0
          iterate_subdir_recursive(path, dp->d_name, NULL);
140
0
        }
141
0
        break;
142
0
        case DT_LNK:
143
0
          handle_link(base, bname, dp->d_name);
144
0
          break;
145
0
        case DT_REG:
146
0
          append_timezone(bname, dp->d_name);
147
0
          break;
148
0
        default:
149
0
          break;
150
0
      }
151
0
    }
152
0
    closedir(d);
153
0
  }
154
0
  free(path);
155
0
}
156
157
static char* get_link_target(const char* base, const char* dir, const char* name)
158
0
{
159
0
  char* apath = NULL;
160
0
  char* path = topath(base, dir, name);
161
0
  if (!path)
162
0
    return NULL;
163
164
0
  SSIZE_T rc = -1;
165
0
  size_t size = 0;
166
0
  char* target = NULL;
167
0
  do
168
0
  {
169
0
    size += 64;
170
0
    char* tmp = realloc(target, size + 1);
171
0
    if (!tmp)
172
0
      goto fail;
173
174
0
    target = tmp;
175
176
0
    memset(target, 0, size + 1);
177
0
    rc = readlink(path, target, size);
178
0
    if (rc < 0)
179
0
      goto fail;
180
0
  } while ((size_t)rc >= size);
181
182
0
  apath = topath(base, dir, target);
183
0
fail:
184
0
  free(target);
185
0
  free(path);
186
0
  return apath;
187
0
}
188
189
void handle_link(const char* base, const char* dir, const char* name)
190
0
{
191
0
  int isDir = -1;
192
193
0
  char* target = get_link_target(base, dir, name);
194
0
  if (target)
195
0
  {
196
0
    struct stat s = { 0 };
197
0
    const int rc3 = stat(target, &s);
198
0
    if (rc3 == 0)
199
0
      isDir = S_ISDIR(s.st_mode);
200
201
0
    free(target);
202
0
  }
203
204
0
  switch (isDir)
205
0
  {
206
0
    case 1:
207
0
      iterate_subdir_recursive(base, dir, name);
208
0
      break;
209
0
    case 0:
210
0
      append_timezone(dir, name);
211
0
      break;
212
0
    default:
213
0
      break;
214
0
  }
215
0
}
216
217
static void TimeZoneIanaAbbrevCleanup(void)
218
0
{
219
0
  if (!TimeZoneIanaAbbrevMap)
220
0
    return;
221
222
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
223
0
  {
224
0
    TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
225
0
    free(entry->Iana);
226
0
    free(entry->Abbrev);
227
0
  }
228
0
  free(TimeZoneIanaAbbrevMap);
229
0
  TimeZoneIanaAbbrevMap = NULL;
230
0
  TimeZoneIanaAbbrevMapSize = 0;
231
0
}
232
233
static BOOL CALLBACK TimeZoneIanaAbbrevInitialize(WINPR_ATTR_UNUSED PINIT_ONCE once,
234
                                                  WINPR_ATTR_UNUSED PVOID param,
235
                                                  WINPR_ATTR_UNUSED PVOID* context)
236
0
{
237
0
  iterate_subdir_recursive(zonepath, NULL, NULL);
238
0
  (void)atexit(TimeZoneIanaAbbrevCleanup);
239
240
0
  return TRUE;
241
0
}
242
243
size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
244
0
{
245
0
  static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT;
246
247
0
  InitOnceExecuteOnce(&init_guard, TimeZoneIanaAbbrevInitialize, NULL, NULL);
248
249
0
  size_t rc = 0;
250
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
251
0
  {
252
0
    const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
253
0
    if (strcmp(abbrev, entry->Abbrev) == 0)
254
0
    {
255
0
      if (listsize > rc)
256
0
        list[rc] = entry->Iana;
257
0
      rc++;
258
0
    }
259
0
  }
260
261
0
  return rc;
262
0
}