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