Coverage Report

Created: 2026-04-12 07:03

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