Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* |
michael@0 | 2 | ******************************************************************************** |
michael@0 | 3 | * Copyright (C) 2005-2013, International Business Machines |
michael@0 | 4 | * Corporation and others. All Rights Reserved. |
michael@0 | 5 | ******************************************************************************** |
michael@0 | 6 | * |
michael@0 | 7 | * File WINTZ.CPP |
michael@0 | 8 | * |
michael@0 | 9 | ******************************************************************************** |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | #include "unicode/utypes.h" |
michael@0 | 13 | |
michael@0 | 14 | #if U_PLATFORM_HAS_WIN32_API |
michael@0 | 15 | |
michael@0 | 16 | #include "wintz.h" |
michael@0 | 17 | #include "cmemory.h" |
michael@0 | 18 | #include "cstring.h" |
michael@0 | 19 | |
michael@0 | 20 | #include "unicode/ustring.h" |
michael@0 | 21 | #include "unicode/ures.h" |
michael@0 | 22 | |
michael@0 | 23 | # define WIN32_LEAN_AND_MEAN |
michael@0 | 24 | # define VC_EXTRALEAN |
michael@0 | 25 | # define NOUSER |
michael@0 | 26 | # define NOSERVICE |
michael@0 | 27 | # define NOIME |
michael@0 | 28 | # define NOMCX |
michael@0 | 29 | #include <windows.h> |
michael@0 | 30 | |
michael@0 | 31 | #define MAX_LENGTH_ID 40 |
michael@0 | 32 | |
michael@0 | 33 | /* The layout of the Tzi value in the registry */ |
michael@0 | 34 | typedef struct |
michael@0 | 35 | { |
michael@0 | 36 | int32_t bias; |
michael@0 | 37 | int32_t standardBias; |
michael@0 | 38 | int32_t daylightBias; |
michael@0 | 39 | SYSTEMTIME standardDate; |
michael@0 | 40 | SYSTEMTIME daylightDate; |
michael@0 | 41 | } TZI; |
michael@0 | 42 | |
michael@0 | 43 | /** |
michael@0 | 44 | * Various registry keys and key fragments. |
michael@0 | 45 | */ |
michael@0 | 46 | static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\"; |
michael@0 | 47 | static const char STANDARD_NAME_REGKEY[] = "StandardName"; |
michael@0 | 48 | static const char STANDARD_TIME_REGKEY[] = " Standard Time"; |
michael@0 | 49 | static const char TZI_REGKEY[] = "TZI"; |
michael@0 | 50 | static const char STD_REGKEY[] = "Std"; |
michael@0 | 51 | |
michael@0 | 52 | /** |
michael@0 | 53 | * HKLM subkeys used to probe for the flavor of Windows. Note that we |
michael@0 | 54 | * specifically check for the "GMT" zone subkey; this is present on |
michael@0 | 55 | * NT, but on XP has become "GMT Standard Time". We need to |
michael@0 | 56 | * discriminate between these cases. |
michael@0 | 57 | */ |
michael@0 | 58 | static const char* const WIN_TYPE_PROBE_REGKEY[] = { |
michael@0 | 59 | /* WIN_9X_ME_TYPE */ |
michael@0 | 60 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones", |
michael@0 | 61 | |
michael@0 | 62 | /* WIN_NT_TYPE */ |
michael@0 | 63 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\GMT" |
michael@0 | 64 | |
michael@0 | 65 | /* otherwise: WIN_2K_XP_TYPE */ |
michael@0 | 66 | }; |
michael@0 | 67 | |
michael@0 | 68 | /** |
michael@0 | 69 | * The time zone root subkeys (under HKLM) for different flavors of |
michael@0 | 70 | * Windows. |
michael@0 | 71 | */ |
michael@0 | 72 | static const char* const TZ_REGKEY[] = { |
michael@0 | 73 | /* WIN_9X_ME_TYPE */ |
michael@0 | 74 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\", |
michael@0 | 75 | |
michael@0 | 76 | /* WIN_NT_TYPE | WIN_2K_XP_TYPE */ |
michael@0 | 77 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\" |
michael@0 | 78 | }; |
michael@0 | 79 | |
michael@0 | 80 | /** |
michael@0 | 81 | * Flavor of Windows, from our perspective. Not a real OS version, |
michael@0 | 82 | * but rather the flavor of the layout of the time zone information in |
michael@0 | 83 | * the registry. |
michael@0 | 84 | */ |
michael@0 | 85 | enum { |
michael@0 | 86 | WIN_9X_ME_TYPE = 1, |
michael@0 | 87 | WIN_NT_TYPE = 2, |
michael@0 | 88 | WIN_2K_XP_TYPE = 3 |
michael@0 | 89 | }; |
michael@0 | 90 | |
michael@0 | 91 | static int32_t gWinType = 0; |
michael@0 | 92 | |
michael@0 | 93 | static int32_t detectWindowsType() |
michael@0 | 94 | { |
michael@0 | 95 | int32_t winType; |
michael@0 | 96 | LONG result; |
michael@0 | 97 | HKEY hkey; |
michael@0 | 98 | |
michael@0 | 99 | /* Detect the version of windows by trying to open a sequence of |
michael@0 | 100 | probe keys. We don't use the OS version API because what we |
michael@0 | 101 | really want to know is how the registry is laid out. |
michael@0 | 102 | Specifically, is it 9x/Me or not, and is it "GMT" or "GMT |
michael@0 | 103 | Standard Time". */ |
michael@0 | 104 | for (winType = 0; winType < 2; winType++) { |
michael@0 | 105 | result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
michael@0 | 106 | WIN_TYPE_PROBE_REGKEY[winType], |
michael@0 | 107 | 0, |
michael@0 | 108 | KEY_QUERY_VALUE, |
michael@0 | 109 | &hkey); |
michael@0 | 110 | RegCloseKey(hkey); |
michael@0 | 111 | |
michael@0 | 112 | if (result == ERROR_SUCCESS) { |
michael@0 | 113 | break; |
michael@0 | 114 | } |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | return winType+1; /* +1 to bring it inline with the enum */ |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | static LONG openTZRegKey(HKEY *hkey, const char *winid) |
michael@0 | 121 | { |
michael@0 | 122 | char subKeyName[110]; /* TODO: why 96?? */ |
michael@0 | 123 | char *name; |
michael@0 | 124 | LONG result; |
michael@0 | 125 | |
michael@0 | 126 | /* This isn't thread safe, but it's good enough because the result should be constant per system. */ |
michael@0 | 127 | if (gWinType <= 0) { |
michael@0 | 128 | gWinType = detectWindowsType(); |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | uprv_strcpy(subKeyName, TZ_REGKEY[(gWinType != WIN_9X_ME_TYPE)]); |
michael@0 | 132 | name = &subKeyName[strlen(subKeyName)]; |
michael@0 | 133 | uprv_strcat(subKeyName, winid); |
michael@0 | 134 | |
michael@0 | 135 | if (gWinType == WIN_9X_ME_TYPE) { |
michael@0 | 136 | /* Remove " Standard Time" */ |
michael@0 | 137 | char *pStd = uprv_strstr(subKeyName, STANDARD_TIME_REGKEY); |
michael@0 | 138 | if (pStd) { |
michael@0 | 139 | *pStd = 0; |
michael@0 | 140 | } |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, |
michael@0 | 144 | subKeyName, |
michael@0 | 145 | 0, |
michael@0 | 146 | KEY_QUERY_VALUE, |
michael@0 | 147 | hkey); |
michael@0 | 148 | return result; |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | static LONG getTZI(const char *winid, TZI *tzi) |
michael@0 | 152 | { |
michael@0 | 153 | DWORD cbData = sizeof(TZI); |
michael@0 | 154 | LONG result; |
michael@0 | 155 | HKEY hkey; |
michael@0 | 156 | |
michael@0 | 157 | result = openTZRegKey(&hkey, winid); |
michael@0 | 158 | |
michael@0 | 159 | if (result == ERROR_SUCCESS) { |
michael@0 | 160 | result = RegQueryValueExA(hkey, |
michael@0 | 161 | TZI_REGKEY, |
michael@0 | 162 | NULL, |
michael@0 | 163 | NULL, |
michael@0 | 164 | (LPBYTE)tzi, |
michael@0 | 165 | &cbData); |
michael@0 | 166 | |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | RegCloseKey(hkey); |
michael@0 | 170 | |
michael@0 | 171 | return result; |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | static LONG getSTDName(const char *winid, char *regStdName, int32_t length) { |
michael@0 | 175 | DWORD cbData = length; |
michael@0 | 176 | LONG result; |
michael@0 | 177 | HKEY hkey; |
michael@0 | 178 | |
michael@0 | 179 | result = openTZRegKey(&hkey, winid); |
michael@0 | 180 | |
michael@0 | 181 | if (result == ERROR_SUCCESS) { |
michael@0 | 182 | result = RegQueryValueExA(hkey, |
michael@0 | 183 | STD_REGKEY, |
michael@0 | 184 | NULL, |
michael@0 | 185 | NULL, |
michael@0 | 186 | (LPBYTE)regStdName, |
michael@0 | 187 | &cbData); |
michael@0 | 188 | |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | RegCloseKey(hkey); |
michael@0 | 192 | |
michael@0 | 193 | return result; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | /* |
michael@0 | 197 | This code attempts to detect the Windows time zone, as set in the |
michael@0 | 198 | Windows Date and Time control panel. It attempts to work on |
michael@0 | 199 | multiple flavors of Windows (9x, Me, NT, 2000, XP) and on localized |
michael@0 | 200 | installs. It works by directly interrogating the registry and |
michael@0 | 201 | comparing the data there with the data returned by the |
michael@0 | 202 | GetTimeZoneInformation API, along with some other strategies. The |
michael@0 | 203 | registry contains time zone data under one of two keys (depending on |
michael@0 | 204 | the flavor of Windows): |
michael@0 | 205 | |
michael@0 | 206 | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\ |
michael@0 | 207 | HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\ |
michael@0 | 208 | |
michael@0 | 209 | Under this key are several subkeys, one for each time zone. These |
michael@0 | 210 | subkeys are named "Pacific" on Win9x/Me and "Pacific Standard Time" |
michael@0 | 211 | on WinNT/2k/XP. There are some other wrinkles; see the code for |
michael@0 | 212 | details. The subkey name is NOT LOCALIZED, allowing us to support |
michael@0 | 213 | localized installs. |
michael@0 | 214 | |
michael@0 | 215 | Under the subkey are data values. We care about: |
michael@0 | 216 | |
michael@0 | 217 | Std Standard time display name, localized |
michael@0 | 218 | TZI Binary block of data |
michael@0 | 219 | |
michael@0 | 220 | The TZI data is of particular interest. It contains the offset, two |
michael@0 | 221 | more offsets for standard and daylight time, and the start and end |
michael@0 | 222 | rules. This is the same data returned by the GetTimeZoneInformation |
michael@0 | 223 | API. The API may modify the data on the way out, so we have to be |
michael@0 | 224 | careful, but essentially we do a binary comparison against the TZI |
michael@0 | 225 | blocks of various registry keys. When we find a match, we know what |
michael@0 | 226 | time zone Windows is set to. Since the registry key is not |
michael@0 | 227 | localized, we can then translate the key through a simple table |
michael@0 | 228 | lookup into the corresponding ICU time zone. |
michael@0 | 229 | |
michael@0 | 230 | This strategy doesn't always work because there are zones which |
michael@0 | 231 | share an offset and rules, so more than one TZI block will match. |
michael@0 | 232 | For example, both Tokyo and Seoul are at GMT+9 with no DST rules; |
michael@0 | 233 | their TZI blocks are identical. For these cases, we fall back to a |
michael@0 | 234 | name lookup. We attempt to match the display name as stored in the |
michael@0 | 235 | registry for the current zone to the display name stored in the |
michael@0 | 236 | registry for various Windows zones. By comparing the registry data |
michael@0 | 237 | directly we avoid conversion complications. |
michael@0 | 238 | |
michael@0 | 239 | Author: Alan Liu |
michael@0 | 240 | Since: ICU 2.6 |
michael@0 | 241 | Based on original code by Carl Brown <cbrown@xnetinc.com> |
michael@0 | 242 | */ |
michael@0 | 243 | |
michael@0 | 244 | /** |
michael@0 | 245 | * Main Windows time zone detection function. Returns the Windows |
michael@0 | 246 | * time zone, translated to an ICU time zone, or NULL upon failure. |
michael@0 | 247 | */ |
michael@0 | 248 | U_CFUNC const char* U_EXPORT2 |
michael@0 | 249 | uprv_detectWindowsTimeZone() { |
michael@0 | 250 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 251 | UResourceBundle* bundle = NULL; |
michael@0 | 252 | char* icuid = NULL; |
michael@0 | 253 | UChar apiStd[MAX_LENGTH_ID]; |
michael@0 | 254 | char apiStdName[MAX_LENGTH_ID]; |
michael@0 | 255 | char regStdName[MAX_LENGTH_ID]; |
michael@0 | 256 | char tmpid[MAX_LENGTH_ID]; |
michael@0 | 257 | int32_t len; |
michael@0 | 258 | int id; |
michael@0 | 259 | int errorCode; |
michael@0 | 260 | char ISOcode[3]; /* 2 letter iso code */ |
michael@0 | 261 | |
michael@0 | 262 | LONG result; |
michael@0 | 263 | TZI tziKey; |
michael@0 | 264 | TZI tziReg; |
michael@0 | 265 | TIME_ZONE_INFORMATION apiTZI; |
michael@0 | 266 | |
michael@0 | 267 | /* Obtain TIME_ZONE_INFORMATION from the API, and then convert it |
michael@0 | 268 | to TZI. We could also interrogate the registry directly; we do |
michael@0 | 269 | this below if needed. */ |
michael@0 | 270 | uprv_memset(&apiTZI, 0, sizeof(apiTZI)); |
michael@0 | 271 | uprv_memset(&tziKey, 0, sizeof(tziKey)); |
michael@0 | 272 | uprv_memset(&tziReg, 0, sizeof(tziReg)); |
michael@0 | 273 | GetTimeZoneInformation(&apiTZI); |
michael@0 | 274 | tziKey.bias = apiTZI.Bias; |
michael@0 | 275 | uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate, |
michael@0 | 276 | sizeof(apiTZI.StandardDate)); |
michael@0 | 277 | uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate, |
michael@0 | 278 | sizeof(apiTZI.DaylightDate)); |
michael@0 | 279 | |
michael@0 | 280 | /* Convert the wchar_t* standard name to char* */ |
michael@0 | 281 | uprv_memset(apiStdName, 0, sizeof(apiStdName)); |
michael@0 | 282 | u_strFromWCS(apiStd, MAX_LENGTH_ID, NULL, apiTZI.StandardName, -1, &status); |
michael@0 | 283 | u_austrncpy(apiStdName, apiStd, sizeof(apiStdName) - 1); |
michael@0 | 284 | |
michael@0 | 285 | tmpid[0] = 0; |
michael@0 | 286 | |
michael@0 | 287 | id = GetUserGeoID(GEOCLASS_NATION); |
michael@0 | 288 | errorCode = GetGeoInfo(id,GEO_ISO2,ISOcode,3,0); |
michael@0 | 289 | |
michael@0 | 290 | bundle = ures_openDirect(NULL, "windowsZones", &status); |
michael@0 | 291 | ures_getByKey(bundle, "mapTimezones", bundle, &status); |
michael@0 | 292 | |
michael@0 | 293 | /* Note: We get the winid not from static tables but from resource bundle. */ |
michael@0 | 294 | while (U_SUCCESS(status) && ures_hasNext(bundle)) { |
michael@0 | 295 | UBool idFound = FALSE; |
michael@0 | 296 | const char* winid; |
michael@0 | 297 | UResourceBundle* winTZ = ures_getNextResource(bundle, NULL, &status); |
michael@0 | 298 | if (U_FAILURE(status)) { |
michael@0 | 299 | break; |
michael@0 | 300 | } |
michael@0 | 301 | winid = ures_getKey(winTZ); |
michael@0 | 302 | result = getTZI(winid, &tziReg); |
michael@0 | 303 | |
michael@0 | 304 | if (result == ERROR_SUCCESS) { |
michael@0 | 305 | /* Windows alters the DaylightBias in some situations. |
michael@0 | 306 | Using the bias and the rules suffices, so overwrite |
michael@0 | 307 | these unreliable fields. */ |
michael@0 | 308 | tziKey.standardBias = tziReg.standardBias; |
michael@0 | 309 | tziKey.daylightBias = tziReg.daylightBias; |
michael@0 | 310 | |
michael@0 | 311 | if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0) { |
michael@0 | 312 | const UChar* icuTZ = NULL; |
michael@0 | 313 | if (errorCode != 0) { |
michael@0 | 314 | icuTZ = ures_getStringByKey(winTZ, ISOcode, &len, &status); |
michael@0 | 315 | } |
michael@0 | 316 | if (errorCode==0 || icuTZ==NULL) { |
michael@0 | 317 | /* fallback to default "001" and reset status */ |
michael@0 | 318 | status = U_ZERO_ERROR; |
michael@0 | 319 | icuTZ = ures_getStringByKey(winTZ, "001", &len, &status); |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | if (U_SUCCESS(status)) { |
michael@0 | 323 | /* Get the standard name from the registry key to compare with |
michael@0 | 324 | the one from Windows API call. */ |
michael@0 | 325 | uprv_memset(regStdName, 0, sizeof(regStdName)); |
michael@0 | 326 | result = getSTDName(winid, regStdName, sizeof(regStdName)); |
michael@0 | 327 | if (result == ERROR_SUCCESS) { |
michael@0 | 328 | if (uprv_strcmp(apiStdName, regStdName) == 0) { |
michael@0 | 329 | idFound = TRUE; |
michael@0 | 330 | } |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | /* tmpid buffer holds the ICU timezone ID corresponding to the timezone ID from Windows. |
michael@0 | 334 | * If none is found, tmpid buffer will contain a fallback ID (i.e. the time zone ID matching |
michael@0 | 335 | * the current time zone information) |
michael@0 | 336 | */ |
michael@0 | 337 | if (idFound || tmpid[0] == 0) { |
michael@0 | 338 | /* if icuTZ has more than one city, take only the first (i.e. terminate icuTZ at first space) */ |
michael@0 | 339 | int index=0; |
michael@0 | 340 | while (! (*icuTZ == '\0' || *icuTZ ==' ')) { |
michael@0 | 341 | tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */ |
michael@0 | 342 | } |
michael@0 | 343 | tmpid[index]='\0'; |
michael@0 | 344 | } |
michael@0 | 345 | } |
michael@0 | 346 | } |
michael@0 | 347 | } |
michael@0 | 348 | ures_close(winTZ); |
michael@0 | 349 | if (idFound) { |
michael@0 | 350 | break; |
michael@0 | 351 | } |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | /* |
michael@0 | 355 | * Copy the timezone ID to icuid to be returned. |
michael@0 | 356 | */ |
michael@0 | 357 | if (tmpid[0] != 0) { |
michael@0 | 358 | len = uprv_strlen(tmpid); |
michael@0 | 359 | icuid = (char*)uprv_calloc(len + 1, sizeof(char)); |
michael@0 | 360 | if (icuid != NULL) { |
michael@0 | 361 | uprv_strcpy(icuid, tmpid); |
michael@0 | 362 | } |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | ures_close(bundle); |
michael@0 | 366 | |
michael@0 | 367 | return icuid; |
michael@0 | 368 | } |
michael@0 | 369 | |
michael@0 | 370 | #endif /* U_PLATFORM_HAS_WIN32_API */ |