Coverage Report

Created: 2026-07-30 07:11

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.38k
{
85
9.38k
  WINPR_ASSERT(instance);
86
9.38k
  instance->AuthenticateEx = client_cli_authenticate_ex;
87
9.38k
  instance->ChooseSmartcard = client_cli_choose_smartcard;
88
9.38k
  instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
89
9.38k
  instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
90
9.38k
  instance->PresentGatewayMessage = client_cli_present_gateway_message;
91
9.38k
  instance->LogonErrorInfo = client_cli_logon_error_info;
92
9.38k
  instance->GetAccessToken = client_cli_get_access_token;
93
9.38k
  instance->RetryDialog = client_common_retry_dialog;
94
95
9.38k
  WINPR_ASSERT(instance->context);
96
9.38k
  WINPR_ASSERT(instance->context->update);
97
9.38k
  instance->context->update->SaveSessionInfo = client_common_save_session_info;
98
9.38k
}
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.38k
{
115
9.38k
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = nullptr;
116
117
9.38k
  WINPR_ASSERT(instance);
118
9.38k
  WINPR_ASSERT(context);
119
120
9.38k
  instance->LoadChannels = freerdp_client_load_channels;
121
9.38k
  set_default_callbacks(instance);
122
123
9.38k
  pEntryPoints = instance->pClientEntryPoints;
124
9.38k
  WINPR_ASSERT(pEntryPoints);
125
126
9.38k
  return IFCALLRESULT(TRUE, pEntryPoints->ClientNew, instance, context);
127
9.38k
}
128
129
static void freerdp_client_common_free(freerdp* instance, rdpContext* context)
130
9.38k
{
131
9.38k
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = nullptr;
132
133
9.38k
  WINPR_ASSERT(instance);
134
9.38k
  WINPR_ASSERT(context);
135
136
9.38k
  pEntryPoints = instance->pClientEntryPoints;
137
9.38k
  WINPR_ASSERT(pEntryPoints);
138
9.38k
  IFCALL(pEntryPoints->ClientFree, instance, context);
139
9.38k
}
140
141
/* Common API */
142
143
rdpContext* freerdp_client_context_new(const RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
144
9.38k
{
145
9.38k
  freerdp* instance = nullptr;
146
9.38k
  rdpContext* context = nullptr;
147
148
9.38k
  if (!pEntryPoints)
149
0
    return nullptr;
150
151
9.38k
  if (!IFCALLRESULT(TRUE, pEntryPoints->GlobalInit))
152
0
    return nullptr;
153
154
9.38k
  instance = freerdp_new();
155
156
9.38k
  if (!instance)
157
0
    return nullptr;
158
159
9.38k
  instance->ContextSize = pEntryPoints->ContextSize;
160
9.38k
  instance->ContextNew = freerdp_client_common_new;
161
9.38k
  instance->ContextFree = freerdp_client_common_free;
162
9.38k
  instance->pClientEntryPoints = (RDP_CLIENT_ENTRY_POINTS*)malloc(pEntryPoints->Size);
163
164
9.38k
  if (!instance->pClientEntryPoints)
165
0
    goto out_fail;
166
167
9.38k
  CopyMemory(instance->pClientEntryPoints, pEntryPoints, pEntryPoints->Size);
168
169
9.38k
  if (!freerdp_context_new_ex(instance, pEntryPoints->settings))
170
0
    goto out_fail2;
171
172
9.38k
  context = instance->context;
173
9.38k
  context->instance = instance;
174
175
9.38k
#if defined(WITH_CLIENT_CHANNELS)
176
9.38k
  if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
177
9.38k
      CHANNEL_RC_OK)
178
0
    goto out_fail2;
179
9.38k
#endif
180
181
9.38k
  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.38k
{
191
9.38k
  freerdp* instance = nullptr;
192
193
9.38k
  if (!context)
194
0
    return;
195
196
9.38k
  instance = context->instance;
197
198
9.38k
  if (instance)
199
9.38k
  {
200
9.38k
    RDP_CLIENT_ENTRY_POINTS* pEntryPoints = instance->pClientEntryPoints;
201
9.38k
    freerdp_context_free(instance);
202
203
9.38k
    if (pEntryPoints)
204
9.38k
      IFCALL(pEntryPoints->GlobalUninit);
205
206
9.38k
    free(instance->pClientEntryPoints);
207
9.38k
    freerdp_free(instance);
208
9.38k
  }
209
9.38k
}
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
        if (answer == EOF)
766
0
          return 0;
767
0
        return 1;
768
769
0
      case 't':
770
0
      case 'T':
771
0
        answer = freerdp_interruptible_getc(instance->context, stdin);
772
0
        if (answer == EOF)
773
0
          return 0;
774
0
        return 2;
775
776
0
      case 'n':
777
0
      case 'N':
778
0
        answer = freerdp_interruptible_getc(instance->context, stdin);
779
0
        if (answer == EOF)
780
0
          return 0;
781
0
        return 0;
782
783
0
      default:
784
0
        break;
785
0
    }
786
787
0
    printf("\n");
788
0
  }
789
0
}
790
791
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
792
 *  when the connection requires it.
793
 *  This function will actually be called by tls_verify_certificate().
794
 *  @see rdp_client_connect() and freerdp_tls_connect()
795
 *  @deprecated Use client_cli_verify_certificate_ex
796
 *  @param instance - pointer to the rdp_freerdp structure that contains the connection settings
797
 *  @param common_name
798
 *  @param subject
799
 *  @param issuer
800
 *  @param fingerprint
801
 *  @param host_mismatch Indicates the certificate host does not match.
802
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
803
 */
804
#if defined(WITH_FREERDP_DEPRECATED)
805
DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name, const char* subject,
806
                                    const char* issuer, const char* fingerprint, BOOL host_mismatch)
807
{
808
  WINPR_UNUSED(common_name);
809
  WINPR_UNUSED(host_mismatch);
810
811
  printf("WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
812
  printf("Certificate details:\n");
813
  printf("\tSubject: %s\n", subject);
814
  printf("\tIssuer: %s\n", issuer);
815
  printf("\tThumbprint: %s\n", fingerprint);
816
  printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
817
         "the CA certificate in your certificate store, or the certificate has expired.\n"
818
         "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
819
  return client_cli_accept_certificate(instance);
820
}
821
#endif
822
823
static char* client_cli_pem_cert(const char* pem)
824
0
{
825
0
  rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
826
0
  if (!cert)
827
0
    return nullptr;
828
829
0
  char* fp = freerdp_certificate_get_fingerprint(cert);
830
0
  char* start = freerdp_certificate_get_validity(cert, TRUE);
831
0
  char* end = freerdp_certificate_get_validity(cert, FALSE);
832
0
  freerdp_certificate_free(cert);
833
834
0
  char* str = nullptr;
835
0
  size_t slen = 0;
836
0
  winpr_asprintf(&str, &slen,
837
0
                 "\tValid from:  %s\n"
838
0
                 "\tValid to:    %s\n"
839
0
                 "\tThumbprint:  %s\n",
840
0
                 start, end, fp);
841
0
  free(fp);
842
0
  free(start);
843
0
  free(end);
844
0
  return str;
845
0
}
846
847
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
848
 *  when the connection requires it.
849
 *  This function will actually be called by tls_verify_certificate().
850
 *  @see rdp_client_connect() and freerdp_tls_connect()
851
 *  @param instance     pointer to the rdp_freerdp structure that contains the connection settings
852
 *  @param host         The host currently connecting to
853
 *  @param port         The port currently connecting to
854
 *  @param common_name  The common name of the certificate, should match host or an alias of it
855
 *  @param subject      The subject of the certificate
856
 *  @param issuer       The certificate issuer name
857
 *  @param fingerprint  The fingerprint of the certificate
858
 *  @param flags        See VERIFY_CERT_FLAG_* for possible values.
859
 *
860
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
861
 */
862
DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
863
                                       const char* common_name, const char* subject,
864
                                       const char* issuer, const char* fingerprint, DWORD flags)
865
0
{
866
0
  const char* type = "RDP-Server";
867
868
0
  WINPR_ASSERT(instance);
869
0
  WINPR_ASSERT(instance->context);
870
0
  WINPR_ASSERT(instance->context->settings);
871
872
0
  if (flags & VERIFY_CERT_FLAG_GATEWAY)
873
0
    type = "RDP-Gateway";
874
875
0
  if (flags & VERIFY_CERT_FLAG_REDIRECT)
876
0
    type = "RDP-Redirect";
877
878
0
  printf("Certificate details for %s:%" PRIu16 " (%s):\n", host, port, type);
879
0
  printf("\tCommon Name: %s\n", common_name);
880
0
  printf("\tSubject:     %s\n", subject);
881
0
  printf("\tIssuer:      %s\n", issuer);
882
  /* Newer versions of FreeRDP allow exposing the whole PEM by setting
883
   * FreeRDP_CertificateCallbackPreferPEM to TRUE
884
   */
885
0
  if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
886
0
  {
887
0
    char* str = client_cli_pem_cert(fingerprint);
888
0
    printf("%s", str);
889
0
    free(str);
890
0
  }
891
0
  else
892
0
    printf("\tThumbprint:  %s\n", fingerprint);
893
894
0
  printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
895
0
         "the CA certificate in your certificate store, or the certificate has expired.\n"
896
0
         "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
897
0
  return client_cli_accept_certificate(instance);
898
0
}
899
900
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
901
 *  when a stored certificate does not match the remote counterpart.
902
 *  This function will actually be called by tls_verify_certificate().
903
 *  @see rdp_client_connect() and freerdp_tls_connect()
904
 *  @deprecated Use client_cli_verify_changed_certificate_ex
905
 *  @param instance - pointer to the rdp_freerdp structure that contains the connection settings
906
 *  @param common_name
907
 *  @param subject
908
 *  @param issuer
909
 *  @param fingerprint
910
 *  @param old_subject
911
 *  @param old_issuer
912
 *  @param old_fingerprint
913
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
914
 */
915
#if defined(WITH_FREERDP_DEPRECATED)
916
DWORD client_cli_verify_changed_certificate(freerdp* instance, const char* common_name,
917
                                            const char* subject, const char* issuer,
918
                                            const char* fingerprint, const char* old_subject,
919
                                            const char* old_issuer, const char* old_fingerprint)
920
{
921
  WINPR_UNUSED(common_name);
922
923
  printf("WARNING: This callback is deprecated, migrate to "
924
         "client_cli_verify_changed_certificate_ex\n");
925
  printf("!!! Certificate has changed !!!\n");
926
  printf("\n");
927
  printf("New Certificate details:\n");
928
  printf("\tSubject: %s\n", subject);
929
  printf("\tIssuer: %s\n", issuer);
930
  printf("\tThumbprint: %s\n", fingerprint);
931
  printf("\n");
932
  printf("Old Certificate details:\n");
933
  printf("\tSubject: %s\n", old_subject);
934
  printf("\tIssuer: %s\n", old_issuer);
935
  printf("\tThumbprint: %s\n", old_fingerprint);
936
  printf("\n");
937
  printf("The above X.509 certificate does not match the certificate used for previous "
938
         "connections.\n"
939
         "This may indicate that the certificate has been tampered with.\n"
940
         "Please contact the administrator of the RDP server and clarify.\n");
941
  return client_cli_accept_certificate(instance);
942
}
943
#endif
944
945
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
946
 *  when a stored certificate does not match the remote counterpart.
947
 *  This function will actually be called by tls_verify_certificate().
948
 *  @see rdp_client_connect() and freerdp_tls_connect()
949
 *  @param instance        pointer to the rdp_freerdp structure that contains the connection
950
 * settings
951
 *  @param host            The host currently connecting to
952
 *  @param port            The port currently connecting to
953
 *  @param common_name     The common name of the certificate, should match host or an alias of it
954
 *  @param subject         The subject of the certificate
955
 *  @param issuer          The certificate issuer name
956
 *  @param fingerprint     The fingerprint of the certificate
957
 *  @param old_subject     The subject of the previous certificate
958
 *  @param old_issuer      The previous certificate issuer name
959
 *  @param old_fingerprint The fingerprint of the previous certificate
960
 *  @param flags           See VERIFY_CERT_FLAG_* for possible values.
961
 *
962
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
963
 */
964
DWORD client_cli_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
965
                                               const char* common_name, const char* subject,
966
                                               const char* issuer, const char* fingerprint,
967
                                               const char* old_subject, const char* old_issuer,
968
                                               const char* old_fingerprint, DWORD flags)
969
0
{
970
0
  const char* type = "RDP-Server";
971
972
0
  WINPR_ASSERT(instance);
973
0
  WINPR_ASSERT(instance->context);
974
0
  WINPR_ASSERT(instance->context->settings);
975
976
0
  if (flags & VERIFY_CERT_FLAG_GATEWAY)
977
0
    type = "RDP-Gateway";
978
979
0
  if (flags & VERIFY_CERT_FLAG_REDIRECT)
980
0
    type = "RDP-Redirect";
981
982
0
  printf("!!!Certificate for %s:%" PRIu16 " (%s) has changed!!!\n", host, port, type);
983
0
  printf("\n");
984
0
  printf("New Certificate details:\n");
985
0
  printf("\tCommon Name: %s\n", common_name);
986
0
  printf("\tSubject:     %s\n", subject);
987
0
  printf("\tIssuer:      %s\n", issuer);
988
  /* Newer versions of FreeRDP allow exposing the whole PEM by setting
989
   * FreeRDP_CertificateCallbackPreferPEM to TRUE
990
   */
991
0
  if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
992
0
  {
993
0
    char* str = client_cli_pem_cert(fingerprint);
994
0
    printf("%s", str);
995
0
    free(str);
996
0
  }
997
0
  else
998
0
    printf("\tThumbprint:  %s\n", fingerprint);
999
0
  printf("\n");
1000
0
  printf("Old Certificate details:\n");
1001
0
  printf("\tSubject:     %s\n", old_subject);
1002
0
  printf("\tIssuer:      %s\n", old_issuer);
1003
  /* Newer versions of FreeRDP allow exposing the whole PEM by setting
1004
   * FreeRDP_CertificateCallbackPreferPEM to TRUE
1005
   */
1006
0
  if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
1007
0
  {
1008
0
    char* str = client_cli_pem_cert(old_fingerprint);
1009
0
    printf("%s", str);
1010
0
    free(str);
1011
0
  }
1012
0
  else
1013
0
    printf("\tThumbprint:  %s\n", old_fingerprint);
1014
0
  printf("\n");
1015
0
  if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
1016
0
  {
1017
0
    printf("\tA matching entry with legacy SHA1 was found in local known_hosts2 store.\n");
1018
0
    printf("\tIf you just upgraded from a FreeRDP version before 2.0 this is expected.\n");
1019
0
    printf("\tThe hashing algorithm has been upgraded from SHA1 to SHA256.\n");
1020
0
    printf("\tAll manually accepted certificates must be reconfirmed!\n");
1021
0
    printf("\n");
1022
0
  }
1023
0
  printf("The above X.509 certificate does not match the certificate used for previous "
1024
0
         "connections.\n"
1025
0
         "This may indicate that the certificate has been tampered with.\n"
1026
0
         "Please contact the administrator of the RDP server and clarify.\n");
1027
0
  return client_cli_accept_certificate(instance);
1028
0
}
1029
1030
BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1031
                                        BOOL isConsentMandatory, size_t length,
1032
                                        const WCHAR* message)
1033
0
{
1034
0
  const char* msgType = (type == GATEWAY_MESSAGE_CONSENT) ? "Consent message" : "Service message";
1035
1036
0
  WINPR_ASSERT(instance);
1037
0
  WINPR_ASSERT(instance->context);
1038
0
  WINPR_ASSERT(instance->context->settings);
1039
1040
0
  if (!isDisplayMandatory && !isConsentMandatory)
1041
0
    return TRUE;
1042
1043
0
  printf("%s:\n", msgType);
1044
#if defined(WIN32)
1045
  printf("%.*S\n", (int)length, message);
1046
#else
1047
0
  {
1048
0
    LPSTR msg = ConvertWCharNToUtf8Alloc(message, length / sizeof(WCHAR), nullptr);
1049
0
    if (!msg)
1050
0
    {
1051
0
      printf("Failed to convert message!\n");
1052
0
      return FALSE;
1053
0
    }
1054
0
    printf("%s\n", msg);
1055
0
    free(msg);
1056
0
  }
1057
0
#endif
1058
1059
0
  while (isConsentMandatory)
1060
0
  {
1061
0
    printf("I understand and agree to the terms of this policy (Y/N) \n");
1062
0
    (void)fflush(stdout);
1063
0
    const int answer = freerdp_interruptible_getc(instance->context, stdin);
1064
1065
0
    if ((answer == EOF) || feof(stdin))
1066
0
    {
1067
0
      printf("\nError: Could not read answer from stdin.\n");
1068
0
      return FALSE;
1069
0
    }
1070
1071
0
    const int confirm = freerdp_interruptible_getc(instance->context, stdin);
1072
0
    switch (answer)
1073
0
    {
1074
0
      case 'y':
1075
0
      case 'Y':
1076
0
        if (confirm == EOF)
1077
0
          return FALSE;
1078
0
        return TRUE;
1079
1080
0
      case 'n':
1081
0
      case 'N':
1082
0
        return FALSE;
1083
1084
0
      default:
1085
0
        break;
1086
0
    }
1087
1088
0
    printf("\n");
1089
0
  }
1090
1091
0
  return TRUE;
1092
0
}
1093
1094
static const char* extract_authorization_code(char* url)
1095
0
{
1096
0
  WINPR_ASSERT(url);
1097
0
1098
0
  for (char* p = strchr(url, '?'); p++ != nullptr; p = strchr(p, '&'))
1099
0
  {
1100
0
    if (strncmp(p, "code=", 5) != 0)
1101
0
      continue;
1102
0
1103
0
    char* end = nullptr;
1104
0
    p += 5;
1105
0
1106
0
    end = strchr(p, '&');
1107
0
    if (end)
1108
0
      *end = '\0';
1109
0
1110
0
    return p;
1111
0
  }
1112
0
1113
0
  return nullptr;
1114
0
}
1115
1116
#if defined(WITH_AAD)
1117
static BOOL client_cli_get_rdsaad_access_token(freerdp* instance, const char* scope,
1118
                                               const char* req_cnf, char** token)
1119
{
1120
  WINPR_ASSERT(instance);
1121
  WINPR_ASSERT(instance->context);
1122
1123
  size_t size = 0;
1124
  char* url = nullptr;
1125
  char* token_request = nullptr;
1126
1127
  WINPR_ASSERT(scope);
1128
  WINPR_ASSERT(req_cnf);
1129
  WINPR_ASSERT(token);
1130
1131
  BOOL rc = FALSE;
1132
  *token = nullptr;
1133
1134
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1135
                                             FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1136
1137
  printf("Browse to: %s\n", request);
1138
  free(request);
1139
  printf("Paste redirect URL here: \n");
1140
1141
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1142
    goto cleanup;
1143
1144
  {
1145
    const char* code = extract_authorization_code(url);
1146
    if (!code)
1147
      goto cleanup;
1148
    token_request =
1149
        freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1150
                                   FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1151
  }
1152
  if (!token_request)
1153
    goto cleanup;
1154
1155
  rc = client_common_get_access_token(instance, token_request, token);
1156
1157
cleanup:
1158
  free(token_request);
1159
  free(url);
1160
  return rc && (*token != nullptr);
1161
}
1162
1163
static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1164
{
1165
  WINPR_ASSERT(instance);
1166
  WINPR_ASSERT(instance->context);
1167
1168
  size_t size = 0;
1169
  char* url = nullptr;
1170
  char* token_request = nullptr;
1171
1172
  WINPR_ASSERT(token);
1173
1174
  BOOL rc = FALSE;
1175
1176
  *token = nullptr;
1177
1178
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1179
                                             FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1180
  if (!request)
1181
    return FALSE;
1182
  printf("Browse to: %s\n", request);
1183
  free(request);
1184
  printf("Paste redirect URL here: \n");
1185
1186
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1187
    goto cleanup;
1188
1189
  {
1190
    const char* code = extract_authorization_code(url);
1191
    if (!code)
1192
      goto cleanup;
1193
    token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1194
                                               FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1195
  }
1196
1197
  if (!token_request)
1198
    goto cleanup;
1199
1200
  rc = client_common_get_access_token(instance, token_request, token);
1201
1202
cleanup:
1203
  free(token_request);
1204
  free(url);
1205
  return rc && (*token != nullptr);
1206
}
1207
#endif
1208
1209
BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1210
                                 size_t count, ...)
1211
0
{
1212
0
  WINPR_ASSERT(instance);
1213
0
  WINPR_ASSERT(token);
1214
1215
0
#if !defined(WITH_AAD)
1216
0
  WLog_ERR(TAG, "Build does not support AAD authentication");
1217
0
  return FALSE;
1218
#else
1219
  BOOL rc = FALSE;
1220
  WINPR_ASSERT(instance->context);
1221
  const BOOL saved =
1222
      freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1223
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1224
                                 TRUE))
1225
    return FALSE;
1226
1227
  switch (tokenType)
1228
  {
1229
    case ACCESS_TOKEN_TYPE_AAD:
1230
    {
1231
      if (count < 2)
1232
      {
1233
        WLog_ERR(TAG,
1234
                 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1235
                 ", aborting",
1236
                 count);
1237
      }
1238
      else
1239
      {
1240
        if (count > 2)
1241
          WLog_WARN(
1242
              TAG,
1243
              "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1244
              ", ignoring",
1245
              count);
1246
        va_list ap = WINPR_C_ARRAY_INIT;
1247
        va_start(ap, count);
1248
        const char* scope = va_arg(ap, const char*);
1249
        const char* req_cnf = va_arg(ap, const char*);
1250
        rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1251
        va_end(ap);
1252
      }
1253
    }
1254
    break;
1255
    case ACCESS_TOKEN_TYPE_AVD:
1256
      if (count != 0)
1257
        WLog_WARN(TAG,
1258
                  "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1259
                  ", ignoring",
1260
                  count);
1261
      rc = client_cli_get_avd_access_token(instance, token);
1262
      break;
1263
    default:
1264
      WLog_ERR(TAG, "Unexpected value for AccessTokenType [%u], aborting", tokenType);
1265
      break;
1266
  }
1267
1268
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1269
                                 saved))
1270
    return FALSE;
1271
  return rc;
1272
#endif
1273
0
}
1274
1275
BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1276
0
{
1277
#ifdef WITH_AAD
1278
  WINPR_ASSERT(request);
1279
  WINPR_ASSERT(token);
1280
1281
  BOOL ret = FALSE;
1282
  long resp_code = 0;
1283
  BYTE* response = nullptr;
1284
  size_t response_length = 0;
1285
1286
  wLog* log = WLog_Get(TAG);
1287
1288
  const char* token_ep =
1289
      freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1290
  if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1291
  {
1292
    WLog_ERR(TAG, "access token request failed");
1293
    return FALSE;
1294
  }
1295
1296
  if (resp_code != HTTP_STATUS_OK)
1297
  {
1298
    char buffer[64] = WINPR_C_ARRAY_INIT;
1299
1300
    WLog_Print(log, WLOG_ERROR,
1301
               "Server unwilling to provide access token; returned status code %s",
1302
               freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1303
    if (response_length > 0)
1304
      WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1305
    goto cleanup;
1306
  }
1307
1308
  *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1309
  if (*token)
1310
    ret = TRUE;
1311
1312
cleanup:
1313
  free(response);
1314
  return ret;
1315
#else
1316
0
  return FALSE;
1317
0
#endif
1318
0
}
1319
1320
SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1321
                                   void* userarg)
1322
0
{
1323
0
  WINPR_UNUSED(instance);
1324
0
  WINPR_ASSERT(instance->context);
1325
0
  WINPR_UNUSED(userarg);
1326
0
  WINPR_ASSERT(instance);
1327
0
  WINPR_ASSERT(what);
1328
1329
0
  if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1330
0
  {
1331
0
    WLog_ERR(TAG, "Unknown module %s, aborting", what);
1332
0
    return -1;
1333
0
  }
1334
1335
0
  if (current == 0)
1336
0
  {
1337
0
    if (strcmp(what, "arm-transport") == 0)
1338
0
      WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1339
0
  }
1340
1341
0
  const rdpSettings* settings = instance->context->settings;
1342
0
  const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1343
0
  if (!enabled)
1344
0
  {
1345
0
    WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1346
0
    return -1;
1347
0
  }
1348
1349
0
  const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1350
0
  const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1351
0
  if (current >= max)
1352
0
  {
1353
0
    WLog_ERR(TAG,
1354
0
             "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1355
0
             "tech support for help if this keeps happening.",
1356
0
             what);
1357
0
    return -1;
1358
0
  }
1359
1360
0
  WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1361
0
            what, current + 1, max, delay);
1362
0
  return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1363
0
}
1364
1365
BOOL client_auto_reconnect(freerdp* instance)
1366
0
{
1367
0
  return client_auto_reconnect_ex(instance, nullptr);
1368
0
}
1369
1370
BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1371
0
{
1372
0
  BOOL retry = TRUE;
1373
0
  UINT32 error = 0;
1374
0
  UINT32 numRetries = 0;
1375
0
  rdpSettings* settings = nullptr;
1376
1377
0
  if (!instance)
1378
0
    return FALSE;
1379
1380
0
  WINPR_ASSERT(instance->context);
1381
1382
0
  settings = instance->context->settings;
1383
0
  WINPR_ASSERT(settings);
1384
1385
0
  const UINT32 maxRetries =
1386
0
      freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1387
1388
  /* Only auto reconnect on network disconnects. */
1389
0
  error = freerdp_error_info(instance);
1390
0
  switch (error)
1391
0
  {
1392
0
    case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1393
      /* A network disconnect was detected */
1394
0
      WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1395
0
                freerdp_get_error_info_string(error));
1396
0
      break;
1397
0
    case ERRINFO_SUCCESS:
1398
      /* A network disconnect was detected */
1399
0
      WLog_INFO(TAG, "Network disconnect!");
1400
0
      break;
1401
0
    default:
1402
0
      WLog_DBG(TAG, "Other error: %s", freerdp_get_error_info_string(error));
1403
0
      return FALSE;
1404
0
  }
1405
1406
0
  if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1407
0
  {
1408
    /* No auto-reconnect - just quit */
1409
0
    WLog_DBG(TAG, "AutoReconnect not enabled, quitting.");
1410
0
    return FALSE;
1411
0
  }
1412
1413
0
  const UINT err = freerdp_get_last_error(instance->context);
1414
0
  switch (err)
1415
0
  {
1416
0
    case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
1417
0
    case FREERDP_ERROR_CONNECT_CLIENT_REVOKED:
1418
0
    case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
1419
0
    case FREERDP_ERROR_CONNECT_ACCESS_DENIED:
1420
0
    case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION:
1421
0
    case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
1422
0
    case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
1423
0
    case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS:
1424
0
      WLog_WARN(TAG, "Connection aborted: credentials do not work [%s]",
1425
0
                freerdp_get_last_error_name(err));
1426
0
      return FALSE;
1427
0
    case FREERDP_ERROR_CONNECT_CANCELLED:
1428
0
      WLog_WARN(TAG, "Connection aborted by user");
1429
0
      return FALSE;
1430
0
    default:
1431
0
      break;
1432
0
  }
1433
1434
  /* Perform an auto-reconnect. */
1435
0
  while (retry)
1436
0
  {
1437
    /* Quit retrying if max retries has been exceeded */
1438
0
    if ((maxRetries > 0) && (numRetries >= maxRetries))
1439
0
    {
1440
0
      WLog_DBG(TAG, "AutoReconnect retries exceeded.");
1441
0
      return FALSE;
1442
0
    }
1443
1444
    /* Attempt the next reconnect */
1445
0
    WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1446
1447
0
    const SSIZE_T delay =
1448
0
        IFCALLRESULT(5000, instance->RetryDialog, instance, "connection", numRetries, nullptr);
1449
0
    if (delay < 0)
1450
0
      return FALSE;
1451
0
    numRetries++;
1452
1453
0
    if (freerdp_reconnect(instance))
1454
0
      return TRUE;
1455
1456
0
    switch (freerdp_get_last_error(instance->context))
1457
0
    {
1458
0
      case FREERDP_ERROR_CONNECT_CANCELLED:
1459
0
        WLog_WARN(TAG, "Autoreconnect aborted by user");
1460
0
        return FALSE;
1461
0
      default:
1462
0
        break;
1463
0
    }
1464
0
    for (SSIZE_T x = 0; x < delay / 10; x++)
1465
0
    {
1466
0
      if (!IFCALLRESULT(TRUE, window_events, instance))
1467
0
      {
1468
0
        WLog_ERR(TAG, "window_events failed!");
1469
0
        return FALSE;
1470
0
      }
1471
1472
0
      Sleep(10);
1473
0
    }
1474
0
  }
1475
1476
0
  WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1477
0
  return FALSE;
1478
0
}
1479
1480
int freerdp_client_common_stop(rdpContext* context)
1481
0
{
1482
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1483
0
  WINPR_ASSERT(cctx);
1484
1485
0
  freerdp_abort_connect_context(&cctx->context);
1486
1487
0
  if (cctx->thread)
1488
0
  {
1489
0
    (void)WaitForSingleObject(cctx->thread, INFINITE);
1490
0
    (void)CloseHandle(cctx->thread);
1491
0
    cctx->thread = nullptr;
1492
0
  }
1493
1494
0
  return 0;
1495
0
}
1496
1497
#if defined(CHANNEL_ENCOMSP_CLIENT)
1498
BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1499
0
{
1500
0
  rdpClientContext* cctx = nullptr;
1501
0
  BOOL state = 0;
1502
1503
0
  if (!encomsp)
1504
0
    return FALSE;
1505
1506
0
  cctx = (rdpClientContext*)encomsp->custom;
1507
1508
0
  state = cctx->controlToggle;
1509
0
  cctx->controlToggle = !cctx->controlToggle;
1510
0
  return freerdp_client_encomsp_set_control(encomsp, state);
1511
0
}
1512
1513
BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1514
0
{
1515
0
  ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = WINPR_C_ARRAY_INIT;
1516
1517
0
  if (!encomsp)
1518
0
    return FALSE;
1519
1520
0
  pdu.ParticipantId = encomsp->participantId;
1521
0
  pdu.Flags = ENCOMSP_REQUEST_VIEW;
1522
1523
0
  if (control)
1524
0
    pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1525
1526
0
  const UINT rc = encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1527
0
  return rc == CHANNEL_RC_OK;
1528
0
}
1529
1530
static UINT
1531
client_encomsp_participant_created(EncomspClientContext* context,
1532
                                   const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1533
0
{
1534
0
  rdpClientContext* cctx = nullptr;
1535
0
  rdpSettings* settings = nullptr;
1536
0
  BOOL request = 0;
1537
1538
0
  if (!context || !context->custom || !participantCreated)
1539
0
    return ERROR_INVALID_PARAMETER;
1540
1541
0
  cctx = (rdpClientContext*)context->custom;
1542
0
  WINPR_ASSERT(cctx);
1543
1544
0
  settings = cctx->context.settings;
1545
0
  WINPR_ASSERT(settings);
1546
1547
0
  if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1548
0
    context->participantId = participantCreated->ParticipantId;
1549
1550
0
  request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1551
0
  if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1552
0
      !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1553
0
  {
1554
0
    if (!freerdp_client_encomsp_set_control(context, TRUE))
1555
0
      return ERROR_INTERNAL_ERROR;
1556
1557
    /* if auto-request-control setting is enabled then only request control once upon connect,
1558
     * otherwise it will auto request control again every time server turns off control which
1559
     * is a bit annoying */
1560
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1561
0
      return ERROR_INTERNAL_ERROR;
1562
0
  }
1563
1564
0
  return CHANNEL_RC_OK;
1565
0
}
1566
1567
static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1568
0
{
1569
0
  cctx->encomsp = encomsp;
1570
0
  encomsp->custom = (void*)cctx;
1571
0
  encomsp->ParticipantCreated = client_encomsp_participant_created;
1572
0
}
1573
1574
static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1575
0
{
1576
0
  if (encomsp)
1577
0
  {
1578
0
    encomsp->custom = nullptr;
1579
0
    encomsp->ParticipantCreated = nullptr;
1580
0
  }
1581
1582
0
  if (cctx)
1583
0
    cctx->encomsp = nullptr;
1584
0
}
1585
#endif
1586
1587
void freerdp_client_OnChannelConnectedEventHandler(void* context,
1588
                                                   const ChannelConnectedEventArgs* e)
1589
0
{
1590
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1591
1592
0
  WINPR_ASSERT(cctx);
1593
0
  WINPR_ASSERT(e);
1594
1595
0
  if (0)
1596
0
  {
1597
0
  }
1598
0
#if defined(CHANNEL_AINPUT_CLIENT)
1599
0
  else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1600
0
    cctx->ainput = (AInputClientContext*)e->pInterface;
1601
0
#endif
1602
0
#if defined(CHANNEL_RDPEI_CLIENT)
1603
0
  else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1604
0
  {
1605
0
    cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1606
0
  }
1607
0
#endif
1608
0
#if defined(CHANNEL_RDPGFX_CLIENT)
1609
0
  else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1610
0
  {
1611
0
    gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1612
0
  }
1613
0
#endif
1614
0
#if defined(CHANNEL_GEOMETRY_CLIENT)
1615
0
  else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1616
0
  {
1617
0
    gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1618
0
  }
1619
0
#endif
1620
0
#if defined(CHANNEL_VIDEO_CLIENT)
1621
0
  else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1622
0
  {
1623
0
    gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1624
0
  }
1625
0
  else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1626
0
  {
1627
0
    gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1628
0
  }
1629
0
#endif
1630
0
#if defined(CHANNEL_ENCOMSP_CLIENT)
1631
0
  else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1632
0
  {
1633
0
    client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1634
0
  }
1635
0
#endif
1636
0
}
1637
1638
void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1639
                                                      const ChannelDisconnectedEventArgs* e)
1640
0
{
1641
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1642
1643
0
  WINPR_ASSERT(cctx);
1644
0
  WINPR_ASSERT(e);
1645
1646
0
  if (0)
1647
0
  {
1648
0
  }
1649
0
#if defined(CHANNEL_AINPUT_CLIENT)
1650
0
  else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1651
0
    cctx->ainput = nullptr;
1652
0
#endif
1653
0
#if defined(CHANNEL_RDPEI_CLIENT)
1654
0
  else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1655
0
  {
1656
0
    cctx->rdpei = nullptr;
1657
0
  }
1658
0
#endif
1659
0
#if defined(CHANNEL_RDPGFX_CLIENT)
1660
0
  else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1661
0
  {
1662
0
    gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1663
0
  }
1664
0
#endif
1665
0
#if defined(CHANNEL_GEOMETRY_CLIENT)
1666
0
  else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1667
0
  {
1668
0
    gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1669
0
  }
1670
0
#endif
1671
0
#if defined(CHANNEL_VIDEO_CLIENT)
1672
0
  else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1673
0
  {
1674
0
    gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1675
0
  }
1676
0
  else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1677
0
  {
1678
0
    gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1679
0
  }
1680
0
#endif
1681
0
#if defined(CHANNEL_ENCOMSP_CLIENT)
1682
0
  else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1683
0
  {
1684
0
    client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1685
0
  }
1686
0
#endif
1687
0
}
1688
1689
BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1690
0
{
1691
0
  BOOL handled = FALSE;
1692
1693
0
  WINPR_ASSERT(cctx);
1694
1695
0
  const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1696
0
  if (state != CONNECTION_STATE_ACTIVE)
1697
0
    return TRUE;
1698
1699
0
#if defined(CHANNEL_AINPUT_CLIENT)
1700
0
  if (cctx->ainput)
1701
0
  {
1702
0
    UINT rc = 0;
1703
0
    UINT64 flags = 0;
1704
0
    INT32 x = 0;
1705
0
    INT32 y = 0;
1706
0
    INT32 value = mflags & 0xFF;
1707
1708
0
    if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1709
0
      value = -1 * (0x100 - value);
1710
1711
    /* We have discrete steps, scale this so we can also support high
1712
     * resolution wheels. */
1713
0
    value *= 0x10000;
1714
1715
0
    if (mflags & PTR_FLAGS_WHEEL)
1716
0
    {
1717
0
      flags |= AINPUT_FLAGS_WHEEL;
1718
0
      y = value;
1719
0
    }
1720
1721
0
    if (mflags & PTR_FLAGS_HWHEEL)
1722
0
    {
1723
0
      flags |= AINPUT_FLAGS_WHEEL;
1724
0
      x = value;
1725
0
    }
1726
1727
0
    WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1728
0
    rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1729
0
    if (rc == CHANNEL_RC_OK)
1730
0
      handled = TRUE;
1731
0
  }
1732
0
#endif
1733
1734
0
  if (!handled)
1735
0
    return freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1736
1737
0
  return TRUE;
1738
0
}
1739
1740
#if defined(CHANNEL_AINPUT_CLIENT)
1741
static inline BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1742
0
{
1743
0
  UINT rc = 0;
1744
1745
0
  WINPR_ASSERT(cctx);
1746
0
  WINPR_ASSERT(cctx->ainput);
1747
0
  WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1748
1749
0
  rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1750
1751
0
  return rc == CHANNEL_RC_OK;
1752
0
}
1753
#endif
1754
1755
static bool button_pressed(const rdpClientContext* cctx)
1756
0
{
1757
0
  WINPR_ASSERT(cctx);
1758
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1759
0
  {
1760
0
    const BOOL cur = cctx->pressed_buttons[x];
1761
0
    if (cur)
1762
0
      return true;
1763
0
  }
1764
0
  return false;
1765
0
}
1766
1767
BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1768
                                      INT32 y)
1769
0
{
1770
0
  BOOL handled = FALSE;
1771
1772
0
  WINPR_ASSERT(cctx);
1773
0
  const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1774
0
  if (state != CONNECTION_STATE_ACTIVE)
1775
0
    return TRUE;
1776
1777
0
  if (mflags & PTR_FLAGS_BUTTON1)
1778
0
    cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1779
0
  if (mflags & PTR_FLAGS_BUTTON2)
1780
0
    cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1781
0
  if (mflags & PTR_FLAGS_BUTTON3)
1782
0
    cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1783
1784
0
  if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1785
0
      !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1786
0
  {
1787
0
    if (!button_pressed(cctx))
1788
0
      return TRUE;
1789
0
  }
1790
1791
0
  const BOOL haveRelative =
1792
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1793
0
  if (relative && haveRelative)
1794
0
  {
1795
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1796
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1797
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1798
0
  }
1799
1800
0
#if defined(CHANNEL_AINPUT_CLIENT)
1801
0
  if (cctx->ainput)
1802
0
  {
1803
0
    UINT64 flags = 0;
1804
1805
0
    if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1806
0
      flags |= AINPUT_FLAGS_HAVE_REL;
1807
1808
0
    if (relative)
1809
0
      flags |= AINPUT_FLAGS_REL;
1810
1811
0
    if (mflags & PTR_FLAGS_DOWN)
1812
0
      flags |= AINPUT_FLAGS_DOWN;
1813
0
    if (mflags & PTR_FLAGS_BUTTON1)
1814
0
      flags |= AINPUT_FLAGS_BUTTON1;
1815
0
    if (mflags & PTR_FLAGS_BUTTON2)
1816
0
      flags |= AINPUT_FLAGS_BUTTON2;
1817
0
    if (mflags & PTR_FLAGS_BUTTON3)
1818
0
      flags |= AINPUT_FLAGS_BUTTON3;
1819
0
    if (mflags & PTR_FLAGS_MOVE)
1820
0
      flags |= AINPUT_FLAGS_MOVE;
1821
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1822
0
  }
1823
0
#endif
1824
1825
0
  if (!handled)
1826
0
  {
1827
0
    if (relative)
1828
0
    {
1829
0
      cctx->lastX += x;
1830
0
      cctx->lastY += y;
1831
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1832
0
    }
1833
0
    else
1834
0
    {
1835
0
      cctx->lastX = x;
1836
0
      cctx->lastY = y;
1837
0
    }
1838
0
    return freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1839
0
                                          (UINT16)cctx->lastY);
1840
0
  }
1841
0
  return TRUE;
1842
0
}
1843
1844
BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1845
                                               INT32 x, INT32 y)
1846
0
{
1847
0
  BOOL handled = FALSE;
1848
0
  WINPR_ASSERT(cctx);
1849
1850
0
  const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
1851
0
  if (state != CONNECTION_STATE_ACTIVE)
1852
0
    return TRUE;
1853
1854
0
  if (mflags & PTR_XFLAGS_BUTTON1)
1855
0
    cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1856
0
  if (mflags & PTR_XFLAGS_BUTTON2)
1857
0
    cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1858
1859
0
  const BOOL haveRelative =
1860
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1861
0
  if (relative && haveRelative)
1862
0
  {
1863
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1864
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1865
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1866
0
  }
1867
1868
0
#if defined(CHANNEL_AINPUT_CLIENT)
1869
0
  if (cctx->ainput)
1870
0
  {
1871
0
    UINT64 flags = 0;
1872
1873
0
    if (relative)
1874
0
      flags |= AINPUT_FLAGS_REL;
1875
0
    if (mflags & PTR_XFLAGS_DOWN)
1876
0
      flags |= AINPUT_FLAGS_DOWN;
1877
0
    if (mflags & PTR_XFLAGS_BUTTON1)
1878
0
      flags |= AINPUT_XFLAGS_BUTTON1;
1879
0
    if (mflags & PTR_XFLAGS_BUTTON2)
1880
0
      flags |= AINPUT_XFLAGS_BUTTON2;
1881
1882
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1883
0
  }
1884
0
#endif
1885
1886
0
  if (!handled)
1887
0
  {
1888
0
    if (relative)
1889
0
    {
1890
0
      cctx->lastX += x;
1891
0
      cctx->lastY += y;
1892
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1893
0
    }
1894
0
    else
1895
0
    {
1896
0
      cctx->lastX = x;
1897
0
      cctx->lastY = y;
1898
0
    }
1899
0
    freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1900
0
                                            (UINT16)cctx->lastY);
1901
0
  }
1902
1903
0
  return TRUE;
1904
0
}
1905
1906
static BOOL freerdp_handle_touch_to_mouse(rdpClientContext* cctx, BOOL down,
1907
                                          const FreeRDP_TouchContact* contact)
1908
0
{
1909
0
  const UINT16 flags = PTR_FLAGS_MOVE | (down ? PTR_FLAGS_DOWN : 0);
1910
0
  const UINT16 xflags = down ? PTR_XFLAGS_DOWN : 0;
1911
0
  WINPR_ASSERT(contact);
1912
0
  WINPR_ASSERT(contact->x <= UINT16_MAX);
1913
0
  WINPR_ASSERT(contact->y <= UINT16_MAX);
1914
1915
0
  switch (contact->count)
1916
0
  {
1917
0
    case 1:
1918
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON1,
1919
0
                                              contact->x, contact->y);
1920
0
    case 2:
1921
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON2,
1922
0
                                              contact->x, contact->y);
1923
0
    case 3:
1924
0
      return freerdp_client_send_button_event(cctx, FALSE, flags | PTR_FLAGS_BUTTON3,
1925
0
                                              contact->x, contact->y);
1926
0
    case 4:
1927
0
      return freerdp_client_send_extended_button_event(
1928
0
          cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1929
0
    case 5:
1930
0
      return freerdp_client_send_extended_button_event(
1931
0
          cctx, FALSE, xflags | PTR_XFLAGS_BUTTON1, contact->x, contact->y);
1932
0
    default:
1933
      /* unmapped events, ignore */
1934
0
      return TRUE;
1935
0
  }
1936
0
}
1937
1938
static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1939
0
{
1940
0
  WINPR_ASSERT(cctx);
1941
0
  WINPR_ASSERT(contact);
1942
1943
0
#if defined(CHANNEL_RDPEI_CLIENT)
1944
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1945
1946
0
  if (!rdpei)
1947
0
    return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1948
1949
0
  int contactId = 0;
1950
1951
0
  if (rdpei->TouchRawEvent)
1952
0
  {
1953
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1954
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1955
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
1956
0
                                    : 0;
1957
    // Ensure contact position is unchanged from "engaged" to "out of range" state
1958
0
    const UINT rc1 =
1959
0
        rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1960
0
                             RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1961
0
                                 RDPINPUT_CONTACT_FLAG_INCONTACT,
1962
0
                             contactFlags, contact->pressure);
1963
0
    if (rc1 != CHANNEL_RC_OK)
1964
0
      return FALSE;
1965
1966
0
    const UINT rc2 = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y,
1967
0
                                          &contactId, flags, contactFlags, contact->pressure);
1968
0
    if (rc2 != CHANNEL_RC_OK)
1969
0
      return FALSE;
1970
0
  }
1971
0
  else
1972
0
  {
1973
0
    WINPR_ASSERT(rdpei->TouchEnd);
1974
0
    const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1975
0
    if (rc != CHANNEL_RC_OK)
1976
0
      return FALSE;
1977
0
  }
1978
0
  return TRUE;
1979
#else
1980
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1981
                 "-DCHANNEL_RDPEI_CLIENT=ON");
1982
  return freerdp_handle_touch_to_mouse(cctx, FALSE, contact);
1983
#endif
1984
0
}
1985
1986
static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1987
0
{
1988
0
  WINPR_ASSERT(cctx);
1989
0
  WINPR_ASSERT(contact);
1990
1991
0
#if defined(CHANNEL_RDPEI_CLIENT)
1992
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1993
1994
  // Emulate mouse click if touch is not possible, like in login screen
1995
0
  if (!rdpei)
1996
0
    return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
1997
1998
0
  int contactId = 0;
1999
2000
0
  if (rdpei->TouchRawEvent)
2001
0
  {
2002
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
2003
0
                         RDPINPUT_CONTACT_FLAG_INCONTACT;
2004
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2005
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
2006
0
                                    : 0;
2007
0
    const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2008
0
                                         flags, contactFlags, contact->pressure);
2009
0
    if (rc != CHANNEL_RC_OK)
2010
0
      return FALSE;
2011
0
  }
2012
0
  else
2013
0
  {
2014
0
    WINPR_ASSERT(rdpei->TouchBegin);
2015
0
    const UINT rc = rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
2016
0
    if (rc != CHANNEL_RC_OK)
2017
0
      return FALSE;
2018
0
  }
2019
2020
0
  return TRUE;
2021
#else
2022
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2023
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2024
  return freerdp_handle_touch_to_mouse(cctx, TRUE, contact);
2025
#endif
2026
0
}
2027
2028
static BOOL freerdp_handle_touch_motion_to_mouse(rdpClientContext* cctx,
2029
                                                 const FreeRDP_TouchContact* contact)
2030
0
{
2031
0
  const UINT16 flags = PTR_FLAGS_MOVE;
2032
2033
0
  WINPR_ASSERT(contact);
2034
0
  WINPR_ASSERT(contact->x <= UINT16_MAX);
2035
0
  WINPR_ASSERT(contact->y <= UINT16_MAX);
2036
0
  return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
2037
0
}
2038
2039
static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2040
0
{
2041
0
  WINPR_ASSERT(cctx);
2042
0
  WINPR_ASSERT(contact);
2043
2044
0
#if defined(CHANNEL_RDPEI_CLIENT)
2045
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2046
2047
0
  if (!rdpei)
2048
0
    return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2049
2050
0
  int contactId = 0;
2051
2052
0
  if (rdpei->TouchRawEvent)
2053
0
  {
2054
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
2055
0
                         RDPINPUT_CONTACT_FLAG_INCONTACT;
2056
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2057
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
2058
0
                                    : 0;
2059
0
    const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2060
0
                                         flags, contactFlags, contact->pressure);
2061
0
    if (rc != CHANNEL_RC_OK)
2062
0
      return FALSE;
2063
0
  }
2064
0
  else
2065
0
  {
2066
0
    WINPR_ASSERT(rdpei->TouchUpdate);
2067
0
    const UINT rc = rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
2068
0
    if (rc != CHANNEL_RC_OK)
2069
0
      return FALSE;
2070
0
  }
2071
2072
0
  return TRUE;
2073
#else
2074
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2075
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2076
  return freerdp_handle_touch_motion_to_mouse(cctx, contact);
2077
#endif
2078
0
}
2079
2080
static BOOL freerdp_handle_touch_cancel(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
2081
0
{
2082
0
  WINPR_ASSERT(cctx);
2083
0
  WINPR_ASSERT(contact);
2084
2085
0
#if defined(CHANNEL_RDPEI_CLIENT)
2086
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2087
2088
0
  if (!rdpei)
2089
0
    return freerdp_handle_touch_to_mouse(cctx, false, contact);
2090
2091
0
  int contactId = 0;
2092
2093
0
  if (rdpei->TouchRawEvent)
2094
0
  {
2095
0
    const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_CANCELED;
2096
0
    const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
2097
0
                                    ? CONTACT_DATA_PRESSURE_PRESENT
2098
0
                                    : 0;
2099
0
    const UINT rc = rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
2100
0
                                         flags, contactFlags, contact->pressure);
2101
0
    if (rc != CHANNEL_RC_OK)
2102
0
      return FALSE;
2103
0
  }
2104
0
  else
2105
0
  {
2106
0
    WINPR_ASSERT(rdpei->TouchUpdate);
2107
0
    const UINT rc = rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
2108
0
    if (rc != CHANNEL_RC_OK)
2109
0
      return FALSE;
2110
0
  }
2111
2112
0
  return TRUE;
2113
#else
2114
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
2115
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2116
  return freerdp_handle_touch_to_mouse(cctx, false, contact);
2117
#endif
2118
0
}
2119
2120
static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
2121
                                        UINT32 pressure, INT32 x, INT32 y,
2122
                                        FreeRDP_TouchContact* pcontact)
2123
0
{
2124
0
  WINPR_ASSERT(cctx);
2125
0
  WINPR_ASSERT(pcontact);
2126
2127
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
2128
0
  {
2129
0
    FreeRDP_TouchContact* contact = &cctx->contacts[i];
2130
2131
0
    const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
2132
0
    if (newcontact || (contact->id == touchId))
2133
0
    {
2134
0
      contact->id = touchId;
2135
0
      contact->flags = flags;
2136
0
      contact->pressure = pressure;
2137
0
      contact->x = x;
2138
0
      contact->y = y;
2139
2140
0
      *pcontact = *contact;
2141
2142
0
      const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
2143
0
      if (resetcontact)
2144
0
      {
2145
0
        FreeRDP_TouchContact empty = WINPR_C_ARRAY_INIT;
2146
0
        *contact = empty;
2147
0
      }
2148
0
      return TRUE;
2149
0
    }
2150
0
  }
2151
2152
0
  return FALSE;
2153
0
}
2154
2155
BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
2156
                                 UINT32 pressure, INT32 x, INT32 y)
2157
0
{
2158
0
  const UINT32 mask =
2159
0
      FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION | FREERDP_TOUCH_CANCEL;
2160
0
  WINPR_ASSERT(cctx);
2161
2162
0
  const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2163
0
  if (state != CONNECTION_STATE_ACTIVE)
2164
0
    return TRUE;
2165
2166
0
  FreeRDP_TouchContact contact = WINPR_C_ARRAY_INIT;
2167
2168
0
  if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
2169
0
    return FALSE;
2170
2171
0
  switch (flags & mask)
2172
0
  {
2173
0
    case FREERDP_TOUCH_DOWN:
2174
0
      return freerdp_handle_touch_down(cctx, &contact);
2175
0
    case FREERDP_TOUCH_UP:
2176
0
      return freerdp_handle_touch_up(cctx, &contact);
2177
0
    case FREERDP_TOUCH_MOTION:
2178
0
      return freerdp_handle_touch_motion(cctx, &contact);
2179
0
    case FREERDP_TOUCH_CANCEL:
2180
0
      return freerdp_handle_touch_cancel(cctx, &contact);
2181
0
    default:
2182
0
      WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %" PRIu32 ", ignoring", flags);
2183
0
      return FALSE;
2184
0
  }
2185
0
}
2186
2187
BOOL freerdp_client_load_channels(freerdp* instance)
2188
0
{
2189
0
  WINPR_ASSERT(instance);
2190
0
  WINPR_ASSERT(instance->context);
2191
2192
0
  if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2193
0
  {
2194
0
    WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2195
0
    return FALSE;
2196
0
  }
2197
0
  return TRUE;
2198
0
}
2199
2200
int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2201
143
{
2202
143
  const char* str_data = freerdp_get_logon_error_info_data(data);
2203
143
  const char* str_type = freerdp_get_logon_error_info_type(type);
2204
2205
143
  if (!instance || !instance->context)
2206
0
    return -1;
2207
2208
143
  WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2209
143
  return 1;
2210
143
}
2211
2212
static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2213
                                                 size_t* pos)
2214
0
{
2215
0
  WINPR_ASSERT(cctx);
2216
2217
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2218
0
  {
2219
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2220
0
    if (deviceid == pen->deviceid)
2221
0
    {
2222
0
      if (pos)
2223
0
        *pos = i;
2224
0
      return pen;
2225
0
    }
2226
0
  }
2227
0
  return nullptr;
2228
0
}
2229
2230
static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2231
                                        double pressure)
2232
0
{
2233
0
  static const INT32 null_deviceid = 0;
2234
2235
0
  WINPR_ASSERT(cctx);
2236
0
  WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2237
0
  if (freerdp_client_is_pen(cctx, deviceid))
2238
0
  {
2239
0
    WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2240
0
    return FALSE;
2241
0
  }
2242
2243
0
  size_t pos = 0;
2244
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2245
0
  if (pen)
2246
0
  {
2247
0
    const FreeRDP_PenDevice empty = WINPR_C_ARRAY_INIT;
2248
0
    *pen = empty;
2249
2250
0
    pen->deviceid = deviceid;
2251
0
    pen->max_pressure = pressure;
2252
0
    pen->flags = flags;
2253
2254
0
    WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2255
0
    return TRUE;
2256
0
  }
2257
2258
0
  WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2259
0
  return TRUE;
2260
0
}
2261
2262
BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2263
0
{
2264
0
  const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2265
0
  if (state != CONNECTION_STATE_ACTIVE)
2266
0
    return TRUE;
2267
2268
0
#if defined(CHANNEL_RDPEI_CLIENT)
2269
0
  if ((flags & FREERDP_PEN_REGISTER) != 0)
2270
0
  {
2271
0
    va_list args = WINPR_C_ARRAY_INIT;
2272
2273
0
    va_start(args, deviceid);
2274
0
    double pressure = va_arg(args, double);
2275
0
    va_end(args);
2276
0
    return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2277
0
  }
2278
0
  size_t pos = 0;
2279
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2280
0
  if (!pen)
2281
0
  {
2282
0
    WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2283
0
    return FALSE;
2284
0
  }
2285
2286
0
  UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2287
0
  UINT32 penFlags =
2288
0
      ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2289
2290
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2291
0
  WINPR_ASSERT(rdpei);
2292
2293
0
  UINT32 normalizedpressure = 1024;
2294
0
  INT32 x = 0;
2295
0
  INT32 y = 0;
2296
0
  UINT16 rotation = 0;
2297
0
  INT16 tiltX = 0;
2298
0
  INT16 tiltY = 0;
2299
0
  va_list args = WINPR_C_ARRAY_INIT;
2300
0
  va_start(args, deviceid);
2301
2302
0
  x = va_arg(args, INT32);
2303
0
  y = va_arg(args, INT32);
2304
0
  if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2305
0
  {
2306
0
    const double pressure = va_arg(args, double);
2307
0
    const double np = (pressure * 1024.0) / pen->max_pressure;
2308
0
    normalizedpressure = (UINT32)lround(np);
2309
0
    WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2310
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2311
0
  }
2312
0
  if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2313
0
  {
2314
0
    const unsigned arg = va_arg(args, unsigned);
2315
0
    rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2316
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2317
0
  }
2318
0
  if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2319
0
  {
2320
0
    const int arg = va_arg(args, int);
2321
0
    tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2322
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2323
0
  }
2324
0
  if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2325
0
  {
2326
0
    const int arg = va_arg(args, int);
2327
0
    tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2328
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2329
0
  }
2330
0
  va_end(args);
2331
2332
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2333
0
  {
2334
    // Ensure that only one button is pressed
2335
0
    if (pen->pressed)
2336
0
      flags = FREERDP_PEN_MOTION |
2337
0
              (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2338
0
    else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2339
0
      pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2340
0
  }
2341
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2342
0
  {
2343
0
    if (!pen->pressed ||
2344
0
        ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2345
0
      flags = FREERDP_PEN_MOTION |
2346
0
              (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2347
0
    else
2348
0
      pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2349
0
  }
2350
2351
0
  flags |= pen->flags;
2352
0
  if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2353
0
    penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2354
0
  if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2355
0
    penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2356
2357
0
  pen->last_x = x;
2358
0
  pen->last_y = y;
2359
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2360
0
  {
2361
0
    WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2362
0
    pen->hovering = FALSE;
2363
0
    pen->pressed = TRUE;
2364
2365
0
    WINPR_ASSERT(rdpei->PenBegin);
2366
0
    const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2367
0
                                    normalizedpressure, rotation, tiltX, tiltY);
2368
0
    return rc == CHANNEL_RC_OK;
2369
0
  }
2370
0
  else if ((flags & FREERDP_PEN_MOTION) != 0)
2371
0
  {
2372
0
    UINT rc = ERROR_INTERNAL_ERROR;
2373
0
    if (pen->pressed)
2374
0
    {
2375
0
      WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2376
2377
      // TODO: what if no rotation is supported but tilt is?
2378
0
      WINPR_ASSERT(rdpei->PenUpdate);
2379
0
      rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2380
0
                            rotation, tiltX, tiltY);
2381
0
    }
2382
0
    else if (pen->hovering)
2383
0
    {
2384
0
      WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2385
2386
0
      WINPR_ASSERT(rdpei->PenHoverUpdate);
2387
0
      rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2388
0
                                 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2389
0
    }
2390
0
    else
2391
0
    {
2392
0
      WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2393
0
      pen->hovering = TRUE;
2394
2395
0
      WINPR_ASSERT(rdpei->PenHoverBegin);
2396
0
      rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2397
0
                                penFlags, normalizedpressure, rotation, tiltX, tiltY);
2398
0
    }
2399
0
    return rc == CHANNEL_RC_OK;
2400
0
  }
2401
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2402
0
  {
2403
0
    WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2404
0
    pen->pressed = FALSE;
2405
0
    pen->hovering = TRUE;
2406
2407
0
    WINPR_ASSERT(rdpei->PenUpdate);
2408
0
    const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2409
0
                                     normalizedpressure, rotation, tiltX, tiltY);
2410
0
    if (rc != CHANNEL_RC_OK)
2411
0
      return FALSE;
2412
0
    WINPR_ASSERT(rdpei->PenEnd);
2413
0
    const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2414
0
                                  penFlags, normalizedpressure, rotation, tiltX, tiltY);
2415
0
    return re == CHANNEL_RC_OK;
2416
0
  }
2417
2418
0
  WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2419
#else
2420
  WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2421
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2422
#endif
2423
2424
0
  return FALSE;
2425
0
}
2426
2427
BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2428
0
{
2429
0
  WINPR_ASSERT(cctx);
2430
2431
0
  const CONNECTION_STATE state = freerdp_get_state(&cctx->context);
2432
0
  if (state != CONNECTION_STATE_ACTIVE)
2433
0
    return TRUE;
2434
2435
0
#if defined(CHANNEL_RDPEI_CLIENT)
2436
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2437
2438
0
  if (!rdpei)
2439
0
    return FALSE;
2440
2441
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2442
0
  {
2443
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2444
0
    if (pen->hovering)
2445
0
    {
2446
0
      WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2447
0
      pen->hovering = FALSE;
2448
0
      const UINT rc =
2449
0
          rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2450
0
      if (rc != CHANNEL_RC_OK)
2451
0
        return FALSE;
2452
0
    }
2453
0
  }
2454
0
  return TRUE;
2455
#else
2456
  WLog_WARN(TAG, "Pen event detected but RDPEI support not compiled in. Recompile with "
2457
                 "-DCHANNEL_RDPEI_CLIENT=ON");
2458
  return FALSE;
2459
#endif
2460
0
}
2461
2462
BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2463
0
{
2464
0
  WINPR_ASSERT(cctx);
2465
2466
0
  if (deviceid == 0)
2467
0
    return FALSE;
2468
2469
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2470
0
  {
2471
0
    const FreeRDP_PenDevice* pen = &cctx->pens[x];
2472
0
    if (pen->deviceid == deviceid)
2473
0
      return TRUE;
2474
0
  }
2475
2476
0
  return FALSE;
2477
0
}
2478
2479
BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* cctx)
2480
0
{
2481
0
  WINPR_ASSERT(cctx);
2482
2483
0
  const rdpSettings* settings = cctx->context.settings;
2484
0
  const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2485
0
  const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2486
0
  BOOL ainput = FALSE;
2487
0
#if defined(CHANNEL_AINPUT_CLIENT)
2488
0
  ainput = cctx->ainput != nullptr;
2489
0
#endif
2490
2491
0
  return useRelative && (haveRelative || ainput);
2492
0
}
2493
2494
#if defined(WITH_AAD)
2495
WINPR_ATTR_MALLOC(free, 1)
2496
static char* get_redirect_uri(const rdpSettings* settings)
2497
{
2498
  char* redirect_uri = nullptr;
2499
  const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2500
  if (cli)
2501
  {
2502
    const char* redirect_fmt =
2503
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2504
    const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2505
    const char* tenantid = "common";
2506
    if (useTenant)
2507
      tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2508
2509
    if (tenantid && redirect_fmt)
2510
    {
2511
      const char* url =
2512
          freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2513
2514
      size_t redirect_len = 0;
2515
      winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2516
    }
2517
  }
2518
  else
2519
  {
2520
    const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2521
    const char* redirect_fmt =
2522
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2523
2524
    size_t redirect_len = 0;
2525
    winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2526
  }
2527
  return redirect_uri;
2528
}
2529
2530
static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2531
{
2532
  const rdpSettings* settings = cctx->context.settings;
2533
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2534
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2535
                                                          AAD_WELLKNOWN_authorization_endpoint);
2536
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2537
2538
  if (!client_id || !ep || !scope)
2539
    return nullptr;
2540
2541
  char* redirect_uri = get_redirect_uri(settings);
2542
  if (!redirect_uri)
2543
    return nullptr;
2544
2545
  char* url = nullptr;
2546
  size_t urllen = 0;
2547
  winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2548
                 client_id, scope, redirect_uri);
2549
  free(redirect_uri);
2550
  return url;
2551
}
2552
2553
static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2554
{
2555
  const rdpSettings* settings = cctx->context.settings;
2556
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2557
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2558
                                                          AAD_WELLKNOWN_authorization_endpoint);
2559
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2560
2561
  if (!client_id || !ep || !scope)
2562
    return nullptr;
2563
2564
  char* redirect_uri = get_redirect_uri(settings);
2565
  if (!redirect_uri)
2566
    return nullptr;
2567
2568
  char* url = nullptr;
2569
  size_t urllen = 0;
2570
2571
  const char* code = va_arg(ap, const char*);
2572
  winpr_asprintf(&url, &urllen,
2573
                 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2574
                 code, client_id, scope, redirect_uri);
2575
  free(redirect_uri);
2576
  return url;
2577
}
2578
2579
static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2580
{
2581
  const rdpSettings* settings = cctx->context.settings;
2582
  char* url = nullptr;
2583
  size_t urllen = 0;
2584
  char* redirect_uri = get_redirect_uri(settings);
2585
2586
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2587
  if (!client_id || !redirect_uri)
2588
    goto cleanup;
2589
2590
  {
2591
    const char* scope = va_arg(ap, const char*);
2592
    if (!scope)
2593
      goto cleanup;
2594
2595
    {
2596
      const char* ep = freerdp_utils_aad_get_wellknown_string(
2597
          &cctx->context, AAD_WELLKNOWN_authorization_endpoint);
2598
      winpr_asprintf(&url, &urllen,
2599
                     "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2600
                     client_id, scope, redirect_uri);
2601
    }
2602
  }
2603
2604
cleanup:
2605
  free(redirect_uri);
2606
  return url;
2607
}
2608
2609
static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2610
{
2611
  const rdpSettings* settings = cctx->context.settings;
2612
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2613
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2614
                                                          AAD_WELLKNOWN_authorization_endpoint);
2615
  const char* scope = va_arg(ap, const char*);
2616
  const char* code = va_arg(ap, const char*);
2617
  const char* req_cnf = va_arg(ap, const char*);
2618
2619
  if (!client_id || !ep || !scope || !code || !req_cnf)
2620
    return nullptr;
2621
2622
  char* redirect_uri = get_redirect_uri(settings);
2623
  if (!redirect_uri)
2624
    return nullptr;
2625
2626
  char* url = nullptr;
2627
  size_t urllen = 0;
2628
2629
  winpr_asprintf(
2630
      &url, &urllen,
2631
      "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2632
      code, client_id, scope, redirect_uri, req_cnf);
2633
  free(redirect_uri);
2634
  return url;
2635
}
2636
#endif
2637
2638
char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2639
0
{
2640
0
  WINPR_ASSERT(cctx);
2641
0
  char* str = nullptr;
2642
2643
0
  va_list ap = WINPR_C_ARRAY_INIT;
2644
0
  va_start(ap, type);
2645
0
  switch (type)
2646
0
  {
2647
#if defined(WITH_AAD)
2648
    case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2649
      str = aad_auth_request(cctx, ap);
2650
      break;
2651
    case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2652
      str = aad_token_request(cctx, ap);
2653
      break;
2654
    case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2655
      str = avd_auth_request(cctx, ap);
2656
      break;
2657
    case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2658
      str = avd_token_request(cctx, ap);
2659
      break;
2660
#endif
2661
0
    default:
2662
0
      break;
2663
0
  }
2664
0
  va_end(ap);
2665
0
  return str;
2666
0
}
2667
2668
BOOL client_common_save_session_info(WINPR_ATTR_UNUSED rdpContext* context, UINT32 type,
2669
                                     const void* data)
2670
317
{
2671
317
  char buffer[128] = WINPR_C_ARRAY_INIT;
2672
317
  WLog_INFO(TAG, "%s [%s]", freerdp_session_logon_type_str(type),
2673
317
            freerdp_session_logon_type_data_str(type, data, buffer, sizeof(buffer)));
2674
317
  return TRUE;
2675
317
}