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