Coverage Report

Created: 2025-12-10 06:43

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