Coverage Report

Created: 2026-02-26 06:54

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.02k
{
81
8.02k
  WINPR_ASSERT(instance);
82
8.02k
  instance->AuthenticateEx = client_cli_authenticate_ex;
83
8.02k
  instance->ChooseSmartcard = client_cli_choose_smartcard;
84
8.02k
  instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
85
8.02k
  instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
86
8.02k
  instance->PresentGatewayMessage = client_cli_present_gateway_message;
87
8.02k
  instance->LogonErrorInfo = client_cli_logon_error_info;
88
8.02k
  instance->GetAccessToken = client_cli_get_access_token;
89
8.02k
  instance->RetryDialog = client_common_retry_dialog;
90
8.02k
}
91
92
static BOOL freerdp_client_common_new(freerdp* instance, rdpContext* context)
93
8.02k
{
94
8.02k
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
95
96
8.02k
  WINPR_ASSERT(instance);
97
8.02k
  WINPR_ASSERT(context);
98
99
8.02k
  instance->LoadChannels = freerdp_client_load_channels;
100
8.02k
  set_default_callbacks(instance);
101
102
8.02k
  pEntryPoints = instance->pClientEntryPoints;
103
8.02k
  WINPR_ASSERT(pEntryPoints);
104
8.02k
  return IFCALLRESULT(TRUE, pEntryPoints->ClientNew, instance, context);
105
8.02k
}
106
107
static void freerdp_client_common_free(freerdp* instance, rdpContext* context)
108
8.02k
{
109
8.02k
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
110
111
8.02k
  WINPR_ASSERT(instance);
112
8.02k
  WINPR_ASSERT(context);
113
114
8.02k
  pEntryPoints = instance->pClientEntryPoints;
115
8.02k
  WINPR_ASSERT(pEntryPoints);
116
8.02k
  IFCALL(pEntryPoints->ClientFree, instance, context);
117
8.02k
}
118
119
/* Common API */
120
121
rdpContext* freerdp_client_context_new(const RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
122
8.02k
{
123
8.02k
  freerdp* instance = NULL;
124
8.02k
  rdpContext* context = NULL;
125
126
8.02k
  if (!pEntryPoints)
127
0
    return NULL;
128
129
8.02k
  if (!IFCALLRESULT(TRUE, pEntryPoints->GlobalInit))
130
0
    return NULL;
131
132
8.02k
  instance = freerdp_new();
133
134
8.02k
  if (!instance)
135
0
    return NULL;
136
137
8.02k
  instance->ContextSize = pEntryPoints->ContextSize;
138
8.02k
  instance->ContextNew = freerdp_client_common_new;
139
8.02k
  instance->ContextFree = freerdp_client_common_free;
140
8.02k
  instance->pClientEntryPoints = (RDP_CLIENT_ENTRY_POINTS*)malloc(pEntryPoints->Size);
141
142
8.02k
  if (!instance->pClientEntryPoints)
143
0
    goto out_fail;
144
145
8.02k
  CopyMemory(instance->pClientEntryPoints, pEntryPoints, pEntryPoints->Size);
146
147
8.02k
  if (!freerdp_context_new_ex(instance, pEntryPoints->settings))
148
0
    goto out_fail2;
149
150
8.02k
  context = instance->context;
151
8.02k
  context->instance = instance;
152
153
8.02k
#if defined(WITH_CLIENT_CHANNELS)
154
8.02k
  if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
155
8.02k
      CHANNEL_RC_OK)
156
0
    goto out_fail2;
157
8.02k
#endif
158
159
8.02k
  return context;
160
0
out_fail2:
161
0
  free(instance->pClientEntryPoints);
162
0
out_fail:
163
0
  freerdp_free(instance);
164
0
  return NULL;
165
0
}
166
167
void freerdp_client_context_free(rdpContext* context)
168
8.02k
{
169
8.02k
  freerdp* instance = NULL;
170
171
8.02k
  if (!context)
172
0
    return;
173
174
8.02k
  instance = context->instance;
175
176
8.02k
  if (instance)
177
8.02k
  {
178
8.02k
    RDP_CLIENT_ENTRY_POINTS* pEntryPoints = instance->pClientEntryPoints;
179
8.02k
    freerdp_context_free(instance);
180
181
8.02k
    if (pEntryPoints)
182
8.02k
      IFCALL(pEntryPoints->GlobalUninit);
183
184
8.02k
    free(instance->pClientEntryPoints);
185
8.02k
    freerdp_free(instance);
186
8.02k
  }
187
8.02k
}
188
189
int freerdp_client_start(rdpContext* context)
190
0
{
191
0
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
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 = NULL;
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 = NULL;
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 NULL;
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 NULL;
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, NULL,
314
0
                                                       0, NULL, NULL);
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 = NULL;
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 = NULL;
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 = NULL;
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 = NULL;
414
0
  char* password = NULL;
415
0
  rdpAssistanceFile* file = NULL;
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 = NULL;
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 = NULL;
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 == NULL)
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 = NULL;
593
0
  *domain = NULL;
594
0
  *password = NULL;
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 = NULL;
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, NULL);
640
0
    char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, NULL);
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 NULL;
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 = NULL;
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), NULL);
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++ != NULL; p = strchr(p, '&'))
1056
0
  {
1057
0
    if (strncmp(p, "code=", 5) != 0)
1058
0
      continue;
1059
0
1060
0
    char* end = NULL;
1061
0
    p += 5;
1062
0
1063
0
    end = strchr(p, '&');
1064
0
    if (end)
1065
0
      *end = '\0';
1066
0
1067
0
    return p;
1068
0
  }
1069
0
1070
0
  return NULL;
1071
0
}
1072
1073
#if defined(WITH_AAD)
1074
static BOOL client_cli_get_rdsaad_access_token(freerdp* instance, const char* scope,
1075
                                               const char* req_cnf, char** token)
1076
{
1077
  WINPR_ASSERT(instance);
1078
  WINPR_ASSERT(instance->context);
1079
1080
  size_t size = 0;
1081
  char* url = NULL;
1082
  char* token_request = NULL;
1083
1084
  WINPR_ASSERT(scope);
1085
  WINPR_ASSERT(req_cnf);
1086
  WINPR_ASSERT(token);
1087
1088
  BOOL rc = FALSE;
1089
  *token = NULL;
1090
1091
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1092
                                             FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1093
1094
  printf("Browse to: %s\n", request);
1095
  free(request);
1096
  printf("Paste redirect URL here: \n");
1097
1098
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1099
    goto cleanup;
1100
1101
  {
1102
    const char* code = extract_authorization_code(url);
1103
    if (!code)
1104
      goto cleanup;
1105
    token_request =
1106
        freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1107
                                   FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1108
  }
1109
  if (!token_request)
1110
    goto cleanup;
1111
1112
  rc = client_common_get_access_token(instance, token_request, token);
1113
1114
cleanup:
1115
  free(token_request);
1116
  free(url);
1117
  return rc && (*token != NULL);
1118
}
1119
1120
static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1121
{
1122
  WINPR_ASSERT(instance);
1123
  WINPR_ASSERT(instance->context);
1124
1125
  size_t size = 0;
1126
  char* url = NULL;
1127
  char* token_request = NULL;
1128
1129
  WINPR_ASSERT(token);
1130
1131
  BOOL rc = FALSE;
1132
1133
  *token = NULL;
1134
1135
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1136
                                             FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1137
  if (!request)
1138
    return FALSE;
1139
  printf("Browse to: %s\n", request);
1140
  free(request);
1141
  printf("Paste redirect URL here: \n");
1142
1143
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1144
    goto cleanup;
1145
1146
  {
1147
    const char* code = extract_authorization_code(url);
1148
    if (!code)
1149
      goto cleanup;
1150
    token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1151
                                               FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1152
  }
1153
1154
  if (!token_request)
1155
    goto cleanup;
1156
1157
  rc = client_common_get_access_token(instance, token_request, token);
1158
1159
cleanup:
1160
  free(token_request);
1161
  free(url);
1162
  return rc && (*token != NULL);
1163
}
1164
#endif
1165
1166
BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1167
                                 size_t count, ...)
1168
0
{
1169
0
  WINPR_ASSERT(instance);
1170
0
  WINPR_ASSERT(token);
1171
1172
0
#if !defined(WITH_AAD)
1173
0
  WLog_ERR(TAG, "Build does not support AAD authentication");
1174
0
  return FALSE;
1175
#else
1176
  BOOL rc = FALSE;
1177
  WINPR_ASSERT(instance->context);
1178
  const BOOL saved =
1179
      freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1180
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1181
                                 TRUE))
1182
    return FALSE;
1183
1184
  switch (tokenType)
1185
  {
1186
    case ACCESS_TOKEN_TYPE_AAD:
1187
    {
1188
      if (count < 2)
1189
      {
1190
        WLog_ERR(TAG,
1191
                 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1192
                 ", aborting",
1193
                 count);
1194
        return FALSE;
1195
      }
1196
      else if (count > 2)
1197
        WLog_WARN(TAG,
1198
                  "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1199
                  ", ignoring",
1200
                  count);
1201
      va_list ap = 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 = NULL;
1238
  size_t response_length = 0;
1239
1240
  wLog* log = WLog_Get(TAG);
1241
1242
  const char* token_ep =
1243
      freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1244
  if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1245
  {
1246
    WLog_ERR(TAG, "access token request failed");
1247
    return FALSE;
1248
  }
1249
1250
  if (resp_code != HTTP_STATUS_OK)
1251
  {
1252
    char buffer[64] = 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, NULL);
1322
0
}
1323
1324
BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1325
0
{
1326
0
  BOOL retry = TRUE;
1327
0
  UINT32 error = 0;
1328
0
  UINT32 numRetries = 0;
1329
0
  rdpSettings* settings = NULL;
1330
1331
0
  if (!instance)
1332
0
    return FALSE;
1333
1334
0
  WINPR_ASSERT(instance->context);
1335
1336
0
  settings = instance->context->settings;
1337
0
  WINPR_ASSERT(settings);
1338
1339
0
  const UINT32 maxRetries =
1340
0
      freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1341
1342
  /* Only auto reconnect on network disconnects. */
1343
0
  error = freerdp_error_info(instance);
1344
0
  switch (error)
1345
0
  {
1346
0
    case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1347
      /* A network disconnect was detected */
1348
0
      WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1349
0
                freerdp_get_error_info_string(error));
1350
0
      break;
1351
0
    case ERRINFO_SUCCESS:
1352
      /* A network disconnect was detected */
1353
0
      WLog_INFO(TAG, "Network disconnect!");
1354
0
      break;
1355
0
    default:
1356
0
      WLog_DBG(TAG, "Other error: %s", freerdp_get_error_info_string(error));
1357
0
      return FALSE;
1358
0
  }
1359
1360
0
  if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1361
0
  {
1362
    /* No auto-reconnect - just quit */
1363
0
    WLog_DBG(TAG, "AutoReconnect not enabled, quitting.");
1364
0
    return FALSE;
1365
0
  }
1366
1367
0
  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, NULL);
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 = NULL;
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 = NULL;
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 = NULL;
1489
0
  rdpSettings* settings = NULL;
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 = NULL;
1533
0
    encomsp->ParticipantCreated = NULL;
1534
0
  }
1535
1536
0
  if (cctx)
1537
0
    cctx->encomsp = NULL;
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 = NULL;
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 = NULL;
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
#if defined(CHANNEL_AINPUT_CLIENT)
1650
0
  if (cctx->ainput)
1651
0
  {
1652
0
    UINT rc = 0;
1653
0
    UINT64 flags = 0;
1654
0
    INT32 x = 0;
1655
0
    INT32 y = 0;
1656
0
    INT32 value = mflags & 0xFF;
1657
1658
0
    if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1659
0
      value = -1 * (0x100 - value);
1660
1661
    /* We have discrete steps, scale this so we can also support high
1662
     * resolution wheels. */
1663
0
    value *= 0x10000;
1664
1665
0
    if (mflags & PTR_FLAGS_WHEEL)
1666
0
    {
1667
0
      flags |= AINPUT_FLAGS_WHEEL;
1668
0
      y = value;
1669
0
    }
1670
1671
0
    if (mflags & PTR_FLAGS_HWHEEL)
1672
0
    {
1673
0
      flags |= AINPUT_FLAGS_WHEEL;
1674
0
      x = value;
1675
0
    }
1676
1677
0
    WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1678
0
    rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1679
0
    if (rc == CHANNEL_RC_OK)
1680
0
      handled = TRUE;
1681
0
  }
1682
0
#endif
1683
1684
0
  if (!handled)
1685
0
    freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1686
1687
0
  return TRUE;
1688
0
}
1689
1690
#if defined(CHANNEL_AINPUT_CLIENT)
1691
static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1692
0
{
1693
0
  UINT rc = 0;
1694
1695
0
  WINPR_ASSERT(cctx);
1696
0
  WINPR_ASSERT(cctx->ainput);
1697
0
  WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1698
1699
0
  rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1700
1701
0
  return rc == CHANNEL_RC_OK;
1702
0
}
1703
#endif
1704
1705
static bool button_pressed(const rdpClientContext* cctx)
1706
0
{
1707
0
  WINPR_ASSERT(cctx);
1708
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1709
0
  {
1710
0
    const BOOL cur = cctx->pressed_buttons[x];
1711
0
    if (cur)
1712
0
      return true;
1713
0
  }
1714
0
  return false;
1715
0
}
1716
1717
BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1718
                                      INT32 y)
1719
0
{
1720
0
  BOOL handled = FALSE;
1721
1722
0
  WINPR_ASSERT(cctx);
1723
1724
0
  if (mflags & PTR_FLAGS_BUTTON1)
1725
0
    cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1726
0
  if (mflags & PTR_FLAGS_BUTTON2)
1727
0
    cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1728
0
  if (mflags & PTR_FLAGS_BUTTON3)
1729
0
    cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1730
1731
0
  if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1732
0
      !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1733
0
  {
1734
0
    if (!button_pressed(cctx))
1735
0
      return TRUE;
1736
0
  }
1737
1738
0
  const BOOL haveRelative =
1739
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1740
0
  if (relative && haveRelative)
1741
0
  {
1742
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1743
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1744
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1745
0
  }
1746
1747
0
#if defined(CHANNEL_AINPUT_CLIENT)
1748
0
  if (cctx->ainput)
1749
0
  {
1750
0
    UINT64 flags = 0;
1751
1752
0
    if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1753
0
      flags |= AINPUT_FLAGS_HAVE_REL;
1754
1755
0
    if (relative)
1756
0
      flags |= AINPUT_FLAGS_REL;
1757
1758
0
    if (mflags & PTR_FLAGS_DOWN)
1759
0
      flags |= AINPUT_FLAGS_DOWN;
1760
0
    if (mflags & PTR_FLAGS_BUTTON1)
1761
0
      flags |= AINPUT_FLAGS_BUTTON1;
1762
0
    if (mflags & PTR_FLAGS_BUTTON2)
1763
0
      flags |= AINPUT_FLAGS_BUTTON2;
1764
0
    if (mflags & PTR_FLAGS_BUTTON3)
1765
0
      flags |= AINPUT_FLAGS_BUTTON3;
1766
0
    if (mflags & PTR_FLAGS_MOVE)
1767
0
      flags |= AINPUT_FLAGS_MOVE;
1768
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1769
0
  }
1770
0
#endif
1771
1772
0
  if (!handled)
1773
0
  {
1774
0
    if (relative)
1775
0
    {
1776
0
      cctx->lastX += x;
1777
0
      cctx->lastY += y;
1778
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1779
0
    }
1780
0
    else
1781
0
    {
1782
0
      cctx->lastX = x;
1783
0
      cctx->lastY = y;
1784
0
    }
1785
0
    return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1786
0
                                          (UINT16)cctx->lastY);
1787
0
  }
1788
0
  return TRUE;
1789
0
}
1790
1791
BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1792
                                               INT32 x, INT32 y)
1793
0
{
1794
0
  BOOL handled = FALSE;
1795
0
  WINPR_ASSERT(cctx);
1796
1797
0
  if (mflags & PTR_XFLAGS_BUTTON1)
1798
0
    cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1799
0
  if (mflags & PTR_XFLAGS_BUTTON2)
1800
0
    cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1801
1802
0
  const BOOL haveRelative =
1803
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1804
0
  if (relative && haveRelative)
1805
0
  {
1806
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1807
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1808
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1809
0
  }
1810
1811
0
#if defined(CHANNEL_AINPUT_CLIENT)
1812
0
  if (cctx->ainput)
1813
0
  {
1814
0
    UINT64 flags = 0;
1815
1816
0
    if (relative)
1817
0
      flags |= AINPUT_FLAGS_REL;
1818
0
    if (mflags & PTR_XFLAGS_DOWN)
1819
0
      flags |= AINPUT_FLAGS_DOWN;
1820
0
    if (mflags & PTR_XFLAGS_BUTTON1)
1821
0
      flags |= AINPUT_XFLAGS_BUTTON1;
1822
0
    if (mflags & PTR_XFLAGS_BUTTON2)
1823
0
      flags |= AINPUT_XFLAGS_BUTTON2;
1824
1825
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1826
0
  }
1827
0
#endif
1828
1829
0
  if (!handled)
1830
0
  {
1831
0
    if (relative)
1832
0
    {
1833
0
      cctx->lastX += x;
1834
0
      cctx->lastY += y;
1835
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1836
0
    }
1837
0
    else
1838
0
    {
1839
0
      cctx->lastX = x;
1840
0
      cctx->lastY = y;
1841
0
    }
1842
0
    freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1843
0
                                            (UINT16)cctx->lastY);
1844
0
  }
1845
1846
0
  return TRUE;
1847
0
}
1848
1849
static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1850
                                          const FreeRDP_TouchContact* contact)
1851
0
{
1852
0
  const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1853
0
  const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1854
0
  WINPR_ASSERT(contact);
1855
0
  WINPR_ASSERT(contact->x <= UINT16_MAX);
1856
0
  WINPR_ASSERT(contact->y <= UINT16_MAX);
1857
1858
0
  switch (contact->count)
1859
0
  {
1860
0
    case 1:
1861
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1862
0
                                              contact->x, contact->y);
1863
0
    case 2:
1864
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1865
0
                                              contact->x, contact->y);
1866
0
    case 3:
1867
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1868
0
                                              contact->x, contact->y);
1869
0
    case 4:
1870
0
      return freerdp_client_send_extended_button_event(
1871
0
          cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1872
0
    case 5:
1873
0
      return freerdp_client_send_extended_button_event(
1874
0
          cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1875
0
    default:
1876
      /* unmapped events, ignore */
1877
0
      return TRUE;
1878
0
  }
1879
0
}
1880
1881
static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1882
0
{
1883
0
  WINPR_ASSERT(cctx);
1884
0
  WINPR_ASSERT(contact);
1885
1886
0
#if defined(CHANNEL_RDPEI_CLIENT)
1887
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1888
1889
0
  if (!rdpei)
1890
0
    return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1891
1892
0
  int contactId = 0;
1893
1894
0
  if (rdpei->TouchRawEvent)
1895
0
  {
1896
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1897
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1898
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
1899
0
                                    : 0;
1900
    // Ensure contact position is unchanged from "engaged" to "out of range" state
1901
0
    const UINT rc1 =
1902
0
        rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1903
0
                             RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1904
0
                                 RDPINPUT_CONTACT_FLAG_INCONTACT,
1905
0
                             contactFlags, contact->pressure);
1906
0
    if (rc1 != CHANNEL_RC_OK)
1907
0
      return FALSE;
1908
1909
0
    const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
1910
0
                                          &contactId, flags, contactFlags, contact->pressure);
1911
0
    if (rc2 != CHANNEL_RC_OK)
1912
0
      return FALSE;
1913
0
  }
1914
0
  else
1915
0
  {
1916
0
    WINPR_ASSERT(rdpei->TouchEnd);
1917
0
    const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1918
0
    if (rc != CHANNEL_RC_OK)
1919
0
      return FALSE;
1920
0
  }
1921
0
  return TRUE;
1922
#else
1923
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1924
                 "-DCHANNEL_RDPEI_CLIENT=ON");
1925
  return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1926
#endif
1927
0
}
1928
1929
static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1930
0
{
1931
0
  WINPR_ASSERT(cctx);
1932
0
  WINPR_ASSERT(contact);
1933
1934
0
#if defined(CHANNEL_RDPEI_CLIENT)
1935
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1936
1937
  // Emulate mouse click if touch is not possible, like in login screen
1938
0
  if (!rdpei)
1939
0
    return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1940
1941
0
  int contactId = 0;
1942
1943
0
  if (rdpei->TouchRawEvent)
1944
0
  {
1945
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1946
0
                         RDPINPUT_CONTACT_FLAG_INCONTACT;
1947
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1948
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
1949
0
                                    : 0;
1950
0
    const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1951
0
                                         flags, contactFlags, contact->pressure);
1952
0
    if (rc != CHANNEL_RC_OK)
1953
0
      return FALSE;
1954
0
  }
1955
0
  else
1956
0
  {
1957
0
    WINPR_ASSERT(rdpei->TouchBegin);
1958
0
    const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1959
0
    if (rc != CHANNEL_RC_OK)
1960
0
      return FALSE;
1961
0
  }
1962
1963
0
  return TRUE;
1964
#else
1965
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1966
                 "-DCHANNEL_RDPEI_CLIENT=ON");
1967
  return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1968
#endif
1969
0
}
1970
1971
static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
1972
                                                 const FreeRDP_TouchContact* contact)
1973
0
{
1974
0
  const UINT16 flags = PTR_FLAGS_MOVE;
1975
1976
0
  WINPR_ASSERT(contact);
1977
0
  WINPR_ASSERT(contact->x <= UINT16_MAX);
1978
0
  WINPR_ASSERT(contact->y <= UINT16_MAX);
1979
0
  return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1980
0
}
1981
1982
static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1983
0
{
1984
0
  WINPR_ASSERT(cctx);
1985
0
  WINPR_ASSERT(contact);
1986
1987
0
#if defined(CHANNEL_RDPEI_CLIENT)
1988
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1989
1990
0
  if (!rdpei)
1991
0
    return freerdp_handle_touch_motion_to_mouse(cctx, contact);
1992
1993
0
  int contactId = 0;
1994
1995
0
  if (rdpei->TouchRawEvent)
1996
0
  {
1997
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1998
0
                         RDPINPUT_CONTACT_FLAG_INCONTACT;
1999
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2000
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
2001
0
                                    : 0;
2002
0
    const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2003
0
                                         flags, contactFlags, contact->pressure);
2004
0
    if (rc != CHANNEL_RC_OK)
2005
0
      return FALSE;
2006
0
  }
2007
0
  else
2008
0
  {
2009
0
    WINPR_ASSERT(rdpei->TouchUpdate);
2010
0
    const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2011
0
    if (rc != CHANNEL_RC_OK)
2012
0
      return FALSE;
2013
0
  }
2014
2015
0
  return TRUE;
2016
#else
2017
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2018
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2019
  return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2020
#endif
2021
0
}
2022
2023
static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2024
0
{
2025
0
  WINPR_ASSERT(cctx);
2026
0
  WINPR_ASSERT(contact);
2027
2028
0
#if defined(CHANNEL_RDPEI_CLIENT)
2029
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2030
2031
0
  if (!rdpei)
2032
0
    return freerdp_handle_touch_to_mouse(cctx, false, contact);
2033
2034
0
  int contactId = 0;
2035
2036
0
  if (rdpei->TouchRawEvent)
2037
0
  {
2038
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2039
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2040
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
2041
0
                                    : 0;
2042
0
    const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2043
0
                                         flags, contactFlags, contact->pressure);
2044
0
    if (rc != CHANNEL_RC_OK)
2045
0
      return FALSE;
2046
0
  }
2047
0
  else
2048
0
  {
2049
0
    WINPR_ASSERT(rdpei->TouchUpdate);
2050
0
    const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2051
0
    if (rc != CHANNEL_RC_OK)
2052
0
      return FALSE;
2053
0
  }
2054
2055
0
  return TRUE;
2056
#else
2057
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2058
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2059
  return freerdp_handle_touch_to_mouse(cctx, false, contact);
2060
#endif
2061
0
}
2062
2063
static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2064
                                        UINT32 pressure, INT32 x, INT32 y,
2065
                                        FreeRDP_TouchContact* pcontact)
2066
0
{
2067
0
  WINPR_ASSERT(cctx);
2068
0
  WINPR_ASSERT(pcontact);
2069
2070
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2071
0
  {
2072
0
    FreeRDP_TouchContact* contact = &cctx->contacts[i];
2073
2074
0
    const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2075
0
    if (newcontact || (contact->id == touchId))
2076
0
    {
2077
0
      contact->id = touchId;
2078
0
      contact->flags = flags;
2079
0
      contact->pressure = pressure;
2080
0
      contact->x = x;
2081
0
      contact->y = y;
2082
2083
0
      *pcontact = *contact;
2084
2085
0
      const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2086
0
      if (resetcontact)
2087
0
      {
2088
0
        FreeRDP_TouchContact empty = WINPR_C_ARRAY_INIT;
2089
0
        *contact = empty;
2090
0
      }
2091
0
      return TRUE;
2092
0
    }
2093
0
  }
2094
2095
0
  return FALSE;
2096
0
}
2097
2098
BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2099
                                 UINT32 pressure, INT32 x, INT32 y)
2100
0
{
2101
0
  const UINT32 mask =
2102
0
      FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2103
0
  WINPR_ASSERT(cctx);
2104
2105
0
  FreeRDP_TouchContact contact = WINPR_C_ARRAY_INIT;
2106
2107
0
  if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2108
0
    return FALSE;
2109
2110
0
  switch (flags & mask)
2111
0
  {
2112
0
    case FREERDP_TOUCH_DOWN:
2113
0
      return freerdp_handle_touch_down(cctx, &contact);
2114
0
    case FREERDP_TOUCH_UP:
2115
0
      return freerdp_handle_touch_up(cctx, &contact);
2116
0
    case FREERDP_TOUCH_MOTION:
2117
0
      return freerdp_handle_touch_motion(cctx, &contact);
2118
0
    case FREERDP_TOUCH_CANCEL:
2119
0
      return freerdp_handle_touch_cancel(cctx, &contact);
2120
0
    default:
2121
0
      WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %" PRIu32 ", ignoring", flags);
2122
0
      return FALSE;
2123
0
  }
2124
0
}
2125
2126
BOOL freerdp_client_load_channels(freerdp* instance)
2127
0
{
2128
0
  WINPR_ASSERT(instance);
2129
0
  WINPR_ASSERT(instance->context);
2130
2131
0
  if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2132
0
  {
2133
0
    WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2134
0
    return FALSE;
2135
0
  }
2136
0
  return TRUE;
2137
0
}
2138
2139
int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2140
103
{
2141
103
  const char* str_data = freerdp_get_logon_error_info_data(data);
2142
103
  const char* str_type = freerdp_get_logon_error_info_type(type);
2143
2144
103
  if (!instance || !instance->context)
2145
0
    return -1;
2146
2147
103
  WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2148
103
  return 1;
2149
103
}
2150
2151
static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2152
                                                 size_t* pos)
2153
0
{
2154
0
  WINPR_ASSERT(cctx);
2155
2156
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2157
0
  {
2158
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2159
0
    if (deviceid == pen->deviceid)
2160
0
    {
2161
0
      if (pos)
2162
0
        *pos = i;
2163
0
      return pen;
2164
0
    }
2165
0
  }
2166
0
  return NULL;
2167
0
}
2168
2169
static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2170
                                        double pressure)
2171
0
{
2172
0
  static const INT32 null_deviceid = 0;
2173
2174
0
  WINPR_ASSERT(cctx);
2175
0
  WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2176
0
  if (freerdp_client_is_pen(cctx, deviceid))
2177
0
  {
2178
0
    WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2179
0
    return FALSE;
2180
0
  }
2181
2182
0
  size_t pos = 0;
2183
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2184
0
  if (pen)
2185
0
  {
2186
0
    const FreeRDP_PenDevice empty = WINPR_C_ARRAY_INIT;
2187
0
    *pen = empty;
2188
2189
0
    pen->deviceid = deviceid;
2190
0
    pen->max_pressure = pressure;
2191
0
    pen->flags = flags;
2192
2193
0
    WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2194
0
    return TRUE;
2195
0
  }
2196
2197
0
  WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2198
0
  return TRUE;
2199
0
}
2200
2201
BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2202
0
{
2203
0
#if defined(CHANNEL_RDPEI_CLIENT)
2204
0
  if ((flags & FREERDP_PEN_REGISTER) != 0)
2205
0
  {
2206
0
    va_list args = WINPR_C_ARRAY_INIT;
2207
2208
0
    va_start(args, deviceid);
2209
0
    double pressure = va_arg(args, double);
2210
0
    va_end(args);
2211
0
    return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2212
0
  }
2213
0
  size_t pos = 0;
2214
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2215
0
  if (!pen)
2216
0
  {
2217
0
    WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2218
0
    return FALSE;
2219
0
  }
2220
2221
0
  UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2222
0
  UINT32 penFlags =
2223
0
      ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2224
2225
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2226
0
  WINPR_ASSERT(rdpei);
2227
2228
0
  UINT32 normalizedpressure = 1024;
2229
0
  INT32 x = 0;
2230
0
  INT32 y = 0;
2231
0
  UINT16 rotation = 0;
2232
0
  INT16 tiltX = 0;
2233
0
  INT16 tiltY = 0;
2234
0
  va_list args = WINPR_C_ARRAY_INIT;
2235
0
  va_start(args, deviceid);
2236
2237
0
  x = va_arg(args, INT32);
2238
0
  y = va_arg(args, INT32);
2239
0
  if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2240
0
  {
2241
0
    const double pressure = va_arg(args, double);
2242
0
    const double np = (pressure * 1024.0) / pen->max_pressure;
2243
0
    normalizedpressure = (UINT32)lround(np);
2244
0
    WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2245
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2246
0
  }
2247
0
  if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2248
0
  {
2249
0
    const unsigned arg = va_arg(args, unsigned);
2250
0
    rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2251
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2252
0
  }
2253
0
  if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2254
0
  {
2255
0
    const int arg = va_arg(args, int);
2256
0
    tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2257
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2258
0
  }
2259
0
  if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2260
0
  {
2261
0
    const int arg = va_arg(args, int);
2262
0
    tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2263
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2264
0
  }
2265
0
  va_end(args);
2266
2267
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2268
0
  {
2269
    // Ensure that only one button is pressed
2270
0
    if (pen->pressed)
2271
0
      flags = FREERDP_PEN_MOTION |
2272
0
              (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2273
0
    else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2274
0
      pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2275
0
  }
2276
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2277
0
  {
2278
0
    if (!pen->pressed ||
2279
0
        ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2280
0
      flags = FREERDP_PEN_MOTION |
2281
0
              (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2282
0
    else
2283
0
      pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2284
0
  }
2285
2286
0
  flags |= pen->flags;
2287
0
  if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2288
0
    penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2289
0
  if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2290
0
    penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2291
2292
0
  pen->last_x = x;
2293
0
  pen->last_y = y;
2294
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2295
0
  {
2296
0
    WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2297
0
    pen->hovering = FALSE;
2298
0
    pen->pressed = TRUE;
2299
2300
0
    WINPR_ASSERT(rdpei->PenBegin);
2301
0
    const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2302
0
                                    normalizedpressure, rotation, tiltX, tiltY);
2303
0
    return rc == CHANNEL_RC_OK;
2304
0
  }
2305
0
  else if ((flags & FREERDP_PEN_MOTION) != 0)
2306
0
  {
2307
0
    UINT rc = ERROR_INTERNAL_ERROR;
2308
0
    if (pen->pressed)
2309
0
    {
2310
0
      WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2311
2312
      // TODO: what if no rotation is supported but tilt is?
2313
0
      WINPR_ASSERT(rdpei->PenUpdate);
2314
0
      rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2315
0
                            rotation, tiltX, tiltY);
2316
0
    }
2317
0
    else if (pen->hovering)
2318
0
    {
2319
0
      WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2320
2321
0
      WINPR_ASSERT(rdpei->PenHoverUpdate);
2322
0
      rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2323
0
                                 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2324
0
    }
2325
0
    else
2326
0
    {
2327
0
      WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2328
0
      pen->hovering = TRUE;
2329
2330
0
      WINPR_ASSERT(rdpei->PenHoverBegin);
2331
0
      rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2332
0
                                penFlags, normalizedpressure, rotation, tiltX, tiltY);
2333
0
    }
2334
0
    return rc == CHANNEL_RC_OK;
2335
0
  }
2336
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2337
0
  {
2338
0
    WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2339
0
    pen->pressed = FALSE;
2340
0
    pen->hovering = TRUE;
2341
2342
0
    WINPR_ASSERT(rdpei->PenUpdate);
2343
0
    const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2344
0
                                     normalizedpressure, rotation, tiltX, tiltY);
2345
0
    if (rc != CHANNEL_RC_OK)
2346
0
      return FALSE;
2347
0
    WINPR_ASSERT(rdpei->PenEnd);
2348
0
    const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2349
0
                                  penFlags, normalizedpressure, rotation, tiltX, tiltY);
2350
0
    return re == CHANNEL_RC_OK;
2351
0
  }
2352
2353
0
  WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2354
#else
2355
  WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2356
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2357
#endif
2358
2359
0
  return FALSE;
2360
0
}
2361
2362
BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2363
0
{
2364
0
  WINPR_ASSERT(cctx);
2365
2366
0
#if defined(CHANNEL_RDPEI_CLIENT)
2367
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2368
2369
0
  if (!rdpei)
2370
0
    return FALSE;
2371
2372
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2373
0
  {
2374
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2375
0
    if (pen->hovering)
2376
0
    {
2377
0
      WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2378
0
      pen->hovering = FALSE;
2379
0
      const UINT rc =
2380
0
          rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2381
0
      if (rc != CHANNEL_RC_OK)
2382
0
        return FALSE;
2383
0
    }
2384
0
  }
2385
0
  return TRUE;
2386
#else
2387
  WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2388
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2389
  return FALSE;
2390
#endif
2391
0
}
2392
2393
BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2394
0
{
2395
0
  WINPR_ASSERT(cctx);
2396
2397
0
  if (deviceid == 0)
2398
0
    return FALSE;
2399
2400
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2401
0
  {
2402
0
    const FreeRDP_PenDevice* pen = &cctx->pens[x];
2403
0
    if (pen->deviceid == deviceid)
2404
0
      return TRUE;
2405
0
  }
2406
2407
0
  return FALSE;
2408
0
}
2409
2410
BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* ccontext)
2411
0
{
2412
0
  WINPR_ASSERT(ccontext);
2413
2414
0
  const rdpSettings* settings = ccontext->context.settings;
2415
0
  const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2416
0
  const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2417
0
  BOOL ainput = FALSE;
2418
0
#if defined(CHANNEL_AINPUT_CLIENT)
2419
0
  ainput = ccontext->ainput != NULL;
2420
0
#endif
2421
2422
0
  return useRelative && (haveRelative || ainput);
2423
0
}
2424
2425
#if defined(WITH_AAD)
2426
WINPR_ATTR_MALLOC(free, 1)
2427
WINPR_ATTR_NODISCARD
2428
static char* get_redirect_uri(const rdpSettings* settings)
2429
{
2430
  char* redirect_uri = NULL;
2431
  const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2432
  if (cli)
2433
  {
2434
    const char* redirect_fmt =
2435
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2436
    const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2437
    const char* tenantid = "common";
2438
    if (useTenant)
2439
      tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2440
2441
    if (tenantid && redirect_fmt)
2442
    {
2443
      const char* url =
2444
          freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2445
2446
      size_t redirect_len = 0;
2447
      winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2448
    }
2449
  }
2450
  else
2451
  {
2452
    const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2453
    const char* redirect_fmt =
2454
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2455
2456
    size_t redirect_len = 0;
2457
    winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2458
  }
2459
  return redirect_uri;
2460
}
2461
2462
static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2463
{
2464
  const rdpSettings* settings = cctx->context.settings;
2465
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2466
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2467
                                                          AAD_WELLKNOWN_authorization_endpoint);
2468
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2469
2470
  if (!client_id || !ep || !scope)
2471
    return NULL;
2472
2473
  char* redirect_uri = get_redirect_uri(settings);
2474
  if (!redirect_uri)
2475
    return NULL;
2476
2477
  char* url = NULL;
2478
  size_t urllen = 0;
2479
  winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2480
                 client_id, scope, redirect_uri);
2481
  free(redirect_uri);
2482
  return url;
2483
}
2484
2485
static char* avd_token_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 NULL;
2495
2496
  char* redirect_uri = get_redirect_uri(settings);
2497
  if (!redirect_uri)
2498
    return NULL;
2499
2500
  char* url = NULL;
2501
  size_t urllen = 0;
2502
2503
  const char* code = va_arg(ap, const char*);
2504
  winpr_asprintf(&url, &urllen,
2505
                 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2506
                 code, client_id, scope, redirect_uri);
2507
  free(redirect_uri);
2508
  return url;
2509
}
2510
2511
static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2512
{
2513
  const rdpSettings* settings = cctx->context.settings;
2514
  char* url = NULL;
2515
  size_t urllen = 0;
2516
  char* redirect_uri = get_redirect_uri(settings);
2517
2518
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2519
  if (!client_id || !redirect_uri)
2520
    goto cleanup;
2521
2522
  {
2523
    const char* scope = va_arg(ap, const char*);
2524
    if (!scope)
2525
      goto cleanup;
2526
2527
    {
2528
      const char* ep = freerdp_utils_aad_get_wellknown_string(
2529
          &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2530
      winpr_asprintf(&url, &urllen,
2531
                     "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2532
                     client_id, scope, redirect_uri);
2533
    }
2534
  }
2535
2536
cleanup:
2537
  free(redirect_uri);
2538
  return url;
2539
}
2540
2541
static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2542
{
2543
  const rdpSettings* settings = cctx->context.settings;
2544
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2545
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2546
                                                          AAD_WELLKNOWN_authorization_endpoint);
2547
  const char* scope = va_arg(ap, const char*);
2548
  const char* code = va_arg(ap, const char*);
2549
  const char* req_cnf = va_arg(ap, const char*);
2550
2551
  if (!client_id || !ep || !scope || !code || !req_cnf)
2552
    return NULL;
2553
2554
  char* redirect_uri = get_redirect_uri(settings);
2555
  if (!redirect_uri)
2556
    return NULL;
2557
2558
  char* url = NULL;
2559
  size_t urllen = 0;
2560
2561
  winpr_asprintf(
2562
      &url, &urllen,
2563
      "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2564
      code, client_id, scope, redirect_uri, req_cnf);
2565
  free(redirect_uri);
2566
  return url;
2567
}
2568
#endif
2569
2570
char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2571
0
{
2572
0
  WINPR_ASSERT(cctx);
2573
0
  char* str = NULL;
2574
2575
0
  va_list ap = WINPR_C_ARRAY_INIT;
2576
0
  va_start(ap, type);
2577
0
  switch (type)
2578
0
  {
2579
#if defined(WITH_AAD)
2580
    case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2581
      str = aad_auth_request(cctx, ap);
2582
      break;
2583
    case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2584
      str = aad_token_request(cctx, ap);
2585
      break;
2586
    case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2587
      str = avd_auth_request(cctx, ap);
2588
      break;
2589
    case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2590
      str = avd_token_request(cctx, ap);
2591
      break;
2592
#endif
2593
0
    default:
2594
0
      break;
2595
0
  }
2596
0
  va_end(ap);
2597
0
  return str;
2598
0
}