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