[winpr,timezone] Fix GetDynamicTimeZoneInformation

* Improve logging, use single function to dump data.
* Use GetDynamicTimeZoneInformation as man getter,
  GetTimeZoneInformation only retrieves a subset.
This commit is contained in:
akallabeth 2024-06-26 20:38:21 +02:00
parent 9b89d8fa23
commit 3c4c827358
No known key found for this signature in database
GPG Key ID: A49454A3FC909FD5
2 changed files with 179 additions and 91 deletions

View File

@ -414,6 +414,8 @@ static BOOL rdp_read_extended_info_packet(rdpRdp* rdp, wStream* s)
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return FALSE;
if (freerdp_settings_get_bool(settings, FreeRDP_SupportDynamicTimeZone))
{
UINT16 cbDynamicDSTTimeZoneKeyName = 0;

View File

@ -534,88 +534,132 @@ static LONG get_bias(const struct tm* start, BOOL dstBias)
return 0;
}
static BOOL map_iana_id(const char* iana, LPTIME_ZONE_INFORMATION tz)
static BOOL map_iana_id(const char* iana, LPDYNAMIC_TIME_ZONE_INFORMATION tz)
{
const char* winId = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_ID);
if (!winId)
return FALSE;
const char* winStd = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_STANDARD);
const char* winDst = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_DAYLIGHT);
ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName));
ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName));
if (winStd)
ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName));
if (winDst)
ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName));
if (winId)
ConvertUtf8ToWChar(winId, tz->TimeZoneKeyName, ARRAYSIZE(tz->TimeZoneKeyName));
return TRUE;
return winId != NULL;
}
static const char* weekday2str(WORD wDayOfWeek)
{
switch (wDayOfWeek)
{
case 0:
return "SUNDAY";
case 1:
return "MONDAY";
case 2:
return "TUESDAY";
case 3:
return "WEDNESDAY";
case 4:
return "THURSDAY";
case 5:
return "FRIDAY";
case 6:
return "SATURDAY";
default:
return "DAY-OF-MAGIC";
}
}
static char* systemtime2str(const SYSTEMTIME* t, char* buffer, size_t len)
{
const SYSTEMTIME empty = { 0 };
if (memcmp(t, &empty, sizeof(SYSTEMTIME)) == 0)
_snprintf(buffer, len, "{ not set }");
else
{
_snprintf(buffer, len,
"{ %" PRIu16 "-%" PRIu16 "-%" PRIu16 " [%s] %" PRIu16 ":%" PRIu16 ":%" PRIu16
".%" PRIu16 "}",
t->wYear, t->wMonth, t->wDay, weekday2str(t->wDayOfWeek), t->wHour, t->wMinute,
t->wSecond, t->wMilliseconds);
}
return buffer;
}
static void log_print(wLog* log, DWORD level, const char* file, const char* fkt, size_t line, ...)
{
if (!WLog_IsLevelActive(log, level))
return;
va_list ap;
va_start(ap, line);
WLog_PrintMessageVA(log, WLOG_MESSAGE_TEXT, level, line, file, fkt, ap);
va_end(ap);
}
#define log_timezone(tzif, result) log_timezone_((tzif), (result), __FILE__, __func__, __LINE__)
static void log_timezone_(const DYNAMIC_TIME_ZONE_INFORMATION* tzif, DWORD result, const char* file,
const char* fkt, size_t line)
{
WINPR_ASSERT(tzif);
char buffer[130] = { 0 };
DWORD level = WLOG_INFO;
wLog* log = WLog_Get(TAG);
log_print(log, level, file, fkt, line, "DYNAMIC_TIME_ZONE_INFORMATION {");
log_print(log, level, file, fkt, line, " Bias=%" PRIu32, tzif->Bias);
ConvertWCharNToUtf8(tzif->StandardName, ARRAYSIZE(tzif->StandardName), buffer,
ARRAYSIZE(buffer));
log_print(log, level, file, fkt, line, " StandardName=%s", buffer);
log_print(log, level, file, fkt, line, " StandardDate=%s",
systemtime2str(&tzif->StandardDate, buffer, sizeof(buffer)));
log_print(log, level, file, fkt, line, " StandardBias=%" PRIu32, tzif->StandardBias);
ConvertWCharNToUtf8(tzif->DaylightName, ARRAYSIZE(tzif->DaylightName), buffer,
ARRAYSIZE(buffer));
log_print(log, level, file, fkt, line, " DaylightName=%s", buffer);
log_print(log, level, file, fkt, line, " DaylightDate=%s",
systemtime2str(&tzif->DaylightDate, buffer, sizeof(buffer)));
log_print(log, level, file, fkt, line, " DaylightBias=%" PRIu32, tzif->DaylightBias);
ConvertWCharNToUtf8(tzif->TimeZoneKeyName, ARRAYSIZE(tzif->TimeZoneKeyName), buffer,
ARRAYSIZE(buffer));
log_print(log, level, file, fkt, line, " TimeZoneKeyName=%s", buffer);
log_print(log, level, file, fkt, line, " DynamicDaylightTimeDisabled=DST-%s",
tzif->DynamicDaylightTimeDisabled ? "disabled" : "enabled");
switch (result)
{
case TIME_ZONE_ID_DAYLIGHT:
log_print(log, level, file, fkt, line, " DaylightDate in use");
break;
case TIME_ZONE_ID_STANDARD:
log_print(log, level, file, fkt, line, " StandardDate in use");
break;
default:
log_print(log, level, file, fkt, line, " UnknownDate in use");
break;
}
log_print(log, level, file, fkt, line, "}");
}
DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
{
const char** list = NULL;
char* tzid = NULL;
const char* defaultName = "Client Local Time";
DWORD res = TIME_ZONE_ID_UNKNOWN;
const TIME_ZONE_INFORMATION empty = { 0 };
LPTIME_ZONE_INFORMATION tz = lpTimeZoneInformation;
WINPR_ASSERT(tz);
*tz = empty;
ConvertUtf8ToWChar(defaultName, tz->StandardName, ARRAYSIZE(tz->StandardName));
const time_t t = time(NULL);
struct tm tres = { 0 };
struct tm* local_time = localtime_r(&t, &tres);
if (!local_time)
goto out_error;
tz->Bias = get_bias(local_time, FALSE);
if (local_time->tm_isdst >= 0)
{
/* DST bias is the difference between standard time and DST in minutes */
const LONG d = get_bias(local_time, TRUE);
tz->DaylightBias = -1 * labs(tz->Bias - d);
get_transition_date(local_time, FALSE, &tz->StandardDate);
get_transition_date(local_time, TRUE, &tz->DaylightDate);
}
tzid = winpr_guess_time_zone();
if (!map_iana_id(tzid, tz))
{
const size_t len = TimeZoneIanaAbbrevGet(local_time->tm_zone, NULL, 0);
list = calloc(len, sizeof(char*));
if (!list)
goto out_error;
const size_t size = TimeZoneIanaAbbrevGet(local_time->tm_zone, list, len);
for (size_t x = 0; x < size; x++)
{
const char* id = list[x];
if (map_iana_id(id, tz))
{
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
break;
}
}
}
else
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
out_error:
free(tzid);
free(list);
switch (res)
{
case TIME_ZONE_ID_DAYLIGHT:
case TIME_ZONE_ID_STANDARD:
WLog_DBG(TAG, "tz: Bias=%" PRId32 " sn='%s' dln='%s'", tz->Bias, tz->StandardName,
tz->DaylightName);
break;
default:
WLog_DBG(TAG, "tz not found, using computed bias %" PRId32 ".", tz->Bias);
break;
}
return res;
DYNAMIC_TIME_ZONE_INFORMATION dyn = { 0 };
DWORD rc = GetDynamicTimeZoneInformation(&dyn);
lpTimeZoneInformation->Bias = dyn.Bias;
lpTimeZoneInformation->DaylightBias = dyn.DaylightBias;
lpTimeZoneInformation->DaylightDate = dyn.DaylightDate;
lpTimeZoneInformation->StandardBias = dyn.StandardBias;
lpTimeZoneInformation->StandardDate = dyn.StandardDate;
memcpy(lpTimeZoneInformation->StandardName, dyn.StandardName,
sizeof(lpTimeZoneInformation->StandardName));
memcpy(lpTimeZoneInformation->DaylightName, dyn.DaylightName,
sizeof(lpTimeZoneInformation->DaylightName));
return rc;
}
BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation)
@ -666,30 +710,72 @@ BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformati
(defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \
!defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */
DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation)
DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION tz)
{
TIME_ZONE_INFORMATION tz = { 0 };
const DWORD rc = GetTimeZoneInformation(&tz);
BOOL doesNotHaveStandardDate = FALSE;
BOOL doesNotHaveDaylightDate = FALSE;
const char** list = NULL;
char* tzid = NULL;
const char* defaultName = "Client Local Time";
DWORD res = TIME_ZONE_ID_UNKNOWN;
const DYNAMIC_TIME_ZONE_INFORMATION empty = { 0 };
WINPR_ASSERT(pTimeZoneInformation);
pTimeZoneInformation->Bias = tz.Bias;
memcpy(pTimeZoneInformation->StandardName, tz.StandardName,
MIN(sizeof(tz.StandardName), sizeof(pTimeZoneInformation->StandardName)));
pTimeZoneInformation->StandardDate = tz.StandardDate;
pTimeZoneInformation->StandardBias = tz.StandardBias;
WINPR_ASSERT(tz);
memcpy(pTimeZoneInformation->DaylightName, tz.DaylightName,
MIN(sizeof(tz.DaylightName), sizeof(pTimeZoneInformation->DaylightName)));
pTimeZoneInformation->DaylightDate = tz.DaylightDate;
pTimeZoneInformation->DaylightBias = tz.DaylightBias;
*tz = empty;
ConvertUtf8ToWChar(defaultName, tz->StandardName, ARRAYSIZE(tz->StandardName));
memcpy(pTimeZoneInformation->TimeZoneKeyName, tz.StandardName,
MIN(sizeof(tz.StandardName), sizeof(pTimeZoneInformation->TimeZoneKeyName)));
const time_t t = time(NULL);
struct tm tres = { 0 };
struct tm* local_time = localtime_r(&t, &tres);
if (!local_time)
goto out_error;
/* https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information
*/
pTimeZoneInformation->DynamicDaylightTimeDisabled = FALSE;
return rc;
tz->Bias = get_bias(local_time, FALSE);
if (local_time->tm_isdst >= 0)
{
/* DST bias is the difference between standard time and DST in minutes */
const LONG d = get_bias(local_time, TRUE);
tz->DaylightBias = -1 * labs(tz->Bias - d);
if (!get_transition_date(local_time, FALSE, &tz->StandardDate))
doesNotHaveStandardDate = TRUE;
if (!get_transition_date(local_time, TRUE, &tz->DaylightDate))
doesNotHaveDaylightDate = TRUE;
}
tzid = winpr_guess_time_zone();
if (!map_iana_id(tzid, tz))
{
const size_t len = TimeZoneIanaAbbrevGet(local_time->tm_zone, NULL, 0);
list = calloc(len, sizeof(char*));
if (!list)
goto out_error;
const size_t size = TimeZoneIanaAbbrevGet(local_time->tm_zone, list, len);
for (size_t x = 0; x < size; x++)
{
const char* id = list[x];
if (map_iana_id(id, tz))
{
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
break;
}
}
}
else
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
if (doesNotHaveDaylightDate)
tz->DaylightBias = 0;
if (doesNotHaveStandardDate)
tz->StandardBias = 0;
out_error:
free(tzid);
free(list);
log_timezone(tz, res);
return res;
}
BOOL SetDynamicTimeZoneInformation(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation)