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 if (answer == EOF)
766 return 0;
767 return 1;
768
769 case 't':
770 case 'T':
771 answer = freerdp_interruptible_getc(instance->context, stdin);
772 if (answer == EOF)
773 return 0;
774 return 2;
775
776 case 'n':
777 case 'N':
778 answer = freerdp_interruptible_getc(instance->context, stdin);
779 if (answer == EOF)
780 return 0;
781 return 0;
782
783 default:
784 break;
785 }
786
787 printf("\n");
788 }
789}
790
804#if defined(WITH_FREERDP_DEPRECATED)
805DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name, const char* subject,
806 const char* issuer, const char* fingerprint, BOOL host_mismatch)
807{
808 WINPR_UNUSED(common_name);
809 WINPR_UNUSED(host_mismatch);
810
811 printf("WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
812 printf("Certificate details:\n");
813 printf("\tSubject: %s\n", subject);
814 printf("\tIssuer: %s\n", issuer);
815 printf("\tThumbprint: %s\n", fingerprint);
816 printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
817 "the CA certificate in your certificate store, or the certificate has expired.\n"
818 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
819 return client_cli_accept_certificate(instance);
820}
821#endif
822
823static char* client_cli_pem_cert(const char* pem)
824{
825 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
826 if (!cert)
827 return nullptr;
828
829 char* fp = freerdp_certificate_get_fingerprint(cert);
830 char* start = freerdp_certificate_get_validity(cert, TRUE);
831 char* end = freerdp_certificate_get_validity(cert, FALSE);
832 freerdp_certificate_free(cert);
833
834 char* str = nullptr;
835 size_t slen = 0;
836 winpr_asprintf(&str, &slen,
837 "\tValid from: %s\n"
838 "\tValid to: %s\n"
839 "\tThumbprint: %s\n",
840 start, end, fp);
841 free(fp);
842 free(start);
843 free(end);
844 return str;
845}
846
862DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
863 const char* common_name, const char* subject,
864 const char* issuer, const char* fingerprint, DWORD flags)
865{
866 const char* type = "RDP-Server";
867
868 WINPR_ASSERT(instance);
869 WINPR_ASSERT(instance->context);
870 WINPR_ASSERT(instance->context->settings);
871
872 if (flags & VERIFY_CERT_FLAG_GATEWAY)
873 type = "RDP-Gateway";
874
875 if (flags & VERIFY_CERT_FLAG_REDIRECT)
876 type = "RDP-Redirect";
877
878 printf("Certificate details for %s:%" PRIu16 " (%s):\n", host, port, type);
879 printf("\tCommon Name: %s\n", common_name);
880 printf("\tSubject: %s\n", subject);
881 printf("\tIssuer: %s\n", issuer);
882 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
883 * FreeRDP_CertificateCallbackPreferPEM to TRUE
884 */
885 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
886 {
887 char* str = client_cli_pem_cert(fingerprint);
888 printf("%s", str);
889 free(str);
890 }
891 else
892 printf("\tThumbprint: %s\n", fingerprint);
893
894 printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
895 "the CA certificate in your certificate store, or the certificate has expired.\n"
896 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
897 return client_cli_accept_certificate(instance);
898}
899
915#if defined(WITH_FREERDP_DEPRECATED)
916DWORD client_cli_verify_changed_certificate(freerdp* instance, const char* common_name,
917 const char* subject, const char* issuer,
918 const char* fingerprint, const char* old_subject,
919 const char* old_issuer, const char* old_fingerprint)
920{
921 WINPR_UNUSED(common_name);
922
923 printf("WARNING: This callback is deprecated, migrate to "
924 "client_cli_verify_changed_certificate_ex\n");
925 printf("!!! Certificate has changed !!!\n");
926 printf("\n");
927 printf("New Certificate details:\n");
928 printf("\tSubject: %s\n", subject);
929 printf("\tIssuer: %s\n", issuer);
930 printf("\tThumbprint: %s\n", fingerprint);
931 printf("\n");
932 printf("Old Certificate details:\n");
933 printf("\tSubject: %s\n", old_subject);
934 printf("\tIssuer: %s\n", old_issuer);
935 printf("\tThumbprint: %s\n", old_fingerprint);
936 printf("\n");
937 printf("The above X.509 certificate does not match the certificate used for previous "
938 "connections.\n"
939 "This may indicate that the certificate has been tampered with.\n"
940 "Please contact the administrator of the RDP server and clarify.\n");
941 return client_cli_accept_certificate(instance);
942}
943#endif
944
964DWORD client_cli_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
965 const char* common_name, const char* subject,
966 const char* issuer, const char* fingerprint,
967 const char* old_subject, const char* old_issuer,
968 const char* old_fingerprint, DWORD flags)
969{
970 const char* type = "RDP-Server";
971
972 WINPR_ASSERT(instance);
973 WINPR_ASSERT(instance->context);
974 WINPR_ASSERT(instance->context->settings);
975
976 if (flags & VERIFY_CERT_FLAG_GATEWAY)
977 type = "RDP-Gateway";
978
979 if (flags & VERIFY_CERT_FLAG_REDIRECT)
980 type = "RDP-Redirect";
981
982 printf("!!!Certificate for %s:%" PRIu16 " (%s) has changed!!!\n", host, port, type);
983 printf("\n");
984 printf("New Certificate details:\n");
985 printf("\tCommon Name: %s\n", common_name);
986 printf("\tSubject: %s\n", subject);
987 printf("\tIssuer: %s\n", issuer);
988 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
989 * FreeRDP_CertificateCallbackPreferPEM to TRUE
990 */
991 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
992 {
993 char* str = client_cli_pem_cert(fingerprint);
994 printf("%s", str);
995 free(str);
996 }
997 else
998 printf("\tThumbprint: %s\n", fingerprint);
999 printf("\n");
1000 printf("Old Certificate details:\n");
1001 printf("\tSubject: %s\n", old_subject);
1002 printf("\tIssuer: %s\n", old_issuer);
1003 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
1004 * FreeRDP_CertificateCallbackPreferPEM to TRUE
1005 */
1006 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
1007 {
1008 char* str = client_cli_pem_cert(old_fingerprint);
1009 printf("%s", str);
1010 free(str);
1011 }
1012 else
1013 printf("\tThumbprint: %s\n", old_fingerprint);
1014 printf("\n");
1015 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
1016 {
1017 printf("\tA matching entry with legacy SHA1 was found in local known_hosts2 store.\n");
1018 printf("\tIf you just upgraded from a FreeRDP version before 2.0 this is expected.\n");
1019 printf("\tThe hashing algorithm has been upgraded from SHA1 to SHA256.\n");
1020 printf("\tAll manually accepted certificates must be reconfirmed!\n");
1021 printf("\n");
1022 }
1023 printf("The above X.509 certificate does not match the certificate used for previous "
1024 "connections.\n"
1025 "This may indicate that the certificate has been tampered with.\n"
1026 "Please contact the administrator of the RDP server and clarify.\n");
1027 return client_cli_accept_certificate(instance);
1028}
1029
1030BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1031 BOOL isConsentMandatory, size_t length,
1032 const WCHAR* message)
1033{
1034 const char* msgType = (type == GATEWAY_MESSAGE_CONSENT) ? "Consent message" : "Service message";
1035
1036 WINPR_ASSERT(instance);
1037 WINPR_ASSERT(instance->context);
1038 WINPR_ASSERT(instance->context->settings);
1039
1040 if (!isDisplayMandatory && !isConsentMandatory)
1041 return TRUE;
1042
1043 printf("%s:\n", msgType);
1044#if defined(WIN32)
1045 printf("%.*S\n", (int)length, message);
1046#else
1047 {
1048 LPSTR msg = ConvertWCharNToUtf8Alloc(message, length / sizeof(WCHAR), nullptr);
1049 if (!msg)
1050 {
1051 printf("Failed to convert message!\n");
1052 return FALSE;
1053 }
1054 printf("%s\n", msg);
1055 free(msg);
1056 }
1057#endif
1058
1059 while (isConsentMandatory)
1060 {
1061 printf("I understand and agree to the terms of this policy (Y/N) \n");
1062 (void)fflush(stdout);
1063 const int answer = freerdp_interruptible_getc(instance->context, stdin);
1064
1065 if ((answer == EOF) || feof(stdin))
1066 {
1067 printf("\nError: Could not read answer from stdin.\n");
1068 return FALSE;
1069 }
1070
1071 const int confirm = freerdp_interruptible_getc(instance->context, stdin);
1072 switch (answer)
1073 {
1074 case 'y':
1075 case 'Y':
1076 if (confirm == EOF)
1077 return FALSE;
1078 return TRUE;
1079
1080 case 'n':
1081 case 'N':
1082 return FALSE;
1083
1084 default:
1085 break;
1086 }
1087
1088 printf("\n");
1089 }
1090
1091 return TRUE;
1092}
1093
1094static const char* extract_authorization_code(char* url)
1095{
1096 WINPR_ASSERT(url);
1097
1098 for (char* p = strchr(url, '?'); p++ != nullptr; p = strchr(p, '&'))
1099 {
1100 if (strncmp(p, "code=", 5) != 0)
1101 continue;
1102
1103 char* end = nullptr;
1104 p += 5;
1105
1106 end = strchr(p, '&');
1107 if (end)
1108 *end = '\0';
1109
1110 return p;
1111 }
1112
1113 return nullptr;
1114}
1115
1116#if defined(WITH_AAD)
1117static BOOL client_cli_get_rdsaad_access_token(freerdp* instance, const char* scope,
1118 const char* req_cnf, char** token)
1119{
1120 WINPR_ASSERT(instance);
1121 WINPR_ASSERT(instance->context);
1122
1123 size_t size = 0;
1124 char* url = nullptr;
1125 char* token_request = nullptr;
1126
1127 WINPR_ASSERT(scope);
1128 WINPR_ASSERT(req_cnf);
1129 WINPR_ASSERT(token);
1130
1131 BOOL rc = FALSE;
1132 *token = nullptr;
1133
1134 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1135 FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1136
1137 printf("Browse to: %s\n", request);
1138 free(request);
1139 printf("Paste redirect URL here: \n");
1140
1141 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1142 goto cleanup;
1143
1144 {
1145 const char* code = extract_authorization_code(url);
1146 if (!code)
1147 goto cleanup;
1148 token_request =
1149 freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1150 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1151 }
1152 if (!token_request)
1153 goto cleanup;
1154
1155 rc = client_common_get_access_token(instance, token_request, token);
1156
1157cleanup:
1158 free(token_request);
1159 free(url);
1160 return rc && (*token != nullptr);
1161}
1162
1163static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1164{
1165 WINPR_ASSERT(instance);
1166 WINPR_ASSERT(instance->context);
1167
1168 size_t size = 0;
1169 char* url = nullptr;
1170 char* token_request = nullptr;
1171
1172 WINPR_ASSERT(token);
1173
1174 BOOL rc = FALSE;
1175
1176 *token = nullptr;
1177
1178 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1179 FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1180 if (!request)
1181 return FALSE;
1182 printf("Browse to: %s\n", request);
1183 free(request);
1184 printf("Paste redirect URL here: \n");
1185
1186 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1187 goto cleanup;
1188
1189 {
1190 const char* code = extract_authorization_code(url);
1191 if (!code)
1192 goto cleanup;
1193 token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1194 FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1195 }
1196
1197 if (!token_request)
1198 goto cleanup;
1199
1200 rc = client_common_get_access_token(instance, token_request, token);
1201
1202cleanup:
1203 free(token_request);
1204 free(url);
1205 return rc && (*token != nullptr);
1206}
1207#endif
1208
1209BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1210 size_t count, ...)
1211{
1212 WINPR_ASSERT(instance);
1213 WINPR_ASSERT(token);
1214
1215#if !defined(WITH_AAD)
1216 WLog_ERR(TAG, "Build does not support AAD authentication");
1217 return FALSE;
1218#else
1219 BOOL rc = FALSE;
1220 WINPR_ASSERT(instance->context);
1221 const BOOL saved =
1222 freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1223 if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1224 TRUE))
1225 return FALSE;
1226
1227 switch (tokenType)
1228 {
1229 case ACCESS_TOKEN_TYPE_AAD:
1230 {
1231 if (count < 2)
1232 {
1233 WLog_ERR(TAG,
1234 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1235 ", aborting",
1236 count);
1237 }
1238 else
1239 {
1240 if (count > 2)
1241 WLog_WARN(
1242 TAG,
1243 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1244 ", ignoring",
1245 count);
1246 va_list ap = WINPR_C_ARRAY_INIT;
1247 va_start(ap, count);
1248 const char* scope = va_arg(ap, const char*);
1249 const char* req_cnf = va_arg(ap, const char*);
1250 rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1251 va_end(ap);
1252 }
1253 }
1254 break;
1255 case ACCESS_TOKEN_TYPE_AVD:
1256 if (count != 0)
1257 WLog_WARN(TAG,
1258 "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1259 ", ignoring",
1260 count);
1261 rc = client_cli_get_avd_access_token(instance, token);
1262 break;
1263 default:
1264 WLog_ERR(TAG, "Unexpected value for AccessTokenType [%u], aborting", tokenType);
1265 break;
1266 }
1267
1268 if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1269 saved))
1270 return FALSE;
1271 return rc;
1272#endif
1273}
1274
1275BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1276{
1277#ifdef WITH_AAD
1278 WINPR_ASSERT(request);
1279 WINPR_ASSERT(token);
1280
1281 BOOL ret = FALSE;
1282 long resp_code = 0;
1283 BYTE* response = nullptr;
1284 size_t response_length = 0;
1285
1286 wLog* log = WLog_Get(TAG);
1287
1288 const char* token_ep =
1289 freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1290 if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1291 {
1292 WLog_ERR(TAG, "access token request failed");
1293 return FALSE;
1294 }
1295
1296 if (resp_code != HTTP_STATUS_OK)
1297 {
1298 char buffer[64] = WINPR_C_ARRAY_INIT;
1299
1300 WLog_Print(log, WLOG_ERROR,
1301 "Server unwilling to provide access token; returned status code %s",
1302 freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1303 if (response_length > 0)
1304 WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1305 goto cleanup;
1306 }
1307
1308 *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1309 if (*token)
1310 ret = TRUE;
1311
1312cleanup:
1313 free(response);
1314 return ret;
1315#else
1316 return FALSE;
1317#endif
1318}
1319
1320SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1321 void* userarg)
1322{
1323 WINPR_UNUSED(instance);
1324 WINPR_ASSERT(instance->context);
1325 WINPR_UNUSED(userarg);
1326 WINPR_ASSERT(instance);
1327 WINPR_ASSERT(what);
1328
1329 if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1330 {
1331 WLog_ERR(TAG, "Unknown module %s, aborting", what);
1332 return -1;
1333 }
1334
1335 if (current == 0)
1336 {
1337 if (strcmp(what, "arm-transport") == 0)
1338 WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1339 }
1340
1341 const rdpSettings* settings = instance->context->settings;
1342 const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1343 if (!enabled)
1344 {
1345 WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1346 return -1;
1347 }
1348
1349 const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1350 const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1351 if (current >= max)
1352 {
1353 WLog_ERR(TAG,
1354 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1355 "tech support for help if this keeps happening.",
1356 what);
1357 return -1;
1358 }
1359
1360 WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1361 what, current + 1, max, delay);
1362 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1363}
1364
1365BOOL client_auto_reconnect(freerdp* instance)
1366{
1367 return client_auto_reconnect_ex(instance, nullptr);
1368}
1369
1370BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1371{
1372 BOOL retry = TRUE;
1373 UINT32 error = 0;
1374 UINT32 numRetries = 0;
1375 rdpSettings* settings = nullptr;
1376
1377 if (!instance)
1378 return FALSE;
1379
1380 WINPR_ASSERT(instance->context);
1381
1382 settings = instance->context->settings;
1383 WINPR_ASSERT(settings);
1384
1385 const UINT32 maxRetries =
1386 freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1387
1388 /* Only auto reconnect on network disconnects. */
1389 error = freerdp_error_info(instance);
1390 switch (error)
1391 {
1392 case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1393 /* A network disconnect was detected */
1394 WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1395 freerdp_get_error_info_string(error));
1396 break;
1397 case ERRINFO_SUCCESS:
1398 /* A network disconnect was detected */
1399 WLog_INFO(TAG, "Network disconnect!");
1400 break;
1401 default:
1402 WLog_DBG(TAG, "Other error: %s", freerdp_get_error_info_string(error));
1403 return FALSE;
1404 }
1405
1406 if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1407 {
1408 /* No auto-reconnect - just quit */
1409 WLog_DBG(TAG, "AutoReconnect not enabled, quitting.");
1410 return FALSE;
1411 }
1412
1413 const UINT err = freerdp_get_last_error(instance->context);
1414 switch (err)
1415 {
1416 case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
1417 case FREERDP_ERROR_CONNECT_CLIENT_REVOKED:
1418 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
1419 case FREERDP_ERROR_CONNECT_ACCESS_DENIED:
1420 case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
1421 case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
1422 case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
1423 case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
1424 WLog_WARN(TAG, "Connection aborted: credentials do not work [%s]",
1425 freerdp_get_last_error_name(err));
1426 return FALSE;
1427 case FREERDP_ERROR_CONNECT_CANCELLED:
1428 WLog_WARN(TAG, "Connection aborted by user");
1429 return FALSE;
1430 default:
1431 break;
1432 }
1433
1434 /* Perform an auto-reconnect. */
1435 while (retry)
1436 {
1437 /* Quit retrying if max retries has been exceeded */
1438 if ((maxRetries > 0) && (numRetries >= maxRetries))
1439 {
1440 WLog_DBG(TAG, "AutoReconnect retries exceeded.");
1441 return FALSE;
1442 }
1443
1444 /* Attempt the next reconnect */
1445 WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1446
1447 const SSIZE_T delay =
1448 IFCALLRESULT(5000, instance->RetryDialog, instance, "connection", numRetries, nullptr);
1449 if (delay < 0)
1450 return FALSE;
1451 numRetries++;
1452
1453 if (freerdp_reconnect(instance))
1454 return TRUE;
1455
1456 switch (freerdp_get_last_error(instance->context))
1457 {
1458 case FREERDP_ERROR_CONNECT_CANCELLED:
1459 WLog_WARN(TAG, "Autoreconnect aborted by user");
1460 return FALSE;
1461 default:
1462 break;
1463 }
1464 for (SSIZE_T x = 0; x < delay / 10; x++)
1465 {
1466 if (!IFCALLRESULT(TRUE, window_events, instance))
1467 {
1468 WLog_ERR(TAG, "window_events failed!");
1469 return FALSE;
1470 }
1471
1472 Sleep(10);
1473 }
1474 }
1475
1476 WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1477 return FALSE;
1478}
1479
1480int freerdp_client_common_stop(rdpContext* context)
1481{
1482 rdpClientContext* cctx = (rdpClientContext*)context;
1483 WINPR_ASSERT(cctx);
1484
1485 freerdp_abort_connect_context(&cctx->context);
1486
1487 if (cctx->thread)
1488 {
1489 (void)WaitForSingleObject(cctx->thread, INFINITE);
1490 (void)CloseHandle(cctx->thread);
1491 cctx->thread = nullptr;
1492 }
1493
1494 return 0;
1495}
1496
1497#if defined(CHANNEL_ENCOMSP_CLIENT)
1498BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1499{
1500 rdpClientContext* cctx = nullptr;
1501 BOOL state = 0;
1502
1503 if (!encomsp)
1504 return FALSE;
1505
1506 cctx = (rdpClientContext*)encomsp->custom;
1507
1508 state = cctx->controlToggle;
1509 cctx->controlToggle = !cctx->controlToggle;
1510 return freerdp_client_encomsp_set_control(encomsp, state);
1511}
1512
1513BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1514{
1515 ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = WINPR_C_ARRAY_INIT;
1516
1517 if (!encomsp)
1518 return FALSE;
1519
1520 pdu.ParticipantId = encomsp->participantId;
1521 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1522
1523 if (control)
1524 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1525
1526 const UINT rc = encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1527 return rc == CHANNEL_RC_OK;
1528}
1529
1530static UINT
1531client_encomsp_participant_created(EncomspClientContext* context,
1532 const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1533{
1534 rdpClientContext* cctx = nullptr;
1535 rdpSettings* settings = nullptr;
1536 BOOL request = 0;
1537
1538 if (!context || !context->custom || !participantCreated)
1539 return ERROR_INVALID_PARAMETER;
1540
1541 cctx = (rdpClientContext*)context->custom;
1542 WINPR_ASSERT(cctx);
1543
1544 settings = cctx->context.settings;
1545 WINPR_ASSERT(settings);
1546
1547 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1548 context->participantId = participantCreated->ParticipantId;
1549
1550 request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1551 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1552 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1553 {
1554 if (!freerdp_client_encomsp_set_control(context, TRUE))
1555 return ERROR_INTERNAL_ERROR;
1556
1557 /* if auto-request-control setting is enabled then only request control once upon connect,
1558 * otherwise it will auto request control again every time server turns off control which
1559 * is a bit annoying */
1560 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1561 return ERROR_INTERNAL_ERROR;
1562 }
1563
1564 return CHANNEL_RC_OK;
1565}
1566
1567static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1568{
1569 cctx->encomsp = encomsp;
1570 encomsp->custom = (void*)cctx;
1571 encomsp->ParticipantCreated = client_encomsp_participant_created;
1572}
1573
1574static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1575{
1576 if (encomsp)
1577 {
1578 encomsp->custom = nullptr;
1579 encomsp->ParticipantCreated = nullptr;
1580 }
1581
1582 if (cctx)
1583 cctx->encomsp = nullptr;
1584}
1585#endif
1586
1587void freerdp_client_OnChannelConnectedEventHandler(void* context,
1588 const ChannelConnectedEventArgs* e)
1589{
1590 rdpClientContext* cctx = (rdpClientContext*)context;
1591
1592 WINPR_ASSERT(cctx);
1593 WINPR_ASSERT(e);
1594
1595 if (0)
1596 {
1597 }
1598#if defined(CHANNEL_AINPUT_CLIENT)
1599 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1600 cctx->ainput = (AInputClientContext*)e->pInterface;
1601#endif
1602#if defined(CHANNEL_RDPEI_CLIENT)
1603 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1604 {
1605 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1606 }
1607#endif
1608#if defined(CHANNEL_RDPGFX_CLIENT)
1609 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1610 {
1611 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1612 }
1613#endif
1614#if defined(CHANNEL_GEOMETRY_CLIENT)
1615 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1616 {
1617 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1618 }
1619#endif
1620#if defined(CHANNEL_VIDEO_CLIENT)
1621 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1622 {
1623 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1624 }
1625 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1626 {
1627 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1628 }
1629#endif
1630#if defined(CHANNEL_ENCOMSP_CLIENT)
1631 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1632 {
1633 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1634 }
1635#endif
1636}
1637
1638void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1639 const ChannelDisconnectedEventArgs* e)
1640{
1641 rdpClientContext* cctx = (rdpClientContext*)context;
1642
1643 WINPR_ASSERT(cctx);
1644 WINPR_ASSERT(e);
1645
1646 if (0)
1647 {
1648 }
1649#if defined(CHANNEL_AINPUT_CLIENT)
1650 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1651 cctx->ainput = nullptr;
1652#endif
1653#if defined(CHANNEL_RDPEI_CLIENT)
1654 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1655 {
1656 cctx->rdpei = nullptr;
1657 }
1658#endif
1659#if defined(CHANNEL_RDPGFX_CLIENT)
1660 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1661 {
1662 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1663 }
1664#endif
1665#if defined(CHANNEL_GEOMETRY_CLIENT)
1666 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1667 {
1668 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1669 }
1670#endif
1671#if defined(CHANNEL_VIDEO_CLIENT)
1672 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1673 {
1674 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1675 }
1676 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1677 {
1678 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1679 }
1680#endif
1681#if defined(CHANNEL_ENCOMSP_CLIENT)
1682 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1683 {
1684 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1685 }
1686#endif
1687}
1688
1689BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1690{
1691 BOOL handled = FALSE;
1692
1693 WINPR_ASSERT(cctx);
1694
1695 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1696 if (state != CONNECTION_STATE_ACTIVE)
1697 return TRUE;
1698
1699#if defined(CHANNEL_AINPUT_CLIENT)
1700 if (cctx->ainput)
1701 {
1702 UINT rc = 0;
1703 UINT64 flags = 0;
1704 INT32 x = 0;
1705 INT32 y = 0;
1706 INT32 value = mflags & 0xFF;
1707
1708 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1709 value = -1 * (0x100 - value);
1710
1711 /* We have discrete steps, scale this so we can also support high
1712 * resolution wheels. */
1713 value *= 0x10000;
1714
1715 if (mflags & PTR_FLAGS_WHEEL)
1716 {
1717 flags |= AINPUT_FLAGS_WHEEL;
1718 y = value;
1719 }
1720
1721 if (mflags & PTR_FLAGS_HWHEEL)
1722 {
1723 flags |= AINPUT_FLAGS_WHEEL;
1724 x = value;
1725 }
1726
1727 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1728 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1729 if (rc == CHANNEL_RC_OK)
1730 handled = TRUE;
1731 }
1732#endif
1733
1734 if (!handled)
1735 return freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1736
1737 return TRUE;
1738}
1739
1740#if defined(CHANNEL_AINPUT_CLIENT)
1741static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1742{
1743 UINT rc = 0;
1744
1745 WINPR_ASSERT(cctx);
1746 WINPR_ASSERT(cctx->ainput);
1747 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1748
1749 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1750
1751 return rc == CHANNEL_RC_OK;
1752}
1753#endif
1754
1755static bool button_pressed(const rdpClientContext* cctx)
1756{
1757 WINPR_ASSERT(cctx);
1758 for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1759 {
1760 const BOOL cur = cctx->pressed_buttons[x];
1761 if (cur)
1762 return true;
1763 }
1764 return false;
1765}
1766
1767BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1768 INT32 y)
1769{
1770 BOOL handled = FALSE;
1771
1772 WINPR_ASSERT(cctx);
1773 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1774 if (state != CONNECTION_STATE_ACTIVE)
1775 return TRUE;
1776
1777 if (mflags & PTR_FLAGS_BUTTON1)
1778 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1779 if (mflags & PTR_FLAGS_BUTTON2)
1780 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1781 if (mflags & PTR_FLAGS_BUTTON3)
1782 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1783
1784 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1785 !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1786 {
1787 if (!button_pressed(cctx))
1788 return TRUE;
1789 }
1790
1791 const BOOL haveRelative =
1792 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1793 if (relative && haveRelative)
1794 {
1795 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1796 WINPR_ASSERTING_INT_CAST(int16_t, x),
1797 WINPR_ASSERTING_INT_CAST(int16_t, y));
1798 }
1799
1800#if defined(CHANNEL_AINPUT_CLIENT)
1801 if (cctx->ainput)
1802 {
1803 UINT64 flags = 0;
1804
1805 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1806 flags |= AINPUT_FLAGS_HAVE_REL;
1807
1808 if (relative)
1809 flags |= AINPUT_FLAGS_REL;
1810
1811 if (mflags & PTR_FLAGS_DOWN)
1812 flags |= AINPUT_FLAGS_DOWN;
1813 if (mflags & PTR_FLAGS_BUTTON1)
1814 flags |= AINPUT_FLAGS_BUTTON1;
1815 if (mflags & PTR_FLAGS_BUTTON2)
1816 flags |= AINPUT_FLAGS_BUTTON2;
1817 if (mflags & PTR_FLAGS_BUTTON3)
1818 flags |= AINPUT_FLAGS_BUTTON3;
1819 if (mflags & PTR_FLAGS_MOVE)
1820 flags |= AINPUT_FLAGS_MOVE;
1821 handled = ainput_send_diff_event(cctx, flags, x, y);
1822 }
1823#endif
1824
1825 if (!handled)
1826 {
1827 if (relative)
1828 {
1829 cctx->lastX += x;
1830 cctx->lastY += y;
1831 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1832 }
1833 else
1834 {
1835 cctx->lastX = x;
1836 cctx->lastY = y;
1837 }
1838 return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1839 (UINT16)cctx->lastY);
1840 }
1841 return TRUE;
1842}
1843
1844BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1845 INT32 x, INT32 y)
1846{
1847 BOOL handled = FALSE;
1848 WINPR_ASSERT(cctx);
1849
1850 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1851 if (state != CONNECTION_STATE_ACTIVE)
1852 return TRUE;
1853
1854 if (mflags & PTR_XFLAGS_BUTTON1)
1855 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1856 if (mflags & PTR_XFLAGS_BUTTON2)
1857 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1858
1859 const BOOL haveRelative =
1860 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1861 if (relative && haveRelative)
1862 {
1863 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1864 WINPR_ASSERTING_INT_CAST(int16_t, x),
1865 WINPR_ASSERTING_INT_CAST(int16_t, y));
1866 }
1867
1868#if defined(CHANNEL_AINPUT_CLIENT)
1869 if (cctx->ainput)
1870 {
1871 UINT64 flags = 0;
1872
1873 if (relative)
1874 flags |= AINPUT_FLAGS_REL;
1875 if (mflags & PTR_XFLAGS_DOWN)
1876 flags |= AINPUT_FLAGS_DOWN;
1877 if (mflags & PTR_XFLAGS_BUTTON1)
1878 flags |= AINPUT_XFLAGS_BUTTON1;
1879 if (mflags & PTR_XFLAGS_BUTTON2)
1880 flags |= AINPUT_XFLAGS_BUTTON2;
1881
1882 handled = ainput_send_diff_event(cctx, flags, x, y);
1883 }
1884#endif
1885
1886 if (!handled)
1887 {
1888 if (relative)
1889 {
1890 cctx->lastX += x;
1891 cctx->lastY += y;
1892 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1893 }
1894 else
1895 {
1896 cctx->lastX = x;
1897 cctx->lastY = y;
1898 }
1899 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1900 (UINT16)cctx->lastY);
1901 }
1902
1903 return TRUE;
1904}
1905
1906static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1907 const FreeRDP_TouchContact* contact)
1908{
1909 const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1910 const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1911 WINPR_ASSERT(contact);
1912 WINPR_ASSERT(contact->x <= UINT16_MAX);
1913 WINPR_ASSERT(contact->y <= UINT16_MAX);
1914
1915 switch (contact->count)
1916 {
1917 case 1:
1918 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1919 contact->x, contact->y);
1920 case 2:
1921 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1922 contact->x, contact->y);
1923 case 3:
1924 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1925 contact->x, contact->y);
1926 case 4:
1927 return freerdp_client_send_extended_button_event(
1928 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1929 case 5:
1930 return freerdp_client_send_extended_button_event(
1931 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1932 default:
1933 /* unmapped events, ignore */
1934 return TRUE;
1935 }
1936}
1937
1938static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1939{
1940 WINPR_ASSERT(cctx);
1941 WINPR_ASSERT(contact);
1942
1943#if defined(CHANNEL_RDPEI_CLIENT)
1944 RdpeiClientContext* rdpei = cctx->rdpei;
1945
1946 if (!rdpei)
1947 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1948
1949 int contactId = 0;
1950
1951 if (rdpei->TouchRawEvent)
1952 {
1953 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1954 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1955 ? CONTACT_DATA_PRESSURE_PRESENT
1956 : 0;
1957 // Ensure contact position is unchanged from "engaged" to "out of range" state
1958 const UINT rc1 =
1959 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1960 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1961 RDPINPUT_CONTACT_FLAG_INCONTACT,
1962 contactFlags, contact->pressure);
1963 if (rc1 != CHANNEL_RC_OK)
1964 return FALSE;
1965
1966 const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
1967 &contactId, flags, contactFlags, contact->pressure);
1968 if (rc2 != CHANNEL_RC_OK)
1969 return FALSE;
1970 }
1971 else
1972 {
1973 WINPR_ASSERT(rdpei->TouchEnd);
1974 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1975 if (rc != CHANNEL_RC_OK)
1976 return FALSE;
1977 }
1978 return TRUE;
1979#else
1980 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1981 "-DCHANNEL_RDPEI_CLIENT=ON");
1982 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1983#endif
1984}
1985
1986static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1987{
1988 WINPR_ASSERT(cctx);
1989 WINPR_ASSERT(contact);
1990
1991#if defined(CHANNEL_RDPEI_CLIENT)
1992 RdpeiClientContext* rdpei = cctx->rdpei;
1993
1994 // Emulate mouse click if touch is not possible, like in login screen
1995 if (!rdpei)
1996 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1997
1998 int contactId = 0;
1999
2000 if (rdpei->TouchRawEvent)
2001 {
2002 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
2003 RDPINPUT_CONTACT_FLAG_INCONTACT;
2004 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2005 ? CONTACT_DATA_PRESSURE_PRESENT
2006 : 0;
2007 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2008 flags, contactFlags, contact->pressure);
2009 if (rc != CHANNEL_RC_OK)
2010 return FALSE;
2011 }
2012 else
2013 {
2014 WINPR_ASSERT(rdpei->TouchBegin);
2015 const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
2016 if (rc != CHANNEL_RC_OK)
2017 return FALSE;
2018 }
2019
2020 return TRUE;
2021#else
2022 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2023 "-DCHANNEL_RDPEI_CLIENT=ON");
2024 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
2025#endif
2026}
2027
2028static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
2029 const FreeRDP_TouchContact* contact)
2030{
2031 const UINT16 flags = PTR_FLAGS_MOVE;
2032
2033 WINPR_ASSERT(contact);
2034 WINPR_ASSERT(contact->x <= UINT16_MAX);
2035 WINPR_ASSERT(contact->y <= UINT16_MAX);
2036 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
2037}
2038
2039static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2040{
2041 WINPR_ASSERT(cctx);
2042 WINPR_ASSERT(contact);
2043
2044#if defined(CHANNEL_RDPEI_CLIENT)
2045 RdpeiClientContext* rdpei = cctx->rdpei;
2046
2047 if (!rdpei)
2048 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2049
2050 int contactId = 0;
2051
2052 if (rdpei->TouchRawEvent)
2053 {
2054 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
2055 RDPINPUT_CONTACT_FLAG_INCONTACT;
2056 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2057 ? CONTACT_DATA_PRESSURE_PRESENT
2058 : 0;
2059 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2060 flags, contactFlags, contact->pressure);
2061 if (rc != CHANNEL_RC_OK)
2062 return FALSE;
2063 }
2064 else
2065 {
2066 WINPR_ASSERT(rdpei->TouchUpdate);
2067 const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2068 if (rc != CHANNEL_RC_OK)
2069 return FALSE;
2070 }
2071
2072 return TRUE;
2073#else
2074 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2075 "-DCHANNEL_RDPEI_CLIENT=ON");
2076 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2077#endif
2078}
2079
2080static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2081{
2082 WINPR_ASSERT(cctx);
2083 WINPR_ASSERT(contact);
2084
2085#if defined(CHANNEL_RDPEI_CLIENT)
2086 RdpeiClientContext* rdpei = cctx->rdpei;
2087
2088 if (!rdpei)
2089 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2090
2091 int contactId = 0;
2092
2093 if (rdpei->TouchRawEvent)
2094 {
2095 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2096 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2097 ? CONTACT_DATA_PRESSURE_PRESENT
2098 : 0;
2099 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2100 flags, contactFlags, contact->pressure);
2101 if (rc != CHANNEL_RC_OK)
2102 return FALSE;
2103 }
2104 else
2105 {
2106 WINPR_ASSERT(rdpei->TouchUpdate);
2107 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2108 if (rc != CHANNEL_RC_OK)
2109 return FALSE;
2110 }
2111
2112 return TRUE;
2113#else
2114 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2115 "-DCHANNEL_RDPEI_CLIENT=ON");
2116 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2117#endif
2118}
2119
2120static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2121 UINT32 pressure, INT32 x, INT32 y,
2122 FreeRDP_TouchContact* pcontact)
2123{
2124 WINPR_ASSERT(cctx);
2125 WINPR_ASSERT(pcontact);
2126
2127 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2128 {
2129 FreeRDP_TouchContact* contact = &cctx->contacts[i];
2130
2131 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2132 if (newcontact || (contact->id == touchId))
2133 {
2134 contact->id = touchId;
2135 contact->flags = flags;
2136 contact->pressure = pressure;
2137 contact->x = x;
2138 contact->y = y;
2139
2140 *pcontact = *contact;
2141
2142 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2143 if (resetcontact)
2144 {
2145 FreeRDP_TouchContact empty = WINPR_C_ARRAY_INIT;
2146 *contact = empty;
2147 }
2148 return TRUE;
2149 }
2150 }
2151
2152 return FALSE;
2153}
2154
2155BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2156 UINT32 pressure, INT32 x, INT32 y)
2157{
2158 const UINT32 mask =
2159 FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2160 WINPR_ASSERT(cctx);
2161
2162 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2163 if (state != CONNECTION_STATE_ACTIVE)
2164 return TRUE;
2165
2166 FreeRDP_TouchContact contact = WINPR_C_ARRAY_INIT;
2167
2168 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2169 return FALSE;
2170
2171 switch (flags & mask)
2172 {
2173 case FREERDP_TOUCH_DOWN:
2174 return freerdp_handle_touch_down(cctx, &contact);
2175 case FREERDP_TOUCH_UP:
2176 return freerdp_handle_touch_up(cctx, &contact);
2177 case FREERDP_TOUCH_MOTION:
2178 return freerdp_handle_touch_motion(cctx, &contact);
2179 case FREERDP_TOUCH_CANCEL:
2180 return freerdp_handle_touch_cancel(cctx, &contact);
2181 default:
2182 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %" PRIu32 ", ignoring", flags);
2183 return FALSE;
2184 }
2185}
2186
2187BOOL freerdp_client_load_channels(freerdp* instance)
2188{
2189 WINPR_ASSERT(instance);
2190 WINPR_ASSERT(instance->context);
2191
2192 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2193 {
2194 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2195 return FALSE;
2196 }
2197 return TRUE;
2198}
2199
2200int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2201{
2202 const char* str_data = freerdp_get_logon_error_info_data(data);
2203 const char* str_type = freerdp_get_logon_error_info_type(type);
2204
2205 if (!instance || !instance->context)
2206 return -1;
2207
2208 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2209 return 1;
2210}
2211
2212static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2213 size_t* pos)
2214{
2215 WINPR_ASSERT(cctx);
2216
2217 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2218 {
2219 FreeRDP_PenDevice* pen = &cctx->pens[i];
2220 if (deviceid == pen->deviceid)
2221 {
2222 if (pos)
2223 *pos = i;
2224 return pen;
2225 }
2226 }
2227 return nullptr;
2228}
2229
2230static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2231 double pressure)
2232{
2233 static const INT32 null_deviceid = 0;
2234
2235 WINPR_ASSERT(cctx);
2236 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2237 if (freerdp_client_is_pen(cctx, deviceid))
2238 {
2239 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2240 return FALSE;
2241 }
2242
2243 size_t pos = 0;
2244 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2245 if (pen)
2246 {
2247 const FreeRDP_PenDevice empty = WINPR_C_ARRAY_INIT;
2248 *pen = empty;
2249
2250 pen->deviceid = deviceid;
2251 pen->max_pressure = pressure;
2252 pen->flags = flags;
2253
2254 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2255 return TRUE;
2256 }
2257
2258 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2259 return TRUE;
2260}
2261
2262BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2263{
2264 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2265 if (state != CONNECTION_STATE_ACTIVE)
2266 return TRUE;
2267
2268#if defined(CHANNEL_RDPEI_CLIENT)
2269 if ((flags & FREERDP_PEN_REGISTER) != 0)
2270 {
2271 va_list args = WINPR_C_ARRAY_INIT;
2272
2273 va_start(args, deviceid);
2274 double pressure = va_arg(args, double);
2275 va_end(args);
2276 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2277 }
2278 size_t pos = 0;
2279 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2280 if (!pen)
2281 {
2282 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2283 return FALSE;
2284 }
2285
2286 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2287 UINT32 penFlags =
2288 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2289
2290 RdpeiClientContext* rdpei = cctx->rdpei;
2291 WINPR_ASSERT(rdpei);
2292
2293 UINT32 normalizedpressure = 1024;
2294 INT32 x = 0;
2295 INT32 y = 0;
2296 UINT16 rotation = 0;
2297 INT16 tiltX = 0;
2298 INT16 tiltY = 0;
2299 va_list args = WINPR_C_ARRAY_INIT;
2300 va_start(args, deviceid);
2301
2302 x = va_arg(args, INT32);
2303 y = va_arg(args, INT32);
2304 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2305 {
2306 const double pressure = va_arg(args, double);
2307 const double np = (pressure * 1024.0) / pen->max_pressure;
2308 normalizedpressure = (UINT32)lround(np);
2309 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2310 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2311 }
2312 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2313 {
2314 const unsigned arg = va_arg(args, unsigned);
2315 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2316 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2317 }
2318 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2319 {
2320 const int arg = va_arg(args, int);
2321 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2322 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2323 }
2324 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2325 {
2326 const int arg = va_arg(args, int);
2327 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2328 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2329 }
2330 va_end(args);
2331
2332 if ((flags & FREERDP_PEN_PRESS) != 0)
2333 {
2334 // Ensure that only one button is pressed
2335 if (pen->pressed)
2336 flags = FREERDP_PEN_MOTION |
2337 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2338 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2339 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2340 }
2341 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2342 {
2343 if (!pen->pressed ||
2344 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2345 flags = FREERDP_PEN_MOTION |
2346 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2347 else
2348 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2349 }
2350
2351 flags |= pen->flags;
2352 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2353 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2354 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2355 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2356
2357 pen->last_x = x;
2358 pen->last_y = y;
2359 if ((flags & FREERDP_PEN_PRESS) != 0)
2360 {
2361 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2362 pen->hovering = FALSE;
2363 pen->pressed = TRUE;
2364
2365 WINPR_ASSERT(rdpei->PenBegin);
2366 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2367 normalizedpressure, rotation, tiltX, tiltY);
2368 return rc == CHANNEL_RC_OK;
2369 }
2370 else if ((flags & FREERDP_PEN_MOTION) != 0)
2371 {
2372 UINT rc = ERROR_INTERNAL_ERROR;
2373 if (pen->pressed)
2374 {
2375 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2376
2377 // TODO: what if no rotation is supported but tilt is?
2378 WINPR_ASSERT(rdpei->PenUpdate);
2379 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2380 rotation, tiltX, tiltY);
2381 }
2382 else if (pen->hovering)
2383 {
2384 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2385
2386 WINPR_ASSERT(rdpei->PenHoverUpdate);
2387 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2388 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2389 }
2390 else
2391 {
2392 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2393 pen->hovering = TRUE;
2394
2395 WINPR_ASSERT(rdpei->PenHoverBegin);
2396 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2397 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2398 }
2399 return rc == CHANNEL_RC_OK;
2400 }
2401 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2402 {
2403 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2404 pen->pressed = FALSE;
2405 pen->hovering = TRUE;
2406
2407 WINPR_ASSERT(rdpei->PenUpdate);
2408 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2409 normalizedpressure, rotation, tiltX, tiltY);
2410 if (rc != CHANNEL_RC_OK)
2411 return FALSE;
2412 WINPR_ASSERT(rdpei->PenEnd);
2413 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2414 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2415 return re == CHANNEL_RC_OK;
2416 }
2417
2418 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2419#else
2420 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2421 "-DCHANNEL_RDPEI_CLIENT=ON");
2422#endif
2423
2424 return FALSE;
2425}
2426
2427BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2428{
2429 WINPR_ASSERT(cctx);
2430
2431 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2432 if (state != CONNECTION_STATE_ACTIVE)
2433 return TRUE;
2434
2435#if defined(CHANNEL_RDPEI_CLIENT)
2436 RdpeiClientContext* rdpei = cctx->rdpei;
2437
2438 if (!rdpei)
2439 return FALSE;
2440
2441 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2442 {
2443 FreeRDP_PenDevice* pen = &cctx->pens[i];
2444 if (pen->hovering)
2445 {
2446 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2447 pen->hovering = FALSE;
2448 const UINT rc =
2449 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2450 if (rc != CHANNEL_RC_OK)
2451 return FALSE;
2452 }
2453 }
2454 return TRUE;
2455#else
2456 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2457 "-DCHANNEL_RDPEI_CLIENT=ON");
2458 return FALSE;
2459#endif
2460}
2461
2462BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2463{
2464 WINPR_ASSERT(cctx);
2465
2466 if (deviceid == 0)
2467 return FALSE;
2468
2469 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2470 {
2471 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2472 if (pen->deviceid == deviceid)
2473 return TRUE;
2474 }
2475
2476 return FALSE;
2477}
2478
2479BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* cctx)
2480{
2481 WINPR_ASSERT(cctx);
2482
2483 const rdpSettings* settings = cctx->context.settings;
2484 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2485 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2486 BOOL ainput = FALSE;
2487#if defined(CHANNEL_AINPUT_CLIENT)
2488 ainput = cctx->ainput != nullptr;
2489#endif
2490
2491 return useRelative && (haveRelative || ainput);
2492}
2493
2494#if defined(WITH_AAD)
2495WINPR_ATTR_MALLOC(free, 1)
2496static char* get_redirect_uri(const rdpSettings* settings)
2497{
2498 char* redirect_uri = nullptr;
2499 const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2500 if (cli)
2501 {
2502 const char* redirect_fmt =
2503 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2504 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2505 const char* tenantid = "common";
2506 if (useTenant)
2507 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2508
2509 if (tenantid && redirect_fmt)
2510 {
2511 const char* url =
2512 freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2513
2514 size_t redirect_len = 0;
2515 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2516 }
2517 }
2518 else
2519 {
2520 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2521 const char* redirect_fmt =
2522 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2523
2524 size_t redirect_len = 0;
2525 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2526 }
2527 return redirect_uri;
2528}
2529
2530static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2531{
2532 const rdpSettings* settings = cctx->context.settings;
2533 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2534 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2535 AAD_WELLKNOWN_authorization_endpoint);
2536 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2537
2538 if (!client_id || !ep || !scope)
2539 return nullptr;
2540
2541 char* redirect_uri = get_redirect_uri(settings);
2542 if (!redirect_uri)
2543 return nullptr;
2544
2545 char* url = nullptr;
2546 size_t urllen = 0;
2547 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2548 client_id, scope, redirect_uri);
2549 free(redirect_uri);
2550 return url;
2551}
2552
2553static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2554{
2555 const rdpSettings* settings = cctx->context.settings;
2556 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2557 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2558 AAD_WELLKNOWN_authorization_endpoint);
2559 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2560
2561 if (!client_id || !ep || !scope)
2562 return nullptr;
2563
2564 char* redirect_uri = get_redirect_uri(settings);
2565 if (!redirect_uri)
2566 return nullptr;
2567
2568 char* url = nullptr;
2569 size_t urllen = 0;
2570
2571 const char* code = va_arg(ap, const char*);
2572 winpr_asprintf(&url, &urllen,
2573 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2574 code, client_id, scope, redirect_uri);
2575 free(redirect_uri);
2576 return url;
2577}
2578
2579static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2580{
2581 const rdpSettings* settings = cctx->context.settings;
2582 char* url = nullptr;
2583 size_t urllen = 0;
2584 char* redirect_uri = get_redirect_uri(settings);
2585
2586 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2587 if (!client_id || !redirect_uri)
2588 goto cleanup;
2589
2590 {
2591 const char* scope = va_arg(ap, const char*);
2592 if (!scope)
2593 goto cleanup;
2594
2595 {
2596 const char* ep = freerdp_utils_aad_get_wellknown_string(
2597 &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2598 winpr_asprintf(&url, &urllen,
2599 "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2600 client_id, scope, redirect_uri);
2601 }
2602 }
2603
2604cleanup:
2605 free(redirect_uri);
2606 return url;
2607}
2608
2609static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2610{
2611 const rdpSettings* settings = cctx->context.settings;
2612 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2613 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2614 AAD_WELLKNOWN_authorization_endpoint);
2615 const char* scope = va_arg(ap, const char*);
2616 const char* code = va_arg(ap, const char*);
2617 const char* req_cnf = va_arg(ap, const char*);
2618
2619 if (!client_id || !ep || !scope || !code || !req_cnf)
2620 return nullptr;
2621
2622 char* redirect_uri = get_redirect_uri(settings);
2623 if (!redirect_uri)
2624 return nullptr;
2625
2626 char* url = nullptr;
2627 size_t urllen = 0;
2628
2629 winpr_asprintf(
2630 &url, &urllen,
2631 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2632 code, client_id, scope, redirect_uri, req_cnf);
2633 free(redirect_uri);
2634 return url;
2635}
2636#endif
2637
2638char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2639{
2640 WINPR_ASSERT(cctx);
2641 char* str = nullptr;
2642
2643 va_list ap = WINPR_C_ARRAY_INIT;
2644 va_start(ap, type);
2645 switch (type)
2646 {
2647#if defined(WITH_AAD)
2648 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2649 str = aad_auth_request(cctx, ap);
2650 break;
2651 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2652 str = aad_token_request(cctx, ap);
2653 break;
2654 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2655 str = avd_auth_request(cctx, ap);
2656 break;
2657 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2658 str = avd_token_request(cctx, ap);
2659 break;
2660#endif
2661 default:
2662 break;
2663 }
2664 va_end(ap);
2665 return str;
2666}
2667
2668BOOL client_common_save_session_info(WINPR_ATTR_UNUSED rdpContext* context, UINT32 type,
2669 const void* data)
2670{
2671 char buffer[128] = WINPR_C_ARRAY_INIT;
2672 WLog_INFO(TAG, "%s [%s]", freerdp_session_logon_type_str(type),
2673 freerdp_session_logon_type_data_str(type, data, buffer, sizeof(buffer)));
2674 return TRUE;
2675}
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.