FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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 char* redirect_uri = NULL;
1051 size_t redirec_uri_len = 0;
1052
1053 WINPR_ASSERT(scope);
1054 WINPR_ASSERT(req_cnf);
1055 WINPR_ASSERT(token);
1056
1057 BOOL rc = FALSE;
1058 *token = NULL;
1059
1060 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1061 FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1062
1063 printf("Browse to: %s\n", request);
1064 free(request);
1065 printf("Paste redirect URL here: \n");
1066
1067 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1068 goto cleanup;
1069
1070 const char* code = extract_authorization_code(url);
1071 if (!code)
1072 goto cleanup;
1073
1074 token_request =
1075 freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1076 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1077 if (!token_request)
1078 goto cleanup;
1079
1080 rc = client_common_get_access_token(instance, token_request, token);
1081
1082cleanup:
1083 free(redirect_uri);
1084 free(token_request);
1085 free(url);
1086 return rc && (*token != NULL);
1087}
1088
1089static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1090{
1091 WINPR_ASSERT(instance);
1092 WINPR_ASSERT(instance->context);
1093
1094 size_t size = 0;
1095 char* url = NULL;
1096 char* token_request = NULL;
1097 char* redirect_uri = NULL;
1098
1099 WINPR_ASSERT(token);
1100
1101 BOOL rc = FALSE;
1102
1103 *token = NULL;
1104
1105 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1106 FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1107 if (!request)
1108 return FALSE;
1109 printf("Browse to: %s\n", request);
1110 free(request);
1111 printf("Paste redirect URL here: \n");
1112
1113 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1114 goto cleanup;
1115
1116 const char* code = extract_authorization_code(url);
1117 if (!code)
1118 goto cleanup;
1119
1120 token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1121 FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1122
1123 if (!token_request)
1124 goto cleanup;
1125
1126 rc = client_common_get_access_token(instance, token_request, token);
1127
1128cleanup:
1129 free(redirect_uri);
1130 free(token_request);
1131 free(url);
1132 return rc && (*token != NULL);
1133}
1134#endif
1135
1136BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1137 size_t count, ...)
1138{
1139 WINPR_ASSERT(instance);
1140 WINPR_ASSERT(token);
1141
1142#if !defined(WITH_AAD)
1143 WLog_ERR(TAG, "Build does not support AAD authentication");
1144 return FALSE;
1145#else
1146 switch (tokenType)
1147 {
1148 case ACCESS_TOKEN_TYPE_AAD:
1149 {
1150 if (count < 2)
1151 {
1152 WLog_ERR(TAG,
1153 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1154 ", aborting",
1155 count);
1156 return FALSE;
1157 }
1158 else if (count > 2)
1159 WLog_WARN(TAG,
1160 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1161 ", ignoring",
1162 count);
1163 va_list ap = { 0 };
1164 va_start(ap, count);
1165 const char* scope = va_arg(ap, const char*);
1166 const char* req_cnf = va_arg(ap, const char*);
1167 BOOL rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1168 va_end(ap);
1169 return rc;
1170 }
1171 case ACCESS_TOKEN_TYPE_AVD:
1172 if (count != 0)
1173 WLog_WARN(TAG,
1174 "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1175 ", ignoring",
1176 count);
1177 return client_cli_get_avd_access_token(instance, token);
1178 default:
1179 WLog_ERR(TAG, "Unexpected value for AccessTokenType [%" PRIuz "], aborting", tokenType);
1180 return FALSE;
1181 }
1182#endif
1183}
1184
1185BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1186{
1187#ifdef WITH_AAD
1188 WINPR_ASSERT(request);
1189 WINPR_ASSERT(token);
1190
1191 BOOL ret = FALSE;
1192 long resp_code = 0;
1193 BYTE* response = NULL;
1194 size_t response_length = 0;
1195
1196 wLog* log = WLog_Get(TAG);
1197
1198 const char* token_ep =
1199 freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1200 if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1201 {
1202 WLog_ERR(TAG, "access token request failed");
1203 return FALSE;
1204 }
1205
1206 if (resp_code != HTTP_STATUS_OK)
1207 {
1208 char buffer[64] = { 0 };
1209
1210 WLog_Print(log, WLOG_ERROR,
1211 "Server unwilling to provide access token; returned status code %s",
1212 freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1213 if (response_length > 0)
1214 WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1215 goto cleanup;
1216 }
1217
1218 *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1219 if (*token)
1220 ret = TRUE;
1221
1222cleanup:
1223 free(response);
1224 return ret;
1225#else
1226 return FALSE;
1227#endif
1228}
1229
1230SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1231 void* userarg)
1232{
1233 WINPR_UNUSED(instance);
1234 WINPR_ASSERT(instance->context);
1235 WINPR_UNUSED(userarg);
1236 WINPR_ASSERT(instance);
1237 WINPR_ASSERT(what);
1238
1239 if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1240 {
1241 WLog_ERR(TAG, "Unknown module %s, aborting", what);
1242 return -1;
1243 }
1244
1245 if (current == 0)
1246 {
1247 if (strcmp(what, "arm-transport") == 0)
1248 WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1249 }
1250
1251 const rdpSettings* settings = instance->context->settings;
1252 const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1253 if (!enabled)
1254 {
1255 WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1256 return -1;
1257 }
1258
1259 const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1260 const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1261 if (current >= max)
1262 {
1263 WLog_ERR(TAG,
1264 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1265 "tech support for help if this keeps happening.",
1266 what);
1267 return -1;
1268 }
1269
1270 WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1271 what, current, max, delay);
1272 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1273}
1274
1275BOOL client_auto_reconnect(freerdp* instance)
1276{
1277 return client_auto_reconnect_ex(instance, NULL);
1278}
1279
1280BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1281{
1282 BOOL retry = TRUE;
1283 UINT32 error = 0;
1284 UINT32 numRetries = 0;
1285 rdpSettings* settings = NULL;
1286
1287 if (!instance)
1288 return FALSE;
1289
1290 WINPR_ASSERT(instance->context);
1291
1292 settings = instance->context->settings;
1293 WINPR_ASSERT(settings);
1294
1295 const UINT32 maxRetries =
1296 freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1297
1298 /* Only auto reconnect on network disconnects. */
1299 error = freerdp_error_info(instance);
1300 switch (error)
1301 {
1302 case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1303 /* A network disconnect was detected */
1304 WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1305 freerdp_get_error_info_string(error));
1306 break;
1307 case ERRINFO_SUCCESS:
1308 /* A network disconnect was detected */
1309 WLog_INFO(TAG, "Network disconnect!");
1310 break;
1311 default:
1312 return FALSE;
1313 }
1314
1315 if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1316 {
1317 /* No auto-reconnect - just quit */
1318 return FALSE;
1319 }
1320
1321 switch (freerdp_get_last_error(instance->context))
1322 {
1323 case FREERDP_ERROR_CONNECT_CANCELLED:
1324 WLog_WARN(TAG, "Connection aborted by user");
1325 return FALSE;
1326 default:
1327 break;
1328 }
1329
1330 /* Perform an auto-reconnect. */
1331 while (retry)
1332 {
1333 /* Quit retrying if max retries has been exceeded */
1334 if ((maxRetries > 0) && (numRetries++ >= maxRetries))
1335 {
1336 return FALSE;
1337 }
1338
1339 /* Attempt the next reconnect */
1340 WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1341
1342 IFCALL(instance->RetryDialog, instance, "connection", numRetries, NULL);
1343
1344 if (freerdp_reconnect(instance))
1345 return TRUE;
1346
1347 switch (freerdp_get_last_error(instance->context))
1348 {
1349 case FREERDP_ERROR_CONNECT_CANCELLED:
1350 WLog_WARN(TAG, "Autoreconnect aborted by user");
1351 return FALSE;
1352 default:
1353 break;
1354 }
1355 for (UINT32 x = 0; x < 50; x++)
1356 {
1357 if (!IFCALLRESULT(TRUE, window_events, instance))
1358 return FALSE;
1359
1360 Sleep(10);
1361 }
1362 }
1363
1364 WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1365 return FALSE;
1366}
1367
1368int freerdp_client_common_stop(rdpContext* context)
1369{
1370 rdpClientContext* cctx = (rdpClientContext*)context;
1371 WINPR_ASSERT(cctx);
1372
1373 freerdp_abort_connect_context(&cctx->context);
1374
1375 if (cctx->thread)
1376 {
1377 (void)WaitForSingleObject(cctx->thread, INFINITE);
1378 (void)CloseHandle(cctx->thread);
1379 cctx->thread = NULL;
1380 }
1381
1382 return 0;
1383}
1384
1385#if defined(CHANNEL_ENCOMSP_CLIENT)
1386BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1387{
1388 rdpClientContext* cctx = NULL;
1389 BOOL state = 0;
1390
1391 if (!encomsp)
1392 return FALSE;
1393
1394 cctx = (rdpClientContext*)encomsp->custom;
1395
1396 state = cctx->controlToggle;
1397 cctx->controlToggle = !cctx->controlToggle;
1398 return freerdp_client_encomsp_set_control(encomsp, state);
1399}
1400
1401BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1402{
1404
1405 if (!encomsp)
1406 return FALSE;
1407
1408 pdu.ParticipantId = encomsp->participantId;
1409 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1410
1411 if (control)
1412 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1413
1414 encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1415
1416 return TRUE;
1417}
1418
1419static UINT
1420client_encomsp_participant_created(EncomspClientContext* context,
1421 const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1422{
1423 rdpClientContext* cctx = NULL;
1424 rdpSettings* settings = NULL;
1425 BOOL request = 0;
1426
1427 if (!context || !context->custom || !participantCreated)
1428 return ERROR_INVALID_PARAMETER;
1429
1430 cctx = (rdpClientContext*)context->custom;
1431 WINPR_ASSERT(cctx);
1432
1433 settings = cctx->context.settings;
1434 WINPR_ASSERT(settings);
1435
1436 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1437 context->participantId = participantCreated->ParticipantId;
1438
1439 request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1440 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1441 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1442 {
1443 if (!freerdp_client_encomsp_set_control(context, TRUE))
1444 return ERROR_INTERNAL_ERROR;
1445
1446 /* if auto-request-control setting is enabled then only request control once upon connect,
1447 * otherwise it will auto request control again every time server turns off control which
1448 * is a bit annoying */
1449 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1450 return ERROR_INTERNAL_ERROR;
1451 }
1452
1453 return CHANNEL_RC_OK;
1454}
1455
1456static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1457{
1458 cctx->encomsp = encomsp;
1459 encomsp->custom = (void*)cctx;
1460 encomsp->ParticipantCreated = client_encomsp_participant_created;
1461}
1462
1463static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1464{
1465 if (encomsp)
1466 {
1467 encomsp->custom = NULL;
1468 encomsp->ParticipantCreated = NULL;
1469 }
1470
1471 if (cctx)
1472 cctx->encomsp = NULL;
1473}
1474#endif
1475
1476void freerdp_client_OnChannelConnectedEventHandler(void* context,
1477 const ChannelConnectedEventArgs* e)
1478{
1479 rdpClientContext* cctx = (rdpClientContext*)context;
1480
1481 WINPR_ASSERT(cctx);
1482 WINPR_ASSERT(e);
1483
1484 if (0)
1485 {
1486 }
1487#if defined(CHANNEL_AINPUT_CLIENT)
1488 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1489 cctx->ainput = (AInputClientContext*)e->pInterface;
1490#endif
1491#if defined(CHANNEL_RDPEI_CLIENT)
1492 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1493 {
1494 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1495 }
1496#endif
1497#if defined(CHANNEL_RDPGFX_CLIENT)
1498 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1499 {
1500 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1501 }
1502#endif
1503#if defined(CHANNEL_GEOMETRY_CLIENT)
1504 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1505 {
1506 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1507 }
1508#endif
1509#if defined(CHANNEL_VIDEO_CLIENT)
1510 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1511 {
1512 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1513 }
1514 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1515 {
1516 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1517 }
1518#endif
1519#if defined(CHANNEL_ENCOMSP_CLIENT)
1520 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1521 {
1522 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1523 }
1524#endif
1525}
1526
1527void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1528 const ChannelDisconnectedEventArgs* e)
1529{
1530 rdpClientContext* cctx = (rdpClientContext*)context;
1531
1532 WINPR_ASSERT(cctx);
1533 WINPR_ASSERT(e);
1534
1535 if (0)
1536 {
1537 }
1538#if defined(CHANNEL_AINPUT_CLIENT)
1539 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1540 cctx->ainput = NULL;
1541#endif
1542#if defined(CHANNEL_RDPEI_CLIENT)
1543 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1544 {
1545 cctx->rdpei = NULL;
1546 }
1547#endif
1548#if defined(CHANNEL_RDPGFX_CLIENT)
1549 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1550 {
1551 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1552 }
1553#endif
1554#if defined(CHANNEL_GEOMETRY_CLIENT)
1555 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1556 {
1557 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1558 }
1559#endif
1560#if defined(CHANNEL_VIDEO_CLIENT)
1561 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1562 {
1563 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1564 }
1565 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1566 {
1567 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1568 }
1569#endif
1570#if defined(CHANNEL_ENCOMSP_CLIENT)
1571 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1572 {
1573 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1574 }
1575#endif
1576}
1577
1578BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1579{
1580 BOOL handled = FALSE;
1581
1582 WINPR_ASSERT(cctx);
1583
1584#if defined(CHANNEL_AINPUT_CLIENT)
1585 if (cctx->ainput)
1586 {
1587 UINT rc = 0;
1588 UINT64 flags = 0;
1589 INT32 x = 0;
1590 INT32 y = 0;
1591 INT32 value = mflags & 0xFF;
1592
1593 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1594 value = -1 * (0x100 - value);
1595
1596 /* We have discrete steps, scale this so we can also support high
1597 * resolution wheels. */
1598 value *= 0x10000;
1599
1600 if (mflags & PTR_FLAGS_WHEEL)
1601 {
1602 flags |= AINPUT_FLAGS_WHEEL;
1603 y = value;
1604 }
1605
1606 if (mflags & PTR_FLAGS_HWHEEL)
1607 {
1608 flags |= AINPUT_FLAGS_WHEEL;
1609 x = value;
1610 }
1611
1612 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1613 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1614 if (rc == CHANNEL_RC_OK)
1615 handled = TRUE;
1616 }
1617#endif
1618
1619 if (!handled)
1620 freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1621
1622 return TRUE;
1623}
1624
1625#if defined(CHANNEL_AINPUT_CLIENT)
1626static INLINE BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1627{
1628 UINT rc = 0;
1629
1630 WINPR_ASSERT(cctx);
1631 WINPR_ASSERT(cctx->ainput);
1632 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1633
1634 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1635
1636 return rc == CHANNEL_RC_OK;
1637}
1638#endif
1639
1640BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1641 INT32 y)
1642{
1643 BOOL handled = FALSE;
1644
1645 WINPR_ASSERT(cctx);
1646
1647 const BOOL haveRelative =
1648 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1649 if (relative && haveRelative)
1650 {
1651 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1652 WINPR_ASSERTING_INT_CAST(int16_t, x),
1653 WINPR_ASSERTING_INT_CAST(int16_t, y));
1654 }
1655
1656#if defined(CHANNEL_AINPUT_CLIENT)
1657 if (cctx->ainput)
1658 {
1659 UINT64 flags = 0;
1660
1661 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1662 flags |= AINPUT_FLAGS_HAVE_REL;
1663
1664 if (relative)
1665 flags |= AINPUT_FLAGS_REL;
1666
1667 if (mflags & PTR_FLAGS_DOWN)
1668 flags |= AINPUT_FLAGS_DOWN;
1669 if (mflags & PTR_FLAGS_BUTTON1)
1670 flags |= AINPUT_FLAGS_BUTTON1;
1671 if (mflags & PTR_FLAGS_BUTTON2)
1672 flags |= AINPUT_FLAGS_BUTTON2;
1673 if (mflags & PTR_FLAGS_BUTTON3)
1674 flags |= AINPUT_FLAGS_BUTTON3;
1675 if (mflags & PTR_FLAGS_MOVE)
1676 flags |= AINPUT_FLAGS_MOVE;
1677 handled = ainput_send_diff_event(cctx, flags, x, y);
1678 }
1679#endif
1680
1681 if (!handled)
1682 {
1683 if (relative)
1684 {
1685 cctx->lastX += x;
1686 cctx->lastY += y;
1687 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1688 }
1689 else
1690 {
1691 cctx->lastX = x;
1692 cctx->lastY = y;
1693 }
1694 freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1695 (UINT16)cctx->lastY);
1696 }
1697 return TRUE;
1698}
1699
1700BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1701 INT32 x, INT32 y)
1702{
1703 BOOL handled = FALSE;
1704 WINPR_ASSERT(cctx);
1705
1706 const BOOL haveRelative =
1707 freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1708 if (relative && haveRelative)
1709 {
1710 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1711 WINPR_ASSERTING_INT_CAST(int16_t, x),
1712 WINPR_ASSERTING_INT_CAST(int16_t, y));
1713 }
1714
1715#if defined(CHANNEL_AINPUT_CLIENT)
1716 if (cctx->ainput)
1717 {
1718 UINT64 flags = 0;
1719
1720 if (relative)
1721 flags |= AINPUT_FLAGS_REL;
1722 if (mflags & PTR_XFLAGS_DOWN)
1723 flags |= AINPUT_FLAGS_DOWN;
1724 if (mflags & PTR_XFLAGS_BUTTON1)
1725 flags |= AINPUT_XFLAGS_BUTTON1;
1726 if (mflags & PTR_XFLAGS_BUTTON2)
1727 flags |= AINPUT_XFLAGS_BUTTON2;
1728
1729 handled = ainput_send_diff_event(cctx, flags, x, y);
1730 }
1731#endif
1732
1733 if (!handled)
1734 {
1735 if (relative)
1736 {
1737 cctx->lastX += x;
1738 cctx->lastY += y;
1739 WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1740 }
1741 else
1742 {
1743 cctx->lastX = x;
1744 cctx->lastY = y;
1745 }
1746 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1747 (UINT16)cctx->lastY);
1748 }
1749
1750 return TRUE;
1751}
1752
1753static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1754{
1755 WINPR_ASSERT(cctx);
1756 WINPR_ASSERT(contact);
1757
1758#if defined(CHANNEL_RDPEI_CLIENT)
1759 RdpeiClientContext* rdpei = cctx->rdpei;
1760
1761 if (!rdpei)
1762 {
1763 UINT16 flags = 0;
1764 flags |= PTR_FLAGS_BUTTON1;
1765
1766 WINPR_ASSERT(contact->x <= UINT16_MAX);
1767 WINPR_ASSERT(contact->y <= UINT16_MAX);
1768 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1769 }
1770 else
1771 {
1772 int contactId = 0;
1773
1774 if (rdpei->TouchRawEvent)
1775 {
1776 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1777 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1778 ? CONTACT_DATA_PRESSURE_PRESENT
1779 : 0;
1780 // Ensure contact position is unchanged from "engaged" to "out of range" state
1781 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1782 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1783 RDPINPUT_CONTACT_FLAG_INCONTACT,
1784 contactFlags, contact->pressure);
1785 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1786 contactFlags, contact->pressure);
1787 }
1788 else
1789 {
1790 WINPR_ASSERT(rdpei->TouchEnd);
1791 rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1792 }
1793 }
1794#else
1795 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1796 "-DWITH_CHANNELS=ON");
1797#endif
1798
1799 return TRUE;
1800}
1801
1802static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1803{
1804 WINPR_ASSERT(cctx);
1805 WINPR_ASSERT(contact);
1806
1807#if defined(CHANNEL_RDPEI_CLIENT)
1808 RdpeiClientContext* rdpei = cctx->rdpei;
1809
1810 // Emulate mouse click if touch is not possible, like in login screen
1811 if (!rdpei)
1812 {
1813 UINT16 flags = 0;
1814 flags |= PTR_FLAGS_DOWN;
1815 flags |= PTR_FLAGS_MOVE;
1816 flags |= PTR_FLAGS_BUTTON1;
1817
1818 WINPR_ASSERT(contact->x <= UINT16_MAX);
1819 WINPR_ASSERT(contact->y <= UINT16_MAX);
1820 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1821 }
1822 else
1823 {
1824 int contactId = 0;
1825
1826 if (rdpei->TouchRawEvent)
1827 {
1828 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1829 RDPINPUT_CONTACT_FLAG_INCONTACT;
1830 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1831 ? CONTACT_DATA_PRESSURE_PRESENT
1832 : 0;
1833 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1834 contactFlags, contact->pressure);
1835 }
1836 else
1837 {
1838 WINPR_ASSERT(rdpei->TouchBegin);
1839 rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1840 }
1841 }
1842#else
1843 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1844 "-DWITH_CHANNELS=ON");
1845#endif
1846
1847 return TRUE;
1848}
1849
1850static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1851{
1852 WINPR_ASSERT(cctx);
1853 WINPR_ASSERT(contact);
1854
1855#if defined(CHANNEL_RDPEI_CLIENT)
1856 RdpeiClientContext* rdpei = cctx->rdpei;
1857
1858 if (!rdpei)
1859 {
1860 UINT16 flags = 0;
1861 flags |= PTR_FLAGS_MOVE;
1862
1863 WINPR_ASSERT(contact->x <= UINT16_MAX);
1864 WINPR_ASSERT(contact->y <= UINT16_MAX);
1865 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1866 }
1867 else
1868 {
1869 int contactId = 0;
1870
1871 if (rdpei->TouchRawEvent)
1872 {
1873 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1874 RDPINPUT_CONTACT_FLAG_INCONTACT;
1875 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1876 ? CONTACT_DATA_PRESSURE_PRESENT
1877 : 0;
1878 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1879 contactFlags, contact->pressure);
1880 }
1881 else
1882 {
1883 WINPR_ASSERT(rdpei->TouchUpdate);
1884 rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
1885 }
1886 }
1887#else
1888 WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1889 "-DWITH_CHANNELS=ON");
1890#endif
1891
1892 return TRUE;
1893}
1894
1895static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
1896 UINT32 pressure, INT32 x, INT32 y,
1897 FreeRDP_TouchContact* pcontact)
1898{
1899 WINPR_ASSERT(cctx);
1900 WINPR_ASSERT(pcontact);
1901
1902 for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
1903 {
1904 FreeRDP_TouchContact* contact = &cctx->contacts[i];
1905
1906 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
1907 if (newcontact || (contact->id == touchId))
1908 {
1909 contact->id = touchId;
1910 contact->flags = flags;
1911 contact->pressure = pressure;
1912 contact->x = x;
1913 contact->y = y;
1914
1915 *pcontact = *contact;
1916
1917 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
1918 if (resetcontact)
1919 {
1920 FreeRDP_TouchContact empty = { 0 };
1921 *contact = empty;
1922 }
1923 return TRUE;
1924 }
1925 }
1926
1927 return FALSE;
1928}
1929
1930BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
1931 UINT32 pressure, INT32 x, INT32 y)
1932{
1933 const UINT32 mask = FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION;
1934 WINPR_ASSERT(cctx);
1935
1936 FreeRDP_TouchContact contact = { 0 };
1937
1938 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
1939 return FALSE;
1940
1941 switch (flags & mask)
1942 {
1943 case FREERDP_TOUCH_DOWN:
1944 return freerdp_handle_touch_down(cctx, &contact);
1945 case FREERDP_TOUCH_UP:
1946 return freerdp_handle_touch_up(cctx, &contact);
1947 case FREERDP_TOUCH_MOTION:
1948 return freerdp_handle_touch_motion(cctx, &contact);
1949 default:
1950 WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %d, ignoring", flags);
1951 return FALSE;
1952 }
1953}
1954
1955BOOL freerdp_client_load_channels(freerdp* instance)
1956{
1957 WINPR_ASSERT(instance);
1958 WINPR_ASSERT(instance->context);
1959
1960 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
1961 {
1962 WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
1963 return FALSE;
1964 }
1965 return TRUE;
1966}
1967
1968int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
1969{
1970 const char* str_data = freerdp_get_logon_error_info_data(data);
1971 const char* str_type = freerdp_get_logon_error_info_type(type);
1972
1973 if (!instance || !instance->context)
1974 return -1;
1975
1976 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
1977 return 1;
1978}
1979
1980static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
1981 size_t* pos)
1982{
1983 WINPR_ASSERT(cctx);
1984
1985 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
1986 {
1987 FreeRDP_PenDevice* pen = &cctx->pens[i];
1988 if (deviceid == pen->deviceid)
1989 {
1990 if (pos)
1991 *pos = i;
1992 return pen;
1993 }
1994 }
1995 return NULL;
1996}
1997
1998static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
1999 double pressure)
2000{
2001 static const INT32 null_deviceid = 0;
2002
2003 WINPR_ASSERT(cctx);
2004 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2005 if (freerdp_client_is_pen(cctx, deviceid))
2006 {
2007 WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2008 return FALSE;
2009 }
2010
2011 size_t pos = 0;
2012 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2013 if (pen)
2014 {
2015 const FreeRDP_PenDevice empty = { 0 };
2016 *pen = empty;
2017
2018 pen->deviceid = deviceid;
2019 pen->max_pressure = pressure;
2020 pen->flags = flags;
2021
2022 WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2023 return TRUE;
2024 }
2025
2026 WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2027 return TRUE;
2028}
2029
2030BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2031{
2032 if ((flags & FREERDP_PEN_REGISTER) != 0)
2033 {
2034 va_list args;
2035
2036 va_start(args, deviceid);
2037 double pressure = va_arg(args, double);
2038 va_end(args);
2039 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2040 }
2041 size_t pos = 0;
2042 FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2043 if (!pen)
2044 {
2045 WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2046 return FALSE;
2047 }
2048
2049 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2050 UINT32 penFlags =
2051 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2052
2053 RdpeiClientContext* rdpei = cctx->rdpei;
2054 WINPR_ASSERT(rdpei);
2055
2056 UINT32 normalizedpressure = 1024;
2057 INT32 x = 0;
2058 INT32 y = 0;
2059 UINT16 rotation = 0;
2060 INT16 tiltX = 0;
2061 INT16 tiltY = 0;
2062 va_list args;
2063 va_start(args, deviceid);
2064
2065 x = va_arg(args, INT32);
2066 y = va_arg(args, INT32);
2067 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2068 {
2069 const double pressure = va_arg(args, double);
2070 const double np = (pressure * 1024.0) / pen->max_pressure;
2071 normalizedpressure = (UINT32)lround(np);
2072 WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2073 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2074 }
2075 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2076 {
2077 const unsigned arg = va_arg(args, unsigned);
2078 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2079 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2080 }
2081 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2082 {
2083 const int arg = va_arg(args, int);
2084 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2085 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2086 }
2087 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2088 {
2089 const int arg = va_arg(args, int);
2090 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2091 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2092 }
2093 va_end(args);
2094
2095 if ((flags & FREERDP_PEN_PRESS) != 0)
2096 {
2097 // Ensure that only one button is pressed
2098 if (pen->pressed)
2099 flags = FREERDP_PEN_MOTION |
2100 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2101 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2102 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2103 }
2104 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2105 {
2106 if (!pen->pressed ||
2107 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2108 flags = FREERDP_PEN_MOTION |
2109 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2110 else
2111 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2112 }
2113
2114 flags |= pen->flags;
2115 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2116 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2117 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2118 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2119
2120 pen->last_x = x;
2121 pen->last_y = y;
2122 if ((flags & FREERDP_PEN_PRESS) != 0)
2123 {
2124 WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2125 pen->hovering = FALSE;
2126 pen->pressed = TRUE;
2127
2128 WINPR_ASSERT(rdpei->PenBegin);
2129 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2130 normalizedpressure, rotation, tiltX, tiltY);
2131 return rc == CHANNEL_RC_OK;
2132 }
2133 else if ((flags & FREERDP_PEN_MOTION) != 0)
2134 {
2135 UINT rc = ERROR_INTERNAL_ERROR;
2136 if (pen->pressed)
2137 {
2138 WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2139
2140 // TODO: what if no rotation is supported but tilt is?
2141 WINPR_ASSERT(rdpei->PenUpdate);
2142 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2143 rotation, tiltX, tiltY);
2144 }
2145 else if (pen->hovering)
2146 {
2147 WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2148
2149 WINPR_ASSERT(rdpei->PenHoverUpdate);
2150 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2151 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2152 }
2153 else
2154 {
2155 WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2156 pen->hovering = TRUE;
2157
2158 WINPR_ASSERT(rdpei->PenHoverBegin);
2159 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2160 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2161 }
2162 return rc == CHANNEL_RC_OK;
2163 }
2164 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2165 {
2166 WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2167 pen->pressed = FALSE;
2168 pen->hovering = TRUE;
2169
2170 WINPR_ASSERT(rdpei->PenUpdate);
2171 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2172 normalizedpressure, rotation, tiltX, tiltY);
2173 if (rc != CHANNEL_RC_OK)
2174 return FALSE;
2175 WINPR_ASSERT(rdpei->PenEnd);
2176 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2177 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2178 return re == CHANNEL_RC_OK;
2179 }
2180
2181 WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2182 return FALSE;
2183}
2184
2185BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2186{
2187 WINPR_ASSERT(cctx);
2188
2189 RdpeiClientContext* rdpei = cctx->rdpei;
2190
2191 if (!rdpei)
2192 return FALSE;
2193
2194 for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2195 {
2196 FreeRDP_PenDevice* pen = &cctx->pens[i];
2197 if (pen->hovering)
2198 {
2199 WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2200 pen->hovering = FALSE;
2201 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2202 }
2203 }
2204 return TRUE;
2205}
2206
2207BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2208{
2209 WINPR_ASSERT(cctx);
2210
2211 if (deviceid == 0)
2212 return FALSE;
2213
2214 for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2215 {
2216 const FreeRDP_PenDevice* pen = &cctx->pens[x];
2217 if (pen->deviceid == deviceid)
2218 return TRUE;
2219 }
2220
2221 return FALSE;
2222}
2223
2224BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* ccontext)
2225{
2226 WINPR_ASSERT(ccontext);
2227
2228 const rdpSettings* settings = ccontext->context.settings;
2229 const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2230 const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2231 BOOL ainput = FALSE;
2232#if defined(CHANNEL_AINPUT_CLIENT)
2233 ainput = ccontext->ainput != NULL;
2234#endif
2235
2236 return useRelative && (haveRelative || ainput);
2237}
2238
2239static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2240{
2241 const rdpSettings* settings = cctx->context.settings;
2242 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2243 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2244 AAD_WELLKNOWN_authorization_endpoint);
2245 const char* redirect_fmt =
2246 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2247 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2248
2249 if (!client_id || !ep || !redirect_fmt || !scope)
2250 return NULL;
2251
2252 char* redirect_uri = NULL;
2253 size_t redirect_len = 0;
2254 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2255
2256 if (!redirect_uri)
2257 return NULL;
2258
2259 char* url = NULL;
2260 size_t urllen = 0;
2261 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2262 client_id, scope, redirect_uri);
2263 free(redirect_uri);
2264 return url;
2265}
2266
2267static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2268{
2269 const rdpSettings* settings = cctx->context.settings;
2270 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2271 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2272 AAD_WELLKNOWN_authorization_endpoint);
2273 const char* redirect_fmt =
2274 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2275 const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2276
2277 if (!client_id || !ep || !redirect_fmt || !scope)
2278 return NULL;
2279
2280 char* redirect_uri = NULL;
2281 size_t redirect_len = 0;
2282 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2283
2284 if (!redirect_uri)
2285 return NULL;
2286
2287 char* url = NULL;
2288 size_t urllen = 0;
2289
2290 const char* code = va_arg(ap, const char*);
2291 winpr_asprintf(&url, &urllen,
2292 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2293 code, client_id, scope, redirect_uri);
2294 free(redirect_uri);
2295 return url;
2296}
2297
2298static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2299{
2300 const rdpSettings* settings = cctx->context.settings;
2301 char* url = NULL;
2302 size_t urllen = 0;
2303 char* redirect_uri = NULL;
2304 size_t redirect_len = 0;
2305
2306 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2307 if (!client_id)
2308 goto cleanup;
2309 const char* base = freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2310 const char* urlFormatString =
2311 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2312 const char* scope = va_arg(ap, const char*);
2313 if (!urlFormatString || !scope)
2314 goto cleanup;
2315
2316 const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2317 const char* tenantid = "common";
2318 if (useTenant)
2319 tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2320 if (!base || !tenantid || !client_id)
2321 goto cleanup;
2322
2323 winpr_asprintf(&redirect_uri, &redirect_len, urlFormatString, base, tenantid);
2324 if (!redirect_uri)
2325 goto cleanup;
2326
2327 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2328 AAD_WELLKNOWN_authorization_endpoint);
2329
2330 winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2331 client_id, scope, redirect_uri);
2332cleanup:
2333 free(redirect_uri);
2334 return url;
2335}
2336
2337static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2338{
2339 const rdpSettings* settings = cctx->context.settings;
2340 const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2341 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2342 AAD_WELLKNOWN_authorization_endpoint);
2343 const char* redirect_fmt =
2344 freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2345 const char* scope = va_arg(ap, const char*);
2346 const char* code = va_arg(ap, const char*);
2347 const char* req_cnf = va_arg(ap, const char*);
2348
2349 if (!client_id || !ep || !redirect_fmt || !scope || !code || !req_cnf)
2350 return NULL;
2351
2352 char* redirect_uri = NULL;
2353 size_t redirect_len = 0;
2354 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2355
2356 if (!redirect_uri)
2357 return NULL;
2358
2359 char* url = NULL;
2360 size_t urllen = 0;
2361
2362 winpr_asprintf(
2363 &url, &urllen,
2364 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2365 code, client_id, scope, redirect_uri, req_cnf);
2366 free(redirect_uri);
2367 return url;
2368}
2369
2370char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2371{
2372 WINPR_ASSERT(cctx);
2373 char* str = NULL;
2374
2375 va_list ap;
2376 va_start(ap, type);
2377 switch (type)
2378 {
2379 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2380 str = aad_auth_request(cctx, ap);
2381 break;
2382 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2383 str = aad_token_request(cctx, ap);
2384 break;
2385 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2386 str = avd_auth_request(cctx, ap);
2387 break;
2388 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2389 str = avd_token_request(cctx, ap);
2390 break;
2391 default:
2392 break;
2393 }
2394 va_end(ap);
2395 return str;
2396}
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.