Coverage Report

Created: 2025-10-10 06:50

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