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