24 #include <freerdp/config.h>
26 #include <freerdp/freerdp.h>
27 #include <freerdp/gdi/gdi.h>
28 #include <freerdp/client/cmdline.h>
30 #include <freerdp/server/proxy/proxy_log.h>
31 #include <freerdp/channels/drdynvc.h>
32 #include <freerdp/channels/encomsp.h>
33 #include <freerdp/channels/rdpdr.h>
34 #include <freerdp/channels/rdpsnd.h>
35 #include <freerdp/channels/cliprdr.h>
36 #include <freerdp/channels/channels.h>
38 #include "pf_client.h"
39 #include "pf_channel.h"
40 #include <freerdp/server/proxy/proxy_context.h>
41 #include "pf_update.h"
43 #include <freerdp/server/proxy/proxy_config.h>
44 #include "proxy_modules.h"
46 #include "channels/pf_channel_rdpdr.h"
47 #include "channels/pf_channel_smartcard.h"
49 #define TAG PROXY_TAG("client")
51 static void channel_data_free(
void* obj);
52 static BOOL proxy_server_reactivate(rdpContext* ps,
const rdpContext* pc)
57 if (!pf_context_copy_settings(ps->settings, pc->settings))
64 WINPR_ASSERT(ps->update);
65 if (!ps->update->DesktopResize(ps))
71 static void pf_client_on_error_info(
void* ctx,
const ErrorInfoEventArgs* e)
73 pClientContext* pc = (pClientContext*)ctx;
74 pServerContext* ps = NULL;
77 WINPR_ASSERT(pc->pdata);
82 if (e->code == ERRINFO_NONE)
85 PROXY_LOG_WARN(TAG, pc,
"received ErrorInfo PDU. code=0x%08" PRIu32
", message: %s", e->code,
86 freerdp_get_error_info_string(e->code));
89 freerdp_set_error_info(ps->context.rdp, e->code);
90 freerdp_send_error_info(ps->context.rdp);
93 static void pf_client_on_activated(
void* ctx,
const ActivatedEventArgs* e)
95 pClientContext* pc = (pClientContext*)ctx;
96 pServerContext* ps = NULL;
97 freerdp_peer* peer = NULL;
100 WINPR_ASSERT(pc->pdata);
105 peer = ps->context.peer;
107 WINPR_ASSERT(peer->context);
109 PROXY_LOG_INFO(TAG, pc,
"client activated, registering server input callbacks");
112 pf_server_register_input_callbacks(peer->context->input);
113 pf_server_register_update_callbacks(peer->context->update);
116 static BOOL pf_client_load_rdpsnd(pClientContext* pc)
118 rdpContext* context = (rdpContext*)pc;
119 pServerContext* ps = NULL;
120 const proxyConfig* config = NULL;
123 WINPR_ASSERT(pc->pdata);
126 config = pc->pdata->config;
127 WINPR_ASSERT(config);
133 if (!freerdp_static_channel_collection_find(context->settings, RDPSND_CHANNEL_NAME))
135 const char* params[2] = { RDPSND_CHANNEL_NAME,
"sys:fake" };
137 if (!freerdp_client_add_static_channel(context->settings, ARRAYSIZE(params), params))
144 static BOOL pf_client_use_peer_load_balance_info(pClientContext* pc)
146 pServerContext* ps = NULL;
147 rdpSettings* settings = NULL;
148 DWORD lb_info_len = 0;
149 const char* lb_info = NULL;
152 WINPR_ASSERT(pc->pdata);
155 settings = pc->context.settings;
156 WINPR_ASSERT(settings);
158 lb_info = freerdp_nego_get_routing_token(&ps->context, &lb_info_len);
166 static BOOL str_is_empty(
const char* str)
170 if (strlen(str) == 0)
175 static BOOL pf_client_use_proxy_smartcard_auth(
const rdpSettings* settings)
184 if (str_is_empty(key))
187 if (str_is_empty(cert))
193 static BOOL freerdp_client_load_static_channel_addin(rdpChannels* channels, rdpSettings* settings,
194 const char* name,
void* data)
196 PVIRTUALCHANNELENTRY entry = NULL;
197 PVIRTUALCHANNELENTRY lentry = freerdp_load_channel_addin_entry(
198 name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
199 PVIRTUALCHANNELENTRYEX entryEx = WINPR_FUNC_PTR_CAST(lentry, PVIRTUALCHANNELENTRYEX);
201 entry = freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
205 if (freerdp_channels_client_load_ex(channels, settings, entryEx, data) == 0)
207 WLog_INFO(TAG,
"loading channelEx %s", name);
213 if (freerdp_channels_client_load(channels, settings, entry, data) == 0)
215 WLog_INFO(TAG,
"loading channel %s", name);
223 static BOOL pf_client_pre_connect(freerdp* instance)
225 pClientContext* pc = NULL;
226 pServerContext* ps = NULL;
227 const proxyConfig* config = NULL;
228 rdpSettings* settings = NULL;
230 WINPR_ASSERT(instance);
231 pc = (pClientContext*)instance->context;
233 WINPR_ASSERT(pc->pdata);
236 WINPR_ASSERT(ps->pdata);
237 config = ps->pdata->config;
238 WINPR_ASSERT(config);
239 settings = instance->context->settings;
240 WINPR_ASSERT(settings);
253 ZeroMemory(OrderSupport, 32);
255 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, DRDYNVC_SVC_CHANNEL_NAME))
269 config->DeviceRedirection) ||
271 config->DisplayControl) ||
275 if (config->RemoteApp)
277 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RAIL_SVC_CHANNEL_NAME))
284 if (config->DeviceRedirection)
286 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RDPDR_SVC_CHANNEL_NAME))
297 config->DisplayControl))
300 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, ENCOMSP_SVC_CHANNEL_NAME))
306 if (config->Clipboard)
308 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, CLIPRDR_SVC_CHANNEL_NAME))
318 PubSub_SubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
319 PubSub_SubscribeActivated(instance->context->pubSub, pf_client_on_activated);
320 if (!pf_client_use_peer_load_balance_info(pc))
323 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_PRE_CONNECT, pc->pdata, pc);
334 static BOOL updateBackIdFn(
const void* key,
void* value,
void* arg)
336 pServerStaticChannelContext* current = (pServerStaticChannelContext*)value;
337 UpdateBackIdArgs* updateArgs = (UpdateBackIdArgs*)arg;
339 if (strcmp(updateArgs->name, current->channel_name) != 0)
342 current->back_channel_id = updateArgs->backId;
343 if (!HashTable_Insert(updateArgs->ps->channelsByBackId, ¤t->back_channel_id, current))
345 WLog_ERR(TAG,
"error inserting channel in channelsByBackId table");
350 static BOOL pf_client_update_back_id(pServerContext* ps,
const char* name, UINT32 backId)
352 UpdateBackIdArgs res = { ps, name, backId };
354 return HashTable_Foreach(ps->channelsByFrontId, updateBackIdFn, &res) == FALSE;
357 static BOOL pf_client_load_channels(freerdp* instance)
359 pClientContext* pc = NULL;
360 pServerContext* ps = NULL;
361 const proxyConfig* config = NULL;
362 rdpSettings* settings = NULL;
364 WINPR_ASSERT(instance);
365 pc = (pClientContext*)instance->context;
367 WINPR_ASSERT(pc->pdata);
370 WINPR_ASSERT(ps->pdata);
371 config = ps->pdata->config;
372 WINPR_ASSERT(config);
373 settings = instance->context->settings;
374 WINPR_ASSERT(settings);
379 PROXY_LOG_INFO(TAG, pc,
"Loading addins");
381 if (!pf_client_load_rdpsnd(pc))
383 PROXY_LOG_ERR(TAG, pc,
"Failed to load rdpsnd client");
387 if (!pf_utils_is_passthrough(config))
389 if (!freerdp_client_load_addins(instance->context->channels, settings))
391 PROXY_LOG_ERR(TAG, pc,
"Failed to load addins");
397 if (!pf_channel_rdpdr_client_new(pc))
399 #if defined(WITH_PROXY_EMULATE_SMARTCARD)
400 if (!pf_channel_smartcard_client_new(pc))
404 if (!freerdp_channels_from_mcs(settings, &ps->context))
410 settings, FreeRDP_ChannelDefArray, 0);
412 UINT32
id = MCS_GLOBAL_CHANNEL_ID + 1;
414 WINPR_ASSERT(channels || (size == 0));
422 dev.channel_name = cur->name;
423 dev.flags = cur->options;
426 if (!pf_modules_run_filter(pc->pdata->module,
427 FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE, pc->pdata,
430 const size_t s = size - MIN(size, x + 1);
436 if (!pf_client_update_back_id(ps, cur->name,
id++))
438 WLog_ERR(TAG,
"unable to update backid for channel %s", cur->name);
449 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOAD_CHANNELS, pc->pdata, pc);
452 static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channelId,
453 const BYTE* xdata,
size_t xsize, UINT32 flags,
456 pClientContext* pc = NULL;
457 pServerContext* ps = NULL;
458 proxyData* pdata = NULL;
459 pServerStaticChannelContext* channel = NULL;
460 UINT64 channelId64 = channelId;
462 WINPR_ASSERT(instance);
463 WINPR_ASSERT(xdata || (xsize == 0));
465 pc = (pClientContext*)instance->context;
467 WINPR_ASSERT(pc->pdata);
475 channel = HashTable_GetItemValue(ps->channelsByBackId, &channelId64);
479 WINPR_ASSERT(channel->onBackData);
480 switch (channel->onBackData(pdata, channel, xdata, xsize, flags, totalSize))
482 case PF_CHANNEL_RESULT_PASS:
486 if (channel->front_channel_id == 0)
489 return ps->context.peer->SendChannelPacket(ps->context.peer, channel->front_channel_id,
490 totalSize, flags, xdata, xsize);
491 case PF_CHANNEL_RESULT_DROP:
493 case PF_CHANNEL_RESULT_ERROR:
499 static BOOL pf_client_on_server_heartbeat(freerdp* instance, BYTE period, BYTE count1, BYTE count2)
501 pClientContext* pc = NULL;
502 pServerContext* ps = NULL;
504 WINPR_ASSERT(instance);
505 pc = (pClientContext*)instance->context;
507 WINPR_ASSERT(pc->pdata);
511 return freerdp_heartbeat_send_heartbeat_pdu(ps->context.peer, period, count1, count2);
519 return Queue_Enqueue(pc->cached_server_channel_data, ev);
522 static BOOL sendQueuedChannelData(pClientContext* pc)
532 Queue_Lock(pc->cached_server_channel_data);
533 while (rc && (ev = Queue_Dequeue(pc->cached_server_channel_data)))
535 UINT16 channelId = 0;
536 WINPR_ASSERT(pc->context.instance);
538 channelId = freerdp_channels_get_id_by_name(pc->context.instance, ev->channel_name);
540 if ((channelId == 0) || (channelId == UINT16_MAX))
544 WINPR_ASSERT(pc->context.instance->SendChannelPacket);
545 rc = pc->context.instance->SendChannelPacket(pc->context.instance, channelId,
546 ev->total_size, ev->flags, ev->data,
549 channel_data_free(ev);
552 Queue_Unlock(pc->cached_server_channel_data);
567 static BOOL pf_client_post_connect(freerdp* instance)
569 rdpContext* context = NULL;
570 rdpSettings* settings = NULL;
571 rdpUpdate* update = NULL;
572 rdpContext* ps = NULL;
573 pClientContext* pc = NULL;
574 const proxyConfig* config = NULL;
576 WINPR_ASSERT(instance);
577 context = instance->context;
578 WINPR_ASSERT(context);
579 settings = context->settings;
580 WINPR_ASSERT(settings);
581 update = context->update;
582 WINPR_ASSERT(update);
583 pc = (pClientContext*)context;
585 WINPR_ASSERT(pc->pdata);
586 ps = (rdpContext*)pc->pdata->ps;
588 config = pc->pdata->config;
589 WINPR_ASSERT(config);
591 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_CONNECT, pc->pdata, pc))
594 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
599 pf_client_register_update_callbacks(update);
602 pc->client_receive_channel_data_original = instance->ReceiveChannelData;
603 instance->ReceiveChannelData = pf_client_receive_channel_data_hook;
605 instance->heartbeat->ServerHeartbeat = pf_client_on_server_heartbeat;
607 pc->connected = TRUE;
610 sendQueuedChannelData(pc);
617 return proxy_server_reactivate(ps, context);
623 static void pf_client_post_disconnect(freerdp* instance)
625 pClientContext* pc = NULL;
626 proxyData* pdata = NULL;
631 if (!instance->context)
634 pc = (pClientContext*)instance->context;
639 #if defined(WITH_PROXY_EMULATE_SMARTCARD)
640 pf_channel_smartcard_client_free(pc);
643 pf_channel_rdpdr_client_free(pc);
645 pc->connected = FALSE;
646 pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_DISCONNECT, pc->pdata, pc);
648 PubSub_UnsubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
652 if (!pc->allow_next_conn_failure)
653 proxy_data_abort_connect(pdata);
656 static BOOL pf_client_redirect(freerdp* instance)
658 pClientContext* pc = NULL;
659 proxyData* pdata = NULL;
664 if (!instance->context)
667 pc = (pClientContext*)instance->context;
672 #if defined(WITH_PROXY_EMULATE_SMARTCARD)
673 pf_channel_smartcard_client_reset(pc);
675 pf_channel_rdpdr_client_reset(pc);
677 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_REDIRECT, pc->pdata, pc);
686 static BOOL pf_client_should_retry_without_nla(pClientContext* pc)
688 rdpSettings* settings = NULL;
689 const proxyConfig* config = NULL;
692 WINPR_ASSERT(pc->pdata);
693 settings = pc->context.settings;
694 WINPR_ASSERT(settings);
695 config = pc->pdata->config;
696 WINPR_ASSERT(config);
698 if (!config->ClientAllowFallbackToTls ||
702 return config->ClientTlsSecurity || config->ClientRdpSecurity;
705 static BOOL pf_client_set_security_settings(pClientContext* pc)
708 WINPR_ASSERT(pc->pdata);
709 rdpSettings* settings = pc->context.settings;
710 WINPR_ASSERT(settings);
711 const proxyConfig* config = pc->pdata->config;
712 WINPR_ASSERT(config);
721 if (pf_client_use_proxy_smartcard_auth(settings))
737 static BOOL pf_client_connect_without_nla(pClientContext* pc)
739 freerdp* instance = NULL;
740 rdpSettings* settings = NULL;
743 instance = pc->context.instance;
744 WINPR_ASSERT(instance);
746 if (!freerdp_context_reset(instance))
749 settings = pc->context.settings;
750 WINPR_ASSERT(settings);
761 pc->allow_next_conn_failure = FALSE;
762 return freerdp_connect(instance);
765 static BOOL pf_client_connect(freerdp* instance)
767 pClientContext* pc = NULL;
768 rdpSettings* settings = NULL;
772 WINPR_ASSERT(instance);
773 pc = (pClientContext*)instance->context;
775 settings = instance->context->settings;
776 WINPR_ASSERT(settings);
778 PROXY_LOG_INFO(TAG, pc,
"connecting using client info: Username: %s, Domain: %s",
782 if (!pf_client_set_security_settings(pc))
785 if (pf_client_should_retry_without_nla(pc))
786 retry = pc->allow_next_conn_failure = TRUE;
788 PROXY_LOG_INFO(TAG, pc,
"connecting using security settings: rdp=%d, tls=%d, nla=%d",
793 if (!freerdp_connect(instance))
795 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata, pc))
801 PROXY_LOG_ERR(TAG, pc,
"failed to connect with NLA. retrying to connect without NLA");
802 if (!pf_client_connect_without_nla(pc))
804 PROXY_LOG_ERR(TAG, pc,
"pf_client_connect_without_nla failed!");
811 pc->allow_next_conn_failure = FALSE;
820 static DWORD WINAPI pf_client_thread_proc(pClientContext* pc)
822 freerdp* instance = NULL;
823 proxyData* pdata = NULL;
826 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
830 instance = pc->context.instance;
831 WINPR_ASSERT(instance);
842 handles[nCount++] = pdata->abort_event;
844 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_INIT_CONNECT, pdata, pc))
846 proxy_data_abort_connect(pdata);
850 if (!pf_client_connect(instance))
852 proxy_data_abort_connect(pdata);
855 handles[nCount++] = Queue_Event(pc->cached_server_channel_data);
857 while (!freerdp_shall_disconnect_context(instance->context))
859 UINT32 tmp = freerdp_get_event_handles(instance->context, &handles[nCount],
860 ARRAYSIZE(handles) - nCount);
864 PROXY_LOG_ERR(TAG, pc,
"freerdp_get_event_handles failed!");
868 status = WaitForMultipleObjects(nCount + tmp, handles, FALSE, INFINITE);
870 if (status == WAIT_FAILED)
872 WLog_ERR(TAG,
"WaitForMultipleObjects failed with %" PRIu32
"", status);
877 if (status == WAIT_OBJECT_0)
880 if (freerdp_shall_disconnect_context(instance->context))
883 if (proxy_data_shall_disconnect(pdata))
886 if (!freerdp_check_event_handles(instance->context))
888 if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
889 WLog_ERR(TAG,
"Failed to check FreeRDP event handles");
893 sendQueuedChannelData(pc);
896 freerdp_disconnect(instance);
899 pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_UNINIT_CONNECT, pdata, pc);
904 static int pf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
906 const char* str_data = freerdp_get_logon_error_info_data(data);
907 const char* str_type = freerdp_get_logon_error_info_type(type);
909 if (!instance || !instance->context)
912 WLog_INFO(TAG,
"Logon Error Info %s [%s]", str_data, str_type);
916 static void pf_client_context_free(freerdp* instance, rdpContext* context)
918 pClientContext* pc = (pClientContext*)context;
919 WINPR_UNUSED(instance);
924 pc->sendChannelData = NULL;
925 Queue_Free(pc->cached_server_channel_data);
926 Stream_Free(pc->remote_pem, TRUE);
927 free(pc->remote_hostname);
928 free(pc->computerName.v);
929 HashTable_Free(pc->interceptContextMap);
932 static int pf_client_verify_X509_certificate(freerdp* instance,
const BYTE* data,
size_t length,
933 const char* hostname, UINT16 port, DWORD flags)
935 pClientContext* pc = NULL;
937 WINPR_ASSERT(instance);
939 WINPR_ASSERT(length > 0);
940 WINPR_ASSERT(hostname);
942 pc = (pClientContext*)instance->context;
945 if (!Stream_EnsureCapacity(pc->remote_pem, length))
947 Stream_SetPosition(pc->remote_pem, 0);
949 free(pc->remote_hostname);
950 pc->remote_hostname = NULL;
953 Stream_Write(pc->remote_pem, data, length);
956 pc->remote_hostname = _strdup(hostname);
957 pc->remote_port = port;
958 pc->remote_flags = flags;
960 Stream_SealLength(pc->remote_pem);
961 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_VERIFY_X509, pc->pdata, pc))
966 void channel_data_free(
void* obj)
979 cnv.cpv = dst->channel_name;
985 static void* channel_data_copy(
const void* obj)
1002 if (src->channel_name)
1004 dst->channel_name = _strdup(src->channel_name);
1005 if (!dst->channel_name)
1008 dst->data = malloc(src->data_len);
1012 cnv.cpv = dst->data;
1013 memcpy(cnv.pv, src->data, src->data_len);
1017 channel_data_free(dst);
1021 static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
1024 pClientContext* pc = (pClientContext*)context;
1026 if (!instance || !context)
1029 instance->LoadChannels = pf_client_load_channels;
1030 instance->PreConnect = pf_client_pre_connect;
1031 instance->PostConnect = pf_client_post_connect;
1032 instance->PostDisconnect = pf_client_post_disconnect;
1033 instance->Redirect = pf_client_redirect;
1034 instance->LogonErrorInfo = pf_logon_error_info;
1035 instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
1037 pc->remote_pem = Stream_New(NULL, 4096);
1038 if (!pc->remote_pem)
1041 pc->sendChannelData = pf_client_send_channel_data;
1042 pc->cached_server_channel_data = Queue_New(TRUE, -1, -1);
1043 if (!pc->cached_server_channel_data)
1045 obj = Queue_Object(pc->cached_server_channel_data);
1047 obj->fnObjectNew = channel_data_copy;
1048 obj->fnObjectFree = channel_data_free;
1050 pc->interceptContextMap = HashTable_New(FALSE);
1051 if (!pc->interceptContextMap)
1054 if (!HashTable_SetupForStringData(pc->interceptContextMap, FALSE))
1057 obj = HashTable_ValueObject(pc->interceptContextMap);
1059 obj->fnObjectFree = intercept_context_entry_free;
1064 static int pf_client_client_stop(rdpContext* context)
1066 pClientContext* pc = (pClientContext*)context;
1067 proxyData* pdata = NULL;
1071 WINPR_ASSERT(pdata);
1073 PROXY_LOG_DBG(TAG, pc,
"aborting client connection");
1074 proxy_data_abort_connect(pdata);
1075 freerdp_abort_connect_context(context);
1080 int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1082 WINPR_ASSERT(pEntryPoints);
1084 ZeroMemory(pEntryPoints,
sizeof(RDP_CLIENT_ENTRY_POINTS));
1085 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
1086 pEntryPoints->Size =
sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1087 pEntryPoints->ContextSize =
sizeof(pClientContext);
1089 pEntryPoints->ClientNew = pf_client_client_new;
1090 pEntryPoints->ClientFree = pf_client_context_free;
1091 pEntryPoints->ClientStop = pf_client_client_stop;
1098 DWORD WINAPI pf_client_start(LPVOID arg)
1101 pClientContext* pc = (pClientContext*)arg;
1104 if (freerdp_client_start(&pc->context) == 0)
1105 rc = pf_client_thread_proc(pc);
1106 freerdp_client_stop(&pc->context);
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
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.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.
FREERDP_API void * freerdp_settings_get_pointer_writable(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a mutable pointer settings value.
This struct contains function pointer to initialize/free objects.