21#include <winpr/cast.h>
23#include <freerdp/config.h>
31#include <freerdp/client.h>
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/event.h>
41#include <freerdp/utils/smartcardlogon.h>
42#include <freerdp/session.h>
44#if defined(CHANNEL_AINPUT_CLIENT)
45#include <freerdp/client/ainput.h>
46#include <freerdp/channels/ainput.h>
49#if defined(CHANNEL_VIDEO_CLIENT)
50#include <freerdp/client/video.h>
51#include <freerdp/channels/video.h>
54#if defined(CHANNEL_RDPGFX_CLIENT)
55#include <freerdp/client/rdpgfx.h>
56#include <freerdp/channels/rdpgfx.h>
57#include <freerdp/gdi/gfx.h>
60#if defined(CHANNEL_GEOMETRY_CLIENT)
61#include <freerdp/client/geometry.h>
62#include <freerdp/channels/geometry.h>
65#if defined(CHANNEL_GEOMETRY_CLIENT) || defined(CHANNEL_VIDEO_CLIENT)
66#include <freerdp/gdi/video.h>
69#include <freerdp/channels/rdpewa.h>
72#include <freerdp/utils/http.h>
73#include <freerdp/utils/aad.h>
77#include "sso_mib_tokens.h"
80#include <freerdp/log.h>
81#define TAG CLIENT_TAG("common")
83static void set_default_callbacks(freerdp* instance)
85 WINPR_ASSERT(instance);
86 instance->AuthenticateEx = client_cli_authenticate_ex;
87 instance->ChooseSmartcard = client_cli_choose_smartcard;
88 instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
89 instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
90 instance->PresentGatewayMessage = client_cli_present_gateway_message;
91 instance->LogonErrorInfo = client_cli_logon_error_info;
92 instance->GetAccessToken = client_cli_get_access_token;
93 instance->RetryDialog = client_common_retry_dialog;
95 WINPR_ASSERT(instance->context);
96 WINPR_ASSERT(instance->context->update);
97 instance->context->update->SaveSessionInfo = client_common_save_session_info;
100static void client_cli_user_notification(
void* context,
const UserNotificationEventArgs* e)
102 WINPR_UNUSED(context);
104 if (strcmp(e->e.Sender, RDPEWA_CHANNEL_NAME) != 0)
107 if (!e->message || e->message[0] ==
'\0')
109 (void)fprintf(stderr,
"[%s] Touch the security key\n", e->e.Sender);
110 (void)fflush(stderr);
113static BOOL freerdp_client_common_new(freerdp* instance, rdpContext* context)
115 RDP_CLIENT_ENTRY_POINTS* pEntryPoints =
nullptr;
117 WINPR_ASSERT(instance);
118 WINPR_ASSERT(context);
120 instance->LoadChannels = freerdp_client_load_channels;
121 set_default_callbacks(instance);
123 pEntryPoints = instance->pClientEntryPoints;
124 WINPR_ASSERT(pEntryPoints);
126 return IFCALLRESULT(TRUE, pEntryPoints->ClientNew, instance, context);
129static void freerdp_client_common_free(freerdp* instance, rdpContext* context)
131 RDP_CLIENT_ENTRY_POINTS* pEntryPoints =
nullptr;
133 WINPR_ASSERT(instance);
134 WINPR_ASSERT(context);
136 pEntryPoints = instance->pClientEntryPoints;
137 WINPR_ASSERT(pEntryPoints);
138 IFCALL(pEntryPoints->ClientFree, instance, context);
143rdpContext* freerdp_client_context_new(
const RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
145 freerdp* instance =
nullptr;
146 rdpContext* context =
nullptr;
151 if (!IFCALLRESULT(TRUE, pEntryPoints->GlobalInit))
154 instance = freerdp_new();
159 instance->ContextSize = pEntryPoints->ContextSize;
160 instance->ContextNew = freerdp_client_common_new;
161 instance->ContextFree = freerdp_client_common_free;
162 instance->pClientEntryPoints = (RDP_CLIENT_ENTRY_POINTS*)malloc(pEntryPoints->Size);
164 if (!instance->pClientEntryPoints)
167 CopyMemory(instance->pClientEntryPoints, pEntryPoints, pEntryPoints->Size);
169 if (!freerdp_context_new_ex(instance, pEntryPoints->settings))
172 context = instance->context;
173 context->instance = instance;
175#if defined(WITH_CLIENT_CHANNELS)
176 if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
183 free(instance->pClientEntryPoints);
185 freerdp_free(instance);
189void freerdp_client_context_free(rdpContext* context)
191 freerdp* instance =
nullptr;
196 instance = context->instance;
200 RDP_CLIENT_ENTRY_POINTS* pEntryPoints = instance->pClientEntryPoints;
201 freerdp_context_free(instance);
204 IFCALL(pEntryPoints->GlobalUninit);
206 free(instance->pClientEntryPoints);
207 freerdp_free(instance);
211int freerdp_client_start(rdpContext* context)
213 RDP_CLIENT_ENTRY_POINTS* pEntryPoints =
nullptr;
215 if (!context || !context->instance || !context->instance->pClientEntryPoints)
216 return ERROR_BAD_ARGUMENTS;
220 set_default_callbacks(context->instance);
224 PubSub_SubscribeUserNotification(context->pubSub, client_cli_user_notification);
231 rdpClientContext* client_context = (rdpClientContext*)context;
232 client_context->mibClientWrapper = sso_mib_new(context);
233 if (!client_context->mibClientWrapper)
234 return ERROR_INTERNAL_ERROR;
237 pEntryPoints = context->instance->pClientEntryPoints;
238 return IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStart, context);
241int freerdp_client_stop(rdpContext* context)
243 RDP_CLIENT_ENTRY_POINTS* pEntryPoints =
nullptr;
245 if (!context || !context->instance || !context->instance->pClientEntryPoints)
246 return ERROR_BAD_ARGUMENTS;
248 pEntryPoints = context->instance->pClientEntryPoints;
249 const int rc = IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
252 PubSub_UnsubscribeUserNotification(context->pubSub, client_cli_user_notification);
255 rdpClientContext* client_context = (rdpClientContext*)context;
256 sso_mib_free(client_context->mibClientWrapper);
257 client_context->mibClientWrapper =
nullptr;
262freerdp* freerdp_client_get_instance(rdpContext* context)
264 if (!context || !context->instance)
267 return context->instance;
270HANDLE freerdp_client_get_thread(rdpContext* context)
275 return ((rdpClientContext*)context)->thread;
278static BOOL freerdp_client_settings_post_process(rdpSettings* settings)
303 settings, FreeRDP_GatewayPassword,
341int freerdp_client_settings_parse_command_line(rdpSettings* settings,
int argc,
char** argv,
345 return freerdp_client_settings_parse_command_line_ex(settings, argc, argv, allowUnknown,
346 nullptr, 0,
nullptr,
nullptr);
349int freerdp_client_settings_parse_command_line_ex(
351 size_t count, freerdp_command_line_handle_option_t handle_option,
void* handle_userdata)
361 status = freerdp_client_settings_parse_command_line_arguments_ex(
362 settings, argc, argv, allowUnknown, args, count, handle_option, handle_userdata);
369 if (!freerdp_client_settings_post_process(settings))
372 const char* name = argv[0];
373 WLog_DBG(TAG,
"This is [%s] %s %s", name, freerdp_get_version_string(),
374 freerdp_get_build_config());
378int freerdp_client_settings_parse_connection_file(rdpSettings* settings,
const char* filename)
380 rdpFile* file =
nullptr;
382 file = freerdp_client_rdp_file_new();
387 if (!freerdp_client_parse_rdp_file(file, filename))
390 if (!freerdp_client_populate_settings_from_rdp_file(file, settings))
395 freerdp_client_rdp_file_free(file);
399int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings,
const BYTE* buffer,
402 rdpFile* file =
nullptr;
404 file = freerdp_client_rdp_file_new();
409 if (freerdp_client_parse_rdp_file_buffer(file, buffer, size) &&
410 freerdp_client_populate_settings_from_rdp_file(file, settings))
415 freerdp_client_rdp_file_free(file);
419int freerdp_client_settings_write_connection_file(
const rdpSettings* settings,
const char* filename,
422 rdpFile* file =
nullptr;
424 file = freerdp_client_rdp_file_new();
429 if (!freerdp_client_populate_rdp_file_from_settings(file, settings))
432 if (!freerdp_client_write_rdp_file(file, filename, unicode))
437 freerdp_client_rdp_file_free(file);
441int freerdp_client_settings_parse_assistance_file(rdpSettings* settings,
int argc,
char* argv[])
445 char* filename =
nullptr;
446 char* password =
nullptr;
447 rdpAssistanceFile* file =
nullptr;
449 if (!settings || !argv || (argc < 2))
454 for (
int x = 2; x < argc; x++)
456 const char* key = strstr(argv[x],
"assistance:");
460 char* sep = strchr(key,
':');
468 file = freerdp_assistance_file_new();
473 status = freerdp_assistance_parse_file(file, filename, password);
478 if (!freerdp_assistance_populate_settings_from_assistance_file(file, settings))
483 freerdp_assistance_file_free(file);
487static int client_cli_read_string(freerdp* instance,
const char* what,
const char* suggestion,
490 WINPR_ASSERT(instance);
492 WINPR_ASSERT(result);
496 (void)fflush(stdout);
498 char* line =
nullptr;
499 if (suggestion && strlen(suggestion) > 0)
501 line = _strdup(suggestion);
502 size = strlen(suggestion);
505 const SSIZE_T rc = freerdp_interruptible_get_line(instance->context, &line, &size, stdin);
508 char ebuffer[256] = WINPR_C_ARRAY_INIT;
509 WLog_ERR(TAG,
"freerdp_interruptible_get_line returned %s [%d]",
510 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)), errno);
520 line = StrSep(&line,
"\r");
521 line = StrSep(&line,
"\n");
542static BOOL client_cli_authenticate_raw(freerdp* instance, rdp_auth_reason reason,
char** username,
543 char** password,
char** domain)
545 static const size_t password_size = 512;
546 const char* userAuth =
"Username: ";
547 const char* domainAuth =
"Domain: ";
548 const char* pwdAuth =
"Password: ";
549 BOOL pinOnly = FALSE;
550 BOOL queryAll = FALSE;
552 WINPR_ASSERT(instance);
553 WINPR_ASSERT(instance->context);
554 WINPR_ASSERT(instance->context->settings);
558 case AUTH_SMARTCARD_PIN:
559 pwdAuth =
"Smartcard-Pin: ";
563 pwdAuth =
"FIDO2 PIN: ";
576 userAuth =
"GatewayUsername: ";
577 domainAuth =
"GatewayDomain: ";
578 pwdAuth =
"GatewayPassword: ";
584 if (!username || !password || !domain)
589 const char* suggest = *username;
590 if (queryAll || !suggest)
592 const int rc = client_cli_read_string(instance, userAuth, suggest, username);
600 const char* suggest = *domain;
601 if (queryAll || !suggest)
603 const int rc = client_cli_read_string(instance, domainAuth, suggest, domain);
610 char* line = calloc(password_size,
sizeof(
char));
615 const BOOL fromStdin =
618 freerdp_passphrase_read(instance->context, pwdAuth, line, password_size, fromStdin);
622 if (password_size > 0)
640BOOL client_cli_authenticate_ex(freerdp* instance,
char** username,
char** password,
char** domain,
641 rdp_auth_reason reason)
643 WINPR_ASSERT(instance);
644 WINPR_ASSERT(username);
645 WINPR_ASSERT(password);
646 WINPR_ASSERT(domain);
656 case AUTH_SMARTCARD_PIN:
658 if ((*username) && (*password))
669 return client_cli_authenticate_raw(instance, reason, username, password, domain);
672BOOL client_cli_choose_smartcard(WINPR_ATTR_UNUSED freerdp* instance,
SmartcardCertInfo** cert_list,
673 DWORD count, DWORD* choice, BOOL gateway)
675 unsigned long answer = 0;
678 printf(
"Multiple smartcards are available for use:\n");
679 for (DWORD i = 0; i < count; i++)
682 char* reader = ConvertWCharToUtf8Alloc(cert->reader,
nullptr);
683 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName,
nullptr);
686 "] %s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s\n",
687 i, container_name, reader, cert->userHint, cert->domainHint, cert->subject,
688 cert->issuer, cert->upn);
691 free(container_name);
696 char input[10] = WINPR_C_ARRAY_INIT;
698 printf(
"\nChoose a smartcard to use for %s (0 - %" PRIu32
"): ",
699 gateway ?
"gateway authentication" :
"logon", count - 1);
700 (void)fflush(stdout);
701 if (!fgets(input, 10, stdin))
703 WLog_ERR(TAG,
"could not read from stdin");
707 answer = strtoul(input, &p, 10);
708 if ((*p ==
'\n' && p != input) && answer < count)
710 *choice = (UINT32)answer;
716#if defined(WITH_FREERDP_DEPRECATED)
717BOOL client_cli_authenticate(freerdp* instance,
char** username,
char** password,
char** domain)
721 WLog_INFO(TAG,
"Authentication via smartcard");
725 return client_cli_authenticate_raw(instance, FALSE, username, password, domain);
728BOOL client_cli_gw_authenticate(freerdp* instance,
char** username,
char** password,
char** domain)
730 return client_cli_authenticate_raw(instance, TRUE, username, password, domain);
734static DWORD client_cli_accept_certificate(freerdp* instance)
738 WINPR_ASSERT(instance);
739 WINPR_ASSERT(instance->context);
741 const rdpSettings* settings = instance->context->settings;
742 WINPR_ASSERT(settings);
750 printf(
"Do you trust the above certificate? (Y/T/N) ");
751 (void)fflush(stdout);
752 answer = freerdp_interruptible_getc(instance->context, stdin);
754 if ((answer == EOF) || feof(stdin))
756 printf(
"\nError: Could not read answer from stdin.\n");
764 answer = freerdp_interruptible_getc(instance->context, stdin);
772 answer = freerdp_interruptible_getc(instance->context, stdin);
780 answer = freerdp_interruptible_getc(instance->context, stdin);
805#if defined(WITH_FREERDP_DEPRECATED)
806DWORD client_cli_verify_certificate(freerdp* instance,
const char* common_name,
const char* subject,
807 const char* issuer,
const char* fingerprint, BOOL host_mismatch)
809 WINPR_UNUSED(common_name);
810 WINPR_UNUSED(host_mismatch);
812 printf(
"WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
813 printf(
"Certificate details:\n");
814 printf(
"\tSubject: %s\n", subject);
815 printf(
"\tIssuer: %s\n", issuer);
816 printf(
"\tThumbprint: %s\n", fingerprint);
817 printf(
"The above X.509 certificate could not be verified, possibly because you do not have\n"
818 "the CA certificate in your certificate store, or the certificate has expired.\n"
819 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
820 return client_cli_accept_certificate(instance);
824static char* client_cli_pem_cert(
const char* pem)
826 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
830 char* fp = freerdp_certificate_get_fingerprint(cert);
831 char* start = freerdp_certificate_get_validity(cert, TRUE);
832 char* end = freerdp_certificate_get_validity(cert, FALSE);
833 freerdp_certificate_free(cert);
837 winpr_asprintf(&str, &slen,
840 "\tThumbprint: %s\n",
863DWORD client_cli_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
864 const char* common_name,
const char* subject,
865 const char* issuer,
const char* fingerprint, DWORD flags)
867 const char* type =
"RDP-Server";
869 WINPR_ASSERT(instance);
870 WINPR_ASSERT(instance->context);
871 WINPR_ASSERT(instance->context->settings);
873 if (flags & VERIFY_CERT_FLAG_GATEWAY)
874 type =
"RDP-Gateway";
876 if (flags & VERIFY_CERT_FLAG_REDIRECT)
877 type =
"RDP-Redirect";
879 printf(
"Certificate details for %s:%" PRIu16
" (%s):\n", host, port, type);
880 printf(
"\tCommon Name: %s\n", common_name);
881 printf(
"\tSubject: %s\n", subject);
882 printf(
"\tIssuer: %s\n", issuer);
886 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
888 char* str = client_cli_pem_cert(fingerprint);
893 printf(
"\tThumbprint: %s\n", fingerprint);
895 printf(
"The above X.509 certificate could not be verified, possibly because you do not have\n"
896 "the CA certificate in your certificate store, or the certificate has expired.\n"
897 "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
898 return client_cli_accept_certificate(instance);
916#if defined(WITH_FREERDP_DEPRECATED)
917DWORD client_cli_verify_changed_certificate(freerdp* instance,
const char* common_name,
918 const char* subject,
const char* issuer,
919 const char* fingerprint,
const char* old_subject,
920 const char* old_issuer,
const char* old_fingerprint)
922 WINPR_UNUSED(common_name);
924 printf(
"WARNING: This callback is deprecated, migrate to "
925 "client_cli_verify_changed_certificate_ex\n");
926 printf(
"!!! Certificate has changed !!!\n");
928 printf(
"New Certificate details:\n");
929 printf(
"\tSubject: %s\n", subject);
930 printf(
"\tIssuer: %s\n", issuer);
931 printf(
"\tThumbprint: %s\n", fingerprint);
933 printf(
"Old Certificate details:\n");
934 printf(
"\tSubject: %s\n", old_subject);
935 printf(
"\tIssuer: %s\n", old_issuer);
936 printf(
"\tThumbprint: %s\n", old_fingerprint);
938 printf(
"The above X.509 certificate does not match the certificate used for previous "
940 "This may indicate that the certificate has been tampered with.\n"
941 "Please contact the administrator of the RDP server and clarify.\n");
942 return client_cli_accept_certificate(instance);
965DWORD client_cli_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
966 const char* common_name,
const char* subject,
967 const char* issuer,
const char* fingerprint,
968 const char* old_subject,
const char* old_issuer,
969 const char* old_fingerprint, DWORD flags)
971 const char* type =
"RDP-Server";
973 WINPR_ASSERT(instance);
974 WINPR_ASSERT(instance->context);
975 WINPR_ASSERT(instance->context->settings);
977 if (flags & VERIFY_CERT_FLAG_GATEWAY)
978 type =
"RDP-Gateway";
980 if (flags & VERIFY_CERT_FLAG_REDIRECT)
981 type =
"RDP-Redirect";
983 printf(
"!!!Certificate for %s:%" PRIu16
" (%s) has changed!!!\n", host, port, type);
985 printf(
"New Certificate details:\n");
986 printf(
"\tCommon Name: %s\n", common_name);
987 printf(
"\tSubject: %s\n", subject);
988 printf(
"\tIssuer: %s\n", issuer);
992 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
994 char* str = client_cli_pem_cert(fingerprint);
999 printf(
"\tThumbprint: %s\n", fingerprint);
1001 printf(
"Old Certificate details:\n");
1002 printf(
"\tSubject: %s\n", old_subject);
1003 printf(
"\tIssuer: %s\n", old_issuer);
1007 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
1009 char* str = client_cli_pem_cert(old_fingerprint);
1014 printf(
"\tThumbprint: %s\n", old_fingerprint);
1016 if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
1018 printf(
"\tA matching entry with legacy SHA1 was found in local known_hosts2 store.\n");
1019 printf(
"\tIf you just upgraded from a FreeRDP version before 2.0 this is expected.\n");
1020 printf(
"\tThe hashing algorithm has been upgraded from SHA1 to SHA256.\n");
1021 printf(
"\tAll manually accepted certificates must be reconfirmed!\n");
1024 printf(
"The above X.509 certificate does not match the certificate used for previous "
1026 "This may indicate that the certificate has been tampered with.\n"
1027 "Please contact the administrator of the RDP server and clarify.\n");
1028 return client_cli_accept_certificate(instance);
1031BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1032 BOOL isConsentMandatory,
size_t length,
1033 const WCHAR* message)
1035 const char* msgType = (type == GATEWAY_MESSAGE_CONSENT) ?
"Consent message" :
"Service message";
1037 WINPR_ASSERT(instance);
1038 WINPR_ASSERT(instance->context);
1039 WINPR_ASSERT(instance->context->settings);
1041 if (!isDisplayMandatory && !isConsentMandatory)
1044 printf(
"%s:\n", msgType);
1046 printf(
"%.*S\n", (
int)length, message);
1049 LPSTR msg = ConvertWCharNToUtf8Alloc(message, length /
sizeof(WCHAR),
nullptr);
1052 printf(
"Failed to convert message!\n");
1055 printf(
"%s\n", msg);
1060 while (isConsentMandatory)
1062 printf(
"I understand and agree to the terms of this policy (Y/N) \n");
1063 (void)fflush(stdout);
1064 const int answer = freerdp_interruptible_getc(instance->context, stdin);
1066 if ((answer == EOF) || feof(stdin))
1068 printf(
"\nError: Could not read answer from stdin.\n");
1072 const int confirm = freerdp_interruptible_getc(instance->context, stdin);
1078 return confirm != EOF;
1093static const char* extract_authorization_code(
char* url)
1097 for (
char* p = strchr(url,
'?'); p++ !=
nullptr; p = strchr(p,
'&'))
1099 if (strncmp(p,
"code=", 5) != 0)
1102 char* end =
nullptr;
1105 end = strchr(p,
'&');
1115#if defined(WITH_AAD)
1116static BOOL client_cli_get_rdsaad_access_token(freerdp* instance,
const char* scope,
1117 const char* req_cnf,
char** token)
1119 WINPR_ASSERT(instance);
1120 WINPR_ASSERT(instance->context);
1123 char* url =
nullptr;
1124 char* token_request =
nullptr;
1126 WINPR_ASSERT(scope);
1127 WINPR_ASSERT(req_cnf);
1128 WINPR_ASSERT(token);
1133 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1134 FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1136 printf(
"Browse to: %s\n", request);
1138 printf(
"Paste redirect URL here: \n");
1140 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1144 const char* code = extract_authorization_code(url);
1148 freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1149 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1154 rc = client_common_get_access_token(instance, token_request, token);
1157 free(token_request);
1159 return rc && (*token !=
nullptr);
1162static BOOL client_cli_get_avd_access_token(freerdp* instance,
char** token)
1164 WINPR_ASSERT(instance);
1165 WINPR_ASSERT(instance->context);
1168 char* url =
nullptr;
1169 char* token_request =
nullptr;
1171 WINPR_ASSERT(token);
1177 char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1178 FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1181 printf(
"Browse to: %s\n", request);
1183 printf(
"Paste redirect URL here: \n");
1185 if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1189 const char* code = extract_authorization_code(url);
1192 token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1193 FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1199 rc = client_common_get_access_token(instance, token_request, token);
1202 free(token_request);
1204 return rc && (*token !=
nullptr);
1208BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType,
char** token,
1211 WINPR_ASSERT(instance);
1212 WINPR_ASSERT(token);
1214#if !defined(WITH_AAD)
1215 WLog_ERR(TAG,
"Build does not support AAD authentication");
1219 WINPR_ASSERT(instance->context);
1228 case ACCESS_TOKEN_TYPE_AAD:
1233 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1242 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1245 va_list ap = WINPR_C_ARRAY_INIT;
1246 va_start(ap, count);
1247 const char* scope = va_arg(ap,
const char*);
1248 const char* req_cnf = va_arg(ap,
const char*);
1249 rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1254 case ACCESS_TOKEN_TYPE_AVD:
1257 "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1260 rc = client_cli_get_avd_access_token(instance, token);
1263 WLog_ERR(TAG,
"Unexpected value for AccessTokenType [%u], aborting", tokenType);
1274BOOL client_common_get_access_token(freerdp* instance,
const char* request,
char** token)
1277 WINPR_ASSERT(request);
1278 WINPR_ASSERT(token);
1282 BYTE* response =
nullptr;
1283 size_t response_length = 0;
1285 wLog* log = WLog_Get(TAG);
1287 const char* token_ep =
1288 freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1289 if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1291 WLog_ERR(TAG,
"access token request failed");
1295 if (resp_code != HTTP_STATUS_OK)
1297 char buffer[64] = WINPR_C_ARRAY_INIT;
1299 WLog_Print(log, WLOG_ERROR,
1300 "Server unwilling to provide access token; returned status code %s",
1301 freerdp_http_status_string_format(resp_code, buffer,
sizeof(buffer)));
1302 if (response_length > 0)
1303 WLog_Print(log, WLOG_ERROR,
"[status message] %s", response);
1307 *token = freerdp_utils_aad_get_access_token(log, (
const char*)response, response_length);
1319SSIZE_T client_common_retry_dialog(freerdp* instance,
const char* what,
size_t current,
1322 WINPR_UNUSED(instance);
1323 WINPR_ASSERT(instance->context);
1324 WINPR_UNUSED(userarg);
1325 WINPR_ASSERT(instance);
1328 if ((strcmp(what,
"arm-transport") != 0) && (strcmp(what,
"connection") != 0))
1330 WLog_ERR(TAG,
"Unknown module %s, aborting", what);
1336 if (strcmp(what,
"arm-transport") == 0)
1337 WLog_INFO(TAG,
"[%s] Starting your VM. It may take up to 5 minutes", what);
1340 const rdpSettings* settings = instance->context->settings;
1344 WLog_WARN(TAG,
"Automatic reconnection disabled, terminating. Try to connect again later");
1353 "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1354 "tech support for help if this keeps happening.",
1359 WLog_INFO(TAG,
"[%s] retry %" PRIuz
"/%" PRIuz
", delaying %" PRIuz
"ms before next attempt",
1360 what, current + 1, max, delay);
1361 return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1364BOOL client_auto_reconnect(freerdp* instance)
1366 return client_auto_reconnect_ex(instance,
nullptr);
1369BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1373 UINT32 numRetries = 0;
1374 rdpSettings* settings =
nullptr;
1379 WINPR_ASSERT(instance->context);
1381 settings = instance->context->settings;
1382 WINPR_ASSERT(settings);
1384 const UINT32 maxRetries =
1388 error = freerdp_error_info(instance);
1391 case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1393 WLog_WARN(TAG,
"Disconnected by server hitting a bug or resource limit [%s]",
1394 freerdp_get_error_info_string(error));
1396 case ERRINFO_SUCCESS:
1398 WLog_INFO(TAG,
"Network disconnect!");
1401 WLog_DBG(TAG,
"Other error: %s", freerdp_get_error_info_string(error));
1408 WLog_DBG(TAG,
"AutoReconnect not enabled, quitting.");
1412 const UINT err = freerdp_get_last_error(instance->context);
1415 case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
1416 case FREERDP_ERROR_CONNECT_CLIENT_REVOKED:
1417 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
1418 case FREERDP_ERROR_CONNECT_ACCESS_DENIED:
1419 case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
1420 case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
1421 case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
1422 case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
1423 WLog_WARN(TAG,
"Connection aborted: credentials do not work [%s]",
1424 freerdp_get_last_error_name(err));
1426 case FREERDP_ERROR_CONNECT_CANCELLED:
1427 WLog_WARN(TAG,
"Connection aborted by user");
1437 if ((maxRetries > 0) && (numRetries >= maxRetries))
1439 WLog_DBG(TAG,
"AutoReconnect retries exceeded.");
1444 WLog_INFO(TAG,
"Attempting reconnect (%" PRIu32
" of %" PRIu32
")", numRetries, maxRetries);
1446 const SSIZE_T delay =
1447 IFCALLRESULT(5000, instance->RetryDialog, instance,
"connection", numRetries,
nullptr);
1452 if (freerdp_reconnect(instance))
1455 switch (freerdp_get_last_error(instance->context))
1457 case FREERDP_ERROR_CONNECT_CANCELLED:
1458 WLog_WARN(TAG,
"Autoreconnect aborted by user");
1463 for (SSIZE_T x = 0; x < delay / 10; x++)
1465 if (!IFCALLRESULT(TRUE, window_events, instance))
1467 WLog_ERR(TAG,
"window_events failed!");
1475 WLog_ERR(TAG,
"Maximum reconnect retries exceeded");
1479int freerdp_client_common_stop(rdpContext* context)
1481 rdpClientContext* cctx = (rdpClientContext*)context;
1484 freerdp_abort_connect_context(&cctx->context);
1488 (void)WaitForSingleObject(cctx->thread, INFINITE);
1489 (void)CloseHandle(cctx->thread);
1490 cctx->thread =
nullptr;
1496#if defined(CHANNEL_ENCOMSP_CLIENT)
1497BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1499 rdpClientContext* cctx =
nullptr;
1505 cctx = (rdpClientContext*)encomsp->custom;
1507 state = cctx->controlToggle;
1508 cctx->controlToggle = !cctx->controlToggle;
1509 return freerdp_client_encomsp_set_control(encomsp, state);
1512BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1519 pdu.ParticipantId = encomsp->participantId;
1520 pdu.Flags = ENCOMSP_REQUEST_VIEW;
1523 pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1525 const UINT rc = encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1526 return rc == CHANNEL_RC_OK;
1530client_encomsp_participant_created(EncomspClientContext* context,
1533 rdpClientContext* cctx =
nullptr;
1534 rdpSettings* settings =
nullptr;
1537 if (!context || !context->custom || !participantCreated)
1538 return ERROR_INVALID_PARAMETER;
1540 cctx = (rdpClientContext*)context->custom;
1543 settings = cctx->context.settings;
1544 WINPR_ASSERT(settings);
1546 if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1547 context->participantId = participantCreated->ParticipantId;
1550 if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1551 !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1553 if (!freerdp_client_encomsp_set_control(context, TRUE))
1554 return ERROR_INTERNAL_ERROR;
1560 return ERROR_INTERNAL_ERROR;
1563 return CHANNEL_RC_OK;
1566static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1568 cctx->encomsp = encomsp;
1569 encomsp->custom = (
void*)cctx;
1570 encomsp->ParticipantCreated = client_encomsp_participant_created;
1573static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1577 encomsp->custom =
nullptr;
1578 encomsp->ParticipantCreated =
nullptr;
1582 cctx->encomsp =
nullptr;
1586void freerdp_client_OnChannelConnectedEventHandler(
void* context,
1587 const ChannelConnectedEventArgs* e)
1589 rdpClientContext* cctx = (rdpClientContext*)context;
1597#if defined(CHANNEL_AINPUT_CLIENT)
1598 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1599 cctx->ainput = (AInputClientContext*)e->pInterface;
1601#if defined(CHANNEL_RDPEI_CLIENT)
1602 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1604 cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1607#if defined(CHANNEL_RDPGFX_CLIENT)
1608 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1610 gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1613#if defined(CHANNEL_GEOMETRY_CLIENT)
1614 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1616 gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1619#if defined(CHANNEL_VIDEO_CLIENT)
1620 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1622 gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1624 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1626 gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1629#if defined(CHANNEL_ENCOMSP_CLIENT)
1630 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1632 client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1637void freerdp_client_OnChannelDisconnectedEventHandler(
void* context,
1638 const ChannelDisconnectedEventArgs* e)
1640 rdpClientContext* cctx = (rdpClientContext*)context;
1648#if defined(CHANNEL_AINPUT_CLIENT)
1649 else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1650 cctx->ainput =
nullptr;
1652#if defined(CHANNEL_RDPEI_CLIENT)
1653 else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1655 cctx->rdpei =
nullptr;
1658#if defined(CHANNEL_RDPGFX_CLIENT)
1659 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1661 gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1664#if defined(CHANNEL_GEOMETRY_CLIENT)
1665 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1667 gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1670#if defined(CHANNEL_VIDEO_CLIENT)
1671 else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1673 gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1675 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1677 gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1680#if defined(CHANNEL_ENCOMSP_CLIENT)
1681 else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1683 client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1688BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1690 BOOL handled = FALSE;
1694 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1695 if (state != CONNECTION_STATE_ACTIVE)
1698#if defined(CHANNEL_AINPUT_CLIENT)
1705 INT32 value = mflags & 0xFF;
1707 if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1708 value = -1 * (0x100 - value);
1714 if (mflags & PTR_FLAGS_WHEEL)
1716 flags |= AINPUT_FLAGS_WHEEL;
1720 if (mflags & PTR_FLAGS_HWHEEL)
1722 flags |= AINPUT_FLAGS_WHEEL;
1726 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1727 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1728 if (rc == CHANNEL_RC_OK)
1734 return freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1739#if defined(CHANNEL_AINPUT_CLIENT)
1740static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1745 WINPR_ASSERT(cctx->ainput);
1746 WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1748 rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1750 return rc == CHANNEL_RC_OK;
1754static bool button_pressed(
const rdpClientContext* cctx)
1757 for (
size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1759 const BOOL cur = cctx->pressed_buttons[x];
1766BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1769 BOOL handled = FALSE;
1772 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1773 if (state != CONNECTION_STATE_ACTIVE)
1776 if (mflags & PTR_FLAGS_BUTTON1)
1777 cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1778 if (mflags & PTR_FLAGS_BUTTON2)
1779 cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1780 if (mflags & PTR_FLAGS_BUTTON3)
1781 cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1783 if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1786 if (!button_pressed(cctx))
1790 const BOOL haveRelative =
1792 if (relative && haveRelative)
1794 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1795 WINPR_ASSERTING_INT_CAST(int16_t, x),
1796 WINPR_ASSERTING_INT_CAST(int16_t, y));
1799#if defined(CHANNEL_AINPUT_CLIENT)
1804 if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1805 flags |= AINPUT_FLAGS_HAVE_REL;
1808 flags |= AINPUT_FLAGS_REL;
1810 if (mflags & PTR_FLAGS_DOWN)
1811 flags |= AINPUT_FLAGS_DOWN;
1812 if (mflags & PTR_FLAGS_BUTTON1)
1813 flags |= AINPUT_FLAGS_BUTTON1;
1814 if (mflags & PTR_FLAGS_BUTTON2)
1815 flags |= AINPUT_FLAGS_BUTTON2;
1816 if (mflags & PTR_FLAGS_BUTTON3)
1817 flags |= AINPUT_FLAGS_BUTTON3;
1818 if (mflags & PTR_FLAGS_MOVE)
1819 flags |= AINPUT_FLAGS_MOVE;
1820 handled = ainput_send_diff_event(cctx, flags, x, y);
1830 WLog_WARN(TAG,
"Relative mouse input channel not available, sending absolute!");
1837 return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1838 (UINT16)cctx->lastY);
1843BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1846 BOOL handled = FALSE;
1849 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1850 if (state != CONNECTION_STATE_ACTIVE)
1853 if (mflags & PTR_XFLAGS_BUTTON1)
1854 cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1855 if (mflags & PTR_XFLAGS_BUTTON2)
1856 cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1858 const BOOL haveRelative =
1860 if (relative && haveRelative)
1862 return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1863 WINPR_ASSERTING_INT_CAST(int16_t, x),
1864 WINPR_ASSERTING_INT_CAST(int16_t, y));
1867#if defined(CHANNEL_AINPUT_CLIENT)
1873 flags |= AINPUT_FLAGS_REL;
1874 if (mflags & PTR_XFLAGS_DOWN)
1875 flags |= AINPUT_FLAGS_DOWN;
1876 if (mflags & PTR_XFLAGS_BUTTON1)
1877 flags |= AINPUT_XFLAGS_BUTTON1;
1878 if (mflags & PTR_XFLAGS_BUTTON2)
1879 flags |= AINPUT_XFLAGS_BUTTON2;
1881 handled = ainput_send_diff_event(cctx, flags, x, y);
1891 WLog_WARN(TAG,
"Relative mouse input channel not available, sending absolute!");
1898 freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1899 (UINT16)cctx->lastY);
1905static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1908 const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1909 const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1910 WINPR_ASSERT(contact);
1911 WINPR_ASSERT(contact->x <= UINT16_MAX);
1912 WINPR_ASSERT(contact->y <= UINT16_MAX);
1914 switch (contact->count)
1917 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1918 contact->x, contact->y);
1920 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1921 contact->x, contact->y);
1923 return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1924 contact->x, contact->y);
1926 return freerdp_client_send_extended_button_event(
1927 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1929 return freerdp_client_send_extended_button_event(
1930 cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1937static BOOL freerdp_handle_touch_up(rdpClientContext* cctx,
const FreeRDP_TouchContact* contact)
1940 WINPR_ASSERT(contact);
1942#if defined(CHANNEL_RDPEI_CLIENT)
1943 RdpeiClientContext* rdpei = cctx->rdpei;
1946 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1950 if (rdpei->TouchRawEvent)
1952 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1953 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1954 ? CONTACT_DATA_PRESSURE_PRESENT
1958 rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1959 RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1960 RDPINPUT_CONTACT_FLAG_INCONTACT,
1961 contactFlags, contact->pressure);
1962 if (rc1 != CHANNEL_RC_OK)
1965 const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
1966 &contactId, flags, contactFlags, contact->pressure);
1967 if (rc2 != CHANNEL_RC_OK)
1972 WINPR_ASSERT(rdpei->TouchEnd);
1973 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1974 if (rc != CHANNEL_RC_OK)
1979 WLog_WARN(TAG,
"Touch event detected but RDPEI support not compiled in. Recompile with "
1980 "-DCHANNEL_RDPEI_CLIENT=ON");
1981 return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1985static BOOL freerdp_handle_touch_down(rdpClientContext* cctx,
const FreeRDP_TouchContact* contact)
1988 WINPR_ASSERT(contact);
1990#if defined(CHANNEL_RDPEI_CLIENT)
1991 RdpeiClientContext* rdpei = cctx->rdpei;
1995 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1999 if (rdpei->TouchRawEvent)
2001 const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
2002 RDPINPUT_CONTACT_FLAG_INCONTACT;
2003 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2004 ? CONTACT_DATA_PRESSURE_PRESENT
2006 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2007 flags, contactFlags, contact->pressure);
2008 if (rc != CHANNEL_RC_OK)
2013 WINPR_ASSERT(rdpei->TouchBegin);
2014 const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
2015 if (rc != CHANNEL_RC_OK)
2021 WLog_WARN(TAG,
"Touch event detected but RDPEI support not compiled in. Recompile with "
2022 "-DCHANNEL_RDPEI_CLIENT=ON");
2023 return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
2027static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
2030 const UINT16 flags = PTR_FLAGS_MOVE;
2032 WINPR_ASSERT(contact);
2033 WINPR_ASSERT(contact->x <= UINT16_MAX);
2034 WINPR_ASSERT(contact->y <= UINT16_MAX);
2035 return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
2038static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx,
const FreeRDP_TouchContact* contact)
2041 WINPR_ASSERT(contact);
2043#if defined(CHANNEL_RDPEI_CLIENT)
2044 RdpeiClientContext* rdpei = cctx->rdpei;
2047 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2051 if (rdpei->TouchRawEvent)
2053 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
2054 RDPINPUT_CONTACT_FLAG_INCONTACT;
2055 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2056 ? CONTACT_DATA_PRESSURE_PRESENT
2058 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2059 flags, contactFlags, contact->pressure);
2060 if (rc != CHANNEL_RC_OK)
2065 WINPR_ASSERT(rdpei->TouchUpdate);
2066 const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2067 if (rc != CHANNEL_RC_OK)
2073 WLog_WARN(TAG,
"Touch event detected but RDPEI support not compiled in. Recompile with "
2074 "-DCHANNEL_RDPEI_CLIENT=ON");
2075 return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2079static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx,
const FreeRDP_TouchContact* contact)
2082 WINPR_ASSERT(contact);
2084#if defined(CHANNEL_RDPEI_CLIENT)
2085 RdpeiClientContext* rdpei = cctx->rdpei;
2088 return freerdp_handle_touch_to_mouse(cctx,
false, contact);
2092 if (rdpei->TouchRawEvent)
2094 const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2095 const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2096 ? CONTACT_DATA_PRESSURE_PRESENT
2098 const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2099 flags, contactFlags, contact->pressure);
2100 if (rc != CHANNEL_RC_OK)
2105 WINPR_ASSERT(rdpei->TouchUpdate);
2106 const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2107 if (rc != CHANNEL_RC_OK)
2113 WLog_WARN(TAG,
"Touch event detected but RDPEI support not compiled in. Recompile with "
2114 "-DCHANNEL_RDPEI_CLIENT=ON");
2115 return freerdp_handle_touch_to_mouse(cctx,
false, contact);
2119static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2120 UINT32 pressure, INT32 x, INT32 y,
2124 WINPR_ASSERT(pcontact);
2126 for (
size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2130 const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2131 if (newcontact || (contact->id == touchId))
2133 contact->id = touchId;
2134 contact->flags = flags;
2135 contact->pressure = pressure;
2139 *pcontact = *contact;
2141 const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2154BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2155 UINT32 pressure, INT32 x, INT32 y)
2158 FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2161 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2162 if (state != CONNECTION_STATE_ACTIVE)
2167 if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2170 switch (flags & mask)
2172 case FREERDP_TOUCH_DOWN:
2173 return freerdp_handle_touch_down(cctx, &contact);
2174 case FREERDP_TOUCH_UP:
2175 return freerdp_handle_touch_up(cctx, &contact);
2176 case FREERDP_TOUCH_MOTION:
2177 return freerdp_handle_touch_motion(cctx, &contact);
2178 case FREERDP_TOUCH_CANCEL:
2179 return freerdp_handle_touch_cancel(cctx, &contact);
2181 WLog_WARN(TAG,
"Unhandled FreeRDPTouchEventType %" PRIu32
", ignoring", flags);
2186BOOL freerdp_client_load_channels(freerdp* instance)
2188 WINPR_ASSERT(instance);
2189 WINPR_ASSERT(instance->context);
2191 if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2193 WLog_ERR(TAG,
"Failed to load addins [%08" PRIx32
"]", GetLastError());
2199int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2201 const char* str_data = freerdp_get_logon_error_info_data(data);
2202 const char* str_type = freerdp_get_logon_error_info_type(type);
2204 if (!instance || !instance->context)
2207 WLog_INFO(TAG,
"Logon Error Info %s [%s]", str_data, str_type);
2211static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2216 for (
size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2219 if (deviceid == pen->deviceid)
2229static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2232 static const INT32 null_deviceid = 0;
2235 WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2236 if (freerdp_client_is_pen(cctx, deviceid))
2238 WLog_WARN(TAG,
"trying to double register pen device %" PRId32, deviceid);
2249 pen->deviceid = deviceid;
2250 pen->max_pressure = pressure;
2253 WLog_DBG(TAG,
"registered pen at index %" PRIuz, pos);
2257 WLog_WARN(TAG,
"No free slot for an additional pen device, skipping");
2261BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2263 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2264 if (state != CONNECTION_STATE_ACTIVE)
2267#if defined(CHANNEL_RDPEI_CLIENT)
2268 if ((flags & FREERDP_PEN_REGISTER) != 0)
2270 va_list args = WINPR_C_ARRAY_INIT;
2272 va_start(args, deviceid);
2273 double pressure = va_arg(args,
double);
2275 return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2281 WLog_WARN(TAG,
"unregistered pen device %" PRId32
" event 0x%08" PRIx32, deviceid, flags);
2285 UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2287 ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2289 RdpeiClientContext* rdpei = cctx->rdpei;
2290 WINPR_ASSERT(rdpei);
2292 UINT32 normalizedpressure = 1024;
2295 UINT16 rotation = 0;
2298 va_list args = WINPR_C_ARRAY_INIT;
2299 va_start(args, deviceid);
2301 x = va_arg(args, INT32);
2302 y = va_arg(args, INT32);
2303 if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2305 const double pressure = va_arg(args,
double);
2306 const double np = (pressure * 1024.0) / pen->max_pressure;
2307 normalizedpressure = (UINT32)lround(np);
2308 WLog_DBG(TAG,
"pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2309 fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2311 if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2313 const unsigned arg = va_arg(args,
unsigned);
2314 rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2315 fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2317 if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2319 const int arg = va_arg(args,
int);
2320 tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2321 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2323 if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2325 const int arg = va_arg(args,
int);
2326 tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2327 fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2331 if ((flags & FREERDP_PEN_PRESS) != 0)
2335 flags = FREERDP_PEN_MOTION |
2336 (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2337 else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2338 pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2340 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2342 if (!pen->pressed ||
2343 ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2344 flags = FREERDP_PEN_MOTION |
2345 (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2347 pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2350 flags |= pen->flags;
2351 if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2352 penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2353 if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2354 penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2358 if ((flags & FREERDP_PEN_PRESS) != 0)
2360 WLog_DBG(TAG,
"Pen press %" PRId32, deviceid);
2361 pen->hovering = FALSE;
2362 pen->pressed = TRUE;
2364 WINPR_ASSERT(rdpei->PenBegin);
2365 const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2366 normalizedpressure, rotation, tiltX, tiltY);
2367 return rc == CHANNEL_RC_OK;
2369 else if ((flags & FREERDP_PEN_MOTION) != 0)
2371 UINT rc = ERROR_INTERNAL_ERROR;
2374 WLog_DBG(TAG,
"Pen update %" PRId32, deviceid);
2377 WINPR_ASSERT(rdpei->PenUpdate);
2378 rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2379 rotation, tiltX, tiltY);
2381 else if (pen->hovering)
2383 WLog_DBG(TAG,
"Pen hover update %" PRId32, deviceid);
2385 WINPR_ASSERT(rdpei->PenHoverUpdate);
2386 rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2387 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2391 WLog_DBG(TAG,
"Pen hover begin %" PRId32, deviceid);
2392 pen->hovering = TRUE;
2394 WINPR_ASSERT(rdpei->PenHoverBegin);
2395 rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2396 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2398 return rc == CHANNEL_RC_OK;
2400 else if ((flags & FREERDP_PEN_RELEASE) != 0)
2402 WLog_DBG(TAG,
"Pen release %" PRId32, deviceid);
2403 pen->pressed = FALSE;
2404 pen->hovering = TRUE;
2406 WINPR_ASSERT(rdpei->PenUpdate);
2407 const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2408 normalizedpressure, rotation, tiltX, tiltY);
2409 if (rc != CHANNEL_RC_OK)
2411 WINPR_ASSERT(rdpei->PenEnd);
2412 const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2413 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2414 return re == CHANNEL_RC_OK;
2417 WLog_WARN(TAG,
"Invalid pen %" PRId32
" flags 0x%08" PRIx32, deviceid, flags);
2419 WLog_WARN(TAG,
"Pen event detected but RDPEI support not compiled in. Recompile with "
2420 "-DCHANNEL_RDPEI_CLIENT=ON");
2426BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2430 const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2431 if (state != CONNECTION_STATE_ACTIVE)
2434#if defined(CHANNEL_RDPEI_CLIENT)
2435 RdpeiClientContext* rdpei = cctx->rdpei;
2440 for (
size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2445 WLog_DBG(TAG,
"unhover pen %" PRId32, pen->deviceid);
2446 pen->hovering = FALSE;
2448 rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2449 if (rc != CHANNEL_RC_OK)
2455 WLog_WARN(TAG,
"Pen event detected but RDPEI support not compiled in. Recompile with "
2456 "-DCHANNEL_RDPEI_CLIENT=ON");
2461BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2468 for (
size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2471 if (pen->deviceid == deviceid)
2478BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* cctx)
2482 const rdpSettings* settings = cctx->context.settings;
2485 BOOL ainput = FALSE;
2486#if defined(CHANNEL_AINPUT_CLIENT)
2487 ainput = cctx->ainput !=
nullptr;
2490 return useRelative && (haveRelative || ainput);
2493#if defined(WITH_AAD)
2494WINPR_ATTR_MALLOC(free, 1)
2495static
char* get_redirect_uri(const rdpSettings* settings)
2497 char* redirect_uri =
nullptr;
2501 const char* redirect_fmt =
2504 const char* tenantid =
"common";
2508 if (tenantid && redirect_fmt)
2513 size_t redirect_len = 0;
2514 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2520 const char* redirect_fmt =
2523 size_t redirect_len = 0;
2524 winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2526 return redirect_uri;
2529static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2531 const rdpSettings* settings = cctx->context.settings;
2533 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2534 AAD_WELLKNOWN_authorization_endpoint);
2537 if (!client_id || !ep || !scope)
2540 char* redirect_uri = get_redirect_uri(settings);
2544 char* url =
nullptr;
2546 winpr_asprintf(&url, &urllen,
"%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2547 client_id, scope, redirect_uri);
2552static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2554 const rdpSettings* settings = cctx->context.settings;
2556 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2557 AAD_WELLKNOWN_authorization_endpoint);
2560 if (!client_id || !ep || !scope)
2563 char* redirect_uri = get_redirect_uri(settings);
2567 char* url =
nullptr;
2570 const char* code = va_arg(ap,
const char*);
2571 winpr_asprintf(&url, &urllen,
2572 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2573 code, client_id, scope, redirect_uri);
2578static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2580 const rdpSettings* settings = cctx->context.settings;
2581 char* url =
nullptr;
2583 char* redirect_uri = get_redirect_uri(settings);
2586 if (!client_id || !redirect_uri)
2590 const char* scope = va_arg(ap,
const char*);
2595 const char* ep = freerdp_utils_aad_get_wellknown_string(
2596 &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2597 winpr_asprintf(&url, &urllen,
2598 "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2599 client_id, scope, redirect_uri);
2608static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2610 const rdpSettings* settings = cctx->context.settings;
2612 const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2613 AAD_WELLKNOWN_authorization_endpoint);
2614 const char* scope = va_arg(ap,
const char*);
2615 const char* code = va_arg(ap,
const char*);
2616 const char* req_cnf = va_arg(ap,
const char*);
2618 if (!client_id || !ep || !scope || !code || !req_cnf)
2621 char* redirect_uri = get_redirect_uri(settings);
2625 char* url =
nullptr;
2630 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2631 code, client_id, scope, redirect_uri, req_cnf);
2637char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2640 char* str =
nullptr;
2642 va_list ap = WINPR_C_ARRAY_INIT;
2646#if defined(WITH_AAD)
2647 case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2648 str = aad_auth_request(cctx, ap);
2650 case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2651 str = aad_token_request(cctx, ap);
2653 case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2654 str = avd_auth_request(cctx, ap);
2656 case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2657 str = avd_token_request(cctx, ap);
2667BOOL client_common_save_session_info(WINPR_ATTR_UNUSED rdpContext* context, UINT32 type,
2670 char buffer[128] = WINPR_C_ARRAY_INIT;
2671 WLog_INFO(TAG,
"%s [%s]", freerdp_session_logon_type_str(type),
2672 freerdp_session_logon_type_data_str(type, data, buffer,
sizeof(buffer)));
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.