Coverage Report

Created: 2026-05-30 06:46

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