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_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
460static BOOL client_cli_authenticate_raw(freerdp* instance, rdp_auth_reason reason, char** username,
461 char** password, char** domain)
462{
463 static const size_t password_size = 512;
464 const char* auth[] = { "Username: ", "Domain: ", "Password: " };
465 const char* authPin[] = { "Username: ", "Domain: ", "Smartcard-Pin: " };
466 const char* gw[] = { "GatewayUsername: ", "GatewayDomain: ", "GatewayPassword: " };
467 const char** prompt = NULL;
468 BOOL pinOnly = FALSE;
469
470 WINPR_ASSERT(instance);
471 WINPR_ASSERT(instance->context);
472 WINPR_ASSERT(instance->context->settings);
473
474 switch (reason)
475 {
476 case AUTH_SMARTCARD_PIN:
477 prompt = authPin;
478 pinOnly = TRUE;
479 break;
480 case AUTH_TLS:
481 case AUTH_RDP:
482 case AUTH_NLA:
483 prompt = auth;
484 break;
485 case GW_AUTH_HTTP:
486 case GW_AUTH_RDG:
487 case GW_AUTH_RPC:
488 prompt = gw;
489 break;
490 default:
491 return FALSE;
492 }
493
494 if (!username || !password || !domain)
495 return FALSE;
496
497 if (!*username && !pinOnly)
498 {
499 size_t username_size = 0;
500 printf("%s", prompt[0]);
501 (void)fflush(stdout);
502
503 if (freerdp_interruptible_get_line(instance->context, username, &username_size, stdin) < 0)
504 {
505 char ebuffer[256] = { 0 };
506 WLog_ERR(TAG, "freerdp_interruptible_get_line returned %s [%d]",
507 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
508 goto fail;
509 }
510
511 if (*username)
512 {
513 *username = StrSep(username, "\r");
514 *username = StrSep(username, "\n");
515 }
516 }
517
518 if (!*domain && !pinOnly)
519 {
520 size_t domain_size = 0;
521 printf("%s", prompt[1]);
522 (void)fflush(stdout);
523
524 if (freerdp_interruptible_get_line(instance->context, domain, &domain_size, stdin) < 0)
525 {
526 char ebuffer[256] = { 0 };
527 WLog_ERR(TAG, "freerdp_interruptible_get_line returned %s [%d]",
528 winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
529 goto fail;
530 }
531
532 if (*domain)
533 {
534 *domain = StrSep(domain, "\r");
535 *domain = StrSep(domain, "\n");
536 }
537 }
538
539 if (!*password)
540 {
541 *password = calloc(password_size, sizeof(char));
542
543 if (!*password)
544 goto fail;
545
546 const BOOL fromStdin =
547 freerdp_settings_get_bool(instance->context->settings, FreeRDP_CredentialsFromStdin);
548 if (freerdp_passphrase_read(instance->context, prompt[2], *password, password_size,
549 fromStdin) == NULL)
550 goto fail;
551 }
552
553 return TRUE;
554fail:
555 free(*username);
556 free(*domain);
557 free(*password);
558 *username = NULL;
559 *domain = NULL;
560 *password = NULL;
561 return FALSE;
562}
563
564BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
565 rdp_auth_reason reason)
566{
567 WINPR_ASSERT(instance);
568 WINPR_ASSERT(username);
569 WINPR_ASSERT(password);
570 WINPR_ASSERT(domain);
571
572 switch (reason)
573 {
574 case AUTH_NLA:
575 break;
576
577 case AUTH_TLS:
578 case AUTH_RDP:
579 case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
580 if ((*username) && (*password))
581 return TRUE;
582 break;
583 case GW_AUTH_HTTP:
584 case GW_AUTH_RDG:
585 case GW_AUTH_RPC:
586 break;
587 default:
588 return FALSE;
589 }
590
591 return client_cli_authenticate_raw(instance, reason, username, password, domain);
592}
593
594BOOL client_cli_choose_smartcard(WINPR_ATTR_UNUSED freerdp* instance, SmartcardCertInfo** cert_list,
595 DWORD count, DWORD* choice, BOOL gateway)
596{
597 unsigned long answer = 0;
598 char* p = NULL;
599
600 printf("Multiple smartcards are available for use:\n");
601 for (DWORD i = 0; i < count; i++)
602 {
603 const SmartcardCertInfo* cert = cert_list[i];
604 char* reader = ConvertWCharToUtf8Alloc(cert->reader, NULL);
605 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, NULL);
606
607 printf("[%" PRIu32
608 "] %s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s\n",
609 i, container_name, reader, cert->userHint, cert->domainHint, cert->subject,
610 cert->issuer, cert->upn);
611
612 free(reader);
613 free(container_name);
614 }
615
616 while (1)
617 {
618 char input[10] = { 0 };
619
620 printf("\nChoose a smartcard to use for %s (0 - %" PRIu32 "): ",
621 gateway ? "gateway authentication" : "logon", count - 1);
622 (void)fflush(stdout);
623 if (!fgets(input, 10, stdin))
624 {
625 WLog_ERR(TAG, "could not read from stdin");
626 return FALSE;
627 }
628
629 answer = strtoul(input, &p, 10);
630 if ((*p == '\n' && p != input) && answer < count)
631 {
632 *choice = (UINT32)answer;
633 return TRUE;
634 }
635 }
636}
637
638#if defined(WITH_FREERDP_DEPRECATED)
639BOOL client_cli_authenticate(freerdp* instance, char** username, char** password, char** domain)
640{
641 if (freerdp_settings_get_bool(instance->settings, FreeRDP_SmartcardLogon))
642 {
643 WLog_INFO(TAG, "Authentication via smartcard");
644 return TRUE;
645 }
646
647 return client_cli_authenticate_raw(instance, FALSE, username, password, domain);
648}
649
650BOOL client_cli_gw_authenticate(freerdp* instance, char** username, char** password, char** domain)
651{
652 return client_cli_authenticate_raw(instance, TRUE, username, password, domain);
653}
654#endif
655
656static DWORD client_cli_accept_certificate(freerdp* instance)
657{
658 int answer = 0;
659
660 WINPR_ASSERT(instance);
661 WINPR_ASSERT(instance->context);
662
663 const rdpSettings* settings = instance->context->settings;
664 WINPR_ASSERT(settings);
665
666 const BOOL fromStdin = freerdp_settings_get_bool(settings, FreeRDP_CredentialsFromStdin);
667 if (fromStdin)
668 return 0;
669
670 while (1)
671 {
672 printf("Do you trust the above certificate? (Y/T/N) ");
673 (void)fflush(stdout);
674 answer = freerdp_interruptible_getc(instance->context, stdin);
675
676 if ((answer == EOF) || feof(stdin))
677 {
678 printf("\nError: Could not read answer from stdin.\n");
679 return 0;
680 }
681
682 switch (answer)
683 {
684 case 'y':
685 case 'Y':
686 answer = freerdp_interruptible_getc(instance->context, stdin);
687 if (answer == EOF)
688 return 0;
689 return 1;
690
691 case 't':
692 case 'T':
693 answer = freerdp_interruptible_getc(instance->context, stdin);
694 if (answer == EOF)
695 return 0;
696 return 2;
697
698 case 'n':
699 case 'N':
700 answer = freerdp_interruptible_getc(instance->context, stdin);
701 if (answer == EOF)
702 return 0;
703 return 0;
704
705 default:
706 break;
707 }
708
709 printf("\n");
710 }
711}
712
726#if defined(WITH_FREERDP_DEPRECATED)
727DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name, const char* subject,
728 const char* issuer, const char* fingerprint, BOOL host_mismatch)
729{
730 WINPR_UNUSED(common_name);
731 WINPR_UNUSED(host_mismatch);
732
733 printf("WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
734 printf("Certificate details:\n");
735 printf("\tSubject: %s\n", subject);
736 printf("\tIssuer: %s\n", issuer);
737 printf("\tThumbprint: %s\n", fingerprint);
738 printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
739 "the CA certificate in your certificate store, or the certificate has expired.\n"
740 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
741 return client_cli_accept_certificate(instance);
742}
743#endif
744
745static char* client_cli_pem_cert(const char* pem)
746{
747 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
748 if (!cert)
749 return NULL;
750
751 char* fp = freerdp_certificate_get_fingerprint(cert);
752 char* start = freerdp_certificate_get_validity(cert, TRUE);
753 char* end = freerdp_certificate_get_validity(cert, FALSE);
754 freerdp_certificate_free(cert);
755
756 char* str = NULL;
757 size_t slen = 0;
758 winpr_asprintf(&str, &slen,
759 "\tValid from: %s\n"
760 "\tValid to: %s\n"
761 "\tThumbprint: %s\n",
762 start, end, fp);
763 free(fp);
764 free(start);
765 free(end);
766 return str;
767}
768
784DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
785 const char* common_name, const char* subject,
786 const char* issuer, const char* fingerprint, DWORD flags)
787{
788 const char* type = "RDP-Server";
789
790 WINPR_ASSERT(instance);
791 WINPR_ASSERT(instance->context);
792 WINPR_ASSERT(instance->context->settings);
793
794 if (flags & VERIFY_CERT_FLAG_GATEWAY)
795 type = "RDP-Gateway";
796
797 if (flags & VERIFY_CERT_FLAG_REDIRECT)
798 type = "RDP-Redirect";
799
800 printf("Certificate details for %s:%" PRIu16 " (%s):\n", host, port, type);
801 printf("\tCommon Name: %s\n", common_name);
802 printf("\tSubject: %s\n", subject);
803 printf("\tIssuer: %s\n", issuer);
804 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
805 * FreeRDP_CertificateCallbackPreferPEM to TRUE
806 */
807 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
808 {
809 char* str = client_cli_pem_cert(fingerprint);
810 printf("%s", str);
811 free(str);
812 }
813 else
814 printf("\tThumbprint: %s\n", fingerprint);
815
816 printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
817 "the CA certificate in your certificate store, or the certificate has expired.\n"
818 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
819 return client_cli_accept_certificate(instance);
820}
821
837#if defined(WITH_FREERDP_DEPRECATED)
838DWORD client_cli_verify_changed_certificate(freerdp* instance, const char* common_name,
839 const char* subject, const char* issuer,
840 const char* fingerprint, const char* old_subject,
841 const char* old_issuer, const char* old_fingerprint)
842{
843 WINPR_UNUSED(common_name);
844
845 printf("WARNING: This callback is deprecated, migrate to "
846 "client_cli_verify_changed_certificate_ex\n");
847 printf("!!! Certificate has changed !!!\n");
848 printf("\n");
849 printf("New Certificate details:\n");
850 printf("\tSubject: %s\n", subject);
851 printf("\tIssuer: %s\n", issuer);
852 printf("\tThumbprint: %s\n", fingerprint);
853 printf("\n");
854 printf("Old Certificate details:\n");
855 printf("\tSubject: %s\n", old_subject);
856 printf("\tIssuer: %s\n", old_issuer);
857 printf("\tThumbprint: %s\n", old_fingerprint);
858 printf("\n");
859 printf("The above X.509 certificate does not match the certificate used for previous "
860 "connections.\n"
861 "This may indicate that the certificate has been tampered with.\n"
862 "Please contact the administrator of the RDP server and clarify.\n");
863 return client_cli_accept_certificate(instance);
864}
865#endif
866
886DWORD client_cli_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
887 const char* common_name, const char* subject,
888 const char* issuer, const char* fingerprint,
889 const char* old_subject, const char* old_issuer,
890 const char* old_fingerprint, DWORD flags)
891{
892 const char* type = "RDP-Server";
893
894 WINPR_ASSERT(instance);
895 WINPR_ASSERT(instance->context);
896 WINPR_ASSERT(instance->context->settings);
897
898 if (flags & VERIFY_CERT_FLAG_GATEWAY)
899 type = "RDP-Gateway";
900
901 if (flags & VERIFY_CERT_FLAG_REDIRECT)
902 type = "RDP-Redirect";
903
904 printf("!!!Certificate for %s:%" PRIu16 " (%s) has changed!!!\n", host, port, type);
905 printf("\n");
906 printf("New Certificate details:\n");
907 printf("\tCommon Name: %s\n", common_name);
908 printf("\tSubject: %s\n", subject);
909 printf("\tIssuer: %s\n", issuer);
910 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
911 * FreeRDP_CertificateCallbackPreferPEM to TRUE
912 */
913 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
914 {
915 char* str = client_cli_pem_cert(fingerprint);
916 printf("%s", str);
917 free(str);
918 }
919 else
920 printf("\tThumbprint: %s\n", fingerprint);
921 printf("\n");
922 printf("Old Certificate details:\n");
923 printf("\tSubject: %s\n", old_subject);
924 printf("\tIssuer: %s\n", old_issuer);
925 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
926 * FreeRDP_CertificateCallbackPreferPEM to TRUE
927 */
928 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
929 {
930 char* str = client_cli_pem_cert(old_fingerprint);
931 printf("%s", str);
932 free(str);
933 }
934 else
935 printf("\tThumbprint: %s\n", old_fingerprint);
936 printf("\n");
937 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
938 {
939 printf("\tA matching entry with legacy SHA1 was found in local known_hosts2 store.\n");
940 printf("\tIf you just upgraded from a FreeRDP version before 2.0 this is expected.\n");
941 printf("\tThe hashing algorithm has been upgraded from SHA1 to SHA256.\n");
942 printf("\tAll manually accepted certificates must be reconfirmed!\n");
943 printf("\n");
944 }
945 printf("The above X.509 certificate does not match the certificate used for previous "
946 "connections.\n"
947 "This may indicate that the certificate has been tampered with.\n"
948 "Please contact the administrator of the RDP server and clarify.\n");
949 return client_cli_accept_certificate(instance);
950}
951
952BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
953 BOOL isConsentMandatory, size_t length,
954 const WCHAR* message)
955{
956 int answer = 0;
957 const char* msgType = (type == GATEWAY_MESSAGE_CONSENT) ? "Consent message" : "Service message";
958
959 WINPR_ASSERT(instance);
960 WINPR_ASSERT(instance->context);
961 WINPR_ASSERT(instance->context->settings);
962
963 if (!isDisplayMandatory && !isConsentMandatory)
964 return TRUE;
965
966 printf("%s:\n", msgType);
967#if defined(WIN32)
968 printf("%.*S\n", (int)length, message);
969#else
970 {
971 LPSTR msg = ConvertWCharNToUtf8Alloc(message, length / sizeof(WCHAR), NULL);
972 if (!msg)
973 {
974 printf("Failed to convert message!\n");
975 return FALSE;
976 }
977 printf("%s\n", msg);
978 free(msg);
979 }
980#endif
981
982 while (isConsentMandatory)
983 {
984 printf("I understand and agree to the terms of this policy (Y/N) \n");
985 (void)fflush(stdout);
986 answer = freerdp_interruptible_getc(instance->context, stdin);
987
988 if ((answer == EOF) || feof(stdin))
989 {
990 printf("\nError: Could not read answer from stdin.\n");
991 return FALSE;
992 }
993
994 switch (answer)
995 {
996 case 'y':
997 case 'Y':
998 answer = freerdp_interruptible_getc(instance->context, stdin);
999 if (answer == EOF)
1000 return FALSE;
1001 return TRUE;
1002
1003 case 'n':
1004 case 'N':
1005 (void)freerdp_interruptible_getc(instance->context, stdin);
1006 return FALSE;
1007
1008 default:
1009 break;
1010 }
1011
1012 printf("\n");
1013 }
1014
1015 return TRUE;
1016}
1017
1018static const char* extract_authorization_code(char* url)
1019{
1020 WINPR_ASSERT(url);
1021
1022 for (char* p = strchr(url, '?'); p++ != NULL; p = strchr(p, '&'))
1023 {
1024 if (strncmp(p, "code=", 5) != 0)
1025 continue;
1026
1027 char* end = NULL;
1028 p += 5;
1029
1030 end = strchr(p, '&');
1031 if (end)
1032 *end = '\0';
1033
1034 return p;
1035 }
1036
1037 return NULL;
1038}
1039
1040#if defined(WITH_AAD)
1041static BOOL client_cli_get_rdsaad_access_token(freerdp* instance, const char* scope,
1042 const char* req_cnf, char** token)
1043{
1044 WINPR_ASSERT(instance);
1045 WINPR_ASSERT(instance->context);
1046
1047 size_t size = 0;
1048 char* url = NULL;
1049 char* token_request = NULL;
1050
1051 WINPR_ASSERT(scope);
1052 WINPR_ASSERT(req_cnf);
1053 WINPR_ASSERT(token);
1054
1055 BOOL rc = FALSE;
1056 *token = NULL;
1057
1058 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1059 FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1060
1061 printf("Browse to: %s\n", request);
1062 free(request);
1063 printf("Paste redirect URL here: \n");
1064
1065 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1066 goto cleanup;
1067
1068 const char* code = extract_authorization_code(url);
1069 if (!code)
1070 goto cleanup;
1071
1072 token_request =
1073 freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1074 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1075 if (!token_request)
1076 goto cleanup;
1077
1078 rc = client_common_get_access_token(instance, token_request, token);
1079
1080cleanup:
1081 free(token_request);
1082 free(url);
1083 return rc && (*token != NULL);
1084}
1085
1086static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1087{
1088 WINPR_ASSERT(instance);
1089 WINPR_ASSERT(instance->context);
1090
1091 size_t size = 0;
1092 char* url = NULL;
1093 char* token_request = NULL;
1094
1095 WINPR_ASSERT(token);
1096
1097 BOOL rc = FALSE;
1098
1099 *token = NULL;
1100
1101 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1102 FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1103 if (!request)
1104 return FALSE;
1105 printf("Browse to: %s\n", request);
1106 free(request);
1107 printf("Paste redirect URL here: \n");
1108
1109 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1110 goto cleanup;
1111
1112 const char* code = extract_authorization_code(url);
1113 if (!code)
1114 goto cleanup;
1115
1116 token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1117 FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1118
1119 if (!token_request)
1120 goto cleanup;
1121
1122 rc = client_common_get_access_token(instance, token_request, token);
1123
1124cleanup:
1125 free(token_request);
1126 free(url);
1127 return rc && (*token != NULL);
1128}
1129#endif
1130
1131BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1132 size_t count, ...)
1133{
1134 WINPR_ASSERT(instance);
1135 WINPR_ASSERT(token);
1136
1137#if !defined(WITH_AAD)
1138 WLog_ERR(TAG, "Build does not support AAD authentication");
1139 return FALSE;
1140#else
1141 BOOL rc = FALSE;
1142 WINPR_ASSERT(instance->context);
1143 const BOOL saved =
1144 freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1145 if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1146 TRUE))
1147 return FALSE;
1148
1149 switch (tokenType)
1150 {
1151 case ACCESS_TOKEN_TYPE_AAD:
1152 {
1153 if (count < 2)
1154 {
1155 WLog_ERR(TAG,
1156 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1157 ", aborting",
1158 count);
1159 return FALSE;
1160 }
1161 else if (count > 2)
1162 WLog_WARN(TAG,
1163 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1164 ", ignoring",
1165 count);
1166 va_list ap = { 0 };
1167 va_start(ap, count);
1168 const char* scope = va_arg(ap, const char*);
1169 const char* req_cnf = va_arg(ap, const char*);
1170 rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1171 va_end(ap);
1172 }
1173 break;
1174 case ACCESS_TOKEN_TYPE_AVD:
1175 if (count != 0)
1176 WLog_WARN(TAG,
1177 "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1178 ", ignoring",
1179 count);
1180 rc = client_cli_get_avd_access_token(instance, token);
1181 break;
1182 default:
1183 WLog_ERR(TAG, "Unexpected value for AccessTokenType [%" PRIuz "], aborting", tokenType);
1184 break;
1185 }
1186
1187 if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1188 saved))
1189 return FALSE;
1190 return rc;
1191#endif
1192}
1193
1194BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1195{
1196#ifdef WITH_AAD
1197 WINPR_ASSERT(request);
1198 WINPR_ASSERT(token);
1199
1200 BOOL ret = FALSE;
1201 long resp_code = 0;
1202 BYTE* response = NULL;
1203 size_t response_length = 0;
1204
1205 wLog* log = WLog_Get(TAG);
1206
1207 const char* token_ep =
1208 freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1209 if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1210 {
1211 WLog_ERR(TAG, "access token request failed");
1212 return FALSE;
1213 }
1214
1215 if (resp_code != HTTP_STATUS_OK)
1216 {
1217 char buffer[64] = { 0 };
1218
1219 WLog_Print(log, WLOG_ERROR,
1220 "Server unwilling to provide access token; returned status code %s",
1221 freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1222 if (response_length > 0)
1223 WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1224 goto cleanup;
1225 }
1226
1227 *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1228 if (*token)
1229 ret = TRUE;
1230
1231cleanup:
1232 free(response);
1233 return ret;
1234#else
1235 return FALSE;
1236#endif
1237}
1238
1239SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1240 void* userarg)
1241{
1242 WINPR_UNUSED(instance);
1243 WINPR_ASSERT(instance->context);
1244 WINPR_UNUSED(userarg);
1245 WINPR_ASSERT(instance);
1246 WINPR_ASSERT(what);
1247
1248 if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1249 {
1250 WLog_ERR(TAG, "Unknown module %s, aborting", what);
1251 return -1;
1252 }
1253
1254 if (current == 0)
1255 {
1256 if (strcmp(what, "arm-transport") == 0)
1257 WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1258 }
1259
1260 const rdpSettings* settings = instance->context->settings;
1261 const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1262 if (!enabled)
1263 {
1264 WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1265 return -1;
1266 }
1267
1268 const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1269 const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1270 if (current >= max)
1271 {
1272 WLog_ERR(TAG,
1273 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1274 "tech support for help if this keeps happening.",
1275 what);
1276 return -1;
1277 }
1278
1279 WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1280 what, current, max, delay);
1281 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1282}
1283
1284BOOL client_auto_reconnect(freerdp* instance)
1285{
1286 return client_auto_reconnect_ex(instance, NULL);
1287}
1288
1289BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1290{
1291 BOOL retry = TRUE;
1292 UINT32 error = 0;
1293 UINT32 numRetries = 0;
1294 rdpSettings* settings = NULL;
1295
1296 if (!instance)
1297 return FALSE;
1298
1299 WINPR_ASSERT(instance->context);
1300
1301 settings = instance->context->settings;
1302 WINPR_ASSERT(settings);
1303
1304 const UINT32 maxRetries =
1305 freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1306
1307 /* Only auto reconnect on network disconnects. */
1308 error = freerdp_error_info(instance);
1309 switch (error)
1310 {
1311 case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1312 /* A network disconnect was detected */
1313 WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1314 freerdp_get_error_info_string(error));
1315 break;
1316 case ERRINFO_SUCCESS:
1317 /* A network disconnect was detected */
1318 WLog_INFO(TAG, "Network disconnect!");
1319 break;
1320 default:
1321 return FALSE;
1322 }
1323
1324 if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1325 {
1326 /* No auto-reconnect - just quit */
1327 return FALSE;
1328 }
1329
1330 switch (freerdp_get_last_error(instance->context))
1331 {
1332 case FREERDP_ERROR_CONNECT_CANCELLED:
1333 WLog_WARN(TAG, "Connection aborted by user");
1334 return FALSE;
1335 default:
1336 break;
1337 }
1338
1339 /* Perform an auto-reconnect. */
1340 while (retry)
1341 {
1342 /* Quit retrying if max retries has been exceeded */
1343 if ((maxRetries > 0) && (numRetries++ >= maxRetries))
1344 {
1345 return FALSE;
1346 }
1347
1348 /* Attempt the next reconnect */
1349 WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1350
1351 IFCALL(instance->RetryDialog, instance, "connection", numRetries, NULL);
1352
1353 if (freerdp_reconnect(instance))
1354 return TRUE;
1355
1356 switch (freerdp_get_last_error(instance->context))
1357 {
1358 case FREERDP_ERROR_CONNECT_CANCELLED:
1359 WLog_WARN(TAG, "Autoreconnect aborted by user");
1360 return FALSE;
1361 default:
1362 break;
1363 }
1364 for (UINT32 x = 0; x < 50; x++)
1365 {
1366 if (!IFCALLRESULT(TRUE, window_events, instance))
1367 return FALSE;
1368
1369 Sleep(10);
1370 }
1371 }
1372
1373 WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1374 return FALSE;
1375}
1376
1377int freerdp_client_common_stop(rdpContext* context)
1378{
1379 rdpClientContext* cctx = (rdpClientContext*)context;
1380 WINPR_ASSERT(cctx);
1381
1382 freerdp_abort_connect_context(&cctx->context);
1383
1384 if (cctx->thread)
1385 {
1386 (void)WaitForSingleObject(cctx->thread, INFINITE);
1387 (void)CloseHandle(cctx->thread);
1388 cctx->thread = NULL;
1389 }
1390
1391 return 0;
1392}
1393
1394#if defined(CHANNEL_ENCOMSP_CLIENT)
1395BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1396{
1397 rdpClientContext* cctx = NULL;
1398 BOOL state = 0;
1399
1400 if (!encomsp)
1401 return FALSE;
1402
1403 cctx = (rdpClientContext*)encomsp->custom;
1404
1405 state = cctx->controlToggle;
1406 cctx->controlToggle = !cctx->controlToggle;
1407 return freerdp_client_encomsp_set_control(encomsp, state);
1408}
1409
1410BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1411{
1413
1414 if (!encomsp)
1415 return FALSE;
1416
1417 pdu.ParticipantId = encomsp->participantId;
1418 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1419
1420 if (control)
1421 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1422
1423 encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1424
1425 return TRUE;
1426}
1427
1428static UINT
1429client_encomsp_participant_created(EncomspClientContext* context,
1430 const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1431{
1432 rdpClientContext* cctx = NULL;
1433 rdpSettings* settings = NULL;
1434 BOOL request = 0;
1435
1436 if (!context || !context->custom || !participantCreated)
1437 return ERROR_INVALID_PARAMETER;
1438
1439 cctx = (rdpClientContext*)context->custom;
1440 WINPR_ASSERT(cctx);
1441
1442 settings = cctx->context.settings;
1443 WINPR_ASSERT(settings);
1444
1445 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1446 context->participantId = participantCreated->ParticipantId;
1447
1448 request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1449 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1450 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1451 {
1452 if (!freerdp_client_encomsp_set_control(context, TRUE))
1453 return ERROR_INTERNAL_ERROR;
1454
1455 /* if auto-request-control setting is enabled then only request control once upon connect,
1456 * otherwise it will auto request control again every time server turns off control which
1457 * is a bit annoying */
1458 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1459 return ERROR_INTERNAL_ERROR;
1460 }
1461
1462 return CHANNEL_RC_OK;
1463}
1464
1465static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1466{
1467 cctx->encomsp = encomsp;
1468 encomsp->custom = (void*)cctx;
1469 encomsp->ParticipantCreated = client_encomsp_participant_created;
1470}
1471
1472static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1473{
1474 if (encomsp)
1475 {
1476 encomsp->custom = NULL;
1477 encomsp->ParticipantCreated = NULL;
1478 }
1479
1480 if (cctx)
1481 cctx->encomsp = NULL;
1482}
1483#endif
1484
1485void freerdp_client_OnChannelConnectedEventHandler(void* context,
1486 const ChannelConnectedEventArgs* e)
1487{
1488 rdpClientContext* cctx = (rdpClientContext*)context;
1489
1490 WINPR_ASSERT(cctx);
1491 WINPR_ASSERT(e);
1492
1493 if (0)
1494 {
1495 }
1496#if defined(CHANNEL_AINPUT_CLIENT)
1497 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1498 cctx->ainput = (AInputClientContext*)e->pInterface;
1499#endif
1500#if defined(CHANNEL_RDPEI_CLIENT)
1501 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1502 {
1503 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1504 }
1505#endif
1506#if defined(CHANNEL_RDPGFX_CLIENT)
1507 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1508 {
1509 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1510 }
1511#endif
1512#if defined(CHANNEL_GEOMETRY_CLIENT)
1513 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1514 {
1515 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1516 }
1517#endif
1518#if defined(CHANNEL_VIDEO_CLIENT)
1519 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1520 {
1521 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1522 }
1523 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1524 {
1525 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1526 }
1527#endif
1528#if defined(CHANNEL_ENCOMSP_CLIENT)
1529 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1530 {
1531 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1532 }
1533#endif
1534}
1535
1536void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1537 const ChannelDisconnectedEventArgs* e)
1538{
1539 rdpClientContext* cctx = (rdpClientContext*)context;
1540
1541 WINPR_ASSERT(cctx);
1542 WINPR_ASSERT(e);
1543
1544 if (0)
1545 {
1546 }
1547#if defined(CHANNEL_AINPUT_CLIENT)
1548 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1549 cctx->ainput = NULL;
1550#endif
1551#if defined(CHANNEL_RDPEI_CLIENT)
1552 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1553 {
1554 cctx->rdpei = NULL;
1555 }
1556#endif
1557#if defined(CHANNEL_RDPGFX_CLIENT)
1558 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1559 {
1560 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1561 }
1562#endif
1563#if defined(CHANNEL_GEOMETRY_CLIENT)
1564 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1565 {
1566 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1567 }
1568#endif
1569#if defined(CHANNEL_VIDEO_CLIENT)
1570 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1571 {
1572 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1573 }
1574 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1575 {
1576 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1577 }
1578#endif
1579#if defined(CHANNEL_ENCOMSP_CLIENT)
1580 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1581 {
1582 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1583 }
1584#endif
1585}
1586
1587BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1588{
1589 BOOL handled = FALSE;
1590
1591 WINPR_ASSERT(cctx);
1592
1593#if defined(CHANNEL_AINPUT_CLIENT)
1594 if (cctx->ainput)
1595 {
1596 UINT rc = 0;
1597 UINT64 flags = 0;
1598 INT32 x = 0;
1599 INT32 y = 0;
1600 INT32 value = mflags & 0xFF;
1601
1602 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1603 value = -1 * (0x100 - value);
1604
1605 /* We have discrete steps, scale this so we can also support high
1606 * resolution wheels. */
1607 value *= 0x10000;
1608
1609 if (mflags & PTR_FLAGS_WHEEL)
1610 {
1611 flags |= AINPUT_FLAGS_WHEEL;
1612 y = value;
1613 }
1614
1615 if (mflags & PTR_FLAGS_HWHEEL)
1616 {
1617 flags |= AINPUT_FLAGS_WHEEL;
1618 x = value;
1619 }
1620
1621 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1622 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1623 if (rc == CHANNEL_RC_OK)
1624 handled = TRUE;
1625 }
1626#endif
1627
1628 if (!handled)
1629 freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1630
1631 return TRUE;
1632}
1633
1634#if defined(CHANNEL_AINPUT_CLIENT)
1635static INLINE BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1636{
1637 UINT rc = 0;
1638
1639 WINPR_ASSERT(cctx);
1640 WINPR_ASSERT(cctx->ainput);
1641 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1642
1643 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1644
1645 return rc == CHANNEL_RC_OK;
1646}
1647#endif
1648
1649static bool button_pressed(const rdpClientContext* cctx)
1650{
1651 WINPR_ASSERT(cctx);
1652 for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1653 {
1654 const BOOL cur = cctx->pressed_buttons[x];
1655 if (cur)
1656 return true;
1657 }
1658 return false;
1659}
1660
1661BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1662 INT32 y)
1663{
1664 BOOL handled = FALSE;
1665
1666 WINPR_ASSERT(cctx);
1667
1668 if (mflags & PTR_FLAGS_BUTTON1)
1669 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1670 if (mflags & PTR_FLAGS_BUTTON2)
1671 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1672 if (mflags & PTR_FLAGS_BUTTON3)
1673 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1674
1675 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1676 !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1677 {
1678 if (!button_pressed(cctx))
1679 return TRUE;
1680 }
1681
1682 const BOOL haveRelative =
1683 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1684 if (relative && haveRelative)
1685 {
1686 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1687 WINPR_ASSERTING_INT_CAST(int16_t, x),
1688 WINPR_ASSERTING_INT_CAST(int16_t, y));
1689 }
1690
1691#if defined(CHANNEL_AINPUT_CLIENT)
1692 if (cctx->ainput)
1693 {
1694 UINT64 flags = 0;
1695
1696 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1697 flags |= AINPUT_FLAGS_HAVE_REL;
1698
1699 if (relative)
1700 flags |= AINPUT_FLAGS_REL;
1701
1702 if (mflags & PTR_FLAGS_DOWN)
1703 flags |= AINPUT_FLAGS_DOWN;
1704 if (mflags & PTR_FLAGS_BUTTON1)
1705 flags |= AINPUT_FLAGS_BUTTON1;
1706 if (mflags & PTR_FLAGS_BUTTON2)
1707 flags |= AINPUT_FLAGS_BUTTON2;
1708 if (mflags & PTR_FLAGS_BUTTON3)
1709 flags |= AINPUT_FLAGS_BUTTON3;
1710 if (mflags & PTR_FLAGS_MOVE)
1711 flags |= AINPUT_FLAGS_MOVE;
1712 handled = ainput_send_diff_event(cctx, flags, x, y);
1713 }
1714#endif
1715
1716 if (!handled)
1717 {
1718 if (relative)
1719 {
1720 cctx->lastX += x;
1721 cctx->lastY += y;
1722 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1723 }
1724 else
1725 {
1726 cctx->lastX = x;
1727 cctx->lastY = y;
1728 }
1729 freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1730 (UINT16)cctx->lastY);
1731 }
1732 return TRUE;
1733}
1734
1735BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1736 INT32 x, INT32 y)
1737{
1738 BOOL handled = FALSE;
1739 WINPR_ASSERT(cctx);
1740
1741 if (mflags & PTR_XFLAGS_BUTTON1)
1742 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1743 if (mflags & PTR_XFLAGS_BUTTON2)
1744 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1745
1746 const BOOL haveRelative =
1747 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1748 if (relative && haveRelative)
1749 {
1750 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1751 WINPR_ASSERTING_INT_CAST(int16_t, x),
1752 WINPR_ASSERTING_INT_CAST(int16_t, y));
1753 }
1754
1755#if defined(CHANNEL_AINPUT_CLIENT)
1756 if (cctx->ainput)
1757 {
1758 UINT64 flags = 0;
1759
1760 if (relative)
1761 flags |= AINPUT_FLAGS_REL;
1762 if (mflags & PTR_XFLAGS_DOWN)
1763 flags |= AINPUT_FLAGS_DOWN;
1764 if (mflags & PTR_XFLAGS_BUTTON1)
1765 flags |= AINPUT_XFLAGS_BUTTON1;
1766 if (mflags & PTR_XFLAGS_BUTTON2)
1767 flags |= AINPUT_XFLAGS_BUTTON2;
1768
1769 handled = ainput_send_diff_event(cctx, flags, x, y);
1770 }
1771#endif
1772
1773 if (!handled)
1774 {
1775 if (relative)
1776 {
1777 cctx->lastX += x;
1778 cctx->lastY += y;
1779 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1780 }
1781 else
1782 {
1783 cctx->lastX = x;
1784 cctx->lastY = y;
1785 }
1786 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1787 (UINT16)cctx->lastY);
1788 }
1789
1790 return TRUE;
1791}
1792
1793static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1794{
1795 WINPR_ASSERT(cctx);
1796 WINPR_ASSERT(contact);
1797
1798#if defined(CHANNEL_RDPEI_CLIENT)
1799 RdpeiClientContext* rdpei = cctx->rdpei;
1800
1801 if (!rdpei)
1802 {
1803 UINT16 flags = 0;
1804 flags |= PTR_FLAGS_BUTTON1;
1805
1806 WINPR_ASSERT(contact->x <= UINT16_MAX);
1807 WINPR_ASSERT(contact->y <= UINT16_MAX);
1808 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1809 }
1810 else
1811 {
1812 int contactId = 0;
1813
1814 if (rdpei->TouchRawEvent)
1815 {
1816 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1817 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1818 ? CONTACT_DATA_PRESSURE_PRESENT
1819 : 0;
1820 // Ensure contact position is unchanged from "engaged" to "out of range" state
1821 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1822 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1823 RDPINPUT_CONTACT_FLAG_INCONTACT,
1824 contactFlags, contact->pressure);
1825 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1826 contactFlags, contact->pressure);
1827 }
1828 else
1829 {
1830 WINPR_ASSERT(rdpei->TouchEnd);
1831 rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1832 }
1833 }
1834#else
1835 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1836 "-DWITH_CHANNELS=ON");
1837#endif
1838
1839 return TRUE;
1840}
1841
1842static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1843{
1844 WINPR_ASSERT(cctx);
1845 WINPR_ASSERT(contact);
1846
1847#if defined(CHANNEL_RDPEI_CLIENT)
1848 RdpeiClientContext* rdpei = cctx->rdpei;
1849
1850 // Emulate mouse click if touch is not possible, like in login screen
1851 if (!rdpei)
1852 {
1853 UINT16 flags = 0;
1854 flags |= PTR_FLAGS_DOWN;
1855 flags |= PTR_FLAGS_MOVE;
1856 flags |= PTR_FLAGS_BUTTON1;
1857
1858 WINPR_ASSERT(contact->x <= UINT16_MAX);
1859 WINPR_ASSERT(contact->y <= UINT16_MAX);
1860 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1861 }
1862 else
1863 {
1864 int contactId = 0;
1865
1866 if (rdpei->TouchRawEvent)
1867 {
1868 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1869 RDPINPUT_CONTACT_FLAG_INCONTACT;
1870 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1871 ? CONTACT_DATA_PRESSURE_PRESENT
1872 : 0;
1873 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1874 contactFlags, contact->pressure);
1875 }
1876 else
1877 {
1878 WINPR_ASSERT(rdpei->TouchBegin);
1879 rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1880 }
1881 }
1882#else
1883 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1884 "-DWITH_CHANNELS=ON");
1885#endif
1886
1887 return TRUE;
1888}
1889
1890static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1891{
1892 WINPR_ASSERT(cctx);
1893 WINPR_ASSERT(contact);
1894
1895#if defined(CHANNEL_RDPEI_CLIENT)
1896 RdpeiClientContext* rdpei = cctx->rdpei;
1897
1898 if (!rdpei)
1899 {
1900 UINT16 flags = 0;
1901 flags |= PTR_FLAGS_MOVE;
1902
1903 WINPR_ASSERT(contact->x <= UINT16_MAX);
1904 WINPR_ASSERT(contact->y <= UINT16_MAX);
1905 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1906 }
1907 else
1908 {
1909 int contactId = 0;
1910
1911 if (rdpei->TouchRawEvent)
1912 {
1913 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1914 RDPINPUT_CONTACT_FLAG_INCONTACT;
1915 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1916 ? CONTACT_DATA_PRESSURE_PRESENT
1917 : 0;
1918 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1919 contactFlags, contact->pressure);
1920 }
1921 else
1922 {
1923 WINPR_ASSERT(rdpei->TouchUpdate);
1924 rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
1925 }
1926 }
1927#else
1928 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1929 "-DWITH_CHANNELS=ON");
1930#endif
1931
1932 return TRUE;
1933}
1934
1935static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
1936 UINT32 pressure, INT32 x, INT32 y,
1937 FreeRDP_TouchContact* pcontact)
1938{
1939 WINPR_ASSERT(cctx);
1940 WINPR_ASSERT(pcontact);
1941
1942 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
1943 {
1944 FreeRDP_TouchContact* contact = &cctx->contacts[i];
1945
1946 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
1947 if (newcontact || (contact->id == touchId))
1948 {
1949 contact->id = touchId;
1950 contact->flags = flags;
1951 contact->pressure = pressure;
1952 contact->x = x;
1953 contact->y = y;
1954
1955 *pcontact = *contact;
1956
1957 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
1958 if (resetcontact)
1959 {
1960 FreeRDP_TouchContact empty = { 0 };
1961 *contact = empty;
1962 }
1963 return TRUE;
1964 }
1965 }
1966
1967 return FALSE;
1968}
1969
1970BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
1971 UINT32 pressure, INT32 x, INT32 y)
1972{
1973 const UINT32 mask = FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION;
1974 WINPR_ASSERT(cctx);
1975
1976 FreeRDP_TouchContact contact = { 0 };
1977
1978 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
1979 return FALSE;
1980
1981 switch (flags & mask)
1982 {
1983 case FREERDP_TOUCH_DOWN:
1984 return freerdp_handle_touch_down(cctx, &contact);
1985 case FREERDP_TOUCH_UP:
1986 return freerdp_handle_touch_up(cctx, &contact);
1987 case FREERDP_TOUCH_MOTION:
1988 return freerdp_handle_touch_motion(cctx, &contact);
1989 default:
1990 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %d, ignoring", flags);
1991 return FALSE;
1992 }
1993}
1994
1995BOOL freerdp_client_load_channels(freerdp* instance)
1996{
1997 WINPR_ASSERT(instance);
1998 WINPR_ASSERT(instance->context);
1999
2000 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2001 {
2002 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2003 return FALSE;
2004 }
2005 return TRUE;
2006}
2007
2008int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2009{
2010 const char* str_data = freerdp_get_logon_error_info_data(data);
2011 const char* str_type = freerdp_get_logon_error_info_type(type);
2012
2013 if (!instance || !instance->context)
2014 return -1;
2015
2016 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2017 return 1;
2018}
2019
2020static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2021 size_t* pos)
2022{
2023 WINPR_ASSERT(cctx);
2024
2025 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2026 {
2027 FreeRDP_PenDevice* pen = &cctx->pens[i];
2028 if (deviceid == pen->deviceid)
2029 {
2030 if (pos)
2031 *pos = i;
2032 return pen;
2033 }
2034 }
2035 return NULL;
2036}
2037
2038static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2039 double pressure)
2040{
2041 static const INT32 null_deviceid = 0;
2042
2043 WINPR_ASSERT(cctx);
2044 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2045 if (freerdp_client_is_pen(cctx, deviceid))
2046 {
2047 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2048 return FALSE;
2049 }
2050
2051 size_t pos = 0;
2052 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2053 if (pen)
2054 {
2055 const FreeRDP_PenDevice empty = { 0 };
2056 *pen = empty;
2057
2058 pen->deviceid = deviceid;
2059 pen->max_pressure = pressure;
2060 pen->flags = flags;
2061
2062 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2063 return TRUE;
2064 }
2065
2066 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2067 return TRUE;
2068}
2069
2070BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2071{
2072 if ((flags & FREERDP_PEN_REGISTER) != 0)
2073 {
2074 va_list args;
2075
2076 va_start(args, deviceid);
2077 double pressure = va_arg(args, double);
2078 va_end(args);
2079 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2080 }
2081 size_t pos = 0;
2082 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2083 if (!pen)
2084 {
2085 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2086 return FALSE;
2087 }
2088
2089 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2090 UINT32 penFlags =
2091 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2092
2093 RdpeiClientContext* rdpei = cctx->rdpei;
2094 WINPR_ASSERT(rdpei);
2095
2096 UINT32 normalizedpressure = 1024;
2097 INT32 x = 0;
2098 INT32 y = 0;
2099 UINT16 rotation = 0;
2100 INT16 tiltX = 0;
2101 INT16 tiltY = 0;
2102 va_list args;
2103 va_start(args, deviceid);
2104
2105 x = va_arg(args, INT32);
2106 y = va_arg(args, INT32);
2107 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2108 {
2109 const double pressure = va_arg(args, double);
2110 const double np = (pressure * 1024.0) / pen->max_pressure;
2111 normalizedpressure = (UINT32)lround(np);
2112 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2113 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2114 }
2115 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2116 {
2117 const unsigned arg = va_arg(args, unsigned);
2118 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2119 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2120 }
2121 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2122 {
2123 const int arg = va_arg(args, int);
2124 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2125 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2126 }
2127 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2128 {
2129 const int arg = va_arg(args, int);
2130 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2131 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2132 }
2133 va_end(args);
2134
2135 if ((flags & FREERDP_PEN_PRESS) != 0)
2136 {
2137 // Ensure that only one button is pressed
2138 if (pen->pressed)
2139 flags = FREERDP_PEN_MOTION |
2140 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2141 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2142 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2143 }
2144 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2145 {
2146 if (!pen->pressed ||
2147 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2148 flags = FREERDP_PEN_MOTION |
2149 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2150 else
2151 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2152 }
2153
2154 flags |= pen->flags;
2155 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2156 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2157 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2158 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2159
2160 pen->last_x = x;
2161 pen->last_y = y;
2162 if ((flags & FREERDP_PEN_PRESS) != 0)
2163 {
2164 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2165 pen->hovering = FALSE;
2166 pen->pressed = TRUE;
2167
2168 WINPR_ASSERT(rdpei->PenBegin);
2169 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2170 normalizedpressure, rotation, tiltX, tiltY);
2171 return rc == CHANNEL_RC_OK;
2172 }
2173 else if ((flags & FREERDP_PEN_MOTION) != 0)
2174 {
2175 UINT rc = ERROR_INTERNAL_ERROR;
2176 if (pen->pressed)
2177 {
2178 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2179
2180 // TODO: what if no rotation is supported but tilt is?
2181 WINPR_ASSERT(rdpei->PenUpdate);
2182 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2183 rotation, tiltX, tiltY);
2184 }
2185 else if (pen->hovering)
2186 {
2187 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2188
2189 WINPR_ASSERT(rdpei->PenHoverUpdate);
2190 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2191 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2192 }
2193 else
2194 {
2195 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2196 pen->hovering = TRUE;
2197
2198 WINPR_ASSERT(rdpei->PenHoverBegin);
2199 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2200 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2201 }
2202 return rc == CHANNEL_RC_OK;
2203 }
2204 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2205 {
2206 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2207 pen->pressed = FALSE;
2208 pen->hovering = TRUE;
2209
2210 WINPR_ASSERT(rdpei->PenUpdate);
2211 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2212 normalizedpressure, rotation, tiltX, tiltY);
2213 if (rc != CHANNEL_RC_OK)
2214 return FALSE;
2215 WINPR_ASSERT(rdpei->PenEnd);
2216 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2217 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2218 return re == CHANNEL_RC_OK;
2219 }
2220
2221 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2222 return FALSE;
2223}
2224
2225BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2226{
2227 WINPR_ASSERT(cctx);
2228
2229 RdpeiClientContext* rdpei = cctx->rdpei;
2230
2231 if (!rdpei)
2232 return FALSE;
2233
2234 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2235 {
2236 FreeRDP_PenDevice* pen = &cctx->pens[i];
2237 if (pen->hovering)
2238 {
2239 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2240 pen->hovering = FALSE;
2241 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2242 }
2243 }
2244 return TRUE;
2245}
2246
2247BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2248{
2249 WINPR_ASSERT(cctx);
2250
2251 if (deviceid == 0)
2252 return FALSE;
2253
2254 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2255 {
2256 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2257 if (pen->deviceid == deviceid)
2258 return TRUE;
2259 }
2260
2261 return FALSE;
2262}
2263
2264BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* ccontext)
2265{
2266 WINPR_ASSERT(ccontext);
2267
2268 const rdpSettings* settings = ccontext->context.settings;
2269 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2270 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2271 BOOL ainput = FALSE;
2272#if defined(CHANNEL_AINPUT_CLIENT)
2273 ainput = ccontext->ainput != NULL;
2274#endif
2275
2276 return useRelative && (haveRelative || ainput);
2277}
2278
2279#if defined(WITH_AAD)
2280WINPR_ATTR_MALLOC(free, 1)
2281static char* get_redirect_uri(const rdpSettings* settings)
2282{
2283 char* redirect_uri = NULL;
2284 const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2285 if (cli)
2286 {
2287 const char* redirect_fmt =
2288 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2289 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2290 const char* tenantid = "common";
2291 if (useTenant)
2292 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2293
2294 if (tenantid && redirect_fmt)
2295 {
2296 const char* url =
2297 freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2298
2299 size_t redirect_len = 0;
2300 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2301 }
2302 }
2303 else
2304 {
2305 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2306 const char* redirect_fmt =
2307 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2308
2309 size_t redirect_len = 0;
2310 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2311 }
2312 return redirect_uri;
2313}
2314
2315static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2316{
2317 const rdpSettings* settings = cctx->context.settings;
2318 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2319 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2320 AAD_WELLKNOWN_authorization_endpoint);
2321 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2322
2323 if (!client_id || !ep || !scope)
2324 return NULL;
2325
2326 char* redirect_uri = get_redirect_uri(settings);
2327 if (!redirect_uri)
2328 return NULL;
2329
2330 char* url = NULL;
2331 size_t urllen = 0;
2332 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2333 client_id, scope, redirect_uri);
2334 free(redirect_uri);
2335 return url;
2336}
2337
2338static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2339{
2340 const rdpSettings* settings = cctx->context.settings;
2341 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2342 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2343 AAD_WELLKNOWN_authorization_endpoint);
2344 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2345
2346 if (!client_id || !ep || !scope)
2347 return NULL;
2348
2349 char* redirect_uri = get_redirect_uri(settings);
2350 if (!redirect_uri)
2351 return NULL;
2352
2353 char* url = NULL;
2354 size_t urllen = 0;
2355
2356 const char* code = va_arg(ap, const char*);
2357 winpr_asprintf(&url, &urllen,
2358 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2359 code, client_id, scope, redirect_uri);
2360 free(redirect_uri);
2361 return url;
2362}
2363
2364static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2365{
2366 const rdpSettings* settings = cctx->context.settings;
2367 char* url = NULL;
2368 size_t urllen = 0;
2369 char* redirect_uri = get_redirect_uri(settings);
2370
2371 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2372 if (!client_id || !redirect_uri)
2373 goto cleanup;
2374 const char* scope = va_arg(ap, const char*);
2375 if (!scope)
2376 goto cleanup;
2377
2378 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2379 AAD_WELLKNOWN_authorization_endpoint);
2380
2381 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2382 client_id, scope, redirect_uri);
2383cleanup:
2384 free(redirect_uri);
2385 return url;
2386}
2387
2388static char* aad_token_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 = va_arg(ap, const char*);
2395 const char* code = va_arg(ap, const char*);
2396 const char* req_cnf = va_arg(ap, const char*);
2397
2398 if (!client_id || !ep || !scope || !code || !req_cnf)
2399 return NULL;
2400
2401 char* redirect_uri = get_redirect_uri(settings);
2402 if (!redirect_uri)
2403 return NULL;
2404
2405 char* url = NULL;
2406 size_t urllen = 0;
2407
2408 winpr_asprintf(
2409 &url, &urllen,
2410 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2411 code, client_id, scope, redirect_uri, req_cnf);
2412 free(redirect_uri);
2413 return url;
2414}
2415#endif
2416
2417char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2418{
2419 WINPR_ASSERT(cctx);
2420 char* str = NULL;
2421
2422 va_list ap;
2423 va_start(ap, type);
2424 switch (type)
2425 {
2426#if defined(WITH_AAD)
2427 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2428 str = aad_auth_request(cctx, ap);
2429 break;
2430 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2431 str = aad_token_request(cctx, ap);
2432 break;
2433 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2434 str = avd_auth_request(cctx, ap);
2435 break;
2436 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2437 str = avd_token_request(cctx, ap);
2438 break;
2439#endif
2440 default:
2441 break;
2442 }
2443 va_end(ap);
2444 return str;
2445}
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.