Coverage Report

Created: 2026-08-31 06:29

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