Merge pull request #10391 from akallabeth/timezone_impro

Timezone improved
This commit is contained in:
akallabeth 2024-07-19 12:45:08 +02:00 committed by GitHub
commit 1fe61c9432
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 231 additions and 41 deletions

View File

@ -30,6 +30,7 @@
#include <winpr/path.h>
#include <winpr/ncrypt.h>
#include <winpr/environment.h>
#include <winpr/timezone.h>
#include <freerdp/freerdp.h>
#include <freerdp/addin.h>
@ -1666,11 +1667,24 @@ static void freerdp_client_print_keyboard_list(void)
RDP_KEYBOARD_LAYOUT_TYPE_IME);
}
static void freerdp_client_print_timezone_list(void)
{
DWORD index = 0;
DYNAMIC_TIME_ZONE_INFORMATION info = { 0 };
while (EnumDynamicTimeZoneInformation(index++, &info) != ERROR_NO_MORE_ITEMS)
{
char TimeZoneKeyName[ARRAYSIZE(info.TimeZoneKeyName) + 1] = { 0 };
ConvertWCharNToUtf8(info.TimeZoneKeyName, ARRAYSIZE(info.TimeZoneKeyName), TimeZoneKeyName,
ARRAYSIZE(TimeZoneKeyName));
printf("%" PRIu32 ": '%s'\n", index, TimeZoneKeyName);
}
}
static void freerdp_client_print_tune_list(const rdpSettings* settings)
{
SSIZE_T type = 0;
printf("%s\t%50s\t%s\t%s", "<index>", "<key>", "<type>", "<default value>\n");
for (size_t x = 0; x < FreeRDP_Settings_StableAPI_MAX; x++)
{
const char* name = freerdp_settings_get_name_for_key(x);
@ -1755,7 +1769,9 @@ int freerdp_client_settings_command_line_status_print_ex(rdpSettings* settings,
if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)
{
if (option_equals("tune", arg->Value))
if (option_equals("timezones", arg->Value))
freerdp_client_print_timezone_list();
else if (option_equals("tune", arg->Value))
freerdp_client_print_tune_list(settings);
else if (option_equals("kbd", arg->Value))
freerdp_client_print_keyboard_list();
@ -4718,6 +4734,43 @@ static int freerdp_client_settings_parse_command_line_arguments_int(
if (!freerdp_settings_set_uint32(settings, FreeRDP_TcpAckTimeout, (UINT32)val))
return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
}
CommandLineSwitchCase(arg, "timezone")
{
BOOL found = FALSE;
DWORD index = 0;
DYNAMIC_TIME_ZONE_INFORMATION info = { 0 };
char TimeZoneKeyName[ARRAYSIZE(info.TimeZoneKeyName) + 1] = { 0 };
while (EnumDynamicTimeZoneInformation(index++, &info) != ERROR_NO_MORE_ITEMS)
{
ConvertWCharNToUtf8(info.TimeZoneKeyName, ARRAYSIZE(info.TimeZoneKeyName),
TimeZoneKeyName, ARRAYSIZE(TimeZoneKeyName));
if (strncmp(TimeZoneKeyName, arg->Value, ARRAYSIZE(TimeZoneKeyName)) == 0)
{
found = TRUE;
break;
}
}
if (!found)
return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
if (!freerdp_settings_set_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName,
TimeZoneKeyName))
return fail_at(arg, COMMAND_LINE_ERROR);
TIME_ZONE_INFORMATION* tz =
freerdp_settings_get_pointer_writable(settings, FreeRDP_ClientTimeZone);
if (!tz)
return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
tz->Bias = info.Bias;
tz->DaylightBias = info.DaylightBias;
tz->DaylightDate = info.DaylightDate;
memcpy(tz->DaylightName, info.DaylightName, sizeof(tz->DaylightName));
tz->StandardBias = info.StandardBias;
tz->StandardDate = info.StandardDate;
memcpy(tz->StandardName, info.StandardName, sizeof(tz->StandardName));
}
CommandLineSwitchCase(arg, "aero")
{
if (!freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, enable))

View File

@ -297,7 +297,7 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
{ "list", COMMAND_LINE_VALUE_REQUIRED | COMMAND_LINE_PRINT,
"[kbd|kbd-scancode|kbd-lang[:<value>]|smartcard[:[pkinit-anchors:<path>][,pkcs11-module:<"
"name>]]|"
"monitor|tune]",
"monitor|tune|timezones]",
"List available options for subcommand", NULL, -1, NULL,
"List available options for subcommand" },
{ "log-filters", COMMAND_LINE_VALUE_REQUIRED, "<tag>:<level>[,<tag>:<level>[,...]]", NULL, NULL,
@ -453,6 +453,9 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
{ "timeout", COMMAND_LINE_VALUE_REQUIRED, "<time in ms>", "9000", NULL, -1, "timeout",
"Advanced setting for high latency links: Adjust connection timeout, use if you encounter "
"timeout failures with your connection" },
{ "timezone", COMMAND_LINE_VALUE_REQUIRED, "<windows timezone>", NULL, NULL, -1, NULL,
"Use supplied windows timezone for connection (requires server support), see /list:timezones "
"for allowed values" },
{ "tls", COMMAND_LINE_VALUE_REQUIRED, "[ciphers|seclevel|secrets-file|enforce]", NULL, NULL, -1,
NULL,
"TLS configuration options:"

View File

@ -110,6 +110,8 @@ extern "C"
const PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, LPDWORD FirstYear,
LPDWORD LastYear);
#else
#pragma comment(lib, "advapi32")
#endif
#ifdef __cplusplus

View File

@ -19,12 +19,12 @@ set(SRCS
TimeZoneNameMap.c
TimeZoneNameMap.h
TimeZoneNameMapUtils.c
timezone.c
)
if (NOT WIN32)
list(APPEND SRCS
TimeZoneIanaAbbrevMap.c
TimeZoneIanaAbbrevMap.h
timezone.c
)
endif()

View File

@ -41,9 +41,7 @@ typedef struct
const char* Iana;
} TimeZoneNameMapEntry;
extern const TimeZoneNameMapEntry TimeZoneNameMap[];
extern const size_t TimeZoneNameMapSize;
const TimeZoneNameMapEntry* TimeZoneGetAt(size_t index);
const char* TimeZoneIanaToWindows(const char* iana, TimeZoneNameType type);
#endif

View File

@ -20,6 +20,7 @@
#include <winpr/config.h>
#include <winpr/assert.h>
#include <winpr/string.h>
#include <winpr/synch.h>
#include <string.h>
@ -31,6 +32,36 @@
#include "TimeZoneNameMap.h"
typedef struct
{
size_t count;
TimeZoneNameMapEntry* entries;
} TimeZoneNameMapContext;
static BOOL CALLBACK load_timezones(PINIT_ONCE once, PVOID param, PVOID* pvcontext)
{
// Do not expose these, only used internally.
extern const TimeZoneNameMapEntry TimeZoneNameMap[];
extern const size_t TimeZoneNameMapSize;
TimeZoneNameMapContext* context = pvcontext;
WINPR_ASSERT(context);
context->count = TimeZoneNameMapSize;
context->entries = TimeZoneNameMap;
}
const TimeZoneNameMapEntry* TimeZoneGetAt(size_t index)
{
static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT;
static TimeZoneNameMapContext context = { 0 };
InitOnceExecuteOnce(&init_guard, load_timezones, NULL, &context);
if (index >= context.count)
return NULL;
return &context.entries[index];
}
static const char* return_type(const TimeZoneNameMapEntry* entry, TimeZoneNameType type)
{
WINPR_ASSERT(entry);
@ -51,6 +82,37 @@ static const char* return_type(const TimeZoneNameMapEntry* entry, TimeZoneNameTy
}
}
static BOOL iana_cmp(const TimeZoneNameMapEntry* entry, const char* iana)
{
if (!entry || !iana || !entry->Iana)
return FALSE;
return strcmp(iana, entry->Iana) == 0;
}
static BOOL id_cmp(const TimeZoneNameMapEntry* entry, const char* id)
{
if (!entry || !id || !entry->Id)
return FALSE;
return strcmp(id, entry->Id) == 0;
}
static const char* get_for_type(const char* val, TimeZoneNameType type,
BOOL (*cmp)(const TimeZoneNameMapEntry*, const char*))
{
WINPR_ASSERT(val);
WINPR_ASSERT(cmp);
size_t index = 0;
while (TRUE)
{
const TimeZoneNameMapEntry* entry = TimeZoneGetAt(index++);
if (!entry)
return NULL;
if (cmp(entry, val))
return return_type(entry, type);
}
}
#if defined(WITH_TIMEZONE_ICU)
static char* get_wzid_icu(const UChar* utzid, size_t utzid_len)
{
@ -88,21 +150,11 @@ static char* get(const char* iana)
static const char* map_fallback(const char* iana, TimeZoneNameType type)
{
const char* res = NULL;
char* wzid = get(iana);
if (!wzid)
return NULL;
for (size_t x = 0; x < TimeZoneNameMapSize; x++)
{
const TimeZoneNameMapEntry* entry = &TimeZoneNameMap[x];
if (strcmp(wzid, entry->Id) == 0)
{
res = return_type(entry, type);
break;
}
}
const char* res = get_for_type(wzid, type, id_cmp);
free(wzid);
return res;
}
@ -149,22 +201,13 @@ const char* TimeZoneIanaToWindows(const char* iana, TimeZoneNameType type)
if (!iana)
return NULL;
for (size_t x = 0; x < TimeZoneNameMapSize; x++)
{
const TimeZoneNameMapEntry* entry = &TimeZoneNameMap[x];
if (strcmp(iana, entry->Iana) == 0)
return return_type(entry, type);
}
const char* val = get_for_type(iana, type, iana_cmp);
if (val)
return val;
const char* wzid = map_fallback(iana, type);
if (!wzid)
return NULL;
for (size_t x = 0; x < TimeZoneNameMapSize; x++)
{
const TimeZoneNameMapEntry* entry = &TimeZoneNameMap[x];
if (strcmp(wzid, entry->Id) == 0)
return return_type(entry, type);
}
return NULL;
return get_for_type(wzid, type, id_cmp);
}

View File

@ -710,6 +710,34 @@ BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformati
(defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \
!defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */
typedef enum
{
HAVE_TRANSITION_DATES = 0,
HAVE_NO_STANDARD_TRANSITION_DATE = 1,
HAVE_NO_DAYLIGHT_TRANSITION_DATE = 2
} dyn_transition_result;
static int dynamic_time_zone_from_localtime(const struct tm* local_time,
PDYNAMIC_TIME_ZONE_INFORMATION tz)
{
WINPR_ASSERT(local_time);
WINPR_ASSERT(tz);
int rc = HAVE_TRANSITION_DATES;
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))
rc |= HAVE_NO_STANDARD_TRANSITION_DATE;
if (!get_transition_date(local_time, TRUE, &tz->DaylightDate))
rc |= HAVE_NO_DAYLIGHT_TRANSITION_DATE;
}
return rc;
}
DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION tz)
{
BOOL doesNotHaveStandardDate = FALSE;
@ -734,12 +762,10 @@ DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION tz)
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))
const int rc = dynamic_time_zone_from_localtime(local_time, tz);
if (rc & HAVE_NO_STANDARD_TRANSITION_DATE)
doesNotHaveStandardDate = TRUE;
if (!get_transition_date(local_time, TRUE, &tz->DaylightDate))
if (rc & HAVE_NO_DAYLIGHT_TRANSITION_DATE)
doesNotHaveDaylightDate = TRUE;
}
@ -817,14 +843,62 @@ BOOL TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTi
#endif
#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0602)) /* Windows 8 */
#if !defined(_WIN32)
DWORD EnumDynamicTimeZoneInformation(const DWORD dwIndex,
PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation)
{
WINPR_UNUSED(dwIndex);
WINPR_UNUSED(lpTimeZoneInformation);
return 0;
if (!lpTimeZoneInformation)
return ERROR_INVALID_PARAMETER;
const DYNAMIC_TIME_ZONE_INFORMATION empty = { 0 };
*lpTimeZoneInformation = empty;
const TimeZoneNameMapEntry* entry = TimeZoneGetAt(dwIndex);
if (!entry)
return ERROR_NO_MORE_ITEMS;
if (entry->DaylightName)
ConvertUtf8ToWChar(entry->DaylightName, lpTimeZoneInformation->DaylightName,
ARRAYSIZE(lpTimeZoneInformation->DaylightName));
if (entry->StandardName)
ConvertUtf8ToWChar(entry->StandardName, lpTimeZoneInformation->StandardName,
ARRAYSIZE(lpTimeZoneInformation->StandardName));
if (entry->Id)
ConvertUtf8ToWChar(entry->Id, lpTimeZoneInformation->TimeZoneKeyName,
ARRAYSIZE(lpTimeZoneInformation->TimeZoneKeyName));
const time_t t = time(NULL);
struct tm tres = { 0 };
const char* tz = getenv("TZ");
char* tzcopy = NULL;
if (tz)
{
size_t tzianalen = 0;
winpr_asprintf(&tzcopy, &tzianalen, "TZ=%s", tz);
}
char* tziana = NULL;
{
size_t tzianalen = 0;
winpr_asprintf(&tziana, &tzianalen, "TZ=%s", entry->Iana);
}
if (tziana)
putenv(tziana);
tzset();
struct tm* local_time = localtime_r(&t, &tres);
free(tziana);
if (tzcopy)
putenv(tzcopy);
else
unsetenv("TZ");
free(tzcopy);
if (local_time)
dynamic_time_zone_from_localtime(local_time, lpTimeZoneInformation);
return ERROR_SUCCESS;
}
DWORD GetDynamicTimeZoneInformationEffectiveYears(
@ -833,7 +907,24 @@ DWORD GetDynamicTimeZoneInformationEffectiveYears(
WINPR_UNUSED(lpTimeZoneInformation);
WINPR_UNUSED(FirstYear);
WINPR_UNUSED(LastYear);
return 0;
return ERROR_FILE_NOT_FOUND;
}
#elif _WIN32_WINNT < 0x0602 /* Windows 8 */
DWORD EnumDynamicTimeZoneInformation(const DWORD dwIndex,
PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation)
{
WINPR_UNUSED(dwIndex);
WINPR_UNUSED(lpTimeZoneInformation);
return ERROR_NO_MORE_ITEMS;
}
DWORD GetDynamicTimeZoneInformationEffectiveYears(
const PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear)
{
WINPR_UNUSED(lpTimeZoneInformation);
WINPR_UNUSED(FirstYear);
WINPR_UNUSED(LastYear);
return ERROR_FILE_NOT_FOUND;
}
#endif