tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

time_zone_lookup.cc (10828B)


      1 // Copyright 2016 Google Inc. All Rights Reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //   https://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 //   Unless required by applicable law or agreed to in writing, software
     10 //   distributed under the License is distributed on an "AS IS" BASIS,
     11 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 //   See the License for the specific language governing permissions and
     13 //   limitations under the License.
     14 
     15 #include "absl/base/config.h"
     16 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
     17 
     18 #if defined(__ANDROID__)
     19 #include <sys/system_properties.h>
     20 #endif
     21 
     22 #if defined(__APPLE__)
     23 #include <CoreFoundation/CFTimeZone.h>
     24 
     25 #include <vector>
     26 #endif
     27 
     28 #if defined(__Fuchsia__)
     29 #include <fuchsia/intl/cpp/fidl.h>
     30 #include <lib/async-loop/cpp/loop.h>
     31 #include <lib/fdio/directory.h>
     32 #include <zircon/types.h>
     33 #endif
     34 
     35 #if defined(_WIN32)
     36 #include <sdkddkver.h>
     37 // Include only when the SDK is for Windows 10 (and later), and the binary is
     38 // targeted for Windows XP and later.
     39 // Note: The Windows SDK added windows.globalization.h file for Windows 10, but
     40 // MinGW did not add it until NTDDI_WIN10_NI (SDK version 10.0.22621.0).
     41 #if ((defined(_WIN32_WINNT_WIN10) && !defined(__MINGW32__)) ||        \
     42     (defined(NTDDI_WIN10_NI) && NTDDI_VERSION >= NTDDI_WIN10_NI)) && \
     43    (_WIN32_WINNT >= _WIN32_WINNT_WINXP)
     44 #define USE_WIN32_LOCAL_TIME_ZONE
     45 #include <roapi.h>
     46 #include <tchar.h>
     47 #include <wchar.h>
     48 #include <windows.globalization.h>
     49 #include <windows.h>
     50 #include <winstring.h>
     51 #endif
     52 #endif
     53 
     54 #include <cstdlib>
     55 #include <cstring>
     56 #include <string>
     57 
     58 #include "absl/time/internal/cctz/src/time_zone_fixed.h"
     59 #include "absl/time/internal/cctz/src/time_zone_impl.h"
     60 
     61 namespace absl {
     62 ABSL_NAMESPACE_BEGIN
     63 namespace time_internal {
     64 namespace cctz {
     65 
     66 namespace {
     67 #if defined(USE_WIN32_LOCAL_TIME_ZONE)
     68 // Calls the WinRT Calendar.GetTimeZone method to obtain the IANA ID of the
     69 // local time zone. Returns an empty vector in case of an error.
     70 std::string win32_local_time_zone(const HMODULE combase) {
     71  std::string result;
     72  const auto ro_activate_instance =
     73      reinterpret_cast<decltype(&RoActivateInstance)>(
     74          GetProcAddress(combase, "RoActivateInstance"));
     75  if (!ro_activate_instance) {
     76    return result;
     77  }
     78  const auto windows_create_string_reference =
     79      reinterpret_cast<decltype(&WindowsCreateStringReference)>(
     80          GetProcAddress(combase, "WindowsCreateStringReference"));
     81  if (!windows_create_string_reference) {
     82    return result;
     83  }
     84  const auto windows_delete_string =
     85      reinterpret_cast<decltype(&WindowsDeleteString)>(
     86          GetProcAddress(combase, "WindowsDeleteString"));
     87  if (!windows_delete_string) {
     88    return result;
     89  }
     90  const auto windows_get_string_raw_buffer =
     91      reinterpret_cast<decltype(&WindowsGetStringRawBuffer)>(
     92          GetProcAddress(combase, "WindowsGetStringRawBuffer"));
     93  if (!windows_get_string_raw_buffer) {
     94    return result;
     95  }
     96 
     97  // The string returned by WindowsCreateStringReference doesn't need to be
     98  // deleted.
     99  HSTRING calendar_class_id;
    100  HSTRING_HEADER calendar_class_id_header;
    101  HRESULT hr = windows_create_string_reference(
    102      RuntimeClass_Windows_Globalization_Calendar,
    103      sizeof(RuntimeClass_Windows_Globalization_Calendar) / sizeof(wchar_t) - 1,
    104      &calendar_class_id_header, &calendar_class_id);
    105  if (FAILED(hr)) {
    106    return result;
    107  }
    108 
    109  IInspectable* calendar;
    110  hr = ro_activate_instance(calendar_class_id, &calendar);
    111  if (FAILED(hr)) {
    112    return result;
    113  }
    114 
    115  ABI::Windows::Globalization::ITimeZoneOnCalendar* time_zone;
    116  hr = calendar->QueryInterface(IID_PPV_ARGS(&time_zone));
    117  if (FAILED(hr)) {
    118    calendar->Release();
    119    return result;
    120  }
    121 
    122  HSTRING tz_hstr;
    123  hr = time_zone->GetTimeZone(&tz_hstr);
    124  if (SUCCEEDED(hr)) {
    125    UINT32 wlen;
    126    const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen);
    127    if (tz_wstr) {
    128      const int size =
    129          WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
    130                              nullptr, 0, nullptr, nullptr);
    131      result.resize(static_cast<size_t>(size));
    132      WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
    133                          &result[0], size, nullptr, nullptr);
    134    }
    135    windows_delete_string(tz_hstr);
    136  }
    137  time_zone->Release();
    138  calendar->Release();
    139  return result;
    140 }
    141 #endif
    142 }  // namespace
    143 
    144 std::string time_zone::name() const { return effective_impl().Name(); }
    145 
    146 time_zone::absolute_lookup time_zone::lookup(
    147    const time_point<seconds>& tp) const {
    148  return effective_impl().BreakTime(tp);
    149 }
    150 
    151 time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
    152  return effective_impl().MakeTime(cs);
    153 }
    154 
    155 bool time_zone::next_transition(const time_point<seconds>& tp,
    156                                civil_transition* trans) const {
    157  return effective_impl().NextTransition(tp, trans);
    158 }
    159 
    160 bool time_zone::prev_transition(const time_point<seconds>& tp,
    161                                civil_transition* trans) const {
    162  return effective_impl().PrevTransition(tp, trans);
    163 }
    164 
    165 std::string time_zone::version() const { return effective_impl().Version(); }
    166 
    167 std::string time_zone::description() const {
    168  return effective_impl().Description();
    169 }
    170 
    171 const time_zone::Impl& time_zone::effective_impl() const {
    172  if (impl_ == nullptr) {
    173    // Dereferencing an implicit-UTC time_zone is expected to be
    174    // rare, so we don't mind paying a small synchronization cost.
    175    return *time_zone::Impl::UTC().impl_;
    176  }
    177  return *impl_;
    178 }
    179 
    180 bool load_time_zone(const std::string& name, time_zone* tz) {
    181  return time_zone::Impl::LoadTimeZone(name, tz);
    182 }
    183 
    184 time_zone utc_time_zone() {
    185  return time_zone::Impl::UTC();  // avoid name lookup
    186 }
    187 
    188 time_zone fixed_time_zone(const seconds& offset) {
    189  time_zone tz;
    190  load_time_zone(FixedOffsetToName(offset), &tz);
    191  return tz;
    192 }
    193 
    194 time_zone local_time_zone() {
    195  const char* zone = ":localtime";
    196 #if defined(__ANDROID__)
    197  char sysprop[PROP_VALUE_MAX];
    198  if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
    199    zone = sysprop;
    200  }
    201 #endif
    202 #if defined(__APPLE__)
    203  std::vector<char> buffer;
    204  CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
    205  if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
    206    CFStringEncoding encoding = kCFStringEncodingUTF8;
    207    CFIndex length = CFStringGetLength(tz_name);
    208    CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1;
    209    buffer.resize(static_cast<size_t>(max_size));
    210    if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) {
    211      zone = &buffer[0];
    212    }
    213  }
    214  CFRelease(tz_default);
    215 #endif
    216 #if defined(__Fuchsia__)
    217  std::string primary_tz;
    218  [&]() {
    219    // Note: We can't use the synchronous FIDL API here because it doesn't
    220    // allow timeouts; if the FIDL call failed, local_time_zone() would never
    221    // return.
    222 
    223    const zx::duration kTimeout = zx::msec(500);
    224 
    225    // Don't attach to the thread because otherwise the thread's dispatcher
    226    // would be set to null when the loop is destroyed, causing any other FIDL
    227    // code running on the same thread to crash.
    228    async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
    229 
    230    fuchsia::intl::PropertyProviderHandle handle;
    231    zx_status_t status = fdio_service_connect_by_name(
    232        fuchsia::intl::PropertyProvider::Name_,
    233        handle.NewRequest().TakeChannel().release());
    234    if (status != ZX_OK) {
    235      return;
    236    }
    237 
    238    fuchsia::intl::PropertyProviderPtr intl_provider;
    239    status = intl_provider.Bind(std::move(handle), loop.dispatcher());
    240    if (status != ZX_OK) {
    241      return;
    242    }
    243 
    244    intl_provider->GetProfile(
    245        [&loop, &primary_tz](fuchsia::intl::Profile profile) {
    246          if (!profile.time_zones().empty()) {
    247            primary_tz = profile.time_zones()[0].id;
    248          }
    249          loop.Quit();
    250        });
    251    loop.Run(zx::deadline_after(kTimeout));
    252  }();
    253 
    254  if (!primary_tz.empty()) {
    255    zone = primary_tz.c_str();
    256  }
    257 #endif
    258 #if defined(USE_WIN32_LOCAL_TIME_ZONE)
    259  // Use the WinRT Calendar class to get the local time zone. This feature is
    260  // available on Windows 10 and later. The library is dynamically linked to
    261  // maintain binary compatibility with Windows XP - Windows 7. On Windows 8,
    262  // The combase.dll API functions are available but the RoActivateInstance
    263  // call will fail for the Calendar class.
    264  std::string winrt_tz;
    265  const HMODULE combase =
    266      LoadLibraryEx(_T("combase.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
    267  if (combase) {
    268    const auto ro_initialize = reinterpret_cast<decltype(&::RoInitialize)>(
    269        GetProcAddress(combase, "RoInitialize"));
    270    const auto ro_uninitialize = reinterpret_cast<decltype(&::RoUninitialize)>(
    271        GetProcAddress(combase, "RoUninitialize"));
    272    if (ro_initialize && ro_uninitialize) {
    273      const HRESULT hr = ro_initialize(RO_INIT_MULTITHREADED);
    274      // RPC_E_CHANGED_MODE means that a previous RoInitialize call specified
    275      // a different concurrency model. The WinRT runtime is initialized and
    276      // should work for our purpose here, but we should *not* call
    277      // RoUninitialize because it's a failure.
    278      if (SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE) {
    279        winrt_tz = win32_local_time_zone(combase);
    280        if (SUCCEEDED(hr)) {
    281          ro_uninitialize();
    282        }
    283      }
    284    }
    285    FreeLibrary(combase);
    286  }
    287  if (!winrt_tz.empty()) {
    288    zone = winrt_tz.c_str();
    289  }
    290 #endif
    291 
    292  // Allow ${TZ} to override to default zone.
    293  char* tz_env = nullptr;
    294 #if defined(_MSC_VER)
    295  _dupenv_s(&tz_env, nullptr, "TZ");
    296 #else
    297  tz_env = std::getenv("TZ");
    298 #endif
    299  if (tz_env) zone = tz_env;
    300 
    301  // We only support the "[:]<zone-name>" form.
    302  if (*zone == ':') ++zone;
    303 
    304  // Map "localtime" to a system-specific name, but
    305  // allow ${LOCALTIME} to override the default name.
    306  char* localtime_env = nullptr;
    307  if (strcmp(zone, "localtime") == 0) {
    308 #if defined(_MSC_VER)
    309    // System-specific default is just "localtime".
    310    _dupenv_s(&localtime_env, nullptr, "LOCALTIME");
    311 #else
    312    zone = "/etc/localtime";  // System-specific default.
    313    localtime_env = std::getenv("LOCALTIME");
    314 #endif
    315    if (localtime_env) zone = localtime_env;
    316  }
    317 
    318  const std::string name = zone;
    319 #if defined(_MSC_VER)
    320  free(localtime_env);
    321  free(tz_env);
    322 #endif
    323 
    324  time_zone tz;
    325  load_time_zone(name, &tz);  // Falls back to UTC.
    326  // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
    327  // arrange for %z to generate "-0000" when we don't know the local
    328  // offset because the load_time_zone() failed and we're using UTC.
    329  return tz;
    330 }
    331 
    332 }  // namespace cctz
    333 }  // namespace time_internal
    334 ABSL_NAMESPACE_END
    335 }  // namespace absl