Coverage Report

Created: 2024-05-20 06:11

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