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