Coverage Report

Created: 2026-03-07 07:07

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