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 {
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 != NULL);
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 = NULL;
1127 char* token_request = NULL;
1128
1129 WINPR_ASSERT(token);
1130
1131 BOOL rc = FALSE;
1132
1133 *token = NULL;
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 != NULL);
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 = { 0 };
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 [%" PRIuz "], 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 = NULL;
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] = { 0 };
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, NULL);
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 = NULL;
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 switch (freerdp_get_last_error(instance->context))
1368 {
1369 case FREERDP_ERROR_CONNECT_CANCELLED:
1370 WLog_WARN(TAG, "Connection aborted by user");
1371 return FALSE;
1372 default:
1373 break;
1374 }
1375
1376 /* Perform an auto-reconnect. */
1377 while (retry)
1378 {
1379 /* Quit retrying if max retries has been exceeded */
1380 if ((maxRetries > 0) && (numRetries >= maxRetries))
1381 {
1382 WLog_DBG(TAG, "AutoReconnect retries exceeded.");
1383 return FALSE;
1384 }
1385
1386 /* Attempt the next reconnect */
1387 WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1388
1389 const SSIZE_T delay =
1390 IFCALLRESULT(5000, instance->RetryDialog, instance, "connection", numRetries, NULL);
1391 if (delay < 0)
1392 return FALSE;
1393 numRetries++;
1394
1395 if (freerdp_reconnect(instance))
1396 return TRUE;
1397
1398 switch (freerdp_get_last_error(instance->context))
1399 {
1400 case FREERDP_ERROR_CONNECT_CANCELLED:
1401 WLog_WARN(TAG, "Autoreconnect aborted by user");
1402 return FALSE;
1403 default:
1404 break;
1405 }
1406 for (UINT32 x = 0; x < delay / 10; x++)
1407 {
1408 if (!IFCALLRESULT(TRUE, window_events, instance))
1409 {
1410 WLog_ERR(TAG, "window_events failed!");
1411 return FALSE;
1412 }
1413
1414 Sleep(10);
1415 }
1416 }
1417
1418 WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1419 return FALSE;
1420}
1421
1422int freerdp_client_common_stop(rdpContext* context)
1423{
1424 rdpClientContext* cctx = (rdpClientContext*)context;
1425 WINPR_ASSERT(cctx);
1426
1427 freerdp_abort_connect_context(&cctx->context);
1428
1429 if (cctx->thread)
1430 {
1431 (void)WaitForSingleObject(cctx->thread, INFINITE);
1432 (void)CloseHandle(cctx->thread);
1433 cctx->thread = NULL;
1434 }
1435
1436 return 0;
1437}
1438
1439#if defined(CHANNEL_ENCOMSP_CLIENT)
1440BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1441{
1442 rdpClientContext* cctx = NULL;
1443 BOOL state = 0;
1444
1445 if (!encomsp)
1446 return FALSE;
1447
1448 cctx = (rdpClientContext*)encomsp->custom;
1449
1450 state = cctx->controlToggle;
1451 cctx->controlToggle = !cctx->controlToggle;
1452 return freerdp_client_encomsp_set_control(encomsp, state);
1453}
1454
1455BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1456{
1458
1459 if (!encomsp)
1460 return FALSE;
1461
1462 pdu.ParticipantId = encomsp->participantId;
1463 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1464
1465 if (control)
1466 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1467
1468 encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1469
1470 return TRUE;
1471}
1472
1473static UINT
1474client_encomsp_participant_created(EncomspClientContext* context,
1475 const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1476{
1477 rdpClientContext* cctx = NULL;
1478 rdpSettings* settings = NULL;
1479 BOOL request = 0;
1480
1481 if (!context || !context->custom || !participantCreated)
1482 return ERROR_INVALID_PARAMETER;
1483
1484 cctx = (rdpClientContext*)context->custom;
1485 WINPR_ASSERT(cctx);
1486
1487 settings = cctx->context.settings;
1488 WINPR_ASSERT(settings);
1489
1490 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1491 context->participantId = participantCreated->ParticipantId;
1492
1493 request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1494 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1495 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1496 {
1497 if (!freerdp_client_encomsp_set_control(context, TRUE))
1498 return ERROR_INTERNAL_ERROR;
1499
1500 /* if auto-request-control setting is enabled then only request control once upon connect,
1501 * otherwise it will auto request control again every time server turns off control which
1502 * is a bit annoying */
1503 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1504 return ERROR_INTERNAL_ERROR;
1505 }
1506
1507 return CHANNEL_RC_OK;
1508}
1509
1510static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1511{
1512 cctx->encomsp = encomsp;
1513 encomsp->custom = (void*)cctx;
1514 encomsp->ParticipantCreated = client_encomsp_participant_created;
1515}
1516
1517static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1518{
1519 if (encomsp)
1520 {
1521 encomsp->custom = NULL;
1522 encomsp->ParticipantCreated = NULL;
1523 }
1524
1525 if (cctx)
1526 cctx->encomsp = NULL;
1527}
1528#endif
1529
1530void freerdp_client_OnChannelConnectedEventHandler(void* context,
1531 const ChannelConnectedEventArgs* e)
1532{
1533 rdpClientContext* cctx = (rdpClientContext*)context;
1534
1535 WINPR_ASSERT(cctx);
1536 WINPR_ASSERT(e);
1537
1538 if (0)
1539 {
1540 }
1541#if defined(CHANNEL_AINPUT_CLIENT)
1542 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1543 cctx->ainput = (AInputClientContext*)e->pInterface;
1544#endif
1545#if defined(CHANNEL_RDPEI_CLIENT)
1546 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1547 {
1548 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1549 }
1550#endif
1551#if defined(CHANNEL_RDPGFX_CLIENT)
1552 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1553 {
1554 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1555 }
1556#endif
1557#if defined(CHANNEL_GEOMETRY_CLIENT)
1558 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1559 {
1560 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1561 }
1562#endif
1563#if defined(CHANNEL_VIDEO_CLIENT)
1564 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1565 {
1566 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1567 }
1568 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1569 {
1570 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1571 }
1572#endif
1573#if defined(CHANNEL_ENCOMSP_CLIENT)
1574 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1575 {
1576 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1577 }
1578#endif
1579}
1580
1581void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1582 const ChannelDisconnectedEventArgs* e)
1583{
1584 rdpClientContext* cctx = (rdpClientContext*)context;
1585
1586 WINPR_ASSERT(cctx);
1587 WINPR_ASSERT(e);
1588
1589 if (0)
1590 {
1591 }
1592#if defined(CHANNEL_AINPUT_CLIENT)
1593 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1594 cctx->ainput = NULL;
1595#endif
1596#if defined(CHANNEL_RDPEI_CLIENT)
1597 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1598 {
1599 cctx->rdpei = NULL;
1600 }
1601#endif
1602#if defined(CHANNEL_RDPGFX_CLIENT)
1603 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1604 {
1605 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1606 }
1607#endif
1608#if defined(CHANNEL_GEOMETRY_CLIENT)
1609 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1610 {
1611 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1612 }
1613#endif
1614#if defined(CHANNEL_VIDEO_CLIENT)
1615 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1616 {
1617 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1618 }
1619 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1620 {
1621 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1622 }
1623#endif
1624#if defined(CHANNEL_ENCOMSP_CLIENT)
1625 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1626 {
1627 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1628 }
1629#endif
1630}
1631
1632BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1633{
1634 BOOL handled = FALSE;
1635
1636 WINPR_ASSERT(cctx);
1637
1638#if defined(CHANNEL_AINPUT_CLIENT)
1639 if (cctx->ainput)
1640 {
1641 UINT rc = 0;
1642 UINT64 flags = 0;
1643 INT32 x = 0;
1644 INT32 y = 0;
1645 INT32 value = mflags & 0xFF;
1646
1647 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1648 value = -1 * (0x100 - value);
1649
1650 /* We have discrete steps, scale this so we can also support high
1651 * resolution wheels. */
1652 value *= 0x10000;
1653
1654 if (mflags & PTR_FLAGS_WHEEL)
1655 {
1656 flags |= AINPUT_FLAGS_WHEEL;
1657 y = value;
1658 }
1659
1660 if (mflags & PTR_FLAGS_HWHEEL)
1661 {
1662 flags |= AINPUT_FLAGS_WHEEL;
1663 x = value;
1664 }
1665
1666 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1667 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1668 if (rc == CHANNEL_RC_OK)
1669 handled = TRUE;
1670 }
1671#endif
1672
1673 if (!handled)
1674 freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1675
1676 return TRUE;
1677}
1678
1679#if defined(CHANNEL_AINPUT_CLIENT)
1680static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1681{
1682 UINT rc = 0;
1683
1684 WINPR_ASSERT(cctx);
1685 WINPR_ASSERT(cctx->ainput);
1686 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1687
1688 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1689
1690 return rc == CHANNEL_RC_OK;
1691}
1692#endif
1693
1694static bool button_pressed(const rdpClientContext* cctx)
1695{
1696 WINPR_ASSERT(cctx);
1697 for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1698 {
1699 const BOOL cur = cctx->pressed_buttons[x];
1700 if (cur)
1701 return true;
1702 }
1703 return false;
1704}
1705
1706BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1707 INT32 y)
1708{
1709 BOOL handled = FALSE;
1710
1711 WINPR_ASSERT(cctx);
1712
1713 if (mflags & PTR_FLAGS_BUTTON1)
1714 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1715 if (mflags & PTR_FLAGS_BUTTON2)
1716 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1717 if (mflags & PTR_FLAGS_BUTTON3)
1718 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1719
1720 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1721 !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1722 {
1723 if (!button_pressed(cctx))
1724 return TRUE;
1725 }
1726
1727 const BOOL haveRelative =
1728 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1729 if (relative && haveRelative)
1730 {
1731 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1732 WINPR_ASSERTING_INT_CAST(int16_t, x),
1733 WINPR_ASSERTING_INT_CAST(int16_t, y));
1734 }
1735
1736#if defined(CHANNEL_AINPUT_CLIENT)
1737 if (cctx->ainput)
1738 {
1739 UINT64 flags = 0;
1740
1741 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1742 flags |= AINPUT_FLAGS_HAVE_REL;
1743
1744 if (relative)
1745 flags |= AINPUT_FLAGS_REL;
1746
1747 if (mflags & PTR_FLAGS_DOWN)
1748 flags |= AINPUT_FLAGS_DOWN;
1749 if (mflags & PTR_FLAGS_BUTTON1)
1750 flags |= AINPUT_FLAGS_BUTTON1;
1751 if (mflags & PTR_FLAGS_BUTTON2)
1752 flags |= AINPUT_FLAGS_BUTTON2;
1753 if (mflags & PTR_FLAGS_BUTTON3)
1754 flags |= AINPUT_FLAGS_BUTTON3;
1755 if (mflags & PTR_FLAGS_MOVE)
1756 flags |= AINPUT_FLAGS_MOVE;
1757 handled = ainput_send_diff_event(cctx, flags, x, y);
1758 }
1759#endif
1760
1761 if (!handled)
1762 {
1763 if (relative)
1764 {
1765 cctx->lastX += x;
1766 cctx->lastY += y;
1767 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1768 }
1769 else
1770 {
1771 cctx->lastX = x;
1772 cctx->lastY = y;
1773 }
1774 freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1775 (UINT16)cctx->lastY);
1776 }
1777 return TRUE;
1778}
1779
1780BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1781 INT32 x, INT32 y)
1782{
1783 BOOL handled = FALSE;
1784 WINPR_ASSERT(cctx);
1785
1786 if (mflags & PTR_XFLAGS_BUTTON1)
1787 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1788 if (mflags & PTR_XFLAGS_BUTTON2)
1789 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1790
1791 const BOOL haveRelative =
1792 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1793 if (relative && haveRelative)
1794 {
1795 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1796 WINPR_ASSERTING_INT_CAST(int16_t, x),
1797 WINPR_ASSERTING_INT_CAST(int16_t, y));
1798 }
1799
1800#if defined(CHANNEL_AINPUT_CLIENT)
1801 if (cctx->ainput)
1802 {
1803 UINT64 flags = 0;
1804
1805 if (relative)
1806 flags |= AINPUT_FLAGS_REL;
1807 if (mflags & PTR_XFLAGS_DOWN)
1808 flags |= AINPUT_FLAGS_DOWN;
1809 if (mflags & PTR_XFLAGS_BUTTON1)
1810 flags |= AINPUT_XFLAGS_BUTTON1;
1811 if (mflags & PTR_XFLAGS_BUTTON2)
1812 flags |= AINPUT_XFLAGS_BUTTON2;
1813
1814 handled = ainput_send_diff_event(cctx, flags, x, y);
1815 }
1816#endif
1817
1818 if (!handled)
1819 {
1820 if (relative)
1821 {
1822 cctx->lastX += x;
1823 cctx->lastY += y;
1824 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1825 }
1826 else
1827 {
1828 cctx->lastX = x;
1829 cctx->lastY = y;
1830 }
1831 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1832 (UINT16)cctx->lastY);
1833 }
1834
1835 return TRUE;
1836}
1837
1838static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1839 const FreeRDP_TouchContact* contact)
1840{
1841 const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1842 const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1843 WINPR_ASSERT(contact);
1844 WINPR_ASSERT(contact->x <= UINT16_MAX);
1845 WINPR_ASSERT(contact->y <= UINT16_MAX);
1846
1847 switch (contact->count)
1848 {
1849 case 1:
1850 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1851 contact->x, contact->y);
1852 case 2:
1853 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1854 contact->x, contact->y);
1855 case 3:
1856 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1857 contact->x, contact->y);
1858 case 4:
1859 return freerdp_client_send_extended_button_event(
1860 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1861 case 5:
1862 return freerdp_client_send_extended_button_event(
1863 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1864 default:
1865 /* unmapped events, ignore */
1866 return TRUE;
1867 }
1868}
1869
1870static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1871{
1872 WINPR_ASSERT(cctx);
1873 WINPR_ASSERT(contact);
1874
1875#if defined(CHANNEL_RDPEI_CLIENT)
1876 RdpeiClientContext* rdpei = cctx->rdpei;
1877
1878 if (!rdpei)
1879 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1880
1881 int contactId = 0;
1882
1883 if (rdpei->TouchRawEvent)
1884 {
1885 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1886 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1887 ? CONTACT_DATA_PRESSURE_PRESENT
1888 : 0;
1889 // Ensure contact position is unchanged from "engaged" to "out of range" state
1890 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1891 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1892 RDPINPUT_CONTACT_FLAG_INCONTACT,
1893 contactFlags, contact->pressure);
1894 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1895 contactFlags, contact->pressure);
1896 }
1897 else
1898 {
1899 WINPR_ASSERT(rdpei->TouchEnd);
1900 rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1901 }
1902 return TRUE;
1903#else
1904 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1905 "-DCHANNEL_RDPEI_CLIENT=ON");
1906 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1907#endif
1908}
1909
1910static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1911{
1912 WINPR_ASSERT(cctx);
1913 WINPR_ASSERT(contact);
1914
1915#if defined(CHANNEL_RDPEI_CLIENT)
1916 RdpeiClientContext* rdpei = cctx->rdpei;
1917
1918 // Emulate mouse click if touch is not possible, like in login screen
1919 if (!rdpei)
1920 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1921
1922 int contactId = 0;
1923
1924 if (rdpei->TouchRawEvent)
1925 {
1926 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1927 RDPINPUT_CONTACT_FLAG_INCONTACT;
1928 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1929 ? CONTACT_DATA_PRESSURE_PRESENT
1930 : 0;
1931 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1932 contactFlags, contact->pressure);
1933 }
1934 else
1935 {
1936 WINPR_ASSERT(rdpei->TouchBegin);
1937 rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1938 }
1939
1940 return TRUE;
1941#else
1942 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1943 "-DCHANNEL_RDPEI_CLIENT=ON");
1944 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1945#endif
1946}
1947
1948static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
1949 const FreeRDP_TouchContact* contact)
1950{
1951 const UINT16 flags = PTR_FLAGS_MOVE;
1952
1953 WINPR_ASSERT(contact);
1954 WINPR_ASSERT(contact->x <= UINT16_MAX);
1955 WINPR_ASSERT(contact->y <= UINT16_MAX);
1956 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1957}
1958
1959static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1960{
1961 WINPR_ASSERT(cctx);
1962 WINPR_ASSERT(contact);
1963
1964#if defined(CHANNEL_RDPEI_CLIENT)
1965 RdpeiClientContext* rdpei = cctx->rdpei;
1966
1967 if (!rdpei)
1968 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
1969
1970 int contactId = 0;
1971
1972 if (rdpei->TouchRawEvent)
1973 {
1974 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1975 RDPINPUT_CONTACT_FLAG_INCONTACT;
1976 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1977 ? CONTACT_DATA_PRESSURE_PRESENT
1978 : 0;
1979 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1980 contactFlags, contact->pressure);
1981 }
1982 else
1983 {
1984 WINPR_ASSERT(rdpei->TouchUpdate);
1985 rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
1986 }
1987
1988 return TRUE;
1989#else
1990 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1991 "-DCHANNEL_RDPEI_CLIENT=ON");
1992 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
1993#endif
1994}
1995
1996static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
1997 UINT32 pressure, INT32 x, INT32 y,
1998 FreeRDP_TouchContact* pcontact)
1999{
2000 WINPR_ASSERT(cctx);
2001 WINPR_ASSERT(pcontact);
2002
2003 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2004 {
2005 FreeRDP_TouchContact* contact = &cctx->contacts[i];
2006
2007 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2008 if (newcontact || (contact->id == touchId))
2009 {
2010 contact->id = touchId;
2011 contact->flags = flags;
2012 contact->pressure = pressure;
2013 contact->x = x;
2014 contact->y = y;
2015
2016 *pcontact = *contact;
2017
2018 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2019 if (resetcontact)
2020 {
2021 FreeRDP_TouchContact empty = { 0 };
2022 *contact = empty;
2023 }
2024 return TRUE;
2025 }
2026 }
2027
2028 return FALSE;
2029}
2030
2031BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2032 UINT32 pressure, INT32 x, INT32 y)
2033{
2034 const UINT32 mask = FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION;
2035 WINPR_ASSERT(cctx);
2036
2037 FreeRDP_TouchContact contact = { 0 };
2038
2039 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2040 return FALSE;
2041
2042 switch (flags & mask)
2043 {
2044 case FREERDP_TOUCH_DOWN:
2045 return freerdp_handle_touch_down(cctx, &contact);
2046 case FREERDP_TOUCH_UP:
2047 return freerdp_handle_touch_up(cctx, &contact);
2048 case FREERDP_TOUCH_MOTION:
2049 return freerdp_handle_touch_motion(cctx, &contact);
2050 default:
2051 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %d, ignoring", flags);
2052 return FALSE;
2053 }
2054}
2055
2056BOOL freerdp_client_load_channels(freerdp* instance)
2057{
2058 WINPR_ASSERT(instance);
2059 WINPR_ASSERT(instance->context);
2060
2061 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2062 {
2063 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2064 return FALSE;
2065 }
2066 return TRUE;
2067}
2068
2069int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2070{
2071 const char* str_data = freerdp_get_logon_error_info_data(data);
2072 const char* str_type = freerdp_get_logon_error_info_type(type);
2073
2074 if (!instance || !instance->context)
2075 return -1;
2076
2077 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2078 return 1;
2079}
2080
2081static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2082 size_t* pos)
2083{
2084 WINPR_ASSERT(cctx);
2085
2086 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2087 {
2088 FreeRDP_PenDevice* pen = &cctx->pens[i];
2089 if (deviceid == pen->deviceid)
2090 {
2091 if (pos)
2092 *pos = i;
2093 return pen;
2094 }
2095 }
2096 return NULL;
2097}
2098
2099static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2100 double pressure)
2101{
2102 static const INT32 null_deviceid = 0;
2103
2104 WINPR_ASSERT(cctx);
2105 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2106 if (freerdp_client_is_pen(cctx, deviceid))
2107 {
2108 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2109 return FALSE;
2110 }
2111
2112 size_t pos = 0;
2113 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2114 if (pen)
2115 {
2116 const FreeRDP_PenDevice empty = { 0 };
2117 *pen = empty;
2118
2119 pen->deviceid = deviceid;
2120 pen->max_pressure = pressure;
2121 pen->flags = flags;
2122
2123 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2124 return TRUE;
2125 }
2126
2127 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2128 return TRUE;
2129}
2130
2131BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2132{
2133#if defined(CHANNEL_RDPEI_CLIENT)
2134 if ((flags & FREERDP_PEN_REGISTER) != 0)
2135 {
2136 va_list args;
2137
2138 va_start(args, deviceid);
2139 double pressure = va_arg(args, double);
2140 va_end(args);
2141 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2142 }
2143 size_t pos = 0;
2144 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2145 if (!pen)
2146 {
2147 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2148 return FALSE;
2149 }
2150
2151 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2152 UINT32 penFlags =
2153 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2154
2155 RdpeiClientContext* rdpei = cctx->rdpei;
2156 WINPR_ASSERT(rdpei);
2157
2158 UINT32 normalizedpressure = 1024;
2159 INT32 x = 0;
2160 INT32 y = 0;
2161 UINT16 rotation = 0;
2162 INT16 tiltX = 0;
2163 INT16 tiltY = 0;
2164 va_list args;
2165 va_start(args, deviceid);
2166
2167 x = va_arg(args, INT32);
2168 y = va_arg(args, INT32);
2169 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2170 {
2171 const double pressure = va_arg(args, double);
2172 const double np = (pressure * 1024.0) / pen->max_pressure;
2173 normalizedpressure = (UINT32)lround(np);
2174 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2175 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2176 }
2177 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2178 {
2179 const unsigned arg = va_arg(args, unsigned);
2180 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2181 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2182 }
2183 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2184 {
2185 const int arg = va_arg(args, int);
2186 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2187 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2188 }
2189 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2190 {
2191 const int arg = va_arg(args, int);
2192 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2193 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2194 }
2195 va_end(args);
2196
2197 if ((flags & FREERDP_PEN_PRESS) != 0)
2198 {
2199 // Ensure that only one button is pressed
2200 if (pen->pressed)
2201 flags = FREERDP_PEN_MOTION |
2202 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2203 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2204 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2205 }
2206 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2207 {
2208 if (!pen->pressed ||
2209 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2210 flags = FREERDP_PEN_MOTION |
2211 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2212 else
2213 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2214 }
2215
2216 flags |= pen->flags;
2217 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2218 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2219 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2220 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2221
2222 pen->last_x = x;
2223 pen->last_y = y;
2224 if ((flags & FREERDP_PEN_PRESS) != 0)
2225 {
2226 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2227 pen->hovering = FALSE;
2228 pen->pressed = TRUE;
2229
2230 WINPR_ASSERT(rdpei->PenBegin);
2231 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2232 normalizedpressure, rotation, tiltX, tiltY);
2233 return rc == CHANNEL_RC_OK;
2234 }
2235 else if ((flags & FREERDP_PEN_MOTION) != 0)
2236 {
2237 UINT rc = ERROR_INTERNAL_ERROR;
2238 if (pen->pressed)
2239 {
2240 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2241
2242 // TODO: what if no rotation is supported but tilt is?
2243 WINPR_ASSERT(rdpei->PenUpdate);
2244 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2245 rotation, tiltX, tiltY);
2246 }
2247 else if (pen->hovering)
2248 {
2249 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2250
2251 WINPR_ASSERT(rdpei->PenHoverUpdate);
2252 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2253 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2254 }
2255 else
2256 {
2257 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2258 pen->hovering = TRUE;
2259
2260 WINPR_ASSERT(rdpei->PenHoverBegin);
2261 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2262 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2263 }
2264 return rc == CHANNEL_RC_OK;
2265 }
2266 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2267 {
2268 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2269 pen->pressed = FALSE;
2270 pen->hovering = TRUE;
2271
2272 WINPR_ASSERT(rdpei->PenUpdate);
2273 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2274 normalizedpressure, rotation, tiltX, tiltY);
2275 if (rc != CHANNEL_RC_OK)
2276 return FALSE;
2277 WINPR_ASSERT(rdpei->PenEnd);
2278 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2279 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2280 return re == CHANNEL_RC_OK;
2281 }
2282
2283 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2284#else
2285 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2286 "-DCHANNEL_RDPEI_CLIENT=ON");
2287#endif
2288
2289 return FALSE;
2290}
2291
2292BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2293{
2294 WINPR_ASSERT(cctx);
2295
2296#if defined(CHANNEL_RDPEI_CLIENT)
2297 RdpeiClientContext* rdpei = cctx->rdpei;
2298
2299 if (!rdpei)
2300 return FALSE;
2301
2302 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2303 {
2304 FreeRDP_PenDevice* pen = &cctx->pens[i];
2305 if (pen->hovering)
2306 {
2307 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2308 pen->hovering = FALSE;
2309 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2310 }
2311 }
2312 return TRUE;
2313#else
2314 WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2315 "-DCHANNEL_RDPEI_CLIENT=ON");
2316 return FALSE;
2317#endif
2318}
2319
2320BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2321{
2322 WINPR_ASSERT(cctx);
2323
2324 if (deviceid == 0)
2325 return FALSE;
2326
2327 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2328 {
2329 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2330 if (pen->deviceid == deviceid)
2331 return TRUE;
2332 }
2333
2334 return FALSE;
2335}
2336
2337BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* ccontext)
2338{
2339 WINPR_ASSERT(ccontext);
2340
2341 const rdpSettings* settings = ccontext->context.settings;
2342 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2343 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2344 BOOL ainput = FALSE;
2345#if defined(CHANNEL_AINPUT_CLIENT)
2346 ainput = ccontext->ainput != NULL;
2347#endif
2348
2349 return useRelative && (haveRelative || ainput);
2350}
2351
2352#if defined(WITH_AAD)
2353WINPR_ATTR_MALLOC(free, 1)
2354static char* get_redirect_uri(const rdpSettings* settings)
2355{
2356 char* redirect_uri = NULL;
2357 const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2358 if (cli)
2359 {
2360 const char* redirect_fmt =
2361 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2362 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2363 const char* tenantid = "common";
2364 if (useTenant)
2365 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2366
2367 if (tenantid && redirect_fmt)
2368 {
2369 const char* url =
2370 freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2371
2372 size_t redirect_len = 0;
2373 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2374 }
2375 }
2376 else
2377 {
2378 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2379 const char* redirect_fmt =
2380 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2381
2382 size_t redirect_len = 0;
2383 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2384 }
2385 return redirect_uri;
2386}
2387
2388static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2389{
2390 const rdpSettings* settings = cctx->context.settings;
2391 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2392 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2393 AAD_WELLKNOWN_authorization_endpoint);
2394 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2395
2396 if (!client_id || !ep || !scope)
2397 return NULL;
2398
2399 char* redirect_uri = get_redirect_uri(settings);
2400 if (!redirect_uri)
2401 return NULL;
2402
2403 char* url = NULL;
2404 size_t urllen = 0;
2405 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2406 client_id, scope, redirect_uri);
2407 free(redirect_uri);
2408 return url;
2409}
2410
2411static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2412{
2413 const rdpSettings* settings = cctx->context.settings;
2414 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2415 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2416 AAD_WELLKNOWN_authorization_endpoint);
2417 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2418
2419 if (!client_id || !ep || !scope)
2420 return NULL;
2421
2422 char* redirect_uri = get_redirect_uri(settings);
2423 if (!redirect_uri)
2424 return NULL;
2425
2426 char* url = NULL;
2427 size_t urllen = 0;
2428
2429 const char* code = va_arg(ap, const char*);
2430 winpr_asprintf(&url, &urllen,
2431 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2432 code, client_id, scope, redirect_uri);
2433 free(redirect_uri);
2434 return url;
2435}
2436
2437static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2438{
2439 const rdpSettings* settings = cctx->context.settings;
2440 char* url = NULL;
2441 size_t urllen = 0;
2442 char* redirect_uri = get_redirect_uri(settings);
2443
2444 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2445 if (!client_id || !redirect_uri)
2446 goto cleanup;
2447
2448 {
2449 const char* scope = va_arg(ap, const char*);
2450 if (!scope)
2451 goto cleanup;
2452
2453 {
2454 const char* ep = freerdp_utils_aad_get_wellknown_string(
2455 &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2456 winpr_asprintf(&url, &urllen,
2457 "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2458 client_id, scope, redirect_uri);
2459 }
2460 }
2461
2462cleanup:
2463 free(redirect_uri);
2464 return url;
2465}
2466
2467static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2468{
2469 const rdpSettings* settings = cctx->context.settings;
2470 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2471 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2472 AAD_WELLKNOWN_authorization_endpoint);
2473 const char* scope = va_arg(ap, const char*);
2474 const char* code = va_arg(ap, const char*);
2475 const char* req_cnf = va_arg(ap, const char*);
2476
2477 if (!client_id || !ep || !scope || !code || !req_cnf)
2478 return NULL;
2479
2480 char* redirect_uri = get_redirect_uri(settings);
2481 if (!redirect_uri)
2482 return NULL;
2483
2484 char* url = NULL;
2485 size_t urllen = 0;
2486
2487 winpr_asprintf(
2488 &url, &urllen,
2489 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2490 code, client_id, scope, redirect_uri, req_cnf);
2491 free(redirect_uri);
2492 return url;
2493}
2494#endif
2495
2496char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2497{
2498 WINPR_ASSERT(cctx);
2499 char* str = NULL;
2500
2501 va_list ap;
2502 va_start(ap, type);
2503 switch (type)
2504 {
2505#if defined(WITH_AAD)
2506 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2507 str = aad_auth_request(cctx, ap);
2508 break;
2509 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2510 str = aad_token_request(cctx, ap);
2511 break;
2512 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2513 str = avd_auth_request(cctx, ap);
2514 break;
2515 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2516 str = avd_token_request(cctx, ap);
2517 break;
2518#endif
2519 default:
2520 break;
2521 }
2522 va_end(ap);
2523 return str;
2524}
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.