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