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