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