diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 4857559f4..e7041bdc9 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -773,6 +773,28 @@ extern "C" const rdpMonitor* monitors, size_t count); + /** @brief A function that converts a \b rdpSettings struct to a \b JSON serialized string. + * + * @param settings The settings instance to serialize + * @param pretty Format the resulting \b JSON human readable + * @param plength An optional pointer that receives the length (strlen) of the returned string. + * @return A \b JSON string representing the serialized form of the \b rdpSettings or \b NULL + * in case of an error. + * @since version 3.16.0 + */ + FREERDP_API char* freerdp_settings_serialize(const rdpSettings* settings, BOOL pretty, + size_t* plength); + + /** @brief A function that converts a \b JSON string to a \b rdpSettings struct + * + * @param json The \b JSON string + * @param length The strlen of the \b JSON string + * @return An allocated \b rdpSettings struct or \b NULL in case of an error + * @since version 3.16.0 + */ + WINPR_ATTR_MALLOC(freerdp_settings_free, 1) + FREERDP_API rdpSettings* freerdp_settings_deserialize(const char* json, size_t length); + #ifdef __cplusplus } #endif diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index 9119893e2..57fe723d4 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -232,7 +233,8 @@ BOOL freerdp_device_collection_add(rdpSettings* settings, RDPDR_DEVICE* device) return FALSE; settings->DeviceArray = new_array; - memset((void*)&settings->DeviceArray[old], 0, (new_size - old) * sizeof(RDPDR_DEVICE*)); + for (size_t x = 0; x < new_size; x++) + settings->DeviceArray[x] = NULL; if (!freerdp_settings_set_uint32(settings, FreeRDP_DeviceArraySize, new_size)) return FALSE; @@ -592,13 +594,12 @@ BOOL freerdp_static_channel_collection_del(rdpSettings* settings, const char* na { if (strcmp(name, cur->argv[0]) == 0) { - const size_t rem = settings->StaticChannelArraySize - count + 1; memmove_s((void*)&settings->StaticChannelArray[x], (count - x) * sizeof(ADDIN_ARGV*), (void*)&settings->StaticChannelArray[x + 1], (count - x - 1) * sizeof(ADDIN_ARGV*)); - memset((void*)&settings->StaticChannelArray[count - 1], 0, - sizeof(ADDIN_ARGV*) * rem); + for (size_t y = count - 1; y < settings->StaticChannelArraySize; y++) + settings->StaticChannelArray[y] = NULL; freerdp_addin_argv_free(cur); return freerdp_settings_set_uint32(settings, FreeRDP_StaticChannelCount, count - 1); @@ -606,8 +607,8 @@ BOOL freerdp_static_channel_collection_del(rdpSettings* settings, const char* na } } { - const size_t rem = settings->StaticChannelArraySize - count; - memset((void*)&settings->StaticChannelArray[count], 0, sizeof(ADDIN_ARGV*) * rem); + for (size_t x = count; x < settings->StaticChannelArraySize; x++) + settings->StaticChannelArray[x] = NULL; } return FALSE; } @@ -637,8 +638,8 @@ BOOL freerdp_static_channel_collection_add(rdpSettings* settings, ADDIN_ARGV* ch settings->StaticChannelArray = new_array; { - const size_t rem = new_size - oldSize; - memset((void*)&settings->StaticChannelArray[oldSize], 0, sizeof(ADDIN_ARGV*) * rem); + for (size_t x = oldSize; x < new_size; x++) + settings->StaticChannelArray[x] = NULL; } if (!freerdp_settings_set_uint32(settings, FreeRDP_StaticChannelArraySize, new_size)) return FALSE; @@ -702,13 +703,12 @@ BOOL freerdp_dynamic_channel_collection_del(rdpSettings* settings, const char* n { if (strcmp(name, cur->argv[0]) == 0) { - const size_t rem = settings->DynamicChannelArraySize - count + 1; memmove_s((void*)&settings->DynamicChannelArray[x], (count - x) * sizeof(ADDIN_ARGV*), (void*)&settings->DynamicChannelArray[x + 1], (count - x - 1) * sizeof(ADDIN_ARGV*)); - memset((void*)&settings->DynamicChannelArray[count - 1], 0, - sizeof(ADDIN_ARGV*) * rem); + for (size_t y = count - 1; y < settings->DynamicChannelArraySize; y++) + settings->DynamicChannelArray[y] = NULL; freerdp_addin_argv_free(cur); return freerdp_settings_set_uint32(settings, FreeRDP_DynamicChannelCount, @@ -745,8 +745,8 @@ BOOL freerdp_dynamic_channel_collection_add(rdpSettings* settings, ADDIN_ARGV* c settings->DynamicChannelArray = new_array; { - const size_t rem = size - oldSize; - memset((void*)&settings->DynamicChannelArray[oldSize], 0, sizeof(ADDIN_ARGV*) * rem); + for (size_t x = oldSize; x < size; x++) + settings->DynamicChannelArray[x] = NULL; } if (!freerdp_settings_set_uint32(settings, FreeRDP_DynamicChannelArraySize, size)) return FALSE; @@ -854,27 +854,85 @@ void freerdp_dynamic_channel_collection_free(rdpSettings* settings) (void)freerdp_settings_set_uint32(settings, FreeRDP_DynamicChannelCount, 0); } -void freerdp_capability_buffer_free(rdpSettings* settings) +static void freerdp_capability_data_free(rdpSettings* settings, size_t offset, BOOL full) { WINPR_ASSERT(settings); if (settings->ReceivedCapabilityData) { - for (UINT32 x = 0; x < settings->ReceivedCapabilitiesSize; x++) + for (size_t x = offset; x < settings->ReceivedCapabilitiesSize; x++) { free(settings->ReceivedCapabilityData[x]); settings->ReceivedCapabilityData[x] = NULL; } + if (full) + { + free((void*)settings->ReceivedCapabilityData); + settings->ReceivedCapabilityData = NULL; + } } - settings->ReceivedCapabilitiesSize = 0; +} + +void freerdp_capability_buffer_free(rdpSettings* settings) +{ + WINPR_ASSERT(settings); + + freerdp_capability_data_free(settings, 0, TRUE); free(settings->ReceivedCapabilityDataSizes); settings->ReceivedCapabilityDataSizes = NULL; - free((void*)settings->ReceivedCapabilityData); - settings->ReceivedCapabilityData = NULL; free(settings->ReceivedCapabilities); settings->ReceivedCapabilities = NULL; + + settings->ReceivedCapabilitiesSize = 0; +} + +static BOOL resize_setting(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, size_t oldsize, + size_t size, size_t base) +{ + void* old = freerdp_settings_get_pointer_writable(settings, id); + uint8_t* ptr = realloc(old, size * base); + if (!ptr) + return FALSE; + + if (size > oldsize) + { + const size_t diff = size - oldsize; + memset(&ptr[oldsize * base], 0, diff * base); + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc + return freerdp_settings_set_pointer(settings, id, ptr); +} + +BOOL freerdp_capability_buffer_resize(rdpSettings* settings, size_t count) +{ + WINPR_ASSERT(settings); + + const uint32_t len = settings->ReceivedCapabilitiesSize; + if (len == count) + return TRUE; + + freerdp_capability_data_free(settings, count, FALSE); + + if (count == 0) + { + freerdp_capability_buffer_free(settings); + return TRUE; + } + + const size_t oldsize = settings->ReceivedCapabilitiesSize; + if (!resize_setting(settings, FreeRDP_ReceivedCapabilityDataSizes, oldsize, count, + sizeof(uint32_t))) + return FALSE; + if (!resize_setting(settings, FreeRDP_ReceivedCapabilityData, oldsize, count, sizeof(uint8_t*))) + return FALSE; + if (!resize_setting(settings, FreeRDP_ReceivedCapabilities, oldsize, count, sizeof(uint32_t))) + return FALSE; + + settings->ReceivedCapabilitiesSize = WINPR_ASSERTING_INT_CAST(uint32_t, count); + return TRUE; } BOOL freerdp_capability_buffer_copy(rdpSettings* settings, const rdpSettings* src) @@ -885,7 +943,7 @@ BOOL freerdp_capability_buffer_copy(rdpSettings* settings, const rdpSettings* sr if (src->ReceivedCapabilitiesSize == 0) return TRUE; - if (!freerdp_capability_buffer_allocate(settings, src->ReceivedCapabilitiesSize)) + if (!freerdp_capability_buffer_resize(settings, src->ReceivedCapabilitiesSize)) return FALSE; for (UINT32 x = 0; x < src->ReceivedCapabilitiesSize; x++) @@ -915,21 +973,59 @@ BOOL freerdp_capability_buffer_copy(rdpSettings* settings, const rdpSettings* sr return TRUE; } -void freerdp_target_net_addresses_free(rdpSettings* settings) +static void target_net_addresses_free(rdpSettings* settings, size_t offset) { WINPR_ASSERT(settings); if (settings->TargetNetAddresses) { - for (UINT32 index = 0; index < settings->TargetNetAddressCount; index++) + for (size_t index = offset; index < settings->TargetNetAddressCount; index++) + { free(settings->TargetNetAddresses[index]); + settings->TargetNetAddresses[index] = NULL; + } } +} + +void freerdp_target_net_addresses_free(rdpSettings* settings) +{ + WINPR_ASSERT(settings); + + target_net_addresses_free(settings, 0); free((void*)settings->TargetNetAddresses); - free(settings->TargetNetPorts); - settings->TargetNetAddressCount = 0; settings->TargetNetAddresses = NULL; + + free(settings->TargetNetPorts); settings->TargetNetPorts = NULL; + + settings->TargetNetAddressCount = 0; +} + +BOOL freerdp_target_net_addresses_resize(rdpSettings* settings, size_t count) +{ + WINPR_ASSERT(settings); + + if (count == 0) + { + freerdp_target_net_addresses_free(settings); + return TRUE; + } + + const uint32_t len = settings->TargetNetAddressCount; + size_t offset = 0; + if (len > count) + offset = count; + + target_net_addresses_free(settings, offset); + + if (!resize_setting(settings, FreeRDP_TargetNetAddresses, len, count, sizeof(char*))) + return FALSE; + if (!resize_setting(settings, FreeRDP_TargetNetPorts, len, count, sizeof(uint32_t))) + return FALSE; + + settings->TargetNetAddressCount = WINPR_ASSERTING_INT_CAST(uint32_t, count); + return TRUE; } void freerdp_server_license_issuers_free(rdpSettings* settings) @@ -1428,35 +1524,30 @@ BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, FreeRDP_Settings_Ke return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_ServerCertificateLength, data, len, sizeof(char)); case FreeRDP_TargetNetAddresses: - if ((data == NULL) && (len == 0)) - { - freerdp_target_net_addresses_free(settings); - return TRUE; - } - WLog_WARN( - TAG, - "[BUG] FreeRDP_TargetNetAddresses must not be resized from outside the library!"); - return FALSE; + if (!freerdp_target_net_addresses_resize(settings, len)) + return FALSE; + if (data == NULL) + target_net_addresses_free(settings, 0); + return TRUE; case FreeRDP_ServerLicenseProductIssuers: if (data == NULL) freerdp_server_license_issuers_free(settings); - return freerdp_settings_set_pointer_len_(settings, FreeRDP_ServerLicenseProductIssuers, - FreeRDP_ServerLicenseProductIssuersCount, data, - len, sizeof(char*)); + return freerdp_settings_set_pointer_len_( + settings, id, FreeRDP_ServerLicenseProductIssuersCount, data, len, sizeof(char*)); case FreeRDP_TargetNetPorts: - if ((data == NULL) && (len == 0)) + if (!freerdp_target_net_addresses_resize(settings, len)) + return FALSE; + if (data == NULL) { - freerdp_target_net_addresses_free(settings); - return TRUE; + for (size_t x = 0; x < len; x++) + settings->TargetNetPorts[x] = 0; } - WLog_WARN(TAG, - "[BUG] FreeRDP_TargetNetPorts must not be resized from outside the library!"); - return FALSE; + return TRUE; case FreeRDP_DeviceArray: if (data == NULL) freerdp_device_collection_free(settings); return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_DeviceArraySize, data, - len, sizeof(ADDIN_ARGV*)); + len, sizeof(RDPDR_DEVICE*)); case FreeRDP_ChannelDefArray: if ((len > 0) && (len < CHANNEL_MAX_COUNT)) WLog_WARN(TAG, @@ -1513,23 +1604,45 @@ BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, FreeRDP_Settings_Ke return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_DynamicChannelArraySize, data, len, sizeof(ADDIN_ARGV*)); case FreeRDP_ReceivedCapabilityData: + if (!freerdp_capability_buffer_resize(settings, len)) + return FALSE; if (data == NULL) - freerdp_capability_buffer_free(settings); - return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_ReceivedCapabilitiesSize, - data, len, sizeof(BYTE*)); + { + freerdp_capability_data_free(settings, 0, FALSE); + } + return TRUE; case FreeRDP_ReceivedCapabilities: + if (!freerdp_capability_buffer_resize(settings, len)) + return FALSE; if (data == NULL) - freerdp_capability_buffer_free(settings); - return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_ReceivedCapabilitiesSize, - data, len, sizeof(char)); + { + for (size_t x = 0; x < settings->ReceivedCapabilitiesSize; x++) + { + settings->ReceivedCapabilities[x] = 0; + } + } + return TRUE; case FreeRDP_OrderSupport: return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_UINT32_UNUSED, data, len, sizeof(char)); case FreeRDP_MonitorIds: - return freerdp_settings_set_pointer_len_( - settings, FreeRDP_MonitorIds, FreeRDP_NumMonitorIds, data, len, sizeof(UINT32)); + return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_NumMonitorIds, data, len, + sizeof(UINT32)); + case FreeRDP_ReceivedCapabilityDataSizes: + if (!freerdp_capability_buffer_resize(settings, len)) + return FALSE; + if (data == NULL) + { + for (size_t x = 0; x < settings->ReceivedCapabilitiesSize; x++) + settings->ReceivedCapabilityDataSizes[x] = 0; + } + return TRUE; + + case FreeRDP_Password51: + return freerdp_settings_set_pointer_len_(settings, id, FreeRDP_Password51Length, data, + len, sizeof(char)); default: if ((data == NULL) && (len == 0)) { @@ -1651,11 +1764,13 @@ void* freerdp_settings_get_pointer_array_writable(const rdpSettings* settings, max = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); if (offset >= max) goto fail; + WINPR_ASSERT(settings->TargetNetAddresses); return settings->TargetNetAddresses[offset]; case FreeRDP_TargetNetPorts: max = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); if (offset >= max) goto fail; + WINPR_ASSERT(settings->TargetNetPorts); return &settings->TargetNetPorts[offset]; case FreeRDP_ClientTimeZone: max = 1; @@ -1672,6 +1787,24 @@ void* freerdp_settings_get_pointer_array_writable(const rdpSettings* settings, if (offset >= max) goto fail; return settings->RdpServerRsaKey; + case FreeRDP_ServerLicenseProductIssuers: + max = freerdp_settings_get_uint32(settings, FreeRDP_ServerLicenseProductIssuersCount); + if (offset >= max) + goto fail; + return settings->ServerLicenseProductIssuers[offset]; + case FreeRDP_ReceivedCapabilityData: + max = freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + if (offset >= max) + goto fail; + WINPR_ASSERT(settings->ReceivedCapabilityData); + return settings->ReceivedCapabilityData[offset]; + + case FreeRDP_ReceivedCapabilityDataSizes: + max = freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + if (offset >= max) + goto fail; + WINPR_ASSERT(settings->ReceivedCapabilityDataSizes); + return &settings->ReceivedCapabilityDataSizes[offset]; default: WLog_WARN(TAG, "Invalid id %s [%" PRIuz "]", freerdp_settings_get_name_for_key(id), id); return NULL; @@ -1720,6 +1853,7 @@ BOOL freerdp_settings_set_pointer_array(rdpSettings* settings, FreeRDP_Settings_ maxOffset = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); if ((offset >= maxOffset) || !data) goto fail; + WINPR_ASSERT(settings->TargetNetAddresses); free(settings->TargetNetAddresses[offset]); settings->TargetNetAddresses[offset] = _strdup((const char*)data); return settings->TargetNetAddresses[offset] != NULL; @@ -1727,6 +1861,7 @@ BOOL freerdp_settings_set_pointer_array(rdpSettings* settings, FreeRDP_Settings_ maxOffset = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); if ((offset >= maxOffset) || !data) goto fail; + WINPR_ASSERT(settings->TargetNetPorts); settings->TargetNetPorts[offset] = *((const UINT32*)data); return TRUE; case FreeRDP_StaticChannelArray: @@ -1829,9 +1964,34 @@ BOOL freerdp_settings_set_pointer_array(rdpSettings* settings, FreeRDP_Settings_ maxOffset = 1; if ((offset >= maxOffset) || !data || !settings->ClientTimeZone) goto fail; - settings->ClientTimeZone[0] = *(const TIME_ZONE_INFORMATION*)data; + settings->ClientTimeZone[offset] = *(const TIME_ZONE_INFORMATION*)data; return TRUE; + case FreeRDP_ServerLicenseProductIssuers: + maxOffset = + freerdp_settings_get_uint32(settings, FreeRDP_ServerLicenseProductIssuersCount); + if ((offset >= maxOffset) || !settings->ServerLicenseProductIssuers) + goto fail; + free(settings->ServerLicenseProductIssuers[offset]); + settings->ServerLicenseProductIssuers[offset] = NULL; + if (data) + settings->ServerLicenseProductIssuers[offset] = _strdup((const char*)data); + return TRUE; + + case FreeRDP_ReceivedCapabilityData: + maxOffset = freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + if (offset >= maxOffset) + goto fail; + WINPR_ASSERT(settings->ReceivedCapabilityData); + settings->ReceivedCapabilityData[offset] = WINPR_CAST_CONST_PTR_AWAY(data, BYTE*); + return TRUE; + case FreeRDP_ReceivedCapabilityDataSizes: + maxOffset = freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + if (offset >= maxOffset) + goto fail; + WINPR_ASSERT(settings->ReceivedCapabilityDataSizes); + settings->ReceivedCapabilityDataSizes[offset] = *(const uint32_t*)data; + return TRUE; default: WLog_WARN(TAG, "Invalid id %s [%" PRIuz "]", freerdp_settings_get_name_for_key(id), id); return FALSE; @@ -1897,8 +2057,8 @@ BOOL freerdp_target_net_addresses_copy(rdpSettings* settings, char** addresses, for (UINT32 i = 0; i < settings->TargetNetAddressCount; i++) { - if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, - addresses[i])) + const char* address = addresses[i]; + if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, address)) { freerdp_target_net_addresses_free(settings); return FALSE; @@ -2373,3 +2533,1991 @@ BOOL freerdp_settings_set_monitor_def_array_sorted(rdpSettings* settings, return freerdp_settings_set_uint32(settings, FreeRDP_MonitorCount, WINPR_ASSERTING_INT_CAST(uint32_t, count)); } + +static BOOL fill_array(WINPR_JSON* array, const void* data, size_t length) +{ + const BYTE* pdata = data; + for (size_t x = 0; x < length; x++) + { + BYTE val = pdata[x]; + if (!WINPR_JSON_AddItemToArray(array, WINPR_JSON_CreateNumber(val))) + return FALSE; + } + return TRUE; +} + +static BOOL fill_uint32_array(WINPR_JSON* array, const uint32_t* data, size_t length) +{ + for (size_t x = 0; x < length; x++) + { + uint32_t val = data[x]; + if (!WINPR_JSON_AddItemToArray(array, WINPR_JSON_CreateNumber(val))) + return FALSE; + } + return TRUE; +} + +static WINPR_JSON* json_from_addin_item(const ADDIN_ARGV* val) +{ + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + goto fail; + + if (val) + { + if (!WINPR_JSON_AddNumberToObject(obj, "argc", val->argc)) + goto fail; + + WINPR_JSON* array = WINPR_JSON_AddArrayToObject(obj, "argv"); + if (!array) + goto fail; + for (int x = 0; x < val->argc; x++) + { + const char* str = val->argv[x]; + + WINPR_JSON* item = NULL; + if (!str) + { + item = WINPR_JSON_CreateNull(); + } + else + { + item = WINPR_JSON_CreateString(str); + } + + if (!WINPR_JSON_AddItemToArray(array, item)) + goto fail; + } + } + return obj; + +fail: + WINPR_JSON_Delete(obj); + return NULL; +} + +static BOOL json_from_addin_item_array(WINPR_JSON* json, const rdpSettings* settings, + FreeRDP_Settings_Keys_Pointer key, size_t count) +{ + if (!json) + return FALSE; + + for (uint32_t x = 0; x < count; x++) + { + const ADDIN_ARGV* cval = freerdp_settings_get_pointer_array(settings, key, x); + if (!WINPR_JSON_AddItemToArray(json, json_from_addin_item(cval))) + return FALSE; + } + return TRUE; +} + +static BOOL add_string_or_null(WINPR_JSON* json, const char* key, const char* value) +{ + if (value) + return WINPR_JSON_AddStringToObject(json, key, value) != NULL; + + return WINPR_JSON_AddNullToObject(json, key) != NULL; +} + +static WINPR_JSON* json_from_device_item(const RDPDR_DEVICE* val) +{ + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + goto fail; + + if (val) + { + union + { + const RDPDR_DEVICE* base; + const RDPDR_PARALLEL* parallel; + const RDPDR_SERIAL* serial; + const RDPDR_SMARTCARD* smartcard; + const RDPDR_PRINTER* printer; + const RDPDR_DRIVE* drive; + const RDPDR_DEVICE* device; + } device; + + device.base = val; + + if (!WINPR_JSON_AddNumberToObject(obj, "Id", val->Id)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "Type", val->Type)) + goto fail; + if (!add_string_or_null(obj, "Name", val->Name)) + goto fail; + switch (val->Type) + { + case RDPDR_DTYP_SERIAL: + if (!add_string_or_null(obj, "Path", device.serial->Path)) + goto fail; + if (!add_string_or_null(obj, "Driver", device.serial->Driver)) + goto fail; + if (!add_string_or_null(obj, "Permissive", device.serial->Permissive)) + goto fail; + break; + case RDPDR_DTYP_PARALLEL: + if (!add_string_or_null(obj, "Path", device.parallel->Path)) + goto fail; + break; + case RDPDR_DTYP_PRINT: + if (!add_string_or_null(obj, "DriverName", device.printer->DriverName)) + goto fail; + if (!WINPR_JSON_AddBoolToObject(obj, "IsDefault", device.printer->IsDefault)) + goto fail; + break; + case RDPDR_DTYP_FILESYSTEM: + if (!add_string_or_null(obj, "Path", device.drive->Path)) + goto fail; + if (!WINPR_JSON_AddBoolToObject(obj, "IsDefault", device.drive->automount)) + goto fail; + break; + case RDPDR_DTYP_SMARTCARD: + default: + break; + } + } + return obj; + +fail: + WINPR_JSON_Delete(obj); + return NULL; +} + +static BOOL json_from_device_item_array(WINPR_JSON* json, const rdpSettings* settings, + FreeRDP_Settings_Keys_Pointer key, size_t count) +{ + if (!json) + return FALSE; + + for (uint32_t x = 0; x < count; x++) + { + const RDPDR_DEVICE* cval = freerdp_settings_get_pointer_array(settings, key, x); + if (!WINPR_JSON_AddItemToArray(json, json_from_device_item(cval))) + return FALSE; + } + return TRUE; +} + +static BOOL string_array_to_json(WINPR_JSON* json, const rdpSettings* settings, uint32_t argc, + FreeRDP_Settings_Keys_Pointer key) +{ + for (uint32_t x = 0; x < argc; x++) + { + const char* cval = freerdp_settings_get_pointer_array(settings, key, x); + + WINPR_JSON* item = NULL; + if (!cval) + item = WINPR_JSON_CreateNull(); + else + item = WINPR_JSON_CreateString(cval); + if (!WINPR_JSON_AddItemToArray(json, item)) + return FALSE; + } + return TRUE; +} + +static BOOL wchar_to_json(WINPR_JSON* obj, const char* key, const WCHAR* wstr, size_t len) +{ + if (len == 0) + return WINPR_JSON_AddStringToObject(obj, key, "") != NULL; + + const size_t slen = len * 6; + char* str = calloc(1, slen); + if (!str) + return FALSE; + + WINPR_JSON* jstr = NULL; + SSIZE_T rc = ConvertWCharNToUtf8(wstr, len, str, slen); + if (rc >= 0) + jstr = WINPR_JSON_AddStringToObject(obj, key, str); + free(str); + return jstr != NULL; +} + +static BOOL wchar_from_json(WCHAR* wstr, size_t len, const WINPR_JSON* obj, const char* key) +{ + if (!obj || !WINPR_JSON_IsObject(obj)) + return FALSE; + WINPR_JSON* item = WINPR_JSON_GetObjectItem(obj, key); + if (!item || !WINPR_JSON_IsString(item)) + return FALSE; + + const char* str = WINPR_JSON_GetStringValue(item); + if (!str) + { + memset(wstr, 0, sizeof(WCHAR) * len); + return TRUE; + } + + SSIZE_T rc = ConvertUtf8ToWChar(str, wstr, len); + return rc >= 0; +} + +static int64_t int_from_json_item(const WINPR_JSON* item, int64_t min, int64_t max) +{ + if (!item || !WINPR_JSON_IsNumber(item)) + { + errno = EINVAL; + return 0; + } + + const double val = WINPR_JSON_GetNumberValue(item); + if (isinf(val) || isnan(val)) + { + errno = ERANGE; + return 0; + } + + const int64_t ival = (int64_t)val; + if ((ival < min) || (ival > max)) + { + errno = ERANGE; + return 0; + } + + return ival; +} + +static int64_t int_from_json(const WINPR_JSON* obj, const char* key, int64_t min, int64_t max) +{ + if (!obj || !WINPR_JSON_IsObject(obj)) + { + errno = EINVAL; + return 0; + } + WINPR_JSON* item = WINPR_JSON_GetObjectItem(obj, key); + return int_from_json_item(item, min, max); +} + +static uint64_t uint_from_json_item(const WINPR_JSON* item, uint64_t max) +{ + if (!item || !WINPR_JSON_IsNumber(item)) + { + errno = EINVAL; + return 0; + } + + const double val = WINPR_JSON_GetNumberValue(item); + if (isinf(val) || isnan(val) || (val < 0.0)) + { + errno = ERANGE; + return 0; + } + + const uint64_t uval = (uint64_t)val; + if (uval > max) + { + errno = ERANGE; + return 0; + } + return uval; +} + +static uint64_t uint_from_json(const WINPR_JSON* obj, const char* key, uint64_t max) +{ + if (!obj || !WINPR_JSON_IsObject(obj)) + { + errno = EINVAL; + return 0; + } + + WINPR_JSON* item = WINPR_JSON_GetObjectItem(obj, key); + return uint_from_json_item(item, max); +} + +static WINPR_JSON* systemtime_to_json(WINPR_JSON* parent, const char* key, const SYSTEMTIME* st) +{ + WINPR_ASSERT(st); + + WINPR_JSON* obj = WINPR_JSON_AddObjectToObject(parent, key); + if (!obj) + return NULL; + + if (!WINPR_JSON_AddNumberToObject(obj, "wYear", st->wYear)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wMonth", st->wMonth)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wDayOfWeek", st->wDayOfWeek)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wDay", st->wDay)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wHour", st->wHour)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wMinute", st->wMinute)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wSecond", st->wSecond)) + goto fail; + if (!WINPR_JSON_AddNumberToObject(obj, "wMilliseconds", st->wMilliseconds)) + goto fail; + + return obj; +fail: + WINPR_JSON_Delete(obj); + return NULL; +} + +static BOOL systemtime_from_json(const WINPR_JSON* pobj, const char* key, SYSTEMTIME* st) +{ + WINPR_ASSERT(st); + + if (!pobj || !WINPR_JSON_IsObject(pobj)) + return FALSE; + + WINPR_JSON* obj = WINPR_JSON_GetObjectItem(pobj, key); + if (!obj || !WINPR_JSON_IsObject(obj)) + return FALSE; + + errno = 0; + st->wYear = (uint16_t)uint_from_json(obj, "wYear", UINT16_MAX); + st->wMonth = (uint16_t)uint_from_json(obj, "wMonth", UINT16_MAX); + st->wDayOfWeek = (uint16_t)uint_from_json(obj, "wDayOfWeek", UINT16_MAX); + st->wDay = (uint16_t)uint_from_json(obj, "wDay", UINT16_MAX); + st->wHour = (uint16_t)uint_from_json(obj, "wHour", UINT16_MAX); + st->wMinute = (uint16_t)uint_from_json(obj, "wMinute", UINT16_MAX); + st->wSecond = (uint16_t)uint_from_json(obj, "wSecond", UINT16_MAX); + st->wMilliseconds = (uint16_t)uint_from_json(obj, "wMilliseconds", UINT16_MAX); + return errno == 0; +} + +static BOOL ts_info_from_json(TIME_ZONE_INFORMATION* tz, const WINPR_JSON* json) +{ + WINPR_ASSERT(tz); + + if (!json || !WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + tz->Bias = (int32_t)int_from_json(json, "Bias", INT32_MIN, INT32_MAX); + tz->StandardBias = (int32_t)int_from_json(json, "StandardBias", INT32_MIN, INT32_MAX); + tz->DaylightBias = (int32_t)int_from_json(json, "DaylightBias", INT32_MIN, INT32_MAX); + if (errno != 0) + return FALSE; + + if (!systemtime_from_json(json, "StandardDate", &tz->StandardDate)) + return FALSE; + if (!systemtime_from_json(json, "DaylightDate", &tz->DaylightDate)) + return FALSE; + + if (!wchar_from_json(tz->StandardName, ARRAYSIZE(tz->StandardName), json, "StandardName")) + return FALSE; + if (!wchar_from_json(tz->DaylightName, ARRAYSIZE(tz->DaylightName), json, "DaylightName")) + return FALSE; + + return TRUE; +} + +static BOOL ts_info_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer key, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, key, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + TIME_ZONE_INFORMATION* tz = freerdp_settings_get_pointer_array_writable(settings, key, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!ts_info_from_json(tz, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL tz_info_to_json(WINPR_JSON* json, const TIME_ZONE_INFORMATION* ptz) +{ + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + TIME_ZONE_INFORMATION tz = { 0 }; + if (ptz) + tz = *ptz; + + if (!WINPR_JSON_AddNumberToObject(obj, "Bias", tz.Bias)) + return FALSE; + + if (!wchar_to_json(obj, "StandardName", tz.StandardName, ARRAYSIZE(tz.StandardName))) + return FALSE; + + if (!systemtime_to_json(obj, "StandardDate", &tz.StandardDate)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "StandardBias", tz.Bias)) + return FALSE; + + if (!wchar_to_json(obj, "DaylightName", tz.DaylightName, ARRAYSIZE(tz.DaylightName))) + return FALSE; + + if (!systemtime_to_json(obj, "DaylightDate", &tz.DaylightDate)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "DaylightBias", tz.Bias)) + return FALSE; + return TRUE; +} + +static BOOL glyph_cache_def_to_json(WINPR_JSON* json, const GLYPH_CACHE_DEFINITION* def) +{ + WINPR_ASSERT(def); + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "cacheEntries", def->cacheEntries)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "cacheMaximumCellSize", def->cacheMaximumCellSize)) + return FALSE; + return TRUE; +} + +static BOOL glyph_cache_def_array_to_json(WINPR_JSON* json, const GLYPH_CACHE_DEFINITION* def, + size_t count) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + if (!glyph_cache_def_to_json(json, &def[x])) + return FALSE; + } + return TRUE; +} + +static BOOL glyph_cache_def_from_json(GLYPH_CACHE_DEFINITION* def, const WINPR_JSON* json) +{ + WINPR_ASSERT(def); + WINPR_ASSERT(json); + + if (!WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + def->cacheEntries = (uint16_t)uint_from_json(json, "cacheEntries", UINT16_MAX); + def->cacheMaximumCellSize = (uint16_t)uint_from_json(json, "cacheMaximumCellSize", UINT16_MAX); + return errno == 0; +} + +static BOOL glyph_cache_def_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, id, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + GLYPH_CACHE_DEFINITION* cache = + freerdp_settings_get_pointer_array_writable(settings, id, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!glyph_cache_def_from_json(cache, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL bitmap_cache_v2_from_json(BITMAP_CACHE_V2_CELL_INFO* info, const WINPR_JSON* json) +{ + WINPR_ASSERT(info); + + if (!json || !WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + info->numEntries = (uint32_t)uint_from_json(json, "numEntries", UINT32_MAX); + if (errno != 0) + return FALSE; + + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, "persistent"); + if (!item || !WINPR_JSON_IsBool(item)) + return FALSE; + + info->persistent = WINPR_JSON_IsTrue(item); + return TRUE; +} + +static BOOL bitmap_cache_v2_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, id, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + BITMAP_CACHE_V2_CELL_INFO* cache = + freerdp_settings_get_pointer_array_writable(settings, id, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!bitmap_cache_v2_from_json(cache, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL client_cookie_from_json(ARC_CS_PRIVATE_PACKET* cookie, const WINPR_JSON* json) +{ + WINPR_ASSERT(cookie); + WINPR_ASSERT(json); + + if (!WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + cookie->cbLen = (uint32_t)uint_from_json(json, "cbLen", UINT32_MAX); + cookie->version = (uint32_t)uint_from_json(json, "version", UINT32_MAX); + cookie->logonId = (uint32_t)uint_from_json(json, "logonId", UINT32_MAX); + if (errno != 0) + return FALSE; + + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, "securityVerifier"); + if (!item || !WINPR_JSON_IsArray(item)) + return FALSE; + + const size_t len = WINPR_JSON_GetArraySize(item); + if (len != ARRAYSIZE(cookie->securityVerifier)) + return FALSE; + + errno = 0; + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* citem = WINPR_JSON_GetArrayItem(item, x); + const uint64_t val = uint_from_json_item(citem, UINT8_MAX); + cookie->securityVerifier[x] = (uint8_t)val; + } + return errno == 0; +} + +static BOOL client_cookie_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, id, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + ARC_CS_PRIVATE_PACKET* cache = freerdp_settings_get_pointer_array_writable(settings, id, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!client_cookie_from_json(cache, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL server_cookie_from_json(ARC_SC_PRIVATE_PACKET* cookie, const WINPR_JSON* json) +{ + WINPR_ASSERT(cookie); + + if (!json || !WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + cookie->cbLen = (uint32_t)uint_from_json(json, "cbLen", UINT32_MAX); + cookie->version = (uint32_t)uint_from_json(json, "version", UINT32_MAX); + cookie->logonId = (uint32_t)uint_from_json(json, "logonId", UINT32_MAX); + if (errno != 0) + return FALSE; + + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, "arcRandomBits"); + if (!item || !WINPR_JSON_IsArray(item)) + return FALSE; + + const size_t len = WINPR_JSON_GetArraySize(item); + if (len != ARRAYSIZE(cookie->arcRandomBits)) + return FALSE; + + errno = 0; + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* citem = WINPR_JSON_GetArrayItem(item, x); + cookie->arcRandomBits[x] = (uint8_t)uint_from_json_item(citem, UINT8_MAX); + } + return errno == 0; +} + +static BOOL server_cookie_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, id, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + ARC_SC_PRIVATE_PACKET* cache = freerdp_settings_get_pointer_array_writable(settings, id, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!server_cookie_from_json(cache, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL channel_def_from_json(CHANNEL_DEF* cookie, const WINPR_JSON* json) +{ + WINPR_ASSERT(cookie); + WINPR_ASSERT(json); + + if (!WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + cookie->options = (uint32_t)uint_from_json(json, "options", UINT32_MAX); + if (errno != 0) + return FALSE; + + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, "name"); + if (!item || !WINPR_JSON_IsString(item)) + return FALSE; + + const char* str = WINPR_JSON_GetStringValue(item); + if (!str) + memset(cookie->name, 0, sizeof(cookie->name)); + else + { + strncpy(cookie->name, str, strnlen(str, ARRAYSIZE(cookie->name))); + } + return TRUE; +} + +static BOOL channel_def_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, id, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + CHANNEL_DEF* cache = freerdp_settings_get_pointer_array_writable(settings, id, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!channel_def_from_json(cache, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL monitor_attributes_from_json(MONITOR_ATTRIBUTES* attributes, const WINPR_JSON* json) +{ + WINPR_ASSERT(attributes); + if (!json || !WINPR_JSON_IsObject(json)) + return FALSE; + + WINPR_JSON* obj = WINPR_JSON_GetObjectItem(json, "attributes"); + if (!obj || !WINPR_JSON_IsObject(obj)) + return FALSE; + + errno = 0; + attributes->physicalWidth = (uint32_t)uint_from_json(obj, "physicalWidth", UINT32_MAX); + attributes->physicalHeight = (uint32_t)uint_from_json(obj, "physicalHeight", UINT32_MAX); + attributes->orientation = (uint32_t)uint_from_json(obj, "orientation", UINT32_MAX); + attributes->desktopScaleFactor = + (uint32_t)uint_from_json(obj, "desktopScaleFactor", UINT32_MAX); + attributes->deviceScaleFactor = (uint32_t)uint_from_json(obj, "deviceScaleFactor", UINT32_MAX); + return errno == 0; +} + +static BOOL monitor_def_from_json(rdpMonitor* monitor, const WINPR_JSON* json) +{ + WINPR_ASSERT(monitor); + + if (!json || !WINPR_JSON_IsObject(json)) + return FALSE; + + errno = 0; + monitor->x = (int32_t)int_from_json(json, "x", INT32_MIN, INT32_MAX); + monitor->y = (int32_t)int_from_json(json, "y", INT32_MIN, INT32_MAX); + monitor->width = (int32_t)int_from_json(json, "width", 0, INT32_MAX); + monitor->height = (int32_t)int_from_json(json, "height", 0, INT32_MAX); + monitor->orig_screen = (uint32_t)uint_from_json(json, "orig_screen", UINT32_MAX); + if (errno != 0) + return FALSE; + + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, "is_primary"); + if (!item) + return FALSE; + if (!WINPR_JSON_IsBool(item)) + return FALSE; + monitor->is_primary = WINPR_JSON_IsTrue(item) ? 1 : 0; + + return monitor_attributes_from_json(&monitor->attributes, json); +} + +static BOOL monitor_def_array_from_json(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer id, + const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, id, NULL, count)) + return FALSE; + + for (size_t x = 0; x < count; x++) + { + rdpMonitor* cache = freerdp_settings_get_pointer_array_writable(settings, id, x); + WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); + if (!monitor_def_from_json(cache, obj)) + return FALSE; + } + return TRUE; +} + +static BOOL client_cookie_to_json(WINPR_JSON* json, const ARC_CS_PRIVATE_PACKET* cs) +{ + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "cbLen", cs->cbLen)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "version", cs->version)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "logonId", cs->logonId)) + return FALSE; + WINPR_JSON* array = WINPR_JSON_AddArrayToObject(obj, "securityVerifier"); + if (!array) + return FALSE; + + for (size_t x = 0; x < ARRAYSIZE(cs->securityVerifier); x++) + { + WINPR_JSON* item = WINPR_JSON_CreateNumber(cs->securityVerifier[x]); + if (!item) + return FALSE; + if (!WINPR_JSON_AddItemToArray(array, item)) + return FALSE; + } + return TRUE; +} + +static BOOL client_cookie_array_to_json(WINPR_JSON* json, const ARC_CS_PRIVATE_PACKET* cs, + size_t count) +{ + for (size_t x = 0; x < count; x++) + { + if (!client_cookie_to_json(json, &cs[x])) + return FALSE; + } + return TRUE; +} + +static BOOL server_cookie_to_json(WINPR_JSON* json, const ARC_SC_PRIVATE_PACKET* cs) +{ + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "cbLen", cs->cbLen)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "version", cs->version)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "logonId", cs->logonId)) + return FALSE; + WINPR_JSON* array = WINPR_JSON_AddArrayToObject(obj, "arcRandomBits"); + if (!array) + return FALSE; + + for (size_t x = 0; x < ARRAYSIZE(cs->arcRandomBits); x++) + { + WINPR_JSON* item = WINPR_JSON_CreateNumber(cs->arcRandomBits[x]); + if (!item) + return FALSE; + if (!WINPR_JSON_AddItemToArray(array, item)) + return FALSE; + } + return TRUE; +} + +static BOOL server_cookie_array_to_json(WINPR_JSON* json, const ARC_SC_PRIVATE_PACKET* cs, + size_t count) +{ + for (size_t x = 0; x < count; x++) + { + if (!server_cookie_to_json(json, &cs[x])) + return FALSE; + } + return TRUE; +} + +static BOOL bitmap_cache_v2_to_json(WINPR_JSON* json, const BITMAP_CACHE_V2_CELL_INFO* info) +{ + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "numEntries", info->numEntries)) + return FALSE; + if (!WINPR_JSON_AddBoolToObject(obj, "persistent", info->persistent)) + return FALSE; + return TRUE; +} + +static BOOL bitmap_cache_v2_array_to_json(WINPR_JSON* json, const BITMAP_CACHE_V2_CELL_INFO* info, + size_t count) +{ + for (size_t x = 0; x < count; x++) + { + if (!bitmap_cache_v2_to_json(json, &info[x])) + return FALSE; + } + return TRUE; +} + +static BOOL monitor_attributes_to_json(WINPR_JSON* pobj, const MONITOR_ATTRIBUTES* attributes) +{ + WINPR_ASSERT(attributes); + WINPR_JSON* obj = WINPR_JSON_AddObjectToObject(pobj, "attributes"); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "physicalWidth", attributes->physicalWidth)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "physicalHeight", attributes->physicalHeight)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "orientation", attributes->orientation)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "desktopScaleFactor", attributes->desktopScaleFactor)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "deviceScaleFactor", attributes->deviceScaleFactor)) + return FALSE; + return TRUE; +} + +static BOOL monitor_def_to_json(WINPR_JSON* json, const rdpMonitor* monitor) +{ + WINPR_ASSERT(monitor); + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + if (!WINPR_JSON_AddNumberToObject(obj, "x", monitor->x)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "y", monitor->y)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "width", monitor->width)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "height", monitor->height)) + return FALSE; + if (!WINPR_JSON_AddBoolToObject(obj, "is_primary", monitor->is_primary != 0)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "orig_screen", monitor->orig_screen)) + return FALSE; + return monitor_attributes_to_json(obj, &monitor->attributes); +} + +static BOOL monitor_def_array_to_json(WINPR_JSON* json, const rdpMonitor* monitors, size_t count) +{ + for (size_t x = 0; x < count; x++) + { + if (!monitor_def_to_json(json, &monitors[x])) + return FALSE; + } + return TRUE; +} + +static BOOL channel_def_to_json(WINPR_JSON* json, const CHANNEL_DEF* channel) +{ + WINPR_ASSERT(channel); + WINPR_JSON* obj = WINPR_JSON_CreateObject(); + if (!obj) + return FALSE; + if (!WINPR_JSON_AddItemToArray(json, obj)) + return FALSE; + + if (!WINPR_JSON_AddStringToObject(obj, "name", channel->name)) + return FALSE; + if (!WINPR_JSON_AddNumberToObject(obj, "options", channel->options)) + return FALSE; + return TRUE; +} + +static BOOL channel_def_array_to_json(WINPR_JSON* json, const CHANNEL_DEF* channels, size_t count) +{ + for (size_t x = 0; x < count; x++) + { + if (!channel_def_to_json(json, &channels[x])) + return FALSE; + } + return TRUE; +} + +static BOOL serialize_pointer(const rdpSettings* settings, WINPR_JSON* json, + FreeRDP_Settings_Keys_Pointer id) +{ + const char* name = freerdp_settings_get_name_for_key(id); + if (!name) + return FALSE; + + WINPR_JSON* jval = WINPR_JSON_AddArrayToObject(json, name); + if (!jval) + return FALSE; + + const void* val = freerdp_settings_get_pointer(settings, id); + if (!val) + return TRUE; + + switch (id) + { + case FreeRDP_instance: + { + union + { + const void* v; + uintptr_t u; + } ptr; + + ptr.v = val; + return fill_array(jval, &ptr.u, sizeof(ptr.u)); + } + case FreeRDP_ServerRandom: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_ServerRandomLength); + return fill_array(jval, val, len); + } + case FreeRDP_ServerCertificate: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_ServerCertificateLength); + return fill_array(jval, val, len); + } + case FreeRDP_ClientRandom: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_ClientRandomLength); + return fill_array(jval, val, len); + } + case FreeRDP_ServerLicenseProductIssuers: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_ServerLicenseProductIssuersCount); + return string_array_to_json(jval, settings, len, FreeRDP_ServerLicenseProductIssuers); + } + case FreeRDP_RedirectionPassword: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_RedirectionPasswordLength); + return fill_array(jval, val, len); + } + case FreeRDP_RedirectionGuid: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_RedirectionGuidLength); + return fill_array(jval, val, len); + } + case FreeRDP_LoadBalanceInfo: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_LoadBalanceInfoLength); + return fill_array(jval, val, len); + } + case FreeRDP_ClientTimeZone: + { + return tz_info_to_json(jval, val); + } + case FreeRDP_RedirectionTsvUrl: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_RedirectionTsvUrlLength); + return fill_array(jval, val, len); + } + case FreeRDP_GlyphCache: + { + return glyph_cache_def_array_to_json(jval, val, val ? 10 : 0); + } + case FreeRDP_FragCache: + { + return glyph_cache_def_array_to_json(jval, val, val ? 1 : 0); + } + case FreeRDP_BitmapCacheV2CellInfo: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells); + return bitmap_cache_v2_array_to_json(jval, val, len); + } + case FreeRDP_OrderSupport: + { + const uint32_t len = 32; + return fill_array(jval, val, len); + } + case FreeRDP_ClientAutoReconnectCookie: + { + return client_cookie_array_to_json(jval, val, val ? 1 : 0); + } + case FreeRDP_ServerAutoReconnectCookie: + { + return server_cookie_array_to_json(jval, val, val ? 1 : 0); + } + case FreeRDP_Password51: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_Password51Length); + return fill_array(jval, val, len); + } + case FreeRDP_ReceivedCapabilities: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + return fill_array(jval, val, len); + } + case FreeRDP_MonitorIds: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds); + return fill_uint32_array(jval, val, len); + } + case FreeRDP_TargetNetPorts: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); + return fill_uint32_array(jval, val, len); + } + case FreeRDP_MonitorDefArray: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_MonitorDefArraySize); + return monitor_def_array_to_json(jval, val, len); + } + case FreeRDP_ChannelDefArray: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_ChannelDefArraySize); + return channel_def_array_to_json(jval, val, len); + } + case FreeRDP_ReceivedCapabilityDataSizes: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + return fill_uint32_array(jval, val, len); + } + case FreeRDP_ReceivedCapabilityData: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_ReceivedCapabilitiesSize); + const UINT32* pclen = + freerdp_settings_get_pointer(settings, FreeRDP_ReceivedCapabilityDataSizes); + if (!pclen) + return FALSE; + + for (uint32_t x = 0; x < len; x++) + { + const char* cval = freerdp_settings_get_pointer_array(settings, id, x); + + WINPR_JSON* item = WINPR_JSON_CreateArray(); + if (!item) + return FALSE; + if (!WINPR_JSON_AddItemToArray(jval, item)) + return FALSE; + if (!fill_array(item, cval, pclen[x])) + return FALSE; + } + return TRUE; + } + case FreeRDP_TargetNetAddresses: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); + return string_array_to_json(jval, settings, len, id); + } + case FreeRDP_RedirectionTargetCertificate: + case FreeRDP_RdpServerCertificate: + { + WINPR_JSON* item = NULL; + size_t len = 0; + char* pem = freerdp_certificate_get_pem(val, &len); + if (pem) + item = WINPR_JSON_CreateString(pem); + else if (val) + item = WINPR_JSON_CreateString(""); + else + item = WINPR_JSON_CreateNull(); + free(pem); + if (!item) + return TRUE; + + return WINPR_JSON_AddItemToArray(jval, item); + } + case FreeRDP_RdpServerRsaKey: + { + WINPR_JSON* item = NULL; + size_t len = 0; + char* pem = freerdp_key_get_pem(val, &len, NULL); + if (pem) + item = WINPR_JSON_CreateString(pem); + free(pem); + if (!item) + return TRUE; + + return WINPR_JSON_AddItemToArray(jval, item); + } + case FreeRDP_DeviceArray: + { + const uint32_t len = freerdp_settings_get_uint32(settings, FreeRDP_DeviceArraySize); + return json_from_device_item_array(jval, settings, id, len); + } + case FreeRDP_StaticChannelArray: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_StaticChannelArraySize); + return json_from_addin_item_array(jval, settings, id, len); + } + case FreeRDP_DynamicChannelArray: + { + const uint32_t len = + freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelArraySize); + return json_from_addin_item_array(jval, settings, id, len); + } + case FreeRDP_POINTER_UNUSED: + default: + return FALSE; + } +} + +char* freerdp_settings_serialize(const rdpSettings* settings, BOOL pretty, size_t* plength) +{ + char* str = NULL; + + if (plength) + *plength = 0; + + if (!settings) + return NULL; + + WINPR_JSON* json = WINPR_JSON_CreateObject(); + if (!json) + return NULL; + + WINPR_JSON* jbool = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_BOOL)); + if (!jbool) + goto fail; + WINPR_JSON* juint16 = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_UINT16)); + if (!juint16) + goto fail; + WINPR_JSON* jint16 = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_INT16)); + if (!jint16) + goto fail; + WINPR_JSON* juint32 = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_UINT32)); + if (!juint32) + goto fail; + WINPR_JSON* jint32 = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_INT32)); + if (!jint32) + goto fail; + WINPR_JSON* juint64 = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_UINT64)); + if (!juint64) + goto fail; + WINPR_JSON* jint64 = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_INT64)); + if (!jint64) + goto fail; + WINPR_JSON* jstring = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_STRING)); + if (!jstring) + goto fail; + WINPR_JSON* jpointer = WINPR_JSON_AddObjectToObject( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_POINTER)); + if (!jpointer) + goto fail; + + for (SSIZE_T x = 0; x < FreeRDP_Settings_StableAPI_MAX; x++) + { + union + { + + int i; + SSIZE_T s; + FreeRDP_Settings_Keys_Bool b; + FreeRDP_Settings_Keys_Int16 i16; + FreeRDP_Settings_Keys_UInt16 u16; + FreeRDP_Settings_Keys_Int32 i32; + FreeRDP_Settings_Keys_UInt32 u32; + FreeRDP_Settings_Keys_Int64 i64; + FreeRDP_Settings_Keys_UInt64 u64; + FreeRDP_Settings_Keys_String str; + FreeRDP_Settings_Keys_Pointer ptr; + } iter; + iter.s = x; + + const char* name = freerdp_settings_get_name_for_key(iter.s); + SSIZE_T type = freerdp_settings_get_type_for_key(iter.s); + switch (type) + { + case RDP_SETTINGS_TYPE_BOOL: + { + const BOOL val = freerdp_settings_get_bool(settings, iter.b); + if (!WINPR_JSON_AddBoolToObject(jbool, name, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_UINT16: + { + const uint16_t val = freerdp_settings_get_uint16(settings, iter.u16); + if (!WINPR_JSON_AddNumberToObject(juint16, name, (double)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_INT16: + { + const int16_t val = freerdp_settings_get_int16(settings, iter.i16); + if (!WINPR_JSON_AddNumberToObject(jint16, name, (double)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_UINT32: + { + const uint32_t val = freerdp_settings_get_uint32(settings, iter.u32); + if (!WINPR_JSON_AddNumberToObject(juint32, name, (double)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_INT32: + { + const int32_t val = freerdp_settings_get_int32(settings, iter.i32); + if (!WINPR_JSON_AddNumberToObject(jint32, name, (double)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_UINT64: + { + const uint64_t val = freerdp_settings_get_uint64(settings, iter.u64); + if (!WINPR_JSON_AddNumberToObject(juint64, name, (double)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_INT64: + { + const int64_t val = freerdp_settings_get_int64(settings, iter.i64); + if (!WINPR_JSON_AddNumberToObject(jint64, name, (double)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_STRING: + { + const char* val = freerdp_settings_get_string(settings, iter.str); + if (val) + { + if (!WINPR_JSON_AddStringToObject(jstring, name, val)) + goto fail; + } + else + { + (void)WINPR_JSON_AddNullToObject(jstring, name); + } + } + break; + case RDP_SETTINGS_TYPE_POINTER: + if (!serialize_pointer(settings, jpointer, iter.ptr)) + goto fail; + break; + default: + break; + } + } + + if (pretty) + str = WINPR_JSON_Print(json); + else + str = WINPR_JSON_PrintUnformatted(json); + + if (!str) + goto fail; + if (plength) + *plength = strlen(str); + +fail: + WINPR_JSON_Delete(json); + return str; +} + +static BOOL val_from_array(rdpSettings* settings, const WINPR_JSON* json, + FreeRDP_Settings_Keys_Pointer key, size_t esize) +{ + if (WINPR_JSON_IsNull(json)) + return freerdp_settings_set_pointer(settings, key, NULL); + if (!WINPR_JSON_IsArray(json)) + return FALSE; + + size_t len = WINPR_JSON_GetArraySize(json); + if (len == 0) + return freerdp_settings_set_pointer(settings, key, NULL); + + size_t count = len / esize; + if (count * esize != len) + return FALSE; + + if (!freerdp_settings_set_pointer_len(settings, key, NULL, count)) + return FALSE; + + BYTE* data = freerdp_settings_get_pointer_writable(settings, key); + if (!data) + return FALSE; + + errno = 0; + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* val = WINPR_JSON_GetArrayItem(json, x); + data[x] = (uint8_t)uint_from_json_item(val, UINT8_MAX); + } + + return errno == 0; +} + +static BOOL uintptr_from_array(rdpSettings* settings, const WINPR_JSON* json) +{ + FreeRDP_Settings_Keys_Pointer key = FreeRDP_instance; + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + size_t len = WINPR_JSON_GetArraySize(json); + if (len > sizeof(void*)) + return FALSE; + + if (len == 0) + return freerdp_settings_set_pointer(settings, key, NULL); + + union + { + void* v; + uint8_t u[sizeof(void*)]; + } ptr; + + errno = 0; + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* val = WINPR_JSON_GetArrayItem(json, x); + ptr.u[x] = (uint8_t)uint_from_json_item(val, UINT8_MAX); + } + if (errno != 0) + return FALSE; + return freerdp_settings_set_pointer(settings, key, ptr.v); +} + +static BOOL val_from_uint32_array(rdpSettings* settings, const WINPR_JSON* json, + FreeRDP_Settings_Keys_Pointer key, + FreeRDP_Settings_Keys_UInt32 keyId) +{ + if (WINPR_JSON_IsNull(json)) + return freerdp_settings_set_pointer(settings, key, NULL); + if (!WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t len = WINPR_JSON_GetArraySize(json); + if ((FreeRDP_UINT32_UNUSED != keyId) && (freerdp_settings_get_uint32(settings, keyId) != len)) + { + if (!freerdp_settings_set_pointer_len(settings, key, NULL, len)) + return FALSE; + } + + errno = 0; + for (size_t x = 0; x < len; x++) + { + UINT32* data = freerdp_settings_get_pointer_array_writable(settings, key, x); + if (!data) + return FALSE; + + WINPR_JSON* val = WINPR_JSON_GetArrayItem(json, x); + data[0] = (uint32_t)uint_from_json_item(val, UINT32_MAX); + } + return errno == 0; +} + +static BOOL caps_data_entry_from_json(rdpSettings* settings, size_t offset, const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t size = WINPR_JSON_GetArraySize(json); + if (size == 0) + { + return freerdp_settings_set_pointer_array(settings, FreeRDP_ReceivedCapabilityData, offset, + NULL); + } + + uint8_t* data = calloc(size, sizeof(uint8_t)); + if (!data) + return FALSE; + + if (!freerdp_settings_set_pointer_array(settings, FreeRDP_ReceivedCapabilityData, offset, data)) + { + free(data); + return FALSE; + } + + errno = 0; + for (size_t x = 0; x < size; x++) + { + WINPR_JSON* item = WINPR_JSON_GetArrayItem(json, x); + data[x] = (uint8_t)uint_from_json_item(item, UINT8_MAX); + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc) + return errno == 0; +} + +static BOOL caps_data_array_from_json(rdpSettings* settings, const WINPR_JSON* json) +{ + if (!json || !WINPR_JSON_IsArray(json)) + return FALSE; + + const size_t count = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ReceivedCapabilityData, NULL, count)) + return FALSE; + + for (uint32_t x = 0; x < count; x++) + { + WINPR_JSON* array = WINPR_JSON_GetArrayItem(json, x); + if (!caps_data_entry_from_json(settings, x, array)) + return FALSE; + } + return TRUE; +} + +static BOOL str_array_from_json(rdpSettings* settings, const WINPR_JSON* json, + FreeRDP_Settings_Keys_Pointer key) +{ + if (WINPR_JSON_IsNull(json)) + return freerdp_settings_set_pointer_len(settings, key, NULL, 0); + if (!WINPR_JSON_IsArray(json)) + return FALSE; + + size_t len = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, key, NULL, len)) + return FALSE; + + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* cval = WINPR_JSON_GetArrayItem(json, x); + if (!cval) + return FALSE; + if (!WINPR_JSON_IsString(cval)) + return FALSE; + + const char* val = WINPR_JSON_GetStringValue(cval); + if (!freerdp_settings_set_pointer_array(settings, key, x, val)) + return FALSE; + } + return TRUE; +} + +static BOOL addin_argv_from_json(rdpSettings* settings, const WINPR_JSON* json, + FreeRDP_Settings_Keys_Pointer key) +{ + if (WINPR_JSON_IsNull(json)) + return freerdp_settings_set_pointer(settings, key, NULL); + + if (!WINPR_JSON_IsArray(json)) + return FALSE; + + size_t len = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, key, NULL, len)) + return FALSE; + + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* val = WINPR_JSON_GetArrayItem(json, x); + if (val && WINPR_JSON_IsObject(val)) + { + WINPR_JSON* jargc = WINPR_JSON_GetObjectItem(val, "argc"); + WINPR_JSON* array = WINPR_JSON_GetObjectItem(val, "argv"); + if (!jargc || !array) + continue; + if (!WINPR_JSON_IsNumber(jargc) || !WINPR_JSON_IsArray(array)) + continue; + + const int argc = (int)int_from_json_item(jargc, INT32_MIN, INT32_MAX); + if (errno != 0) + return FALSE; + const size_t jlen = WINPR_JSON_GetArraySize(array); + if (jlen != (size_t)argc) + return FALSE; + if (jlen == 0) + continue; + + const char** argv = (const char**)calloc(jlen, sizeof(char*)); + if (!argv) + return FALSE; + for (size_t y = 0; y < jlen; y++) + { + WINPR_JSON* item = WINPR_JSON_GetArrayItem(array, y); + if (!item || !WINPR_JSON_IsString(item)) + { + free((void*)argv); + return FALSE; + } + argv[y] = WINPR_JSON_GetStringValue(item); + } + + ADDIN_ARGV* cval = freerdp_addin_argv_new(jlen, argv); + free((void*)argv); + if (!cval) + return FALSE; + const BOOL rc = freerdp_settings_set_pointer_array(settings, key, x, cval); + freerdp_addin_argv_free(cval); + if (!rc) + return FALSE; + } + } + return TRUE; +} + +static char* get_string(const WINPR_JSON* json, const char* key) +{ + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, key); + if (!item || !WINPR_JSON_IsString(item)) + return NULL; + const char* str = WINPR_JSON_GetStringValue(item); + return WINPR_CAST_CONST_PTR_AWAY(str, char*); +} + +static BOOL get_bool(const WINPR_JSON* json, const char* key) +{ + WINPR_JSON* item = WINPR_JSON_GetObjectItem(json, key); + if (!item || !WINPR_JSON_IsBool(item)) + return FALSE; + return WINPR_JSON_IsTrue(item); +} + +static BOOL device_from_json_item(rdpSettings* settings, FreeRDP_Settings_Keys_Pointer key, + size_t offset, const WINPR_JSON* val) +{ + if (!val || !WINPR_JSON_IsObject(val)) + return FALSE; + + union + { + RDPDR_DEVICE base; + RDPDR_PARALLEL parallel; + RDPDR_SERIAL serial; + RDPDR_SMARTCARD smartcard; + RDPDR_PRINTER printer; + RDPDR_DRIVE drive; + RDPDR_DEVICE device; + } device; + + memset(&device, 0, sizeof(device)); + + errno = 0; + device.base.Id = (uint32_t)uint_from_json(val, "Id", UINT32_MAX); + device.base.Type = (uint32_t)uint_from_json(val, "Type", UINT32_MAX); + if (errno != 0) + return FALSE; + device.base.Name = get_string(val, "Name"); + if (!device.base.Name) + return FALSE; + + switch (device.base.Type) + { + case RDPDR_DTYP_SERIAL: + device.serial.Path = get_string(val, "Path"); + device.serial.Driver = get_string(val, "Driver"); + device.serial.Permissive = get_string(val, "Permissive"); + break; + case RDPDR_DTYP_PARALLEL: + device.parallel.Path = get_string(val, "Path"); + break; + case RDPDR_DTYP_PRINT: + device.printer.DriverName = get_string(val, "DriverName"); + device.printer.IsDefault = get_bool(val, "IsDefault"); + break; + case RDPDR_DTYP_FILESYSTEM: + device.drive.Path = get_string(val, "Path"); + device.drive.automount = get_bool(val, "automount"); + break; + case RDPDR_DTYP_SMARTCARD: + default: + break; + } + return freerdp_settings_set_pointer_array(settings, key, offset, &device); +} + +static BOOL device_array_from_json(rdpSettings* settings, const WINPR_JSON* json, + FreeRDP_Settings_Keys_Pointer key) +{ + if (WINPR_JSON_IsNull(json)) + return freerdp_settings_set_pointer(settings, key, NULL); + + if (!WINPR_JSON_IsArray(json)) + return FALSE; + + size_t len = WINPR_JSON_GetArraySize(json); + if (!freerdp_settings_set_pointer_len(settings, key, NULL, len)) + return FALSE; + + for (size_t x = 0; x < len; x++) + { + WINPR_JSON* val = WINPR_JSON_GetArrayItem(json, x); + if (!device_from_json_item(settings, key, x, val)) + return FALSE; + } + return TRUE; +} + +static const char* pem_from_json(const WINPR_JSON* jval, size_t* plen, BOOL* pvalid) +{ + WINPR_ASSERT(jval); + WINPR_ASSERT(plen); + WINPR_ASSERT(pvalid); + + *pvalid = FALSE; + *plen = 0; + + if (WINPR_JSON_IsNull(jval)) + return NULL; + + size_t len = WINPR_JSON_GetArraySize(jval); + if (len == 0) + { + *pvalid = TRUE; + return NULL; + } + + WINPR_JSON* item = WINPR_JSON_GetArrayItem(jval, 0); + if (!item) + return NULL; + if (!WINPR_JSON_IsString(item)) + return NULL; + + *plen = len; + *pvalid = TRUE; + return WINPR_JSON_GetStringValue(item); +} + +static BOOL deserialize_pointer(const WINPR_JSON* json, rdpSettings* settings, + FreeRDP_Settings_Keys_Pointer id) +{ + const char* name = freerdp_settings_get_name_for_key(id); + if (!name) + return FALSE; + + if (!WINPR_JSON_HasObjectItem(json, name)) + return FALSE; + + WINPR_JSON* jval = WINPR_JSON_GetObjectItem(json, name); + if (!WINPR_JSON_IsNull(jval) && !WINPR_JSON_IsArray(jval)) + return FALSE; + + switch (id) + { + case FreeRDP_instance: + return uintptr_from_array(settings, jval); + case FreeRDP_ServerRandom: + case FreeRDP_ServerCertificate: + case FreeRDP_ClientRandom: + case FreeRDP_RedirectionPassword: + case FreeRDP_RedirectionGuid: + case FreeRDP_LoadBalanceInfo: + case FreeRDP_RedirectionTsvUrl: + case FreeRDP_OrderSupport: + case FreeRDP_Password51: + return val_from_array(settings, jval, id, 1); + case FreeRDP_ReceivedCapabilities: + return val_from_array(settings, jval, id, 1); + case FreeRDP_ClientTimeZone: + return ts_info_array_from_json(settings, id, jval); + case FreeRDP_GlyphCache: + return glyph_cache_def_array_from_json(settings, id, jval); + case FreeRDP_FragCache: + return glyph_cache_def_array_from_json(settings, id, jval); + case FreeRDP_BitmapCacheV2CellInfo: + return bitmap_cache_v2_array_from_json(settings, id, jval); + case FreeRDP_ClientAutoReconnectCookie: + return client_cookie_array_from_json(settings, id, jval); + case FreeRDP_ServerAutoReconnectCookie: + return server_cookie_array_from_json(settings, id, jval); + case FreeRDP_MonitorDefArray: + return monitor_def_array_from_json(settings, id, jval); + case FreeRDP_ChannelDefArray: + return channel_def_array_from_json(settings, id, jval); + case FreeRDP_MonitorIds: + return val_from_uint32_array(settings, jval, id, FreeRDP_NumMonitorIds); + case FreeRDP_TargetNetPorts: + return val_from_uint32_array(settings, jval, id, FreeRDP_TargetNetAddressCount); + case FreeRDP_ServerLicenseProductIssuers: + case FreeRDP_TargetNetAddresses: + return str_array_from_json(settings, jval, id); + case FreeRDP_ReceivedCapabilityDataSizes: + return val_from_uint32_array(settings, jval, id, FreeRDP_ReceivedCapabilitiesSize); + case FreeRDP_ReceivedCapabilityData: + return caps_data_array_from_json(settings, jval); + case FreeRDP_RedirectionTargetCertificate: + case FreeRDP_RdpServerCertificate: + { + size_t len = 0; + BOOL valid = FALSE; + const char* pem = pem_from_json(jval, &len, &valid); + if (!valid) + return FALSE; + if (!freerdp_settings_set_pointer_len(settings, id, NULL, len)) + return FALSE; + + rdpCertificate* cert = NULL; + if (!pem) + return TRUE; + + if (strnlen(pem, 2) == 0) + cert = freerdp_certificate_new(); + else + cert = freerdp_certificate_new_from_pem(pem); + if (!cert) + return FALSE; + return freerdp_settings_set_pointer_len(settings, id, cert, 1); + } + case FreeRDP_RdpServerRsaKey: + { + size_t len = 0; + BOOL valid = FALSE; + const char* pem = pem_from_json(jval, &len, &valid); + if (!valid) + return FALSE; + if (!freerdp_settings_set_pointer_len(settings, id, NULL, len)) + return FALSE; + if (!pem) + return TRUE; + + rdpPrivateKey* key = freerdp_key_new_from_pem_enc(pem, NULL); + if (!key) + return FALSE; + return freerdp_settings_set_pointer_len(settings, id, key, 1); + } + case FreeRDP_DeviceArray: + return device_array_from_json(settings, jval, id); + case FreeRDP_StaticChannelArray: + case FreeRDP_DynamicChannelArray: + return addin_argv_from_json(settings, jval, id); + case FreeRDP_POINTER_UNUSED: + default: + return TRUE; + } +} + +rdpSettings* freerdp_settings_deserialize(const char* jstr, size_t length) +{ + WINPR_JSON* json = WINPR_JSON_ParseWithLength(jstr, length); + if (!json) + return NULL; + rdpSettings* settings = freerdp_settings_new(0); + if (!settings) + goto fail; + + WINPR_JSON* jbool = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_BOOL)); + if (!jbool) + goto fail; + WINPR_JSON* juint16 = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_UINT16)); + if (!juint16) + goto fail; + WINPR_JSON* jint16 = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_INT16)); + if (!jint16) + goto fail; + WINPR_JSON* juint32 = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_UINT32)); + if (!juint32) + goto fail; + WINPR_JSON* jint32 = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_INT32)); + if (!jint32) + goto fail; + WINPR_JSON* juint64 = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_UINT64)); + if (!juint64) + goto fail; + WINPR_JSON* jint64 = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_INT64)); + if (!jint64) + goto fail; + WINPR_JSON* jstring = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_STRING)); + if (!jstring) + goto fail; + WINPR_JSON* jpointer = WINPR_JSON_GetObjectItem( + json, freerdp_settings_get_type_name_for_type(RDP_SETTINGS_TYPE_POINTER)); + if (!jpointer) + goto fail; + + for (SSIZE_T x = 0; x < FreeRDP_Settings_StableAPI_MAX; x++) + { + union + { + + int i; + SSIZE_T s; + FreeRDP_Settings_Keys_Bool b; + FreeRDP_Settings_Keys_Int16 i16; + FreeRDP_Settings_Keys_UInt16 u16; + FreeRDP_Settings_Keys_Int32 i32; + FreeRDP_Settings_Keys_UInt32 u32; + FreeRDP_Settings_Keys_Int64 i64; + FreeRDP_Settings_Keys_UInt64 u64; + FreeRDP_Settings_Keys_String str; + FreeRDP_Settings_Keys_Pointer ptr; + } iter; + iter.s = x; + + SSIZE_T type = freerdp_settings_get_type_for_key(iter.s); + switch (type) + { + case RDP_SETTINGS_TYPE_POINTER: + if (!deserialize_pointer(jpointer, settings, iter.ptr)) + goto fail; + break; + default: + break; + } + } + + for (SSIZE_T x = 0; x < FreeRDP_Settings_StableAPI_MAX; x++) + { + union + { + + int i; + SSIZE_T s; + FreeRDP_Settings_Keys_Bool b; + FreeRDP_Settings_Keys_Int16 i16; + FreeRDP_Settings_Keys_UInt16 u16; + FreeRDP_Settings_Keys_Int32 i32; + FreeRDP_Settings_Keys_UInt32 u32; + FreeRDP_Settings_Keys_Int64 i64; + FreeRDP_Settings_Keys_UInt64 u64; + FreeRDP_Settings_Keys_String str; + FreeRDP_Settings_Keys_Pointer ptr; + } iter; + iter.s = x; + + const char* name = freerdp_settings_get_name_for_key(iter.s); + SSIZE_T type = freerdp_settings_get_type_for_key(iter.s); + switch (type) + { + case RDP_SETTINGS_TYPE_BOOL: + { + WINPR_JSON* item = WINPR_JSON_GetObjectItem(jbool, name); + if (!item) + goto fail; + if (!WINPR_JSON_IsBool(item)) + goto fail; + const BOOL val = WINPR_JSON_IsTrue(item); + if (!freerdp_settings_set_bool(settings, iter.b, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_UINT16: + { + WINPR_JSON* item = WINPR_JSON_GetObjectItem(juint16, name); + const uint16_t val = (uint16_t)uint_from_json_item(item, UINT16_MAX); + if (errno != 0) + goto fail; + if (!freerdp_settings_set_uint16(settings, iter.u16, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_INT16: + { + WINPR_JSON* item = WINPR_JSON_GetObjectItem(jint16, name); + const int16_t val = (int16_t)int_from_json_item(item, INT16_MIN, INT16_MAX); + if (errno != 0) + goto fail; + if (!freerdp_settings_set_int16(settings, iter.i16, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_UINT32: + { + WINPR_JSON* item = WINPR_JSON_GetObjectItem(juint32, name); + const uint32_t val = (uint32_t)uint_from_json_item(item, UINT32_MAX); + if (errno != 0) + goto fail; + if (!freerdp_settings_set_uint32(settings, iter.u32, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_INT32: + { + const int64_t val = int_from_json(jint32, name, INT32_MIN, INT32_MAX); + if (errno != 0) + goto fail; + if (!freerdp_settings_set_int32(settings, iter.i32, (int32_t)val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_UINT64: + { + const uint64_t val = uint_from_json(juint64, name, UINT64_MAX); + if (errno != 0) + goto fail; + if (!freerdp_settings_set_uint64(settings, iter.u64, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_INT64: + { + WINPR_JSON* item = WINPR_JSON_GetObjectItem(jint64, name); + const int64_t val = int_from_json_item(item, INT64_MIN, INT64_MAX); + if (errno != 0) + goto fail; + if (!freerdp_settings_set_int64(settings, iter.i64, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_STRING: + { + const char* val = NULL; + WINPR_JSON* item = WINPR_JSON_GetObjectItem(jstring, name); + if (item && !WINPR_JSON_IsNull(item)) + { + if (!WINPR_JSON_IsString(item)) + goto fail; + val = WINPR_JSON_GetStringValue(item); + if (!val) + goto fail; + } + if (!freerdp_settings_set_string(settings, iter.str, val)) + goto fail; + } + break; + case RDP_SETTINGS_TYPE_POINTER: + default: + break; + } + } + + WINPR_JSON_Delete(json); + return settings; + +fail: + freerdp_settings_free(settings); + WINPR_JSON_Delete(json); + return NULL; +} diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 3ec1003ed..ed7330325 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -2366,8 +2366,7 @@ BOOL freerdp_settings_set_uint32(WINPR_ATTR_UNUSED rdpSettings* settings, break; case FreeRDP_ReceivedCapabilitiesSize: - settings->ReceivedCapabilitiesSize = cnv.c; - break; + return freerdp_capability_buffer_resize(settings, cnv.c); case FreeRDP_RedirectedSessionId: settings->RedirectedSessionId = cnv.c; @@ -2494,8 +2493,7 @@ BOOL freerdp_settings_set_uint32(WINPR_ATTR_UNUSED rdpSettings* settings, break; case FreeRDP_TargetNetAddressCount: - settings->TargetNetAddressCount = cnv.c; - break; + return freerdp_target_net_addresses_resize(settings, cnv.c); case FreeRDP_TcpAckTimeout: settings->TcpAckTimeout = cnv.c; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 769ac53f4..fff968166 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -79,11 +79,14 @@ BOOL freerdp_settings_clone_keys(rdpSettings* dst, const rdpSettings* src) break; case FREERDP_SETTINGS_TYPE_POINTER: /* pointer */ { - const void* sval = - freerdp_settings_get_pointer(src, (FreeRDP_Settings_Keys_Pointer)cur->id); - if (!freerdp_settings_set_pointer(dst, (FreeRDP_Settings_Keys_Pointer)cur->id, - sval)) - return FALSE; + if (cur->id == FreeRDP_instance) + { + const void* sval = + freerdp_settings_get_pointer(src, (FreeRDP_Settings_Keys_Pointer)cur->id); + if (!freerdp_settings_set_pointer(dst, (FreeRDP_Settings_Keys_Pointer)cur->id, + sval)) + return FALSE; + } } break; default: diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index f8ba69340..b04cedcc1 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -752,38 +752,6 @@ BOOL freerdp_settings_set_default_order_support(rdpSettings* settings) return TRUE; } -BOOL freerdp_capability_buffer_allocate(rdpSettings* settings, UINT32 count) -{ - WINPR_ASSERT(settings); - WINPR_ASSERT(count > 0); - WINPR_ASSERT(count == 32); - - freerdp_capability_buffer_free(settings); - WINPR_ASSERT(settings->ReceivedCapabilitiesSize == 0); - - settings->ReceivedCapabilitiesSize = count; - void* tmp = realloc(settings->ReceivedCapabilities, count * sizeof(BYTE)); - if (!tmp) - return FALSE; - memset(tmp, 0, count * sizeof(BYTE)); - settings->ReceivedCapabilities = tmp; - - tmp = realloc((void*)settings->ReceivedCapabilityData, count * sizeof(BYTE*)); - if (!tmp) - return FALSE; - memset(tmp, 0, count * sizeof(BYTE*)); - settings->ReceivedCapabilityData = (BYTE**)tmp; - - tmp = realloc(settings->ReceivedCapabilityDataSizes, count * sizeof(UINT32)); - if (!tmp) - return FALSE; - memset(tmp, 0, count * sizeof(UINT32)); - settings->ReceivedCapabilityDataSizes = tmp; - - return (settings->ReceivedCapabilities && settings->ReceivedCapabilityData && - settings->ReceivedCapabilityDataSizes); -} - #if !defined(WITH_FULL_CONFIG_PATH) static char* freerdp_settings_get_legacy_config_path(void) { @@ -998,7 +966,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, NULL, 1)) goto out_fail; - if (!freerdp_capability_buffer_allocate(settings, 32)) + if (!freerdp_capability_buffer_resize(settings, 32)) goto out_fail; { @@ -1396,12 +1364,12 @@ static BOOL freerdp_settings_int_buffer_copy(rdpSettings* _settings, const rdpSe if (!_settings->OrderSupport) goto out_fail; - if (!freerdp_capability_buffer_copy(_settings, settings)) - goto out_fail; - CopyMemory(_settings->OrderSupport, settings->OrderSupport, 32); } + if (!freerdp_capability_buffer_copy(_settings, settings)) + goto out_fail; + const UINT32 glyphCacheCount = 10; const GLYPH_CACHE_DEFINITION* glyphCache = freerdp_settings_get_pointer(settings, FreeRDP_GlyphCache); @@ -1432,7 +1400,6 @@ static BOOL freerdp_settings_int_buffer_copy(rdpSettings* _settings, const rdpSe const UINT32 nrports = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount); if (!freerdp_target_net_adresses_reset(_settings, nrports)) goto out_fail; - ; for (UINT32 i = 0; i < nrports; i++) { @@ -1526,34 +1493,6 @@ BOOL freerdp_settings_copy(rdpSettings* _settings, const rdpSettings* settings) * NULL to fix issues during cleanup */ rc = freerdp_settings_clone_keys(_settings, settings); - _settings->LoadBalanceInfo = NULL; - _settings->ServerRandom = NULL; - _settings->ClientRandom = NULL; - _settings->ServerCertificate = NULL; - _settings->RdpServerCertificate = NULL; - _settings->RdpServerRsaKey = NULL; - _settings->ChannelDefArray = NULL; - _settings->MonitorDefArray = NULL; - _settings->MonitorIds = NULL; - _settings->OrderSupport = NULL; - _settings->BitmapCacheV2CellInfo = NULL; - _settings->GlyphCache = NULL; - _settings->FragCache = NULL; - _settings->ClientAutoReconnectCookie = NULL; - _settings->ServerAutoReconnectCookie = NULL; - _settings->ClientTimeZone = NULL; - _settings->RedirectionPassword = NULL; - _settings->RedirectionTsvUrl = NULL; - _settings->TargetNetAddresses = NULL; - _settings->TargetNetPorts = NULL; - _settings->RedirectionGuid = NULL; - _settings->DeviceArray = NULL; - _settings->StaticChannelArray = NULL; - _settings->DynamicChannelArray = NULL; - _settings->ReceivedCapabilities = NULL; - _settings->ReceivedCapabilityData = NULL; - _settings->ReceivedCapabilityDataSizes = NULL; - _settings->ServerLicenseProductIssuersCount = 0; _settings->ServerLicenseProductIssuers = NULL; @@ -1702,17 +1641,7 @@ BOOL freerdp_target_net_adresses_reset(rdpSettings* settings, size_t size) { freerdp_target_net_addresses_free(settings); - if (size > 0) - { - if (!freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetPorts, - FreeRDP_UINT32_UNUSED, NULL, size, sizeof(UINT32))) - return FALSE; - if (!freerdp_settings_set_pointer_len_(settings, FreeRDP_TargetNetAddresses, - FreeRDP_TargetNetAddressCount, NULL, size, - sizeof(char*))) - return FALSE; - } - return TRUE; + return freerdp_target_net_addresses_resize(settings, size); } BOOL freerdp_settings_enforce_monitor_exists(rdpSettings* settings) diff --git a/libfreerdp/core/settings.h b/libfreerdp/core/settings.h index 86a633a6d..841b5805f 100644 --- a/libfreerdp/core/settings.h +++ b/libfreerdp/core/settings.h @@ -48,7 +48,7 @@ FREERDP_LOCAL BOOL freerdp_settings_set_string_(rdpSettings* settings, FREERDP_LOCAL BOOL freerdp_settings_set_string_copy_(rdpSettings* settings, FreeRDP_Settings_Keys_String id, const char* val, size_t len, BOOL cleanup); -FREERDP_LOCAL BOOL freerdp_capability_buffer_allocate(rdpSettings* settings, UINT32 count); +FREERDP_LOCAL BOOL freerdp_capability_buffer_resize(rdpSettings* settings, size_t count); FREERDP_LOCAL BOOL identity_set_from_settings_with_pwd(SEC_WINNT_AUTH_IDENTITY_W* identity, const rdpSettings* settings, @@ -75,4 +75,6 @@ FREERDP_LOCAL BOOL freerdp_settings_set_pointer_len_(rdpSettings* settings, const void* data, size_t len, size_t size); FREERDP_LOCAL BOOL freerdp_target_net_adresses_reset(rdpSettings* settings, size_t size); +FREERDP_LOCAL BOOL freerdp_target_net_addresses_resize(rdpSettings* settings, size_t count); + #endif /* FREERDP_LIB_CORE_SETTINGS_H */ diff --git a/libfreerdp/core/test/TestSettings.c b/libfreerdp/core/test/TestSettings.c index 3778a9415..325bb02ad 100644 --- a/libfreerdp/core/test/TestSettings.c +++ b/libfreerdp/core/test/TestSettings.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -256,7 +257,7 @@ static BOOL test_copy(void) goto fail; if (freerdp_settings_print_diff(log, WLOG_WARN, settings, copy)) goto fail; - if (!freerdp_settings_print_diff(log, WLOG_WARN, settings, modified)) + if (!freerdp_settings_print_diff(log, WLOG_INFO, settings, modified)) goto fail; rc = TRUE; @@ -555,6 +556,16 @@ static BOOL check_key_helpers(size_t key, const char* stype) winpr_RAND(&intEntryType, sizeof(intEntryType)); winpr_RAND(&val.u64, sizeof(val.u64)); + switch (key) + { + case FreeRDP_ReceivedCapabilitiesSize: + case FreeRDP_TargetNetAddressCount: + val.u64 %= 512; + break; + default: + break; + } + switch (type) { case RDP_SETTINGS_TYPE_BOOL: @@ -598,8 +609,8 @@ static BOOL check_key_helpers(size_t key, const char* stype) have = freerdp_settings_set_value_for_name(settings, name, value); if (have != expect) { - printf("[%s] have[%s] != expect[%s]\n", stype, have ? "TRUE" : "FALSE", - expect ? "TRUE" : "FALSE"); + printf("[%s] %s=%s have [%s] != expect[%s]\n", stype, name, value, + have ? "TRUE" : "FALSE", expect ? "TRUE" : "FALSE"); goto fail; } @@ -1517,6 +1528,342 @@ fail: return rc; } +static BOOL test_serialize_with(rdpSettings* src, const char* name) +{ + BOOL rc = FALSE; + size_t slen = 0; + rdpSettings* dst = NULL; + char* str = NULL; + if (!src) + goto fail; + + str = freerdp_settings_serialize(src, TRUE, &slen); + if (!str || (slen == 0)) + goto fail; + + dst = freerdp_settings_deserialize(str, slen); + if (!dst) + goto fail; + + rc = !freerdp_settings_print_diff(WLog_Get("TestSettings::serialize"), WLOG_WARN, src, dst); + +fail: + freerdp_settings_free(src); + freerdp_settings_free(dst); + free(str); + printf("Test %s: %s\n", name, rc ? "success" : "failure"); + return rc; +} + +static BOOL test_serialize_strings(DWORD flags, const char* str) +{ + rdpSettings* src = freerdp_settings_new(flags); + if (!src) + return FALSE; + + for (SSIZE_T x = 0; x < FreeRDP_Settings_StableAPI_MAX; x++) + { + union + { + SSIZE_T s; + FreeRDP_Settings_Keys_Pointer ptr; + } iter; + iter.s = x; + + SSIZE_T type = freerdp_settings_get_type_for_key(iter.s); + switch (type) + { + case RDP_SETTINGS_TYPE_STRING: + if (!freerdp_settings_set_string(src, iter.ptr, str)) + { + freerdp_settings_free(src); + return FALSE; + } + break; + default: + break; + } + } + + char buffer[128] = { 0 }; + (void)_snprintf(buffer, sizeof(buffer), "%s flags 0x%08" PRIx32 " {%s}", __func__, flags, str); + return test_serialize_with(src, buffer); +} + +static BOOL add_argv(rdpSettings* src, size_t argc, const char* argv[]) +{ + ADDIN_ARGV* val = freerdp_addin_argv_new(argc, argv); + if (!val) + return FALSE; + if (!freerdp_static_channel_collection_add(src, val)) + return FALSE; + if (!freerdp_static_channel_collection_add(src, freerdp_addin_argv_clone(val))) + return FALSE; + if (!freerdp_dynamic_channel_collection_add(src, freerdp_addin_argv_clone(val))) + return FALSE; + + return TRUE; +} + +static BOOL add_dev_argv(rdpSettings* src, size_t argc, const char* argv[]) +{ + FreeRDP_Settings_Keys_Pointer key = FreeRDP_DeviceArray; + size_t count = 6; + if (!freerdp_settings_set_pointer_len(src, key, NULL, count)) + return FALSE; + + const uint32_t types[] = { RDPDR_DTYP_SERIAL, RDPDR_DTYP_PARALLEL, RDPDR_DTYP_PRINT, + RDPDR_DTYP_FILESYSTEM, RDPDR_DTYP_SMARTCARD }; + + for (size_t x = 0; x < count; x++) + { + const uint32_t type = types[x % ARRAYSIZE(types)]; + RDPDR_DEVICE* arg = freerdp_device_new(type, argc, argv); + if (!arg) + return FALSE; + const BOOL rc = freerdp_settings_set_pointer_array(src, key, x, arg); + freerdp_device_free(arg); + if (!rc) + return FALSE; + } + return TRUE; +} + +static BOOL fill_random(rdpSettings* src, FreeRDP_Settings_Keys_Pointer key, size_t elem, + size_t len) +{ + if (!freerdp_settings_set_pointer_len(src, key, NULL, len)) + return FALSE; + uint8_t* data = freerdp_settings_get_pointer_writable(src, key); + if (!data) + return FALSE; + winpr_RAND(data, len * elem); + return TRUE; +} + +static BOOL fill_random_timezone(rdpSettings* src) +{ + FreeRDP_Settings_Keys_Pointer key = FreeRDP_ClientTimeZone; + if (!fill_random(src, key, sizeof(TIME_ZONE_INFORMATION), 1)) + return FALSE; + + TIME_ZONE_INFORMATION* data = freerdp_settings_get_pointer_writable(src, key); + if (!data) + return FALSE; + (void)ConvertUtf8ToWChar("testXXXXDaylight", data->DaylightName, ARRAYSIZE(data->DaylightName)); + (void)ConvertUtf8ToWChar("testXXXX", data->StandardName, ARRAYSIZE(data->StandardName)); + + return TRUE; +} + +static BOOL set_private_key(rdpSettings* src) +{ + if (!freerdp_settings_set_pointer_len(src, FreeRDP_RdpServerRsaKey, NULL, 1)) + return FALSE; + + rdpPrivateKey* key = + freerdp_settings_get_pointer_array_writable(src, FreeRDP_RdpServerRsaKey, 0); + if (!key) + return FALSE; + + return freerdp_key_generate(key, "RSA", 1, 4096); +} + +static BOOL set_cert(rdpSettings* src, FreeRDP_Settings_Keys_Pointer key) +{ + const char pem[] = "-----BEGIN CERTIFICATE-----\n" + "MIICvTCCAaWgAwIBAgIEZrM9yjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlt\n" + "b3RvcmhlYWQwHhcNMjUwNDE1MTExMjE5WhcNMjYwNDE1MTExMjE5WjAUMRIwEAYD\n" + "VQQDDAltb3RvcmhlYWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw\n" + "g0tADY3kh5Hi6YsTsQbuaPs50zlTpIv+rCCK3BNIblsIh4cSO1YGdWfB0gP9bUD1\n" + "L7mPWRnIiAvwrRA/Mgyo+UgiYj/aE3xxN3adB9/QsUzNrI07o6L8MupV4237txMj\n" + "uxVmarB4c7E4wFgSxwbMQPhQtoNNew3bY+EeqhQBMFfYy4z+rg60xl0QHGcMePY/\n" + "xz0WMHrIz6FhZfBIr+BGViRtjIchbjcU0HfTSujX+MT0D5MBISe8aiFvrewFItfT\n" + "vglriDLeNMiB9U/aRLV8OtW+heGNhi5qSC9JXEW70OFeGAoqtwyRHLnSh38Fo2xv\n" + "fEc90zjkCan8usEDKuzBAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0G\n" + "CSqGSIb3DQEBCwUAA4IBAQBY1wkJ6XWduNmTi+UNdcZ5e9GWV/3+SYLtFALwKVrU\n" + "KQQbsYnGLfyUKXFc7e9JoZ+UCTJgY3EyL+6p79io+cFeTtpp1RVKljibbeRAP01W\n" + "WbcxcHZFKgBlH1KNSeO7iOAPet3aCaVDKl1XSU7fhxtsfBBI9YTtaMZM5e9WhuHK\n" + "lL11Un6ePThX+4NG1yYp0X+emqUHd/qaq8IShnU6ajvzoloWGf4vLlDSsuFHJJsK\n" + "LnshNFOFGAjp1Se4DjhtUSr6Xofdse+kx9cSQazCZ5vFJNeHkxr0B7ojQ4bN37Tg\n" + "2uyfSclCCLnmjcoRMlIGUiL2bevCPDRNRWiblDgx3tGG\n" + "-----END CERTIFICATE-----\n"; + + rdpCertificate* cert = freerdp_certificate_new_from_pem(pem); + if (!cert) + return FALSE; + return freerdp_settings_set_pointer_len(src, key, cert, 1); +} + +static BOOL set_string_array(rdpSettings* src, FreeRDP_Settings_Keys_Pointer key, uint32_t max) +{ + uint32_t count = 0; + winpr_RAND(&count, sizeof(count)); + count = count % max; + + if (!freerdp_settings_set_pointer_len(src, key, NULL, count)) + return FALSE; + + for (uint32_t x = 0; x < count; x++) + { + char buffer[32] = { 0 }; + (void)_snprintf(buffer, sizeof(buffer), "foobar-0x%08" PRIu32, x); + if (!freerdp_settings_set_pointer_array(src, key, x, buffer)) + return FALSE; + } + return TRUE; +} + +static BOOL test_serialize_pointer(DWORD flags) +{ + rdpSettings* src = freerdp_settings_new(flags); + if (!src) + return FALSE; + + const char* argv1[] = { "foobar", "lala", "haha" }; + const char* argv2[] = { "lala", "haha" }; + const char* argv3[] = { "haha" }; + if (!add_argv(src, ARRAYSIZE(argv1), argv1)) + goto fail; + if (!add_argv(src, ARRAYSIZE(argv2), argv2)) + goto fail; + if (!add_argv(src, ARRAYSIZE(argv3), argv3)) + goto fail; + if (!add_dev_argv(src, ARRAYSIZE(argv3), argv3)) + goto fail; + + struct key_len_pair + { + FreeRDP_Settings_Keys_Pointer key; + size_t elem; + size_t len; + }; + + const struct key_len_pair keys[] = { + { FreeRDP_ServerRandom, 1, 123 }, + { FreeRDP_RedirectionPassword, 1, 13 }, + { FreeRDP_ClientRandom, 1, 23 }, + { FreeRDP_RedirectionGuid, 1, 22 }, + { FreeRDP_LoadBalanceInfo, 1, 21 }, + { FreeRDP_ServerCertificate, 1, 512 }, + { FreeRDP_RedirectionTsvUrl, 1, 33 }, + { FreeRDP_GlyphCache, sizeof(GLYPH_CACHE_DEFINITION), 10 }, + { FreeRDP_FragCache, sizeof(GLYPH_CACHE_DEFINITION), 1 }, + { FreeRDP_BitmapCacheV2CellInfo, sizeof(BITMAP_CACHE_V2_CELL_INFO), 1 }, + { FreeRDP_OrderSupport, 1, 32 }, + { FreeRDP_ClientAutoReconnectCookie, sizeof(ARC_CS_PRIVATE_PACKET), 1 }, + { FreeRDP_ServerAutoReconnectCookie, sizeof(ARC_SC_PRIVATE_PACKET), 1 }, + { FreeRDP_Password51, 1, 54 }, + { FreeRDP_MonitorIds, 1, 111 }, + { FreeRDP_MonitorDefArray, 1, 7 }, + { FreeRDP_ChannelDefArray, 1, 31 }, + { FreeRDP_ReceivedCapabilities, sizeof(uint8_t), 33 }, + { FreeRDP_ReceivedCapabilityData, sizeof(uint8_t*), 33 }, + { FreeRDP_ReceivedCapabilityDataSizes, sizeof(UINT32), 33 } + }; + + for (size_t x = 0; x < ARRAYSIZE(keys); x++) + { + const struct key_len_pair* cur = &keys[x]; + if (!fill_random(src, cur->key, cur->elem, cur->len)) + goto fail; + } + + if (!fill_random_timezone(src)) + goto fail; + + void* ptr = NULL; + winpr_RAND((void*)&ptr, sizeof(void*)); + if (!freerdp_settings_set_pointer(src, FreeRDP_instance, ptr)) + return FALSE; + + if (!set_private_key(src)) + goto fail; + if (!set_cert(src, FreeRDP_RedirectionTargetCertificate)) + goto fail; + if (!set_cert(src, FreeRDP_RdpServerCertificate)) + goto fail; + + if (!set_string_array(src, FreeRDP_ServerLicenseProductIssuers, 43)) + goto fail; + + char addresses[12][43] = { 0 }; + char* strptr[12] = { 0 }; + + for (size_t x = 0; x < ARRAYSIZE(addresses); x++) + { + (void)_snprintf(addresses[x], 43, "foobar-0x%08" PRIx32, x); + strptr[x] = addresses[x]; + } + + if (!freerdp_target_net_addresses_copy(src, strptr, ARRAYSIZE(addresses))) + goto fail; + + for (size_t x = 0; x < ARRAYSIZE(addresses); x++) + { + uint32_t port = 0; + winpr_RAND(&port, sizeof(port)); + if (!freerdp_settings_set_pointer_array(src, FreeRDP_TargetNetPorts, x, &port)) + goto fail; + } + + uint32_t count = freerdp_settings_get_uint32(src, FreeRDP_ReceivedCapabilitiesSize); + if (count != 33) + goto fail; + + void* caps = freerdp_settings_get_pointer_writable(src, FreeRDP_ReceivedCapabilities); + if (!caps) + goto fail; + winpr_RAND(caps, count); + + for (uint32_t x = 0; x < count; x++) + { + uint8_t* buffer = calloc(64, sizeof(uint8_t)); + if (!buffer) + goto fail; + winpr_RAND(buffer, sizeof(buffer)); + uint32_t blen = (buffer[0] % 52) + 13; + + if (!freerdp_settings_set_pointer_array(src, FreeRDP_ReceivedCapabilityData, x, buffer)) + goto fail; + if (!freerdp_settings_set_pointer_array(src, FreeRDP_ReceivedCapabilityDataSizes, x, &blen)) + goto fail; + } + + char buffer[128] = { 0 }; + (void)_snprintf(buffer, sizeof(buffer), "%s flags 0x%08" PRIx32, __func__, flags); + return test_serialize_with(src, buffer); +fail: + printf("Test %s: (pre)failure\n", __func__); + freerdp_settings_free(src); + return FALSE; +} + +static BOOL test_serialize(void) +{ + if (WINPR_JSON_version(NULL, 0) < 0) + return TRUE; + + for (uint32_t flags = 0; flags <= (FREERDP_SETTINGS_SERVER_MODE | FREERDP_SETTINGS_REMOTE_MODE); + flags++) + { + char buffer[32] = { 0 }; + (void)_snprintf(buffer, sizeof(buffer), "default (flags 0x%08" PRIx32 ")", flags); + if (!test_serialize_with(freerdp_settings_new(flags), buffer)) + return FALSE; + if (!test_serialize_strings(flags, "foobar")) + return FALSE; + if (!test_serialize_strings(flags, "")) + return FALSE; + if (!test_serialize_strings(flags, NULL)) + return FALSE; + if (!test_serialize_pointer(flags)) + return FALSE; + } + + return TRUE; +} + int TestSettings(int argc, char* argv[]) { int rc = -1; @@ -1526,6 +1873,8 @@ int TestSettings(int argc, char* argv[]) WINPR_UNUSED(argc); WINPR_UNUSED(argv); + if (!test_serialize()) + goto fail; if (!test_dyn_channels()) goto fail; if (!test_static_channels()) diff --git a/winpr/libwinpr/utils/json/json.c b/winpr/libwinpr/utils/json/json.c index 160e25425..e232dee20 100644 --- a/winpr/libwinpr/utils/json/json.c +++ b/winpr/libwinpr/utils/json/json.c @@ -104,7 +104,8 @@ int WINPR_JSON_version(char* buffer, size_t len) #elif defined(WITH_CJSON) return _snprintf(buffer, len, "cJSON %s", cJSON_Version()); #else - return _snprintf(buffer, len, "JSON support not available"); + (void)_snprintf(buffer, len, "JSON support not available"); + return -1; #endif }