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