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_context(instance->context, token_ep, request, &resp_code, &response,
1391 &response_length))
1392 {
1393 WLog_ERR(TAG, "access token request failed");
1394 return FALSE;
1395 }
1396
1397 if (resp_code != HTTP_STATUS_OK)
1398 {
1399 char buffer[64] = WINPR_C_ARRAY_INIT;
1400
1401 WLog_Print(log, WLOG_ERROR,
1402 "Server unwilling to provide access token; returned status code %s",
1403 freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1404 if (response_length > 0)
1405 WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1406 goto cleanup;
1407 }
1408
1409 *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1410 if (*token)
1411 ret = TRUE;
1412
1413cleanup:
1414 free(response);
1415 return ret;
1416#else
1417 WLog_ERR(TAG, "Build does not support AAD authentication");
1418 return FALSE;
1419#endif
1420}
1421
1422SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1423 void* userarg)
1424{
1425 WINPR_UNUSED(instance);
1426 WINPR_ASSERT(instance->context);
1427 WINPR_UNUSED(userarg);
1428 WINPR_ASSERT(instance);
1429 WINPR_ASSERT(what);
1430
1431 if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1432 {
1433 WLog_ERR(TAG, "Unknown module %s, aborting", what);
1434 return -1;
1435 }
1436
1437 if (current == 0)
1438 {
1439 if (strcmp(what, "arm-transport") == 0)
1440 WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1441 }
1442
1443 const rdpSettings* settings = instance->context->settings;
1444 const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1445 if (!enabled)
1446 {
1447 WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1448 return -1;
1449 }
1450
1451 const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1452 const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1453 if (current >= max)
1454 {
1455 WLog_ERR(TAG,
1456 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1457 "tech support for help if this keeps happening.",
1458 what);
1459 return -1;
1460 }
1461
1462 WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1463 what, current + 1, max, delay);
1464 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1465}
1466
1467BOOL client_auto_reconnect(freerdp* instance)
1468{
1469 return client_auto_reconnect_ex(instance, nullptr);
1470}
1471
1472WINPR_ATTR_NODISCARD
1473static BOOL client_auto_reconnect_credentials_rejected(UINT32 error)
1474{
1475 switch (error)
1476 {
1477 case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
1478 case FREERDP_ERROR_CONNECT_CLIENT_REVOKED:
1479 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
1480 case FREERDP_ERROR_CONNECT_ACCESS_DENIED:
1481 case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
1482 case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
1483 case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
1484 case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
1485 return TRUE;
1486 default:
1487 return FALSE;
1488 }
1489}
1490
1491WINPR_ATTR_NODISCARD
1492static BOOL client_auto_reconnect_do(freerdp* instance, BOOL retry, UINT32 numRetries,
1493 UINT32 maxRetries)
1494{
1495 WINPR_ASSERT(instance);
1496
1497 /* Perform an auto-reconnect. */
1498 while (retry)
1499 {
1500 /* Quit retrying if max retries has been exceeded */
1501 if ((maxRetries > 0) && (numRetries >= maxRetries))
1502 {
1503 WLog_DBG(TAG, "AutoReconnect retries exceeded.");
1504 return FALSE;
1505 }
1506
1507 /* Attempt the next reconnect */
1508 WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1509
1510 const SSIZE_T delay =
1511 IFCALLRESULT(5000, instance->RetryDialog, instance, "connection", numRetries, nullptr);
1512 if (delay < 0)
1513 return FALSE;
1514 numRetries++;
1515
1516 if (freerdp_reconnect(instance))
1517 return TRUE;
1518
1519 /* The server can also reject the credentials on a reconnect attempt, e.g. after a
1520 * password change. Retrying them only locks the account sooner. */
1521 const UINT err = freerdp_get_last_error(instance->context);
1522 if (client_auto_reconnect_credentials_rejected(err))
1523 {
1524 WLog_WARN(TAG, "Autoreconnect aborted: credentials do not work [%s]",
1525 freerdp_get_last_error_name(err));
1526 return FALSE;
1527 }
1528 if (err == FREERDP_ERROR_CONNECT_CANCELLED)
1529 {
1530 WLog_WARN(TAG, "Autoreconnect aborted by user");
1531 return FALSE;
1532 }
1533
1534 for (SSIZE_T x = 0; x < delay / 10; x++)
1535 Sleep(10);
1536 }
1537
1538 WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1539 return FALSE;
1540}
1541
1542WINPR_ATTR_NODISCARD
1543static DWORD WINAPI handle_window_events(void* parg)
1544{
1545 handle_window_events_t* arg = parg;
1546 WINPR_ASSERT(arg);
1547
1548 while (arg->running)
1549 {
1550 WINPR_ASSERT(arg->window_events);
1551 if (!arg->window_events(arg->instance))
1552 {
1553 WLog_ERR(TAG, "window_events failed!");
1554 }
1555 Sleep(10);
1556 }
1557 return 0;
1558}
1559
1560BOOL client_auto_reconnect_ex(freerdp* instance, window_events_fkt_t window_events)
1561{
1562 BOOL retry = TRUE;
1563 UINT32 error = 0;
1564 UINT32 numRetries = 0;
1565 rdpSettings* settings = nullptr;
1566
1567 if (!instance)
1568 return FALSE;
1569
1570 WINPR_ASSERT(instance->context);
1571
1572 settings = instance->context->settings;
1573 WINPR_ASSERT(settings);
1574
1575 const UINT32 maxRetries =
1576 freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1577
1578 /* Only auto reconnect on network disconnects. */
1579 error = freerdp_error_info(instance);
1580 switch (error)
1581 {
1582 case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1583 /* A network disconnect was detected */
1584 WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1585 freerdp_get_error_info_string(error));
1586 break;
1587 case ERRINFO_SUCCESS:
1588 /* A network disconnect was detected */
1589 WLog_INFO(TAG, "Network disconnect!");
1590 break;
1591 default:
1592 WLog_DBG(TAG, "Other error: %s", freerdp_get_error_info_string(error));
1593 return FALSE;
1594 }
1595
1596 if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1597 {
1598 /* No auto-reconnect - just quit */
1599 WLog_DBG(TAG, "AutoReconnect not enabled, quitting.");
1600 return FALSE;
1601 }
1602
1603 const UINT err = freerdp_get_last_error(instance->context);
1604 if (client_auto_reconnect_credentials_rejected(err))
1605 {
1606 WLog_WARN(TAG, "Connection aborted: credentials do not work [%s]",
1607 freerdp_get_last_error_name(err));
1608 return FALSE;
1609 }
1610 if (err == FREERDP_ERROR_CONNECT_CANCELLED)
1611 {
1612 WLog_WARN(TAG, "Connection aborted by user");
1613 return FALSE;
1614 }
1615
1616 handle_window_events_t arg = { .instance = instance,
1617 .window_events = window_events,
1618 .running = true };
1619 HANDLE thread = nullptr;
1620 if (window_events)
1621 thread = CreateThread(nullptr, 0, handle_window_events, &arg, 0, nullptr);
1622 const BOOL rc = client_auto_reconnect_do(instance, retry, numRetries, maxRetries);
1623 if (thread)
1624 {
1625 arg.running = FALSE;
1626 WaitForSingleObject(thread, INFINITE);
1627 CloseHandle(thread);
1628 }
1629 return rc;
1630}
1631
1632#if !defined(_WIN32)
1633static BOOL common_inject_keypress(rdpContext* context, const char* buffer, size_t size)
1634{
1635 WCHAR wbuffer[64] = WINPR_C_ARRAY_INIT;
1636 const SSIZE_T len = ConvertUtf8NToWChar(buffer, size, wbuffer, ARRAYSIZE(wbuffer));
1637 if (len < 0)
1638 return FALSE;
1639
1640 rdpInput* input = context->input;
1641 WINPR_ASSERT(input);
1642
1643 for (SSIZE_T x = 0; x < len; x++)
1644 {
1645 const WCHAR code = wbuffer[x];
1646 freerdp_input_send_unicode_keyboard_event(input, 0, code);
1647 Sleep(5);
1648 freerdp_input_send_unicode_keyboard_event(input, KBD_FLAGS_RELEASE, code);
1649 Sleep(5);
1650 }
1651 return TRUE;
1652}
1653
1654static BOOL common_process_pipe(rdpContext* context, const char* pipe)
1655{
1656 int fd = open(pipe, O_NONBLOCK | O_RDONLY);
1657 if (fd < 0)
1658 {
1659 char ebuffer[256] = WINPR_C_ARRAY_INIT;
1660 WLog_ERR(TAG, "pipe '%s' open returned %s [%d]", pipe,
1661 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1662 return FALSE;
1663 }
1664 while (!freerdp_shall_disconnect_context(context))
1665 {
1666 char buffer[64] = WINPR_C_ARRAY_INIT;
1667 errno = 0;
1668 ssize_t rd = read(fd, buffer, sizeof(buffer) - 1);
1669 if (rd == 0)
1670 {
1671 char ebuffer[256] = WINPR_C_ARRAY_INIT;
1672 if ((errno == EAGAIN) || (errno == 0))
1673 {
1674 Sleep(100);
1675 continue;
1676 }
1677
1678 // EOF, abort reading.
1679 WLog_ERR(TAG, "pipe '%s' read returned %s [%d]", pipe,
1680 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1681 break;
1682 }
1683 else if (rd < 0)
1684 {
1685 char ebuffer[256] = WINPR_C_ARRAY_INIT;
1686 WLog_ERR(TAG, "pipe '%s' read returned %s [%d]", pipe,
1687 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1688 break;
1689 }
1690 else
1691 {
1692 if (!common_inject_keypress(context, buffer, WINPR_ASSERTING_INT_CAST(size_t, rd)))
1693 break;
1694 }
1695 }
1696 close(fd);
1697 return TRUE;
1698}
1699
1700static void common_cleanup_pipe(WINPR_ATTR_UNUSED int signum, WINPR_ATTR_UNUSED const char* signame,
1701 void* context)
1702{
1703 const char* pipe = context;
1704 if (!pipe)
1705 return;
1706 unlink(pipe);
1707}
1708
1709static DWORD WINAPI common_handle_pipe(void* arg)
1710{
1711 rdpClientContext* cctx = arg;
1712 WINPR_ASSERT(cctx);
1713
1714 rdpContext* context = &cctx->context;
1715 WINPR_ASSERT(context);
1716
1717 rdpSettings* settings = context->settings;
1718 WINPR_ASSERT(settings);
1719
1720 const char* pipe = freerdp_settings_get_string(settings, FreeRDP_KeyboardPipeName);
1721 WINPR_ASSERT(pipe);
1722
1723 const int rc = mkfifo(pipe, S_IWUSR | S_IRUSR);
1724 if (rc != 0)
1725 {
1726 char ebuffer[256] = WINPR_C_ARRAY_INIT;
1727 WLog_ERR(TAG, "Failed to create named pipe '%s': %s [%d]", pipe,
1728 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
1729 return 0;
1730 }
1731
1732 void* ctx = WINPR_CAST_CONST_PTR_AWAY(pipe, void*);
1733 if (freerdp_add_signal_cleanup_handler(ctx, common_cleanup_pipe))
1734 {
1735
1736 common_process_pipe(context, pipe);
1737
1738 freerdp_del_signal_cleanup_handler(ctx, common_cleanup_pipe);
1739 }
1740
1741 unlink(pipe);
1742
1743 return 0;
1744}
1745#endif
1746
1747static BOOL commonPreConnect(freerdp* instance)
1748{
1749 WINPR_ASSERT(instance);
1750 WINPR_ASSERT(requireRdpClientContext(instance->context));
1751
1752 rdpClientContext* cctx = (rdpClientContext*)instance->context;
1753
1754 return IFCALLRESULT(TRUE, cctx->PreConnect, instance);
1755}
1756
1757static BOOL commonPostConnect(freerdp* instance)
1758{
1759 WINPR_ASSERT(instance);
1760 WINPR_ASSERT(requireRdpClientContext(instance->context));
1761
1762 rdpClientContext* cctx = (rdpClientContext*)instance->context;
1763
1764 if (!IFCALLRESULT(TRUE, cctx->PostConnect, instance))
1765 return FALSE;
1766
1767 const char* pipe =
1768 freerdp_settings_get_string(cctx->context.settings, FreeRDP_KeyboardPipeName);
1769 if (pipe)
1770 {
1771#if !defined(_WIN32)
1772 cctx->pipethread = CreateThread(nullptr, 0, common_handle_pipe, cctx, 0, nullptr);
1773 if (!cctx->pipethread)
1774 return FALSE;
1775#else
1776 WLog_WARN(TAG, "/kbd:pipe:<filename> not supported on windows");
1777#endif
1778 }
1779
1780 return TRUE;
1781}
1782
1783static void commonPostDisconnect(freerdp* instance)
1784{
1785 WINPR_ASSERT(instance);
1786 WINPR_ASSERT(requireRdpClientContext(instance->context));
1787
1788 rdpClientContext* cctx = (rdpClientContext*)instance->context;
1789
1790#if !defined(_WIN32)
1791 if (cctx->pipethread)
1792 {
1793 (void)WaitForSingleObject(cctx->pipethread, INFINITE);
1794 (void)CloseHandle(cctx->pipethread);
1795 cctx->pipethread = nullptr;
1796 }
1797#endif
1798
1799 IFCALL(cctx->PostDisconnect, instance);
1800
1801 // TODO
1802}
1803
1804static void commonPostFinalDisconnect(freerdp* instance)
1805{
1806 WINPR_ASSERT(instance);
1807 WINPR_ASSERT(requireRdpClientContext(instance->context));
1808
1809 rdpClientContext* cctx = (rdpClientContext*)instance->context;
1810
1811 IFCALL(cctx->PostFinalDisconnect, instance);
1812
1813 // TODO
1814}
1815
1816BOOL freerdp_client_common_pre_connect(rdpContext* context)
1817{
1818 WINPR_ASSERT(context);
1819
1820 if (requireRdpClientContext(context))
1821 {
1822 rdpClientContext* cctx = (rdpClientContext*)context;
1823 freerdp* instance = context->instance;
1824
1825 cctx->PreConnect = instance->PreConnect;
1826 cctx->PostConnect = instance->PostConnect;
1827 cctx->PostDisconnect = instance->PostDisconnect;
1828 cctx->PostFinalDisconnect = instance->PostFinalDisconnect;
1829
1830 instance->PreConnect = commonPreConnect;
1831 instance->PostConnect = commonPostConnect;
1832 instance->PostDisconnect = commonPostDisconnect;
1833 instance->PostFinalDisconnect = commonPostFinalDisconnect;
1834 }
1835 return TRUE;
1836}
1837
1838int freerdp_client_common_stop(rdpContext* context)
1839{
1840 freerdp_abort_connect_context(context);
1841
1842 if (requireRdpClientContext(context))
1843 {
1844
1845 rdpClientContext* cctx = (rdpClientContext*)context;
1846 WINPR_ASSERT(cctx);
1847 if (cctx->thread)
1848 {
1849 (void)WaitForSingleObject(cctx->thread, INFINITE);
1850 (void)CloseHandle(cctx->thread);
1851 cctx->thread = nullptr;
1852 }
1853#if defined(WITH_AAD)
1854 aad_auth_helper_stop(cctx->aadHelper);
1855 cctx->aadHelper = nullptr;
1856#endif
1857 }
1858
1859 return 0;
1860}
1861
1862#if defined(CHANNEL_ENCOMSP_CLIENT)
1863BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1864{
1865 BOOL state = 0;
1866
1867 if (!encomsp)
1868 return FALSE;
1869
1870 rdpClientContext* cctx = (rdpClientContext*)encomsp->custom;
1871 WINPR_ASSERT(cctx);
1872 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
1873 if (!cctx || !requireRdpClientContext(&cctx->context))
1874 return FALSE;
1875
1876 state = cctx->controlToggle;
1877 cctx->controlToggle = !cctx->controlToggle;
1878 return freerdp_client_encomsp_set_control(encomsp, state);
1879}
1880
1881BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1882{
1883 ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = WINPR_C_ARRAY_INIT;
1884
1885 if (!encomsp)
1886 return FALSE;
1887
1888 pdu.ParticipantId = encomsp->participantId;
1889 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1890
1891 if (control)
1892 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1893
1894 const UINT rc = encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1895 return rc == CHANNEL_RC_OK;
1896}
1897
1898static UINT
1899client_encomsp_participant_created(EncomspClientContext* context,
1900 const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1901{
1902 BOOL request = 0;
1903
1904 if (!context || !context->custom || !participantCreated)
1905 return ERROR_INVALID_PARAMETER;
1906
1907 rdpClientContext* cctx = (rdpClientContext*)context->custom;
1908 WINPR_ASSERT(cctx);
1909 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
1910 if (!cctx || !requireRdpClientContext(&cctx->context))
1911 return ERROR_INVALID_PARAMETER;
1912
1913 rdpSettings* settings = cctx->context.settings;
1914 WINPR_ASSERT(settings);
1915
1916 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1917 context->participantId = participantCreated->ParticipantId;
1918
1919 request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1920 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1921 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1922 {
1923 if (!freerdp_client_encomsp_set_control(context, TRUE))
1924 return ERROR_INTERNAL_ERROR;
1925
1926 /* if auto-request-control setting is enabled then only request control once upon connect,
1927 * otherwise it will auto request control again every time server turns off control which
1928 * is a bit annoying */
1929 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1930 return ERROR_INTERNAL_ERROR;
1931 }
1932
1933 return CHANNEL_RC_OK;
1934}
1935
1936static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1937{
1938 WINPR_ASSERT(cctx);
1939 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
1940 if (!cctx || !requireRdpClientContext(&cctx->context))
1941 return;
1942
1943 cctx->encomsp = encomsp;
1944 encomsp->custom = (void*)cctx;
1945 encomsp->ParticipantCreated = client_encomsp_participant_created;
1946}
1947
1948static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1949{
1950 if (encomsp)
1951 {
1952 encomsp->custom = nullptr;
1953 encomsp->ParticipantCreated = nullptr;
1954 }
1955
1956 if (cctx)
1957 {
1958 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
1959 if (requireRdpClientContext(&cctx->context))
1960 cctx->encomsp = nullptr;
1961 }
1962}
1963#endif
1964
1965void freerdp_client_OnChannelConnectedEventHandler(void* context,
1966 const ChannelConnectedEventArgs* e)
1967{
1968 rdpClientContext* cctx = (rdpClientContext*)context;
1969
1970 WINPR_ASSERT(cctx);
1971 WINPR_ASSERT(e);
1972
1973 if (0)
1974 {
1975 }
1976#if defined(CHANNEL_AINPUT_CLIENT)
1977 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1978 {
1979
1980 WINPR_ASSERT(requireRdpClientContext(context));
1981 if (!requireRdpClientContext(context))
1982 return;
1983 cctx->ainput = (AInputClientContext*)e->pInterface;
1984 }
1985#endif
1986#if defined(CHANNEL_RDPEI_CLIENT)
1987 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1988 {
1989 WINPR_ASSERT(requireRdpClientContext(context));
1990 if (!requireRdpClientContext(context))
1991 return;
1992 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1993 }
1994#endif
1995#if defined(CHANNEL_RDPGFX_CLIENT)
1996 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1997 {
1998 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1999 }
2000#endif
2001#if defined(CHANNEL_GEOMETRY_CLIENT)
2002 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
2003 {
2004 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
2005 }
2006#endif
2007#if defined(CHANNEL_VIDEO_CLIENT)
2008 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
2009 {
2010 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
2011 }
2012 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
2013 {
2014 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
2015 }
2016#endif
2017#if defined(CHANNEL_ENCOMSP_CLIENT)
2018 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
2019 {
2020 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
2021 }
2022#endif
2023}
2024
2025void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
2026 const ChannelDisconnectedEventArgs* e)
2027{
2028 rdpClientContext* cctx = (rdpClientContext*)context;
2029
2030 WINPR_ASSERT(cctx);
2031 WINPR_ASSERT(e);
2032
2033 if (0)
2034 {
2035 }
2036#if defined(CHANNEL_AINPUT_CLIENT)
2037 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
2038 {
2039 WINPR_ASSERT(requireRdpClientContext(context));
2040 if (!requireRdpClientContext(context))
2041 return;
2042 cctx->ainput = nullptr;
2043 }
2044#endif
2045#if defined(CHANNEL_RDPEI_CLIENT)
2046 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
2047 {
2048 WINPR_ASSERT(requireRdpClientContext(context));
2049 if (!requireRdpClientContext(context))
2050 return;
2051 cctx->rdpei = nullptr;
2052 }
2053#endif
2054#if defined(CHANNEL_RDPGFX_CLIENT)
2055 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
2056 {
2057 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
2058 }
2059#endif
2060#if defined(CHANNEL_GEOMETRY_CLIENT)
2061 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
2062 {
2063 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
2064 }
2065#endif
2066#if defined(CHANNEL_VIDEO_CLIENT)
2067 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
2068 {
2069 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
2070 }
2071 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
2072 {
2073 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
2074 }
2075#endif
2076#if defined(CHANNEL_ENCOMSP_CLIENT)
2077 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
2078 {
2079 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
2080 }
2081#endif
2082}
2083
2084BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
2085{
2086 BOOL handled = FALSE;
2087
2088 WINPR_ASSERT(cctx);
2089 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2090 if (!cctx || !requireRdpClientContext(&cctx->context))
2091 return FALSE;
2092
2093 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2094 if (state != CONNECTION_STATE_ACTIVE)
2095 return TRUE;
2096
2097#if defined(CHANNEL_AINPUT_CLIENT)
2098 /* The channel thread resets this when the server closes the channel, only read it once */
2099 AInputClientContext* ainput = cctx->ainput;
2100 if (ainput)
2101 {
2102 UINT rc = 0;
2103 UINT64 flags = 0;
2104 INT32 x = 0;
2105 INT32 y = 0;
2106 INT32 value = mflags & 0xFF;
2107
2108 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
2109 value = -1 * (0x100 - value);
2110
2111 /* We have discrete steps, scale this so we can also support high
2112 * resolution wheels. */
2113 value *= 0x10000;
2114
2115 if (mflags & PTR_FLAGS_WHEEL)
2116 {
2117 flags |= AINPUT_FLAGS_WHEEL;
2118 y = value;
2119 }
2120
2121 if (mflags & PTR_FLAGS_HWHEEL)
2122 {
2123 flags |= AINPUT_FLAGS_WHEEL;
2124 x = value;
2125 }
2126
2127 WINPR_ASSERT(ainput->AInputSendInputEvent);
2128 rc = ainput->AInputSendInputEvent(ainput, flags, x, y);
2129 if (rc == CHANNEL_RC_OK)
2130 handled = TRUE;
2131 }
2132#endif
2133
2134 if (!handled)
2135 return freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
2136
2137 return TRUE;
2138}
2139
2140#if defined(CHANNEL_AINPUT_CLIENT)
2141WINPR_ATTR_NODISCARD
2142static inline BOOL ainput_send_diff_event(AInputClientContext* ainput, UINT64 flags, INT32 x,
2143 INT32 y)
2144{
2145 WINPR_ASSERT(ainput);
2146 WINPR_ASSERT(ainput->AInputSendInputEvent);
2147
2148 const UINT rc = ainput->AInputSendInputEvent(ainput, flags, x, y);
2149
2150 return rc == CHANNEL_RC_OK;
2151}
2152#endif
2153
2154WINPR_ATTR_NODISCARD
2155static bool button_pressed(const rdpClientContext* cctx)
2156{
2157 WINPR_ASSERT(cctx);
2158 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2159 if (!cctx || !requireRdpClientContext(&cctx->context))
2160 return false;
2161
2162 for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
2163 {
2164 const BOOL cur = cctx->pressed_buttons[x];
2165 if (cur)
2166 return true;
2167 }
2168 return false;
2169}
2170
2171BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
2172 INT32 y)
2173{
2174 BOOL handled = FALSE;
2175
2176 WINPR_ASSERT(cctx);
2177 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2178 if (!cctx || !requireRdpClientContext(&cctx->context))
2179 return FALSE;
2180
2181 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2182 if (state != CONNECTION_STATE_ACTIVE)
2183 return TRUE;
2184
2185 if (mflags & PTR_FLAGS_BUTTON1)
2186 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
2187 if (mflags & PTR_FLAGS_BUTTON2)
2188 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
2189 if (mflags & PTR_FLAGS_BUTTON3)
2190 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
2191
2192 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
2193 !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
2194 {
2195 if (!button_pressed(cctx))
2196 return TRUE;
2197 }
2198
2199 const BOOL haveRelative =
2200 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
2201 if (relative && haveRelative)
2202 {
2203 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
2204 WINPR_ASSERTING_INT_CAST(int16_t, x),
2205 WINPR_ASSERTING_INT_CAST(int16_t, y));
2206 }
2207
2208#if defined(CHANNEL_AINPUT_CLIENT)
2209 /* The channel thread resets this when the server closes the channel, only read it once */
2210 AInputClientContext* ainput = cctx->ainput;
2211 if (ainput)
2212 {
2213 UINT64 flags = 0;
2214
2215 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
2216 flags |= AINPUT_FLAGS_HAVE_REL;
2217
2218 if (relative)
2219 flags |= AINPUT_FLAGS_REL;
2220
2221 if (mflags & PTR_FLAGS_DOWN)
2222 flags |= AINPUT_FLAGS_DOWN;
2223 if (mflags & PTR_FLAGS_BUTTON1)
2224 flags |= AINPUT_FLAGS_BUTTON1;
2225 if (mflags & PTR_FLAGS_BUTTON2)
2226 flags |= AINPUT_FLAGS_BUTTON2;
2227 if (mflags & PTR_FLAGS_BUTTON3)
2228 flags |= AINPUT_FLAGS_BUTTON3;
2229 if (mflags & PTR_FLAGS_MOVE)
2230 flags |= AINPUT_FLAGS_MOVE;
2231 handled = ainput_send_diff_event(ainput, flags, x, y);
2232 }
2233#endif
2234
2235 if (!handled)
2236 {
2237 if (relative)
2238 {
2239 cctx->lastX += x;
2240 cctx->lastY += y;
2241 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
2242 }
2243 else
2244 {
2245 cctx->lastX = x;
2246 cctx->lastY = y;
2247 }
2248 return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
2249 (UINT16)cctx->lastY);
2250 }
2251 return TRUE;
2252}
2253
2254BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
2255 INT32 x, INT32 y)
2256{
2257 BOOL handled = FALSE;
2258 WINPR_ASSERT(cctx);
2259 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2260
2261 if (!cctx || !requireRdpClientContext(&cctx->context))
2262 return FALSE;
2263
2264 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2265 if (state != CONNECTION_STATE_ACTIVE)
2266 return TRUE;
2267
2268 if (mflags & PTR_XFLAGS_BUTTON1)
2269 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
2270 if (mflags & PTR_XFLAGS_BUTTON2)
2271 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
2272
2273 const BOOL haveRelative =
2274 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
2275 if (relative && haveRelative)
2276 {
2277 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
2278 WINPR_ASSERTING_INT_CAST(int16_t, x),
2279 WINPR_ASSERTING_INT_CAST(int16_t, y));
2280 }
2281
2282#if defined(CHANNEL_AINPUT_CLIENT)
2283 /* The channel thread resets this when the server closes the channel, only read it once */
2284 AInputClientContext* ainput = cctx->ainput;
2285 if (ainput)
2286 {
2287 UINT64 flags = 0;
2288
2289 if (relative)
2290 flags |= AINPUT_FLAGS_REL;
2291 if (mflags & PTR_XFLAGS_DOWN)
2292 flags |= AINPUT_FLAGS_DOWN;
2293 if (mflags & PTR_XFLAGS_BUTTON1)
2294 flags |= AINPUT_XFLAGS_BUTTON1;
2295 if (mflags & PTR_XFLAGS_BUTTON2)
2296 flags |= AINPUT_XFLAGS_BUTTON2;
2297
2298 handled = ainput_send_diff_event(ainput, flags, x, y);
2299 }
2300#endif
2301
2302 if (!handled)
2303 {
2304 if (relative)
2305 {
2306 cctx->lastX += x;
2307 cctx->lastY += y;
2308 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
2309 }
2310 else
2311 {
2312 cctx->lastX = x;
2313 cctx->lastY = y;
2314 }
2315 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
2316 (UINT16)cctx->lastY);
2317 }
2318
2319 return TRUE;
2320}
2321
2322WINPR_ATTR_NODISCARD
2323static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
2324 const FreeRDP_TouchContact* contact)
2325{
2326 WINPR_ASSERT(cctx);
2327 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2328 if (!cctx || !requireRdpClientContext(&cctx->context))
2329 return FALSE;
2330
2331 const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
2332 const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
2333 WINPR_ASSERT(contact);
2334 WINPR_ASSERT(contact->x <= UINT16_MAX);
2335 WINPR_ASSERT(contact->y <= UINT16_MAX);
2336
2337 switch (contact->count)
2338 {
2339 case 1:
2340 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
2341 contact->x, contact->y);
2342 case 2:
2343 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
2344 contact->x, contact->y);
2345 case 3:
2346 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
2347 contact->x, contact->y);
2348 case 4:
2349 return freerdp_client_send_extended_button_event(
2350 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
2351 case 5:
2352 return freerdp_client_send_extended_button_event(
2353 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
2354 default:
2355 /* unmapped events, ignore */
2356 return TRUE;
2357 }
2358}
2359
2360WINPR_ATTR_NODISCARD
2361static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2362{
2363 WINPR_ASSERT(cctx);
2364 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2365 WINPR_ASSERT(contact);
2366
2367 if (!cctx || !requireRdpClientContext(&cctx->context))
2368 return FALSE;
2369
2370#if defined(CHANNEL_RDPEI_CLIENT)
2371 RdpeiClientContext* rdpei = cctx->rdpei;
2372
2373 if (!rdpei)
2374 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
2375
2376 int contactId = 0;
2377
2378 if (rdpei->TouchRawEvent)
2379 {
2380 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
2381 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2382 ? CONTACT_DATA_PRESSURE_PRESENT
2383 : 0;
2384 // Ensure contact position is unchanged from "engaged" to "out of range" state
2385 const UINT rc1 =
2386 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2387 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
2388 RDPINPUT_CONTACT_FLAG_INCONTACT,
2389 contactFlags, contact->pressure);
2390 if (rc1 != CHANNEL_RC_OK)
2391 return FALSE;
2392
2393 const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
2394 &contactId, flags, contactFlags, contact->pressure);
2395 if (rc2 != CHANNEL_RC_OK)
2396 return FALSE;
2397 }
2398 else
2399 {
2400 WINPR_ASSERT(rdpei->TouchEnd);
2401 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2402 if (rc != CHANNEL_RC_OK)
2403 return FALSE;
2404 }
2405 return TRUE;
2406#else
2407 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2408 "-DCHANNEL_RDPEI_CLIENT=ON");
2409 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
2410#endif
2411}
2412
2413WINPR_ATTR_NODISCARD
2414static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2415{
2416 WINPR_ASSERT(cctx);
2417 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2418 WINPR_ASSERT(contact);
2419
2420 if (!cctx || !requireRdpClientContext(&cctx->context))
2421 return FALSE;
2422
2423#if defined(CHANNEL_RDPEI_CLIENT)
2424 RdpeiClientContext* rdpei = cctx->rdpei;
2425
2426 // Emulate mouse click if touch is not possible, like in login screen
2427 if (!rdpei)
2428 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
2429
2430 int contactId = 0;
2431
2432 if (rdpei->TouchRawEvent)
2433 {
2434 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
2435 RDPINPUT_CONTACT_FLAG_INCONTACT;
2436 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2437 ? CONTACT_DATA_PRESSURE_PRESENT
2438 : 0;
2439 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2440 flags, contactFlags, contact->pressure);
2441 if (rc != CHANNEL_RC_OK)
2442 return FALSE;
2443 }
2444 else
2445 {
2446 WINPR_ASSERT(rdpei->TouchBegin);
2447 const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
2448 if (rc != CHANNEL_RC_OK)
2449 return FALSE;
2450 }
2451
2452 return TRUE;
2453#else
2454 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2455 "-DCHANNEL_RDPEI_CLIENT=ON");
2456 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
2457#endif
2458}
2459
2460WINPR_ATTR_NODISCARD
2461static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
2462 const FreeRDP_TouchContact* contact)
2463{
2464 WINPR_ASSERT(cctx);
2465 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2466
2467 if (!cctx || !requireRdpClientContext(&cctx->context))
2468 return FALSE;
2469
2470 const UINT16 flags = PTR_FLAGS_MOVE;
2471
2472 WINPR_ASSERT(contact);
2473 WINPR_ASSERT(contact->x <= UINT16_MAX);
2474 WINPR_ASSERT(contact->y <= UINT16_MAX);
2475 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
2476}
2477
2478WINPR_ATTR_NODISCARD
2479static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2480{
2481 WINPR_ASSERT(cctx);
2482 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2483 WINPR_ASSERT(contact);
2484
2485 if (!cctx || !requireRdpClientContext(&cctx->context))
2486 return FALSE;
2487
2488#if defined(CHANNEL_RDPEI_CLIENT)
2489 RdpeiClientContext* rdpei = cctx->rdpei;
2490
2491 if (!rdpei)
2492 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2493
2494 int contactId = 0;
2495
2496 if (rdpei->TouchRawEvent)
2497 {
2498 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
2499 RDPINPUT_CONTACT_FLAG_INCONTACT;
2500 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2501 ? CONTACT_DATA_PRESSURE_PRESENT
2502 : 0;
2503 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2504 flags, contactFlags, contact->pressure);
2505 if (rc != CHANNEL_RC_OK)
2506 return FALSE;
2507 }
2508 else
2509 {
2510 WINPR_ASSERT(rdpei->TouchUpdate);
2511 const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2512 if (rc != CHANNEL_RC_OK)
2513 return FALSE;
2514 }
2515
2516 return TRUE;
2517#else
2518 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2519 "-DCHANNEL_RDPEI_CLIENT=ON");
2520 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2521#endif
2522}
2523
2524WINPR_ATTR_NODISCARD
2525static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2526{
2527 WINPR_ASSERT(cctx);
2528 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2529 WINPR_ASSERT(contact);
2530
2531 if (!cctx || !requireRdpClientContext(&cctx->context))
2532 return FALSE;
2533
2534#if defined(CHANNEL_RDPEI_CLIENT)
2535 RdpeiClientContext* rdpei = cctx->rdpei;
2536
2537 if (!rdpei)
2538 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2539
2540 int contactId = 0;
2541
2542 if (rdpei->TouchRawEvent)
2543 {
2544 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2545 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2546 ? CONTACT_DATA_PRESSURE_PRESENT
2547 : 0;
2548 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2549 flags, contactFlags, contact->pressure);
2550 if (rc != CHANNEL_RC_OK)
2551 return FALSE;
2552 }
2553 else
2554 {
2555 WINPR_ASSERT(rdpei->TouchUpdate);
2556 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2557 if (rc != CHANNEL_RC_OK)
2558 return FALSE;
2559 }
2560
2561 return TRUE;
2562#else
2563 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2564 "-DCHANNEL_RDPEI_CLIENT=ON");
2565 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2566#endif
2567}
2568
2569WINPR_ATTR_NODISCARD
2570static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2571 UINT32 pressure, INT32 x, INT32 y,
2572 FreeRDP_TouchContact* pcontact)
2573{
2574 WINPR_ASSERT(cctx);
2575 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2576 WINPR_ASSERT(pcontact);
2577
2578 if (!cctx || !requireRdpClientContext(&cctx->context))
2579 return FALSE;
2580
2581 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2582 {
2583 FreeRDP_TouchContact* contact = &cctx->contacts[i];
2584
2585 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2586 if (newcontact || (contact->id == touchId))
2587 {
2588 contact->id = touchId;
2589 contact->flags = flags;
2590 contact->pressure = pressure;
2591 contact->x = x;
2592 contact->y = y;
2593 contact->count = WINPR_ASSERTING_INT_CAST(UINT32, i + 1);
2594
2595 *pcontact = *contact;
2596
2597 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2598 if (resetcontact)
2599 {
2600 FreeRDP_TouchContact empty = WINPR_C_ARRAY_INIT;
2601 *contact = empty;
2602 }
2603 return TRUE;
2604 }
2605 }
2606
2607 return FALSE;
2608}
2609
2610BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2611 UINT32 pressure, INT32 x, INT32 y)
2612{
2613 WINPR_ASSERT(cctx);
2614 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2615
2616 if (!cctx || !requireRdpClientContext(&cctx->context))
2617 return FALSE;
2618
2619 const UINT32 mask =
2620 FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2621 WINPR_ASSERT(cctx);
2622
2623 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2624 if (state != CONNECTION_STATE_ACTIVE)
2625 return TRUE;
2626
2627 FreeRDP_TouchContact contact = WINPR_C_ARRAY_INIT;
2628
2629 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2630 return FALSE;
2631
2632 switch (flags & mask)
2633 {
2634 case FREERDP_TOUCH_DOWN:
2635 return freerdp_handle_touch_down(cctx, &contact);
2636 case FREERDP_TOUCH_UP:
2637 return freerdp_handle_touch_up(cctx, &contact);
2638 case FREERDP_TOUCH_MOTION:
2639 return freerdp_handle_touch_motion(cctx, &contact);
2640 case FREERDP_TOUCH_CANCEL:
2641 return freerdp_handle_touch_cancel(cctx, &contact);
2642 default:
2643 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %" PRIu32 ", ignoring", flags);
2644 return FALSE;
2645 }
2646}
2647
2648BOOL freerdp_client_load_channels(freerdp* instance)
2649{
2650 WINPR_ASSERT(instance);
2651 WINPR_ASSERT(instance->context);
2652
2653 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2654 {
2655 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2656 return FALSE;
2657 }
2658 return TRUE;
2659}
2660
2661int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2662{
2663 const char* str_data = freerdp_get_logon_error_info_data(data);
2664 const char* str_type = freerdp_get_logon_error_info_type(type);
2665
2666 if (!instance || !instance->context)
2667 return -1;
2668
2669 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2670 return 1;
2671}
2672
2673WINPR_ATTR_NODISCARD
2674static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2675 size_t* pos)
2676{
2677 WINPR_ASSERT(cctx);
2678 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2679
2680 if (!cctx || !requireRdpClientContext(&cctx->context))
2681 return nullptr;
2682
2683 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2684 {
2685 FreeRDP_PenDevice* pen = &cctx->pens[i];
2686 if (deviceid == pen->deviceid)
2687 {
2688 if (pos)
2689 *pos = i;
2690 return pen;
2691 }
2692 }
2693 return nullptr;
2694}
2695
2696WINPR_ATTR_NODISCARD
2697static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2698 double pressure)
2699{
2700 static const INT32 null_deviceid = 0;
2701
2702 WINPR_ASSERT(cctx);
2703 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2704 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2705
2706 if (!cctx || !requireRdpClientContext(&cctx->context))
2707 return FALSE;
2708
2709 if (freerdp_client_is_pen(cctx, deviceid))
2710 {
2711 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2712 return FALSE;
2713 }
2714
2715 size_t pos = 0;
2716 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2717 if (pen)
2718 {
2719 const FreeRDP_PenDevice empty = WINPR_C_ARRAY_INIT;
2720 *pen = empty;
2721
2722 pen->deviceid = deviceid;
2723 pen->max_pressure = pressure;
2724 pen->flags = flags;
2725
2726 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2727 return TRUE;
2728 }
2729
2730 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2731 return TRUE;
2732}
2733
2734BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2735{
2736 WINPR_ASSERT(cctx);
2737 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2738
2739 if (!cctx || !requireRdpClientContext(&cctx->context))
2740 return FALSE;
2741
2742 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2743 if (state != CONNECTION_STATE_ACTIVE)
2744 return TRUE;
2745
2746#if defined(CHANNEL_RDPEI_CLIENT)
2747 if ((flags & FREERDP_PEN_REGISTER) != 0)
2748 {
2749 va_list args = WINPR_C_ARRAY_INIT;
2750
2751 va_start(args, deviceid);
2752 double pressure = va_arg(args, double);
2753 va_end(args);
2754 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2755 }
2756 size_t pos = 0;
2757 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2758 if (!pen)
2759 {
2760 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2761 return FALSE;
2762 }
2763
2764 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2765 UINT32 penFlags =
2766 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2767
2768 RdpeiClientContext* rdpei = cctx->rdpei;
2769 WINPR_ASSERT(rdpei);
2770
2771 UINT32 normalizedpressure = 1024;
2772 INT32 x = 0;
2773 INT32 y = 0;
2774 UINT16 rotation = 0;
2775 INT16 tiltX = 0;
2776 INT16 tiltY = 0;
2777 va_list args = WINPR_C_ARRAY_INIT;
2778 va_start(args, deviceid);
2779
2780 x = va_arg(args, INT32);
2781 y = va_arg(args, INT32);
2782 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2783 {
2784 const double pressure = va_arg(args, double);
2785 const double np = (pressure * 1024.0) / pen->max_pressure;
2786 normalizedpressure = (UINT32)lround(np);
2787 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2788 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2789 }
2790 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2791 {
2792 const unsigned arg = va_arg(args, unsigned);
2793 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2794 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2795 }
2796 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2797 {
2798 const int arg = va_arg(args, int);
2799 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2800 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2801 }
2802 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2803 {
2804 const int arg = va_arg(args, int);
2805 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2806 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2807 }
2808 va_end(args);
2809
2810 if ((flags & FREERDP_PEN_PRESS) != 0)
2811 {
2812 // Ensure that only one button is pressed
2813 if (pen->pressed)
2814 flags = FREERDP_PEN_MOTION |
2815 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2816 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2817 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2818 }
2819 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2820 {
2821 if (!pen->pressed ||
2822 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2823 flags = FREERDP_PEN_MOTION |
2824 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2825 else
2826 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2827 }
2828
2829 flags |= pen->flags;
2830 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2831 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2832 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2833 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2834
2835 pen->last_x = x;
2836 pen->last_y = y;
2837 if ((flags & FREERDP_PEN_PRESS) != 0)
2838 {
2839 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2840 pen->hovering = FALSE;
2841 pen->pressed = TRUE;
2842
2843 WINPR_ASSERT(rdpei->PenBegin);
2844 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2845 normalizedpressure, rotation, tiltX, tiltY);
2846 return rc == CHANNEL_RC_OK;
2847 }
2848 else if ((flags & FREERDP_PEN_MOTION) != 0)
2849 {
2850 UINT rc = ERROR_INTERNAL_ERROR;
2851 if (pen->pressed)
2852 {
2853 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2854
2855 // TODO: what if no rotation is supported but tilt is?
2856 WINPR_ASSERT(rdpei->PenUpdate);
2857 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2858 rotation, tiltX, tiltY);
2859 }
2860 else if (pen->hovering)
2861 {
2862 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2863
2864 WINPR_ASSERT(rdpei->PenHoverUpdate);
2865 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2866 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2867 }
2868 else
2869 {
2870 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2871 pen->hovering = TRUE;
2872
2873 WINPR_ASSERT(rdpei->PenHoverBegin);
2874 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2875 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2876 }
2877 return rc == CHANNEL_RC_OK;
2878 }
2879 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2880 {
2881 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2882 pen->pressed = FALSE;
2883 pen->hovering = TRUE;
2884
2885 WINPR_ASSERT(rdpei->PenUpdate);
2886 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2887 normalizedpressure, rotation, tiltX, tiltY);
2888 if (rc != CHANNEL_RC_OK)
2889 return FALSE;
2890 WINPR_ASSERT(rdpei->PenEnd);
2891 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2892 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2893 return re == CHANNEL_RC_OK;
2894 }
2895
2896 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2897#else
2898 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2899 "-DCHANNEL_RDPEI_CLIENT=ON");
2900#endif
2901
2902 return FALSE;
2903}
2904
2905BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2906{
2907 WINPR_ASSERT(cctx);
2908 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2909
2910 if (!cctx || !requireRdpClientContext(&cctx->context))
2911 return FALSE;
2912
2913 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2914 if (state != CONNECTION_STATE_ACTIVE)
2915 return TRUE;
2916
2917#if defined(CHANNEL_RDPEI_CLIENT)
2918 RdpeiClientContext* rdpei = cctx->rdpei;
2919
2920 if (!rdpei)
2921 return FALSE;
2922
2923 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2924 {
2925 FreeRDP_PenDevice* pen = &cctx->pens[i];
2926 if (pen->hovering)
2927 {
2928 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2929 pen->hovering = FALSE;
2930 const UINT rc =
2931 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2932 if (rc != CHANNEL_RC_OK)
2933 return FALSE;
2934 }
2935 }
2936 return TRUE;
2937#else
2938 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2939 "-DCHANNEL_RDPEI_CLIENT=ON");
2940 return FALSE;
2941#endif
2942}
2943
2944BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2945{
2946 WINPR_ASSERT(cctx);
2947 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2948
2949 if (!cctx || !requireRdpClientContext(&cctx->context))
2950 return FALSE;
2951
2952 if (deviceid == 0)
2953 return FALSE;
2954
2955 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2956 {
2957 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2958 if (pen->deviceid == deviceid)
2959 return TRUE;
2960 }
2961
2962 return FALSE;
2963}
2964
2965BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* cctx)
2966{
2967 WINPR_ASSERT(cctx);
2968 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
2969
2970 if (!cctx || !requireRdpClientContext(&cctx->context))
2971 return FALSE;
2972
2973 const rdpSettings* settings = cctx->context.settings;
2974 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2975 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2976 BOOL ainput = FALSE;
2977#if defined(CHANNEL_AINPUT_CLIENT)
2978 ainput = cctx->ainput != nullptr;
2979#endif
2980
2981 return useRelative && (haveRelative || ainput);
2982}
2983
2984#if defined(WITH_AAD)
2985WINPR_ATTR_MALLOC(free, 1)
2986static char* get_redirect_uri(const rdpSettings* settings)
2987{
2988 char* redirect_uri = nullptr;
2989 const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2990 if (cli)
2991 {
2992 const char* redirect_fmt =
2993 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2994 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2995 const char* tenantid = "common";
2996 if (useTenant)
2997 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2998
2999 if (tenantid && redirect_fmt)
3000 {
3001 const char* url =
3002 freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
3003
3004 size_t redirect_len = 0;
3005 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
3006 }
3007 }
3008 else
3009 {
3010 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
3011 const char* redirect_fmt =
3012 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
3013
3014 size_t redirect_len = 0;
3015 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
3016 }
3017 return redirect_uri;
3018}
3019
3022WINPR_ATTR_MALLOC(free, 1)
3023static char* get_login_hint(const rdpSettings* settings, FreeRDP_Settings_Keys_String primary,
3024 FreeRDP_Settings_Keys_String secondary)
3025{
3026 const FreeRDP_Settings_Keys_String keys[] = { primary, secondary };
3027 for (size_t x = 0; x < ARRAYSIZE(keys); x++)
3028 {
3029 const char* user = freerdp_settings_get_string(settings, keys[x]);
3030 /* AAD accounts are UPNs: a DOMAIN\user or bare name would only prefill the form with an
3031 * account that can't sign in */
3032 if (!user || !strchr(user, '@') || strchr(user, '\\'))
3033 continue;
3034
3035 char* encoded = winpr_str_url_encode(user, strlen(user));
3036 if (!encoded)
3037 return nullptr;
3038
3039 char* param = nullptr;
3040 size_t len = 0;
3041 winpr_asprintf(&param, &len, "&login_hint=%s", encoded);
3042 free(encoded);
3043 return param;
3044 }
3045 return _strdup("");
3046}
3047
3048WINPR_ATTR_MALLOC(free, 1)
3049static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap, size_t* plen)
3050{
3051 WINPR_ASSERT(cctx);
3052 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
3053 WINPR_ASSERT(plen);
3054 *plen = 0;
3055
3056 if (!cctx || !requireRdpClientContext(&cctx->context))
3057 return nullptr;
3058
3059 const rdpSettings* settings = cctx->context.settings;
3060 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
3061 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
3062 AAD_WELLKNOWN_authorization_endpoint);
3063 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
3064
3065 if (!client_id || !ep || !scope)
3066 return nullptr;
3067
3068 char* redirect_uri = get_redirect_uri(settings);
3069 /* the AVD token is for the gateway: prefer the gateway user */
3070 char* login_hint = get_login_hint(settings, FreeRDP_GatewayUsername, FreeRDP_Username);
3071 char* url = nullptr;
3072 if (redirect_uri && login_hint)
3073 winpr_asprintf(&url, plen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s%s",
3074 ep, client_id, scope, redirect_uri, login_hint);
3075 free(login_hint);
3076 free(redirect_uri);
3077 return url;
3078}
3079
3080WINPR_ATTR_MALLOC(free, 1)
3081static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap, size_t* plen)
3082{
3083 WINPR_ASSERT(cctx);
3084 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
3085 WINPR_ASSERT(plen);
3086 *plen = 0;
3087
3088 if (!cctx || !requireRdpClientContext(&cctx->context))
3089 return nullptr;
3090
3091 const rdpSettings* settings = cctx->context.settings;
3092 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
3093 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
3094 AAD_WELLKNOWN_authorization_endpoint);
3095 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
3096
3097 if (!client_id || !ep || !scope)
3098 return nullptr;
3099
3100 char* redirect_uri = get_redirect_uri(settings);
3101 if (!redirect_uri)
3102 return nullptr;
3103
3104 char* url = nullptr;
3105
3106 const char* code = va_arg(ap, const char*);
3107 winpr_asprintf(&url, plen,
3108 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
3109 code, client_id, scope, redirect_uri);
3110 free(redirect_uri);
3111 return url;
3112}
3113
3114WINPR_ATTR_MALLOC(free, 1)
3115static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap, size_t* plen)
3116{
3117 WINPR_ASSERT(cctx);
3118 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
3119 WINPR_ASSERT(plen);
3120
3121 *plen = 0;
3122 if (!cctx || !requireRdpClientContext(&cctx->context))
3123 return nullptr;
3124
3125 const rdpSettings* settings = cctx->context.settings;
3126 char* url = nullptr;
3127 char* redirect_uri = get_redirect_uri(settings);
3128 /* the RDSTLS token is for the session host: prefer the host user */
3129 char* login_hint = get_login_hint(settings, FreeRDP_Username, FreeRDP_GatewayUsername);
3130
3131 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
3132 if (!client_id || !redirect_uri || !login_hint)
3133 goto cleanup;
3134
3135 {
3136 const char* scope = va_arg(ap, const char*);
3137 if (!scope)
3138 goto cleanup;
3139
3140 {
3141 const char* ep = freerdp_utils_aad_get_wellknown_string(
3142 &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
3143 winpr_asprintf(&url, plen,
3144 "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s%s", ep,
3145 client_id, scope, redirect_uri, login_hint);
3146 }
3147 }
3148
3149cleanup:
3150 free(login_hint);
3151 free(redirect_uri);
3152 return url;
3153}
3154
3155WINPR_ATTR_MALLOC(free, 1)
3156static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap, size_t* plen)
3157{
3158 WINPR_ASSERT(cctx);
3159 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
3160 WINPR_ASSERT(plen);
3161 *plen = 0;
3162
3163 if (!cctx || !requireRdpClientContext(&cctx->context))
3164 return nullptr;
3165
3166 const rdpSettings* settings = cctx->context.settings;
3167 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
3168 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
3169 AAD_WELLKNOWN_authorization_endpoint);
3170 const char* scope = va_arg(ap, const char*);
3171 const char* code = va_arg(ap, const char*);
3172 const char* req_cnf = va_arg(ap, const char*);
3173
3174 if (!client_id || !ep || !scope || !code || !req_cnf)
3175 return nullptr;
3176
3177 char* redirect_uri = get_redirect_uri(settings);
3178 if (!redirect_uri)
3179 return nullptr;
3180
3181 char* url = nullptr;
3182
3183 winpr_asprintf(
3184 &url, plen,
3185 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
3186 code, client_id, scope, redirect_uri, req_cnf);
3187 free(redirect_uri);
3188 return url;
3189}
3190#endif
3191
3192char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
3193{
3194 WINPR_ASSERT(cctx);
3195 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
3196
3197 if (!cctx || !requireRdpClientContext(&cctx->context))
3198 return nullptr;
3199
3200 char* str = nullptr;
3201 size_t len = 0;
3202
3203 va_list ap = WINPR_C_ARRAY_INIT;
3204 va_start(ap, type);
3205 switch (type)
3206 {
3207#if defined(WITH_AAD)
3208 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
3209 if (freerdp_oauth2_reset(cctx->oauth2))
3210 str = aad_auth_request(cctx, ap, &len);
3211 break;
3212 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
3213 str = aad_token_request(cctx, ap, &len);
3214 break;
3215 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
3216 if (freerdp_oauth2_reset(cctx->oauth2))
3217 str = avd_auth_request(cctx, ap, &len);
3218 break;
3219 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
3220 str = avd_token_request(cctx, ap, &len);
3221 break;
3222#endif
3223 default:
3224 break;
3225 }
3226 va_end(ap);
3227
3228 char* safestr = freerdp_oauth2_append_state(cctx->oauth2, str, len, nullptr);
3229 winpr_zfree(str);
3230 return safestr;
3231}
3232
3233BOOL client_common_save_session_info(WINPR_ATTR_UNUSED rdpContext* context, UINT32 type,
3234 const void* data)
3235{
3236 char buffer[128] = WINPR_C_ARRAY_INIT;
3237 WLog_INFO(TAG, "%s [%s]", freerdp_session_logon_type_str(type),
3238 freerdp_session_logon_type_data_str(type, data, buffer, sizeof(buffer)));
3239 return TRUE;
3240}
3241
3242char* freerdp_client_extract_aad_code(rdpClientContext* cctx, const char* data, size_t length)
3243{
3244 WINPR_ASSERT(cctx);
3245 WINPR_ASSERT(requireRdpClientContext(&cctx->context));
3246
3247 if (!cctx || !requireRdpClientContext(&cctx->context))
3248 return nullptr;
3249 return freerdp_oauth2_extract_code(cctx->oauth2, data, length);
3250}
3251
3252WINPR_ATTR_NODISCARD
3253static BOOL client_helper_get_access_token_va(freerdp* instance,
3254 WINPR_ATTR_UNUSED AccessTokenType tokenType,
3255 WINPR_ATTR_UNUSED char** token,
3256 WINPR_ATTR_UNUSED size_t count, va_list ap)
3257{
3258 WINPR_ASSERT(instance);
3259 WINPR_ASSERT(token);
3260
3261 *token = nullptr;
3262
3263 WINPR_ASSERT(requireRdpClientContext(instance->context));
3264
3265#if defined(WITH_AAD)
3266 rdpClientContext* cctx = (rdpClientContext*)instance->context;
3267 WINPR_ASSERT(cctx);
3268
3269 /* Keep one helper for the whole client lifetime (it is stopped when the context is freed):
3270 * its browser session then carries over from one token request to the next, e.g. AVD
3271 * gateway then session host, instead of prompting for a login each time. Only start a new
3272 * one if there's none yet or the previous one exited. */
3273 if (!aad_auth_helper_is_running(cctx->aadHelper))
3274 {
3275 aad_auth_helper_stop(cctx->aadHelper);
3276 cctx->aadHelper = aad_auth_helper_start(cctx);
3277 if (!cctx->aadHelper)
3278 return FALSE;
3279 }
3280
3281 const BOOL rc =
3282 aad_auth_helper_get_access_token_v(cctx->aadHelper, tokenType, token, count, ap);
3283
3284 /* a helper that died during the request (crash, user killed it) is restarted next time */
3285 if (!rc && !aad_auth_helper_is_running(cctx->aadHelper))
3286 {
3287 aad_auth_helper_stop(cctx->aadHelper);
3288 cctx->aadHelper = nullptr;
3289 }
3290 return rc;
3291#else
3292 WLog_ERR(TAG, "Build does not support AAD authentication");
3293 return FALSE;
3294#endif
3295}
3296
3297BOOL client_failsafe_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
3298 size_t count, ...)
3299{
3300 {
3301 va_list ap = WINPR_C_ARRAY_INIT;
3302 va_start(ap, count);
3303 const BOOL rc = client_helper_get_access_token_va(instance, tokenType, token, count, ap);
3304 va_end(ap);
3305 if (rc)
3306 return rc;
3307 }
3308 {
3309 va_list ap = WINPR_C_ARRAY_INIT;
3310 va_start(ap, count);
3311 const BOOL rc = client_cli_get_access_token_va(instance, tokenType, token, count, ap);
3312 va_end(ap);
3313 return rc;
3314 }
3315}
3316
3317BOOL client_helper_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
3318 size_t count, ...)
3319{
3320 va_list ap = WINPR_C_ARRAY_INIT;
3321 va_start(ap, count);
3322 const BOOL rc = client_helper_get_access_token_va(instance, tokenType, token, count, ap);
3323 va_end(ap);
3324 return rc;
3325}
3326
3327WINPR_ATTR_NODISCARD
3328static BOOL is_scancode_key(UINT32 scancode)
3329{
3330 switch (scancode)
3331 {
3332 case RDP_SCANCODE_SPACE:
3333 case RDP_SCANCODE_CAPSLOCK:
3334 case RDP_SCANCODE_LMENU:
3335 case RDP_SCANCODE_RMENU:
3336 case RDP_SCANCODE_LWIN:
3337 case RDP_SCANCODE_RWIN:
3338 case RDP_SCANCODE_RCONTROL:
3339 case RDP_SCANCODE_LCONTROL:
3340 case RDP_SCANCODE_F1:
3341 case RDP_SCANCODE_F2:
3342 case RDP_SCANCODE_F3:
3343 case RDP_SCANCODE_F4:
3344 case RDP_SCANCODE_F5:
3345 case RDP_SCANCODE_F6:
3346 case RDP_SCANCODE_F7:
3347 case RDP_SCANCODE_F8:
3348 case RDP_SCANCODE_F9:
3349 case RDP_SCANCODE_F10:
3350 case RDP_SCANCODE_F11:
3351 case RDP_SCANCODE_F12:
3352 case RDP_SCANCODE_F13:
3353 case RDP_SCANCODE_F14:
3354 case RDP_SCANCODE_F15:
3355 case RDP_SCANCODE_F16:
3356 case RDP_SCANCODE_F17:
3357 case RDP_SCANCODE_F18:
3358 case RDP_SCANCODE_F19:
3359 case RDP_SCANCODE_F20:
3360 case RDP_SCANCODE_F21:
3361 case RDP_SCANCODE_F22:
3362 case RDP_SCANCODE_F23:
3363 case RDP_SCANCODE_F24:
3364 case RDP_SCANCODE_HOME:
3365 case RDP_SCANCODE_END:
3366 case RDP_SCANCODE_UP:
3367 case RDP_SCANCODE_DOWN:
3368 case RDP_SCANCODE_LEFT:
3369 case RDP_SCANCODE_RIGHT:
3370 case RDP_SCANCODE_PRIOR:
3371 case RDP_SCANCODE_NEXT:
3372 case RDP_SCANCODE_INSERT:
3373 case RDP_SCANCODE_DELETE:
3374 case RDP_SCANCODE_NUMLOCK:
3375 case RDP_SCANCODE_NUMPAD0:
3376 case RDP_SCANCODE_NUMPAD1:
3377 case RDP_SCANCODE_NUMPAD2:
3378 case RDP_SCANCODE_NUMPAD3:
3379 case RDP_SCANCODE_NUMPAD4:
3380 case RDP_SCANCODE_NUMPAD5:
3381 case RDP_SCANCODE_NUMPAD6:
3382 case RDP_SCANCODE_NUMPAD7:
3383 case RDP_SCANCODE_NUMPAD8:
3384 case RDP_SCANCODE_NUMPAD9:
3385 case RDP_SCANCODE_SYSREQ:
3386 case RDP_SCANCODE_SLEEP:
3387 case RDP_SCANCODE_CONVERT_JP:
3388 case RDP_SCANCODE_NONCONVERT_JP:
3389 case RDP_SCANCODE_RETURN_KP:
3390 case RDP_SCANCODE_RETURN:
3391 case RDP_SCANCODE_VOLUME_MUTE:
3392 case RDP_SCANCODE_VOLUME_DOWN:
3393 case RDP_SCANCODE_VOLUME_UP:
3394 return TRUE;
3395 default:
3396 return FALSE;
3397 }
3398}
3399BOOL freerdp_client_send_key_as_unicode(rdpClientContext* cctx, UINT16 flags, const char* str,
3400 UINT32 scancode, UINT32 modmask)
3401{
3402 const BOOL asKey = is_scancode_key(scancode);
3403 const BOOL modPressed =
3404 (modmask & (FREERDP_CLIENT_KBD_MOD_LCONTROL | FREERDP_CLIENT_KBD_MOD_RCONTROL |
3405 FREERDP_CLIENT_KBD_MOD_LMENU | FREERDP_CLIENT_KBD_MOD_RMENU |
3406 FREERDP_CLIENT_KBD_MOD_LWIN | FREERDP_CLIENT_KBD_MOD_RWIN)) != 0;
3407 WCHAR wbuffer[32] = WINPR_C_ARRAY_INIT;
3408 SSIZE_T rc = -1;
3409 if (!asKey && !modPressed)
3410 rc = ConvertUtf8ToWChar(str, wbuffer, ARRAYSIZE(wbuffer));
3411 if (rc <= 0)
3412 {
3413 return freerdp_input_send_keyboard_event_ex(
3414 cctx->context.input, (flags & KBD_FLAGS_RELEASE) == 0, FALSE, scancode);
3415 }
3416 for (SSIZE_T x = 0; x < rc; x++)
3417 {
3418 if (!freerdp_input_send_unicode_keyboard_event(cctx->context.input, flags, wbuffer[x]))
3419 return FALSE;
3420 }
3421 return TRUE;
3422}
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.