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