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#if defined(CHANNEL_AINPUT_CLIENT)
1650 if (cctx->ainput)
1651 {
1652 UINT rc = 0;
1653 UINT64 flags = 0;
1654 INT32 x = 0;
1655 INT32 y = 0;
1656 INT32 value = mflags & 0xFF;
1657
1658 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1659 value = -1 * (0x100 - value);
1660
1661 /* We have discrete steps, scale this so we can also support high
1662 * resolution wheels. */
1663 value *= 0x10000;
1664
1665 if (mflags & PTR_FLAGS_WHEEL)
1666 {
1667 flags |= AINPUT_FLAGS_WHEEL;
1668 y = value;
1669 }
1670
1671 if (mflags & PTR_FLAGS_HWHEEL)
1672 {
1673 flags |= AINPUT_FLAGS_WHEEL;
1674 x = value;
1675 }
1676
1677 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1678 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1679 if (rc == CHANNEL_RC_OK)
1680 handled = TRUE;
1681 }
1682#endif
1683
1684 if (!handled)
1685 freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1686
1687 return TRUE;
1688}
1689
1690#if defined(CHANNEL_AINPUT_CLIENT)
1691static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1692{
1693 UINT rc = 0;
1694
1695 WINPR_ASSERT(cctx);
1696 WINPR_ASSERT(cctx->ainput);
1697 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1698
1699 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1700
1701 return rc == CHANNEL_RC_OK;
1702}
1703#endif
1704
1705static bool button_pressed(const rdpClientContext* cctx)
1706{
1707 WINPR_ASSERT(cctx);
1708 for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1709 {
1710 const BOOL cur = cctx->pressed_buttons[x];
1711 if (cur)
1712 return true;
1713 }
1714 return false;
1715}
1716
1717BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1718 INT32 y)
1719{
1720 BOOL handled = FALSE;
1721
1722 WINPR_ASSERT(cctx);
1723
1724 if (mflags & PTR_FLAGS_BUTTON1)
1725 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1726 if (mflags & PTR_FLAGS_BUTTON2)
1727 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1728 if (mflags & PTR_FLAGS_BUTTON3)
1729 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1730
1731 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1732 !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1733 {
1734 if (!button_pressed(cctx))
1735 return TRUE;
1736 }
1737
1738 const BOOL haveRelative =
1739 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1740 if (relative && haveRelative)
1741 {
1742 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1743 WINPR_ASSERTING_INT_CAST(int16_t, x),
1744 WINPR_ASSERTING_INT_CAST(int16_t, y));
1745 }
1746
1747#if defined(CHANNEL_AINPUT_CLIENT)
1748 if (cctx->ainput)
1749 {
1750 UINT64 flags = 0;
1751
1752 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1753 flags |= AINPUT_FLAGS_HAVE_REL;
1754
1755 if (relative)
1756 flags |= AINPUT_FLAGS_REL;
1757
1758 if (mflags & PTR_FLAGS_DOWN)
1759 flags |= AINPUT_FLAGS_DOWN;
1760 if (mflags & PTR_FLAGS_BUTTON1)
1761 flags |= AINPUT_FLAGS_BUTTON1;
1762 if (mflags & PTR_FLAGS_BUTTON2)
1763 flags |= AINPUT_FLAGS_BUTTON2;
1764 if (mflags & PTR_FLAGS_BUTTON3)
1765 flags |= AINPUT_FLAGS_BUTTON3;
1766 if (mflags & PTR_FLAGS_MOVE)
1767 flags |= AINPUT_FLAGS_MOVE;
1768 handled = ainput_send_diff_event(cctx, flags, x, y);
1769 }
1770#endif
1771
1772 if (!handled)
1773 {
1774 if (relative)
1775 {
1776 cctx->lastX += x;
1777 cctx->lastY += y;
1778 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1779 }
1780 else
1781 {
1782 cctx->lastX = x;
1783 cctx->lastY = y;
1784 }
1785 return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1786 (UINT16)cctx->lastY);
1787 }
1788 return TRUE;
1789}
1790
1791BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1792 INT32 x, INT32 y)
1793{
1794 BOOL handled = FALSE;
1795 WINPR_ASSERT(cctx);
1796
1797 if (mflags & PTR_XFLAGS_BUTTON1)
1798 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1799 if (mflags & PTR_XFLAGS_BUTTON2)
1800 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1801
1802 const BOOL haveRelative =
1803 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1804 if (relative && haveRelative)
1805 {
1806 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1807 WINPR_ASSERTING_INT_CAST(int16_t, x),
1808 WINPR_ASSERTING_INT_CAST(int16_t, y));
1809 }
1810
1811#if defined(CHANNEL_AINPUT_CLIENT)
1812 if (cctx->ainput)
1813 {
1814 UINT64 flags = 0;
1815
1816 if (relative)
1817 flags |= AINPUT_FLAGS_REL;
1818 if (mflags & PTR_XFLAGS_DOWN)
1819 flags |= AINPUT_FLAGS_DOWN;
1820 if (mflags & PTR_XFLAGS_BUTTON1)
1821 flags |= AINPUT_XFLAGS_BUTTON1;
1822 if (mflags & PTR_XFLAGS_BUTTON2)
1823 flags |= AINPUT_XFLAGS_BUTTON2;
1824
1825 handled = ainput_send_diff_event(cctx, flags, x, y);
1826 }
1827#endif
1828
1829 if (!handled)
1830 {
1831 if (relative)
1832 {
1833 cctx->lastX += x;
1834 cctx->lastY += y;
1835 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1836 }
1837 else
1838 {
1839 cctx->lastX = x;
1840 cctx->lastY = y;
1841 }
1842 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1843 (UINT16)cctx->lastY);
1844 }
1845
1846 return TRUE;
1847}
1848
1849static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1850 const FreeRDP_TouchContact* contact)
1851{
1852 const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1853 const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1854 WINPR_ASSERT(contact);
1855 WINPR_ASSERT(contact->x <= UINT16_MAX);
1856 WINPR_ASSERT(contact->y <= UINT16_MAX);
1857
1858 switch (contact->count)
1859 {
1860 case 1:
1861 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1862 contact->x, contact->y);
1863 case 2:
1864 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1865 contact->x, contact->y);
1866 case 3:
1867 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1868 contact->x, contact->y);
1869 case 4:
1870 return freerdp_client_send_extended_button_event(
1871 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1872 case 5:
1873 return freerdp_client_send_extended_button_event(
1874 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1875 default:
1876 /* unmapped events, ignore */
1877 return TRUE;
1878 }
1879}
1880
1881static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1882{
1883 WINPR_ASSERT(cctx);
1884 WINPR_ASSERT(contact);
1885
1886#if defined(CHANNEL_RDPEI_CLIENT)
1887 RdpeiClientContext* rdpei = cctx->rdpei;
1888
1889 if (!rdpei)
1890 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1891
1892 int contactId = 0;
1893
1894 if (rdpei->TouchRawEvent)
1895 {
1896 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1897 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1898 ? CONTACT_DATA_PRESSURE_PRESENT
1899 : 0;
1900 // Ensure contact position is unchanged from "engaged" to "out of range" state
1901 const UINT rc1 =
1902 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1903 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1904 RDPINPUT_CONTACT_FLAG_INCONTACT,
1905 contactFlags, contact->pressure);
1906 if (rc1 != CHANNEL_RC_OK)
1907 return FALSE;
1908
1909 const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
1910 &contactId, flags, contactFlags, contact->pressure);
1911 if (rc2 != CHANNEL_RC_OK)
1912 return FALSE;
1913 }
1914 else
1915 {
1916 WINPR_ASSERT(rdpei->TouchEnd);
1917 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1918 if (rc != CHANNEL_RC_OK)
1919 return FALSE;
1920 }
1921 return TRUE;
1922#else
1923 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1924 "-DCHANNEL_RDPEI_CLIENT=ON");
1925 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1926#endif
1927}
1928
1929static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1930{
1931 WINPR_ASSERT(cctx);
1932 WINPR_ASSERT(contact);
1933
1934#if defined(CHANNEL_RDPEI_CLIENT)
1935 RdpeiClientContext* rdpei = cctx->rdpei;
1936
1937 // Emulate mouse click if touch is not possible, like in login screen
1938 if (!rdpei)
1939 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1940
1941 int contactId = 0;
1942
1943 if (rdpei->TouchRawEvent)
1944 {
1945 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1946 RDPINPUT_CONTACT_FLAG_INCONTACT;
1947 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1948 ? CONTACT_DATA_PRESSURE_PRESENT
1949 : 0;
1950 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1951 flags, contactFlags, contact->pressure);
1952 if (rc != CHANNEL_RC_OK)
1953 return FALSE;
1954 }
1955 else
1956 {
1957 WINPR_ASSERT(rdpei->TouchBegin);
1958 const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1959 if (rc != CHANNEL_RC_OK)
1960 return FALSE;
1961 }
1962
1963 return TRUE;
1964#else
1965 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1966 "-DCHANNEL_RDPEI_CLIENT=ON");
1967 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1968#endif
1969}
1970
1971static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
1972 const FreeRDP_TouchContact* contact)
1973{
1974 const UINT16 flags = PTR_FLAGS_MOVE;
1975
1976 WINPR_ASSERT(contact);
1977 WINPR_ASSERT(contact->x <= UINT16_MAX);
1978 WINPR_ASSERT(contact->y <= UINT16_MAX);
1979 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1980}
1981
1982static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1983{
1984 WINPR_ASSERT(cctx);
1985 WINPR_ASSERT(contact);
1986
1987#if defined(CHANNEL_RDPEI_CLIENT)
1988 RdpeiClientContext* rdpei = cctx->rdpei;
1989
1990 if (!rdpei)
1991 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
1992
1993 int contactId = 0;
1994
1995 if (rdpei->TouchRawEvent)
1996 {
1997 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1998 RDPINPUT_CONTACT_FLAG_INCONTACT;
1999 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2000 ? CONTACT_DATA_PRESSURE_PRESENT
2001 : 0;
2002 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2003 flags, contactFlags, contact->pressure);
2004 if (rc != CHANNEL_RC_OK)
2005 return FALSE;
2006 }
2007 else
2008 {
2009 WINPR_ASSERT(rdpei->TouchUpdate);
2010 const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2011 if (rc != CHANNEL_RC_OK)
2012 return FALSE;
2013 }
2014
2015 return TRUE;
2016#else
2017 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2018 "-DCHANNEL_RDPEI_CLIENT=ON");
2019 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2020#endif
2021}
2022
2023static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2024{
2025 WINPR_ASSERT(cctx);
2026 WINPR_ASSERT(contact);
2027
2028#if defined(CHANNEL_RDPEI_CLIENT)
2029 RdpeiClientContext* rdpei = cctx->rdpei;
2030
2031 if (!rdpei)
2032 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2033
2034 int contactId = 0;
2035
2036 if (rdpei->TouchRawEvent)
2037 {
2038 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2039 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2040 ? CONTACT_DATA_PRESSURE_PRESENT
2041 : 0;
2042 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2043 flags, contactFlags, contact->pressure);
2044 if (rc != CHANNEL_RC_OK)
2045 return FALSE;
2046 }
2047 else
2048 {
2049 WINPR_ASSERT(rdpei->TouchUpdate);
2050 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2051 if (rc != CHANNEL_RC_OK)
2052 return FALSE;
2053 }
2054
2055 return TRUE;
2056#else
2057 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2058 "-DCHANNEL_RDPEI_CLIENT=ON");
2059 return freerdp_handle_touch_to_mouse(cctx, false, contact);
2060#endif
2061}
2062
2063static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2064 UINT32 pressure, INT32 x, INT32 y,
2065 FreeRDP_TouchContact* pcontact)
2066{
2067 WINPR_ASSERT(cctx);
2068 WINPR_ASSERT(pcontact);
2069
2070 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2071 {
2072 FreeRDP_TouchContact* contact = &cctx->contacts[i];
2073
2074 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2075 if (newcontact || (contact->id == touchId))
2076 {
2077 contact->id = touchId;
2078 contact->flags = flags;
2079 contact->pressure = pressure;
2080 contact->x = x;
2081 contact->y = y;
2082
2083 *pcontact = *contact;
2084
2085 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2086 if (resetcontact)
2087 {
2088 FreeRDP_TouchContact empty = WINPR_C_ARRAY_INIT;
2089 *contact = empty;
2090 }
2091 return TRUE;
2092 }
2093 }
2094
2095 return FALSE;
2096}
2097
2098BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2099 UINT32 pressure, INT32 x, INT32 y)
2100{
2101 const UINT32 mask =
2102 FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2103 WINPR_ASSERT(cctx);
2104
2105 FreeRDP_TouchContact contact = WINPR_C_ARRAY_INIT;
2106
2107 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2108 return FALSE;
2109
2110 switch (flags & mask)
2111 {
2112 case FREERDP_TOUCH_DOWN:
2113 return freerdp_handle_touch_down(cctx, &contact);
2114 case FREERDP_TOUCH_UP:
2115 return freerdp_handle_touch_up(cctx, &contact);
2116 case FREERDP_TOUCH_MOTION:
2117 return freerdp_handle_touch_motion(cctx, &contact);
2118 case FREERDP_TOUCH_CANCEL:
2119 return freerdp_handle_touch_cancel(cctx, &contact);
2120 default:
2121 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %" PRIu32 ", ignoring", flags);
2122 return FALSE;
2123 }
2124}
2125
2126BOOL freerdp_client_load_channels(freerdp* instance)
2127{
2128 WINPR_ASSERT(instance);
2129 WINPR_ASSERT(instance->context);
2130
2131 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2132 {
2133 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2134 return FALSE;
2135 }
2136 return TRUE;
2137}
2138
2139int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2140{
2141 const char* str_data = freerdp_get_logon_error_info_data(data);
2142 const char* str_type = freerdp_get_logon_error_info_type(type);
2143
2144 if (!instance || !instance->context)
2145 return -1;
2146
2147 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2148 return 1;
2149}
2150
2151static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2152 size_t* pos)
2153{
2154 WINPR_ASSERT(cctx);
2155
2156 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2157 {
2158 FreeRDP_PenDevice* pen = &cctx->pens[i];
2159 if (deviceid == pen->deviceid)
2160 {
2161 if (pos)
2162 *pos = i;
2163 return pen;
2164 }
2165 }
2166 return nullptr;
2167}
2168
2169static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2170 double pressure)
2171{
2172 static const INT32 null_deviceid = 0;
2173
2174 WINPR_ASSERT(cctx);
2175 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2176 if (freerdp_client_is_pen(cctx, deviceid))
2177 {
2178 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2179 return FALSE;
2180 }
2181
2182 size_t pos = 0;
2183 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2184 if (pen)
2185 {
2186 const FreeRDP_PenDevice empty = WINPR_C_ARRAY_INIT;
2187 *pen = empty;
2188
2189 pen->deviceid = deviceid;
2190 pen->max_pressure = pressure;
2191 pen->flags = flags;
2192
2193 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2194 return TRUE;
2195 }
2196
2197 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2198 return TRUE;
2199}
2200
2201BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2202{
2203#if defined(CHANNEL_RDPEI_CLIENT)
2204 if ((flags & FREERDP_PEN_REGISTER) != 0)
2205 {
2206 va_list args = WINPR_C_ARRAY_INIT;
2207
2208 va_start(args, deviceid);
2209 double pressure = va_arg(args, double);
2210 va_end(args);
2211 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2212 }
2213 size_t pos = 0;
2214 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2215 if (!pen)
2216 {
2217 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2218 return FALSE;
2219 }
2220
2221 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2222 UINT32 penFlags =
2223 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2224
2225 RdpeiClientContext* rdpei = cctx->rdpei;
2226 WINPR_ASSERT(rdpei);
2227
2228 UINT32 normalizedpressure = 1024;
2229 INT32 x = 0;
2230 INT32 y = 0;
2231 UINT16 rotation = 0;
2232 INT16 tiltX = 0;
2233 INT16 tiltY = 0;
2234 va_list args = WINPR_C_ARRAY_INIT;
2235 va_start(args, deviceid);
2236
2237 x = va_arg(args, INT32);
2238 y = va_arg(args, INT32);
2239 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2240 {
2241 const double pressure = va_arg(args, double);
2242 const double np = (pressure * 1024.0) / pen->max_pressure;
2243 normalizedpressure = (UINT32)lround(np);
2244 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2245 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2246 }
2247 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2248 {
2249 const unsigned arg = va_arg(args, unsigned);
2250 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2251 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2252 }
2253 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2254 {
2255 const int arg = va_arg(args, int);
2256 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2257 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2258 }
2259 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2260 {
2261 const int arg = va_arg(args, int);
2262 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2263 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2264 }
2265 va_end(args);
2266
2267 if ((flags & FREERDP_PEN_PRESS) != 0)
2268 {
2269 // Ensure that only one button is pressed
2270 if (pen->pressed)
2271 flags = FREERDP_PEN_MOTION |
2272 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2273 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2274 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2275 }
2276 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2277 {
2278 if (!pen->pressed ||
2279 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2280 flags = FREERDP_PEN_MOTION |
2281 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2282 else
2283 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2284 }
2285
2286 flags |= pen->flags;
2287 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2288 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2289 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2290 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2291
2292 pen->last_x = x;
2293 pen->last_y = y;
2294 if ((flags & FREERDP_PEN_PRESS) != 0)
2295 {
2296 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2297 pen->hovering = FALSE;
2298 pen->pressed = TRUE;
2299
2300 WINPR_ASSERT(rdpei->PenBegin);
2301 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2302 normalizedpressure, rotation, tiltX, tiltY);
2303 return rc == CHANNEL_RC_OK;
2304 }
2305 else if ((flags & FREERDP_PEN_MOTION) != 0)
2306 {
2307 UINT rc = ERROR_INTERNAL_ERROR;
2308 if (pen->pressed)
2309 {
2310 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2311
2312 // TODO: what if no rotation is supported but tilt is?
2313 WINPR_ASSERT(rdpei->PenUpdate);
2314 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2315 rotation, tiltX, tiltY);
2316 }
2317 else if (pen->hovering)
2318 {
2319 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2320
2321 WINPR_ASSERT(rdpei->PenHoverUpdate);
2322 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2323 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2324 }
2325 else
2326 {
2327 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2328 pen->hovering = TRUE;
2329
2330 WINPR_ASSERT(rdpei->PenHoverBegin);
2331 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2332 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2333 }
2334 return rc == CHANNEL_RC_OK;
2335 }
2336 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2337 {
2338 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2339 pen->pressed = FALSE;
2340 pen->hovering = TRUE;
2341
2342 WINPR_ASSERT(rdpei->PenUpdate);
2343 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2344 normalizedpressure, rotation, tiltX, tiltY);
2345 if (rc != CHANNEL_RC_OK)
2346 return FALSE;
2347 WINPR_ASSERT(rdpei->PenEnd);
2348 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2349 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2350 return re == CHANNEL_RC_OK;
2351 }
2352
2353 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2354#else
2355 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2356 "-DCHANNEL_RDPEI_CLIENT=ON");
2357#endif
2358
2359 return FALSE;
2360}
2361
2362BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2363{
2364 WINPR_ASSERT(cctx);
2365
2366#if defined(CHANNEL_RDPEI_CLIENT)
2367 RdpeiClientContext* rdpei = cctx->rdpei;
2368
2369 if (!rdpei)
2370 return FALSE;
2371
2372 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2373 {
2374 FreeRDP_PenDevice* pen = &cctx->pens[i];
2375 if (pen->hovering)
2376 {
2377 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2378 pen->hovering = FALSE;
2379 const UINT rc =
2380 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2381 if (rc != CHANNEL_RC_OK)
2382 return FALSE;
2383 }
2384 }
2385 return TRUE;
2386#else
2387 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2388 "-DCHANNEL_RDPEI_CLIENT=ON");
2389 return FALSE;
2390#endif
2391}
2392
2393BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2394{
2395 WINPR_ASSERT(cctx);
2396
2397 if (deviceid == 0)
2398 return FALSE;
2399
2400 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2401 {
2402 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2403 if (pen->deviceid == deviceid)
2404 return TRUE;
2405 }
2406
2407 return FALSE;
2408}
2409
2410BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* cctx)
2411{
2412 WINPR_ASSERT(cctx);
2413
2414 const rdpSettings* settings = cctx->context.settings;
2415 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2416 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2417 BOOL ainput = FALSE;
2418#if defined(CHANNEL_AINPUT_CLIENT)
2419 ainput = cctx->ainput != nullptr;
2420#endif
2421
2422 return useRelative && (haveRelative || ainput);
2423}
2424
2425#if defined(WITH_AAD)
2426WINPR_ATTR_MALLOC(free, 1)
2427WINPR_ATTR_NODISCARD
2428static char* get_redirect_uri(const rdpSettings* settings)
2429{
2430 char* redirect_uri = nullptr;
2431 const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2432 if (cli)
2433 {
2434 const char* redirect_fmt =
2435 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2436 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2437 const char* tenantid = "common";
2438 if (useTenant)
2439 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2440
2441 if (tenantid && redirect_fmt)
2442 {
2443 const char* url =
2444 freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2445
2446 size_t redirect_len = 0;
2447 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2448 }
2449 }
2450 else
2451 {
2452 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2453 const char* redirect_fmt =
2454 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2455
2456 size_t redirect_len = 0;
2457 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2458 }
2459 return redirect_uri;
2460}
2461
2462static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2463{
2464 const rdpSettings* settings = cctx->context.settings;
2465 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2466 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2467 AAD_WELLKNOWN_authorization_endpoint);
2468 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2469
2470 if (!client_id || !ep || !scope)
2471 return nullptr;
2472
2473 char* redirect_uri = get_redirect_uri(settings);
2474 if (!redirect_uri)
2475 return nullptr;
2476
2477 char* url = nullptr;
2478 size_t urllen = 0;
2479 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2480 client_id, scope, redirect_uri);
2481 free(redirect_uri);
2482 return url;
2483}
2484
2485static char* avd_token_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
2503 const char* code = va_arg(ap, const char*);
2504 winpr_asprintf(&url, &urllen,
2505 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2506 code, client_id, scope, redirect_uri);
2507 free(redirect_uri);
2508 return url;
2509}
2510
2511static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2512{
2513 const rdpSettings* settings = cctx->context.settings;
2514 char* url = nullptr;
2515 size_t urllen = 0;
2516 char* redirect_uri = get_redirect_uri(settings);
2517
2518 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2519 if (!client_id || !redirect_uri)
2520 goto cleanup;
2521
2522 {
2523 const char* scope = va_arg(ap, const char*);
2524 if (!scope)
2525 goto cleanup;
2526
2527 {
2528 const char* ep = freerdp_utils_aad_get_wellknown_string(
2529 &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2530 winpr_asprintf(&url, &urllen,
2531 "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2532 client_id, scope, redirect_uri);
2533 }
2534 }
2535
2536cleanup:
2537 free(redirect_uri);
2538 return url;
2539}
2540
2541static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2542{
2543 const rdpSettings* settings = cctx->context.settings;
2544 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2545 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2546 AAD_WELLKNOWN_authorization_endpoint);
2547 const char* scope = va_arg(ap, const char*);
2548 const char* code = va_arg(ap, const char*);
2549 const char* req_cnf = va_arg(ap, const char*);
2550
2551 if (!client_id || !ep || !scope || !code || !req_cnf)
2552 return nullptr;
2553
2554 char* redirect_uri = get_redirect_uri(settings);
2555 if (!redirect_uri)
2556 return nullptr;
2557
2558 char* url = nullptr;
2559 size_t urllen = 0;
2560
2561 winpr_asprintf(
2562 &url, &urllen,
2563 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2564 code, client_id, scope, redirect_uri, req_cnf);
2565 free(redirect_uri);
2566 return url;
2567}
2568#endif
2569
2570char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2571{
2572 WINPR_ASSERT(cctx);
2573 char* str = nullptr;
2574
2575 va_list ap = WINPR_C_ARRAY_INIT;
2576 va_start(ap, type);
2577 switch (type)
2578 {
2579#if defined(WITH_AAD)
2580 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2581 str = aad_auth_request(cctx, ap);
2582 break;
2583 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2584 str = aad_token_request(cctx, ap);
2585 break;
2586 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2587 str = avd_auth_request(cctx, ap);
2588 break;
2589 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2590 str = avd_token_request(cctx, ap);
2591 break;
2592#endif
2593 default:
2594 break;
2595 }
2596 va_end(ap);
2597 return str;
2598}
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 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 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_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
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.