jdk

Форк
0
573 строки · 17.9 Кб
1
/*
2
 * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
 *
5
 * This code is free software; you can redistribute it and/or modify it
6
 * under the terms of the GNU General Public License version 2 only, as
7
 * published by the Free Software Foundation.  Oracle designates this
8
 * particular file as subject to the "Classpath" exception as provided
9
 * by Oracle in the LICENSE file that accompanied this code.
10
 *
11
 * This code is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14
 * version 2 for more details (a copy is included in the LICENSE file that
15
 * accompanied this code).
16
 *
17
 * You should have received a copy of the GNU General Public License version
18
 * 2 along with this work; if not, write to the Free Software Foundation,
19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
 *
21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
 * or visit www.oracle.com if you need additional information or have any
23
 * questions.
24
 */
25

26
#include <windows.h>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include "jvm.h"
30
#include "TimeZone_md.h"
31

32
#define VALUE_UNKNOWN           0
33
#define VALUE_KEY               1
34
#define VALUE_MAPID             2
35
#define VALUE_GMTOFFSET         3
36

37
#define MAX_ZONE_CHAR           256
38
#define MAX_MAPID_LENGTH        32
39
#define MAX_REGION_LENGTH       4
40

41
#define NT_TZ_KEY               "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
42
#define WIN_TZ_KEY              "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"
43
#define WIN_CURRENT_TZ_KEY      "System\\CurrentControlSet\\Control\\TimeZoneInformation"
44

45
typedef struct _TziValue {
46
    LONG        bias;
47
    LONG        stdBias;
48
    LONG        dstBias;
49
    SYSTEMTIME  stdDate;
50
    SYSTEMTIME  dstDate;
51
} TziValue;
52

53
/*
54
 * Registry key names
55
 */
56
static void *keyNames[] = {
57
    (void *) L"StandardName",
58
    (void *) "StandardName",
59
    (void *) L"Std",
60
    (void *) "Std"
61
};
62

63
/*
64
 * Indices to keyNames[]
65
 */
66
#define STANDARD_NAME           0
67
#define STD_NAME                2
68

69
/*
70
 * Calls RegQueryValueEx() to get the value for the specified key. If
71
 * the platform is NT, 2000 or XP, it calls the Unicode
72
 * version. Otherwise, it calls the ANSI version and converts the
73
 * value to Unicode. In this case, it assumes that the current ANSI
74
 * Code Page is the same as the native platform code page (e.g., Code
75
 * Page 932 for the Japanese Windows systems.
76
 *
77
 * `keyIndex' is an index value to the keyNames in Unicode
78
 * (WCHAR). `keyIndex' + 1 points to its ANSI value.
79
 *
80
 * Returns the status value. ERROR_SUCCESS if succeeded, a
81
 * non-ERROR_SUCCESS value otherwise.
82
 */
83
static LONG
84
getValueInRegistry(HKEY hKey,
85
                   int keyIndex,
86
                   LPDWORD typePtr,
87
                   LPBYTE buf,
88
                   LPDWORD bufLengthPtr)
89
{
90
    LONG ret;
91
    DWORD bufLength = *bufLengthPtr;
92
    char val[MAX_ZONE_CHAR];
93
    DWORD valSize;
94
    int len;
95

96
    *typePtr = 0;
97
    ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL,
98
                           typePtr, buf, bufLengthPtr);
99
    if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) {
100
        return ret;
101
    }
102

103
    valSize = sizeof(val);
104
    ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL,
105
                           typePtr, val, &valSize);
106
    if (ret != ERROR_SUCCESS) {
107
        return ret;
108
    }
109
    if (*typePtr != REG_SZ) {
110
        return ERROR_BADKEY;
111
    }
112

113
    len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
114
                              (LPCSTR) val, -1,
115
                              (LPWSTR) buf, bufLength/sizeof(WCHAR));
116
    if (len <= 0) {
117
        return ERROR_BADKEY;
118
    }
119
    return ERROR_SUCCESS;
120
}
121

122
/*
123
 * Produces custom name "GMT+hh:mm" from the given bias in buffer.
124
 */
125
static void customZoneName(LONG bias, char *buffer, size_t bufSize) {
126
    LONG gmtOffset;
127
    int sign;
128

129
    if (bias > 0) {
130
        gmtOffset = bias;
131
        sign = -1;
132
    } else {
133
        gmtOffset = -bias;
134
        sign = 1;
135
    }
136
    if (gmtOffset != 0) {
137
        snprintf(buffer, bufSize, "GMT%c%02d:%02d",
138
                ((sign >= 0) ? '+' : '-'),
139
                gmtOffset / 60,
140
                gmtOffset % 60);
141
    } else {
142
        strcpy(buffer, "GMT");
143
    }
144
}
145

146
/*
147
 * Gets the current time zone entry in the "Time Zones" registry.
148
 */
149
static int getWinTimeZone(char *winZoneName, size_t winZoneNameBufSize)
150
{
151
    DYNAMIC_TIME_ZONE_INFORMATION dtzi;
152
    DWORD timeType;
153
    DWORD bufSize;
154
    DWORD val;
155
    HANDLE hKey = NULL;
156
    LONG ret;
157
    ULONG valueType;
158

159
    /*
160
     * Get the dynamic time zone information so that time zone redirection
161
     * can be supported. (see JDK-7044727)
162
     */
163
    timeType = GetDynamicTimeZoneInformation(&dtzi);
164
    if (timeType == TIME_ZONE_ID_INVALID) {
165
        goto err;
166
    }
167

168
    /*
169
     * Make sure TimeZoneKeyName is available from the API call. If
170
     * DynamicDaylightTime is disabled, return a custom time zone name
171
     * based on the GMT offset. Otherwise, return the TimeZoneKeyName
172
     * value.
173
     */
174
    if (dtzi.TimeZoneKeyName[0] != 0) {
175
        if (dtzi.DynamicDaylightTimeDisabled) {
176
            customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize);
177
            return VALUE_GMTOFFSET;
178
        }
179
        wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR);
180
        return VALUE_KEY;
181
    }
182

183
    /*
184
     * If TimeZoneKeyName is not available, check whether StandardName
185
     * is available to fall back to the older API GetTimeZoneInformation.
186
     * If not, directly read the value from registry keys.
187
     */
188
    if (dtzi.StandardName[0] == 0) {
189
        ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
190
                           KEY_READ, (PHKEY)&hKey);
191
        if (ret != ERROR_SUCCESS) {
192
            goto err;
193
        }
194

195
        /*
196
         * Determine if auto-daylight time adjustment is turned off.
197
         */
198
        bufSize = sizeof(val);
199
        ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,
200
                               &valueType, (LPBYTE) &val, &bufSize);
201
        if (ret != ERROR_SUCCESS) {
202
            goto err;
203
        }
204
        /*
205
         * Return a custom time zone name if auto-daylight time adjustment
206
         * is disabled.
207
         */
208
        if (val == 1) {
209
            customZoneName(dtzi.Bias, winZoneName, winZoneNameBufSize);
210
            (void) RegCloseKey(hKey);
211
            return VALUE_GMTOFFSET;
212
        }
213

214
        bufSize = MAX_ZONE_CHAR;
215
        ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL,
216
                               &valueType, (LPBYTE) winZoneName, &bufSize);
217
        if (ret != ERROR_SUCCESS) {
218
            goto err;
219
        }
220
        (void) RegCloseKey(hKey);
221
        return VALUE_KEY;
222
    } else {
223
        /*
224
         * Fall back to GetTimeZoneInformation
225
         */
226
        TIME_ZONE_INFORMATION tzi;
227
        HANDLE hSubKey = NULL;
228
        DWORD nSubKeys, i;
229
        ULONG valueType;
230
        TCHAR subKeyName[MAX_ZONE_CHAR];
231
        TCHAR szValue[MAX_ZONE_CHAR];
232
        WCHAR stdNameInReg[MAX_ZONE_CHAR];
233
        TziValue tempTzi;
234
        WCHAR *stdNamePtr = tzi.StandardName;
235
        int onlyMapID;
236

237
        timeType = GetTimeZoneInformation(&tzi);
238
        if (timeType == TIME_ZONE_ID_INVALID) {
239
            goto err;
240
        }
241

242
        ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
243
                           KEY_READ, (PHKEY)&hKey);
244
        if (ret == ERROR_SUCCESS) {
245
            /*
246
             * Determine if auto-daylight time adjustment is turned off.
247
             */
248
            bufSize = sizeof(val);
249
            ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL,
250
                                   &valueType, (LPBYTE) &val, &bufSize);
251
            if (ret == ERROR_SUCCESS) {
252
                if (val == 1 && tzi.DaylightDate.wMonth != 0) {
253
                    (void) RegCloseKey(hKey);
254
                    customZoneName(tzi.Bias, winZoneName, winZoneNameBufSize);
255
                    return VALUE_GMTOFFSET;
256
                }
257
            }
258

259
            /*
260
             * Win32 problem: If the length of the standard time name is equal
261
             * to (or probably longer than) 32 in the registry,
262
             * GetTimeZoneInformation() on NT returns a null string as its
263
             * standard time name. We need to work around this problem by
264
             * getting the same information from the TimeZoneInformation
265
             * registry.
266
             */
267
            if (tzi.StandardName[0] == 0) {
268
                bufSize = sizeof(stdNameInReg);
269
                ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType,
270
                                         (LPBYTE) stdNameInReg, &bufSize);
271
                if (ret != ERROR_SUCCESS) {
272
                    goto err;
273
                }
274
                stdNamePtr = stdNameInReg;
275
            }
276
            (void) RegCloseKey(hKey);
277
        }
278

279
        /*
280
         * Open the "Time Zones" registry.
281
         */
282
        ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
283
        if (ret != ERROR_SUCCESS) {
284
            ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey);
285
            /*
286
             * If both failed, then give up.
287
             */
288
            if (ret != ERROR_SUCCESS) {
289
                return VALUE_UNKNOWN;
290
            }
291
        }
292

293
        /*
294
         * Get the number of subkeys of the "Time Zones" registry for
295
         * enumeration.
296
         */
297
        ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys,
298
                              NULL, NULL, NULL, NULL, NULL, NULL, NULL);
299
        if (ret != ERROR_SUCCESS) {
300
            goto err;
301
        }
302

303
        /*
304
         * Compare to the "Std" value of each subkey and find the entry that
305
         * matches the current control panel setting.
306
         */
307
        onlyMapID = 0;
308
        for (i = 0; i < nSubKeys; ++i) {
309
            DWORD size = sizeof(subKeyName);
310
            ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL);
311
            if (ret != ERROR_SUCCESS) {
312
                goto err;
313
            }
314
            ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey);
315
            if (ret != ERROR_SUCCESS) {
316
                goto err;
317
            }
318

319
            size = sizeof(szValue);
320
            ret = getValueInRegistry(hSubKey, STD_NAME, &valueType,
321
                                     szValue, &size);
322
            if (ret != ERROR_SUCCESS) {
323
                /*
324
                 * NT 4.0 SP3 fails here since it doesn't have the "Std"
325
                 * entry in the Time Zones registry.
326
                 */
327
                RegCloseKey(hSubKey);
328
                onlyMapID = 1;
329
                ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey);
330
                if (ret != ERROR_SUCCESS) {
331
                    goto err;
332
                }
333
                break;
334
            }
335

336
            if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) {
337
                /*
338
                 * Some localized Win32 platforms use a same name to
339
                 * different time zones. So, we can't rely only on the name
340
                 * here. We need to check GMT offsets and transition dates
341
                 * to make sure it's the registry of the current time
342
                 * zone.
343
                 */
344
                DWORD tziValueSize = sizeof(tempTzi);
345
                ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType,
346
                                      (unsigned char *) &tempTzi, &tziValueSize);
347
                if (ret == ERROR_SUCCESS) {
348
                    if ((tzi.Bias != tempTzi.bias) ||
349
                        (memcmp((const void *) &tzi.StandardDate,
350
                                (const void *) &tempTzi.stdDate,
351
                                sizeof(SYSTEMTIME)) != 0)) {
352
                        goto out;
353
                    }
354

355
                    if (tzi.DaylightBias != 0) {
356
                        if ((tzi.DaylightBias != tempTzi.dstBias) ||
357
                            (memcmp((const void *) &tzi.DaylightDate,
358
                                    (const void *) &tempTzi.dstDate,
359
                                    sizeof(SYSTEMTIME)) != 0)) {
360
                            goto out;
361
                        }
362
                    }
363
                }
364

365
                /*
366
                 * found matched record, terminate search
367
                 */
368
                strcpy(winZoneName, subKeyName);
369
                break;
370
            }
371
        out:
372
            (void) RegCloseKey(hSubKey);
373
        }
374

375
        (void) RegCloseKey(hKey);
376
    }
377

378
    return VALUE_KEY;
379

380
 err:
381
    if (hKey != NULL) {
382
        (void) RegCloseKey(hKey);
383
    }
384
    return VALUE_UNKNOWN;
385
}
386

387
/*
388
 * The mapping table file name.
389
 */
390
#define MAPPINGS_FILE "\\lib\\tzmappings"
391

392
/*
393
 * Index values for the mapping table.
394
 */
395
#define TZ_WIN_NAME     0
396
#define TZ_REGION       1
397
#define TZ_JAVA_NAME    2
398

399
#define TZ_NITEMS       3       /* number of items (fields) */
400

401
/*
402
 * Looks up the mapping table (tzmappings) and returns a Java time
403
 * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is
404
 * returned.
405
 */
406
static char *matchJavaTZ(const char *java_home_dir, char *tzName)
407
{
408
    int line;
409
    FILE *fp;
410
    char *javaTZName = NULL;
411
    char *items[TZ_NITEMS];
412
    char *mapFileName;
413
    char lineBuffer[MAX_ZONE_CHAR * 4];
414
    int offset = 0;
415
    const char* errorMessage = "unknown error";
416
    char region[MAX_REGION_LENGTH];
417

418
    // Get the user's location
419
    if (GetGeoInfo(GetUserGeoID(GEOCLASS_NATION),
420
            GEO_ISO2, region, MAX_REGION_LENGTH, 0) == 0) {
421
        // If GetGeoInfo fails, fallback to LCID's country
422
        LCID lcid = GetUserDefaultLCID();
423
        if (GetLocaleInfo(lcid,
424
                          LOCALE_SISO3166CTRYNAME, region, MAX_REGION_LENGTH) == 0 &&
425
            GetLocaleInfo(lcid,
426
                          LOCALE_SISO3166CTRYNAME2, region, MAX_REGION_LENGTH) == 0) {
427
            region[0] = '\0';
428
        }
429
    }
430

431
    mapFileName = malloc(strlen(java_home_dir) + strlen(MAPPINGS_FILE) + 1);
432
    if (mapFileName == NULL) {
433
        return NULL;
434
    }
435
    strcpy(mapFileName, java_home_dir);
436
    strcat(mapFileName, MAPPINGS_FILE);
437

438
    if ((fp = fopen(mapFileName, "r")) == NULL) {
439
        jio_fprintf(stderr, "can't open %s.\n", mapFileName);
440
        free((void *) mapFileName);
441
        return NULL;
442
    }
443
    free((void *) mapFileName);
444

445
    line = 0;
446
    while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) {
447
        char *start, *idx, *endp;
448
        int itemIndex = 0;
449

450
        line++;
451
        start = idx = lineBuffer;
452
        endp = &lineBuffer[sizeof(lineBuffer)];
453

454
        /*
455
         * Ignore comment and blank lines.
456
         */
457
        if (*idx == '#' || *idx == '\n') {
458
            continue;
459
        }
460

461
        for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) {
462
            items[itemIndex] = start;
463
            while (*idx && *idx != ':') {
464
                if (++idx >= endp) {
465
                    errorMessage = "premature end of line";
466
                    offset = (int)(idx - lineBuffer);
467
                    goto illegal_format;
468
                }
469
            }
470
            if (*idx == '\0') {
471
                errorMessage = "illegal null character found";
472
                offset = (int)(idx - lineBuffer);
473
                goto illegal_format;
474
            }
475
            *idx++ = '\0';
476
            start = idx;
477
        }
478

479
        if (*idx != '\n') {
480
            errorMessage = "illegal non-newline character found";
481
            offset = (int)(idx - lineBuffer);
482
            goto illegal_format;
483
        }
484

485
        /*
486
         * We need to scan items until the
487
         * exact match is found or the end of data is detected.
488
         */
489
        if (strcmp(items[TZ_WIN_NAME], tzName) == 0) {
490
            /*
491
             * Found the time zone in the mapping table.
492
             * Check the region code and select the appropriate entry
493
             */
494
            if (strcmp(items[TZ_REGION], region) == 0 ||
495
                strcmp(items[TZ_REGION], "001") == 0) {
496
                javaTZName = _strdup(items[TZ_JAVA_NAME]);
497
                break;
498
            }
499
        }
500
    }
501
    fclose(fp);
502

503
    return javaTZName;
504

505
 illegal_format:
506
    (void) fclose(fp);
507
    jio_fprintf(stderr, "Illegal format in tzmappings file: %s at line %d, offset %d.\n",
508
                errorMessage, line, offset);
509
    return NULL;
510
}
511

512
/*
513
 * Detects the platform time zone which maps to a Java time zone ID.
514
 */
515
char *findJavaTZ_md(const char *java_home_dir)
516
{
517
    char winZoneName[MAX_ZONE_CHAR];
518
    char *std_timezone = NULL;
519
    int  result;
520

521
    result = getWinTimeZone(winZoneName, sizeof(winZoneName));
522

523
    if (result != VALUE_UNKNOWN) {
524
        if (result == VALUE_GMTOFFSET) {
525
            std_timezone = _strdup(winZoneName);
526
        } else {
527
            std_timezone = matchJavaTZ(java_home_dir, winZoneName);
528
            if (std_timezone == NULL) {
529
                std_timezone = getGMTOffsetID();
530
            }
531
        }
532
    }
533
    return std_timezone;
534
}
535

536
/**
537
 * Returns a GMT-offset-based time zone ID.
538
 */
539
char *
540
getGMTOffsetID()
541
{
542
    LONG bias = 0;
543
    LONG ret;
544
    HANDLE hKey = NULL;
545
    char zonename[32];
546

547
    // Obtain the current GMT offset value of ActiveTimeBias.
548
    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0,
549
                       KEY_READ, (PHKEY)&hKey);
550
    if (ret == ERROR_SUCCESS) {
551
        DWORD val;
552
        DWORD bufSize = sizeof(val);
553
        ULONG valueType = 0;
554
        ret = RegQueryValueExA(hKey, "ActiveTimeBias",
555
                               NULL, &valueType, (LPBYTE) &val, &bufSize);
556
        if (ret == ERROR_SUCCESS) {
557
            bias = (LONG) val;
558
        }
559
        (void) RegCloseKey(hKey);
560
    }
561

562
    // If we can't get the ActiveTimeBias value, use Bias of TimeZoneInformation.
563
    // Note: Bias doesn't reflect current daylight saving.
564
    if (ret != ERROR_SUCCESS) {
565
        TIME_ZONE_INFORMATION tzi;
566
        if (GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) {
567
            bias = tzi.Bias;
568
        }
569
    }
570

571
    customZoneName(bias, zonename, sizeof(zonename));
572
    return _strdup(zonename);
573
}
574

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.