1 /* 2 * Copyright (c) 1999, 2010, 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 40 #define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" 41 #define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones" 42 #define WIN_CURRENT_TZ_KEY "System\\CurrentControlSet\\Control\\TimeZoneInformation" 43 44 typedef struct _TziValue { 45 LONG bias; 46 LONG stdBias; 47 LONG dstBias; 48 SYSTEMTIME stdDate; 49 SYSTEMTIME dstDate; 50 } TziValue; 51 52 /* 53 * Registry key names 54 */ 55 static void *keyNames[] = { 56 (void *) L"StandardName", 57 (void *) "StandardName", 58 (void *) L"Std", 59 (void *) "Std" 60 }; 61 62 /* 63 * Indices to keyNames[] 64 */ 65 #define STANDARD_NAME 0 66 #define STD_NAME 2 67 68 static int isNT = FALSE; /* TRUE if it is NT. */ 69 70 /* 71 * Calls RegQueryValueEx() to get the value for the specified key. If 72 * the platform is NT, 2000 or XP, it calls the Unicode 73 * version. Otherwise, it calls the ANSI version and converts the 74 * value to Unicode. In this case, it assumes that the current ANSI 75 * Code Page is the same as the native platform code page (e.g., Code 76 * Page 932 for the Japanese Windows systems. 77 * 78 * `keyIndex' is an index value to the keyNames in Unicode 79 * (WCHAR). `keyIndex' + 1 points to its ANSI value. 80 * 81 * Returns the status value. ERROR_SUCCESS if succeeded, a 82 * non-ERROR_SUCCESS value otherwise. 83 */ 84 static LONG 85 getValueInRegistry(HKEY hKey, 86 int keyIndex, 87 LPDWORD typePtr, 88 LPBYTE buf, 89 LPDWORD bufLengthPtr) 90 { 91 LONG ret; 92 DWORD bufLength = *bufLengthPtr; 93 char val[MAX_ZONE_CHAR]; 94 DWORD valSize; 95 int len; 96 97 *typePtr = 0; 98 if (isNT) { 99 ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL, 100 typePtr, buf, bufLengthPtr); 101 if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) { 102 return ret; 103 } 104 } 105 106 valSize = sizeof(val); 107 ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL, 108 typePtr, val, &valSize); 109 if (ret != ERROR_SUCCESS) { 110 return ret; 111 } 112 if (*typePtr != REG_SZ) { 113 return ERROR_BADKEY; 114 } 115 116 len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, 117 (LPCSTR) val, -1, 118 (LPWSTR) buf, bufLength/sizeof(WCHAR)); 119 if (len <= 0) { 120 return ERROR_BADKEY; 121 } 122 return ERROR_SUCCESS; 123 } 124 125 /* 126 * Produces custom name "GMT+hh:mm" from the given bias in buffer. 127 */ 128 static void customZoneName(LONG bias, char *buffer) { 129 LONG gmtOffset; 130 int sign; 131 132 if (bias > 0) { 133 gmtOffset = bias; 134 sign = -1; 135 } else { 136 gmtOffset = -bias; 137 sign = 1; 138 } 139 if (gmtOffset != 0) { 140 sprintf(buffer, "GMT%c%02d:%02d", 141 ((sign >= 0) ? '+' : '-'), 142 gmtOffset / 60, 143 gmtOffset % 60); 144 } else { 145 strcpy(buffer, "GMT"); 146 } 147 } 148 149 /* 150 * Gets the current time zone entry in the "Time Zones" registry. 151 */ 152 static int getWinTimeZone(char *winZoneName, char *winMapID) 153 { 154 TIME_ZONE_INFORMATION tzi; 155 OSVERSIONINFO ver; 156 int onlyMapID; 157 HANDLE hKey = NULL, hSubKey = NULL; 158 LONG ret; 159 DWORD nSubKeys, i; 160 ULONG valueType; 161 TCHAR subKeyName[MAX_ZONE_CHAR]; 162 TCHAR szValue[MAX_ZONE_CHAR]; 163 WCHAR stdNameInReg[MAX_ZONE_CHAR]; 164 TziValue tempTzi; 165 WCHAR *stdNamePtr = tzi.StandardName; 166 DWORD valueSize; 167 DWORD timeType; 168 169 /* 170 * Get the current time zone setting of the platform. 171 */ 172 timeType = GetTimeZoneInformation(&tzi); 173 if (timeType == TIME_ZONE_ID_INVALID) { 174 goto err; 175 } 176 177 /* 178 * Determine if this is an NT system. 179 */ 180 ver.dwOSVersionInfoSize = sizeof(ver); 181 GetVersionEx(&ver); 182 isNT = ver.dwPlatformId == VER_PLATFORM_WIN32_NT; 183 184 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, 185 KEY_READ, (PHKEY)&hKey); 186 if (ret == ERROR_SUCCESS) { 187 DWORD val; 188 DWORD bufSize; 189 190 /* 191 * Determine if auto-daylight time adjustment is turned off. 192 */ 193 valueType = 0; 194 bufSize = sizeof(val); 195 ret = RegQueryValueExA(hKey, "DisableAutoDaylightTimeSet", 196 NULL, &valueType, (LPBYTE) &val, &bufSize); 197 /* 198 * Vista uses the different key name. 199 */ 200 if (ret != ERROR_SUCCESS) { 201 bufSize = sizeof(val); 202 ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", 203 NULL, &valueType, (LPBYTE) &val, &bufSize); 204 } 205 if (ret == ERROR_SUCCESS) { 206 if (val == 1) { 207 (void) RegCloseKey(hKey); 208 customZoneName(tzi.Bias, winZoneName); 209 return VALUE_GMTOFFSET; 210 } 211 } 212 213 /* 214 * Vista has the key for the current "Time Zones" entry. 215 */ 216 if (isNT && ver.dwMajorVersion >= 6) { 217 valueType = 0; 218 bufSize = MAX_ZONE_CHAR; 219 ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL, 220 &valueType, (LPBYTE) winZoneName, &bufSize); 221 if (ret != ERROR_SUCCESS) { 222 goto err; 223 } 224 (void) RegCloseKey(hKey); 225 return VALUE_KEY; 226 } 227 228 /* 229 * Win32 problem: If the length of the standard time name is equal 230 * to (or probably longer than) 32 in the registry, 231 * GetTimeZoneInformation() on NT returns a null string as its 232 * standard time name. We need to work around this problem by 233 * getting the same information from the TimeZoneInformation 234 * registry. The function on Win98 seems to return its key name. 235 * We can't do anything in that case. 236 */ 237 if (tzi.StandardName[0] == 0) { 238 bufSize = sizeof(stdNameInReg); 239 ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType, 240 (LPBYTE) stdNameInReg, &bufSize); 241 if (ret != ERROR_SUCCESS) { 242 goto err; 243 } 244 stdNamePtr = stdNameInReg; 245 } 246 (void) RegCloseKey(hKey); 247 } 248 249 /* 250 * Open the "Time Zones" registry. 251 */ 252 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey); 253 if (ret != ERROR_SUCCESS) { 254 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey); 255 /* 256 * If both failed, then give up. 257 */ 258 if (ret != ERROR_SUCCESS) { 259 return VALUE_UNKNOWN; 260 } 261 } 262 263 /* 264 * Get the number of subkeys of the "Time Zones" registry for 265 * enumeration. 266 */ 267 ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys, 268 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 269 if (ret != ERROR_SUCCESS) { 270 goto err; 271 } 272 273 /* 274 * Compare to the "Std" value of each subkey and find the entry that 275 * matches the current control panel setting. 276 */ 277 onlyMapID = 0; 278 for (i = 0; i < nSubKeys; ++i) { 279 DWORD size = sizeof(subKeyName); 280 ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL); 281 if (ret != ERROR_SUCCESS) { 282 goto err; 283 } 284 ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey); 285 if (ret != ERROR_SUCCESS) { 286 goto err; 287 } 288 289 size = sizeof(szValue); 290 ret = getValueInRegistry(hSubKey, STD_NAME, &valueType, 291 szValue, &size); 292 if (ret != ERROR_SUCCESS) { 293 /* 294 * NT 4.0 SP3 fails here since it doesn't have the "Std" 295 * entry in the Time Zones registry. 296 */ 297 RegCloseKey(hSubKey); 298 onlyMapID = 1; 299 ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey); 300 if (ret != ERROR_SUCCESS) { 301 goto err; 302 } 303 break; 304 } 305 306 if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) { 307 /* 308 * Some localized Win32 platforms use a same name to 309 * different time zones. So, we can't rely only on the name 310 * here. We need to check GMT offsets and transition dates 311 * to make sure it's the registry of the current time 312 * zone. 313 */ 314 DWORD tziValueSize = sizeof(tempTzi); 315 ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType, 316 (unsigned char *) &tempTzi, &tziValueSize); 317 if (ret == ERROR_SUCCESS) { 318 if ((tzi.Bias != tempTzi.bias) || 319 (memcmp((const void *) &tzi.StandardDate, 320 (const void *) &tempTzi.stdDate, 321 sizeof(SYSTEMTIME)) != 0)) { 322 goto out; 323 } 324 325 if (tzi.DaylightBias != 0) { 326 if ((tzi.DaylightBias != tempTzi.dstBias) || 327 (memcmp((const void *) &tzi.DaylightDate, 328 (const void *) &tempTzi.dstDate, 329 sizeof(SYSTEMTIME)) != 0)) { 330 goto out; 331 } 332 } 333 } 334 335 /* 336 * found matched record, terminate search 337 */ 338 strcpy(winZoneName, subKeyName); 339 break; 340 } 341 out: 342 (void) RegCloseKey(hSubKey); 343 } 344 345 /* 346 * Get the "MapID" value of the registry to be able to eliminate 347 * duplicated key names later. 348 */ 349 valueSize = MAX_MAPID_LENGTH; 350 ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize); 351 (void) RegCloseKey(hSubKey); 352 (void) RegCloseKey(hKey); 353 354 if (ret != ERROR_SUCCESS) { 355 /* 356 * Vista doesn't have mapID. VALUE_UNKNOWN should be returned 357 * only for Windows NT. 358 */ 359 if (onlyMapID == 1) { 360 return VALUE_UNKNOWN; 361 } 362 } 363 364 return VALUE_KEY; 365 366 err: 367 if (hKey != NULL) { 368 (void) RegCloseKey(hKey); 369 } 370 return VALUE_UNKNOWN; 371 } 372 373 /* 374 * The mapping table file name. 375 */ 376 #define MAPPINGS_FILE "\\lib\\tzmappings" 377 378 /* 379 * Index values for the mapping table. 380 */ 381 #define TZ_WIN_NAME 0 382 #define TZ_MAPID 1 383 #define TZ_REGION 2 384 #define TZ_JAVA_NAME 3 385 386 #define TZ_NITEMS 4 /* number of items (fields) */ 387 388 /* 389 * Looks up the mapping table (tzmappings) and returns a Java time 390 * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is 391 * returned. 392 * 393 * value_type is one of the following values: 394 * VALUE_KEY for exact key matching 395 * VALUE_MAPID for MapID and country-based mapping (this is 396 * required for the old Windows, such as NT 4.0 SP3). 397 */ 398 static char *matchJavaTZ(const char *java_home_dir, int value_type, char *tzName, 399 char *mapID, const char *country) 400 { 401 int line; 402 int IDmatched = 0; 403 FILE *fp; 404 char *javaTZName = NULL; 405 char *items[TZ_NITEMS]; 406 char mapFileName[_MAX_PATH + 1]; 407 char lineBuffer[MAX_ZONE_CHAR * 4]; 408 char bestMatch[MAX_ZONE_CHAR]; 409 int noMapID = *mapID == '\0'; /* no mapID on Vista */ 410 411 bestMatch[0] = '\0'; 412 413 strcpy(mapFileName, java_home_dir); 414 strcat(mapFileName, MAPPINGS_FILE); 415 416 if ((fp = fopen(mapFileName, "r")) == NULL) { 417 jio_fprintf(stderr, "can't open %s.\n", mapFileName); 418 return NULL; 419 } 420 421 line = 0; 422 while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) { 423 char *start, *idx, *endp; 424 int itemIndex = 0; 425 426 line++; 427 start = idx = lineBuffer; 428 endp = &lineBuffer[sizeof(lineBuffer)]; 429 430 /* 431 * Ignore comment and blank lines. 432 */ 433 if (*idx == '#' || *idx == '\n') { 434 continue; 435 } 436 437 for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) { 438 items[itemIndex] = start; 439 while (*idx && *idx != ':') { 440 if (++idx >= endp) { 441 goto illegal_format; 442 } 443 } 444 if (*idx == '\0') { 445 goto illegal_format; 446 } 447 *idx++ = '\0'; 448 start = idx; 449 } 450 451 if (*idx != '\n') { 452 goto illegal_format; 453 } 454 455 if (noMapID || strcmp(mapID, items[TZ_MAPID]) == 0) { 456 /* 457 * When there's no mapID, we need to scan items until the 458 * exact match is found or the end of data is detected. 459 */ 460 if (!noMapID) { 461 IDmatched = 1; 462 } 463 if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { 464 /* 465 * Found the time zone in the mapping table. 466 */ 467 javaTZName = _strdup(items[TZ_JAVA_NAME]); 468 break; 469 } 470 /* 471 * Try to find the most likely time zone. 472 */ 473 if (*items[TZ_REGION] == '\0') { 474 strncpy(bestMatch, items[TZ_JAVA_NAME], MAX_ZONE_CHAR); 475 } else if (country != NULL && strcmp(items[TZ_REGION], country) == 0) { 476 if (value_type == VALUE_MAPID) { 477 javaTZName = _strdup(items[TZ_JAVA_NAME]); 478 break; 479 } 480 strncpy(bestMatch, items[TZ_JAVA_NAME], MAX_ZONE_CHAR); 481 } 482 } else { 483 if (IDmatched == 1) { 484 /* 485 * No need to look up the mapping table further. 486 */ 487 break; 488 } 489 } 490 } 491 fclose(fp); 492 493 if (javaTZName == NULL && bestMatch[0] != '\0') { 494 javaTZName = _strdup(bestMatch); 495 } 496 return javaTZName; 497 498 illegal_format: 499 (void) fclose(fp); 500 jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", line); 501 return NULL; 502 } 503 504 /* 505 * Detects the platform time zone which maps to a Java time zone ID. 506 */ 507 char *findJavaTZ_md(const char *java_home_dir, const char *country) 508 { 509 char winZoneName[MAX_ZONE_CHAR]; 510 char winMapID[MAX_MAPID_LENGTH]; 511 char *std_timezone = NULL; 512 int result; 513 514 winMapID[0] = 0; 515 result = getWinTimeZone(winZoneName, winMapID); 516 517 if (result != VALUE_UNKNOWN) { 518 if (result == VALUE_GMTOFFSET) { 519 std_timezone = _strdup(winZoneName); 520 } else { 521 std_timezone = matchJavaTZ(java_home_dir, result, 522 winZoneName, winMapID, country); 523 } 524 } 525 526 return std_timezone; 527 } 528 529 /** 530 * Returns a GMT-offset-based time zone ID. On Win32, it always return 531 * NULL since the fall back is performed in getWinTimeZone(). 532 */ 533 char * 534 getGMTOffsetID() 535 { 536 return NULL; 537 }