Coverage Report

Created: 2025-11-24 06:38

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