FreeRDP
Loading...
Searching...
No Matches
TimeZoneIanaAliases.c
1
19#include <winpr/config.h>
20
21#include <winpr/atexit.h>
22#include <winpr/assert.h>
23#include <winpr/string.h>
24#include <winpr/synch.h>
25#include <winpr/json.h>
26#include <winpr/path.h>
27#include <winpr/file.h>
28
29#include "../log.h"
30
31#include <string.h>
32
33#define TAG WINPR_TAG("timezone.aliases")
34
35#include "TimeZoneNameMap.h"
36#include "TimeZoneIanaAliases.h"
37
38#if defined(WITH_TIMEZONE_COMPILED)
39#include "TimeZoneIanaAliases_static.h"
40#endif
41
42typedef struct
43{
44 size_t count;
45 size_t size;
47} TimeZoneIanaAliasContext;
48
49static TimeZoneIanaAliasContext alias_context = WINPR_C_ARRAY_INIT;
50
51static void alias_context_free(void)
52{
53 for (size_t x = 0; x < alias_context.count; x++)
54 {
55 free(alias_context.entries[x].Current);
56 free(alias_context.entries[x].Legacy);
57 }
58 free(alias_context.entries);
59
60 const TimeZoneIanaAliasContext empty = WINPR_C_ARRAY_INIT;
61 alias_context = empty;
62}
63
64WINPR_ATTR_NODISCARD
65static BOOL alias_reserve(TimeZoneIanaAliasContext* context, size_t size_to_add)
66{
67 WINPR_ASSERT(context);
68
70 realloc(context->entries, (context->size + size_to_add) * sizeof(TimeZoneIanaAliasEntry));
71 if (!tmp)
72 {
73 WLog_WARN(TAG,
74 "Failed to reallocate TimeZoneIanaAliasEntry::entries to %" PRIuz " elements",
75 context->size + size_to_add);
76 return FALSE;
77 }
78 context->entries = tmp;
79 context->size += size_to_add;
80 return TRUE;
81}
82
83/* takes ownership of current and legacy, source and pos name the entry in log messages */
84WINPR_ATTR_NODISCARD
85static BOOL alias_add(TimeZoneIanaAliasContext* context, const char* source, size_t pos,
86 char* current, char* legacy)
87{
88 WINPR_ASSERT(context);
89 WINPR_ASSERT(source);
90 WINPR_ASSERT(context->count < context->size);
91
92 if (!current || !legacy)
93 {
94 WLog_WARN(TAG, "Invalid timezone alias at entry %" PRIuz " of %s", pos, source);
95 free(current);
96 free(legacy);
97 return FALSE;
98 }
99
100 context->entries[context->count].Current = current;
101 context->entries[context->count].Legacy = legacy;
102 context->count++;
103 return TRUE;
104}
105
106#if defined(WITH_TIMEZONE_FROM_FILE) && defined(WITH_WINPR_JSON)
107static void load_aliases_from_file(TimeZoneIanaAliasContext* context)
108{
109 WINPR_JSON* json = nullptr;
110 char* filename = GetCombinedPath(WINPR_RESOURCE_ROOT, "TimeZoneIanaAliases.json");
111 if (!filename)
112 {
113 WLog_WARN(TAG, "Could not create WinPR timezone alias resource filename");
114 goto end;
115 }
116
117 json = TimeZoneJsonParseFile(filename);
118 if (!json)
119 goto end;
120
121 WINPR_JSON* obj = WINPR_JSON_IsObject(json)
122 ? WINPR_JSON_GetObjectItemCaseSensitive(json, "TimeZoneIanaAliases")
123 : nullptr;
124 if (!WINPR_JSON_IsArray(obj))
125 {
126 WLog_WARN(TAG, "Invalid top level JSON type in file %s, expected an array", filename);
127 goto end;
128 }
129
130 const size_t count = WINPR_JSON_GetArraySize(obj);
131 if (!alias_reserve(context, count))
132 goto end;
133 for (size_t x = 0; x < count; x++)
134 {
135 WINPR_JSON* entry = WINPR_JSON_GetArrayItem(obj, x);
136 if (!alias_add(context, filename, x, TimeZoneJsonGetString(entry, x, "Current"),
137 TimeZoneJsonGetString(entry, x, "Legacy")))
138 goto end;
139 }
140
141end:
142 free(filename);
143 WINPR_JSON_Delete(json);
144}
145#endif
146
147static BOOL CALLBACK load_aliases(PINIT_ONCE once, PVOID param, PVOID* pvcontext)
148{
149 TimeZoneIanaAliasContext* context = param;
150 WINPR_ASSERT(context);
151 WINPR_UNUSED(pvcontext);
152 WINPR_UNUSED(once);
153
154#if defined(WITH_TIMEZONE_FROM_FILE) && defined(WITH_WINPR_JSON)
155 load_aliases_from_file(context);
156#endif
157
158#if defined(WITH_TIMEZONE_COMPILED)
159 if (!alias_reserve(context, TimeZoneIanaAliasesSize))
160 {
161 alias_context_free();
162 return FALSE;
163 }
164 for (size_t x = 0; x < TimeZoneIanaAliasesSize; x++)
165 {
166 if (!alias_add(context, "the compiled in table", x, _strdup(TimeZoneIanaAliases[x].Current),
167 _strdup(TimeZoneIanaAliases[x].Legacy)))
168 {
169 alias_context_free();
170 return FALSE;
171 }
172 }
173#endif
174
175 (void)winpr_atexit(alias_context_free);
176 return TRUE;
177}
178
179const char* TimeZoneIanaAlias(const char* iana)
180{
181 static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT;
182
183 if (!iana)
184 return nullptr;
185 if (!InitOnceExecuteOnce(&init_guard, load_aliases, &alias_context, nullptr))
186 return nullptr;
187
188 for (size_t x = 0; x < alias_context.count; x++)
189 {
190 const TimeZoneIanaAliasEntry* alias = &alias_context.entries[x];
191 if (strcmp(iana, alias->Current) == 0)
192 return alias->Legacy;
193 if (strcmp(iana, alias->Legacy) == 0)
194 return alias->Current;
195 }
196 return nullptr;
197}
WINPR_ATTR_NODISCARD WINPR_API size_t WINPR_JSON_GetArraySize(const WINPR_JSON *array)
Get the number of arrayitems from an array.
Definition c-json.c:114
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON *object, const char *string)
Same as WINPR_JSON_GetObjectItem but with case sensitive matching.
Definition c-json.c:127
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetArrayItem(const WINPR_JSON *array, size_t index)
Return a pointer to an item in the array.
Definition c-json.c:108
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsArray(const WINPR_JSON *item)
Check if JSON item is of type Array.
Definition c-json.c:187
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsObject(const WINPR_JSON *item)
Check if JSON item is of type Object.
Definition c-json.c:192
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition c-json.c:103