Coverage Report

Created: 2024-09-08 06:20

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