From d68a25daaf1433c780d93d610b68323fe47a132f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Mon, 15 Aug 2022 07:26:17 +0200 Subject: [PATCH] Allow non UTF-8 time zones Though it is unlikely that the time zone is stored in an encoding other than UTF-8, it's not much work add to support for this edge case. --- src/tz_macos.rs | 68 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/tz_macos.rs b/src/tz_macos.rs index 6e831fd..41ead59 100644 --- a/src/tz_macos.rs +++ b/src/tz_macos.rs @@ -1,22 +1,62 @@ -use core_foundation_sys::base::{CFRelease, CFTypeRef}; -use core_foundation_sys::string::{kCFStringEncodingUTF8, CFStringGetCStringPtr}; +use core_foundation_sys::base::{Boolean, CFRange, CFRelease, CFTypeRef}; +use core_foundation_sys::string::{ + kCFStringEncodingUTF8, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength, +}; use core_foundation_sys::timezone::{CFTimeZoneCopySystem, CFTimeZoneGetName}; pub(crate) fn get_timezone_inner() -> Result { - unsafe { - if let Some(tz) = Dropping::new(CFTimeZoneCopySystem()) { - if let Some(name) = Dropping::new(CFTimeZoneGetName(tz.0)) { - let name = CFStringGetCStringPtr(name.0, kCFStringEncodingUTF8); - if !name.is_null() { - let name = std::ffi::CStr::from_ptr(name); - if let Ok(name) = name.to_str() { - return Ok(name.to_owned()); - } - } - } + unsafe { get_timezone().ok_or(crate::GetTimezoneError::OsError) } +} + +#[inline] +unsafe fn get_timezone() -> Option { + // The longest name in the IANA time zone database is 25 ASCII characters long. + const MAX_LEN: usize = 32; + + // Get system time zone, and borrow its name. + let tz = Dropping::new(CFTimeZoneCopySystem())?; + let name = CFTimeZoneGetName(tz.0); + if name.is_null() { + return None; + } + + // If the name is encoded in UTF-8, copy it directly. + let cstr = CFStringGetCStringPtr(name, kCFStringEncodingUTF8); + if !cstr.is_null() { + let cstr = std::ffi::CStr::from_ptr(cstr); + if let Ok(name) = cstr.to_str() { + return Some(name.to_owned()); } } - Err(crate::GetTimezoneError::OsError) + + // Otherwise convert the name to UTF-8. + let mut buf = [0; MAX_LEN]; + let mut buf_bytes = 0; + let range = CFRange { + location: 0, + length: CFStringGetLength(name), + }; + if CFStringGetBytes( + name, + range, + kCFStringEncodingUTF8, + b'\0', + false as Boolean, + buf.as_mut_ptr(), + buf.len() as isize, + &mut buf_bytes, + ) != range.length + { + // Could not convert the name. + None + } else if !(1..MAX_LEN as isize).contains(&buf_bytes) { + // The name should not be empty, or excessively long. + None + } else { + // Convert the name to a `String`. + let name = core::str::from_utf8(&buf[..buf_bytes as usize]).ok()?; + Some(name.to_owned()) + } } struct Dropping(*const T);