Coverage Report

Created: 2026-01-13 06:16

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.48k
{
81
9.48k
  WINPR_ASSERT(instance);
82
9.48k
  instance->AuthenticateEx = client_cli_authenticate_ex;
83
9.48k
  instance->ChooseSmartcard = client_cli_choose_smartcard;
84
9.48k
  instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
85
9.48k
  instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
86
9.48k
  instance->PresentGatewayMessage = client_cli_present_gateway_message;
87
9.48k
  instance->LogonErrorInfo = client_cli_logon_error_info;
88
9.48k
  instance->GetAccessToken = client_cli_get_access_token;
89
9.48k
  instance->RetryDialog = client_common_retry_dialog;
90
9.48k
}
91
92
static BOOL freerdp_client_common_new(freerdp* instance, rdpContext* context)
93
9.48k
{
94
9.48k
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
95
96
9.48k
  WINPR_ASSERT(instance);
97
9.48k
  WINPR_ASSERT(context);
98
99
9.48k
  instance->LoadChannels = freerdp_client_load_channels;
100
9.48k
  set_default_callbacks(instance);
101
102
9.48k
  pEntryPoints = instance->pClientEntryPoints;
103
9.48k
  WINPR_ASSERT(pEntryPoints);
104
9.48k
  return IFCALLRESULT(TRUE, pEntryPoints->ClientNew, instance, context);
105
9.48k
}
106
107
static void freerdp_client_common_free(freerdp* instance, rdpContext* context)
108
9.48k
{
109
9.48k
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
110
111
9.48k
  WINPR_ASSERT(instance);
112
9.48k
  WINPR_ASSERT(context);
113
114
9.48k
  pEntryPoints = instance->pClientEntryPoints;
115
9.48k
  WINPR_ASSERT(pEntryPoints);
116
9.48k
  IFCALL(pEntryPoints->ClientFree, instance, context);
117
9.48k
}
118
119
/* Common API */
120
121
rdpContext* freerdp_client_context_new(const RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
122
9.48k
{
123
9.48k
  freerdp* instance = NULL;
124
9.48k
  rdpContext* context = NULL;
125
126
9.48k
  if (!pEntryPoints)
127
0
    return NULL;
128
129
9.48k
  IFCALL(pEntryPoints->GlobalInit);
130
9.48k
  instance = freerdp_new();
131
132
9.48k
  if (!instance)
133
0
    return NULL;
134
135
9.48k
  instance->ContextSize = pEntryPoints->ContextSize;
136
9.48k
  instance->ContextNew = freerdp_client_common_new;
137
9.48k
  instance->ContextFree = freerdp_client_common_free;
138
9.48k
  instance->pClientEntryPoints = (RDP_CLIENT_ENTRY_POINTS*)malloc(pEntryPoints->Size);
139
140
9.48k
  if (!instance->pClientEntryPoints)
141
0
    goto out_fail;
142
143
9.48k
  CopyMemory(instance->pClientEntryPoints, pEntryPoints, pEntryPoints->Size);
144
145
9.48k
  if (!freerdp_context_new_ex(instance, pEntryPoints->settings))
146
0
    goto out_fail2;
147
148
9.48k
  context = instance->context;
149
9.48k
  context->instance = instance;
150
151
9.48k
#if defined(WITH_CLIENT_CHANNELS)
152
9.48k
  if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
153
9.48k
      CHANNEL_RC_OK)
154
0
    goto out_fail2;
155
9.48k
#endif
156
157
9.48k
  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.48k
{
167
9.48k
  freerdp* instance = NULL;
168
169
9.48k
  if (!context)
170
0
    return;
171
172
9.48k
  instance = context->instance;
173
174
9.48k
  if (instance)
175
9.48k
  {
176
9.48k
    RDP_CLIENT_ENTRY_POINTS* pEntryPoints = instance->pClientEntryPoints;
177
9.48k
    freerdp_context_free(instance);
178
179
9.48k
    if (pEntryPoints)
180
9.48k
      IFCALL(pEntryPoints->GlobalUninit);
181
182
9.48k
    free(instance->pClientEntryPoints);
183
9.48k
    freerdp_free(instance);
184
9.48k
  }
185
9.48k
}
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
  {
1102
    const char* code = extract_authorization_code(url);
1103
    if (!code)
1104
      goto cleanup;
1105
    token_request =
1106
        freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1107
                                   FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1108
  }
1109
  if (!token_request)
1110
    goto cleanup;
1111
1112
  rc = client_common_get_access_token(instance, token_request, token);
1113
1114
cleanup:
1115
  free(token_request);
1116
  free(url);
1117
  return rc && (*token != NULL);
1118
}
1119
1120
static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1121
{
1122
  WINPR_ASSERT(instance);
1123
  WINPR_ASSERT(instance->context);
1124
1125
  size_t size = 0;
1126
  char* url = NULL;
1127
  char* token_request = NULL;
1128
1129
  WINPR_ASSERT(token);
1130
1131
  BOOL rc = FALSE;
1132
1133
  *token = NULL;
1134
1135
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1136
                                             FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1137
  if (!request)
1138
    return FALSE;
1139
  printf("Browse to: %s\n", request);
1140
  free(request);
1141
  printf("Paste redirect URL here: \n");
1142
1143
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1144
    goto cleanup;
1145
1146
  {
1147
    const char* code = extract_authorization_code(url);
1148
    if (!code)
1149
      goto cleanup;
1150
    token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1151
                                               FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1152
  }
1153
1154
  if (!token_request)
1155
    goto cleanup;
1156
1157
  rc = client_common_get_access_token(instance, token_request, token);
1158
1159
cleanup:
1160
  free(token_request);
1161
  free(url);
1162
  return rc && (*token != NULL);
1163
}
1164
#endif
1165
1166
BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1167
                                 size_t count, ...)
1168
0
{
1169
0
  WINPR_ASSERT(instance);
1170
0
  WINPR_ASSERT(token);
1171
1172
0
#if !defined(WITH_AAD)
1173
0
  WLog_ERR(TAG, "Build does not support AAD authentication");
1174
0
  return FALSE;
1175
#else
1176
  BOOL rc = FALSE;
1177
  WINPR_ASSERT(instance->context);
1178
  const BOOL saved =
1179
      freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1180
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1181
                                 TRUE))
1182
    return FALSE;
1183
1184
  switch (tokenType)
1185
  {
1186
    case ACCESS_TOKEN_TYPE_AAD:
1187
    {
1188
      if (count < 2)
1189
      {
1190
        WLog_ERR(TAG,
1191
                 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1192
                 ", aborting",
1193
                 count);
1194
        return FALSE;
1195
      }
1196
      else if (count > 2)
1197
        WLog_WARN(TAG,
1198
                  "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1199
                  ", ignoring",
1200
                  count);
1201
      va_list ap = { 0 };
1202
      va_start(ap, count);
1203
      const char* scope = va_arg(ap, const char*);
1204
      const char* req_cnf = va_arg(ap, const char*);
1205
      rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1206
      va_end(ap);
1207
    }
1208
    break;
1209
    case ACCESS_TOKEN_TYPE_AVD:
1210
      if (count != 0)
1211
        WLog_WARN(TAG,
1212
                  "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1213
                  ", ignoring",
1214
                  count);
1215
      rc = client_cli_get_avd_access_token(instance, token);
1216
      break;
1217
    default:
1218
      WLog_ERR(TAG, "Unexpected value for AccessTokenType [%" PRIuz "], aborting", tokenType);
1219
      break;
1220
  }
1221
1222
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1223
                                 saved))
1224
    return FALSE;
1225
  return rc;
1226
#endif
1227
0
}
1228
1229
BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1230
0
{
1231
#ifdef WITH_AAD
1232
  WINPR_ASSERT(request);
1233
  WINPR_ASSERT(token);
1234
1235
  BOOL ret = FALSE;
1236
  long resp_code = 0;
1237
  BYTE* response = NULL;
1238
  size_t response_length = 0;
1239
1240
  wLog* log = WLog_Get(TAG);
1241
1242
  const char* token_ep =
1243
      freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1244
  if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1245
  {
1246
    WLog_ERR(TAG, "access token request failed");
1247
    return FALSE;
1248
  }
1249
1250
  if (resp_code != HTTP_STATUS_OK)
1251
  {
1252
    char buffer[64] = { 0 };
1253
1254
    WLog_Print(log, WLOG_ERROR,
1255
               "Server unwilling to provide access token; returned status code %s",
1256
               freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1257
    if (response_length > 0)
1258
      WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1259
    goto cleanup;
1260
  }
1261
1262
  *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1263
  if (*token)
1264
    ret = TRUE;
1265
1266
cleanup:
1267
  free(response);
1268
  return ret;
1269
#else
1270
0
  return FALSE;
1271
0
#endif
1272
0
}
1273
1274
SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1275
                                   void* userarg)
1276
0
{
1277
0
  WINPR_UNUSED(instance);
1278
0
  WINPR_ASSERT(instance->context);
1279
0
  WINPR_UNUSED(userarg);
1280
0
  WINPR_ASSERT(instance);
1281
0
  WINPR_ASSERT(what);
1282
1283
0
  if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1284
0
  {
1285
0
    WLog_ERR(TAG, "Unknown module %s, aborting", what);
1286
0
    return -1;
1287
0
  }
1288
1289
0
  if (current == 0)
1290
0
  {
1291
0
    if (strcmp(what, "arm-transport") == 0)
1292
0
      WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1293
0
  }
1294
1295
0
  const rdpSettings* settings = instance->context->settings;
1296
0
  const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1297
0
  if (!enabled)
1298
0
  {
1299
0
    WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1300
0
    return -1;
1301
0
  }
1302
1303
0
  const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1304
0
  const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1305
0
  if (current >= max)
1306
0
  {
1307
0
    WLog_ERR(TAG,
1308
0
             "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1309
0
             "tech support for help if this keeps happening.",
1310
0
             what);
1311
0
    return -1;
1312
0
  }
1313
1314
0
  WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1315
0
            what, current + 1, max, delay);
1316
0
  return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1317
0
}
1318
1319
BOOL client_auto_reconnect(freerdp* instance)
1320
0
{
1321
0
  return client_auto_reconnect_ex(instance, NULL);
1322
0
}
1323
1324
BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1325
0
{
1326
0
  BOOL retry = TRUE;
1327
0
  UINT32 error = 0;
1328
0
  UINT32 numRetries = 0;
1329
0
  rdpSettings* settings = NULL;
1330
1331
0
  if (!instance)
1332
0
    return FALSE;
1333
1334
0
  WINPR_ASSERT(instance->context);
1335
1336
0
  settings = instance->context->settings;
1337
0
  WINPR_ASSERT(settings);
1338
1339
0
  const UINT32 maxRetries =
1340
0
      freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1341
1342
  /* Only auto reconnect on network disconnects. */
1343
0
  error = freerdp_error_info(instance);
1344
0
  switch (error)
1345
0
  {
1346
0
    case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1347
      /* A network disconnect was detected */
1348
0
      WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1349
0
                freerdp_get_error_info_string(error));
1350
0
      break;
1351
0
    case ERRINFO_SUCCESS:
1352
      /* A network disconnect was detected */
1353
0
      WLog_INFO(TAG, "Network disconnect!");
1354
0
      break;
1355
0
    default:
1356
0
      WLog_DBG(TAG, "Other error: %s", freerdp_get_error_info_string(error));
1357
0
      return FALSE;
1358
0
  }
1359
1360
0
  if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1361
0
  {
1362
    /* No auto-reconnect - just quit */
1363
0
    WLog_DBG(TAG, "AutoReconnect not enabled, quitting.");
1364
0
    return FALSE;
1365
0
  }
1366
1367
0
  switch (freerdp_get_last_error(instance->context))
1368
0
  {
1369
0
    case FREERDP_ERROR_CONNECT_CANCELLED:
1370
0
      WLog_WARN(TAG, "Connection aborted by user");
1371
0
      return FALSE;
1372
0
    default:
1373
0
      break;
1374
0
  }
1375
1376
  /* Perform an auto-reconnect. */
1377
0
  while (retry)
1378
0
  {
1379
    /* Quit retrying if max retries has been exceeded */
1380
0
    if ((maxRetries > 0) && (numRetries >= maxRetries))
1381
0
    {
1382
0
      WLog_DBG(TAG, "AutoReconnect retries exceeded.");
1383
0
      return FALSE;
1384
0
    }
1385
1386
    /* Attempt the next reconnect */
1387
0
    WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1388
1389
0
    const SSIZE_T delay =
1390
0
        IFCALLRESULT(5000, instance->RetryDialog, instance, "connection", numRetries, NULL);
1391
0
    if (delay < 0)
1392
0
      return FALSE;
1393
0
    numRetries++;
1394
1395
0
    if (freerdp_reconnect(instance))
1396
0
      return TRUE;
1397
1398
0
    switch (freerdp_get_last_error(instance->context))
1399
0
    {
1400
0
      case FREERDP_ERROR_CONNECT_CANCELLED:
1401
0
        WLog_WARN(TAG, "Autoreconnect aborted by user");
1402
0
        return FALSE;
1403
0
      default:
1404
0
        break;
1405
0
    }
1406
0
    for (SSIZE_T x = 0; x < delay / 10; x++)
1407
0
    {
1408
0
      if (!IFCALLRESULT(TRUE, window_events, instance))
1409
0
      {
1410
0
        WLog_ERR(TAG, "window_events failed!");
1411
0
        return FALSE;
1412
0
      }
1413
1414
0
      Sleep(10);
1415
0
    }
1416
0
  }
1417
1418
0
  WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1419
0
  return FALSE;
1420
0
}
1421
1422
int freerdp_client_common_stop(rdpContext* context)
1423
0
{
1424
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1425
0
  WINPR_ASSERT(cctx);
1426
1427
0
  freerdp_abort_connect_context(&cctx->context);
1428
1429
0
  if (cctx->thread)
1430
0
  {
1431
0
    (void)WaitForSingleObject(cctx->thread, INFINITE);
1432
0
    (void)CloseHandle(cctx->thread);
1433
0
    cctx->thread = NULL;
1434
0
  }
1435
1436
0
  return 0;
1437
0
}
1438
1439
#if defined(CHANNEL_ENCOMSP_CLIENT)
1440
BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1441
0
{
1442
0
  rdpClientContext* cctx = NULL;
1443
0
  BOOL state = 0;
1444
1445
0
  if (!encomsp)
1446
0
    return FALSE;
1447
1448
0
  cctx = (rdpClientContext*)encomsp->custom;
1449
1450
0
  state = cctx->controlToggle;
1451
0
  cctx->controlToggle = !cctx->controlToggle;
1452
0
  return freerdp_client_encomsp_set_control(encomsp, state);
1453
0
}
1454
1455
BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1456
0
{
1457
0
  ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = { 0 };
1458
1459
0
  if (!encomsp)
1460
0
    return FALSE;
1461
1462
0
  pdu.ParticipantId = encomsp->participantId;
1463
0
  pdu.Flags = ENCOMSP_REQUEST_VIEW;
1464
1465
0
  if (control)
1466
0
    pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1467
1468
0
  encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1469
1470
0
  return TRUE;
1471
0
}
1472
1473
static UINT
1474
client_encomsp_participant_created(EncomspClientContext* context,
1475
                                   const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1476
0
{
1477
0
  rdpClientContext* cctx = NULL;
1478
0
  rdpSettings* settings = NULL;
1479
0
  BOOL request = 0;
1480
1481
0
  if (!context || !context->custom || !participantCreated)
1482
0
    return ERROR_INVALID_PARAMETER;
1483
1484
0
  cctx = (rdpClientContext*)context->custom;
1485
0
  WINPR_ASSERT(cctx);
1486
1487
0
  settings = cctx->context.settings;
1488
0
  WINPR_ASSERT(settings);
1489
1490
0
  if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1491
0
    context->participantId = participantCreated->ParticipantId;
1492
1493
0
  request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1494
0
  if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1495
0
      !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1496
0
  {
1497
0
    if (!freerdp_client_encomsp_set_control(context, TRUE))
1498
0
      return ERROR_INTERNAL_ERROR;
1499
1500
    /* if auto-request-control setting is enabled then only request control once upon connect,
1501
     * otherwise it will auto request control again every time server turns off control which
1502
     * is a bit annoying */
1503
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1504
0
      return ERROR_INTERNAL_ERROR;
1505
0
  }
1506
1507
0
  return CHANNEL_RC_OK;
1508
0
}
1509
1510
static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1511
0
{
1512
0
  cctx->encomsp = encomsp;
1513
0
  encomsp->custom = (void*)cctx;
1514
0
  encomsp->ParticipantCreated = client_encomsp_participant_created;
1515
0
}
1516
1517
static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1518
0
{
1519
0
  if (encomsp)
1520
0
  {
1521
0
    encomsp->custom = NULL;
1522
0
    encomsp->ParticipantCreated = NULL;
1523
0
  }
1524
1525
0
  if (cctx)
1526
0
    cctx->encomsp = NULL;
1527
0
}
1528
#endif
1529
1530
void freerdp_client_OnChannelConnectedEventHandler(void* context,
1531
                                                   const ChannelConnectedEventArgs* e)
1532
0
{
1533
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1534
1535
0
  WINPR_ASSERT(cctx);
1536
0
  WINPR_ASSERT(e);
1537
1538
0
  if (0)
1539
0
  {
1540
0
  }
1541
0
#if defined(CHANNEL_AINPUT_CLIENT)
1542
0
  else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1543
0
    cctx->ainput = (AInputClientContext*)e->pInterface;
1544
0
#endif
1545
0
#if defined(CHANNEL_RDPEI_CLIENT)
1546
0
  else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1547
0
  {
1548
0
    cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1549
0
  }
1550
0
#endif
1551
0
#if defined(CHANNEL_RDPGFX_CLIENT)
1552
0
  else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1553
0
  {
1554
0
    gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1555
0
  }
1556
0
#endif
1557
0
#if defined(CHANNEL_GEOMETRY_CLIENT)
1558
0
  else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1559
0
  {
1560
0
    gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1561
0
  }
1562
0
#endif
1563
0
#if defined(CHANNEL_VIDEO_CLIENT)
1564
0
  else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1565
0
  {
1566
0
    gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1567
0
  }
1568
0
  else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1569
0
  {
1570
0
    gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1571
0
  }
1572
0
#endif
1573
0
#if defined(CHANNEL_ENCOMSP_CLIENT)
1574
0
  else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1575
0
  {
1576
0
    client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1577
0
  }
1578
0
#endif
1579
0
}
1580
1581
void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1582
                                                      const ChannelDisconnectedEventArgs* e)
1583
0
{
1584
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1585
1586
0
  WINPR_ASSERT(cctx);
1587
0
  WINPR_ASSERT(e);
1588
1589
0
  if (0)
1590
0
  {
1591
0
  }
1592
0
#if defined(CHANNEL_AINPUT_CLIENT)
1593
0
  else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1594
0
    cctx->ainput = NULL;
1595
0
#endif
1596
0
#if defined(CHANNEL_RDPEI_CLIENT)
1597
0
  else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1598
0
  {
1599
0
    cctx->rdpei = NULL;
1600
0
  }
1601
0
#endif
1602
0
#if defined(CHANNEL_RDPGFX_CLIENT)
1603
0
  else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1604
0
  {
1605
0
    gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1606
0
  }
1607
0
#endif
1608
0
#if defined(CHANNEL_GEOMETRY_CLIENT)
1609
0
  else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1610
0
  {
1611
0
    gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1612
0
  }
1613
0
#endif
1614
0
#if defined(CHANNEL_VIDEO_CLIENT)
1615
0
  else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1616
0
  {
1617
0
    gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1618
0
  }
1619
0
  else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1620
0
  {
1621
0
    gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1622
0
  }
1623
0
#endif
1624
0
#if defined(CHANNEL_ENCOMSP_CLIENT)
1625
0
  else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1626
0
  {
1627
0
    client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1628
0
  }
1629
0
#endif
1630
0
}
1631
1632
BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1633
0
{
1634
0
  BOOL handled = FALSE;
1635
1636
0
  WINPR_ASSERT(cctx);
1637
1638
0
#if defined(CHANNEL_AINPUT_CLIENT)
1639
0
  if (cctx->ainput)
1640
0
  {
1641
0
    UINT rc = 0;
1642
0
    UINT64 flags = 0;
1643
0
    INT32 x = 0;
1644
0
    INT32 y = 0;
1645
0
    INT32 value = mflags & 0xFF;
1646
1647
0
    if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1648
0
      value = -1 * (0x100 - value);
1649
1650
    /* We have discrete steps, scale this so we can also support high
1651
     * resolution wheels. */
1652
0
    value *= 0x10000;
1653
1654
0
    if (mflags & PTR_FLAGS_WHEEL)
1655
0
    {
1656
0
      flags |= AINPUT_FLAGS_WHEEL;
1657
0
      y = value;
1658
0
    }
1659
1660
0
    if (mflags & PTR_FLAGS_HWHEEL)
1661
0
    {
1662
0
      flags |= AINPUT_FLAGS_WHEEL;
1663
0
      x = value;
1664
0
    }
1665
1666
0
    WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1667
0
    rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1668
0
    if (rc == CHANNEL_RC_OK)
1669
0
      handled = TRUE;
1670
0
  }
1671
0
#endif
1672
1673
0
  if (!handled)
1674
0
    freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1675
1676
0
  return TRUE;
1677
0
}
1678
1679
#if defined(CHANNEL_AINPUT_CLIENT)
1680
static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1681
0
{
1682
0
  UINT rc = 0;
1683
1684
0
  WINPR_ASSERT(cctx);
1685
0
  WINPR_ASSERT(cctx->ainput);
1686
0
  WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1687
1688
0
  rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1689
1690
0
  return rc == CHANNEL_RC_OK;
1691
0
}
1692
#endif
1693
1694
static bool button_pressed(const rdpClientContext* cctx)
1695
0
{
1696
0
  WINPR_ASSERT(cctx);
1697
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1698
0
  {
1699
0
    const BOOL cur = cctx->pressed_buttons[x];
1700
0
    if (cur)
1701
0
      return true;
1702
0
  }
1703
0
  return false;
1704
0
}
1705
1706
BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1707
                                      INT32 y)
1708
0
{
1709
0
  BOOL handled = FALSE;
1710
1711
0
  WINPR_ASSERT(cctx);
1712
1713
0
  if (mflags & PTR_FLAGS_BUTTON1)
1714
0
    cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1715
0
  if (mflags & PTR_FLAGS_BUTTON2)
1716
0
    cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1717
0
  if (mflags & PTR_FLAGS_BUTTON3)
1718
0
    cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1719
1720
0
  if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1721
0
      !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1722
0
  {
1723
0
    if (!button_pressed(cctx))
1724
0
      return TRUE;
1725
0
  }
1726
1727
0
  const BOOL haveRelative =
1728
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1729
0
  if (relative && haveRelative)
1730
0
  {
1731
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1732
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1733
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1734
0
  }
1735
1736
0
#if defined(CHANNEL_AINPUT_CLIENT)
1737
0
  if (cctx->ainput)
1738
0
  {
1739
0
    UINT64 flags = 0;
1740
1741
0
    if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1742
0
      flags |= AINPUT_FLAGS_HAVE_REL;
1743
1744
0
    if (relative)
1745
0
      flags |= AINPUT_FLAGS_REL;
1746
1747
0
    if (mflags & PTR_FLAGS_DOWN)
1748
0
      flags |= AINPUT_FLAGS_DOWN;
1749
0
    if (mflags & PTR_FLAGS_BUTTON1)
1750
0
      flags |= AINPUT_FLAGS_BUTTON1;
1751
0
    if (mflags & PTR_FLAGS_BUTTON2)
1752
0
      flags |= AINPUT_FLAGS_BUTTON2;
1753
0
    if (mflags & PTR_FLAGS_BUTTON3)
1754
0
      flags |= AINPUT_FLAGS_BUTTON3;
1755
0
    if (mflags & PTR_FLAGS_MOVE)
1756
0
      flags |= AINPUT_FLAGS_MOVE;
1757
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1758
0
  }
1759
0
#endif
1760
1761
0
  if (!handled)
1762
0
  {
1763
0
    if (relative)
1764
0
    {
1765
0
      cctx->lastX += x;
1766
0
      cctx->lastY += y;
1767
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1768
0
    }
1769
0
    else
1770
0
    {
1771
0
      cctx->lastX = x;
1772
0
      cctx->lastY = y;
1773
0
    }
1774
0
    freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1775
0
                                   (UINT16)cctx->lastY);
1776
0
  }
1777
0
  return TRUE;
1778
0
}
1779
1780
BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1781
                                               INT32 x, INT32 y)
1782
0
{
1783
0
  BOOL handled = FALSE;
1784
0
  WINPR_ASSERT(cctx);
1785
1786
0
  if (mflags & PTR_XFLAGS_BUTTON1)
1787
0
    cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1788
0
  if (mflags & PTR_XFLAGS_BUTTON2)
1789
0
    cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1790
1791
0
  const BOOL haveRelative =
1792
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1793
0
  if (relative && haveRelative)
1794
0
  {
1795
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1796
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1797
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1798
0
  }
1799
1800
0
#if defined(CHANNEL_AINPUT_CLIENT)
1801
0
  if (cctx->ainput)
1802
0
  {
1803
0
    UINT64 flags = 0;
1804
1805
0
    if (relative)
1806
0
      flags |= AINPUT_FLAGS_REL;
1807
0
    if (mflags & PTR_XFLAGS_DOWN)
1808
0
      flags |= AINPUT_FLAGS_DOWN;
1809
0
    if (mflags & PTR_XFLAGS_BUTTON1)
1810
0
      flags |= AINPUT_XFLAGS_BUTTON1;
1811
0
    if (mflags & PTR_XFLAGS_BUTTON2)
1812
0
      flags |= AINPUT_XFLAGS_BUTTON2;
1813
1814
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1815
0
  }
1816
0
#endif
1817
1818
0
  if (!handled)
1819
0
  {
1820
0
    if (relative)
1821
0
    {
1822
0
      cctx->lastX += x;
1823
0
      cctx->lastY += y;
1824
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1825
0
    }
1826
0
    else
1827
0
    {
1828
0
      cctx->lastX = x;
1829
0
      cctx->lastY = y;
1830
0
    }
1831
0
    freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1832
0
                                            (UINT16)cctx->lastY);
1833
0
  }
1834
1835
0
  return TRUE;
1836
0
}
1837
1838
static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1839
                                          const FreeRDP_TouchContact* contact)
1840
0
{
1841
0
  const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1842
0
  const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1843
0
  WINPR_ASSERT(contact);
1844
0
  WINPR_ASSERT(contact->x <= UINT16_MAX);
1845
0
  WINPR_ASSERT(contact->y <= UINT16_MAX);
1846
1847
0
  switch (contact->count)
1848
0
  {
1849
0
    case 1:
1850
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1851
0
                                              contact->x, contact->y);
1852
0
    case 2:
1853
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1854
0
                                              contact->x, contact->y);
1855
0
    case 3:
1856
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1857
0
                                              contact->x, contact->y);
1858
0
    case 4:
1859
0
      return freerdp_client_send_extended_button_event(
1860
0
          cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1861
0
    case 5:
1862
0
      return freerdp_client_send_extended_button_event(
1863
0
          cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1864
0
    default:
1865
      /* unmapped events, ignore */
1866
0
      return TRUE;
1867
0
  }
1868
0
}
1869
1870
static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1871
0
{
1872
0
  WINPR_ASSERT(cctx);
1873
0
  WINPR_ASSERT(contact);
1874
1875
0
#if defined(CHANNEL_RDPEI_CLIENT)
1876
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1877
1878
0
  if (!rdpei)
1879
0
    return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1880
1881
0
  int contactId = 0;
1882
1883
0
  if (rdpei->TouchRawEvent)
1884
0
  {
1885
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1886
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1887
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
1888
0
                                    : 0;
1889
    // Ensure contact position is unchanged from "engaged" to "out of range" state
1890
0
    rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1891
0
                         RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1892
0
                             RDPINPUT_CONTACT_FLAG_INCONTACT,
1893
0
                         contactFlags, contact->pressure);
1894
0
    rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1895
0
                         contactFlags, contact->pressure);
1896
0
  }
1897
0
  else
1898
0
  {
1899
0
    WINPR_ASSERT(rdpei->TouchEnd);
1900
0
    rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1901
0
  }
1902
0
  return TRUE;
1903
#else
1904
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1905
                 "-DCHANNEL_RDPEI_CLIENT=ON");
1906
  return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1907
#endif
1908
0
}
1909
1910
static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1911
0
{
1912
0
  WINPR_ASSERT(cctx);
1913
0
  WINPR_ASSERT(contact);
1914
1915
0
#if defined(CHANNEL_RDPEI_CLIENT)
1916
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1917
1918
  // Emulate mouse click if touch is not possible, like in login screen
1919
0
  if (!rdpei)
1920
0
    return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1921
1922
0
  int contactId = 0;
1923
1924
0
  if (rdpei->TouchRawEvent)
1925
0
  {
1926
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1927
0
                         RDPINPUT_CONTACT_FLAG_INCONTACT;
1928
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1929
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
1930
0
                                    : 0;
1931
0
    rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1932
0
                         contactFlags, contact->pressure);
1933
0
  }
1934
0
  else
1935
0
  {
1936
0
    WINPR_ASSERT(rdpei->TouchBegin);
1937
0
    rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1938
0
  }
1939
1940
0
  return TRUE;
1941
#else
1942
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1943
                 "-DCHANNEL_RDPEI_CLIENT=ON");
1944
  return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1945
#endif
1946
0
}
1947
1948
static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
1949
                                                 const FreeRDP_TouchContact* contact)
1950
0
{
1951
0
  const UINT16 flags = PTR_FLAGS_MOVE;
1952
1953
0
  WINPR_ASSERT(contact);
1954
0
  WINPR_ASSERT(contact->x <= UINT16_MAX);
1955
0
  WINPR_ASSERT(contact->y <= UINT16_MAX);
1956
0
  return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1957
0
}
1958
1959
static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1960
0
{
1961
0
  WINPR_ASSERT(cctx);
1962
0
  WINPR_ASSERT(contact);
1963
1964
0
#if defined(CHANNEL_RDPEI_CLIENT)
1965
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1966
1967
0
  if (!rdpei)
1968
0
    return freerdp_handle_touch_motion_to_mouse(cctx, contact);
1969
1970
0
  int contactId = 0;
1971
1972
0
  if (rdpei->TouchRawEvent)
1973
0
  {
1974
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1975
0
                         RDPINPUT_CONTACT_FLAG_INCONTACT;
1976
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1977
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
1978
0
                                    : 0;
1979
0
    rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1980
0
                         contactFlags, contact->pressure);
1981
0
  }
1982
0
  else
1983
0
  {
1984
0
    WINPR_ASSERT(rdpei->TouchUpdate);
1985
0
    rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
1986
0
  }
1987
1988
0
  return TRUE;
1989
#else
1990
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1991
                 "-DCHANNEL_RDPEI_CLIENT=ON");
1992
  return freerdp_handle_touch_motion_to_mouse(cctx, contact);
1993
#endif
1994
0
}
1995
1996
static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
1997
                                        UINT32 pressure, INT32 x, INT32 y,
1998
                                        FreeRDP_TouchContact* pcontact)
1999
0
{
2000
0
  WINPR_ASSERT(cctx);
2001
0
  WINPR_ASSERT(pcontact);
2002
2003
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2004
0
  {
2005
0
    FreeRDP_TouchContact* contact = &cctx->contacts[i];
2006
2007
0
    const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2008
0
    if (newcontact || (contact->id == touchId))
2009
0
    {
2010
0
      contact->id = touchId;
2011
0
      contact->flags = flags;
2012
0
      contact->pressure = pressure;
2013
0
      contact->x = x;
2014
0
      contact->y = y;
2015
2016
0
      *pcontact = *contact;
2017
2018
0
      const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2019
0
      if (resetcontact)
2020
0
      {
2021
0
        FreeRDP_TouchContact empty = { 0 };
2022
0
        *contact = empty;
2023
0
      }
2024
0
      return TRUE;
2025
0
    }
2026
0
  }
2027
2028
0
  return FALSE;
2029
0
}
2030
2031
BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2032
                                 UINT32 pressure, INT32 x, INT32 y)
2033
0
{
2034
0
  const UINT32 mask = FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION;
2035
0
  WINPR_ASSERT(cctx);
2036
2037
0
  FreeRDP_TouchContact contact = { 0 };
2038
2039
0
  if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2040
0
    return FALSE;
2041
2042
0
  switch (flags & mask)
2043
0
  {
2044
0
    case FREERDP_TOUCH_DOWN:
2045
0
      return freerdp_handle_touch_down(cctx, &contact);
2046
0
    case FREERDP_TOUCH_UP:
2047
0
      return freerdp_handle_touch_up(cctx, &contact);
2048
0
    case FREERDP_TOUCH_MOTION:
2049
0
      return freerdp_handle_touch_motion(cctx, &contact);
2050
0
    default:
2051
0
      WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %d, ignoring", flags);
2052
0
      return FALSE;
2053
0
  }
2054
0
}
2055
2056
BOOL freerdp_client_load_channels(freerdp* instance)
2057
0
{
2058
0
  WINPR_ASSERT(instance);
2059
0
  WINPR_ASSERT(instance->context);
2060
2061
0
  if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2062
0
  {
2063
0
    WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2064
0
    return FALSE;
2065
0
  }
2066
0
  return TRUE;
2067
0
}
2068
2069
int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2070
133
{
2071
133
  const char* str_data = freerdp_get_logon_error_info_data(data);
2072
133
  const char* str_type = freerdp_get_logon_error_info_type(type);
2073
2074
133
  if (!instance || !instance->context)
2075
0
    return -1;
2076
2077
133
  WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2078
133
  return 1;
2079
133
}
2080
2081
static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2082
                                                 size_t* pos)
2083
0
{
2084
0
  WINPR_ASSERT(cctx);
2085
2086
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2087
0
  {
2088
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2089
0
    if (deviceid == pen->deviceid)
2090
0
    {
2091
0
      if (pos)
2092
0
        *pos = i;
2093
0
      return pen;
2094
0
    }
2095
0
  }
2096
0
  return NULL;
2097
0
}
2098
2099
static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2100
                                        double pressure)
2101
0
{
2102
0
  static const INT32 null_deviceid = 0;
2103
2104
0
  WINPR_ASSERT(cctx);
2105
0
  WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2106
0
  if (freerdp_client_is_pen(cctx, deviceid))
2107
0
  {
2108
0
    WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2109
0
    return FALSE;
2110
0
  }
2111
2112
0
  size_t pos = 0;
2113
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2114
0
  if (pen)
2115
0
  {
2116
0
    const FreeRDP_PenDevice empty = { 0 };
2117
0
    *pen = empty;
2118
2119
0
    pen->deviceid = deviceid;
2120
0
    pen->max_pressure = pressure;
2121
0
    pen->flags = flags;
2122
2123
0
    WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2124
0
    return TRUE;
2125
0
  }
2126
2127
0
  WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2128
0
  return TRUE;
2129
0
}
2130
2131
BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2132
0
{
2133
0
#if defined(CHANNEL_RDPEI_CLIENT)
2134
0
  if ((flags & FREERDP_PEN_REGISTER) != 0)
2135
0
  {
2136
0
    va_list args;
2137
2138
0
    va_start(args, deviceid);
2139
0
    double pressure = va_arg(args, double);
2140
0
    va_end(args);
2141
0
    return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2142
0
  }
2143
0
  size_t pos = 0;
2144
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2145
0
  if (!pen)
2146
0
  {
2147
0
    WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2148
0
    return FALSE;
2149
0
  }
2150
2151
0
  UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2152
0
  UINT32 penFlags =
2153
0
      ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2154
2155
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2156
0
  WINPR_ASSERT(rdpei);
2157
2158
0
  UINT32 normalizedpressure = 1024;
2159
0
  INT32 x = 0;
2160
0
  INT32 y = 0;
2161
0
  UINT16 rotation = 0;
2162
0
  INT16 tiltX = 0;
2163
0
  INT16 tiltY = 0;
2164
0
  va_list args;
2165
0
  va_start(args, deviceid);
2166
2167
0
  x = va_arg(args, INT32);
2168
0
  y = va_arg(args, INT32);
2169
0
  if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2170
0
  {
2171
0
    const double pressure = va_arg(args, double);
2172
0
    const double np = (pressure * 1024.0) / pen->max_pressure;
2173
0
    normalizedpressure = (UINT32)lround(np);
2174
0
    WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2175
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2176
0
  }
2177
0
  if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2178
0
  {
2179
0
    const unsigned arg = va_arg(args, unsigned);
2180
0
    rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2181
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2182
0
  }
2183
0
  if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2184
0
  {
2185
0
    const int arg = va_arg(args, int);
2186
0
    tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2187
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2188
0
  }
2189
0
  if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2190
0
  {
2191
0
    const int arg = va_arg(args, int);
2192
0
    tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2193
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2194
0
  }
2195
0
  va_end(args);
2196
2197
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2198
0
  {
2199
    // Ensure that only one button is pressed
2200
0
    if (pen->pressed)
2201
0
      flags = FREERDP_PEN_MOTION |
2202
0
              (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2203
0
    else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2204
0
      pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2205
0
  }
2206
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2207
0
  {
2208
0
    if (!pen->pressed ||
2209
0
        ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2210
0
      flags = FREERDP_PEN_MOTION |
2211
0
              (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2212
0
    else
2213
0
      pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2214
0
  }
2215
2216
0
  flags |= pen->flags;
2217
0
  if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2218
0
    penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2219
0
  if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2220
0
    penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2221
2222
0
  pen->last_x = x;
2223
0
  pen->last_y = y;
2224
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2225
0
  {
2226
0
    WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2227
0
    pen->hovering = FALSE;
2228
0
    pen->pressed = TRUE;
2229
2230
0
    WINPR_ASSERT(rdpei->PenBegin);
2231
0
    const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2232
0
                                    normalizedpressure, rotation, tiltX, tiltY);
2233
0
    return rc == CHANNEL_RC_OK;
2234
0
  }
2235
0
  else if ((flags & FREERDP_PEN_MOTION) != 0)
2236
0
  {
2237
0
    UINT rc = ERROR_INTERNAL_ERROR;
2238
0
    if (pen->pressed)
2239
0
    {
2240
0
      WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2241
2242
      // TODO: what if no rotation is supported but tilt is?
2243
0
      WINPR_ASSERT(rdpei->PenUpdate);
2244
0
      rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2245
0
                            rotation, tiltX, tiltY);
2246
0
    }
2247
0
    else if (pen->hovering)
2248
0
    {
2249
0
      WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2250
2251
0
      WINPR_ASSERT(rdpei->PenHoverUpdate);
2252
0
      rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2253
0
                                 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2254
0
    }
2255
0
    else
2256
0
    {
2257
0
      WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2258
0
      pen->hovering = TRUE;
2259
2260
0
      WINPR_ASSERT(rdpei->PenHoverBegin);
2261
0
      rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2262
0
                                penFlags, normalizedpressure, rotation, tiltX, tiltY);
2263
0
    }
2264
0
    return rc == CHANNEL_RC_OK;
2265
0
  }
2266
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2267
0
  {
2268
0
    WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2269
0
    pen->pressed = FALSE;
2270
0
    pen->hovering = TRUE;
2271
2272
0
    WINPR_ASSERT(rdpei->PenUpdate);
2273
0
    const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2274
0
                                     normalizedpressure, rotation, tiltX, tiltY);
2275
0
    if (rc != CHANNEL_RC_OK)
2276
0
      return FALSE;
2277
0
    WINPR_ASSERT(rdpei->PenEnd);
2278
0
    const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2279
0
                                  penFlags, normalizedpressure, rotation, tiltX, tiltY);
2280
0
    return re == CHANNEL_RC_OK;
2281
0
  }
2282
2283
0
  WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2284
#else
2285
  WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2286
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2287
#endif
2288
2289
0
  return FALSE;
2290
0
}
2291
2292
BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2293
0
{
2294
0
  WINPR_ASSERT(cctx);
2295
2296
0
#if defined(CHANNEL_RDPEI_CLIENT)
2297
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2298
2299
0
  if (!rdpei)
2300
0
    return FALSE;
2301
2302
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2303
0
  {
2304
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2305
0
    if (pen->hovering)
2306
0
    {
2307
0
      WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2308
0
      pen->hovering = FALSE;
2309
0
      rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2310
0
    }
2311
0
  }
2312
0
  return TRUE;
2313
#else
2314
  WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2315
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2316
  return FALSE;
2317
#endif
2318
0
}
2319
2320
BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2321
0
{
2322
0
  WINPR_ASSERT(cctx);
2323
2324
0
  if (deviceid == 0)
2325
0
    return FALSE;
2326
2327
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2328
0
  {
2329
0
    const FreeRDP_PenDevice* pen = &cctx->pens[x];
2330
0
    if (pen->deviceid == deviceid)
2331
0
      return TRUE;
2332
0
  }
2333
2334
0
  return FALSE;
2335
0
}
2336
2337
BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* ccontext)
2338
0
{
2339
0
  WINPR_ASSERT(ccontext);
2340
2341
0
  const rdpSettings* settings = ccontext->context.settings;
2342
0
  const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2343
0
  const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2344
0
  BOOL ainput = FALSE;
2345
0
#if defined(CHANNEL_AINPUT_CLIENT)
2346
0
  ainput = ccontext->ainput != NULL;
2347
0
#endif
2348
2349
0
  return useRelative && (haveRelative || ainput);
2350
0
}
2351
2352
#if defined(WITH_AAD)
2353
WINPR_ATTR_MALLOC(free, 1)
2354
static char* get_redirect_uri(const rdpSettings* settings)
2355
{
2356
  char* redirect_uri = NULL;
2357
  const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2358
  if (cli)
2359
  {
2360
    const char* redirect_fmt =
2361
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2362
    const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2363
    const char* tenantid = "common";
2364
    if (useTenant)
2365
      tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2366
2367
    if (tenantid && redirect_fmt)
2368
    {
2369
      const char* url =
2370
          freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2371
2372
      size_t redirect_len = 0;
2373
      winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2374
    }
2375
  }
2376
  else
2377
  {
2378
    const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2379
    const char* redirect_fmt =
2380
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2381
2382
    size_t redirect_len = 0;
2383
    winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2384
  }
2385
  return redirect_uri;
2386
}
2387
2388
static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2389
{
2390
  const rdpSettings* settings = cctx->context.settings;
2391
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2392
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2393
                                                          AAD_WELLKNOWN_authorization_endpoint);
2394
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2395
2396
  if (!client_id || !ep || !scope)
2397
    return NULL;
2398
2399
  char* redirect_uri = get_redirect_uri(settings);
2400
  if (!redirect_uri)
2401
    return NULL;
2402
2403
  char* url = NULL;
2404
  size_t urllen = 0;
2405
  winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2406
                 client_id, scope, redirect_uri);
2407
  free(redirect_uri);
2408
  return url;
2409
}
2410
2411
static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2412
{
2413
  const rdpSettings* settings = cctx->context.settings;
2414
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2415
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2416
                                                          AAD_WELLKNOWN_authorization_endpoint);
2417
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2418
2419
  if (!client_id || !ep || !scope)
2420
    return NULL;
2421
2422
  char* redirect_uri = get_redirect_uri(settings);
2423
  if (!redirect_uri)
2424
    return NULL;
2425
2426
  char* url = NULL;
2427
  size_t urllen = 0;
2428
2429
  const char* code = va_arg(ap, const char*);
2430
  winpr_asprintf(&url, &urllen,
2431
                 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2432
                 code, client_id, scope, redirect_uri);
2433
  free(redirect_uri);
2434
  return url;
2435
}
2436
2437
static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2438
{
2439
  const rdpSettings* settings = cctx->context.settings;
2440
  char* url = NULL;
2441
  size_t urllen = 0;
2442
  char* redirect_uri = get_redirect_uri(settings);
2443
2444
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2445
  if (!client_id || !redirect_uri)
2446
    goto cleanup;
2447
2448
  {
2449
    const char* scope = va_arg(ap, const char*);
2450
    if (!scope)
2451
      goto cleanup;
2452
2453
    {
2454
      const char* ep = freerdp_utils_aad_get_wellknown_string(
2455
          &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2456
      winpr_asprintf(&url, &urllen,
2457
                     "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2458
                     client_id, scope, redirect_uri);
2459
    }
2460
  }
2461
2462
cleanup:
2463
  free(redirect_uri);
2464
  return url;
2465
}
2466
2467
static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2468
{
2469
  const rdpSettings* settings = cctx->context.settings;
2470
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2471
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2472
                                                          AAD_WELLKNOWN_authorization_endpoint);
2473
  const char* scope = va_arg(ap, const char*);
2474
  const char* code = va_arg(ap, const char*);
2475
  const char* req_cnf = va_arg(ap, const char*);
2476
2477
  if (!client_id || !ep || !scope || !code || !req_cnf)
2478
    return NULL;
2479
2480
  char* redirect_uri = get_redirect_uri(settings);
2481
  if (!redirect_uri)
2482
    return NULL;
2483
2484
  char* url = NULL;
2485
  size_t urllen = 0;
2486
2487
  winpr_asprintf(
2488
      &url, &urllen,
2489
      "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2490
      code, client_id, scope, redirect_uri, req_cnf);
2491
  free(redirect_uri);
2492
  return url;
2493
}
2494
#endif
2495
2496
char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2497
0
{
2498
0
  WINPR_ASSERT(cctx);
2499
0
  char* str = NULL;
2500
2501
0
  va_list ap;
2502
0
  va_start(ap, type);
2503
0
  switch (type)
2504
0
  {
2505
#if defined(WITH_AAD)
2506
    case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2507
      str = aad_auth_request(cctx, ap);
2508
      break;
2509
    case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2510
      str = aad_token_request(cctx, ap);
2511
      break;
2512
    case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2513
      str = avd_auth_request(cctx, ap);
2514
      break;
2515
    case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2516
      str = avd_token_request(cctx, ap);
2517
      break;
2518
#endif
2519
0
    default:
2520
0
      break;
2521
0
  }
2522
0
  va_end(ap);
2523
0
  return str;
2524
0
}