Merge pull request #11961 from akallabeth/config-extension

Config extension
This commit is contained in:
akallabeth
2025-10-28 12:08:12 +01:00
committed by GitHub
15 changed files with 235 additions and 50 deletions

View File

@@ -512,7 +512,16 @@ struct rdp_settings
SETTINGS_DEPRECATED(ALIGN64 char* GatewayAvdAccessAadFormat); /** 2023
* @since version 3.16.0
*/
UINT64 padding2112[2112 - 2024]; /* 2024 */
SETTINGS_DEPRECATED(ALIGN64 char* GatewayHttpReferer); /** 2024
* @since version 3.18.0
*/
SETTINGS_DEPRECATED(ALIGN64 char* GatewayHttpUserAgent); /** 2025
* @since version 3.18.0
*/
SETTINGS_DEPRECATED(ALIGN64 char* GatewayHttpMsUserAgent); /** 2026
* @since version 3.18.0
*/
UINT64 padding2112[2112 - 2027]; /* 2027 */
/**
* RemoteApp

View File

@@ -2373,7 +2373,8 @@ BOOL freerdp_settings_set_uint32(WINPR_ATTR_UNUSED rdpSettings* settings,
break;
case FreeRDP_ReceivedCapabilitiesSize:
return freerdp_capability_buffer_resize(settings, cnv.c, FALSE);
settings->ReceivedCapabilitiesSize = cnv.c;
break;
case FreeRDP_RedirectedSessionId:
settings->RedirectedSessionId = cnv.c;
@@ -2500,7 +2501,8 @@ BOOL freerdp_settings_set_uint32(WINPR_ATTR_UNUSED rdpSettings* settings,
break;
case FreeRDP_TargetNetAddressCount:
return freerdp_target_net_addresses_resize(settings, cnv.c);
settings->TargetNetAddressCount = cnv.c;
break;
case FreeRDP_TcpAckTimeout:
settings->TcpAckTimeout = cnv.c;
@@ -2863,6 +2865,15 @@ const char* freerdp_settings_get_string(WINPR_ATTR_UNUSED const rdpSettings* set
case FreeRDP_GatewayHttpExtAuthBearer:
return settings->GatewayHttpExtAuthBearer;
case FreeRDP_GatewayHttpMsUserAgent:
return settings->GatewayHttpMsUserAgent;
case FreeRDP_GatewayHttpReferer:
return settings->GatewayHttpReferer;
case FreeRDP_GatewayHttpUserAgent:
return settings->GatewayHttpUserAgent;
case FreeRDP_GatewayPassword:
return settings->GatewayPassword;
@@ -3187,6 +3198,15 @@ char* freerdp_settings_get_string_writable(rdpSettings* settings, FreeRDP_Settin
case FreeRDP_GatewayHttpExtAuthBearer:
return settings->GatewayHttpExtAuthBearer;
case FreeRDP_GatewayHttpMsUserAgent:
return settings->GatewayHttpMsUserAgent;
case FreeRDP_GatewayHttpReferer:
return settings->GatewayHttpReferer;
case FreeRDP_GatewayHttpUserAgent:
return settings->GatewayHttpUserAgent;
case FreeRDP_GatewayPassword:
return settings->GatewayPassword;
@@ -3522,6 +3542,15 @@ BOOL freerdp_settings_set_string_(WINPR_ATTR_UNUSED rdpSettings* settings,
case FreeRDP_GatewayHttpExtAuthBearer:
return update_string_(&settings->GatewayHttpExtAuthBearer, cnv.c, len);
case FreeRDP_GatewayHttpMsUserAgent:
return update_string_(&settings->GatewayHttpMsUserAgent, cnv.c, len);
case FreeRDP_GatewayHttpReferer:
return update_string_(&settings->GatewayHttpReferer, cnv.c, len);
case FreeRDP_GatewayHttpUserAgent:
return update_string_(&settings->GatewayHttpUserAgent, cnv.c, len);
case FreeRDP_GatewayPassword:
return update_string_(&settings->GatewayPassword, cnv.c, len);
@@ -3877,6 +3906,15 @@ BOOL freerdp_settings_set_string_copy_(WINPR_ATTR_UNUSED rdpSettings* settings,
case FreeRDP_GatewayHttpExtAuthBearer:
return update_string_copy_(&settings->GatewayHttpExtAuthBearer, cnv.cc, len, cleanup);
case FreeRDP_GatewayHttpMsUserAgent:
return update_string_copy_(&settings->GatewayHttpMsUserAgent, cnv.cc, len, cleanup);
case FreeRDP_GatewayHttpReferer:
return update_string_copy_(&settings->GatewayHttpReferer, cnv.cc, len, cleanup);
case FreeRDP_GatewayHttpUserAgent:
return update_string_copy_(&settings->GatewayHttpUserAgent, cnv.cc, len, cleanup);
case FreeRDP_GatewayPassword:
return update_string_copy_(&settings->GatewayPassword, cnv.cc, len, cleanup);

View File

@@ -513,6 +513,10 @@ static const struct settings_str_entry settings_map[] = {
{ FreeRDP_GatewayHostname, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_GatewayHostname" },
{ FreeRDP_GatewayHttpExtAuthBearer, FREERDP_SETTINGS_TYPE_STRING,
"FreeRDP_GatewayHttpExtAuthBearer" },
{ FreeRDP_GatewayHttpMsUserAgent, FREERDP_SETTINGS_TYPE_STRING,
"FreeRDP_GatewayHttpMsUserAgent" },
{ FreeRDP_GatewayHttpReferer, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_GatewayHttpReferer" },
{ FreeRDP_GatewayHttpUserAgent, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_GatewayHttpUserAgent" },
{ FreeRDP_GatewayPassword, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_GatewayPassword" },
{ FreeRDP_GatewayUrl, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_GatewayUrl" },
{ FreeRDP_GatewayUsername, FREERDP_SETTINGS_TYPE_STRING, "FreeRDP_GatewayUsername" },

View File

@@ -186,8 +186,6 @@ static wStream* arm_build_http_request(rdpArm* arm, const char* method,
size_t content_length)
{
wStream* s = NULL;
HttpRequest* request = NULL;
const char* uri = NULL;
WINPR_ASSERT(arm);
WINPR_ASSERT(method);
@@ -196,16 +194,18 @@ static wStream* arm_build_http_request(rdpArm* arm, const char* method,
WINPR_ASSERT(arm->context);
WINPR_ASSERT(arm->context->rdp);
uri = http_context_get_uri(arm->http);
request = http_request_new();
const char* uri = http_context_get_uri(arm->http);
HttpRequest* request = http_request_new();
if (!request)
return NULL;
rdpSettings* settings = arm->context->settings;
if (!http_request_set_method(request, method) || !http_request_set_uri(request, uri))
goto out;
if (!freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpExtAuthBearer))
if (!freerdp_settings_get_string(settings, FreeRDP_GatewayHttpExtAuthBearer))
{
char* token = NULL;
@@ -225,8 +225,7 @@ static wStream* arm_build_http_request(rdpArm* arm, const char* method,
goto out;
}
if (!freerdp_settings_set_string(arm->context->settings, FreeRDP_GatewayHttpExtAuthBearer,
token))
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpExtAuthBearer, token))
{
free(token);
goto out;
@@ -236,8 +235,7 @@ static wStream* arm_build_http_request(rdpArm* arm, const char* method,
if (!http_request_set_auth_scheme(request, "Bearer") ||
!http_request_set_auth_param(
request,
freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpExtAuthBearer)))
request, freerdp_settings_get_string(settings, FreeRDP_GatewayHttpExtAuthBearer)))
goto out;
if (!http_request_set_transfer_encoding(request, transferEncoding) ||
@@ -246,6 +244,39 @@ static wStream* arm_build_http_request(rdpArm* arm, const char* method,
goto out;
s = http_request_write(arm->http, request);
if (!s)
goto out;
const char* referer = freerdp_settings_get_string(settings, FreeRDP_GatewayHttpReferer);
if (referer && (strlen(referer) > 0))
{
if (!http_request_append_header(s, "Referer", "%s", referer))
goto out;
}
const char* tenantId = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
if (!http_request_append_header(s, "x-ms-tenant-id", "%s", tenantId))
goto out;
const char* wvd = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdWvdEndpointPool);
if (!wvd)
goto out;
if (!http_request_append_header(s, "MS-WVD-Activity-Hint", "ms-wvd-hp:%s", wvd))
goto out;
const char* armpath = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdArmpath);
if (!armpath)
goto out;
char* value = crypto_base64url_encode((const BYTE*)armpath, strlen(armpath));
if (!value)
goto out;
const BOOL rc = http_request_append_header(s, "x-ms-opsarmpath64", "%s", value);
free(value);
if (!rc)
goto out;
out:
http_request_free(request);
@@ -1046,10 +1077,16 @@ static BOOL arm_handle_bad_request(rdpArm* arm, const HttpResponse* response, BO
BOOL rc = FALSE;
http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
const size_t len = http_response_get_body_length(response);
const char* msg = (const char*)http_response_get_body(response);
if (strnlen(msg, len + 1) > len)
if (msg && (strnlen(msg, len + 1) > len))
{
WLog_Print(arm->log, WLOG_ERROR, "Got HTTP Response data, but length is invalid");
return FALSE;
}
WLog_Print(arm->log, WLOG_DEBUG, "Got HTTP Response data: %s", msg);
@@ -1062,31 +1099,31 @@ static BOOL arm_handle_bad_request(rdpArm* arm, const HttpResponse* response, BO
return FALSE;
}
WINPR_JSON* gateway_code_obj = WINPR_JSON_GetObjectItemCaseSensitive(json, "Code");
const char* gw_code_str = WINPR_JSON_GetStringValue(gateway_code_obj);
if (gw_code_str == NULL)
{
WLog_Print(arm->log, WLOG_ERROR, "Response has no \"Code\" property");
http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
goto fail;
}
if (strcmp(gw_code_str, "E_PROXY_ORCHESTRATION_LB_SESSIONHOST_DEALLOCATED") == 0)
{
*retry = TRUE;
WINPR_JSON* message = WINPR_JSON_GetObjectItemCaseSensitive(json, "Message");
const char* msgstr = WINPR_JSON_GetStringValue(message);
if (!msgstr)
WLog_WARN(TAG, "Starting your VM. It may take up to 5 minutes");
else
WLog_WARN(TAG, "%s", msgstr);
freerdp_set_last_error_if_not(arm->context, FREERDP_ERROR_CONNECT_TARGET_BOOTING);
}
else
{
http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
goto fail;
WINPR_JSON* gateway_code_obj = WINPR_JSON_GetObjectItemCaseSensitive(json, "Code");
const char* gw_code_str = WINPR_JSON_GetStringValue(gateway_code_obj);
if (gw_code_str == NULL)
{
WLog_Print(arm->log, WLOG_ERROR, "Response has no \"Code\" property");
goto fail;
}
if (strcmp(gw_code_str, "E_PROXY_ORCHESTRATION_LB_SESSIONHOST_DEALLOCATED") == 0)
{
*retry = TRUE;
WINPR_JSON* message = WINPR_JSON_GetObjectItemCaseSensitive(json, "Message");
const char* msgstr = WINPR_JSON_GetStringValue(message);
if (!msgstr)
WLog_WARN(TAG, "Starting your VM. It may take up to 5 minutes");
else
WLog_WARN(TAG, "%s", msgstr);
freerdp_set_last_error_if_not(arm->context, FREERDP_ERROR_CONNECT_TARGET_BOOTING);
}
else
{
goto fail;
}
}
rc = TRUE;
@@ -1113,13 +1150,17 @@ static BOOL arm_handle_request(rdpArm* arm, BOOL* retry, DWORD timeout)
HttpResponse* response = NULL;
long StatusCode = 0;
if (!http_context_set_uri(arm->http, "/api/arm/v2/connections/") ||
const char* useragent =
freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpUserAgent);
const char* msuseragent =
freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpMsUserAgent);
if (!http_context_set_uri(arm->http, "/api/arm/v2/connections") ||
!http_context_set_accept(arm->http, "application/json") ||
!http_context_set_cache_control(arm->http, "no-cache") ||
!http_context_set_pragma(arm->http, "no-cache") ||
!http_context_set_connection(arm->http, "Keep-Alive") ||
!http_context_set_user_agent(arm->http, FREERDP_USER_AGENT) ||
!http_context_set_x_ms_user_agent(arm->http, FREERDP_USER_AGENT) ||
!http_context_set_user_agent(arm->http, useragent) ||
!http_context_set_x_ms_user_agent(arm->http, msuseragent) ||
!http_context_set_host(arm->http, freerdp_settings_get_string(arm->context->settings,
FreeRDP_GatewayHostname)))
goto arm_error;

View File

@@ -1486,7 +1486,7 @@ HttpResponse* http_response_recv(rdpTls* tls, BOOL readContentLength)
WINPR_ASSERT(response->BodyLength == 0);
bodyLength = response->BodyLength; /* expected body length */
if (readContentLength)
if (readContentLength && (response->BodyLength > 0))
{
const char* cur = response->ContentType;
@@ -1737,3 +1737,18 @@ void http_response_log_error_status_(wLog* log, DWORD level, const HttpResponse*
freerdp_http_status_string_format(status, buffer, ARRAYSIZE(buffer)));
http_response_print(log, level, response, file, line, fkt);
}
WINPR_ATTR_FORMAT_ARG(3, 4)
BOOL http_request_append_header(wStream* stream, const char* param,
WINPR_FORMAT_ARG const char* value, ...)
{
va_list ap;
va_start(ap, value);
char* str = NULL;
size_t slen = 0;
winpr_vasprintf(&str, &slen, value, ap);
va_end(ap);
const BOOL rc = http_encode_body_line(stream, param, str);
free(str);
return rc;
}

View File

@@ -104,6 +104,10 @@ FREERDP_LOCAL BOOL http_request_set_transfer_encoding(HttpRequest* request,
FREERDP_LOCAL wStream* http_request_write(HttpContext* context, HttpRequest* request);
WINPR_ATTR_FORMAT_ARG(3, 4)
FREERDP_LOCAL BOOL http_request_append_header(wStream* stream, const char* param,
WINPR_FORMAT_ARG const char* value, ...);
/* HTTP response */
typedef struct s_http_response HttpResponse;

View File

@@ -68,7 +68,7 @@ struct rdp_wst
websocket_context* wscontext;
};
static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=FreeRDP%%2F3.0";
static const char arm_query_param[] = "%s%cClmTk=Bearer%%20%s&X-MS-User-Agent=%s";
static BOOL wst_get_gateway_credentials(rdpContext* context, rdp_auth_reason reason)
{
@@ -356,10 +356,16 @@ static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, D
/* AVD returns a 403 response with a ARRAffinity cookie set. retry with that cookie */
const char* affinity = http_response_get_setcookie(*ppresponse, "ARRAffinity");
if (affinity && freerdp_settings_get_bool(wst->context->settings, FreeRDP_GatewayArmTransport))
const char* samesite = http_response_get_setcookie(*ppresponse, "ARRAffinitySameSite");
if ((affinity || samesite) &&
freerdp_settings_get_bool(wst->context->settings, FreeRDP_GatewayArmTransport))
{
WLog_DBG(TAG, "Got Affinity cookie %s", affinity);
http_context_set_cookie(wst->http, "ARRAffinity", affinity);
WLog_INFO(TAG, "Got ARRAffinity cookie %s", affinity);
WLog_INFO(TAG, "Got ARRAffinitySameSite cookie %s", samesite);
if (affinity)
http_context_set_cookie(wst->http, "ARRAffinity", affinity);
if (samesite)
http_context_set_cookie(wst->http, "ARRAffinitySameSite", samesite);
http_response_free(*ppresponse);
*ppresponse = NULL;
/* Terminate this connection and make a new one with the Loadbalancing Cookie */
@@ -378,9 +384,12 @@ static BOOL wst_handle_ok_or_forbidden(rdpWst* wst, HttpResponse** ppresponse, D
char* urlWithAuth = NULL;
size_t urlLen = 0;
char firstParam = (strchr(wst->gwpath, '?') != NULL) ? '&' : '?';
winpr_asprintf(&urlWithAuth, &urlLen, arm_query_param, wst->gwpath, firstParam,
freerdp_settings_get_string(wst->context->settings,
FreeRDP_GatewayHttpExtAuthBearer));
const char* bearer = freerdp_settings_get_string(wst->context->settings,
FreeRDP_GatewayHttpExtAuthBearer);
const char* ua =
freerdp_settings_get_string(wst->context->settings, FreeRDP_GatewayHttpMsUserAgent);
winpr_asprintf(&urlWithAuth, &urlLen, arm_query_param, wst->gwpath, firstParam, bearer,
ua);
if (!urlWithAuth)
return FALSE;
free(wst->gwpath);
@@ -838,13 +847,17 @@ rdpWst* wst_new(rdpContext* context)
if (!wst->http)
goto wst_alloc_error;
const char* useragent =
freerdp_settings_get_string(context->settings, FreeRDP_GatewayHttpUserAgent);
const char* msuseragent =
freerdp_settings_get_string(context->settings, FreeRDP_GatewayHttpMsUserAgent);
if (!http_context_set_uri(wst->http, wst->gwpath) ||
!http_context_set_accept(wst->http, "*/*") ||
!http_context_set_cache_control(wst->http, "no-cache") ||
!http_context_set_pragma(wst->http, "no-cache") ||
!http_context_set_connection(wst->http, "Keep-Alive") ||
!http_context_set_user_agent(wst->http, FREERDP_USER_AGENT) ||
!http_context_set_x_ms_user_agent(wst->http, FREERDP_USER_AGENT) ||
!http_context_set_user_agent(wst->http, useragent) ||
!http_context_set_x_ms_user_agent(wst->http, msuseragent) ||
!http_context_set_host(wst->http, wst->gwhostname) ||
!http_context_enable_websocket_upgrade(wst->http, TRUE))
{

View File

@@ -34,6 +34,7 @@
#include <winpr/registry.h>
#include <winpr/wtsapi.h>
#include <freerdp/version.h>
#include <freerdp/settings.h>
#include <freerdp/utils/helpers.h>
#include <freerdp/build-config.h>
@@ -770,6 +771,15 @@ rdpSettings* freerdp_settings_new(DWORD flags)
if (!server && !remote)
{
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpUserAgent,
FREERDP_USER_AGENT))
goto out_fail;
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpMsUserAgent,
FREERDP_USER_AGENT))
goto out_fail;
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpReferer, ""))
goto out_fail;
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdAccessTokenFormat,
"ms-appx-web%%3a%%2f%%2fMicrosoft.AAD.BrokerPlugin%%2f%s"))
goto out_fail;
@@ -777,7 +787,8 @@ rdpSettings* freerdp_settings_new(DWORD flags)
"https%%3A%%2F%%2F%s%%2F%s%%2Foauth2%%2Fnativeclient"))
goto out_fail;
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdScope,
"https%3A%2F%2Fwww.wvd.microsoft.com%2F.default"))
"https%3A%2F%2Fwww.wvd.microsoft.com%2F.default%20openid%"
"20profile%20offline_access"))
goto out_fail;
if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAvdClientID,

View File

@@ -405,6 +405,9 @@ static const size_t string_list_indices[] = {
FreeRDP_GatewayDomain,
FreeRDP_GatewayHostname,
FreeRDP_GatewayHttpExtAuthBearer,
FreeRDP_GatewayHttpMsUserAgent,
FreeRDP_GatewayHttpReferer,
FreeRDP_GatewayHttpUserAgent,
FreeRDP_GatewayPassword,
FreeRDP_GatewayUrl,
FreeRDP_GatewayUsername,

View File

@@ -569,6 +569,17 @@ char* x509_utils_get_issuer(const X509* xcert)
return issuer;
}
static int asn1_object_cmp(const ASN1_OBJECT* const* a, const ASN1_OBJECT* const* b)
{
if (!a || !b)
return (a == b) ? 0 : (a ? 1 : -1);
if (!*a || !*b)
return (*a == *b) ? 0 : (*a ? 1 : -1);
return OBJ_cmp(*a, *b);
}
BOOL x509_utils_check_eku(const X509* xcert, int nid)
{
BOOL ret = FALSE;
@@ -586,6 +597,7 @@ BOOL x509_utils_check_eku(const X509* xcert, int nid)
if (!oid_stack)
return FALSE;
sk_ASN1_OBJECT_set_cmp_func(oid_stack, asn1_object_cmp);
if (sk_ASN1_OBJECT_find(oid_stack, oid) >= 0)
ret = TRUE;

View File

@@ -346,6 +346,16 @@ extern "C"
WINPR_API WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name,
double number);
/**
* @brief WINPR_JSON_AddIntegerToObject
* @param object The JSON object the new item is added to
* @param name The name of the object
* @return the new JSON item added
* @since version 3.6.0
*/
WINPR_API WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name,
int64_t number);
/**
* @brief WINPR_JSON_AddStringToObject
* @param object The JSON object the new item is added to

View File

@@ -40,6 +40,7 @@ extern "C"
WINPR_API void winpr_CArrayDump(const char* tag, UINT32 level, const void* data, size_t length,
size_t width);
WINPR_ATTR_MALLOC(free, 1)
WINPR_API char* winpr_BinToHexString(const BYTE* data, size_t length, BOOL space);
WINPR_API size_t winpr_BinToHexStringBuffer(const BYTE* data, size_t length, char* dstStr,
size_t dstSize, BOOL space);

View File

@@ -259,6 +259,13 @@ WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, d
return cJSON_AddNumberToObject((cJSON*)object, name, number);
}
WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name, int64_t number)
{
char str[64] = { 0 };
(void)_snprintf(str, sizeof(str), "%" PRId64, number);
return cJSON_AddRawToObject((cJSON*)object, name, str);
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
return cJSON_AddStringToObject((cJSON*)object, name, string);

View File

@@ -298,6 +298,12 @@ WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, d
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name, int64_t number)
{
json_t* obj = json_integer(number);
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
json_t* obj = json_string(string);

View File

@@ -274,6 +274,17 @@ WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, d
return obj;
}
WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name, int64_t number)
{
struct json_object* obj = json_object_new_int64(number);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return NULL;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
struct json_object* obj = json_object_new_string(string);