FreeRDP
Loading...
Searching...
No Matches
client/common/client.c
1
21#include <winpr/cast.h>
22
23#include <freerdp/config.h>
24
25#include <string.h>
26#include <errno.h>
27#include <math.h>
28#include <limits.h>
29#include <float.h>
30
31#include <freerdp/client.h>
32
33#include <freerdp/freerdp.h>
34#include <freerdp/addin.h>
35#include <freerdp/assistance.h>
36#include <freerdp/client/file.h>
37#include <freerdp/utils/passphrase.h>
38#include <freerdp/client/cmdline.h>
39#include <freerdp/client/channels.h>
40#include <freerdp/event.h>
41#include <freerdp/utils/smartcardlogon.h>
42
43#if defined(CHANNEL_AINPUT_CLIENT)
44#include <freerdp/client/ainput.h>
45#include <freerdp/channels/ainput.h>
46#endif
47
48#if defined(CHANNEL_VIDEO_CLIENT)
49#include <freerdp/client/video.h>
50#include <freerdp/channels/video.h>
51#endif
52
53#if defined(CHANNEL_RDPGFX_CLIENT)
54#include <freerdp/client/rdpgfx.h>
55#include <freerdp/channels/rdpgfx.h>
56#include <freerdp/gdi/gfx.h>
57#endif
58
59#if defined(CHANNEL_GEOMETRY_CLIENT)
60#include <freerdp/client/geometry.h>
61#include <freerdp/channels/geometry.h>
62#endif
63
64#if defined(CHANNEL_GEOMETRY_CLIENT) || defined(CHANNEL_VIDEO_CLIENT)
65#include <freerdp/gdi/video.h>
66#endif
67
68#ifdef WITH_AAD
69#include <freerdp/utils/http.h>
70#include <freerdp/utils/aad.h>
71#endif
72
73#ifdef WITH_SSO_MIB
74#include "sso_mib_tokens.h"
75#endif
76
77#include <freerdp/log.h>
78#define TAG CLIENT_TAG("common")
79
80static void set_default_callbacks(freerdp* instance)
81{
82 WINPR_ASSERT(instance);
83 instance->AuthenticateEx = client_cli_authenticate_ex;
84 instance->ChooseSmartcard = client_cli_choose_smartcard;
85 instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
86 instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
87 instance->PresentGatewayMessage = client_cli_present_gateway_message;
88 instance->LogonErrorInfo = client_cli_logon_error_info;
89 instance->GetAccessToken = client_cli_get_access_token;
90 instance->RetryDialog = client_common_retry_dialog;
91}
92
93static void client_cli_user_notification(void* context, const UserNotificationEventArgs* e)
94{
95 WINPR_UNUSED(context);
96 WINPR_ASSERT(e);
97 if (!e->message || e->message[0] == '\0')
98 return;
99 (void)fprintf(stderr, "[%s] Touch the security key\n", e->e.Sender);
100 (void)fflush(stderr);
101}
102
103static BOOL freerdp_client_common_new(freerdp* instance, rdpContext* context)
104{
105 RDP_CLIENT_ENTRY_POINTS* pEntryPoints = nullptr;
106
107 WINPR_ASSERT(instance);
108 WINPR_ASSERT(context);
109
110 instance->LoadChannels = freerdp_client_load_channels;
111 set_default_callbacks(instance);
112
113 pEntryPoints = instance->pClientEntryPoints;
114 WINPR_ASSERT(pEntryPoints);
115
116 return IFCALLRESULT(TRUE, pEntryPoints->ClientNew, instance, context);
117}
118
119static void freerdp_client_common_free(freerdp* instance, rdpContext* context)
120{
121 RDP_CLIENT_ENTRY_POINTS* pEntryPoints = nullptr;
122
123 WINPR_ASSERT(instance);
124 WINPR_ASSERT(context);
125
126 pEntryPoints = instance->pClientEntryPoints;
127 WINPR_ASSERT(pEntryPoints);
128 IFCALL(pEntryPoints->ClientFree, instance, context);
129}
130
131/* Common API */
132
133rdpContext* freerdp_client_context_new(const RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
134{
135 freerdp* instance = nullptr;
136 rdpContext* context = nullptr;
137
138 if (!pEntryPoints)
139 return nullptr;
140
141 if (!IFCALLRESULT(TRUE, pEntryPoints->GlobalInit))
142 return nullptr;
143
144 instance = freerdp_new();
145
146 if (!instance)
147 return nullptr;
148
149 instance->ContextSize = pEntryPoints->ContextSize;
150 instance->ContextNew = freerdp_client_common_new;
151 instance->ContextFree = freerdp_client_common_free;
152 instance->pClientEntryPoints = (RDP_CLIENT_ENTRY_POINTS*)malloc(pEntryPoints->Size);
153
154 if (!instance->pClientEntryPoints)
155 goto out_fail;
156
157 CopyMemory(instance->pClientEntryPoints, pEntryPoints, pEntryPoints->Size);
158
159 if (!freerdp_context_new_ex(instance, pEntryPoints->settings))
160 goto out_fail2;
161
162 context = instance->context;
163 context->instance = instance;
164
165#if defined(WITH_CLIENT_CHANNELS)
166 if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
167 CHANNEL_RC_OK)
168 goto out_fail2;
169#endif
170
171 return context;
172out_fail2:
173 free(instance->pClientEntryPoints);
174out_fail:
175 freerdp_free(instance);
176 return nullptr;
177}
178
179void freerdp_client_context_free(rdpContext* context)
180{
181 freerdp* instance = nullptr;
182
183 if (!context)
184 return;
185
186 instance = context->instance;
187
188 if (instance)
189 {
190 RDP_CLIENT_ENTRY_POINTS* pEntryPoints = instance->pClientEntryPoints;
191 freerdp_context_free(instance);
192
193 if (pEntryPoints)
194 IFCALL(pEntryPoints->GlobalUninit);
195
196 free(instance->pClientEntryPoints);
197 freerdp_free(instance);
198 }
199}
200
201int freerdp_client_start(rdpContext* context)
202{
203 RDP_CLIENT_ENTRY_POINTS* pEntryPoints = nullptr;
204
205 if (!context || !context->instance || !context->instance->pClientEntryPoints)
206 return ERROR_BAD_ARGUMENTS;
207
208 if (freerdp_settings_get_bool(context->settings, FreeRDP_UseCommonStdioCallbacks))
209 {
210 set_default_callbacks(context->instance);
211 if (context->pubSub)
212 {
213 const int rc =
214 PubSub_SubscribeUserNotification(context->pubSub, client_cli_user_notification);
215 if (rc < 0)
216 return FALSE;
217 }
218 }
219
220#ifdef WITH_SSO_MIB
221 rdpClientContext* client_context = (rdpClientContext*)context;
222 client_context->mibClientWrapper = sso_mib_new(context);
223 if (!client_context->mibClientWrapper)
224 return ERROR_INTERNAL_ERROR;
225#endif
226
227 pEntryPoints = context->instance->pClientEntryPoints;
228 return IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStart, context);
229}
230
231int freerdp_client_stop(rdpContext* context)
232{
233 RDP_CLIENT_ENTRY_POINTS* pEntryPoints = nullptr;
234
235 if (!context || !context->instance || !context->instance->pClientEntryPoints)
236 return ERROR_BAD_ARGUMENTS;
237
238 pEntryPoints = context->instance->pClientEntryPoints;
239 const int rc = IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
240
241 if (freerdp_settings_get_bool(context->settings, FreeRDP_UseCommonStdioCallbacks))
242 PubSub_UnsubscribeUserNotification(context->pubSub, client_cli_user_notification);
243
244#ifdef WITH_SSO_MIB
245 rdpClientContext* client_context = (rdpClientContext*)context;
246 sso_mib_free(client_context->mibClientWrapper);
247 client_context->mibClientWrapper = nullptr;
248#endif // WITH_SSO_MIB
249 return rc;
250}
251
252freerdp* freerdp_client_get_instance(rdpContext* context)
253{
254 if (!context || !context->instance)
255 return nullptr;
256
257 return context->instance;
258}
259
260HANDLE freerdp_client_get_thread(rdpContext* context)
261{
262 if (!context)
263 return nullptr;
264
265 return ((rdpClientContext*)context)->thread;
266}
267
268static BOOL freerdp_client_settings_post_process(rdpSettings* settings)
269{
270 /* Moved GatewayUseSameCredentials logic outside of cmdline.c, so
271 * that the rdp file also triggers this functionality */
272 if (freerdp_settings_get_bool(settings, FreeRDP_GatewayEnabled))
273 {
274 if (freerdp_settings_get_bool(settings, FreeRDP_GatewayUseSameCredentials))
275 {
276 const char* Username = freerdp_settings_get_string(settings, FreeRDP_Username);
277 const char* Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
278 if (Username)
279 {
280 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayUsername, Username))
281 goto out_error;
282 }
283
284 if (Domain)
285 {
286 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayDomain, Domain))
287 goto out_error;
288 }
289
290 if (freerdp_settings_get_string(settings, FreeRDP_Password))
291 {
293 settings, FreeRDP_GatewayPassword,
294 freerdp_settings_get_string(settings, FreeRDP_Password)))
295 goto out_error;
296 }
297 }
298 }
299
300 /* Moved logic for Multimon and Span monitors to force fullscreen, so
301 * that the rdp file also triggers this functionality */
302 if (freerdp_settings_get_bool(settings, FreeRDP_SpanMonitors))
303 {
304 if (!freerdp_settings_set_bool(settings, FreeRDP_UseMultimon, TRUE))
305 goto out_error;
306 if (!freerdp_settings_set_bool(settings, FreeRDP_Fullscreen, TRUE))
307 goto out_error;
308 }
309 else if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
310 {
311 if (!freerdp_settings_set_bool(settings, FreeRDP_Fullscreen, TRUE))
312 goto out_error;
313 }
314
315 /* deal with the smartcard / smartcard logon stuff */
316 if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon))
317 {
318 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE))
319 goto out_error;
320 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
321 goto out_error;
322 if (!freerdp_settings_set_bool(settings, FreeRDP_PasswordIsSmartcardPin, TRUE))
323 goto out_error;
324 }
325
326 return TRUE;
327out_error:
328 return FALSE;
329}
330
331int freerdp_client_settings_parse_command_line(rdpSettings* settings, int argc, char** argv,
332 BOOL allowUnknown)
333
334{
335 return freerdp_client_settings_parse_command_line_ex(settings, argc, argv, allowUnknown,
336 nullptr, 0, nullptr, nullptr);
337}
338
339int freerdp_client_settings_parse_command_line_ex(
340 rdpSettings* settings, int argc, char** argv, BOOL allowUnknown, COMMAND_LINE_ARGUMENT_A* args,
341 size_t count, freerdp_command_line_handle_option_t handle_option, void* handle_userdata)
342{
343 int status = 0;
344
345 if (argc < 1)
346 return 0;
347
348 if (!argv)
349 return -1;
350
351 status = freerdp_client_settings_parse_command_line_arguments_ex(
352 settings, argc, argv, allowUnknown, args, count, handle_option, handle_userdata);
353
354 if (status < 0)
355 return status;
356
357 /* This function will call logic that is applicable to the settings
358 * from command line parsing AND the rdp file parsing */
359 if (!freerdp_client_settings_post_process(settings))
360 status = -1;
361
362 const char* name = argv[0];
363 WLog_DBG(TAG, "This is [%s] %s %s", name, freerdp_get_version_string(),
364 freerdp_get_build_config());
365 return status;
366}
367
368int freerdp_client_settings_parse_connection_file(rdpSettings* settings, const char* filename)
369{
370 rdpFile* file = nullptr;
371 int ret = -1;
372 file = freerdp_client_rdp_file_new();
373
374 if (!file)
375 return -1;
376
377 if (!freerdp_client_parse_rdp_file(file, filename))
378 goto out;
379
380 if (!freerdp_client_populate_settings_from_rdp_file(file, settings))
381 goto out;
382
383 ret = 0;
384out:
385 freerdp_client_rdp_file_free(file);
386 return ret;
387}
388
389int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, const BYTE* buffer,
390 size_t size)
391{
392 rdpFile* file = nullptr;
393 int status = -1;
394 file = freerdp_client_rdp_file_new();
395
396 if (!file)
397 return -1;
398
399 if (freerdp_client_parse_rdp_file_buffer(file, buffer, size) &&
400 freerdp_client_populate_settings_from_rdp_file(file, settings))
401 {
402 status = 0;
403 }
404
405 freerdp_client_rdp_file_free(file);
406 return status;
407}
408
409int freerdp_client_settings_write_connection_file(const rdpSettings* settings, const char* filename,
410 BOOL unicode)
411{
412 rdpFile* file = nullptr;
413 int ret = -1;
414 file = freerdp_client_rdp_file_new();
415
416 if (!file)
417 return -1;
418
419 if (!freerdp_client_populate_rdp_file_from_settings(file, settings))
420 goto out;
421
422 if (!freerdp_client_write_rdp_file(file, filename, unicode))
423 goto out;
424
425 ret = 0;
426out:
427 freerdp_client_rdp_file_free(file);
428 return ret;
429}
430
431int freerdp_client_settings_parse_assistance_file(rdpSettings* settings, int argc, char* argv[])
432{
433 int status = 0;
434 int ret = -1;
435 char* filename = nullptr;
436 char* password = nullptr;
437 rdpAssistanceFile* file = nullptr;
438
439 if (!settings || !argv || (argc < 2))
440 return -1;
441
442 filename = argv[1];
443
444 for (int x = 2; x < argc; x++)
445 {
446 const char* key = strstr(argv[x], "assistance:");
447
448 if (key)
449 password = strchr(key, ':') + 1;
450 }
451
452 file = freerdp_assistance_file_new();
453
454 if (!file)
455 return -1;
456
457 status = freerdp_assistance_parse_file(file, filename, password);
458
459 if (status < 0)
460 goto out;
461
462 if (!freerdp_assistance_populate_settings_from_assistance_file(file, settings))
463 goto out;
464
465 ret = 0;
466out:
467 freerdp_assistance_file_free(file);
468 return ret;
469}
470
471static int client_cli_read_string(freerdp* instance, const char* what, const char* suggestion,
472 char** result)
473{
474 WINPR_ASSERT(instance);
475 WINPR_ASSERT(what);
476 WINPR_ASSERT(result);
477
478 size_t size = 0;
479 printf("%s", what);
480 (void)fflush(stdout);
481
482 char* line = nullptr;
483 if (suggestion && strlen(suggestion) > 0)
484 {
485 line = _strdup(suggestion);
486 size = strlen(suggestion);
487 }
488
489 const SSIZE_T rc = freerdp_interruptible_get_line(instance->context, &line, &size, stdin);
490 if (rc < 0)
491 {
492 char ebuffer[256] = WINPR_C_ARRAY_INIT;
493 WLog_ERR(TAG, "freerdp_interruptible_get_line returned %s [%d]",
494 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
495 free(line);
496 return -1;
497 }
498
499 free(*result);
500 *result = nullptr;
501
502 if (line)
503 {
504 line = StrSep(&line, "\r");
505 line = StrSep(&line, "\n");
506 *result = line;
507 }
508 return 0;
509}
510
526static BOOL client_cli_authenticate_raw(freerdp* instance, rdp_auth_reason reason, char** username,
527 char** password, char** domain)
528{
529 static const size_t password_size = 512;
530 const char* userAuth = "Username: ";
531 const char* domainAuth = "Domain: ";
532 const char* pwdAuth = "Password: ";
533 BOOL pinOnly = FALSE;
534 BOOL queryAll = FALSE;
535
536 WINPR_ASSERT(instance);
537 WINPR_ASSERT(instance->context);
538 WINPR_ASSERT(instance->context->settings);
539
540 switch (reason)
541 {
542 case AUTH_SMARTCARD_PIN:
543 pwdAuth = "Smartcard-Pin: ";
544 pinOnly = TRUE;
545 break;
546 case AUTH_FIDO_PIN:
547 pwdAuth = "FIDO2 PIN: ";
548 pinOnly = TRUE;
549 break;
550 case AUTH_RDSTLS:
551 queryAll = TRUE;
552 break;
553 case AUTH_TLS:
554 case AUTH_RDP:
555 case AUTH_NLA:
556 break;
557 case GW_AUTH_HTTP:
558 case GW_AUTH_RDG:
559 case GW_AUTH_RPC:
560 userAuth = "GatewayUsername: ";
561 domainAuth = "GatewayDomain: ";
562 pwdAuth = "GatewayPassword: ";
563 break;
564 default:
565 return FALSE;
566 }
567
568 if (!username || !password || !domain)
569 return FALSE;
570
571 if (!pinOnly)
572 {
573 const char* suggest = *username;
574 if (queryAll || !suggest)
575 {
576 const int rc = client_cli_read_string(instance, userAuth, suggest, username);
577 if (rc < 0)
578 goto fail;
579 }
580 }
581
582 if (!pinOnly)
583 {
584 const char* suggest = *domain;
585 if (queryAll || !suggest)
586 {
587 const int rc = client_cli_read_string(instance, domainAuth, suggest, domain);
588 if (rc < 0)
589 goto fail;
590 }
591 }
592
593 {
594 char* line = calloc(password_size, sizeof(char));
595
596 if (!line)
597 goto fail;
598
599 const BOOL fromStdin =
600 freerdp_settings_get_bool(instance->context->settings, FreeRDP_CredentialsFromStdin);
601 const char* rc =
602 freerdp_passphrase_read(instance->context, pwdAuth, line, password_size, fromStdin);
603 if (rc == nullptr)
604 goto fail;
605
606 if (password_size > 0)
607 {
608 free(*password);
609 *password = line;
610 }
611 }
612
613 return TRUE;
614fail:
615 free(*username);
616 free(*domain);
617 free(*password);
618 *username = nullptr;
619 *domain = nullptr;
620 *password = nullptr;
621 return FALSE;
622}
623
624BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
625 rdp_auth_reason reason)
626{
627 WINPR_ASSERT(instance);
628 WINPR_ASSERT(username);
629 WINPR_ASSERT(password);
630 WINPR_ASSERT(domain);
631
632 switch (reason)
633 {
634 case AUTH_RDSTLS:
635 case AUTH_NLA:
636 break;
637
638 case AUTH_TLS:
639 case AUTH_RDP:
640 case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
641 case AUTH_FIDO_PIN:
642 if ((*username) && (*password))
643 return TRUE;
644 break;
645 case GW_AUTH_HTTP:
646 case GW_AUTH_RDG:
647 case GW_AUTH_RPC:
648 break;
649 default:
650 return FALSE;
651 }
652
653 return client_cli_authenticate_raw(instance, reason, username, password, domain);
654}
655
656BOOL client_cli_choose_smartcard(WINPR_ATTR_UNUSED freerdp* instance, SmartcardCertInfo** cert_list,
657 DWORD count, DWORD* choice, BOOL gateway)
658{
659 unsigned long answer = 0;
660 char* p = nullptr;
661
662 printf("Multiple smartcards are available for use:\n");
663 for (DWORD i = 0; i < count; i++)
664 {
665 const SmartcardCertInfo* cert = cert_list[i];
666 char* reader = ConvertWCharToUtf8Alloc(cert->reader, nullptr);
667 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, nullptr);
668
669 printf("[%" PRIu32
670 "] %s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s\n",
671 i, container_name, reader, cert->userHint, cert->domainHint, cert->subject,
672 cert->issuer, cert->upn);
673
674 free(reader);
675 free(container_name);
676 }
677
678 while (1)
679 {
680 char input[10] = WINPR_C_ARRAY_INIT;
681
682 printf("\nChoose a smartcard to use for %s (0 - %" PRIu32 "): ",
683 gateway ? "gateway authentication" : "logon", count - 1);
684 (void)fflush(stdout);
685 if (!fgets(input, 10, stdin))
686 {
687 WLog_ERR(TAG, "could not read from stdin");
688 return FALSE;
689 }
690
691 answer = strtoul(input, &p, 10);
692 if ((*p == '\n' && p != input) && answer < count)
693 {
694 *choice = (UINT32)answer;
695 return TRUE;
696 }
697 }
698}
699
700#if defined(WITH_FREERDP_DEPRECATED)
701BOOL client_cli_authenticate(freerdp* instance, char** username, char** password, char** domain)
702{
703 if (freerdp_settings_get_bool(instance->settings, FreeRDP_SmartcardLogon))
704 {
705 WLog_INFO(TAG, "Authentication via smartcard");
706 return TRUE;
707 }
708
709 return client_cli_authenticate_raw(instance, FALSE, username, password, domain);
710}
711
712BOOL client_cli_gw_authenticate(freerdp* instance, char** username, char** password, char** domain)
713{
714 return client_cli_authenticate_raw(instance, TRUE, username, password, domain);
715}
716#endif
717
718static DWORD client_cli_accept_certificate(freerdp* instance)
719{
720 int answer = 0;
721
722 WINPR_ASSERT(instance);
723 WINPR_ASSERT(instance->context);
724
725 const rdpSettings* settings = instance->context->settings;
726 WINPR_ASSERT(settings);
727
728 const BOOL fromStdin = freerdp_settings_get_bool(settings, FreeRDP_CredentialsFromStdin);
729 if (fromStdin)
730 return 0;
731
732 while (1)
733 {
734 printf("Do you trust the above certificate? (Y/T/N) ");
735 (void)fflush(stdout);
736 answer = freerdp_interruptible_getc(instance->context, stdin);
737
738 if ((answer == EOF) || feof(stdin))
739 {
740 printf("\nError: Could not read answer from stdin.\n");
741 return 0;
742 }
743
744 switch (answer)
745 {
746 case 'y':
747 case 'Y':
748 answer = freerdp_interruptible_getc(instance->context, stdin);
749 if (answer == EOF)
750 return 0;
751 return 1;
752
753 case 't':
754 case 'T':
755 answer = freerdp_interruptible_getc(instance->context, stdin);
756 if (answer == EOF)
757 return 0;
758 return 2;
759
760 case 'n':
761 case 'N':
762 answer = freerdp_interruptible_getc(instance->context, stdin);
763 if (answer == EOF)
764 return 0;
765 return 0;
766
767 default:
768 break;
769 }
770
771 printf("\n");
772 }
773}
774
788#if defined(WITH_FREERDP_DEPRECATED)
789DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name, const char* subject,
790 const char* issuer, const char* fingerprint, BOOL host_mismatch)
791{
792 WINPR_UNUSED(common_name);
793 WINPR_UNUSED(host_mismatch);
794
795 printf("WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
796 printf("Certificate details:\n");
797 printf("\tSubject: %s\n", subject);
798 printf("\tIssuer: %s\n", issuer);
799 printf("\tThumbprint: %s\n", fingerprint);
800 printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
801 "the CA certificate in your certificate store, or the certificate has expired.\n"
802 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
803 return client_cli_accept_certificate(instance);
804}
805#endif
806
807static char* client_cli_pem_cert(const char* pem)
808{
809 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
810 if (!cert)
811 return nullptr;
812
813 char* fp = freerdp_certificate_get_fingerprint(cert);
814 char* start = freerdp_certificate_get_validity(cert, TRUE);
815 char* end = freerdp_certificate_get_validity(cert, FALSE);
816 freerdp_certificate_free(cert);
817
818 char* str = nullptr;
819 size_t slen = 0;
820 winpr_asprintf(&str, &slen,
821 "\tValid from: %s\n"
822 "\tValid to: %s\n"
823 "\tThumbprint: %s\n",
824 start, end, fp);
825 free(fp);
826 free(start);
827 free(end);
828 return str;
829}
830
846DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
847 const char* common_name, const char* subject,
848 const char* issuer, const char* fingerprint, DWORD flags)
849{
850 const char* type = "RDP-Server";
851
852 WINPR_ASSERT(instance);
853 WINPR_ASSERT(instance->context);
854 WINPR_ASSERT(instance->context->settings);
855
856 if (flags & VERIFY_CERT_FLAG_GATEWAY)
857 type = "RDP-Gateway";
858
859 if (flags & VERIFY_CERT_FLAG_REDIRECT)
860 type = "RDP-Redirect";
861
862 printf("Certificate details for %s:%" PRIu16 " (%s):\n", host, port, type);
863 printf("\tCommon Name: %s\n", common_name);
864 printf("\tSubject: %s\n", subject);
865 printf("\tIssuer: %s\n", issuer);
866 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
867 * FreeRDP_CertificateCallbackPreferPEM to TRUE
868 */
869 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
870 {
871 char* str = client_cli_pem_cert(fingerprint);
872 printf("%s", str);
873 free(str);
874 }
875 else
876 printf("\tThumbprint: %s\n", fingerprint);
877
878 printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
879 "the CA certificate in your certificate store, or the certificate has expired.\n"
880 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
881 return client_cli_accept_certificate(instance);
882}
883
899#if defined(WITH_FREERDP_DEPRECATED)
900DWORD client_cli_verify_changed_certificate(freerdp* instance, const char* common_name,
901 const char* subject, const char* issuer,
902 const char* fingerprint, const char* old_subject,
903 const char* old_issuer, const char* old_fingerprint)
904{
905 WINPR_UNUSED(common_name);
906
907 printf("WARNING: This callback is deprecated, migrate to "
908 "client_cli_verify_changed_certificate_ex\n");
909 printf("!!! Certificate has changed !!!\n");
910 printf("\n");
911 printf("New Certificate details:\n");
912 printf("\tSubject: %s\n", subject);
913 printf("\tIssuer: %s\n", issuer);
914 printf("\tThumbprint: %s\n", fingerprint);
915 printf("\n");
916 printf("Old Certificate details:\n");
917 printf("\tSubject: %s\n", old_subject);
918 printf("\tIssuer: %s\n", old_issuer);
919 printf("\tThumbprint: %s\n", old_fingerprint);
920 printf("\n");
921 printf("The above X.509 certificate does not match the certificate used for previous "
922 "connections.\n"
923 "This may indicate that the certificate has been tampered with.\n"
924 "Please contact the administrator of the RDP server and clarify.\n");
925 return client_cli_accept_certificate(instance);
926}
927#endif
928
948DWORD client_cli_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
949 const char* common_name, const char* subject,
950 const char* issuer, const char* fingerprint,
951 const char* old_subject, const char* old_issuer,
952 const char* old_fingerprint, DWORD flags)
953{
954 const char* type = "RDP-Server";
955
956 WINPR_ASSERT(instance);
957 WINPR_ASSERT(instance->context);
958 WINPR_ASSERT(instance->context->settings);
959
960 if (flags & VERIFY_CERT_FLAG_GATEWAY)
961 type = "RDP-Gateway";
962
963 if (flags & VERIFY_CERT_FLAG_REDIRECT)
964 type = "RDP-Redirect";
965
966 printf("!!!Certificate for %s:%" PRIu16 " (%s) has changed!!!\n", host, port, type);
967 printf("\n");
968 printf("New Certificate details:\n");
969 printf("\tCommon Name: %s\n", common_name);
970 printf("\tSubject: %s\n", subject);
971 printf("\tIssuer: %s\n", issuer);
972 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
973 * FreeRDP_CertificateCallbackPreferPEM to TRUE
974 */
975 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
976 {
977 char* str = client_cli_pem_cert(fingerprint);
978 printf("%s", str);
979 free(str);
980 }
981 else
982 printf("\tThumbprint: %s\n", fingerprint);
983 printf("\n");
984 printf("Old Certificate details:\n");
985 printf("\tSubject: %s\n", old_subject);
986 printf("\tIssuer: %s\n", old_issuer);
987 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
988 * FreeRDP_CertificateCallbackPreferPEM to TRUE
989 */
990 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
991 {
992 char* str = client_cli_pem_cert(old_fingerprint);
993 printf("%s", str);
994 free(str);
995 }
996 else
997 printf("\tThumbprint: %s\n", old_fingerprint);
998 printf("\n");
999 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
1000 {
1001 printf("\tA matching entry with legacy SHA1 was found in local known_hosts2 store.\n");
1002 printf("\tIf you just upgraded from a FreeRDP version before 2.0 this is expected.\n");
1003 printf("\tThe hashing algorithm has been upgraded from SHA1 to SHA256.\n");
1004 printf("\tAll manually accepted certificates must be reconfirmed!\n");
1005 printf("\n");
1006 }
1007 printf("The above X.509 certificate does not match the certificate used for previous "
1008 "connections.\n"
1009 "This may indicate that the certificate has been tampered with.\n"
1010 "Please contact the administrator of the RDP server and clarify.\n");
1011 return client_cli_accept_certificate(instance);
1012}
1013
1014BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1015 BOOL isConsentMandatory, size_t length,
1016 const WCHAR* message)
1017{
1018 const char* msgType = (type == GATEWAY_MESSAGE_CONSENT) ? "Consent message" : "Service message";
1019
1020 WINPR_ASSERT(instance);
1021 WINPR_ASSERT(instance->context);
1022 WINPR_ASSERT(instance->context->settings);
1023
1024 if (!isDisplayMandatory && !isConsentMandatory)
1025 return TRUE;
1026
1027 printf("%s:\n", msgType);
1028#if defined(WIN32)
1029 printf("%.*S\n", (int)length, message);
1030#else
1031 {
1032 LPSTR msg = ConvertWCharNToUtf8Alloc(message, length / sizeof(WCHAR), nullptr);
1033 if (!msg)
1034 {
1035 printf("Failed to convert message!\n");
1036 return FALSE;
1037 }
1038 printf("%s\n", msg);
1039 free(msg);
1040 }
1041#endif
1042
1043 while (isConsentMandatory)
1044 {
1045 printf("I understand and agree to the terms of this policy (Y/N) \n");
1046 (void)fflush(stdout);
1047 const int answer = freerdp_interruptible_getc(instance->context, stdin);
1048
1049 if ((answer == EOF) || feof(stdin))
1050 {
1051 printf("\nError: Could not read answer from stdin.\n");
1052 return FALSE;
1053 }
1054
1055 const int confirm = freerdp_interruptible_getc(instance->context, stdin);
1056 switch (answer)
1057 {
1058 case 'y':
1059 case 'Y':
1060 if (confirm == EOF)
1061 return FALSE;
1062 return TRUE;
1063
1064 case 'n':
1065 case 'N':
1066 return FALSE;
1067
1068 default:
1069 break;
1070 }
1071
1072 printf("\n");
1073 }
1074
1075 return TRUE;
1076}
1077
1078static const char* extract_authorization_code(char* url)
1079{
1080 WINPR_ASSERT(url);
1081
1082 for (char* p = strchr(url, '?'); p++ != nullptr; p = strchr(p, '&'))
1083 {
1084 if (strncmp(p, "code=", 5) != 0)
1085 continue;
1086
1087 char* end = nullptr;
1088 p += 5;
1089
1090 end = strchr(p, '&');
1091 if (end)
1092 *end = '\0';
1093
1094 return p;
1095 }
1096
1097 return nullptr;
1098}
1099
1100#if defined(WITH_AAD)
1101static BOOL client_cli_get_rdsaad_access_token(freerdp* instance, const char* scope,
1102 const char* req_cnf, char** token)
1103{
1104 WINPR_ASSERT(instance);
1105 WINPR_ASSERT(instance->context);
1106
1107 size_t size = 0;
1108 char* url = nullptr;
1109 char* token_request = nullptr;
1110
1111 WINPR_ASSERT(scope);
1112 WINPR_ASSERT(req_cnf);
1113 WINPR_ASSERT(token);
1114
1115 BOOL rc = FALSE;
1116 *token = nullptr;
1117
1118 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1119 FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1120
1121 printf("Browse to: %s\n", request);
1122 free(request);
1123 printf("Paste redirect URL here: \n");
1124
1125 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1126 goto cleanup;
1127
1128 {
1129 const char* code = extract_authorization_code(url);
1130 if (!code)
1131 goto cleanup;
1132 token_request =
1133 freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1134 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1135 }
1136 if (!token_request)
1137 goto cleanup;
1138
1139 rc = client_common_get_access_token(instance, token_request, token);
1140
1141cleanup:
1142 free(token_request);
1143 free(url);
1144 return rc && (*token != nullptr);
1145}
1146
1147static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1148{
1149 WINPR_ASSERT(instance);
1150 WINPR_ASSERT(instance->context);
1151
1152 size_t size = 0;
1153 char* url = nullptr;
1154 char* token_request = nullptr;
1155
1156 WINPR_ASSERT(token);
1157
1158 BOOL rc = FALSE;
1159
1160 *token = nullptr;
1161
1162 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1163 FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1164 if (!request)
1165 return FALSE;
1166 printf("Browse to: %s\n", request);
1167 free(request);
1168 printf("Paste redirect URL here: \n");
1169
1170 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1171 goto cleanup;
1172
1173 {
1174 const char* code = extract_authorization_code(url);
1175 if (!code)
1176 goto cleanup;
1177 token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1178 FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1179 }
1180
1181 if (!token_request)
1182 goto cleanup;
1183
1184 rc = client_common_get_access_token(instance, token_request, token);
1185
1186cleanup:
1187 free(token_request);
1188 free(url);
1189 return rc && (*token != nullptr);
1190}
1191#endif
1192
1193BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1194 size_t count, ...)
1195{
1196 WINPR_ASSERT(instance);
1197 WINPR_ASSERT(token);
1198
1199#if !defined(WITH_AAD)
1200 WLog_ERR(TAG, "Build does not support AAD authentication");
1201 return FALSE;
1202#else
1203 BOOL rc = FALSE;
1204 WINPR_ASSERT(instance->context);
1205 const BOOL saved =
1206 freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1207 if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1208 TRUE))
1209 return FALSE;
1210
1211 switch (tokenType)
1212 {
1213 case ACCESS_TOKEN_TYPE_AAD:
1214 {
1215 if (count < 2)
1216 {
1217 WLog_ERR(TAG,
1218 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1219 ", aborting",
1220 count);
1221 return FALSE;
1222 }
1223 else if (count > 2)
1224 WLog_WARN(TAG,
1225 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1226 ", ignoring",
1227 count);
1228 va_list ap = WINPR_C_ARRAY_INIT;
1229 va_start(ap, count);
1230 const char* scope = va_arg(ap, const char*);
1231 const char* req_cnf = va_arg(ap, const char*);
1232 rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1233 va_end(ap);
1234 }
1235 break;
1236 case ACCESS_TOKEN_TYPE_AVD:
1237 if (count != 0)
1238 WLog_WARN(TAG,
1239 "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1240 ", ignoring",
1241 count);
1242 rc = client_cli_get_avd_access_token(instance, token);
1243 break;
1244 default:
1245 WLog_ERR(TAG, "Unexpected value for AccessTokenType [%u], aborting", tokenType);
1246 break;
1247 }
1248
1249 if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1250 saved))
1251 return FALSE;
1252 return rc;
1253#endif
1254}
1255
1256BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1257{
1258#ifdef WITH_AAD
1259 WINPR_ASSERT(request);
1260 WINPR_ASSERT(token);
1261
1262 BOOL ret = FALSE;
1263 long resp_code = 0;
1264 BYTE* response = nullptr;
1265 size_t response_length = 0;
1266
1267 wLog* log = WLog_Get(TAG);
1268
1269 const char* token_ep =
1270 freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1271 if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1272 {
1273 WLog_ERR(TAG, "access token request failed");
1274 return FALSE;
1275 }
1276
1277 if (resp_code != HTTP_STATUS_OK)
1278 {
1279 char buffer[64] = WINPR_C_ARRAY_INIT;
1280
1281 WLog_Print(log, WLOG_ERROR,
1282 "Server unwilling to provide access token; returned status code %s",
1283 freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1284 if (response_length > 0)
1285 WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1286 goto cleanup;
1287 }
1288
1289 *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1290 if (*token)
1291 ret = TRUE;
1292
1293cleanup:
1294 free(response);
1295 return ret;
1296#else
1297 return FALSE;
1298#endif
1299}
1300
1301SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1302 void* userarg)
1303{
1304 WINPR_UNUSED(instance);
1305 WINPR_ASSERT(instance->context);
1306 WINPR_UNUSED(userarg);
1307 WINPR_ASSERT(instance);
1308 WINPR_ASSERT(what);
1309
1310 if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1311 {
1312 WLog_ERR(TAG, "Unknown module %s, aborting", what);
1313 return -1;
1314 }
1315
1316 if (current == 0)
1317 {
1318 if (strcmp(what, "arm-transport") == 0)
1319 WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1320 }
1321
1322 const rdpSettings* settings = instance->context->settings;
1323 const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1324 if (!enabled)
1325 {
1326 WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1327 return -1;
1328 }
1329
1330 const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1331 const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1332 if (current >= max)
1333 {
1334 WLog_ERR(TAG,
1335 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1336 "tech support for help if this keeps happening.",
1337 what);
1338 return -1;
1339 }
1340
1341 WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1342 what, current + 1, max, delay);
1343 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1344}
1345
1346BOOL client_auto_reconnect(freerdp* instance)
1347{
1348 return client_auto_reconnect_ex(instance, nullptr);
1349}
1350
1351BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1352{
1353 BOOL retry = TRUE;
1354 UINT32 error = 0;
1355 UINT32 numRetries = 0;
1356 rdpSettings* settings = nullptr;
1357
1358 if (!instance)
1359 return FALSE;
1360
1361 WINPR_ASSERT(instance->context);
1362
1363 settings = instance->context->settings;
1364 WINPR_ASSERT(settings);
1365
1366 const UINT32 maxRetries =
1367 freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1368
1369 /* Only auto reconnect on network disconnects. */
1370 error = freerdp_error_info(instance);
1371 switch (error)
1372 {
1373 case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1374 /* A network disconnect was detected */
1375 WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1376 freerdp_get_error_info_string(error));
1377 break;
1378 case ERRINFO_SUCCESS:
1379 /* A network disconnect was detected */
1380 WLog_INFO(TAG, "Network disconnect!");
1381 break;
1382 default:
1383 WLog_DBG(TAG, "Other error: %s", freerdp_get_error_info_string(error));
1384 return FALSE;
1385 }
1386
1387 if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1388 {
1389 /* No auto-reconnect - just quit */
1390 WLog_DBG(TAG, "AutoReconnect not enabled, quitting.");
1391 return FALSE;
1392 }
1393
1394 const UINT err = freerdp_get_last_error(instance->context);
1395 switch (err)
1396 {
1397 case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
1398 case FREERDP_ERROR_CONNECT_CLIENT_REVOKED:
1399 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
1400 case FREERDP_ERROR_CONNECT_ACCESS_DENIED:
1401 case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
1402 case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
1403 case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
1404 case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
1405 WLog_WARN(TAG, "Connection aborted: credentials do not work [%s]",
1406 freerdp_get_last_error_name(err));
1407 return FALSE;
1408 case FREERDP_ERROR_CONNECT_CANCELLED:
1409 WLog_WARN(TAG, "Connection aborted by user");
1410 return FALSE;
1411 default:
1412 break;
1413 }
1414
1415 /* Perform an auto-reconnect. */
1416 while (retry)
1417 {
1418 /* Quit retrying if max retries has been exceeded */
1419 if ((maxRetries > 0) && (numRetries >= maxRetries))
1420 {
1421 WLog_DBG(TAG, "AutoReconnect retries exceeded.");
1422 return FALSE;
1423 }
1424
1425 /* Attempt the next reconnect */
1426 WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1427
1428 const SSIZE_T delay =
1429 IFCALLRESULT(5000, instance->RetryDialog, instance, "connection", numRetries, nullptr);
1430 if (delay < 0)
1431 return FALSE;
1432 numRetries++;
1433
1434 if (freerdp_reconnect(instance))
1435 return TRUE;
1436
1437 switch (freerdp_get_last_error(instance->context))
1438 {
1439 case FREERDP_ERROR_CONNECT_CANCELLED:
1440 WLog_WARN(TAG, "Autoreconnect aborted by user");
1441 return FALSE;
1442 default:
1443 break;
1444 }
1445 for (SSIZE_T x = 0; x < delay / 10; x++)
1446 {
1447 if (!IFCALLRESULT(TRUE, window_events, instance))
1448 {
1449 WLog_ERR(TAG, "window_events failed!");
1450 return FALSE;
1451 }
1452
1453 Sleep(10);
1454 }
1455 }
1456
1457 WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1458 return FALSE;
1459}
1460
1461int freerdp_client_common_stop(rdpContext* context)
1462{
1463 rdpClientContext* cctx = (rdpClientContext*)context;
1464 WINPR_ASSERT(cctx);
1465
1466 freerdp_abort_connect_context(&cctx->context);
1467
1468 if (cctx->thread)
1469 {
1470 (void)WaitForSingleObject(cctx->thread, INFINITE);
1471 (void)CloseHandle(cctx->thread);
1472 cctx->thread = nullptr;
1473 }
1474
1475 return 0;
1476}
1477
1478#if defined(CHANNEL_ENCOMSP_CLIENT)
1479BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1480{
1481 rdpClientContext* cctx = nullptr;
1482 BOOL state = 0;
1483
1484 if (!encomsp)
1485 return FALSE;
1486
1487 cctx = (rdpClientContext*)encomsp->custom;
1488
1489 state = cctx->controlToggle;
1490 cctx->controlToggle = !cctx->controlToggle;
1491 return freerdp_client_encomsp_set_control(encomsp, state);
1492}
1493
1494BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1495{
1496 ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = WINPR_C_ARRAY_INIT;
1497
1498 if (!encomsp)
1499 return FALSE;
1500
1501 pdu.ParticipantId = encomsp->participantId;
1502 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1503
1504 if (control)
1505 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1506
1507 const UINT rc = encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1508 return rc == CHANNEL_RC_OK;
1509}
1510
1511static UINT
1512client_encomsp_participant_created(EncomspClientContext* context,
1513 const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1514{
1515 rdpClientContext* cctx = nullptr;
1516 rdpSettings* settings = nullptr;
1517 BOOL request = 0;
1518
1519 if (!context || !context->custom || !participantCreated)
1520 return ERROR_INVALID_PARAMETER;
1521
1522 cctx = (rdpClientContext*)context->custom;
1523 WINPR_ASSERT(cctx);
1524
1525 settings = cctx->context.settings;
1526 WINPR_ASSERT(settings);
1527
1528 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1529 context->participantId = participantCreated->ParticipantId;
1530
1531 request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1532 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1533 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1534 {
1535 if (!freerdp_client_encomsp_set_control(context, TRUE))
1536 return ERROR_INTERNAL_ERROR;
1537
1538 /* if auto-request-control setting is enabled then only request control once upon connect,
1539 * otherwise it will auto request control again every time server turns off control which
1540 * is a bit annoying */
1541 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1542 return ERROR_INTERNAL_ERROR;
1543 }
1544
1545 return CHANNEL_RC_OK;
1546}
1547
1548static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1549{
1550 cctx->encomsp = encomsp;
1551 encomsp->custom = (void*)cctx;
1552 encomsp->ParticipantCreated = client_encomsp_participant_created;
1553}
1554
1555static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1556{
1557 if (encomsp)
1558 {
1559 encomsp->custom = nullptr;
1560 encomsp->ParticipantCreated = nullptr;
1561 }
1562
1563 if (cctx)
1564 cctx->encomsp = nullptr;
1565}
1566#endif
1567
1568void freerdp_client_OnChannelConnectedEventHandler(void* context,
1569 const ChannelConnectedEventArgs* e)
1570{
1571 rdpClientContext* cctx = (rdpClientContext*)context;
1572
1573 WINPR_ASSERT(cctx);
1574 WINPR_ASSERT(e);
1575
1576 if (0)
1577 {
1578 }
1579#if defined(CHANNEL_AINPUT_CLIENT)
1580 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1581 cctx->ainput = (AInputClientContext*)e->pInterface;
1582#endif
1583#if defined(CHANNEL_RDPEI_CLIENT)
1584 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1585 {
1586 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1587 }
1588#endif
1589#if defined(CHANNEL_RDPGFX_CLIENT)
1590 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1591 {
1592 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1593 }
1594#endif
1595#if defined(CHANNEL_GEOMETRY_CLIENT)
1596 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1597 {
1598 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1599 }
1600#endif
1601#if defined(CHANNEL_VIDEO_CLIENT)
1602 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1603 {
1604 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1605 }
1606 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1607 {
1608 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1609 }
1610#endif
1611#if defined(CHANNEL_ENCOMSP_CLIENT)
1612 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1613 {
1614 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1615 }
1616#endif
1617}
1618
1619void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1620 const ChannelDisconnectedEventArgs* e)
1621{
1622 rdpClientContext* cctx = (rdpClientContext*)context;
1623
1624 WINPR_ASSERT(cctx);
1625 WINPR_ASSERT(e);
1626
1627 if (0)
1628 {
1629 }
1630#if defined(CHANNEL_AINPUT_CLIENT)
1631 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1632 cctx->ainput = nullptr;
1633#endif
1634#if defined(CHANNEL_RDPEI_CLIENT)
1635 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1636 {
1637 cctx->rdpei = nullptr;
1638 }
1639#endif
1640#if defined(CHANNEL_RDPGFX_CLIENT)
1641 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1642 {
1643 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1644 }
1645#endif
1646#if defined(CHANNEL_GEOMETRY_CLIENT)
1647 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1648 {
1649 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1650 }
1651#endif
1652#if defined(CHANNEL_VIDEO_CLIENT)
1653 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1654 {
1655 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1656 }
1657 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1658 {
1659 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1660 }
1661#endif
1662#if defined(CHANNEL_ENCOMSP_CLIENT)
1663 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1664 {
1665 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1666 }
1667#endif
1668}
1669
1670BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1671{
1672 BOOL handled = FALSE;
1673
1674 WINPR_ASSERT(cctx);
1675
1676 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1677 if (state != CONNECTION_STATE_ACTIVE)
1678 return TRUE;
1679
1680#if defined(CHANNEL_AINPUT_CLIENT)
1681 if (cctx->ainput)
1682 {
1683 UINT rc = 0;
1684 UINT64 flags = 0;
1685 INT32 x = 0;
1686 INT32 y = 0;
1687 INT32 value = mflags & 0xFF;
1688
1689 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1690 value = -1 * (0x100 - value);
1691
1692 /* We have discrete steps, scale this so we can also support high
1693 * resolution wheels. */
1694 value *= 0x10000;
1695
1696 if (mflags & PTR_FLAGS_WHEEL)
1697 {
1698 flags |= AINPUT_FLAGS_WHEEL;
1699 y = value;
1700 }
1701
1702 if (mflags & PTR_FLAGS_HWHEEL)
1703 {
1704 flags |= AINPUT_FLAGS_WHEEL;
1705 x = value;
1706 }
1707
1708 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1709 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1710 if (rc == CHANNEL_RC_OK)
1711 handled = TRUE;
1712 }
1713#endif
1714
1715 if (!handled)
1716 return freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1717
1718 return TRUE;
1719}
1720
1721#if defined(CHANNEL_AINPUT_CLIENT)
1722static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1723{
1724 UINT rc = 0;
1725
1726 WINPR_ASSERT(cctx);
1727 WINPR_ASSERT(cctx->ainput);
1728 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1729
1730 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1731
1732 return rc == CHANNEL_RC_OK;
1733}
1734#endif
1735
1736static bool button_pressed(const rdpClientContext* cctx)
1737{
1738 WINPR_ASSERT(cctx);
1739 for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1740 {
1741 const BOOL cur = cctx->pressed_buttons[x];
1742 if (cur)
1743 return true;
1744 }
1745 return false;
1746}
1747
1748BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1749 INT32 y)
1750{
1751 BOOL handled = FALSE;
1752
1753 WINPR_ASSERT(cctx);
1754 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1755 if (state != CONNECTION_STATE_ACTIVE)
1756 return TRUE;
1757
1758 if (mflags & PTR_FLAGS_BUTTON1)
1759 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1760 if (mflags & PTR_FLAGS_BUTTON2)
1761 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1762 if (mflags & PTR_FLAGS_BUTTON3)
1763 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1764
1765 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1766 !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1767 {
1768 if (!button_pressed(cctx))
1769 return TRUE;
1770 }
1771
1772 const BOOL haveRelative =
1773 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1774 if (relative && haveRelative)
1775 {
1776 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1777 WINPR_ASSERTING_INT_CAST(int16_t, x),
1778 WINPR_ASSERTING_INT_CAST(int16_t, y));
1779 }
1780
1781#if defined(CHANNEL_AINPUT_CLIENT)
1782 if (cctx->ainput)
1783 {
1784 UINT64 flags = 0;
1785
1786 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1787 flags |= AINPUT_FLAGS_HAVE_REL;
1788
1789 if (relative)
1790 flags |= AINPUT_FLAGS_REL;
1791
1792 if (mflags & PTR_FLAGS_DOWN)
1793 flags |= AINPUT_FLAGS_DOWN;
1794 if (mflags & PTR_FLAGS_BUTTON1)
1795 flags |= AINPUT_FLAGS_BUTTON1;
1796 if (mflags & PTR_FLAGS_BUTTON2)
1797 flags |= AINPUT_FLAGS_BUTTON2;
1798 if (mflags & PTR_FLAGS_BUTTON3)
1799 flags |= AINPUT_FLAGS_BUTTON3;
1800 if (mflags & PTR_FLAGS_MOVE)
1801 flags |= AINPUT_FLAGS_MOVE;
1802 handled = ainput_send_diff_event(cctx, flags, x, y);
1803 }
1804#endif
1805
1806 if (!handled)
1807 {
1808 if (relative)
1809 {
1810 cctx->lastX += x;
1811 cctx->lastY += y;
1812 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1813 }
1814 else
1815 {
1816 cctx->lastX = x;
1817 cctx->lastY = y;
1818 }
1819 return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1820 (UINT16)cctx->lastY);
1821 }
1822 return TRUE;
1823}
1824
1825BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1826 INT32 x, INT32 y)
1827{
1828 BOOL handled = FALSE;
1829 WINPR_ASSERT(cctx);
1830
1831 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1832 if (state != CONNECTION_STATE_ACTIVE)
1833 return TRUE;
1834
1835 if (mflags & PTR_XFLAGS_BUTTON1)
1836 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1837 if (mflags & PTR_XFLAGS_BUTTON2)
1838 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1839
1840 const BOOL haveRelative =
1841 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1842 if (relative && haveRelative)
1843 {
1844 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1845 WINPR_ASSERTING_INT_CAST(int16_t, x),
1846 WINPR_ASSERTING_INT_CAST(int16_t, y));
1847 }
1848
1849#if defined(CHANNEL_AINPUT_CLIENT)
1850 if (cctx->ainput)
1851 {
1852 UINT64 flags = 0;
1853
1854 if (relative)
1855 flags |= AINPUT_FLAGS_REL;
1856 if (mflags & PTR_XFLAGS_DOWN)
1857 flags |= AINPUT_FLAGS_DOWN;
1858 if (mflags & PTR_XFLAGS_BUTTON1)
1859 flags |= AINPUT_XFLAGS_BUTTON1;
1860 if (mflags & PTR_XFLAGS_BUTTON2)
1861 flags |= AINPUT_XFLAGS_BUTTON2;
1862
1863 handled = ainput_send_diff_event(cctx, flags, x, y);
1864 }
1865#endif
1866
1867 if (!handled)
1868 {
1869 if (relative)
1870 {
1871 cctx->lastX += x;
1872 cctx->lastY += y;
1873 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1874 }
1875 else
1876 {
1877 cctx->lastX = x;
1878 cctx->lastY = y;
1879 }
1880 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1881 (UINT16)cctx->lastY);
1882 }
1883
1884 return TRUE;
1885}
1886
1887static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1888 const FreeRDP_TouchContact* contact)
1889{
1890 const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1891 const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1892 WINPR_ASSERT(contact);
1893 WINPR_ASSERT(contact->x <= UINT16_MAX);
1894 WINPR_ASSERT(contact->y <= UINT16_MAX);
1895
1896 switch (contact->count)
1897 {
1898 case 1:
1899 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1900 contact->x, contact->y);
1901 case 2:
1902 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1903 contact->x, contact->y);
1904 case 3:
1905 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1906 contact->x, contact->y);
1907 case 4:
1908 return freerdp_client_send_extended_button_event(
1909 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1910 case 5:
1911 return freerdp_client_send_extended_button_event(
1912 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1913 default:
1914 /* unmapped events, ignore */
1915 return TRUE;
1916 }
1917}
1918
1919static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1920{
1921 WINPR_ASSERT(cctx);
1922 WINPR_ASSERT(contact);
1923
1924#if defined(CHANNEL_RDPEI_CLIENT)
1925 RdpeiClientContext* rdpei = cctx->rdpei;
1926
1927 if (!rdpei)
1928 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1929
1930 int contactId = 0;
1931
1932 if (rdpei->TouchRawEvent)
1933 {
1934 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1935 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1936 ? CONTACT_DATA_PRESSURE_PRESENT
1937 : 0;
1938 // Ensure contact position is unchanged from "engaged" to "out of range" state
1939 const UINT rc1 =
1940 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1941 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1942 RDPINPUT_CONTACT_FLAG_INCONTACT,
1943 contactFlags, contact->pressure);
1944 if (rc1 != CHANNEL_RC_OK)
1945 return FALSE;
1946
1947 const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
1948 &contactId, flags, contactFlags, contact->pressure);
1949 if (rc2 != CHANNEL_RC_OK)
1950 return FALSE;
1951 }
1952 else
1953 {
1954 WINPR_ASSERT(rdpei->TouchEnd);
1955 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1956 if (rc != CHANNEL_RC_OK)
1957 return FALSE;
1958 }
1959 return TRUE;
1960#else
1961 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1962 "-DCHANNEL_RDPEI_CLIENT=ON");
1963 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1964#endif
1965}
1966
1967static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1968{
1969 WINPR_ASSERT(cctx);
1970 WINPR_ASSERT(contact);
1971
1972#if defined(CHANNEL_RDPEI_CLIENT)
1973 RdpeiClientContext* rdpei = cctx->rdpei;
1974
1975 // Emulate mouse click if touch is not possible, like in login screen
1976 if (!rdpei)
1977 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1978
1979 int contactId = 0;
1980
1981 if (rdpei->TouchRawEvent)
1982 {
1983 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1984 RDPINPUT_CONTACT_FLAG_INCONTACT;
1985 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1986 ? CONTACT_DATA_PRESSURE_PRESENT
1987 : 0;
1988 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1989 flags, contactFlags, contact->pressure);
1990 if (rc != CHANNEL_RC_OK)
1991 return FALSE;
1992 }
1993 else
1994 {
1995 WINPR_ASSERT(rdpei->TouchBegin);
1996 const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1997 if (rc != CHANNEL_RC_OK)
1998 return FALSE;
1999 }
2000
2001 return TRUE;
2002#else
2003 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2004 "-DCHANNEL_RDPEI_CLIENT=ON");
2005 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
2006#endif
2007}
2008
2009static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
2010 const FreeRDP_TouchContact* contact)
2011{
2012 const UINT16 flags = PTR_FLAGS_MOVE;
2013
2014 WINPR_ASSERT(contact);
2015 WINPR_ASSERT(contact->x <= UINT16_MAX);
2016 WINPR_ASSERT(contact->y <= UINT16_MAX);
2017 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
2018}
2019
2020static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2021{
2022 WINPR_ASSERT(cctx);
2023 WINPR_ASSERT(contact);
2024
2025#if defined(CHANNEL_RDPEI_CLIENT)
2026 RdpeiClientContext* rdpei = cctx->rdpei;
2027
2028 if (!rdpei)
2029 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2030
2031 int contactId = 0;
2032
2033 if (rdpei->TouchRawEvent)
2034 {
2035 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
2036 RDPINPUT_CONTACT_FLAG_INCONTACT;
2037 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2038 ? CONTACT_DATA_PRESSURE_PRESENT
2039 : 0;
2040 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2041 flags, contactFlags, contact->pressure);
2042 if (rc != CHANNEL_RC_OK)
2043 return FALSE;
2044 }
2045 else
2046 {
2047 WINPR_ASSERT(rdpei->TouchUpdate);
2048 const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2049 if (rc != CHANNEL_RC_OK)
2050 return FALSE;
2051 }
2052
2053 return TRUE;
2054#else
2055 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2056 "-DCHANNEL_RDPEI_CLIENT=ON");
2057 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2058#endif
2059}
2060
2061static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2062{
2063 WINPR_ASSERT(cctx);
2064 WINPR_ASSERT(contact);
2065
2066#if defined(CHANNEL_RDPEI_CLIENT)
2067 RdpeiClientContext* rdpei = cctx->rdpei;
2068
2069 if (!rdpei)
2070 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2071
2072 int contactId = 0;
2073
2074 if (rdpei->TouchRawEvent)
2075 {
2076 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2077 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2078 ? CONTACT_DATA_PRESSURE_PRESENT
2079 : 0;
2080 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2081 flags, contactFlags, contact->pressure);
2082 if (rc != CHANNEL_RC_OK)
2083 return FALSE;
2084 }
2085 else
2086 {
2087 WINPR_ASSERT(rdpei->TouchUpdate);
2088 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2089 if (rc != CHANNEL_RC_OK)
2090 return FALSE;
2091 }
2092
2093 return TRUE;
2094#else
2095 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2096 "-DCHANNEL_RDPEI_CLIENT=ON");
2097 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2098#endif
2099}
2100
2101static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2102 UINT32 pressure, INT32 x, INT32 y,
2103 FreeRDP_TouchContact* pcontact)
2104{
2105 WINPR_ASSERT(cctx);
2106 WINPR_ASSERT(pcontact);
2107
2108 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2109 {
2110 FreeRDP_TouchContact* contact = &cctx->contacts[i];
2111
2112 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2113 if (newcontact || (contact->id == touchId))
2114 {
2115 contact->id = touchId;
2116 contact->flags = flags;
2117 contact->pressure = pressure;
2118 contact->x = x;
2119 contact->y = y;
2120
2121 *pcontact = *contact;
2122
2123 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2124 if (resetcontact)
2125 {
2126 FreeRDP_TouchContact empty = WINPR_C_ARRAY_INIT;
2127 *contact = empty;
2128 }
2129 return TRUE;
2130 }
2131 }
2132
2133 return FALSE;
2134}
2135
2136BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2137 UINT32 pressure, INT32 x, INT32 y)
2138{
2139 const UINT32 mask =
2140 FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2141 WINPR_ASSERT(cctx);
2142
2143 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2144 if (state != CONNECTION_STATE_ACTIVE)
2145 return TRUE;
2146
2147 FreeRDP_TouchContact contact = WINPR_C_ARRAY_INIT;
2148
2149 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2150 return FALSE;
2151
2152 switch (flags & mask)
2153 {
2154 case FREERDP_TOUCH_DOWN:
2155 return freerdp_handle_touch_down(cctx, &contact);
2156 case FREERDP_TOUCH_UP:
2157 return freerdp_handle_touch_up(cctx, &contact);
2158 case FREERDP_TOUCH_MOTION:
2159 return freerdp_handle_touch_motion(cctx, &contact);
2160 case FREERDP_TOUCH_CANCEL:
2161 return freerdp_handle_touch_cancel(cctx, &contact);
2162 default:
2163 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %" PRIu32 ", ignoring", flags);
2164 return FALSE;
2165 }
2166}
2167
2168BOOL freerdp_client_load_channels(freerdp* instance)
2169{
2170 WINPR_ASSERT(instance);
2171 WINPR_ASSERT(instance->context);
2172
2173 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2174 {
2175 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2176 return FALSE;
2177 }
2178 return TRUE;
2179}
2180
2181int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2182{
2183 const char* str_data = freerdp_get_logon_error_info_data(data);
2184 const char* str_type = freerdp_get_logon_error_info_type(type);
2185
2186 if (!instance || !instance->context)
2187 return -1;
2188
2189 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2190 return 1;
2191}
2192
2193static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2194 size_t* pos)
2195{
2196 WINPR_ASSERT(cctx);
2197
2198 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2199 {
2200 FreeRDP_PenDevice* pen = &cctx->pens[i];
2201 if (deviceid == pen->deviceid)
2202 {
2203 if (pos)
2204 *pos = i;
2205 return pen;
2206 }
2207 }
2208 return nullptr;
2209}
2210
2211static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2212 double pressure)
2213{
2214 static const INT32 null_deviceid = 0;
2215
2216 WINPR_ASSERT(cctx);
2217 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2218 if (freerdp_client_is_pen(cctx, deviceid))
2219 {
2220 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2221 return FALSE;
2222 }
2223
2224 size_t pos = 0;
2225 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2226 if (pen)
2227 {
2228 const FreeRDP_PenDevice empty = WINPR_C_ARRAY_INIT;
2229 *pen = empty;
2230
2231 pen->deviceid = deviceid;
2232 pen->max_pressure = pressure;
2233 pen->flags = flags;
2234
2235 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2236 return TRUE;
2237 }
2238
2239 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2240 return TRUE;
2241}
2242
2243BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2244{
2245 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2246 if (state != CONNECTION_STATE_ACTIVE)
2247 return TRUE;
2248
2249#if defined(CHANNEL_RDPEI_CLIENT)
2250 if ((flags & FREERDP_PEN_REGISTER) != 0)
2251 {
2252 va_list args = WINPR_C_ARRAY_INIT;
2253
2254 va_start(args, deviceid);
2255 double pressure = va_arg(args, double);
2256 va_end(args);
2257 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2258 }
2259 size_t pos = 0;
2260 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2261 if (!pen)
2262 {
2263 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2264 return FALSE;
2265 }
2266
2267 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2268 UINT32 penFlags =
2269 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2270
2271 RdpeiClientContext* rdpei = cctx->rdpei;
2272 WINPR_ASSERT(rdpei);
2273
2274 UINT32 normalizedpressure = 1024;
2275 INT32 x = 0;
2276 INT32 y = 0;
2277 UINT16 rotation = 0;
2278 INT16 tiltX = 0;
2279 INT16 tiltY = 0;
2280 va_list args = WINPR_C_ARRAY_INIT;
2281 va_start(args, deviceid);
2282
2283 x = va_arg(args, INT32);
2284 y = va_arg(args, INT32);
2285 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2286 {
2287 const double pressure = va_arg(args, double);
2288 const double np = (pressure * 1024.0) / pen->max_pressure;
2289 normalizedpressure = (UINT32)lround(np);
2290 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2291 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2292 }
2293 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2294 {
2295 const unsigned arg = va_arg(args, unsigned);
2296 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2297 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2298 }
2299 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2300 {
2301 const int arg = va_arg(args, int);
2302 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2303 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2304 }
2305 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2306 {
2307 const int arg = va_arg(args, int);
2308 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2309 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2310 }
2311 va_end(args);
2312
2313 if ((flags & FREERDP_PEN_PRESS) != 0)
2314 {
2315 // Ensure that only one button is pressed
2316 if (pen->pressed)
2317 flags = FREERDP_PEN_MOTION |
2318 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2319 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2320 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2321 }
2322 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2323 {
2324 if (!pen->pressed ||
2325 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2326 flags = FREERDP_PEN_MOTION |
2327 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2328 else
2329 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2330 }
2331
2332 flags |= pen->flags;
2333 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2334 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2335 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2336 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2337
2338 pen->last_x = x;
2339 pen->last_y = y;
2340 if ((flags & FREERDP_PEN_PRESS) != 0)
2341 {
2342 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2343 pen->hovering = FALSE;
2344 pen->pressed = TRUE;
2345
2346 WINPR_ASSERT(rdpei->PenBegin);
2347 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2348 normalizedpressure, rotation, tiltX, tiltY);
2349 return rc == CHANNEL_RC_OK;
2350 }
2351 else if ((flags & FREERDP_PEN_MOTION) != 0)
2352 {
2353 UINT rc = ERROR_INTERNAL_ERROR;
2354 if (pen->pressed)
2355 {
2356 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2357
2358 // TODO: what if no rotation is supported but tilt is?
2359 WINPR_ASSERT(rdpei->PenUpdate);
2360 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2361 rotation, tiltX, tiltY);
2362 }
2363 else if (pen->hovering)
2364 {
2365 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2366
2367 WINPR_ASSERT(rdpei->PenHoverUpdate);
2368 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2369 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2370 }
2371 else
2372 {
2373 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2374 pen->hovering = TRUE;
2375
2376 WINPR_ASSERT(rdpei->PenHoverBegin);
2377 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2378 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2379 }
2380 return rc == CHANNEL_RC_OK;
2381 }
2382 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2383 {
2384 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2385 pen->pressed = FALSE;
2386 pen->hovering = TRUE;
2387
2388 WINPR_ASSERT(rdpei->PenUpdate);
2389 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2390 normalizedpressure, rotation, tiltX, tiltY);
2391 if (rc != CHANNEL_RC_OK)
2392 return FALSE;
2393 WINPR_ASSERT(rdpei->PenEnd);
2394 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2395 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2396 return re == CHANNEL_RC_OK;
2397 }
2398
2399 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2400#else
2401 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2402 "-DCHANNEL_RDPEI_CLIENT=ON");
2403#endif
2404
2405 return FALSE;
2406}
2407
2408BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2409{
2410 WINPR_ASSERT(cctx);
2411
2412 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2413 if (state != CONNECTION_STATE_ACTIVE)
2414 return TRUE;
2415
2416#if defined(CHANNEL_RDPEI_CLIENT)
2417 RdpeiClientContext* rdpei = cctx->rdpei;
2418
2419 if (!rdpei)
2420 return FALSE;
2421
2422 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2423 {
2424 FreeRDP_PenDevice* pen = &cctx->pens[i];
2425 if (pen->hovering)
2426 {
2427 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2428 pen->hovering = FALSE;
2429 const UINT rc =
2430 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2431 if (rc != CHANNEL_RC_OK)
2432 return FALSE;
2433 }
2434 }
2435 return TRUE;
2436#else
2437 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2438 "-DCHANNEL_RDPEI_CLIENT=ON");
2439 return FALSE;
2440#endif
2441}
2442
2443BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2444{
2445 WINPR_ASSERT(cctx);
2446
2447 if (deviceid == 0)
2448 return FALSE;
2449
2450 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2451 {
2452 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2453 if (pen->deviceid == deviceid)
2454 return TRUE;
2455 }
2456
2457 return FALSE;
2458}
2459
2460BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* cctx)
2461{
2462 WINPR_ASSERT(cctx);
2463
2464 const rdpSettings* settings = cctx->context.settings;
2465 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2466 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2467 BOOL ainput = FALSE;
2468#if defined(CHANNEL_AINPUT_CLIENT)
2469 ainput = cctx->ainput != nullptr;
2470#endif
2471
2472 return useRelative && (haveRelative || ainput);
2473}
2474
2475#if defined(WITH_AAD)
2476WINPR_ATTR_MALLOC(free, 1)
2477WINPR_ATTR_NODISCARD
2478static char* get_redirect_uri(const rdpSettings* settings)
2479{
2480 char* redirect_uri = nullptr;
2481 const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2482 if (cli)
2483 {
2484 const char* redirect_fmt =
2485 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2486 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2487 const char* tenantid = "common";
2488 if (useTenant)
2489 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2490
2491 if (tenantid && redirect_fmt)
2492 {
2493 const char* url =
2494 freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2495
2496 size_t redirect_len = 0;
2497 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2498 }
2499 }
2500 else
2501 {
2502 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2503 const char* redirect_fmt =
2504 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2505
2506 size_t redirect_len = 0;
2507 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2508 }
2509 return redirect_uri;
2510}
2511
2512static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2513{
2514 const rdpSettings* settings = cctx->context.settings;
2515 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2516 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2517 AAD_WELLKNOWN_authorization_endpoint);
2518 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2519
2520 if (!client_id || !ep || !scope)
2521 return nullptr;
2522
2523 char* redirect_uri = get_redirect_uri(settings);
2524 if (!redirect_uri)
2525 return nullptr;
2526
2527 char* url = nullptr;
2528 size_t urllen = 0;
2529 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2530 client_id, scope, redirect_uri);
2531 free(redirect_uri);
2532 return url;
2533}
2534
2535static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2536{
2537 const rdpSettings* settings = cctx->context.settings;
2538 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2539 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2540 AAD_WELLKNOWN_authorization_endpoint);
2541 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2542
2543 if (!client_id || !ep || !scope)
2544 return nullptr;
2545
2546 char* redirect_uri = get_redirect_uri(settings);
2547 if (!redirect_uri)
2548 return nullptr;
2549
2550 char* url = nullptr;
2551 size_t urllen = 0;
2552
2553 const char* code = va_arg(ap, const char*);
2554 winpr_asprintf(&url, &urllen,
2555 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2556 code, client_id, scope, redirect_uri);
2557 free(redirect_uri);
2558 return url;
2559}
2560
2561static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2562{
2563 const rdpSettings* settings = cctx->context.settings;
2564 char* url = nullptr;
2565 size_t urllen = 0;
2566 char* redirect_uri = get_redirect_uri(settings);
2567
2568 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2569 if (!client_id || !redirect_uri)
2570 goto cleanup;
2571
2572 {
2573 const char* scope = va_arg(ap, const char*);
2574 if (!scope)
2575 goto cleanup;
2576
2577 {
2578 const char* ep = freerdp_utils_aad_get_wellknown_string(
2579 &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2580 winpr_asprintf(&url, &urllen,
2581 "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2582 client_id, scope, redirect_uri);
2583 }
2584 }
2585
2586cleanup:
2587 free(redirect_uri);
2588 return url;
2589}
2590
2591static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2592{
2593 const rdpSettings* settings = cctx->context.settings;
2594 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2595 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2596 AAD_WELLKNOWN_authorization_endpoint);
2597 const char* scope = va_arg(ap, const char*);
2598 const char* code = va_arg(ap, const char*);
2599 const char* req_cnf = va_arg(ap, const char*);
2600
2601 if (!client_id || !ep || !scope || !code || !req_cnf)
2602 return nullptr;
2603
2604 char* redirect_uri = get_redirect_uri(settings);
2605 if (!redirect_uri)
2606 return nullptr;
2607
2608 char* url = nullptr;
2609 size_t urllen = 0;
2610
2611 winpr_asprintf(
2612 &url, &urllen,
2613 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2614 code, client_id, scope, redirect_uri, req_cnf);
2615 free(redirect_uri);
2616 return url;
2617}
2618#endif
2619
2620char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2621{
2622 WINPR_ASSERT(cctx);
2623 char* str = nullptr;
2624
2625 va_list ap = WINPR_C_ARRAY_INIT;
2626 va_start(ap, type);
2627 switch (type)
2628 {
2629#if defined(WITH_AAD)
2630 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2631 str = aad_auth_request(cctx, ap);
2632 break;
2633 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2634 str = aad_token_request(cctx, ap);
2635 break;
2636 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2637 str = avd_auth_request(cctx, ap);
2638 break;
2639 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2640 str = avd_token_request(cctx, ap);
2641 break;
2642#endif
2643 default:
2644 break;
2645 }
2646 va_end(ap);
2647 return str;
2648}
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 UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
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.