21#include <freerdp/config.h>
22#include <freerdp/version.h>
24#include "../settings.h"
26#include <winpr/assert.h>
29#include <winpr/synch.h>
30#include <winpr/print.h>
31#include <winpr/stream.h>
32#include <winpr/winsock.h>
33#include <winpr/cred.h>
34#include <winpr/bcrypt.h>
36#include <freerdp/log.h>
37#include <freerdp/error.h>
38#include <freerdp/crypto/certificate.h>
39#include <freerdp/utils/ringbuffer.h>
40#include <freerdp/utils/smartcardlogon.h>
41#include <freerdp/utils/aad.h>
47#include "../credssp_auth.h"
50#include "../../crypto/crypto.h"
51#include "../../crypto/certificate.h"
52#include "../../crypto/opensslcompat.h"
55#include "../redirection.h"
57#include <winpr/json.h>
71typedef struct rdp_arm rdpArm;
73#define TAG FREERDP_TAG("core.gateway.arm")
78#define ARM_MAX_TARGET_ADDRESSES 1024
81static BOOL arm_tls_connect(rdpArm* arm, rdpTls* tls, UINT32 timeout)
86 rdpSettings* settings = arm->context->settings;
95 if (peerPort > UINT16_MAX)
98 tls->hostname = peerHostname;
99 tls->port = (UINT16)MIN(UINT16_MAX, peerPort);
100 tls->isGatewayTransport = TRUE;
101 return utils_tls_connect(arm->context, arm->tls, arm->log, peerHostname, tls->port, FALSE,
106static BOOL arm_fetch_wellknown(rdpArm* arm)
109 WINPR_ASSERT(arm->context);
110 WINPR_ASSERT(arm->context->rdp);
112 rdpRdp* rdp = arm->context->rdp;
118 const BOOL useTenant =
120 const char* tenantid =
"common";
126 freerdp_utils_aad_get_wellknown_context(arm->context, arm->log, base, tenantid);
127 return (rdp->wellknown !=
nullptr);
130WINPR_ATTR_MALLOC(Stream_Free, 1)
131static
wStream* arm_build_http_request(rdpArm* arm, const
char* method,
132 TRANSFER_ENCODING transferEncoding, const
char* content_type,
133 size_t content_length)
138 WINPR_ASSERT(method);
139 WINPR_ASSERT(content_type);
141 WINPR_ASSERT(arm->context);
142 WINPR_ASSERT(arm->context->rdp);
144 const char* uri = http_context_get_uri(arm->http);
145 HttpRequest* request = http_request_new();
150 rdpSettings* settings = arm->context->settings;
152 if (!http_request_set_method(request, method) || !http_request_set_uri(request, uri))
157 pGetCommonAccessToken GetCommonAccessToken = freerdp_get_common_access_token(arm->context);
158 if (!GetCommonAccessToken)
160 WLog_Print(arm->log, WLOG_ERROR,
"No authorization token provided");
164 if (!arm_fetch_wellknown(arm))
167 char* token =
nullptr;
168 if (!GetCommonAccessToken(arm->context, ACCESS_TOKEN_TYPE_AVD, &token, 0))
171 WLog_Print(arm->log, WLOG_ERROR,
"Unable to obtain access token");
182 if (!http_request_set_auth_scheme(request,
"Bearer") ||
183 !http_request_set_auth_param(
187 if (!http_request_set_transfer_encoding(request, transferEncoding) ||
188 !http_request_set_content_length(request, content_length) ||
189 !http_request_set_content_type(request, content_type))
192 s = http_request_write(arm->http, request);
194 http_request_free(request);
197 Stream_SealLength(s);
203static BOOL arm_send_http_request(rdpArm* arm, rdpTls* tls,
const char* method,
204 const char* content_type,
const char* data,
size_t content_length)
208 arm_build_http_request(arm, method, TransferEncodingIdentity, content_type, content_length);
213 const size_t sz = Stream_Length(s);
214 WLog_Print(arm->log, WLOG_TRACE,
"header [%" PRIuz
"]: %s", sz, Stream_Buffer(s));
215 WLog_Print(arm->log, WLOG_TRACE,
"body [%" PRIuz
"]: %s", content_length, data);
216 status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz);
218 Stream_Free(s, TRUE);
219 if (status >= 0 && content_length > 0 && data)
220 status = freerdp_tls_write_all(tls, (
const BYTE*)data, content_length);
222 return (status >= 0);
225static void arm_free(rdpArm* arm)
230 freerdp_tls_free(arm->tls);
231 http_context_free(arm->http);
236WINPR_ATTR_MALLOC(arm_free, 1)
237static rdpArm* arm_new(rdpContext* context)
239 WINPR_ASSERT(context);
241 rdpArm* arm = (rdpArm*)calloc(1,
sizeof(rdpArm));
245 arm->log = WLog_Get(TAG);
246 arm->context = context;
247 arm->tls = freerdp_tls_new(context);
251 arm->http = http_context_new();
263WINPR_ATTR_MALLOC(arm_free, 1)
264static
char* arm_create_request_json(rdpArm* arm,
size_t* pLen)
270 char* message =
nullptr;
277 goto arm_create_cleanup;
281 goto arm_create_cleanup;
285 lbi = calloc(len + 1ull,
sizeof(
char));
287 goto arm_create_cleanup;
293 goto arm_create_cleanup;
295 goto arm_create_cleanup;
297 goto arm_create_cleanup;
303 winpr_znfree(lbi, lbiLen);
305 *pLen = strlen(message);
323WINPR_ATTR_MALLOC(winpr_Cipher_Free, 1)
324static WINPR_CIPHER_CTX* treatAuthBlob(wLog* log, const BYTE* pbInput,
size_t cbInput,
327 WINPR_CIPHER_CTX* ret =
nullptr;
328 char algoName[100] = WINPR_C_ARRAY_INIT;
330 WINPR_ASSERT(pBlockSize);
331 SSIZE_T algoSz = ConvertWCharNToUtf8(WINPR_PACKED_ALIGN_CAST(
const WCHAR*, pbInput),
332 cbInput /
sizeof(WCHAR), algoName,
sizeof(algoName) - 1);
335 WLog_Print(log, WLOG_ERROR,
"invalid algoName");
339 if (strcmp(algoName,
"AES") != 0)
341 WLog_Print(log, WLOG_ERROR,
"only AES is supported for now");
345 *pBlockSize = WINPR_AES_BLOCK_SIZE;
346 const size_t algoLen = WINPR_ASSERTING_INT_CAST(
size_t, (algoSz + 1)) *
sizeof(WCHAR);
347 if (cbInput < algoLen)
349 WLog_Print(log, WLOG_ERROR,
"invalid AuthBlob size");
356 wStream staticStream = WINPR_C_ARRAY_INIT;
357 wStream* s = Stream_StaticConstInit(&staticStream, &pbInput[algoLen], cbInput);
359 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 12))
362 const UINT32 dwMagic = Stream_Get_UINT32(s);
363 if (dwMagic != BCRYPT_KEY_DATA_BLOB_MAGIC)
365 WLog_Print(log, WLOG_ERROR,
"unsupported authBlob type");
369 const UINT32 dwVersion = Stream_Get_UINT32(s);
370 if (dwVersion != BCRYPT_KEY_DATA_BLOB_VERSION1)
372 WLog_Print(log, WLOG_ERROR,
"unsupported authBlob version %" PRIu32
", expecting %d",
373 dwVersion, BCRYPT_KEY_DATA_BLOB_VERSION1);
377 const UINT32 cbKeyData = Stream_Get_UINT32(s);
378 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, cbKeyData))
380 WLog_Print(log, WLOG_ERROR,
"invalid authBlob size");
384 WINPR_CIPHER_TYPE cipherType = WINPR_CIPHER_NONE;
388 cipherType = WINPR_CIPHER_AES_128_CBC;
391 cipherType = WINPR_CIPHER_AES_192_CBC;
394 cipherType = WINPR_CIPHER_AES_256_CBC;
397 WLog_Print(log, WLOG_ERROR,
"invalid authBlob cipher size");
401 ret = winpr_Cipher_NewEx(cipherType, WINPR_ENCRYPT, Stream_Pointer(s), cbKeyData,
nullptr, 0);
404 WLog_Print(log, WLOG_ERROR,
"error creating cipher");
408 if (!winpr_Cipher_SetPadding(ret, TRUE))
410 WLog_Print(log, WLOG_ERROR,
"unable to enable padding on cipher");
411 winpr_Cipher_Free(ret);
419static BOOL arm_stringEncodeW(
const BYTE* pin,
size_t cbIn, BYTE** ppOut,
size_t* pcbOut)
426 char* b64encoded = crypto_base64_encode_ex_len(pin, cbIn, TRUE, &b64len);
432 *ppOut = (BYTE*)ConvertUtf8NToWCharAlloc(b64encoded, b64len, &outSz);
438 *pcbOut = (outSz + 1) *
sizeof(WCHAR);
443static BOOL arm_encodeRedirectPasswd(wLog* log, rdpSettings* settings,
const rdpCertificate* cert,
444 WINPR_CIPHER_CTX* cipher,
size_t blockSize)
447 BYTE* output =
nullptr;
448 BYTE* finalOutput =
nullptr;
454 size_t wpasswdLen = 0;
458 WLog_Print(log, WLOG_ERROR,
"error when converting password to UTF16");
462 const size_t wpasswdBytes = (wpasswdLen + 1) *
sizeof(WCHAR);
463 BYTE* encryptedPass = calloc(1, wpasswdBytes + blockSize);
469 size_t encryptedPassLen = 0;
470 if (!winpr_Cipher_Update(cipher, wpasswd, wpasswdBytes, encryptedPass, &encryptedPassLen))
473 if (encryptedPassLen > wpasswdBytes)
478 if (!winpr_Cipher_Final(cipher, &encryptedPass[encryptedPassLen], &finalLen))
480 WLog_Print(log, WLOG_ERROR,
"error when ciphering password");
483 encryptedPassLen += finalLen;
488 size_t output_length = 0;
489 if (!freerdp_certificate_publickey_encrypt(cert, encryptedPass, encryptedPassLen,
490 &output, &output_length))
492 WLog_Print(log, WLOG_ERROR,
"unable to encrypt with the server's public key");
497 size_t finalOutputLen = 0;
498 if (!arm_stringEncodeW(output, output_length, &finalOutput, &finalOutputLen))
500 WLog_Print(log, WLOG_ERROR,
"unable to base64+utf16 final blob");
505 finalOutput, finalOutputLen))
507 WLog_Print(log, WLOG_ERROR,
508 "unable to set the redirection password in settings");
528 LB_PASSWORD_IS_PK_ENCRYPTED))
544static BOOL arm_pick_base64Utf16Field(wLog* log,
const WINPR_JSON* json,
const char* name,
545 BYTE** poutput,
size_t* plen)
558 BYTE* output1 =
nullptr;
560 crypto_base64_decode(nodeValue, strlen(nodeValue), &output1, &len1);
561 if (!output1 || !len1)
563 WLog_Print(log, WLOG_ERROR,
"error when first unbase64 for %s", name);
569 char* output2 = ConvertWCharNToUtf8Alloc(WINPR_PACKED_ALIGN_CAST(WCHAR*, output1),
570 len1 /
sizeof(WCHAR), &len2);
572 if (!output2 || !len2)
574 WLog_Print(log, WLOG_ERROR,
"error when decode('utf-16') for %s", name);
579 BYTE* output =
nullptr;
580 crypto_base64_decode(output2, len2, &output, plen);
582 if (!output || !*plen)
584 WLog_Print(log, WLOG_ERROR,
"error when second unbase64 for %s", name);
613static size_t arm_parse_ipvx_count(WINPR_JSON* ipvX)
625static BOOL arm_parse_ipv6(rdpSettings* settings, WINPR_JSON* ipv6,
size_t* pAddressIdx)
627 WINPR_ASSERT(settings);
629 WINPR_ASSERT(pAddressIdx);
639 for (
size_t j = 0; j < naddresses; j++)
646 if (utils_str_is_empty(addr))
649 if (*pAddressIdx >= count)
651 WLog_ERR(TAG,
"Exceeded TargetNetAddresses, parsing failed");
655 if (!utils_is_valid_ip(addr) && !winpr_str_is_valid_hostname(addr))
657 WLog_ERR(TAG,
"TargetNetAddress[%" PRIuz
"]: %s not an FQDN or IP", j, addr);
661 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
662 (*pAddressIdx)++, addr))
669static BOOL arm_parse_ipv4(rdpSettings* settings, WINPR_JSON* ipv4,
size_t* pAddressIdx)
671 WINPR_ASSERT(settings);
673 WINPR_ASSERT(pAddressIdx);
681 for (
size_t j = 0; j < naddresses; j++)
687 WINPR_JSON* publicIpNode =
692 if (!utils_str_is_empty(publicIp))
694 if (*pAddressIdx >= count)
696 WLog_ERR(TAG,
"Exceeded TargetNetAddresses, parsing failed");
700 if (!utils_is_valid_ip(publicIp))
702 WLog_ERR(TAG,
"publicIpAddress value %s: not a valid IP", publicIp);
706 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
707 (*pAddressIdx)++, publicIp))
712 WINPR_JSON* privateIpNode =
717 if (!utils_str_is_empty(privateIp))
719 if (*pAddressIdx >= count)
721 WLog_ERR(TAG,
"Exceeded TargetNetAddresses, parsing failed");
724 if (!utils_is_valid_ip(privateIp))
726 WLog_ERR(TAG,
"privateIpAddress value %s: not a valid IP", privateIp);
730 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
731 (*pAddressIdx)++, privateIp))
740static BOOL arm_treat_azureInstanceNetworkMetadata(wLog* log,
const char* metadata,
741 rdpSettings* settings)
745 WINPR_ASSERT(settings);
747 if (!freerdp_target_net_adresses_reset(settings, 0))
753 WLog_Print(log, WLOG_ERROR,
"invalid azureInstanceNetworkMetadata");
766 WLog_Print(log, WLOG_ERROR,
"expecting interface to be an Array");
772 if (interfaceSz == 0)
774 WLog_WARN(TAG,
"no addresses in azure instance metadata");
779 if (interfaceSz > ARM_MAX_TARGET_ADDRESSES)
781 WLog_Print(log, WLOG_ERROR,
"too many interfaces in azure instance metadata: %" PRIuz,
788 for (
size_t i = 0; i < interfaceSz; i++)
796 count += arm_parse_ipvx_count(ipv6);
800 count += arm_parse_ipvx_count(ipv4);
803 if (count > ARM_MAX_TARGET_ADDRESSES)
805 WLog_Print(log, WLOG_ERROR,
806 "too many addresses in azure instance metadata: %" PRIuz, count);
810 if (!freerdp_target_net_adresses_reset(settings, count))
815 size_t addressIdx = 0;
816 for (
size_t i = 0; i < interfaceSz; i++)
825 if (!arm_parse_ipv6(settings, ipv6, &addressIdx))
832 if (!arm_parse_ipv4(settings, ipv4, &addressIdx))
836 if (addressIdx > UINT32_MAX)
843 ret = addressIdx > 0;
853static BOOL arm_fill_rdstls(rdpArm* arm, rdpSettings* settings,
const WINPR_JSON* json,
854 const rdpCertificate* redirectedServerCert)
857 WINPR_ASSERT(settings);
860 if (!redirectedServerCert)
864 BYTE* authBlob =
nullptr;
865 WCHAR* wGUID =
nullptr;
893 WINPR_ASSERT(arm->context);
894 WINPR_ASSERT(arm->context->instance);
896 char* username =
nullptr;
897 char* password =
nullptr;
898 char* domain =
nullptr;
901 domain = _strdup(ddomain);
903 username = _strdup(duser);
906 IFCALLRESULT(FALSE, arm->context->instance->AuthenticateEx, arm->context->instance,
907 &username, &password, &domain, AUTH_RDSTLS);
912 winpr_zfree(username);
913 winpr_zfree(password);
915 if (!rc || !rc1 || !rc2 || !rc3)
922 WINPR_JSON* redirectedAuthGuidNode =
929 if (!redirectedAuthGuid)
933 size_t wGUID_len = 0;
934 wGUID = ConvertUtf8ToWCharAlloc(redirectedAuthGuid, &wGUID_len);
935 if (!wGUID || (wGUID_len == 0))
937 WLog_Print(arm->log, WLOG_ERROR,
938 "unable to allocate space for redirectedAuthGuid");
944 settings, FreeRDP_RedirectionGuid, wGUID, (wGUID_len + 1) *
sizeof(WCHAR));
948 WLog_Print(arm->log, WLOG_ERROR,
"unable to set RedirectionGuid");
958 size_t authBlobLen = 0;
959 if (!arm_pick_base64Utf16Field(arm->log, json,
"redirectedAuthBlob", &authBlob,
964 size_t blockSize = 0;
965 WINPR_CIPHER_CTX* cipher = treatAuthBlob(arm->log, authBlob, authBlobLen, &blockSize);
970 const BOOL rerp = arm_encodeRedirectPasswd(arm->log, settings, redirectedServerCert,
972 winpr_Cipher_Free(cipher);
988static BOOL arm_fill_gateway_parameters(rdpArm* arm,
const char* message,
size_t len)
991 WINPR_ASSERT(arm->context);
992 WINPR_ASSERT(message);
994 rdpCertificate* redirectedServerCert =
nullptr;
999 WLog_Print(arm->log, WLOG_ERROR,
"Response data is not valid JSON: %s",
1004 if (WLog_IsLevelActive(arm->log, WLOG_DEBUG))
1007 WLog_Print(arm->log, WLOG_DEBUG,
"Got HTTP Response data: %s", str);
1011 rdpSettings* settings = arm->context->settings;
1016 if (gwurlstr !=
nullptr)
1018 WLog_Print(arm->log, WLOG_DEBUG,
"extracted target url %s", gwurlstr);
1024 WINPR_JSON* serverNameNode =
1038 const char key[] =
"redirectedUsername";
1041 const char* userName =
nullptr;
1051 WINPR_JSON* azureMeta =
1055 if (!arm_treat_azureInstanceNetworkMetadata(
1058 WLog_Print(arm->log, WLOG_ERROR,
1059 "error when treating azureInstanceNetworkMetadata");
1068 BYTE* cert =
nullptr;
1069 if (arm_pick_base64Utf16Field(arm->log, json,
"redirectedServerCert", &cert, &certLen))
1071 const BOOL rc = rdp_redirection_read_target_cert(&redirectedServerCert, cert, certLen);
1075 else if (!rdp_set_target_certificate(settings, redirectedServerCert))
1083 status = arm_fill_rdstls(arm, settings, json, redirectedServerCert);
1087 freerdp_certificate_free(redirectedServerCert);
1092static BOOL arm_handle_request_ok(rdpArm* arm,
const HttpResponse* response)
1094 const size_t len = http_response_get_body_length(response);
1095 const char* msg = http_response_get_body(response);
1096 if ((len == 0) || !msg)
1098 WLog_Print(arm->log, WLOG_ERROR,
"Got HTTP Response data with empty body");
1101 const size_t alen = strnlen(msg, len + 1);
1104 WLog_Print(arm->log, WLOG_ERROR,
"Got HTTP Response data with invalid termination");
1108 return arm_fill_gateway_parameters(arm, msg, len);
1112static BOOL arm_handle_bad_request(rdpArm* arm,
const HttpResponse* response, BOOL* retry)
1114 WINPR_ASSERT(response);
1115 WINPR_ASSERT(retry);
1121 http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
1123 const size_t len = http_response_get_body_length(response);
1124 const char* msg = http_response_get_body(response);
1125 if (msg && (strnlen(msg, len + 1) > len))
1127 WLog_Print(arm->log, WLOG_ERROR,
"Got HTTP Response data, but length is invalid");
1132 WLog_Print(arm->log, WLOG_DEBUG,
"Got HTTP Response data: %s", msg);
1135 if (json ==
nullptr)
1138 WLog_Print(arm->log, WLOG_ERROR,
"WINPR_JSON_ParseWithLength: %s", error_ptr);
1146 if (gw_code_str ==
nullptr)
1148 WLog_Print(arm->log, WLOG_ERROR,
"Response has no \"Code\" property");
1152 if (strcmp(gw_code_str,
"E_PROXY_ORCHESTRATION_LB_SESSIONHOST_DEALLOCATED") == 0)
1158 WLog_WARN(TAG,
"Starting your VM. It may take up to 5 minutes");
1160 WLog_WARN(TAG,
"%s", msgstr);
1161 freerdp_set_last_error_if_not(arm->context, FREERDP_ERROR_CONNECT_TARGET_BOOTING);
1176static BOOL arm_handle_request(rdpArm* arm, BOOL* retry, DWORD timeout)
1178 WINPR_ASSERT(retry);
1180 if (!arm_fetch_wellknown(arm))
1188 size_t messageLen = 0;
1189 char* message =
nullptr;
1192 HttpResponse* response =
nullptr;
1193 long StatusCode = 0;
1195 const char* useragent =
1197 const char* msuseragent =
1199 if (!http_context_set_uri(arm->http,
"/api/arm/v2/connections") ||
1200 !http_context_set_accept(arm->http,
"*/*") ||
1201 !http_context_set_cache_control(arm->http,
"no-cache") ||
1202 !http_context_set_pragma(arm->http,
"no-cache") ||
1203 !http_context_set_connection(arm->http,
"Keep-Alive") ||
1204 !http_context_set_user_agent(arm->http, useragent) ||
1205 !http_context_set_x_ms_user_agent(arm->http, msuseragent) ||
1207 FreeRDP_GatewayHostname)))
1210 if (!arm_tls_connect(arm, arm->tls, timeout))
1213 message = arm_create_request_json(arm, &messageLen);
1217 if (!arm_send_http_request(arm, arm->tls,
"POST",
"application/json", message, messageLen))
1220 response = http_response_recv(arm->tls, TRUE);
1224 StatusCode = http_response_get_status_code(response);
1225 if (StatusCode == HTTP_STATUS_OK)
1227 if (!arm_handle_request_ok(arm, response))
1230 else if (StatusCode == HTTP_STATUS_BAD_REQUEST)
1232 if (!arm_handle_bad_request(arm, response, retry))
1237 http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
1243 http_response_free(response);
1244 winpr_znfree(message, messageLen);
1250BOOL arm_resolve_endpoint(wLog* log, rdpContext* context, DWORD timeout)
1253 WLog_Print(log, WLOG_ERROR,
"arm gateway support not compiled in");
1260 if (!context->settings)
1267 WLog_Print(log, WLOG_ERROR,
"loadBalanceInfo and RemoteApplicationProgram needed");
1271 rdpArm* arm = arm_new(context);
1281 freerdp* instance = context->instance;
1282 WINPR_ASSERT(instance);
1283 SSIZE_T delay = IFCALLRESULT(-1, instance->RetryDialog, instance,
"arm-transport",
1284 arm->gateway_retry, arm);
1285 arm->gateway_retry++;
1290 WLog_Print(arm->log, WLOG_DEBUG,
"Delay for %" PRIdz
"ms before next attempt",
1294 DWORD slp = (UINT32)delay;
1295 if (delay > UINT32_MAX)
1302 rc = arm_handle_request(arm, &retry, timeout);
1304 }
while (retry && rc);
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_CreateObject(void)
WINPR_JSON_CreateObject.
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON *object, const char *string)
Check if JSON has an object matching the name.
WINPR_ATTR_NODISCARD WINPR_API size_t WINPR_JSON_GetArraySize(const WINPR_JSON *array)
Get the number of arrayitems from an array.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON *object, const char *string)
Same as WINPR_JSON_GetObjectItem but with case sensitive matching.
WINPR_API WINPR_JSON * WINPR_JSON_ParseWithLength(const char *value, size_t buffer_length)
Parse a JSON string.
WINPR_ATTR_NODISCARD WINPR_API const char * WINPR_JSON_GetErrorPtr(void)
Return an error string.
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetArrayItem(const WINPR_JSON *array, size_t index)
Return a pointer to an item in the array.
WINPR_API char * WINPR_JSON_PrintUnformatted(WINPR_JSON *item)
Serialize a JSON instance to string without formatting for human readable formatted output see WINPR_...
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsArray(const WINPR_JSON *item)
Check if JSON item is of type Array.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddNullToObject(WINPR_JSON *object, const char *name)
WINPR_JSON_AddNullToObject.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddStringToObject(WINPR_JSON *object, const char *name, const char *string)
WINPR_JSON_AddStringToObject.
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
WINPR_ATTR_NODISCARD WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
WINPR_API WINPR_JSON * WINPR_JSON_Parse(const char *value)
Parse a '\0' terminated JSON string.
WINPR_ATTR_NODISCARD FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API WCHAR * freerdp_settings_get_string_as_utf16(const rdpSettings *settings, FreeRDP_Settings_Keys_String id, size_t *pCharLen)
Return an allocated UTF16 string.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.