Coverage Report

Created: 2024-05-20 06:11

/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
31
typedef struct
32
{
33
  const char* Iana;
34
  const char* Abbrev;
35
} TimeZoneInanaAbbrevMapEntry;
36
37
const static char* zonepath = "/usr/share/zoneinfo";
38
39
static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = NULL;
40
static size_t TimeZoneIanaAbbrevMapSize = 0;
41
42
static void append(const char* iana, const char* sname)
43
0
{
44
0
  const size_t size = TimeZoneIanaAbbrevMapSize + 1;
45
46
0
  TimeZoneInanaAbbrevMapEntry* tmp =
47
0
      realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
48
0
  if (!tmp)
49
0
    return;
50
0
  TimeZoneIanaAbbrevMap = tmp;
51
0
  TimeZoneIanaAbbrevMapSize = size;
52
53
0
  TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
54
0
  cur->Abbrev = _strdup(sname);
55
0
  cur->Iana = _strdup(iana);
56
0
}
57
58
static void append_timezone(const char* dir, const char* name)
59
0
{
60
0
  char* tz = NULL;
61
0
  if (!dir && !name)
62
0
    return;
63
0
  if (!dir)
64
0
  {
65
0
    size_t len = 0;
66
0
    winpr_asprintf(&tz, &len, "%s", name);
67
0
  }
68
0
  else
69
0
  {
70
0
    size_t len = 0;
71
0
    winpr_asprintf(&tz, &len, "%s/%s", dir, name);
72
0
  }
73
0
  if (!tz)
74
0
    return;
75
76
0
  const char* otz = getenv("TZ");
77
0
  char* oldtz = NULL;
78
0
  if (otz)
79
0
    oldtz = _strdup(otz);
80
0
  setenv("TZ", tz, 1);
81
0
  tzset();
82
0
  const time_t t = time(NULL);
83
0
  struct tm lt = { 0 };
84
0
  localtime_r(&t, &lt);
85
0
  append(tz, lt.tm_zone);
86
0
  if (oldtz)
87
0
  {
88
0
    setenv("TZ", oldtz, 1);
89
0
    free(oldtz);
90
0
  }
91
0
  else
92
0
    unsetenv("TZ");
93
0
  free(tz);
94
0
}
95
96
static void handle_link(const char* base, const char* dir, const char* name);
97
98
static char* topath(const char* base, const char* bname, const char* name)
99
0
{
100
0
  size_t plen = 0;
101
0
  char* path = NULL;
102
103
0
  if (!base && !bname && !name)
104
0
    return NULL;
105
106
0
  if (!base && !name)
107
0
    return _strdup(bname);
108
109
0
  if (!bname && !name)
110
0
    return _strdup(base);
111
112
0
  if (!base && !bname)
113
0
    return _strdup(name);
114
115
0
  if (!base)
116
0
    winpr_asprintf(&path, &plen, "%s/%s", bname, name);
117
0
  else if (!bname)
118
0
    winpr_asprintf(&path, &plen, "%s/%s", base, name);
119
0
  else if (!name)
120
0
    winpr_asprintf(&path, &plen, "%s/%s", base, bname);
121
0
  else
122
0
    winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
123
0
  return path;
124
0
}
125
126
static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
127
0
{
128
0
  char* path = topath(base, bname, name);
129
0
  if (!path)
130
0
    return;
131
132
0
  DIR* d = opendir(path);
133
0
  if (d)
134
0
  {
135
0
    struct dirent* dp = NULL;
136
0
    while ((dp = readdir(d)) != NULL)
137
0
    {
138
0
      switch (dp->d_type)
139
0
      {
140
0
        case DT_DIR:
141
0
        {
142
0
          if (strcmp(dp->d_name, ".") == 0)
143
0
            continue;
144
0
          if (strcmp(dp->d_name, "..") == 0)
145
0
            continue;
146
0
          iterate_subdir_recursive(path, dp->d_name, NULL);
147
0
        }
148
0
        break;
149
0
        case DT_LNK:
150
0
          handle_link(base, bname, dp->d_name);
151
0
          break;
152
0
        case DT_REG:
153
0
          append_timezone(bname, dp->d_name);
154
0
          break;
155
0
        default:
156
0
          break;
157
0
      }
158
0
    }
159
0
    closedir(d);
160
0
  }
161
0
  free(path);
162
0
}
163
164
static char* get_link_target(const char* base, const char* dir, const char* name)
165
0
{
166
0
  char* apath = NULL;
167
0
  char* path = topath(base, dir, name);
168
0
  if (!path)
169
0
    return NULL;
170
171
0
  SSIZE_T rc = -1;
172
0
  size_t size = 0;
173
0
  char* target = NULL;
174
0
  do
175
0
  {
176
0
    size += 64;
177
0
    char* tmp = realloc(target, size + 1);
178
0
    if (!tmp)
179
0
      goto fail;
180
181
0
    target = tmp;
182
183
0
    memset(target, 0, size + 1);
184
0
    rc = readlink(path, target, size);
185
0
    if (rc < 0)
186
0
      goto fail;
187
0
  } while (rc >= size);
188
189
0
  apath = topath(base, dir, target);
190
0
fail:
191
0
  free(target);
192
0
  free(path);
193
0
  return apath;
194
0
}
195
196
void handle_link(const char* base, const char* dir, const char* name)
197
0
{
198
0
  int isDir = -1;
199
200
0
  char* target = get_link_target(base, dir, name);
201
0
  if (target)
202
0
  {
203
0
    struct stat s = { 0 };
204
0
    const int rc3 = stat(target, &s);
205
0
    if (rc3 == 0)
206
0
      isDir = S_ISDIR(s.st_mode);
207
208
0
    free(target);
209
0
  }
210
211
0
  switch (isDir)
212
0
  {
213
0
    case 1:
214
0
      iterate_subdir_recursive(base, dir, name);
215
0
      break;
216
0
    case 0:
217
0
      append_timezone(dir, name);
218
0
      break;
219
0
    default:
220
0
      break;
221
0
  }
222
0
}
223
224
static void TimeZoneIanaAbbrevCleanup(void)
225
0
{
226
0
  if (!TimeZoneIanaAbbrevMap)
227
0
    return;
228
229
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
230
0
  {
231
0
    TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
232
0
    free(entry->Iana);
233
0
    free(entry->Abbrev);
234
0
  }
235
0
  free(TimeZoneIanaAbbrevMap);
236
0
  TimeZoneIanaAbbrevMap = NULL;
237
0
  TimeZoneIanaAbbrevMapSize = 0;
238
0
}
239
240
static void TimeZoneIanaAbbrevInitialize(void)
241
0
{
242
0
  static BOOL initialized = FALSE;
243
0
  if (initialized)
244
0
    return;
245
246
0
  iterate_subdir_recursive(zonepath, NULL, NULL);
247
0
  (void)atexit(TimeZoneIanaAbbrevCleanup);
248
0
  initialized = TRUE;
249
0
}
250
251
size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
252
0
{
253
0
  TimeZoneIanaAbbrevInitialize();
254
255
0
  size_t rc = 0;
256
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
257
0
  {
258
0
    const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
259
0
    if (strcmp(abbrev, entry->Abbrev) == 0)
260
0
    {
261
0
      if (listsize > rc)
262
0
        list[rc] = entry->Iana;
263
0
      rc++;
264
0
    }
265
0
  }
266
267
0
  return rc;
268
0
}