xref: /reactos/sdk/lib/tzlib/tzlib.c (revision 9393fc32)
1 /*
2  * PROJECT:     ReactOS TimeZone Utilities Library
3  * LICENSE:     GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
4  * PURPOSE:     Provides time-zone utility wrappers around Win32 functions,
5  *              that are used by different ReactOS modules such as
6  *              timedate.cpl, syssetup.dll.
7  * COPYRIGHT:   Copyright 2004-2005 Eric Kohl
8  *              Copyright 2016 Carlo Bramini
9  *              Copyright 2020 Hermes Belusca-Maito
10  */
11 
12 #include <stdlib.h>
13 #include <windef.h>
14 #include <winbase.h>
15 #include <winreg.h>
16 
17 #include "tzlib.h"
18 
19 BOOL
20 GetTimeZoneListIndex(
21     IN OUT PULONG pIndex)
22 {
23     LONG lError;
24     HKEY hKey;
25     DWORD dwType;
26     DWORD dwValueSize;
27     DWORD Length;
28     LPWSTR Buffer;
29     LPWSTR Ptr, End;
30     BOOL bFound = FALSE;
31     unsigned long iLanguageID;
32     WCHAR szLanguageIdString[9];
33 
34     if (*pIndex == -1)
35     {
36         *pIndex = 85; /* fallback to GMT time zone */
37 
38         lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
39                                L"SYSTEM\\CurrentControlSet\\Control\\NLS\\Language",
40                                0,
41                                KEY_QUERY_VALUE,
42                                &hKey);
43         if (lError != ERROR_SUCCESS)
44         {
45             return FALSE;
46         }
47 
48         dwValueSize = sizeof(szLanguageIdString);
49         lError = RegQueryValueExW(hKey,
50                                   L"Default",
51                                   NULL,
52                                   NULL,
53                                   (LPBYTE)szLanguageIdString,
54                                   &dwValueSize);
55         if (lError != ERROR_SUCCESS)
56         {
57             RegCloseKey(hKey);
58             return FALSE;
59         }
60 
61         iLanguageID = wcstoul(szLanguageIdString, NULL, 16);
62         RegCloseKey(hKey);
63     }
64     else
65     {
66         iLanguageID = *pIndex;
67     }
68 
69     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
70                            L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
71                            0,
72                            KEY_QUERY_VALUE,
73                            &hKey);
74     if (lError != ERROR_SUCCESS)
75     {
76         return FALSE;
77     }
78 
79     dwValueSize = 0;
80     lError = RegQueryValueExW(hKey,
81                               L"IndexMapping",
82                               NULL,
83                               &dwType,
84                               NULL,
85                               &dwValueSize);
86     if ((lError != ERROR_SUCCESS) || (dwType != REG_MULTI_SZ))
87     {
88         RegCloseKey(hKey);
89         return FALSE;
90     }
91 
92     Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwValueSize);
93     if (Buffer == NULL)
94     {
95         RegCloseKey(hKey);
96         return FALSE;
97     }
98 
99     lError = RegQueryValueExW(hKey,
100                               L"IndexMapping",
101                               NULL,
102                               &dwType,
103                               (LPBYTE)Buffer,
104                               &dwValueSize);
105 
106     RegCloseKey(hKey);
107 
108     if ((lError != ERROR_SUCCESS) || (dwType != REG_MULTI_SZ))
109     {
110         HeapFree(GetProcessHeap(), 0, Buffer);
111         return FALSE;
112     }
113 
114     Ptr = Buffer;
115     while (*Ptr != 0)
116     {
117         Length = wcslen(Ptr);
118         if (wcstoul(Ptr, NULL, 16) == iLanguageID)
119             bFound = TRUE;
120 
121         Ptr = Ptr + Length + 1;
122         if (*Ptr == 0)
123             break;
124 
125         if (bFound)
126         {
127             *pIndex = wcstoul(Ptr, &End, 10);
128             HeapFree(GetProcessHeap(), 0, Buffer);
129             return TRUE;
130         }
131 
132         Length = wcslen(Ptr);
133         Ptr = Ptr + Length + 1;
134     }
135 
136     HeapFree(GetProcessHeap(), 0, Buffer);
137     return FALSE;
138 }
139 
140 LONG
141 QueryTimeZoneData(
142     IN HKEY hZoneKey,
143     OUT PULONG Index OPTIONAL,
144     OUT PREG_TZI_FORMAT TimeZoneInfo,
145     OUT PWCHAR Description OPTIONAL,
146     IN OUT PULONG DescriptionSize OPTIONAL,
147     OUT PWCHAR StandardName OPTIONAL,
148     IN OUT PULONG StandardNameSize OPTIONAL,
149     OUT PWCHAR DaylightName OPTIONAL,
150     IN OUT PULONG DaylightNameSize OPTIONAL)
151 {
152     LONG lError;
153     DWORD dwValueSize;
154 
155     if (Index)
156     {
157         dwValueSize = sizeof(*Index);
158         lError = RegQueryValueExW(hZoneKey,
159                                   L"Index",
160                                   NULL,
161                                   NULL,
162                                   (LPBYTE)Index,
163                                   &dwValueSize);
164         if (lError != ERROR_SUCCESS)
165             *Index = 0;
166     }
167 
168     /* The time zone information structure is mandatory for a valid time zone */
169     dwValueSize = sizeof(*TimeZoneInfo);
170     lError = RegQueryValueExW(hZoneKey,
171                               L"TZI",
172                               NULL,
173                               NULL,
174                               (LPBYTE)TimeZoneInfo,
175                               &dwValueSize);
176     if (lError != ERROR_SUCCESS)
177         return lError;
178 
179     if (Description && DescriptionSize && *DescriptionSize > 0)
180     {
181         lError = RegQueryValueExW(hZoneKey,
182                                   L"Display",
183                                   NULL,
184                                   NULL,
185                                   (LPBYTE)Description,
186                                   DescriptionSize);
187         if (lError != ERROR_SUCCESS)
188             *Description = 0;
189     }
190 
191     if (StandardName && StandardNameSize && *StandardNameSize > 0)
192     {
193         lError = RegQueryValueExW(hZoneKey,
194                                   L"Std",
195                                   NULL,
196                                   NULL,
197                                   (LPBYTE)StandardName,
198                                   StandardNameSize);
199         if (lError != ERROR_SUCCESS)
200             *StandardName = 0;
201     }
202 
203     if (DaylightName && DaylightNameSize && *DaylightNameSize > 0)
204     {
205         lError = RegQueryValueExW(hZoneKey,
206                                   L"Dlt",
207                                   NULL,
208                                   NULL,
209                                   (LPBYTE)DaylightName,
210                                   DaylightNameSize);
211         if (lError != ERROR_SUCCESS)
212             *DaylightName = 0;
213     }
214 
215     return ERROR_SUCCESS;
216 }
217 
218 //
219 // NOTE: Very similar to the EnumDynamicTimeZoneInformation() function
220 // introduced in Windows 8.
221 //
222 VOID
223 EnumerateTimeZoneList(
224     IN PENUM_TIMEZONE_CALLBACK Callback,
225     IN PVOID Context OPTIONAL)
226 {
227     LONG lError;
228     HKEY hZonesKey;
229     HKEY hZoneKey;
230     DWORD dwIndex;
231     DWORD dwNameSize;
232     WCHAR szKeyName[256];
233 
234     /* Open the registry key containing the list of time zones */
235     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
236                            L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
237                            0,
238                            KEY_ENUMERATE_SUB_KEYS,
239                            &hZonesKey);
240     if (lError != ERROR_SUCCESS)
241         return;
242 
243     /* Enumerate it */
244     for (dwIndex = 0; ; dwIndex++)
245     {
246         dwNameSize = sizeof(szKeyName);
247         lError = RegEnumKeyExW(hZonesKey,
248                                dwIndex,
249                                szKeyName,
250                                &dwNameSize,
251                                NULL,
252                                NULL,
253                                NULL,
254                                NULL);
255         // if (lError != ERROR_SUCCESS && lError != ERROR_MORE_DATA)
256         if (lError == ERROR_NO_MORE_ITEMS)
257             break;
258 
259         /* Open the time zone sub-key */
260         if (RegOpenKeyExW(hZonesKey,
261                           szKeyName,
262                           0,
263                           KEY_QUERY_VALUE,
264                           &hZoneKey))
265         {
266             /* We failed, continue with another sub-key */
267             continue;
268         }
269 
270         /* Call the user-provided callback */
271         lError = Callback(hZoneKey, Context);
272         // lError = QueryTimeZoneData(hZoneKey, Context);
273 
274         RegCloseKey(hZoneKey);
275     }
276 
277     RegCloseKey(hZonesKey);
278 }
279 
280 // Returns TRUE if AutoDaylight is ON.
281 // Returns FALSE if AutoDaylight is OFF.
282 BOOL
283 GetAutoDaylight(VOID)
284 {
285     LONG lError;
286     HKEY hKey;
287     DWORD dwType;
288     DWORD dwDisabled;
289     DWORD dwValueSize;
290 
291     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
292                            L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
293                            0,
294                            KEY_QUERY_VALUE,
295                            &hKey);
296     if (lError != ERROR_SUCCESS)
297         return FALSE;
298 
299     // NOTE: On Vista+: REG_DWORD "DynamicDaylightTimeDisabled"
300     dwValueSize = sizeof(dwDisabled);
301     lError = RegQueryValueExW(hKey,
302                               L"DisableAutoDaylightTimeSet",
303                               NULL,
304                               &dwType,
305                               (LPBYTE)&dwDisabled,
306                               &dwValueSize);
307 
308     RegCloseKey(hKey);
309 
310     if ((lError != ERROR_SUCCESS) || (dwType != REG_DWORD) || (dwValueSize != sizeof(dwDisabled)))
311     {
312         /*
313          * The call failed (non zero) because the registry value isn't available,
314          * which means auto-daylight shouldn't be disabled.
315          */
316         dwDisabled = FALSE;
317     }
318 
319     return !dwDisabled;
320 }
321 
322 VOID
323 SetAutoDaylight(
324     IN BOOL EnableAutoDaylightTime)
325 {
326     LONG lError;
327     HKEY hKey;
328     DWORD dwDisabled = TRUE;
329 
330     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
331                            L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
332                            0,
333                            KEY_SET_VALUE,
334                            &hKey);
335     if (lError != ERROR_SUCCESS)
336         return;
337 
338     if (!EnableAutoDaylightTime)
339     {
340         /* Auto-Daylight disabled: set the value to TRUE */
341         // NOTE: On Vista+: REG_DWORD "DynamicDaylightTimeDisabled"
342         RegSetValueExW(hKey,
343                        L"DisableAutoDaylightTimeSet",
344                        0,
345                        REG_DWORD,
346                        (LPBYTE)&dwDisabled,
347                        sizeof(dwDisabled));
348     }
349     else
350     {
351         /* Auto-Daylight enabled: just delete the value */
352         RegDeleteValueW(hKey, L"DisableAutoDaylightTimeSet");
353     }
354 
355     RegCloseKey(hKey);
356 }
357