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