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