FreeRDP
Loading...
Searching...
No Matches
client/common/cmdline.c
1
22#include <freerdp/config.h>
23#include <freerdp/utils/helpers.h>
24
25#include <ctype.h>
26#include <errno.h>
27
28#include <winpr/assert.h>
29#include <winpr/string.h>
30#include <winpr/crt.h>
31#include <winpr/wlog.h>
32#include <winpr/path.h>
33#include <winpr/ncrypt.h>
34#include <winpr/environment.h>
35#include <winpr/timezone.h>
36
37#include <freerdp/freerdp.h>
38#include <freerdp/addin.h>
39#include <freerdp/settings.h>
40#include <freerdp/client.h>
41#include <freerdp/client/channels.h>
42#include <freerdp/channels/drdynvc.h>
43#include <freerdp/channels/cliprdr.h>
44#include <freerdp/channels/encomsp.h>
45#include <freerdp/channels/rdpear.h>
46#include <freerdp/channels/rdpewa.h>
47#include <freerdp/channels/rdp2tcp.h>
48#include <freerdp/channels/remdesk.h>
49#include <freerdp/channels/rdpsnd.h>
50#include <freerdp/channels/disp.h>
51#include <freerdp/crypto/crypto.h>
52#include <freerdp/locale/keyboard.h>
53#include <freerdp/utils/passphrase.h>
54#include <freerdp/utils/proxy_utils.h>
55#include <freerdp/utils/string.h>
56#include <freerdp/channels/urbdrc.h>
57#include <freerdp/channels/rdpdr.h>
58#include <freerdp/locale/locale.h>
59
60#if defined(CHANNEL_AINPUT_CLIENT)
61#include <freerdp/channels/ainput.h>
62#endif
63
64#include <freerdp/channels/audin.h>
65#include <freerdp/channels/echo.h>
66
67#include <freerdp/client/cmdline.h>
68#include <freerdp/version.h>
69#include <freerdp/client/utils/smartcard_cli.h>
70
71#include <openssl/tls1.h>
72#include "cmdline.h"
73
74#include <freerdp/log.h>
75#define TAG CLIENT_TAG("common.cmdline")
76
77static const char str_force[] = "force";
78
79static const char* credential_args[] = { "p", "smartcard-logon",
80#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
81 "gp", "gat",
82#endif
83 "pth", "reconnect-cookie",
84 "assistance" };
85
86static const char* option_starts_with(const char* what, const char* val);
87static BOOL option_ends_with(const char* str, const char* ext);
88static BOOL option_equals(const char* what, const char* val);
89
90static BOOL freerdp_client_print_codepages(const char* arg)
91{
92 size_t count = 0;
93 DWORD column = 2;
94 const char* filter = nullptr;
95 RDP_CODEPAGE* pages = nullptr;
96
97 if (arg)
98 {
99 filter = strchr(arg, ',');
100 if (!filter)
101 filter = arg;
102 else
103 filter++;
104 }
105 pages = freerdp_keyboard_get_matching_codepages(column, filter, &count);
106 if (!pages)
107 return TRUE;
108
109 printf("%-10s %-8s %-60s %-36s %-48s\n", "<id>", "<locale>", "<win langid>", "<language>",
110 "<country>");
111 for (size_t x = 0; x < count; x++)
112 {
113 const RDP_CODEPAGE* page = &pages[x];
114 char buffer[2048] = WINPR_C_ARRAY_INIT;
115
116 if (strnlen(page->subLanguageSymbol, ARRAYSIZE(page->subLanguageSymbol)) > 0)
117 (void)_snprintf(buffer, sizeof(buffer), "[%s|%s]", page->primaryLanguageSymbol,
118 page->subLanguageSymbol);
119 else
120 (void)_snprintf(buffer, sizeof(buffer), "[%s]", page->primaryLanguageSymbol);
121 printf("id=0x%04" PRIx16 ": [%-6s] %-60s %-36s %-48s\n", page->id, page->locale, buffer,
122 page->primaryLanguage, page->subLanguage);
123 }
124 freerdp_codepages_free(pages);
125 return TRUE;
126}
127
128static BOOL freerdp_path_valid(const char* path, BOOL* special)
129{
130 const char DynamicDrives[] = "DynamicDrives";
131 BOOL isPath = FALSE;
132 BOOL isSpecial = 0;
133 if (!path)
134 return FALSE;
135
136 isSpecial = (option_equals("*", path) || option_equals(DynamicDrives, path) ||
137 option_equals("%", path));
138 if (!isSpecial)
139 isPath = winpr_PathFileExists(path);
140
141 if (special)
142 *special = isSpecial;
143
144 return isSpecial || isPath;
145}
146
147static BOOL freerdp_sanitize_drive_name(char* name, const char* invalid, const char* replacement)
148{
149 if (!name || !invalid || !replacement)
150 return FALSE;
151 if (strlen(invalid) != strlen(replacement))
152 return FALSE;
153
154 while (*invalid != '\0')
155 {
156 const char what = *invalid++;
157 const char with = *replacement++;
158
159 char* cur = name;
160 while ((cur = strchr(cur, what)) != nullptr)
161 *cur = with;
162 }
163 return TRUE;
164}
165
166static char* name_from_path(const char* path)
167{
168 const char* name = "nullptr";
169 if (path)
170 {
171 if (option_equals("%", path))
172 name = "home";
173 else if (option_equals("*", path))
174 name = "hotplug-all";
175 else if (option_equals("DynamicDrives", path))
176 name = "hotplug";
177 else
178 name = path;
179 }
180 return _strdup(name);
181}
182
183static BOOL freerdp_client_add_drive(rdpSettings* settings, const char* path, const char* name)
184{
185 char* dname = nullptr;
186 RDPDR_DEVICE* device = nullptr;
187
188 if (name)
189 {
190 BOOL skip = FALSE;
191 if (path)
192 {
193 switch (path[0])
194 {
195 case '*':
196 case '%':
197 skip = TRUE;
198 break;
199 default:
200 break;
201 }
202 }
203 /* Path was entered as secondary argument, swap */
204 if (!skip && winpr_PathFileExists(name))
205 {
206 if (!winpr_PathFileExists(path) || (!PathIsRelativeA(name) && PathIsRelativeA(path)))
207 {
208 const char* tmp = path;
209 path = name;
210 name = tmp;
211 }
212 }
213 }
214
215 if (name)
216 dname = _strdup(name);
217 else /* We need a name to send to the server. */
218 dname = name_from_path(path);
219
220 if (freerdp_sanitize_drive_name(dname, "\\/", "__"))
221 {
222 const char* args[] = { dname, path };
223 device = freerdp_device_new(RDPDR_DTYP_FILESYSTEM, ARRAYSIZE(args), args);
224 }
225 free(dname);
226 if (!device)
227 goto fail;
228
229 if (!path)
230 goto fail;
231
232 {
233 BOOL isSpecial = FALSE;
234 BOOL isPath = freerdp_path_valid(path, &isSpecial);
235
236 if (!isPath && !isSpecial)
237 {
238 WLog_WARN(TAG, "Invalid drive to redirect: '%s' does not exist, skipping.", path);
239 freerdp_device_free(device);
240 }
241 else if (!freerdp_device_collection_add(settings, device))
242 goto fail;
243 }
244
245 return TRUE;
246
247fail:
248 freerdp_device_free(device);
249 return FALSE;
250}
251
252static BOOL value_to_int(const char* value, LONGLONG* result, LONGLONG min, LONGLONG max)
253{
254 long long rc = 0;
255
256 if (!value || !result)
257 return FALSE;
258
259 errno = 0;
260 rc = _strtoi64(value, nullptr, 0);
261
262 if (errno != 0)
263 return FALSE;
264
265 if ((rc < min) || (rc > max))
266 return FALSE;
267
268 *result = rc;
269 return TRUE;
270}
271
272static BOOL value_to_uint(const char* value, ULONGLONG* result, ULONGLONG min, ULONGLONG max)
273{
274 unsigned long long rc = 0;
275
276 if (!value || !result)
277 return FALSE;
278
279 errno = 0;
280 rc = _strtoui64(value, nullptr, 0);
281
282 if (errno != 0)
283 return FALSE;
284
285 if ((rc < min) || (rc > max))
286 return FALSE;
287
288 *result = rc;
289 return TRUE;
290}
291
292BOOL freerdp_client_print_version(void)
293{
294 printf("This is FreeRDP version %s (%s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
295 return TRUE;
296}
297
298BOOL freerdp_client_print_version_ex(int argc, char** argv)
299{
300 WINPR_ASSERT(argc >= 0);
301 WINPR_ASSERT(argv || (argc == 0));
302 const char* name = (argc > 0) ? argv[0] : "argc < 1";
303 printf("This is FreeRDP version [%s] %s (%s)\n", name, FREERDP_VERSION_FULL,
304 FREERDP_GIT_REVISION);
305 return TRUE;
306}
307
308BOOL freerdp_client_print_buildconfig(void)
309{
310 printf("%s", freerdp_get_build_config());
311 return TRUE;
312}
313
314BOOL freerdp_client_print_buildconfig_ex(int argc, char** argv)
315{
316 WINPR_ASSERT(argc >= 0);
317 WINPR_ASSERT(argv || (argc == 0));
318 const char* name = (argc > 0) ? argv[0] : "argc < 1";
319 printf("[%s] %s", name, freerdp_get_build_config());
320 return TRUE;
321}
322
323static void freerdp_client_print_scancodes(void)
324{
325 printf("RDP scancodes and their name for use with /kbd:remap\n");
326
327 for (UINT32 x = 0; x < UINT16_MAX; x++)
328 {
329 const char* name = freerdp_keyboard_scancode_name(x);
330 if (name)
331 printf("0x%04" PRIx32 " --> %s\n", x, name);
332 }
333}
334
335static BOOL is_delimiter(char c, const char* delimiters)
336{
337 char d = 0;
338 while ((d = *delimiters++) != '\0')
339 {
340 if (c == d)
341 return TRUE;
342 }
343 return FALSE;
344}
345
346static const char* get_last(const char* start, size_t len, const char* delimiters)
347{
348 const char* last = nullptr;
349 for (size_t x = 0; x < len; x++)
350 {
351 char c = start[x];
352 if (is_delimiter(c, delimiters))
353 last = &start[x];
354 }
355 return last;
356}
357
358static SSIZE_T next_delimiter(const char* text, size_t len, size_t max, const char* delimiters)
359{
360 if (len < max)
361 return -1;
362
363 const char* last = get_last(text, max, delimiters);
364 if (!last)
365 return -1;
366
367 return (SSIZE_T)(last - text);
368}
369
370static SSIZE_T forced_newline_at(const char* text, size_t len, size_t limit,
371 const char* force_newline)
372{
373 char d = 0;
374 while ((d = *force_newline++) != '\0')
375 {
376 const char* tok = strchr(text, d);
377 if (tok)
378 {
379 const size_t offset = WINPR_ASSERTING_INT_CAST(size_t, tok - text);
380 if ((offset > len) || (offset > limit))
381 continue;
382 return (SSIZE_T)(offset);
383 }
384 }
385 return -1;
386}
387
388static BOOL print_align(size_t start_offset, size_t* current)
389{
390 WINPR_ASSERT(current);
391 if (*current < start_offset)
392 {
393 const int rc = printf("%*c", (int)(start_offset - *current), ' ');
394 if (rc < 0)
395 return FALSE;
396 *current += (size_t)rc;
397 }
398 return TRUE;
399}
400
401static char* print_token(char* text, size_t start_offset, size_t* current, size_t limit,
402 const char* delimiters, const char* force_newline)
403{
404 int rc = 0;
405 const size_t tlen = strnlen(text, limit);
406 size_t len = tlen;
407 const SSIZE_T force_at = forced_newline_at(text, len, limit - *current, force_newline);
408 BOOL isForce = (force_at >= 0);
409
410 if (isForce)
411 len = MIN(len, (size_t)force_at);
412
413 if (!print_align(start_offset, current))
414 return nullptr;
415
416 const SSIZE_T delim = next_delimiter(text, len, limit - *current, delimiters);
417 const BOOL isDelim = delim > 0;
418 if (isDelim)
419 {
420 len = MIN(len, (size_t)delim + 1);
421 }
422
423 rc = printf("%.*s", (int)len, text);
424 if (rc < 0)
425 return nullptr;
426
427 if (isForce || isDelim)
428 {
429 printf("\n");
430 *current = 0;
431
432 const size_t offset = len + ((isForce && (force_at == 0)) ? 1 : 0);
433 return &text[offset];
434 }
435
436 *current += (size_t)rc;
437
438 if (tlen == (size_t)rc)
439 return nullptr;
440 return &text[(size_t)rc];
441}
442
443static size_t print_optionals(const char* text, size_t start_offset, size_t current)
444{
445 const size_t limit = 80;
446 char* str = _strdup(text);
447 char* cur = str;
448
449 do
450 {
451 cur = print_token(cur, start_offset + 1, &current, limit, "[], ", "\r\n");
452 } while (cur != nullptr);
453
454 free(str);
455 return current;
456}
457
458static size_t print_description(const char* text, size_t start_offset, size_t current)
459{
460 const size_t limit = 80;
461 char* str = _strdup(text);
462 char* cur = str;
463
464 while (cur != nullptr)
465 cur = print_token(cur, start_offset, &current, limit, " ", "\r\n");
466
467 free(str);
468 const int rc = printf("\n");
469 if (rc >= 0)
470 {
471 const size_t src = WINPR_ASSERTING_INT_CAST(size_t, rc);
472 WINPR_ASSERT(SIZE_MAX - src > current);
473 current += src;
474 }
475 return current;
476}
477
478static int cmp_cmdline_args(const void* pva, const void* pvb)
479{
482
483 if (!a->Name && !b->Name)
484 return 0;
485 if (!a->Name)
486 return 1;
487 if (!b->Name)
488 return -1;
489 return strcmp(a->Name, b->Name);
490}
491
492static void freerdp_client_print_command_line_args(COMMAND_LINE_ARGUMENT_A* parg, size_t count)
493{
494 if (!parg)
495 return;
496
497 qsort(parg, count, sizeof(COMMAND_LINE_ARGUMENT_A), cmp_cmdline_args);
498
499 const COMMAND_LINE_ARGUMENT_A* arg = parg;
500 do
501 {
502 int rc = 0;
503 size_t pos = 0;
504 const size_t description_offset = 30 + 8;
505
506 if (arg->Flags & (COMMAND_LINE_VALUE_BOOL | COMMAND_LINE_VALUE_FLAG))
507 {
508 if ((arg->Flags & (uint32_t)~COMMAND_LINE_VALUE_BOOL) == 0)
509 rc = printf(" %s%s", arg->Default ? "-" : "+", arg->Name);
510 else if ((arg->Flags & COMMAND_LINE_VALUE_OPTIONAL) != 0)
511 rc = printf(" [%s|/]%s", arg->Default ? "-" : "+", arg->Name);
512 else
513 {
514 rc = printf(" %s%s", arg->Default ? "-" : "+", arg->Name);
515 }
516 }
517 else
518 rc = printf(" /%s", arg->Name);
519
520 if (rc < 0)
521 return;
522 pos += (size_t)rc;
523
524 if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
525 (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
526 {
527 if (arg->Format)
528 {
529 if (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL)
530 {
531 rc = printf("[:");
532 if (rc < 0)
533 return;
534 pos += (size_t)rc;
535 pos = print_optionals(arg->Format, pos, pos);
536 rc = printf("]");
537 if (rc < 0)
538 return;
539 pos += (size_t)rc;
540 }
541 else
542 {
543 rc = printf(":");
544 if (rc < 0)
545 return;
546 pos += (size_t)rc;
547 pos = print_optionals(arg->Format, pos, pos);
548 }
549
550 if (pos > description_offset)
551 {
552 printf("\n");
553 pos = 0;
554 }
555 }
556 }
557
558 rc = printf("%*c", (int)(description_offset - pos), ' ');
559 if (rc < 0)
560 return;
561 pos += (size_t)rc;
562
563 if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
564 {
565 rc = printf("%s ", arg->Default ? "Disable" : "Enable");
566 if (rc < 0)
567 return;
568 pos += (size_t)rc;
569 }
570
571 print_description(arg->Text, description_offset, pos);
572 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
573}
574
575BOOL freerdp_client_print_command_line_help(int argc, char** argv)
576{
577 return freerdp_client_print_command_line_help_ex(argc, argv, nullptr);
578}
579
580static COMMAND_LINE_ARGUMENT_A* create_merged_args(const COMMAND_LINE_ARGUMENT_A* custom,
581 SSIZE_T count, size_t* pcount)
582{
583 WINPR_ASSERT(pcount);
584 if (count < 0)
585 {
586 const COMMAND_LINE_ARGUMENT_A* cur = custom;
587 count = 0;
588 while (cur && cur->Name)
589 {
590 count++;
591 cur++;
592 }
593 }
594
596 calloc((size_t)count + ARRAYSIZE(global_cmd_args), sizeof(COMMAND_LINE_ARGUMENT_A));
597 *pcount = 0;
598 if (!largs)
599 return nullptr;
600
601 size_t lcount = 0;
602 const COMMAND_LINE_ARGUMENT_A* cur = custom;
603 while (cur && cur->Name)
604 {
605 largs[lcount++] = *cur++;
606 }
607
608 cur = global_cmd_args;
609 while (cur && cur->Name)
610 {
611 largs[lcount++] = *cur++;
612 }
613 *pcount = lcount;
614 return largs;
615}
616
617static void freerdp_client_print_command_line_usage(int argc, char** argv)
618{
619 WINPR_ASSERT(argv || (argc < 1));
620
621 const char* name = freerdp_getApplicationDetailsString();
622 if (argc > 0)
623 name = argv[0];
624 printf("\n");
625 printf("%s - A Free Remote Desktop Protocol Implementation\n", name);
626 printf("To show full command line help type\n");
627 printf("%s /?\n", name);
628 printf("\n");
629}
630
631BOOL freerdp_client_print_command_line_help_ex(int argc, char** argv,
632 const COMMAND_LINE_ARGUMENT_A* custom)
633{
634 const char* name = freerdp_getApplicationDetailsString();
635
636 /* allocate a merged copy of implementation defined and default arguments */
637 size_t lcount = 0;
638 COMMAND_LINE_ARGUMENT_A* largs = create_merged_args(custom, -1, &lcount);
639 if (!largs)
640 return FALSE;
641
642 if (argc > 0)
643 name = argv[0];
644
645 printf("\n");
646 printf("%s - A Free Remote Desktop Protocol Implementation\n", name);
647 printf("See www.freerdp.com for more information\n");
648 printf("\n");
649 printf("Usage: %s [file] [options] [/v:<server>[:port]]\n", argv[0]);
650 printf("\n");
651 printf("Syntax:\n");
652 printf(" /flag (enables flag)\n");
653 printf(" /option:<value> (specifies option with value)\n");
654 printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
655 printf("\n");
656
657 freerdp_client_print_command_line_args(largs, lcount);
658 free(largs);
659
660 printf("\n");
661 printf("Examples:\n");
662 printf(" %s connection.rdp /p:Pwd123! /f\n", name);
663 printf(" %s /u:CONTOSO\\JohnDoe /p:Pwd123! /v:rdp.contoso.com\n", name);
664 printf(" %s /u:JohnDoe /p:Pwd123! /w:1366 /h:768 /v:192.168.1.100:4489\n", name);
665 printf(" %s /u:JohnDoe /p:Pwd123! /vmconnect:C824F53E-95D2-46C6-9A18-23A5BB403532 "
666 "/v:192.168.1.100\n",
667 name);
668 printf(" %s /u:\\AzureAD\\user@corp.example /p:pwd /v:host\n", name);
669 printf("Use a generic pipe as transport:");
670 printf(" %s /v:/path/to/pipe\n", name);
671 printf("Use a external socket:");
672 printf(" %s /v:|:1234\n", name);
673 printf("\n");
674 printf("Connect to a system with TLS security and open the greeter:");
675 printf("NOTE: Needs a server configured to not require NLA or it will fail!");
676 printf("\n");
677 printf(" %s /sec:tls /p /v:rdp.contoso.com\n", name);
678 printf("\n");
679 printf("Disable clipboard redirection: -clipboard\n");
680 printf("\n");
681 printf("Drive Redirection: /drive:home,/home/user\n");
682 printf("Smartcard Redirection: /smartcard:<device>\n");
683 printf("Smartcard logon with Kerberos authentication: /smartcard-logon /sec:nla\n");
684
685#if defined(CHANNEL_SERIAL_CLIENT)
686 printf("Serial Port Redirection: /serial:<name>,<device>,[SerCx2|SerCx|Serial],[permissive]\n");
687 printf("Serial Port Redirection: /serial:COM1,/dev/ttyS0\n");
688#endif
689#if defined(CHANNEL_PARALLEL_CLIENT)
690 printf("Parallel Port Redirection: /parallel:<name>,<device>\n");
691#endif
692 printf("Printer Redirection: /printer:<device>,<driver>,[default]\n");
693 printf("TCP redirection: /rdp2tcp:/usr/bin/rdp2tcp\n");
694 printf("\n");
695 printf("Audio Output Redirection: /sound:sys:oss,dev:1,format:1\n");
696 printf("Audio Output Redirection: /sound:sys:alsa\n");
697 printf("Audio Input Redirection: /microphone:sys:oss,dev:1,format:1\n");
698 printf("Audio Input Redirection: /microphone:sys:alsa\n");
699 printf("\n");
700 printf("Multimedia Redirection: /video\n");
701#ifdef CHANNEL_URBDRC_CLIENT
702 printf("USB Device Redirection: /usb:id:054c:0268#4669:6e6b,addr:04:0c\n");
703#endif
704 printf("\n");
705 printf("For Gateways, the https_proxy environment variable is respected:\n");
706#ifdef _WIN32
707 printf(" set HTTPS_PROXY=http://proxy.contoso.com:3128/\n");
708#else
709 printf(" export https_proxy=http://proxy.contoso.com:3128/\n");
710#endif
711 printf(" %s /gateway:g:rdp.contoso.com ...\n", name);
712 printf("\n");
713 printf("More documentation is coming, in the meantime consult source files\n");
714 printf("\n");
715 return TRUE;
716}
717
718static BOOL option_is_rdp_file(const char* option)
719{
720 WINPR_ASSERT(option);
721
722 if (option_ends_with(option, ".rdp"))
723 return TRUE;
724 if (option_ends_with(option, ".rdpw"))
725 return TRUE;
726 return FALSE;
727}
728
729static BOOL option_is_incident_file(const char* option)
730{
731 WINPR_ASSERT(option);
732
733 return (option_ends_with(option, ".msrcIncident"));
734}
735
736static int freerdp_client_command_line_pre_filter(void* context, int index, int argc, LPSTR* argv)
737{
738 if (index == 1)
739 {
740 size_t length = 0;
741 rdpSettings* settings = nullptr;
742
743 if (argc <= index)
744 return -1;
745
746 length = strlen(argv[index]);
747
748 if (length > 4)
749 {
750 if (option_is_rdp_file(argv[index]))
751 {
752 settings = (rdpSettings*)context;
753
754 if (!freerdp_settings_set_string(settings, FreeRDP_ConnectionFile, argv[index]))
755 return COMMAND_LINE_ERROR_MEMORY;
756
757 return 1;
758 }
759 }
760
761 if (length > 13)
762 {
763 if (option_is_incident_file(argv[index]))
764 {
765 settings = (rdpSettings*)context;
766
767 if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, argv[index]))
768 return COMMAND_LINE_ERROR_MEMORY;
769
770 return 1;
771 }
772 }
773 }
774
775 return 0;
776}
777
778BOOL freerdp_client_add_device_channel(rdpSettings* settings, size_t count,
779 const char* const* params)
780{
781 WINPR_ASSERT(settings);
782 WINPR_ASSERT(params);
783 WINPR_ASSERT(count > 0);
784
785 if (option_equals(params[0], "drive"))
786 {
787 BOOL rc = 0;
788 if (count < 2)
789 return FALSE;
790
791 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
792 return FALSE;
793 if (count < 3)
794 rc = freerdp_client_add_drive(settings, params[1], nullptr);
795 else
796 rc = freerdp_client_add_drive(settings, params[2], params[1]);
797
798 return rc;
799 }
800 else if (option_equals(params[0], "printer"))
801 {
802 RDPDR_DEVICE* printer = nullptr;
803
804 if (count < 1)
805 return FALSE;
806
807 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectPrinters, TRUE))
808 return FALSE;
809 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
810 return FALSE;
811
812 printer = freerdp_device_new(RDPDR_DTYP_PRINT, count - 1, &params[1]);
813 if (!printer)
814 return FALSE;
815
816 if (!freerdp_device_collection_add(settings, printer))
817 {
818 freerdp_device_free(printer);
819 return FALSE;
820 }
821
822 return TRUE;
823 }
824 else if (option_equals(params[0], "smartcard"))
825 {
826 RDPDR_DEVICE* smartcard = nullptr;
827
828 if (count < 1)
829 return FALSE;
830
831 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE))
832 return FALSE;
833 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
834 return FALSE;
835
836 smartcard = freerdp_device_new(RDPDR_DTYP_SMARTCARD, count - 1, &params[1]);
837
838 if (!smartcard)
839 return FALSE;
840
841 if (!freerdp_device_collection_add(settings, smartcard))
842 {
843 freerdp_device_free(smartcard);
844 return FALSE;
845 }
846
847 return TRUE;
848 }
849#if defined(CHANNEL_SERIAL_CLIENT)
850 else if (option_equals(params[0], "serial"))
851 {
852 RDPDR_DEVICE* serial = nullptr;
853
854 if (count < 1)
855 return FALSE;
856
857 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSerialPorts, TRUE))
858 return FALSE;
859 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
860 return FALSE;
861
862 serial = freerdp_device_new(RDPDR_DTYP_SERIAL, count - 1, &params[1]);
863
864 if (!serial)
865 return FALSE;
866
867 if (!freerdp_device_collection_add(settings, serial))
868 {
869 freerdp_device_free(serial);
870 return FALSE;
871 }
872
873 return TRUE;
874 }
875#endif
876 else if (option_equals(params[0], "parallel"))
877 {
878 RDPDR_DEVICE* parallel = nullptr;
879
880 if (count < 1)
881 return FALSE;
882
883 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectParallelPorts, TRUE))
884 return FALSE;
885 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
886 return FALSE;
887
888 parallel = freerdp_device_new(RDPDR_DTYP_PARALLEL, count - 1, &params[1]);
889
890 if (!parallel)
891 return FALSE;
892
893 if (!freerdp_device_collection_add(settings, parallel))
894 {
895 freerdp_device_free(parallel);
896 return FALSE;
897 }
898
899 return TRUE;
900 }
901
902 return FALSE;
903}
904
905BOOL freerdp_client_del_static_channel(rdpSettings* settings, const char* name)
906{
907 return freerdp_static_channel_collection_del(settings, name);
908}
909
910BOOL freerdp_client_add_static_channel(rdpSettings* settings, size_t count,
911 const char* const* params)
912{
913 ADDIN_ARGV* _args = nullptr;
914
915 if (!settings || !params || !params[0] || (count > INT_MAX))
916 return FALSE;
917
918 if (freerdp_static_channel_collection_find(settings, params[0]))
919 return TRUE;
920
921 _args = freerdp_addin_argv_new(count, params);
922
923 if (!_args)
924 return FALSE;
925
926 if (!freerdp_static_channel_collection_add(settings, _args))
927 goto fail;
928
929 return TRUE;
930fail:
931 freerdp_addin_argv_free(_args);
932 return FALSE;
933}
934
935BOOL freerdp_client_del_dynamic_channel(rdpSettings* settings, const char* name)
936{
937 return freerdp_dynamic_channel_collection_del(settings, name);
938}
939
940BOOL freerdp_client_add_dynamic_channel(rdpSettings* settings, size_t count,
941 const char* const* params)
942{
943 ADDIN_ARGV* _args = nullptr;
944
945 if (!settings || !params || !params[0] || (count > INT_MAX))
946 return FALSE;
947
948 if (freerdp_dynamic_channel_collection_find(settings, params[0]))
949 return TRUE;
950
951 _args = freerdp_addin_argv_new(count, params);
952
953 if (!_args)
954 return FALSE;
955
956 if (!freerdp_dynamic_channel_collection_add(settings, _args))
957 goto fail;
958
959 return TRUE;
960
961fail:
962 freerdp_addin_argv_free(_args);
963 return FALSE;
964}
965
966static BOOL read_pem_file(rdpSettings* settings, FreeRDP_Settings_Keys_String id, const char* file)
967{
968 size_t length = 0;
969 char* pem = crypto_read_pem(file, &length);
970 if (!pem || (length == 0))
971 {
972 free(pem);
973 return FALSE;
974 }
975
976 BOOL rc = freerdp_settings_set_string_len(settings, id, pem, length);
977 free(pem);
978 return rc;
979}
980
982typedef enum
983{
984 CMDLINE_SUBOPTION_STRING,
985 CMDLINE_SUBOPTION_FILE,
986} CmdLineSubOptionType;
987
988typedef BOOL (*CmdLineSubOptionCb)(const char* value, rdpSettings* settings);
989typedef struct
990{
991 const char* optname;
992 FreeRDP_Settings_Keys_String id;
993 CmdLineSubOptionType opttype;
994 WINPR_ATTR_NODISCARD CmdLineSubOptionCb cb;
995} CmdLineSubOptions;
996
997static BOOL parseSubOptions(rdpSettings* settings, const CmdLineSubOptions* opts, size_t count,
998 const char* arg)
999{
1000 BOOL found = FALSE;
1001
1002 for (size_t xx = 0; xx < count; xx++)
1003 {
1004 const CmdLineSubOptions* opt = &opts[xx];
1005
1006 if (option_starts_with(opt->optname, arg))
1007 {
1008 const size_t optlen = strlen(opt->optname);
1009 const char* val = &arg[optlen];
1010 BOOL status = 0;
1011
1012 switch (opt->opttype)
1013 {
1014 case CMDLINE_SUBOPTION_STRING:
1015 status = freerdp_settings_set_string(settings, opt->id, val);
1016 break;
1017 case CMDLINE_SUBOPTION_FILE:
1018 status = read_pem_file(settings, opt->id, val);
1019 break;
1020 default:
1021 WLog_ERR(TAG, "invalid subOption type");
1022 return FALSE;
1023 }
1024
1025 if (!status)
1026 return FALSE;
1027
1028 if (opt->cb && !opt->cb(val, settings))
1029 return FALSE;
1030
1031 found = TRUE;
1032 break;
1033 }
1034 }
1035
1036 if (!found)
1037 WLog_ERR(TAG, "option %s not handled", arg);
1038
1039 return found;
1040}
1041
1042#define fail_at(arg, rc) fail_at_((arg), (rc), __FILE__, __func__, __LINE__)
1043static int fail_at_(const COMMAND_LINE_ARGUMENT_A* arg, int rc, const char* file, const char* fkt,
1044 size_t line)
1045{
1046 if (rc == 0)
1047 return rc;
1048
1049 const DWORD level = WLOG_ERROR;
1050 wLog* log = WLog_Get(TAG);
1051 if (WLog_IsLevelActive(log, level))
1052 {
1053 const char* val = arg->Value;
1054 if ((arg->Flags & COMMAND_LINE_VALUE_FLAG) != 0)
1055 val = arg->Value == nullptr ? "Disable" : "Enable";
1056 if ((arg->Flags & COMMAND_LINE_VALUE_BOOL) != 0)
1057 val = arg->Value == nullptr ? "Disable" : "Enable";
1058
1059 WLog_PrintTextMessage(log, level, line, file, fkt,
1060 "Command line parsing failed at '%s' value '%s' [%d]", arg->Name, val,
1061 rc);
1062 }
1063 return rc;
1064}
1065
1066static int freerdp_client_command_line_post_filter_int(void* context, COMMAND_LINE_ARGUMENT_A* arg)
1067{
1068 rdpSettings* settings = (rdpSettings*)context;
1069 int status = CHANNEL_RC_OK;
1070 BOOL enable = (arg->Value != nullptr);
1071
1072 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "a")
1073 {
1074 size_t count = 0;
1075 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
1076
1077 if (!freerdp_client_add_device_channel(settings, count, (const char* const*)ptr))
1078 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1079 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
1080 status = COMMAND_LINE_ERROR;
1081
1082 CommandLineParserFree(ptr);
1083 if (status)
1084 return fail_at(arg, status);
1085 }
1086 CommandLineSwitchCase(arg, "kerberos")
1087 {
1088 size_t count = 0;
1089
1090 char** ptr = CommandLineParseCommaSeparatedValuesEx("kerberos", arg->Value, &count);
1091 if (ptr)
1092 {
1093 const CmdLineSubOptions opts[] = {
1094 { "kdc-url:", FreeRDP_KerberosKdcUrl, CMDLINE_SUBOPTION_STRING, nullptr },
1095 { "start-time:", FreeRDP_KerberosStartTime, CMDLINE_SUBOPTION_STRING, nullptr },
1096 { "lifetime:", FreeRDP_KerberosLifeTime, CMDLINE_SUBOPTION_STRING, nullptr },
1097 { "renewable-lifetime:", FreeRDP_KerberosRenewableLifeTime,
1098 CMDLINE_SUBOPTION_STRING, nullptr },
1099 { "cache:", FreeRDP_KerberosCache, CMDLINE_SUBOPTION_STRING, nullptr },
1100 { "armor:", FreeRDP_KerberosArmor, CMDLINE_SUBOPTION_STRING, nullptr },
1101 { "pkinit-anchors:", FreeRDP_PkinitAnchors, CMDLINE_SUBOPTION_STRING, nullptr },
1102 { "pkcs11-module:", FreeRDP_Pkcs11Module, CMDLINE_SUBOPTION_STRING, nullptr }
1103 };
1104
1105 for (size_t x = 1; x < count; x++)
1106 {
1107 const char* cur = ptr[x];
1108 if (!parseSubOptions(settings, opts, ARRAYSIZE(opts), cur))
1109 {
1110 CommandLineParserFree(ptr);
1111 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
1112 }
1113 }
1114 }
1115 CommandLineParserFree(ptr);
1116 }
1117
1118 CommandLineSwitchCase(arg, "vc")
1119 {
1120 size_t count = 0;
1121 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
1122 if (!freerdp_client_add_static_channel(settings, count, (const char* const*)ptr))
1123 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1124 CommandLineParserFree(ptr);
1125 if (status)
1126 return fail_at(arg, status);
1127 }
1128 CommandLineSwitchCase(arg, "dvc")
1129 {
1130 size_t count = 0;
1131 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
1132 if (!freerdp_client_add_dynamic_channel(settings, count, (const char* const*)ptr))
1133 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1134 CommandLineParserFree(ptr);
1135 if (status)
1136 return fail_at(arg, status);
1137 }
1138 CommandLineSwitchCase(arg, "drive")
1139 {
1140 size_t count = 0;
1141 char** ptr = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
1142 if (!freerdp_client_add_device_channel(settings, count, (const char* const*)ptr))
1143 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1144 CommandLineParserFree(ptr);
1145 if (status)
1146 return fail_at(arg, status);
1147 }
1148#if defined(CHANNEL_SERIAL_CLIENT)
1149 CommandLineSwitchCase(arg, "serial")
1150 {
1151 size_t count = 0;
1152 char** ptr = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
1153 if (!freerdp_client_add_device_channel(settings, count, (const char* const*)ptr))
1154 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1155 CommandLineParserFree(ptr);
1156 if (status)
1157 return fail_at(arg, status);
1158 }
1159#endif
1160#if defined(CHANNEL_PARALLEL_CLIENT)
1161 CommandLineSwitchCase(arg, "parallel")
1162 {
1163 size_t count = 0;
1164 char** ptr = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
1165 if (!freerdp_client_add_device_channel(settings, count, (const char* const*)ptr))
1166 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1167 CommandLineParserFree(ptr);
1168 if (status)
1169 return fail_at(arg, status);
1170 }
1171#endif
1172 CommandLineSwitchCase(arg, "smartcard")
1173 {
1174 size_t count = 0;
1175 char** ptr = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
1176 if (!freerdp_client_add_device_channel(settings, count, (const char* const*)ptr))
1177 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1178 CommandLineParserFree(ptr);
1179 if (status)
1180 return fail_at(arg, status);
1181 }
1182 CommandLineSwitchCase(arg, "printer")
1183 {
1184 size_t count = 0;
1185 char** ptr = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
1186 if (!freerdp_client_add_device_channel(settings, count, (const char* const*)ptr))
1187 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1188 CommandLineParserFree(ptr);
1189 if (status)
1190 return fail_at(arg, status);
1191 }
1192 CommandLineSwitchCase(arg, "usb")
1193 {
1194 size_t count = 0;
1195 char** ptr =
1196 CommandLineParseCommaSeparatedValuesEx(URBDRC_CHANNEL_NAME, arg->Value, &count);
1197 if (!freerdp_client_add_dynamic_channel(settings, count, (const char* const*)ptr))
1198 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1199 CommandLineParserFree(ptr);
1200 if (status)
1201 return fail_at(arg, status);
1202 }
1203 CommandLineSwitchCase(arg, "multitouch")
1204 {
1205 if (!freerdp_settings_set_bool(settings, FreeRDP_MultiTouchInput, enable))
1206 return fail_at(arg, COMMAND_LINE_ERROR);
1207 }
1208 CommandLineSwitchCase(arg, "gestures")
1209 {
1210 if (!freerdp_settings_set_bool(settings, FreeRDP_MultiTouchGestures, enable))
1211 return fail_at(arg, COMMAND_LINE_ERROR);
1212 }
1213 CommandLineSwitchCase(arg, "echo")
1214 {
1215 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportEchoChannel, enable))
1216 return fail_at(arg, COMMAND_LINE_ERROR);
1217 }
1218 CommandLineSwitchCase(arg, "ssh-agent")
1219 {
1220 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportSSHAgentChannel, enable))
1221 return fail_at(arg, COMMAND_LINE_ERROR);
1222 }
1223 CommandLineSwitchCase(arg, "disp")
1224 {
1225 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl, enable))
1226 return fail_at(arg, COMMAND_LINE_ERROR);
1227 }
1228 CommandLineSwitchCase(arg, "geometry")
1229 {
1230 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGeometryTracking, enable))
1231 return fail_at(arg, COMMAND_LINE_ERROR);
1232 }
1233 CommandLineSwitchCase(arg, "video")
1234 {
1235 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGeometryTracking,
1236 enable)) /* this requires geometry tracking */
1237 return fail_at(arg, COMMAND_LINE_ERROR);
1238 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportVideoOptimized, enable))
1239 return fail_at(arg, COMMAND_LINE_ERROR);
1240 }
1241 CommandLineSwitchCase(arg, "sound")
1242 {
1243 size_t count = 0;
1244 char** ptr =
1245 CommandLineParseCommaSeparatedValuesEx(RDPSND_CHANNEL_NAME, arg->Value, &count);
1246 if (!freerdp_client_add_static_channel(settings, count, (const char* const*)ptr))
1247 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1248 if (!freerdp_client_add_dynamic_channel(settings, count, (const char* const*)ptr))
1249 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1250
1251 CommandLineParserFree(ptr);
1252 if (status)
1253 return fail_at(arg, status);
1254 }
1255 CommandLineSwitchCase(arg, "microphone")
1256 {
1257 size_t count = 0;
1258 char** ptr = CommandLineParseCommaSeparatedValuesEx(AUDIN_CHANNEL_NAME, arg->Value, &count);
1259 if (!freerdp_client_add_dynamic_channel(settings, count, (const char* const*)ptr))
1260 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1261 CommandLineParserFree(ptr);
1262 if (status)
1263 return fail_at(arg, status);
1264 }
1265#if defined(CHANNEL_TSMF_CLIENT)
1266 CommandLineSwitchCase(arg, "multimedia")
1267 {
1268 size_t count = 0;
1269 char** ptr = CommandLineParseCommaSeparatedValuesEx("tsmf", arg->Value, &count);
1270 if (!freerdp_client_add_dynamic_channel(settings, count, (const char* const*)ptr))
1271 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1272 CommandLineParserFree(ptr);
1273 if (status)
1274 return fail_at(arg, status);
1275 }
1276#endif
1277 CommandLineSwitchCase(arg, "heartbeat")
1278 {
1279 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportHeartbeatPdu, enable))
1280 return fail_at(arg, COMMAND_LINE_ERROR);
1281 }
1282 CommandLineSwitchCase(arg, "multitransport")
1283 {
1284 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMultitransport, enable))
1285 return fail_at(arg, COMMAND_LINE_ERROR);
1286
1287 UINT32 flags = 0;
1288 if (freerdp_settings_get_bool(settings, FreeRDP_SupportMultitransport))
1289 flags =
1290 (TRANSPORT_TYPE_UDP_FECR | TRANSPORT_TYPE_UDP_FECL | TRANSPORT_TYPE_UDP_PREFERRED);
1291
1292 if (!freerdp_settings_set_uint32(settings, FreeRDP_MultitransportFlags, flags))
1293 return fail_at(arg, COMMAND_LINE_ERROR);
1294 }
1295 CommandLineSwitchEnd(arg)
1296
1297 return status;
1298}
1299
1300static int freerdp_client_command_line_post_filter(void* context, COMMAND_LINE_ARGUMENT_A* arg)
1301{
1302 int status = freerdp_client_command_line_post_filter_int(context, arg);
1303 return status == CHANNEL_RC_OK ? 1 : -1;
1304}
1305
1306static BOOL freerdp_parse_username_ptr(const char* username, const char** user, size_t* userlen,
1307 const char** domain, size_t* domainlen)
1308{
1309 WINPR_ASSERT(user);
1310 WINPR_ASSERT(userlen);
1311 WINPR_ASSERT(domain);
1312 WINPR_ASSERT(domainlen);
1313
1314 if (!username)
1315 return FALSE;
1316
1317 const char* p = strchr(username, '\\');
1318
1319 *user = nullptr;
1320 *userlen = 0;
1321
1322 *domain = nullptr;
1323 *domainlen = 0;
1324
1325 if (p)
1326 {
1327 const size_t length = (size_t)(p - username);
1328 *user = &p[1];
1329 *userlen = strlen(*user);
1330
1331 *domain = username;
1332 *domainlen = length;
1333 }
1334 else
1335 {
1336 /* Do not break up the name for '@'; both credSSP and the
1337 * ClientInfo PDU expect 'user@corp.net' to be transmitted
1338 * as username 'user@corp.net', domain empty (not nullptr!).
1339 */
1340 *user = username;
1341 *userlen = strlen(username);
1342 }
1343
1344 return TRUE;
1345}
1346
1347static BOOL freerdp_parse_username_settings(const char* username, rdpSettings* settings,
1348 FreeRDP_Settings_Keys_String userID,
1349 FreeRDP_Settings_Keys_String domainID)
1350{
1351 const char* user = nullptr;
1352 const char* domain = nullptr;
1353 size_t userlen = 0;
1354 size_t domainlen = 0;
1355
1356 const BOOL rc = freerdp_parse_username_ptr(username, &user, &userlen, &domain, &domainlen);
1357 if (!rc)
1358 return FALSE;
1359 if (!freerdp_settings_set_string_len(settings, userID, user, userlen))
1360 return FALSE;
1361 return freerdp_settings_set_string_len(settings, domainID, domain, domainlen);
1362}
1363
1364BOOL freerdp_parse_username(const char* username, char** puser, char** pdomain)
1365{
1366 const char* user = nullptr;
1367 const char* domain = nullptr;
1368 size_t userlen = 0;
1369 size_t domainlen = 0;
1370
1371 *puser = nullptr;
1372 *pdomain = nullptr;
1373
1374 const BOOL rc = freerdp_parse_username_ptr(username, &user, &userlen, &domain, &domainlen);
1375 if (!rc)
1376 return FALSE;
1377
1378 if (userlen > 0)
1379 {
1380 *puser = strndup(user, userlen);
1381 if (!*puser)
1382 return FALSE;
1383 }
1384
1385 if (domainlen > 0)
1386 {
1387 *pdomain = strndup(domain, domainlen);
1388 if (!*pdomain)
1389 {
1390 free(*puser);
1391 *puser = nullptr;
1392 return FALSE;
1393 }
1394 }
1395
1396 return TRUE;
1397}
1398
1399BOOL freerdp_parse_hostname(const char* hostname, char** host, int* port)
1400{
1401 char* p = nullptr;
1402 p = strrchr(hostname, ':');
1403
1404 if (p)
1405 {
1406 size_t length = (size_t)(p - hostname);
1407 LONGLONG val = 0;
1408
1409 if (!value_to_int(p + 1, &val, 1, UINT16_MAX))
1410 return FALSE;
1411
1412 *host = (char*)calloc(length + 1UL, sizeof(char));
1413
1414 if (!(*host))
1415 return FALSE;
1416
1417 CopyMemory(*host, hostname, length);
1418 (*host)[length] = '\0';
1419 *port = (UINT16)val;
1420 }
1421 else
1422 {
1423 *host = _strdup(hostname);
1424
1425 if (!(*host))
1426 return FALSE;
1427
1428 *port = -1;
1429 }
1430
1431 return TRUE;
1432}
1433
1434static BOOL freerdp_apply_connection_type(rdpSettings* settings, UINT32 type)
1435{
1436 struct network_settings
1437 {
1438 FreeRDP_Settings_Keys_Bool id;
1439 BOOL value[7];
1440 };
1441 const struct network_settings config[] = {
1442 { FreeRDP_DisableWallpaper, { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE } },
1443 { FreeRDP_AllowFontSmoothing, { FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE } },
1444 { FreeRDP_AllowDesktopComposition, { FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE } },
1445 { FreeRDP_DisableFullWindowDrag, { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE } },
1446 { FreeRDP_DisableMenuAnims, { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE } },
1447 { FreeRDP_DisableThemes, { TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE } }
1448 };
1449
1450 switch (type)
1451 {
1452 case CONNECTION_TYPE_INVALID:
1453 return TRUE;
1454
1455 case CONNECTION_TYPE_MODEM:
1456 case CONNECTION_TYPE_BROADBAND_LOW:
1457 case CONNECTION_TYPE_BROADBAND_HIGH:
1458 case CONNECTION_TYPE_SATELLITE:
1459 case CONNECTION_TYPE_WAN:
1460 case CONNECTION_TYPE_LAN:
1461 case CONNECTION_TYPE_AUTODETECT:
1462 break;
1463 default:
1464 WLog_WARN(TAG, "Unknown ConnectionType %" PRIu32 ", aborting", type);
1465 return FALSE;
1466 }
1467
1468 for (size_t x = 0; x < ARRAYSIZE(config); x++)
1469 {
1470 const struct network_settings* cur = &config[x];
1471 if (!freerdp_settings_set_bool(settings, cur->id, cur->value[type - 1]))
1472 return FALSE;
1473 }
1474 return TRUE;
1475}
1476
1477BOOL freerdp_set_connection_type(rdpSettings* settings, UINT32 type)
1478{
1479
1480 if (!freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, type))
1481 return FALSE;
1482
1483 switch (type)
1484 {
1485 case CONNECTION_TYPE_INVALID:
1486 case CONNECTION_TYPE_MODEM:
1487 case CONNECTION_TYPE_BROADBAND_LOW:
1488 case CONNECTION_TYPE_SATELLITE:
1489 case CONNECTION_TYPE_BROADBAND_HIGH:
1490 case CONNECTION_TYPE_WAN:
1491 case CONNECTION_TYPE_LAN:
1492 if (!freerdp_apply_connection_type(settings, type))
1493 return FALSE;
1494 break;
1495 case CONNECTION_TYPE_AUTODETECT:
1496 if (!freerdp_apply_connection_type(settings, type))
1497 return FALSE;
1498 /* Automatically activate GFX and RFX codec support */
1499#ifdef WITH_GFX_H264
1500 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, TRUE) ||
1501 !freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, TRUE) ||
1502 !freerdp_settings_set_bool(settings, FreeRDP_GfxH264, TRUE))
1503 return FALSE;
1504#endif
1505 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE) ||
1506 !freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE))
1507 return FALSE;
1508 break;
1509 default:
1510 WLog_WARN(TAG, "Unknown ConnectionType %" PRIu32 ", aborting", type);
1511 return FALSE;
1512 }
1513
1514 return TRUE;
1515}
1516
1517static UINT32 freerdp_get_keyboard_layout_for_type(const char* name, WINPR_ATTR_UNUSED DWORD type)
1518{
1519 UINT32 res = 0;
1520 size_t count = 0;
1521 RDP_KEYBOARD_LAYOUT* layouts =
1522 freerdp_keyboard_get_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD, &count);
1523
1524 if (!layouts || (count == 0))
1525 goto fail;
1526
1527 for (size_t x = 0; x < count; x++)
1528 {
1529 const RDP_KEYBOARD_LAYOUT* layout = &layouts[x];
1530 if (option_equals(layout->name, name))
1531 {
1532 res = layout->code;
1533 break;
1534 }
1535 }
1536
1537fail:
1538 freerdp_keyboard_layouts_free(layouts, count);
1539 return res;
1540}
1541
1542static UINT32 freerdp_map_keyboard_layout_name_to_id(const char* name)
1543{
1544 const UINT32 variants[] = { RDP_KEYBOARD_LAYOUT_TYPE_STANDARD, RDP_KEYBOARD_LAYOUT_TYPE_VARIANT,
1545 RDP_KEYBOARD_LAYOUT_TYPE_IME };
1546
1547 for (size_t x = 0; x < ARRAYSIZE(variants); x++)
1548 {
1549 UINT32 rc = freerdp_get_keyboard_layout_for_type(name, variants[x]);
1550 if (rc > 0)
1551 return rc;
1552 }
1553
1554 return 0;
1555}
1556
1557static int freerdp_detect_command_line_pre_filter(void* context, int index, int argc, LPSTR* argv)
1558{
1559 size_t length = 0;
1560 WINPR_UNUSED(context);
1561
1562 if (index == 1)
1563 {
1564 if (argc < index)
1565 return -1;
1566
1567 length = strlen(argv[index]);
1568
1569 if (length > 4)
1570 {
1571 if (option_is_rdp_file(argv[index]))
1572 {
1573 return 1;
1574 }
1575 }
1576
1577 if (length > 13)
1578 {
1579 if (option_is_incident_file(argv[index]))
1580 {
1581 return 1;
1582 }
1583 }
1584 }
1585
1586 return 0;
1587}
1588
1589static int freerdp_detect_windows_style_command_line_syntax(int argc, char** argv, size_t* count,
1590 BOOL ignoreUnknown)
1591{
1592 int status = 0;
1593 DWORD flags = 0;
1594 int detect_status = 0;
1595 const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
1596 COMMAND_LINE_ARGUMENT_A largs[ARRAYSIZE(global_cmd_args)];
1597 memcpy(largs, global_cmd_args, sizeof(global_cmd_args));
1598
1599 flags = COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_SILENCE_PARSER;
1600 flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
1601
1602 if (ignoreUnknown)
1603 {
1604 flags |= COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
1605 }
1606
1607 *count = 0;
1608 detect_status = 0;
1609 CommandLineClearArgumentsA(largs);
1610 status = CommandLineParseArgumentsA(argc, argv, largs, flags, nullptr,
1611 freerdp_detect_command_line_pre_filter, nullptr);
1612
1613 if (status < 0)
1614 return status;
1615
1616 arg = largs;
1617
1618 do
1619 {
1620 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
1621 continue;
1622
1623 (*count)++;
1624 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
1625
1626 return detect_status;
1627}
1628
1629static int freerdp_detect_posix_style_command_line_syntax(int argc, char** argv, size_t* count,
1630 BOOL ignoreUnknown)
1631{
1632 int status = 0;
1633 DWORD flags = 0;
1634 int detect_status = 0;
1635 const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
1636 COMMAND_LINE_ARGUMENT_A largs[ARRAYSIZE(global_cmd_args)];
1637 memcpy(largs, global_cmd_args, sizeof(global_cmd_args));
1638
1639 flags = COMMAND_LINE_SEPARATOR_SPACE | COMMAND_LINE_SILENCE_PARSER;
1640 flags |= COMMAND_LINE_SIGIL_DASH | COMMAND_LINE_SIGIL_DOUBLE_DASH;
1641 flags |= COMMAND_LINE_SIGIL_ENABLE_DISABLE;
1642
1643 if (ignoreUnknown)
1644 {
1645 flags |= COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
1646 }
1647
1648 *count = 0;
1649 detect_status = 0;
1650 CommandLineClearArgumentsA(largs);
1651 status = CommandLineParseArgumentsA(argc, argv, largs, flags, nullptr,
1652 freerdp_detect_command_line_pre_filter, nullptr);
1653
1654 if (status < 0)
1655 return status;
1656
1657 arg = largs;
1658
1659 do
1660 {
1661 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
1662 continue;
1663
1664 (*count)++;
1665 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
1666
1667 return detect_status;
1668}
1669
1670static BOOL freerdp_client_detect_command_line(int argc, char** argv, DWORD* flags)
1671{
1672 size_t posix_cli_count = 0;
1673 size_t windows_cli_count = 0;
1674 const BOOL ignoreUnknown = TRUE;
1675 const int windows_cli_status = freerdp_detect_windows_style_command_line_syntax(
1676 argc, argv, &windows_cli_count, ignoreUnknown);
1677 const int posix_cli_status =
1678 freerdp_detect_posix_style_command_line_syntax(argc, argv, &posix_cli_count, ignoreUnknown);
1679
1680 /* Default is POSIX syntax */
1681 *flags = COMMAND_LINE_SEPARATOR_SPACE;
1682 *flags |= COMMAND_LINE_SIGIL_DASH | COMMAND_LINE_SIGIL_DOUBLE_DASH;
1683 *flags |= COMMAND_LINE_SIGIL_ENABLE_DISABLE;
1684
1685 if (posix_cli_status <= COMMAND_LINE_STATUS_PRINT)
1686 return FALSE;
1687
1688 /* Check, if this may be windows style syntax... */
1689 if ((windows_cli_count && (windows_cli_count >= posix_cli_count)) ||
1690 (windows_cli_status <= COMMAND_LINE_STATUS_PRINT))
1691 {
1692 windows_cli_count = 1;
1693 *flags = COMMAND_LINE_SEPARATOR_COLON;
1694 *flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
1695 }
1696
1697 WLog_DBG(TAG, "windows: %d/%" PRIuz " posix: %d/%" PRIuz "", windows_cli_status,
1698 windows_cli_count, posix_cli_status, posix_cli_count);
1699 if ((posix_cli_count == 0) && (windows_cli_count == 0))
1700 {
1701 if ((posix_cli_status == COMMAND_LINE_ERROR) && (windows_cli_status == COMMAND_LINE_ERROR))
1702 return TRUE;
1703 }
1704 return FALSE;
1705}
1706
1707int freerdp_client_settings_command_line_status_print(rdpSettings* settings, int status, int argc,
1708 char** argv)
1709{
1710 return freerdp_client_settings_command_line_status_print_ex(settings, status, argc, argv,
1711 nullptr);
1712}
1713
1714static void freerdp_client_print_keyboard_type_list(const char* msg, DWORD type)
1715{
1716 size_t count = 0;
1717 RDP_KEYBOARD_LAYOUT* layouts = nullptr;
1718 layouts = freerdp_keyboard_get_layouts(type, &count);
1719
1720 printf("\n%s\n", msg);
1721
1722 for (size_t x = 0; x < count; x++)
1723 {
1724 const RDP_KEYBOARD_LAYOUT* layout = &layouts[x];
1725 printf("0x%08" PRIX32 "\t%s\n", layout->code, layout->name);
1726 }
1727
1728 freerdp_keyboard_layouts_free(layouts, count);
1729}
1730
1731static void freerdp_client_print_keyboard_list(void)
1732{
1733 freerdp_client_print_keyboard_type_list("Keyboard Layouts", RDP_KEYBOARD_LAYOUT_TYPE_STANDARD);
1734 freerdp_client_print_keyboard_type_list("Keyboard Layout Variants",
1735 RDP_KEYBOARD_LAYOUT_TYPE_VARIANT);
1736 freerdp_client_print_keyboard_type_list("Keyboard Layout Variants",
1737 RDP_KEYBOARD_LAYOUT_TYPE_IME);
1738}
1739
1740static void freerdp_client_print_timezone_list(void)
1741{
1742 DWORD index = 0;
1743 DYNAMIC_TIME_ZONE_INFORMATION info = WINPR_C_ARRAY_INIT;
1744 while (EnumDynamicTimeZoneInformation(index++, &info) != ERROR_NO_MORE_ITEMS)
1745 {
1746 char TimeZoneKeyName[ARRAYSIZE(info.TimeZoneKeyName) + 1] = WINPR_C_ARRAY_INIT;
1747
1748 (void)ConvertWCharNToUtf8(info.TimeZoneKeyName, ARRAYSIZE(info.TimeZoneKeyName),
1749 TimeZoneKeyName, ARRAYSIZE(TimeZoneKeyName));
1750 printf("%" PRIu32 ": '%s'\n", index, TimeZoneKeyName);
1751 }
1752}
1753
1754static void freerdp_client_print_tune_list(const rdpSettings* settings)
1755{
1756 SSIZE_T type = 0;
1757
1758 for (SSIZE_T x = 0; x < FreeRDP_Settings_StableAPI_MAX; x++)
1759 {
1760 const char* name = freerdp_settings_get_name_for_key(x);
1762
1763 // NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange)
1764 switch (type)
1765 {
1766 case RDP_SETTINGS_TYPE_BOOL:
1767 printf("%" PRIdz "\t%50s\tBOOL\t%s\n", x, name,
1768 freerdp_settings_get_bool(settings, (FreeRDP_Settings_Keys_Bool)x)
1769 ? "TRUE"
1770 : "FALSE");
1771 break;
1772 case RDP_SETTINGS_TYPE_UINT16:
1773 printf("%" PRIdz "\t%50s\tUINT16\t%" PRIu16 "\n", x, name,
1774 freerdp_settings_get_uint16(settings, (FreeRDP_Settings_Keys_UInt16)x));
1775 break;
1776 case RDP_SETTINGS_TYPE_INT16:
1777 printf("%" PRIdz "\t%50s\tINT16\t%" PRId16 "\n", x, name,
1778 freerdp_settings_get_int16(settings, (FreeRDP_Settings_Keys_Int16)x));
1779 break;
1780 case RDP_SETTINGS_TYPE_UINT32:
1781 printf("%" PRIdz "\t%50s\tUINT32\t%" PRIu32 "\n", x, name,
1782 freerdp_settings_get_uint32(settings, (FreeRDP_Settings_Keys_UInt32)x));
1783 break;
1784 case RDP_SETTINGS_TYPE_INT32:
1785 printf("%" PRIdz "\t%50s\tINT32\t%" PRId32 "\n", x, name,
1786 freerdp_settings_get_int32(settings, (FreeRDP_Settings_Keys_Int32)x));
1787 break;
1788 case RDP_SETTINGS_TYPE_UINT64:
1789 printf("%" PRIdz "\t%50s\tUINT64\t%" PRIu64 "\n", x, name,
1790 freerdp_settings_get_uint64(settings, (FreeRDP_Settings_Keys_UInt64)x));
1791 break;
1792 case RDP_SETTINGS_TYPE_INT64:
1793 printf("%" PRIdz "\t%50s\tINT64\t%" PRId64 "\n", x, name,
1794 freerdp_settings_get_int64(settings, (FreeRDP_Settings_Keys_Int64)x));
1795 break;
1796 case RDP_SETTINGS_TYPE_STRING:
1797 printf("%" PRIdz "\t%50s\tSTRING\t%s"
1798 "\n",
1799 x, name,
1800 freerdp_settings_get_string(settings, (FreeRDP_Settings_Keys_String)x));
1801 break;
1802 case RDP_SETTINGS_TYPE_POINTER:
1803 printf("%" PRIdz "\t%50s\tPOINTER\t%p"
1804 "\n",
1805 x, name,
1806 freerdp_settings_get_pointer(settings, (FreeRDP_Settings_Keys_Pointer)x));
1807 break;
1808 default:
1809 break;
1810 }
1811 // NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange)
1812 }
1813}
1814
1815static int evaluate_result(int argc, char* argv[], int rc, rdpSettings* settings,
1816 const COMMAND_LINE_ARGUMENT_A* largs)
1817{
1818 WINPR_ASSERT(settings);
1819 WINPR_ASSERT(largs);
1820
1821 if (rc != COMMAND_LINE_STATUS_PRINT)
1822 {
1823 freerdp_client_print_command_line_usage(argc, argv);
1824 return rc;
1825 }
1826
1827 const COMMAND_LINE_ARGUMENT_A* arg = CommandLineFindArgumentA(largs, "list");
1828 WINPR_ASSERT(arg);
1829
1830 if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)
1831 {
1832 if (option_equals("timezones", arg->Value))
1833 freerdp_client_print_timezone_list();
1834 else if (option_equals("tune", arg->Value))
1835 freerdp_client_print_tune_list(settings);
1836 else if (option_equals("kbd", arg->Value))
1837 freerdp_client_print_keyboard_list();
1838 else if (option_starts_with("kbd-lang", arg->Value))
1839 {
1840 const char* val = nullptr;
1841 if (option_starts_with("kbd-lang:", arg->Value))
1842 val = &arg->Value[9];
1843 else if (!option_equals("kbd-lang", arg->Value))
1844 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1845
1846 if (val && strchr(val, ','))
1847 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1848 freerdp_client_print_codepages(val);
1849 }
1850 else if (option_equals("kbd-scancode", arg->Value))
1851 freerdp_client_print_scancodes();
1852 else if (option_equals("monitor", arg->Value))
1853 {
1854 if (!freerdp_settings_set_bool(settings, FreeRDP_ListMonitors, TRUE))
1855 return COMMAND_LINE_ERROR;
1856 }
1857 else if (option_starts_with("smartcard", arg->Value))
1858 {
1859 BOOL opts = FALSE;
1860 if (option_starts_with("smartcard:", arg->Value))
1861 opts = TRUE;
1862 else if (!option_equals("smartcard", arg->Value))
1863 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1864
1865 if (opts)
1866 {
1867 const char* sub = strchr(arg->Value, ':') + 1;
1868 const CmdLineSubOptions options[] = {
1869 { "pkinit-anchors:", FreeRDP_PkinitAnchors, CMDLINE_SUBOPTION_STRING, nullptr },
1870 { "pkcs11-module:", FreeRDP_Pkcs11Module, CMDLINE_SUBOPTION_STRING, nullptr }
1871 };
1872
1873 size_t count = 0;
1874
1875 char** ptr = CommandLineParseCommaSeparatedValuesEx("smartcard", sub, &count);
1876 if (!ptr)
1877 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1878 if (count < 2)
1879 {
1880 CommandLineParserFree(ptr);
1881 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1882 }
1883
1884 for (size_t x = 1; x < count; x++)
1885 {
1886 const char* cur = ptr[x];
1887 if (!parseSubOptions(settings, options, ARRAYSIZE(options), cur))
1888 {
1889 CommandLineParserFree(ptr);
1890 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
1891 }
1892 }
1893
1894 CommandLineParserFree(ptr);
1895 }
1896
1897 freerdp_smartcard_list(settings);
1898 }
1899 else
1900 {
1901 freerdp_client_print_command_line_usage(argc, argv);
1902 return COMMAND_LINE_ERROR;
1903 }
1904 }
1905#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
1906 arg = CommandLineFindArgumentA(largs, "tune-list");
1907 WINPR_ASSERT(arg);
1908
1909 if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)
1910 {
1911 WLog_WARN(TAG, "Option /tune-list is deprecated, use /list:tune instead");
1912 freerdp_client_print_tune_list(settings);
1913 }
1914
1915 arg = CommandLineFindArgumentA(largs, "kbd-lang-list");
1916 WINPR_ASSERT(arg);
1917
1918 if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)
1919 {
1920 WLog_WARN(TAG, "Option /kbd-lang-list is deprecated, use /list:kbd-lang instead");
1921 freerdp_client_print_codepages(arg->Value);
1922 }
1923
1924 arg = CommandLineFindArgumentA(largs, "kbd-list");
1925 WINPR_ASSERT(arg);
1926
1927 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
1928 {
1929 WLog_WARN(TAG, "Option /kbd-list is deprecated, use /list:kbd instead");
1930 freerdp_client_print_keyboard_list();
1931 }
1932
1933 arg = CommandLineFindArgumentA(largs, "monitor-list");
1934 WINPR_ASSERT(arg);
1935
1936 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
1937 {
1938 WLog_WARN(TAG, "Option /monitor-list is deprecated, use /list:monitor instead");
1939 if (!freerdp_settings_set_bool(settings, FreeRDP_ListMonitors, TRUE))
1940 return COMMAND_LINE_ERROR;
1941 }
1942
1943 arg = CommandLineFindArgumentA(largs, "smartcard-list");
1944 WINPR_ASSERT(arg);
1945
1946 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
1947 {
1948 WLog_WARN(TAG, "Option /smartcard-list is deprecated, use /list:smartcard instead");
1949 freerdp_smartcard_list(settings);
1950 }
1951
1952 arg = CommandLineFindArgumentA(largs, "kbd-scancode-list");
1953 WINPR_ASSERT(arg);
1954
1955 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
1956 {
1957 WLog_WARN(TAG,
1958 "Option /kbd-scancode-list is deprecated, use /list:kbd-scancode instead");
1959 freerdp_client_print_scancodes();
1960 return COMMAND_LINE_STATUS_PRINT;
1961 }
1962#endif
1963 return COMMAND_LINE_STATUS_PRINT;
1964}
1965
1966int freerdp_client_settings_command_line_status_print_ex(rdpSettings* settings, int status,
1967 int argc, char** argv,
1968 const COMMAND_LINE_ARGUMENT_A* custom)
1969{
1970 if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
1971 {
1972 freerdp_client_print_version();
1973 goto out;
1974 }
1975
1976 if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
1977 {
1978 freerdp_client_print_version_ex(argc, argv);
1979 freerdp_client_print_buildconfig_ex(argc, argv);
1980 goto out;
1981 }
1982 else if (status == COMMAND_LINE_STATUS_PRINT)
1983 {
1984 const DWORD flags =
1985 COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_SIGIL_PLUS_MINUS | COMMAND_LINE_SIGIL_SLASH;
1986
1987 size_t customcount = 0;
1988 {
1989 const COMMAND_LINE_ARGUMENT_A* cur = custom;
1990 while (cur && cur->Name)
1991 {
1992 customcount++;
1993 cur++;
1994 }
1995 }
1996 size_t globalcount = 0;
1997 {
1998 const COMMAND_LINE_ARGUMENT_A* cur = global_cmd_args;
1999 while (cur && cur->Name)
2000 {
2001 globalcount++;
2002 cur++;
2003 }
2004 }
2005
2007 calloc(1ull + customcount + globalcount, sizeof(COMMAND_LINE_ARGUMENT_A));
2008 if (!largs)
2009 goto out;
2010 memcpy(largs, global_cmd_args, globalcount * sizeof(COMMAND_LINE_ARGUMENT_A));
2011 if (custom)
2012 memcpy(&largs[globalcount], custom, customcount * sizeof(COMMAND_LINE_ARGUMENT_A));
2013
2014 const int rc =
2015 CommandLineParseArgumentsA(argc, argv, largs, flags, nullptr, nullptr, nullptr);
2016 status = evaluate_result(argc, argv, rc, settings, largs);
2017 free(largs);
2018 goto out;
2019 }
2020 else if (status == COMMAND_LINE_STATUS_PRINT_HELP)
2021 {
2022 freerdp_client_print_command_line_help_ex(argc, argv, custom);
2023 goto out;
2024 }
2025 else if (status < 0)
2026 {
2027 freerdp_client_print_command_line_usage(argc, argv);
2028 goto out;
2029 }
2030
2031out:
2032 if (status <= COMMAND_LINE_STATUS_PRINT && status >= COMMAND_LINE_STATUS_PRINT_LAST)
2033 return 0;
2034 return status;
2035}
2036
2045static BOOL parseSizeValue(const char* input, unsigned long* v1, unsigned long* v2)
2046{
2047 const char* xcharpos = nullptr;
2048 char* endPtr = nullptr;
2049 unsigned long v = 0;
2050 errno = 0;
2051 v = strtoul(input, &endPtr, 10);
2052
2053 if ((v == 0 || v == ULONG_MAX) && (errno != 0))
2054 return FALSE;
2055
2056 if (v1)
2057 *v1 = v;
2058
2059 xcharpos = strchr(input, 'x');
2060
2061 if (!xcharpos || xcharpos != endPtr)
2062 return FALSE;
2063
2064 errno = 0;
2065 v = strtoul(xcharpos + 1, &endPtr, 10);
2066
2067 if ((v == 0 || v == ULONG_MAX) && (errno != 0))
2068 return FALSE;
2069
2070 if (*endPtr != '\0')
2071 return FALSE;
2072
2073 if (v2)
2074 *v2 = v;
2075
2076 return TRUE;
2077}
2078
2079static BOOL prepare_default_settings(rdpSettings* settings, COMMAND_LINE_ARGUMENT_A* args,
2080 BOOL rdp_file)
2081{
2082 const char* arguments[] = { "network", "gfx", "rfx", "bpp" };
2083 WINPR_ASSERT(settings);
2084 WINPR_ASSERT(args);
2085
2086 if (rdp_file)
2087 return FALSE;
2088
2089 for (size_t x = 0; x < ARRAYSIZE(arguments); x++)
2090 {
2091 const char* arg = arguments[x];
2092 const COMMAND_LINE_ARGUMENT_A* p = CommandLineFindArgumentA(args, arg);
2093 if (p && (p->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
2094 return FALSE;
2095 }
2096
2097 return freerdp_set_connection_type(settings, CONNECTION_TYPE_AUTODETECT);
2098}
2099
2100static BOOL setSmartcardEmulation(WINPR_ATTR_UNUSED const char* value, rdpSettings* settings)
2101{
2102 return freerdp_settings_set_bool(settings, FreeRDP_SmartcardEmulation, TRUE);
2103}
2104
2105const char* option_starts_with(const char* what, const char* val)
2106{
2107 WINPR_ASSERT(what);
2108 WINPR_ASSERT(val);
2109 const size_t wlen = strlen(what);
2110
2111 if (_strnicmp(what, val, wlen) != 0)
2112 return nullptr;
2113 return &val[wlen];
2114}
2115
2116BOOL option_ends_with(const char* str, const char* ext)
2117{
2118 WINPR_ASSERT(str);
2119 WINPR_ASSERT(ext);
2120 const size_t strLen = strlen(str);
2121 const size_t extLen = strlen(ext);
2122
2123 if (strLen < extLen)
2124 return FALSE;
2125
2126 return _strnicmp(&str[strLen - extLen], ext, extLen) == 0;
2127}
2128
2129BOOL option_equals(const char* what, const char* val)
2130{
2131 WINPR_ASSERT(what);
2132 WINPR_ASSERT(val);
2133 return _stricmp(what, val) == 0;
2134}
2135
2136typedef enum
2137{
2138 PARSE_ON,
2139 PARSE_OFF,
2140 PARSE_NONE,
2141 PARSE_FAIL
2142} PARSE_ON_OFF_RESULT;
2143
2144static PARSE_ON_OFF_RESULT parse_on_off_option(const char* value)
2145{
2146 WINPR_ASSERT(value);
2147 const char* sep = strchr(value, ':');
2148 if (!sep)
2149 return PARSE_NONE;
2150 if (option_equals("on", &sep[1]))
2151 return PARSE_ON;
2152 if (option_equals("off", &sep[1]))
2153 return PARSE_OFF;
2154 return PARSE_FAIL;
2155}
2156
2157typedef enum
2158{
2159 CLIP_DIR_PARSE_ALL,
2160 CLIP_DIR_PARSE_OFF,
2161 CLIP_DIR_PARSE_LOCAL,
2162 CLIP_DIR_PARSE_REMOTE,
2163 CLIP_DIR_PARSE_FAIL
2164} PARSE_CLIP_DIR_RESULT;
2165
2166static PARSE_CLIP_DIR_RESULT parse_clip_direciton_to_option(const char* value)
2167{
2168 WINPR_ASSERT(value);
2169 const char* sep = strchr(value, ':');
2170 if (!sep)
2171 return CLIP_DIR_PARSE_FAIL;
2172 if (option_equals("all", &sep[1]))
2173 return CLIP_DIR_PARSE_ALL;
2174 if (option_equals("off", &sep[1]))
2175 return CLIP_DIR_PARSE_OFF;
2176 if (option_equals("local", &sep[1]))
2177 return CLIP_DIR_PARSE_LOCAL;
2178 if (option_equals("remote", &sep[1]))
2179 return CLIP_DIR_PARSE_REMOTE;
2180 return CLIP_DIR_PARSE_FAIL;
2181}
2182
2183static int parse_tls_ciphers(rdpSettings* settings, const char* Value)
2184{
2185 const char* ciphers = nullptr;
2186 if (!Value)
2187 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2188
2189 if (option_equals(Value, "netmon"))
2190 {
2191 ciphers = "ALL:!ECDH:!ADH:!DHE";
2192 }
2193 else if (option_equals(Value, "ma"))
2194 {
2195 ciphers = "AES128-SHA";
2196 }
2197 else
2198 {
2199 ciphers = Value;
2200 }
2201
2202 if (!freerdp_settings_set_string(settings, FreeRDP_AllowedTlsCiphers, ciphers))
2203 return COMMAND_LINE_ERROR_MEMORY;
2204 return 0;
2205}
2206
2207static int parse_tls_seclevel(rdpSettings* settings, const char* Value)
2208{
2209 LONGLONG val = 0;
2210
2211 if (!value_to_int(Value, &val, 0, 5))
2212 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2213
2214 if (!freerdp_settings_set_uint32(settings, FreeRDP_TlsSecLevel, (UINT32)val))
2215 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2216 return 0;
2217}
2218
2219static int parse_tls_secrets_file(rdpSettings* settings, const char* Value)
2220{
2221 if (!Value)
2222 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2223
2224 if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, Value))
2225 return COMMAND_LINE_ERROR_MEMORY;
2226 return 0;
2227}
2228
2229static int parse_tls_enforce(rdpSettings* settings, const char* Value)
2230{
2231 UINT16 version = TLS1_2_VERSION;
2232
2233 if (Value)
2234 {
2235 struct map_t
2236 {
2237 const char* name;
2238 UINT16 version;
2239 };
2240 const struct map_t map[] = { { "1.0", TLS1_VERSION },
2241 { "1.1", TLS1_1_VERSION },
2242 { "1.2", TLS1_2_VERSION }
2243#if defined(TLS1_3_VERSION)
2244 ,
2245 { "1.3", TLS1_3_VERSION }
2246#endif
2247 };
2248
2249 const struct map_t* found = nullptr;
2250 for (size_t x = 0; x < ARRAYSIZE(map); x++)
2251 {
2252 const struct map_t* cur = &map[x];
2253 if (option_equals(cur->name, Value))
2254 {
2255 found = cur;
2256 break;
2257 }
2258 }
2259
2260 if (!found)
2261 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2262 version = found->version;
2263 }
2264
2265 if (!(freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, version) &&
2266 freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, version)))
2267 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2268 return 0;
2269}
2270
2271static int parse_tls_cipher_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2272{
2273 int rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2274 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "tls")
2275 {
2276 if (option_starts_with("ciphers:", arg->Value))
2277 rc = fail_at(arg, parse_tls_ciphers(settings, &arg->Value[8]));
2278 else if (option_starts_with("seclevel:", arg->Value))
2279 rc = fail_at(arg, parse_tls_seclevel(settings, &arg->Value[9]));
2280 else if (option_starts_with("secrets-file:", arg->Value))
2281 rc = fail_at(arg, parse_tls_secrets_file(settings, &arg->Value[13]));
2282 else if (option_starts_with("enforce:", arg->Value))
2283 rc = fail_at(arg, parse_tls_enforce(settings, &arg->Value[8]));
2284 }
2285
2286#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
2287 CommandLineSwitchCase(arg, "tls-ciphers")
2288 {
2289 WLog_WARN(TAG, "Option /tls-ciphers is deprecated, use /tls:ciphers instead");
2290 rc = fail_at(arg, parse_tls_ciphers(settings, arg->Value));
2291 }
2292 CommandLineSwitchCase(arg, "tls-seclevel")
2293 {
2294 WLog_WARN(TAG, "Option /tls-seclevel is deprecated, use /tls:seclevel instead");
2295 rc = fail_at(arg, parse_tls_seclevel(settings, arg->Value));
2296 }
2297 CommandLineSwitchCase(arg, "tls-secrets-file")
2298 {
2299 WLog_WARN(TAG, "Option /tls-secrets-file is deprecated, use /tls:secrets-file instead");
2300 rc = fail_at(arg, parse_tls_secrets_file(settings, arg->Value));
2301 }
2302 CommandLineSwitchCase(arg, "enforce-tlsv1_2")
2303 {
2304 WLog_WARN(TAG, "Option /enforce-tlsv1_2 is deprecated, use /tls:enforce:1.2 instead");
2305 rc = fail_at(arg, parse_tls_enforce(settings, "1.2"));
2306 }
2307#endif
2308 CommandLineSwitchDefault(arg)
2309 {
2310 }
2311 CommandLineSwitchEnd(arg)
2312
2313 return rc;
2314}
2315
2316static int parse_tls_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2317{
2318 WINPR_ASSERT(settings);
2319 WINPR_ASSERT(arg);
2320
2321 size_t count = 0;
2322 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
2323 for (size_t x = 0; x < count; x++)
2324 {
2325 COMMAND_LINE_ARGUMENT_A larg = *arg;
2326 larg.Value = ptr[x];
2327
2328 int rc = parse_tls_cipher_options(settings, &larg);
2329 if (rc != 0)
2330 {
2331 CommandLineParserFree(ptr);
2332 return rc;
2333 }
2334 }
2335 CommandLineParserFree(ptr);
2336 return 0;
2337}
2338
2339static int parse_gfx_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2340{
2341 WINPR_ASSERT(settings);
2342 WINPR_ASSERT(arg);
2343
2344 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE))
2345 return COMMAND_LINE_ERROR;
2346
2347 if (arg->Value)
2348 {
2349 int rc = CHANNEL_RC_OK;
2350 size_t count = 0;
2351 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
2352 if (!ptr || (count == 0))
2353 rc = COMMAND_LINE_ERROR;
2354 else
2355 {
2356 BOOL GfxH264 = FALSE;
2357 BOOL GfxAVC444 = FALSE;
2358 BOOL RemoteFxCodec = FALSE;
2359 BOOL GfxProgressive = FALSE;
2360 BOOL codecSelected = FALSE;
2361
2362 for (size_t x = 0; x < count; x++)
2363 {
2364 const char* val = ptr[x];
2365#ifdef WITH_GFX_H264
2366 if (option_starts_with("AVC444", val))
2367 {
2368 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2369 if (bval == PARSE_FAIL)
2370 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2371 else
2372 GfxAVC444 = bval != PARSE_OFF;
2373 codecSelected = TRUE;
2374 }
2375 else if (option_starts_with("AVC420", val))
2376 {
2377 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2378 if (bval == PARSE_FAIL)
2379 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2380 else
2381 GfxH264 = bval != PARSE_OFF;
2382 codecSelected = TRUE;
2383 }
2384 else
2385#endif
2386 if (option_starts_with("RFX", val))
2387 {
2388 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2389 if (bval == PARSE_FAIL)
2390 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2391 else
2392 RemoteFxCodec = bval != PARSE_OFF;
2393 codecSelected = TRUE;
2394 }
2395 else if (option_starts_with("progressive", val))
2396 {
2397 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2398 if (bval == PARSE_FAIL)
2399 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2400 else
2401 GfxProgressive = bval != PARSE_OFF;
2402 codecSelected = TRUE;
2403 }
2404 else if (option_starts_with("mask:", val))
2405 {
2406 ULONGLONG v = 0;
2407 const char* uv = &val[5];
2408 if (!value_to_uint(uv, &v, 0, UINT32_MAX))
2409 rc = COMMAND_LINE_ERROR;
2410 else
2411 {
2412 if (!freerdp_settings_set_uint32(settings, FreeRDP_GfxCapsFilter,
2413 (UINT32)v))
2414 rc = COMMAND_LINE_ERROR;
2415 }
2416 }
2417 else if (option_starts_with("small-cache", val))
2418 {
2419 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2420 if (bval == PARSE_FAIL)
2421 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2422 else if (!freerdp_settings_set_bool(settings, FreeRDP_GfxSmallCache,
2423 bval != PARSE_OFF))
2424 rc = COMMAND_LINE_ERROR;
2425 }
2426 else if (option_starts_with("thin-client", val))
2427 {
2428 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2429 if (bval == PARSE_FAIL)
2430 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2431 else if (!freerdp_settings_set_bool(settings, FreeRDP_GfxThinClient,
2432 bval != PARSE_OFF))
2433 rc = COMMAND_LINE_ERROR;
2434 if ((rc == CHANNEL_RC_OK) && (bval > 0))
2435 {
2436 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxSmallCache,
2437 bval != PARSE_OFF))
2438 rc = COMMAND_LINE_ERROR;
2439 }
2440 }
2441 else if (option_starts_with("frame-ack", val))
2442 {
2443 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2444 if (bval == PARSE_FAIL)
2445 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2446 else if (!freerdp_settings_set_bool(settings, FreeRDP_GfxSuspendFrameAck,
2447 bval == PARSE_OFF))
2448 rc = COMMAND_LINE_ERROR;
2449 }
2450#if defined(WITH_GFX_AV1)
2451 else if (option_starts_with("AV1", val))
2452 {
2453 uint32_t profile = 1;
2454 BOOL enabled = FALSE;
2455 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
2456 if (bval == PARSE_FAIL)
2457 {
2458 if (_stricmp("av1:i420", val) == 0)
2459 {
2460 enabled = TRUE;
2461 profile = 0;
2462 }
2463 else if (_stricmp("av1:i444", val) == 0)
2464 {
2465 enabled = TRUE;
2466 profile = 1;
2467 }
2468 else
2469 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2470 }
2471 else
2472 enabled = bval == PARSE_ON;
2473
2474 if (enabled || (bval != PARSE_FAIL))
2475 {
2476 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxCodecAV1, enabled))
2477 rc = COMMAND_LINE_ERROR;
2478 else if (!freerdp_settings_set_uint32(settings, FreeRDP_GfxCodecAV1Profile,
2479 profile))
2480 rc = COMMAND_LINE_ERROR;
2481 }
2482 }
2483#endif
2484 else
2485 rc = COMMAND_LINE_ERROR;
2486 }
2487
2488 if ((rc == CHANNEL_RC_OK) && codecSelected)
2489 {
2490 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, GfxAVC444))
2491 rc = COMMAND_LINE_ERROR;
2492 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, GfxAVC444))
2493 rc = COMMAND_LINE_ERROR;
2494 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, GfxH264))
2495 rc = COMMAND_LINE_ERROR;
2496 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, RemoteFxCodec))
2497 rc = COMMAND_LINE_ERROR;
2498 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, GfxProgressive))
2499 rc = COMMAND_LINE_ERROR;
2500 }
2501 }
2502 CommandLineParserFree(ptr);
2503 if (rc != CHANNEL_RC_OK)
2504 return rc;
2505 }
2506 return CHANNEL_RC_OK;
2507}
2508
2509static int parse_kbd_layout(rdpSettings* settings, const char* value)
2510{
2511 WINPR_ASSERT(settings);
2512 WINPR_ASSERT(value);
2513
2514 int rc = 0;
2515 LONGLONG ival = 0;
2516 const BOOL isInt = value_to_int(value, &ival, 1, UINT32_MAX);
2517 if (!isInt)
2518 {
2519 ival = freerdp_map_keyboard_layout_name_to_id(value);
2520
2521 if (ival == 0)
2522 {
2523 WLog_ERR(TAG, "Could not identify keyboard layout: %s", value);
2524 WLog_ERR(TAG, "Use /list:kbd to list available layouts");
2525 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2526 }
2527 }
2528
2529 if (rc == 0)
2530 {
2531 if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, (UINT32)ival))
2532 rc = COMMAND_LINE_ERROR;
2533 }
2534 return rc;
2535}
2536
2537#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
2538static int parse_codec_cache_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2539{
2540 WINPR_ASSERT(settings);
2541 WINPR_ASSERT(arg);
2542
2543 if (!arg->Value)
2544 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2545 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheV3Enabled, TRUE))
2546 return COMMAND_LINE_ERROR;
2547
2548 if (option_equals(arg->Value, "rfx"))
2549 {
2550 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
2551 return COMMAND_LINE_ERROR;
2552 }
2553 else if (option_equals(arg->Value, "nsc"))
2554 {
2555 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, TRUE))
2556 return COMMAND_LINE_ERROR;
2557 }
2558
2559#if defined(WITH_JPEG)
2560 else if (option_equals(arg->Value, "jpeg"))
2561 {
2562 if (!freerdp_settings_set_bool(settings, FreeRDP_JpegCodec, TRUE))
2563 return COMMAND_LINE_ERROR;
2564
2565 if (freerdp_settings_get_uint32(settings, FreeRDP_JpegQuality) == 0)
2566 {
2567 if (!freerdp_settings_set_uint32(settings, FreeRDP_JpegQuality, 75))
2568 return COMMAND_LINE_ERROR;
2569 }
2570 }
2571
2572#endif
2573 return 0;
2574}
2575#endif
2576
2577static BOOL check_kbd_remap_valid(const char* token)
2578{
2579 UINT32 key = 0;
2580 UINT32 value = 0;
2581
2582 WINPR_ASSERT(token);
2583 /* The remapping is only allowed for scancodes, so maximum is 999=999 */
2584 if (strlen(token) > 10)
2585 return FALSE;
2586
2587 if (!freerdp_extract_key_value(token, &key, &value))
2588 {
2589 WLog_WARN(TAG, "/kbd:remap invalid entry '%s'", token);
2590 return FALSE;
2591 }
2592 return TRUE;
2593}
2594
2595static int parse_host_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2596{
2597 WINPR_ASSERT(settings);
2598 WINPR_ASSERT(arg);
2599
2600 if (!arg->Value)
2601 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2602 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, nullptr))
2603 return COMMAND_LINE_ERROR_MEMORY;
2604 char* p = strchr(arg->Value, '[');
2605
2606 /* ipv4 */
2607 if (!p)
2608 {
2609 const char scheme[] = "://";
2610 const char* val = strstr(arg->Value, scheme);
2611 if (val)
2612 val += strnlen(scheme, sizeof(scheme));
2613 else
2614 val = arg->Value;
2615 p = strchr(val, ':');
2616
2617 if (p)
2618 {
2619 LONGLONG lval = 0;
2620 size_t length = 0;
2621
2622 if (!value_to_int(&p[1], &lval, 1, UINT16_MAX))
2623 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2624
2625 length = (size_t)(p - arg->Value);
2626 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT16)lval))
2627 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2628 if (!freerdp_settings_set_string_len(settings, FreeRDP_ServerHostname, arg->Value,
2629 length))
2630 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2631 }
2632 else
2633 {
2634 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, arg->Value))
2635 return COMMAND_LINE_ERROR_MEMORY;
2636 }
2637 }
2638 else /* ipv6 */
2639 {
2640 size_t length = 0;
2641 char* p2 = strchr(arg->Value, ']');
2642
2643 /* not a valid [] ipv6 addr found */
2644 if (!p2)
2645 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2646
2647 length = (size_t)(p2 - p);
2648 if (!freerdp_settings_set_string_len(settings, FreeRDP_ServerHostname, p + 1, length - 1))
2649 return COMMAND_LINE_ERROR_MEMORY;
2650
2651 if (*(p2 + 1) == ':')
2652 {
2653 LONGLONG val = 0;
2654
2655 if (!value_to_int(&p2[2], &val, 0, UINT16_MAX))
2656 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2657
2658 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, (UINT16)val))
2659 return COMMAND_LINE_ERROR;
2660 }
2661
2662 printf("hostname %s port %" PRIu32 "\n",
2663 freerdp_settings_get_string(settings, FreeRDP_ServerHostname),
2664 freerdp_settings_get_uint32(settings, FreeRDP_ServerPort));
2665 }
2666 return 0;
2667}
2668
2669static int parse_redirect_prefer_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2670{
2671 WINPR_ASSERT(settings);
2672 WINPR_ASSERT(arg);
2673
2674 size_t count = 0;
2675 char* cur = arg->Value;
2676 if (!arg->Value)
2677 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2678 if (!freerdp_settings_set_uint32(settings, FreeRDP_RedirectionPreferType, 0))
2679 return COMMAND_LINE_ERROR;
2680
2681 UINT32 value = 0;
2682 do
2683 {
2684 UINT32 mask = 0;
2685 char* next = strchr(cur, ',');
2686
2687 if (next)
2688 {
2689 *next = '\0';
2690 next++;
2691 }
2692
2693 if (option_equals("fqdn", cur))
2694 mask = 0x06U;
2695 else if (option_equals("ip", cur))
2696 mask = 0x05U;
2697 else if (option_equals("netbios", cur))
2698 mask = 0x03U;
2699 else
2700 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2701
2702 cur = next;
2703 mask = (mask & 0x07);
2704 value |= mask << (count * 3);
2705 count++;
2706 } while (cur != nullptr);
2707
2708 if (!freerdp_settings_set_uint32(settings, FreeRDP_RedirectionPreferType, value))
2709 return COMMAND_LINE_ERROR;
2710
2711 if (count > 3)
2712 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2713 return 0;
2714}
2715
2716static int parse_prevent_session_lock_options(rdpSettings* settings,
2717 const COMMAND_LINE_ARGUMENT_A* arg)
2718{
2719 WINPR_ASSERT(settings);
2720 WINPR_ASSERT(arg);
2721
2722 if (!freerdp_settings_set_uint32(settings, FreeRDP_FakeMouseMotionInterval, 180))
2723 return COMMAND_LINE_ERROR_MEMORY;
2724
2725 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
2726 {
2727 LONGLONG val = 0;
2728
2729 if (!value_to_int(arg->Value, &val, 1, UINT32_MAX))
2730 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2731
2732 if (!freerdp_settings_set_uint32(settings, FreeRDP_FakeMouseMotionInterval, (UINT32)val))
2733 return COMMAND_LINE_ERROR_MEMORY;
2734 }
2735
2736 return 0;
2737}
2738
2739static int parse_vmconnect_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2740{
2741 WINPR_ASSERT(settings);
2742 WINPR_ASSERT(arg);
2743
2744 if (!freerdp_settings_set_bool(settings, FreeRDP_VmConnectMode, TRUE))
2745 return COMMAND_LINE_ERROR;
2746
2747 UINT32 port = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
2748 if (port == 3389)
2749 port = 2179;
2750
2751 if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, port))
2752 return COMMAND_LINE_ERROR;
2753 if (!freerdp_settings_set_bool(settings, FreeRDP_NegotiateSecurityLayer, FALSE))
2754 return COMMAND_LINE_ERROR;
2755
2756 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
2757 {
2758 if (!freerdp_settings_set_bool(settings, FreeRDP_SendPreconnectionPdu, TRUE))
2759 return COMMAND_LINE_ERROR;
2760
2761 if (!freerdp_settings_set_string(settings, FreeRDP_PreconnectionBlob, arg->Value))
2762 return COMMAND_LINE_ERROR_MEMORY;
2763 }
2764 return 0;
2765}
2766
2767static int parse_size_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2768{
2769 int status = 0;
2770 WINPR_ASSERT(settings);
2771 WINPR_ASSERT(arg);
2772
2773 if (!arg->Value)
2774 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2775 char* p = strchr(arg->Value, 'x');
2776
2777 if (p)
2778 {
2779 unsigned long w = 0;
2780 unsigned long h = 0;
2781
2782 if (!parseSizeValue(arg->Value, &w, &h) || (w > UINT16_MAX) || (h > UINT16_MAX))
2783 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2784
2785 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, (UINT32)w))
2786 return COMMAND_LINE_ERROR;
2787 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, (UINT32)h))
2788 return COMMAND_LINE_ERROR;
2789 }
2790 else
2791 {
2792 char* str = _strdup(arg->Value);
2793 if (!str)
2794 return COMMAND_LINE_ERROR_MEMORY;
2795
2796 p = strchr(str, '%');
2797
2798 if (p)
2799 {
2800 BOOL partial = FALSE;
2801
2802 status = COMMAND_LINE_ERROR;
2803 if (strchr(p, 'w'))
2804 {
2805 if (!freerdp_settings_set_bool(settings, FreeRDP_PercentScreenUseWidth, TRUE))
2806 goto fail;
2807 partial = TRUE;
2808 }
2809
2810 if (strchr(p, 'h'))
2811 {
2812 if (!freerdp_settings_set_bool(settings, FreeRDP_PercentScreenUseHeight, TRUE))
2813 goto fail;
2814 partial = TRUE;
2815 }
2816
2817 if (!partial)
2818 {
2819 if (!freerdp_settings_set_bool(settings, FreeRDP_PercentScreenUseWidth, TRUE))
2820 goto fail;
2821 if (!freerdp_settings_set_bool(settings, FreeRDP_PercentScreenUseHeight, TRUE))
2822 goto fail;
2823 }
2824
2825 *p = '\0';
2826 {
2827 LONGLONG val = 0;
2828
2829 if (!value_to_int(str, &val, 0, 100))
2830 {
2831 status = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2832 goto fail;
2833 }
2834
2835 if (!freerdp_settings_set_uint32(settings, FreeRDP_PercentScreen, (UINT32)val))
2836 goto fail;
2837 }
2838
2839 status = 0;
2840 }
2841
2842 fail:
2843 free(str);
2844 }
2845
2846 return status;
2847}
2848
2849static int parse_monitors_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2850{
2851 WINPR_ASSERT(settings);
2852 WINPR_ASSERT(arg);
2853
2854 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
2855 {
2856 size_t count = 0;
2857 UINT32* MonitorIds = nullptr;
2858 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
2859
2860 if (!ptr)
2861 return COMMAND_LINE_ERROR_MEMORY;
2862
2863 if (count > 16)
2864 count = 16;
2865
2866 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_MonitorIds, nullptr, count))
2867 {
2868 CommandLineParserFree(ptr);
2869 return FALSE;
2870 }
2871
2872 MonitorIds = freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorIds, 0);
2873 for (UINT32 i = 0; i < count; i++)
2874 {
2875 LONGLONG val = 0;
2876
2877 if (!value_to_int(ptr[i], &val, 0, UINT16_MAX))
2878 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2879
2880 MonitorIds[i] = (UINT32)val;
2881 }
2882
2883 CommandLineParserFree(ptr);
2884 }
2885
2886 return 0;
2887}
2888
2889static int parse_dynamic_resolution_options(rdpSettings* settings,
2890 const COMMAND_LINE_ARGUMENT_A* arg)
2891{
2892 WINPR_ASSERT(settings);
2893 WINPR_ASSERT(arg);
2894
2895 const BOOL val = arg->Value != nullptr;
2896
2897 if (val && freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
2898 {
2899 WLog_ERR(TAG, "Smart sizing and dynamic resolution are mutually exclusive options");
2900 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2901 }
2902
2903 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl, val))
2904 return COMMAND_LINE_ERROR;
2905 if (!freerdp_settings_set_bool(settings, FreeRDP_DynamicResolutionUpdate, val))
2906 return COMMAND_LINE_ERROR;
2907
2908 return 0;
2909}
2910
2911static int parse_smart_sizing_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2912{
2913 WINPR_ASSERT(settings);
2914 WINPR_ASSERT(arg);
2915
2916 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
2917 {
2918 WLog_ERR(TAG, "Smart sizing and dynamic resolution are mutually exclusive options");
2919 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2920 }
2921
2922 if (!freerdp_settings_set_bool(settings, FreeRDP_SmartSizing, TRUE))
2923 return COMMAND_LINE_ERROR;
2924
2925 if (arg->Value)
2926 {
2927 unsigned long w = 0;
2928 unsigned long h = 0;
2929
2930 if (!parseSizeValue(arg->Value, &w, &h) || (w > UINT16_MAX) || (h > UINT16_MAX))
2931 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2932
2933 if (!freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingWidth, (UINT32)w))
2934 return COMMAND_LINE_ERROR;
2935 if (!freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingHeight, (UINT32)h))
2936 return COMMAND_LINE_ERROR;
2937 }
2938 return 0;
2939}
2940
2941static int parse_bpp_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2942{
2943 WINPR_ASSERT(settings);
2944 WINPR_ASSERT(arg);
2945
2946 LONGLONG val = 0;
2947
2948 if (!value_to_int(arg->Value, &val, 0, UINT32_MAX))
2949 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2950
2951 switch (val)
2952 {
2953 case 32:
2954 case 24:
2955 case 16:
2956 case 15:
2957 case 8:
2958 if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, (UINT32)val))
2959 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2960 break;
2961
2962 default:
2963 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2964 }
2965 return 0;
2966}
2967
2968static int parse_kbd_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
2969{
2970 WINPR_ASSERT(settings);
2971 WINPR_ASSERT(arg);
2972
2973 int rc = CHANNEL_RC_OK;
2974 size_t count = 0;
2975 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
2976 if (!ptr || (count == 0))
2977 rc = COMMAND_LINE_ERROR;
2978 else
2979 {
2980 for (size_t x = 0; x < count; x++)
2981 {
2982 const char* val = ptr[x];
2983
2984 if (option_starts_with("remap:", val))
2985 {
2986 /* Append this new occurrence to the already existing list */
2987 char* now = _strdup(&val[6]);
2988 const char* old =
2989 freerdp_settings_get_string(settings, FreeRDP_KeyboardRemappingList);
2990
2991 /* Basic sanity test. Entries must be like <key>=<value>, e.g. 1=2 */
2992 if (!check_kbd_remap_valid(now))
2993 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
2994 else if (old)
2995 {
2996 const size_t olen = strlen(old);
2997 const size_t alen = strlen(now);
2998 const size_t tlen = olen + alen + 2;
2999 char* tmp = calloc(tlen, sizeof(char));
3000 if (!tmp)
3001 rc = COMMAND_LINE_ERROR_MEMORY;
3002 else
3003 (void)_snprintf(tmp, tlen, "%s,%s", old, now);
3004 free(now);
3005 now = tmp;
3006 }
3007
3008 if (rc == 0)
3009 {
3010 if (!freerdp_settings_set_string(settings, FreeRDP_KeyboardRemappingList, now))
3011 rc = COMMAND_LINE_ERROR;
3012 }
3013 free(now);
3014 }
3015 else if (option_starts_with("layout:", val))
3016 {
3017 rc = parse_kbd_layout(settings, &val[7]);
3018 }
3019 else if (option_starts_with("lang:", val))
3020 {
3021 LONGLONG ival = 0;
3022 const BOOL isInt = value_to_int(&val[5], &ival, 1, UINT32_MAX);
3023 if (!isInt)
3024 ival = freerdp_get_locale_id_from_string(&val[5]);
3025
3026 if (ival <= 0)
3027 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3028 else if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardCodePage,
3029 (UINT32)ival))
3030 rc = COMMAND_LINE_ERROR;
3031 }
3032 else if (option_starts_with("type:", val))
3033 {
3034 LONGLONG ival = 0;
3035 const BOOL isInt = value_to_int(&val[5], &ival, 1, UINT32_MAX);
3036 if (!isInt)
3037 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3038 else if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardType, (UINT32)ival))
3039 rc = COMMAND_LINE_ERROR;
3040 }
3041 else if (option_starts_with("subtype:", val))
3042 {
3043 LONGLONG ival = 0;
3044 const BOOL isInt = value_to_int(&val[8], &ival, 1, UINT32_MAX);
3045 if (!isInt)
3046 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3047 else if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardSubType,
3048 (UINT32)ival))
3049 rc = COMMAND_LINE_ERROR;
3050 }
3051 else if (option_starts_with("fn-key:", val))
3052 {
3053 LONGLONG ival = 0;
3054 const BOOL isInt = value_to_int(&val[7], &ival, 1, UINT32_MAX);
3055 if (!isInt)
3056 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3057 else if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardFunctionKey,
3058 (UINT32)ival))
3059 rc = COMMAND_LINE_ERROR;
3060 }
3061 else if (option_starts_with("unicode", val))
3062 {
3063 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
3064 if (bval == PARSE_FAIL)
3065 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3066 else if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput,
3067 bval != PARSE_OFF))
3068 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3069 }
3070 else if (option_starts_with("pipe:", val))
3071 {
3072 if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput, TRUE))
3073 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3074 else if (!freerdp_settings_set_string(settings, FreeRDP_KeyboardPipeName, &val[5]))
3075 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3076 }
3077#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
3078 else if (count == 1)
3079 {
3080 /* Legacy, allow /kbd:<value> for setting keyboard layout */
3081 rc = parse_kbd_layout(settings, val);
3082 }
3083#endif
3084 else
3085 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3086
3087 if (rc != 0)
3088 break;
3089 }
3090 }
3091 CommandLineParserFree(ptr);
3092 return rc;
3093}
3094
3095static int parse_proxy_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3096{
3097 WINPR_ASSERT(settings);
3098 WINPR_ASSERT(arg);
3099
3100 /* initial value */
3101 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
3102 return COMMAND_LINE_ERROR_MEMORY;
3103
3104 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
3105 {
3106 const char* cur = arg->Value;
3107
3108 if (!cur)
3109 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3110 /* value is [scheme://][user:password@]hostname:port */
3111 if (!proxy_parse_uri(settings, cur))
3112 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3113 }
3114 else
3115 {
3116 WLog_ERR(TAG, "Option http-proxy needs argument.");
3117 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3118 }
3119 return 0;
3120}
3121
3122static int parse_dump_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3123{
3124 WINPR_ASSERT(settings);
3125 WINPR_ASSERT(arg);
3126
3127 BOOL failed = FALSE;
3128 size_t count = 0;
3129 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3130 if (!ptr)
3131 failed = TRUE;
3132 else
3133 {
3134 BOOL modernsyntax = FALSE;
3135 BOOL oldsyntax = FALSE;
3136 for (size_t x = 0; (x < count) && !failed; x++)
3137 {
3138 const char* carg = ptr[x];
3139 if (option_starts_with("file:", carg))
3140 {
3141 const char* val = &carg[5];
3142 if (oldsyntax)
3143 failed = TRUE;
3144 else if (!freerdp_settings_set_string(settings, FreeRDP_TransportDumpFile, val))
3145 failed = TRUE;
3146 modernsyntax = TRUE;
3147 }
3148 else if (option_equals("replay", carg))
3149 {
3150 if (!freerdp_settings_set_bool(settings, FreeRDP_TransportDump, FALSE))
3151 failed = TRUE;
3152 else if (!freerdp_settings_set_bool(settings, FreeRDP_TransportDumpReplay, TRUE))
3153 failed = TRUE;
3154 }
3155 else if (option_equals("record", carg))
3156 {
3157 if (!freerdp_settings_set_bool(settings, FreeRDP_TransportDump, TRUE))
3158 failed = TRUE;
3159 else if (!freerdp_settings_set_bool(settings, FreeRDP_TransportDumpReplay, FALSE))
3160 failed = TRUE;
3161 }
3162 else if (option_equals("nodelay", carg))
3163 {
3164 if (oldsyntax)
3165 failed = TRUE;
3166 else if (!freerdp_settings_set_bool(settings, FreeRDP_TransportDumpReplayNodelay,
3167 TRUE))
3168 failed = TRUE;
3169 modernsyntax = TRUE;
3170 }
3171 else
3172 {
3173 /* compat:
3174 * support syntax record,<filename> and replay,<filename>
3175 */
3176 if (modernsyntax)
3177 failed = TRUE;
3178 else if (!freerdp_settings_set_string(settings, FreeRDP_TransportDumpFile, carg))
3179 failed = TRUE;
3180 oldsyntax = TRUE;
3181 }
3182 }
3183
3184 if (oldsyntax && (count != 2))
3185 failed = TRUE;
3186 }
3187 CommandLineParserFree(ptr);
3188 if (failed)
3189 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3190 return 0;
3191}
3192
3193static int parse_clipboard_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3194{
3195 WINPR_ASSERT(settings);
3196 WINPR_ASSERT(arg);
3197
3198 if (arg->Value == BoolValueTrue || arg->Value == BoolValueFalse)
3199 {
3200 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard,
3201 (arg->Value == BoolValueTrue)))
3202 return COMMAND_LINE_ERROR;
3203 }
3204 else
3205 {
3206 int rc = 0;
3207 size_t count = 0;
3208 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3209 for (size_t x = 0; (x < count) && (rc == 0); x++)
3210 {
3211 const char* usesel = "use-selection:";
3212
3213 const char* cur = ptr[x];
3214 if (option_starts_with(usesel, cur))
3215 {
3216 const char* val = &cur[strlen(usesel)];
3217 if (!freerdp_settings_set_string(settings, FreeRDP_ClipboardUseSelection, val))
3218 rc = COMMAND_LINE_ERROR_MEMORY;
3219 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, TRUE))
3220 return COMMAND_LINE_ERROR;
3221 }
3222 else if (option_starts_with("direction-to", cur))
3223 {
3224 const UINT32 mask =
3225 freerdp_settings_get_uint32(settings, FreeRDP_ClipboardFeatureMask) &
3226 (uint32_t)~(CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_REMOTE_TO_LOCAL);
3227 const PARSE_CLIP_DIR_RESULT bval = parse_clip_direciton_to_option(cur);
3228 UINT32 bflags = 0;
3229 switch (bval)
3230 {
3231 case CLIP_DIR_PARSE_ALL:
3232 bflags |= CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_REMOTE_TO_LOCAL;
3233 break;
3234 case CLIP_DIR_PARSE_LOCAL:
3235 bflags |= CLIPRDR_FLAG_REMOTE_TO_LOCAL;
3236 break;
3237 case CLIP_DIR_PARSE_REMOTE:
3238 bflags |= CLIPRDR_FLAG_LOCAL_TO_REMOTE;
3239 break;
3240 case CLIP_DIR_PARSE_OFF:
3241 break;
3242 case CLIP_DIR_PARSE_FAIL:
3243 default:
3244 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3245 break;
3246 }
3247
3248 if (!freerdp_settings_set_uint32(settings, FreeRDP_ClipboardFeatureMask,
3249 mask | bflags))
3250 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3251 }
3252 else if (option_starts_with("files-to", cur))
3253 {
3254 const UINT32 mask =
3255 freerdp_settings_get_uint32(settings, FreeRDP_ClipboardFeatureMask) &
3256 (uint32_t)~(CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES |
3257 CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES);
3258 const PARSE_CLIP_DIR_RESULT bval = parse_clip_direciton_to_option(cur);
3259 UINT32 bflags = 0;
3260 switch (bval)
3261 {
3262 case CLIP_DIR_PARSE_ALL:
3263 bflags |=
3264 CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES | CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES;
3265 break;
3266 case CLIP_DIR_PARSE_LOCAL:
3267 bflags |= CLIPRDR_FLAG_REMOTE_TO_LOCAL_FILES;
3268 break;
3269 case CLIP_DIR_PARSE_REMOTE:
3270 bflags |= CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES;
3271 break;
3272 case CLIP_DIR_PARSE_OFF:
3273 break;
3274 case CLIP_DIR_PARSE_FAIL:
3275 default:
3276 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3277 break;
3278 }
3279
3280 if (!freerdp_settings_set_uint32(settings, FreeRDP_ClipboardFeatureMask,
3281 mask | bflags))
3282 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3283 }
3284 else
3285 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3286 }
3287 CommandLineParserFree(ptr);
3288
3289 if (rc)
3290 return rc;
3291 }
3292 return 0;
3293}
3294
3295static int parse_audio_mode_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3296{
3297 WINPR_ASSERT(settings);
3298 WINPR_ASSERT(arg);
3299
3300 LONGLONG val = 0;
3301 if (!arg->Value)
3302 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3303
3304 if (strcmp(arg->Value, "none") == 0)
3305 val = AUDIO_MODE_NONE;
3306 else if (strcmp(arg->Value, "redirect") == 0)
3307 val = AUDIO_MODE_REDIRECT;
3308 else if (strcmp(arg->Value, "server") == 0)
3309 val = AUDIO_MODE_PLAY_ON_SERVER;
3310 else if (!value_to_int(arg->Value, &val, 0, UINT32_MAX))
3311 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3312
3313 switch (val)
3314 {
3315 case AUDIO_MODE_REDIRECT:
3316 if (!freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, TRUE))
3317 return COMMAND_LINE_ERROR;
3318 break;
3319
3320 case AUDIO_MODE_PLAY_ON_SERVER:
3321 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteConsoleAudio, TRUE))
3322 return COMMAND_LINE_ERROR;
3323 break;
3324
3325 case AUDIO_MODE_NONE:
3326 if (!freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, FALSE))
3327 return COMMAND_LINE_ERROR;
3328 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteConsoleAudio, FALSE))
3329 return COMMAND_LINE_ERROR;
3330 break;
3331
3332 default:
3333 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3334 }
3335 return 0;
3336}
3337
3338static int parse_network_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3339{
3340 WINPR_ASSERT(settings);
3341 WINPR_ASSERT(arg);
3342
3343 UINT32 type = CONNECTION_TYPE_INVALID;
3344
3345 if (option_equals(arg->Value, "invalid"))
3346 type = CONNECTION_TYPE_INVALID;
3347 else if (option_equals(arg->Value, "modem"))
3348 type = CONNECTION_TYPE_MODEM;
3349 else if (option_equals(arg->Value, "broadband"))
3350 type = CONNECTION_TYPE_BROADBAND_HIGH;
3351 else if (option_equals(arg->Value, "broadband-low"))
3352 type = CONNECTION_TYPE_BROADBAND_LOW;
3353 else if (option_equals(arg->Value, "broadband-high"))
3354 type = CONNECTION_TYPE_BROADBAND_HIGH;
3355 else if (option_equals(arg->Value, "wan"))
3356 type = CONNECTION_TYPE_WAN;
3357 else if (option_equals(arg->Value, "lan"))
3358 type = CONNECTION_TYPE_LAN;
3359 else if ((option_equals(arg->Value, "autodetect")) || (option_equals(arg->Value, "auto")) ||
3360 (option_equals(arg->Value, "detect")))
3361 {
3362 type = CONNECTION_TYPE_AUTODETECT;
3363 }
3364 else
3365 {
3366 LONGLONG val = 0;
3367
3368 if (!value_to_int(arg->Value, &val, 0, 7))
3369 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3370
3371 type = (UINT32)val;
3372 }
3373
3374 if (!freerdp_set_connection_type(settings, type))
3375 return COMMAND_LINE_ERROR;
3376 return 0;
3377}
3378
3379static int parse_sec_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3380{
3381 WINPR_ASSERT(settings);
3382 WINPR_ASSERT(arg);
3383
3384 size_t count = 0;
3385 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3386 if (count == 0)
3387 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3388
3389 FreeRDP_Settings_Keys_Bool singleOptionWithoutOnOff = FreeRDP_BOOL_UNUSED;
3390 for (size_t x = 0; x < count; x++)
3391 {
3392 const char* cur = ptr[x];
3393 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(cur);
3394 if (bval == PARSE_FAIL)
3395 {
3396 CommandLineParserFree(ptr);
3397 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3398 }
3399
3400 const BOOL val = bval != PARSE_OFF;
3401 FreeRDP_Settings_Keys_Bool id = FreeRDP_BOOL_UNUSED;
3402 if (option_starts_with("rdp", cur)) /* Standard RDP */
3403 {
3404 id = FreeRDP_RdpSecurity;
3405 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, val))
3406 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3407 }
3408 else if (option_starts_with("tls", cur)) /* TLS */
3409 id = FreeRDP_TlsSecurity;
3410 else if (option_starts_with("nla", cur)) /* NLA */
3411 id = FreeRDP_NlaSecurity;
3412 else if (option_starts_with("ext", cur)) /* NLA Extended */
3413 id = FreeRDP_ExtSecurity;
3414 else if (option_equals("aad", cur)) /* RDSAAD */
3415 id = FreeRDP_AadSecurity;
3416 else
3417 {
3418 WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
3419 CommandLineParserFree(ptr);
3420 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3421 }
3422
3423 if ((bval == PARSE_NONE) && (count == 1))
3424 singleOptionWithoutOnOff = id;
3425 if (!freerdp_settings_set_bool(settings, id, val))
3426 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3427 }
3428
3429 if (singleOptionWithoutOnOff != FreeRDP_BOOL_UNUSED)
3430 {
3431 const FreeRDP_Settings_Keys_Bool options[] = { FreeRDP_AadSecurity,
3432 FreeRDP_UseRdpSecurityLayer,
3433 FreeRDP_RdpSecurity, FreeRDP_NlaSecurity,
3434 FreeRDP_TlsSecurity };
3435
3436 for (size_t i = 0; i < ARRAYSIZE(options); i++)
3437 {
3438 if (!freerdp_settings_set_bool(settings, options[i], FALSE))
3439 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3440 }
3441
3442 if (!freerdp_settings_set_bool(settings, singleOptionWithoutOnOff, TRUE))
3443 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3444 if (singleOptionWithoutOnOff == FreeRDP_RdpSecurity)
3445 {
3446 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
3447 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3448 }
3449 }
3450 CommandLineParserFree(ptr);
3451 return 0;
3452}
3453
3454static int parse_encryption_methods_options(rdpSettings* settings,
3455 const COMMAND_LINE_ARGUMENT_A* arg)
3456{
3457 WINPR_ASSERT(settings);
3458 WINPR_ASSERT(arg);
3459
3460 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
3461 {
3462 size_t count = 0;
3463 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3464
3465 UINT32 EncryptionMethods = 0;
3466 for (UINT32 i = 0; i < count; i++)
3467 {
3468 if (option_equals(ptr[i], "40"))
3469 EncryptionMethods |= ENCRYPTION_METHOD_40BIT;
3470 else if (option_equals(ptr[i], "56"))
3471 EncryptionMethods |= ENCRYPTION_METHOD_56BIT;
3472 else if (option_equals(ptr[i], "128"))
3473 EncryptionMethods |= ENCRYPTION_METHOD_128BIT;
3474 else if (option_equals(ptr[i], "FIPS"))
3475 EncryptionMethods |= ENCRYPTION_METHOD_FIPS;
3476 else
3477 WLog_ERR(TAG, "unknown encryption method '%s'", ptr[i]);
3478 }
3479
3480 if (!freerdp_settings_set_uint32(settings, FreeRDP_EncryptionMethods, EncryptionMethods))
3481 return COMMAND_LINE_ERROR;
3482 CommandLineParserFree(ptr);
3483 }
3484 return 0;
3485}
3486
3487static int parse_cert_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3488{
3489 WINPR_ASSERT(settings);
3490 WINPR_ASSERT(arg);
3491
3492 int rc = 0;
3493 size_t count = 0;
3494 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3495 for (size_t x = 0; (x < count) && (rc == 0); x++)
3496 {
3497 const char deny[] = "deny";
3498 const char ignore[] = "ignore";
3499 const char tofu[] = "tofu";
3500 const char name[] = "name:";
3501 const char fingerprints[] = "fingerprint:";
3502
3503 const char* cur = ptr[x];
3504 if (option_equals(deny, cur))
3505 {
3506 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoDenyCertificate, TRUE))
3507 return COMMAND_LINE_ERROR;
3508 }
3509 else if (option_equals(ignore, cur))
3510 {
3511 if (!freerdp_settings_set_bool(settings, FreeRDP_IgnoreCertificate, TRUE))
3512 return COMMAND_LINE_ERROR;
3513 }
3514 else if (option_equals(tofu, cur))
3515 {
3516 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoAcceptCertificate, TRUE))
3517 return COMMAND_LINE_ERROR;
3518 }
3519 else if (option_starts_with(name, cur))
3520 {
3521 const char* val = &cur[strnlen(name, sizeof(name))];
3522 if (!freerdp_settings_set_string(settings, FreeRDP_CertificateName, val))
3523 rc = COMMAND_LINE_ERROR_MEMORY;
3524 }
3525 else if (option_starts_with(fingerprints, cur))
3526 {
3527 const char* val = &cur[strnlen(fingerprints, sizeof(fingerprints))];
3528 if (!freerdp_settings_append_string(settings, FreeRDP_CertificateAcceptedFingerprints,
3529 ",", val))
3530 rc = COMMAND_LINE_ERROR_MEMORY;
3531 }
3532 else
3533 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3534 }
3535 CommandLineParserFree(ptr);
3536
3537 return rc;
3538}
3539
3540static int parse_mouse_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3541{
3542 WINPR_ASSERT(settings);
3543 WINPR_ASSERT(arg);
3544
3545 size_t count = 0;
3546 char** ptr = CommandLineParseCommaSeparatedValuesEx("mouse", arg->Value, &count);
3547 int rc = 0;
3548 if (ptr)
3549 {
3550 for (size_t x = 1; x < count; x++)
3551 {
3552 const char* cur = ptr[x];
3553
3554 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(cur);
3555 if (bval == PARSE_FAIL)
3556 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3557 else
3558 {
3559 const BOOL val = bval != PARSE_OFF;
3560
3561 if (option_starts_with("relative", cur))
3562 {
3563 if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, val))
3564 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3565 }
3566 else if (option_starts_with("grab", cur))
3567 {
3568 if (!freerdp_settings_set_bool(settings, FreeRDP_GrabMouse, val))
3569 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3570 }
3571 }
3572
3573 if (rc != 0)
3574 break;
3575 }
3576 }
3577 CommandLineParserFree(ptr);
3578
3579 return rc;
3580}
3581
3582static int parse_floatbar_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3583{
3584 WINPR_ASSERT(settings);
3585 WINPR_ASSERT(arg);
3586
3587 /* Defaults are enabled, visible, sticky, fullscreen */
3588 UINT32 Floatbar = 0x0017;
3589
3590 if (arg->Value)
3591 {
3592 char* start = arg->Value;
3593
3594 do
3595 {
3596 char* cur = start;
3597 start = strchr(start, ',');
3598
3599 if (start)
3600 {
3601 *start = '\0';
3602 start = start + 1;
3603 }
3604
3605 /* sticky:[on|off] */
3606 if (option_starts_with("sticky:", cur))
3607 {
3608 Floatbar &= ~0x02u;
3609
3610 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(cur);
3611 switch (bval)
3612 {
3613 case PARSE_ON:
3614 case PARSE_NONE:
3615 Floatbar |= 0x02u;
3616 break;
3617 case PARSE_OFF:
3618 Floatbar &= ~0x02u;
3619 break;
3620 case PARSE_FAIL:
3621 default:
3622 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3623 }
3624 }
3625 /* default:[visible|hidden] */
3626 else if (option_starts_with("default:", cur))
3627 {
3628 const char* val = cur + 8;
3629 Floatbar &= ~0x04u;
3630
3631 if (option_equals("visible", val))
3632 Floatbar |= 0x04u;
3633 else if (option_equals("hidden", val))
3634 Floatbar &= ~0x04u;
3635 else
3636 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3637 }
3638 /* show:[always|fullscreen|window] */
3639 else if (option_starts_with("show:", cur))
3640 {
3641 const char* val = cur + 5;
3642 Floatbar &= ~0x30u;
3643
3644 if (option_equals("always", val))
3645 Floatbar |= 0x30u;
3646 else if (option_equals("fullscreen", val))
3647 Floatbar |= 0x10u;
3648 else if (option_equals("window", val))
3649 Floatbar |= 0x20u;
3650 else
3651 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3652 }
3653 else
3654 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3655 } while (start);
3656 }
3657 if (!freerdp_settings_set_uint32(settings, FreeRDP_Floatbar, Floatbar))
3658 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3659 return 0;
3660}
3661
3662static int parse_reconnect_cookie_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3663{
3664 WINPR_ASSERT(settings);
3665 WINPR_ASSERT(arg);
3666
3667 BYTE* base64 = nullptr;
3668 size_t length = 0;
3669 if (!arg->Value)
3670 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3671
3672 crypto_base64_decode((const char*)(arg->Value), strlen(arg->Value), &base64, &length);
3673
3674 if ((base64 != nullptr) && (length == sizeof(ARC_SC_PRIVATE_PACKET)))
3675 {
3676 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_ServerAutoReconnectCookie, base64,
3677 1))
3678 return COMMAND_LINE_ERROR;
3679 }
3680 else
3681 {
3682 WLog_ERR(TAG, "reconnect-cookie: invalid base64 '%s'", arg->Value);
3683 }
3684
3685 free(base64);
3686 return 0;
3687}
3688
3689static BOOL set_monitor_override(rdpSettings* settings, uint64_t flag)
3690{
3691 const FreeRDP_Settings_Keys_UInt64 key = FreeRDP_MonitorOverrideFlags;
3692 uint64_t mask = freerdp_settings_get_uint64(settings, key);
3693 mask |= flag;
3694 return freerdp_settings_set_uint64(settings, key, mask);
3695}
3696
3697static int parse_scale_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3698{
3699 WINPR_ASSERT(settings);
3700 WINPR_ASSERT(arg);
3701
3702 LONGLONG val = 0;
3703
3704 if (!value_to_int(arg->Value, &val, 100, 180))
3705 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3706
3707 switch (val)
3708 {
3709 case 100:
3710 case 140:
3711 case 180:
3712 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopScaleFactor, (UINT32)val))
3713 return COMMAND_LINE_ERROR;
3714 if (!freerdp_settings_set_uint32(settings, FreeRDP_DeviceScaleFactor, (UINT32)val))
3715 return COMMAND_LINE_ERROR;
3716 if (!set_monitor_override(settings, FREERDP_MONITOR_OVERRIDE_DESKTOP_SCALE |
3717 FREERDP_MONITOR_OVERRIDE_DEVICE_SCALE))
3718 return fail_at(arg, COMMAND_LINE_ERROR);
3719 break;
3720
3721 default:
3722 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3723 }
3724 return 0;
3725}
3726
3727static int parse_scale_device_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3728{
3729 WINPR_ASSERT(settings);
3730 WINPR_ASSERT(arg);
3731
3732 LONGLONG val = 0;
3733
3734 if (!value_to_int(arg->Value, &val, 100, 180))
3735 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3736
3737 switch (val)
3738 {
3739 case 100:
3740 case 140:
3741 case 180:
3742 if (!freerdp_settings_set_uint32(settings, FreeRDP_DeviceScaleFactor, (UINT32)val))
3743 return COMMAND_LINE_ERROR;
3744 if (!set_monitor_override(settings, FREERDP_MONITOR_OVERRIDE_DEVICE_SCALE))
3745 return fail_at(arg, COMMAND_LINE_ERROR);
3746 break;
3747
3748 default:
3749 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3750 }
3751 return 0;
3752}
3753
3754static int parse_smartcard_logon_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3755{
3756 WINPR_ASSERT(settings);
3757 WINPR_ASSERT(arg);
3758
3759 size_t count = 0;
3760
3761 if (!freerdp_settings_set_bool(settings, FreeRDP_SmartcardLogon, TRUE))
3762 return COMMAND_LINE_ERROR;
3763
3764 char** ptr = CommandLineParseCommaSeparatedValuesEx("smartcard-logon", arg->Value, &count);
3765 if (ptr)
3766 {
3767 const CmdLineSubOptions opts[] = {
3768 { "cert:", FreeRDP_SmartcardCertificate, CMDLINE_SUBOPTION_FILE,
3769 setSmartcardEmulation },
3770 { "key:", FreeRDP_SmartcardPrivateKey, CMDLINE_SUBOPTION_FILE, setSmartcardEmulation },
3771 { "pin:", FreeRDP_Password, CMDLINE_SUBOPTION_STRING, nullptr },
3772 { "csp:", FreeRDP_CspName, CMDLINE_SUBOPTION_STRING, nullptr },
3773 { "reader:", FreeRDP_ReaderName, CMDLINE_SUBOPTION_STRING, nullptr },
3774 { "card:", FreeRDP_CardName, CMDLINE_SUBOPTION_STRING, nullptr },
3775 { "container:", FreeRDP_ContainerName, CMDLINE_SUBOPTION_STRING, nullptr }
3776 };
3777
3778 for (size_t x = 1; x < count; x++)
3779 {
3780 const char* cur = ptr[x];
3781 if (!parseSubOptions(settings, opts, ARRAYSIZE(opts), cur))
3782 {
3783 CommandLineParserFree(ptr);
3784 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3785 }
3786 }
3787 }
3788 CommandLineParserFree(ptr);
3789 return 0;
3790}
3791
3792static int parse_tune_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3793{
3794 WINPR_ASSERT(settings);
3795 WINPR_ASSERT(arg);
3796
3797 size_t count = 0;
3798 char** ptr = CommandLineParseCommaSeparatedValuesEx("tune", arg->Value, &count);
3799 if (!ptr)
3800 return COMMAND_LINE_ERROR;
3801 for (size_t x = 1; x < count; x++)
3802 {
3803 const char* cur = ptr[x];
3804 char* sep = strchr(cur, ':');
3805 if (!sep)
3806 {
3807 CommandLineParserFree(ptr);
3808 return COMMAND_LINE_ERROR;
3809 }
3810 *sep++ = '\0';
3811 if (!freerdp_settings_set_value_for_name(settings, cur, sep))
3812 {
3813 CommandLineParserFree(ptr);
3814 return COMMAND_LINE_ERROR;
3815 }
3816 }
3817
3818 CommandLineParserFree(ptr);
3819 return 0;
3820}
3821
3822static int parse_app_option_program(rdpSettings* settings, const char* cmd)
3823{
3824 const FreeRDP_Settings_Keys_Bool ids[] = { FreeRDP_RemoteApplicationMode,
3825 FreeRDP_RemoteAppLanguageBarSupported,
3826 FreeRDP_Workarea, FreeRDP_DisableWallpaper,
3827 FreeRDP_DisableFullWindowDrag };
3828
3829 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationProgram, cmd))
3830 return COMMAND_LINE_ERROR_MEMORY;
3831
3832 for (size_t y = 0; y < ARRAYSIZE(ids); y++)
3833 {
3834 if (!freerdp_settings_set_bool(settings, ids[y], TRUE))
3835 return COMMAND_LINE_ERROR;
3836 }
3837 return CHANNEL_RC_OK;
3838}
3839
3840static int parse_aad_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3841{
3842 WINPR_ASSERT(settings);
3843 WINPR_ASSERT(arg);
3844
3845 int rc = CHANNEL_RC_OK;
3846 size_t count = 0;
3847 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3848 if (!ptr || (count == 0))
3849 rc = COMMAND_LINE_ERROR;
3850 else
3851 {
3852 struct app_map
3853 {
3854 const char* name;
3855 SSIZE_T id;
3856 int (*fkt)(rdpSettings* settings, const char* value);
3857 };
3858 const struct app_map amap[] = {
3859 { "tenantid:", FreeRDP_GatewayAvdAadtenantid, nullptr },
3860 { "ad:", FreeRDP_GatewayAzureActiveDirectory, nullptr },
3861 { "avd-access:", FreeRDP_GatewayAvdAccessAadFormat, nullptr },
3862 { "avd-token:", FreeRDP_GatewayAvdAccessTokenFormat, nullptr },
3863 { "avd-scope:", FreeRDP_GatewayAvdScope, nullptr }
3864
3865 };
3866 for (size_t x = 0; x < count; x++)
3867 {
3868 BOOL handled = FALSE;
3869 const char* val = ptr[x];
3870
3871 if (option_starts_with("use-tenantid", val))
3872 {
3873 PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
3874 if (bval == PARSE_FAIL)
3875 {
3876 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3877 break;
3878 }
3879 else
3880 {
3881 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayAvdUseTenantid,
3882 bval != PARSE_OFF))
3883 {
3884 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3885 break;
3886 }
3887 }
3888 continue;
3889 }
3890 for (size_t y = 0; y < ARRAYSIZE(amap); y++)
3891 {
3892 const struct app_map* cur = &amap[y];
3893 if (option_starts_with(cur->name, val))
3894 {
3895 const char* xval = &val[strlen(cur->name)];
3896 if (cur->fkt)
3897 rc = cur->fkt(settings, xval);
3898 else
3899 {
3900 const char* name = freerdp_settings_get_name_for_key(cur->id);
3901 if (!freerdp_settings_set_value_for_name(settings, name, xval))
3902 rc = COMMAND_LINE_ERROR_MEMORY;
3903 }
3904
3905 handled = TRUE;
3906 break;
3907 }
3908 }
3909
3910 if (!handled)
3911 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3912
3913 if (rc != 0)
3914 break;
3915 }
3916 }
3917
3918 CommandLineParserFree(ptr);
3919 return rc;
3920}
3921
3922static int parse_app_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3923{
3924 WINPR_ASSERT(settings);
3925 WINPR_ASSERT(arg);
3926
3927 int rc = CHANNEL_RC_OK;
3928 size_t count = 0;
3929 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
3930 if (!ptr || (count == 0))
3931 rc = COMMAND_LINE_ERROR;
3932 else
3933 {
3934 struct app_map
3935 {
3936 const char* name;
3937 SSIZE_T id;
3938 int (*fkt)(rdpSettings* settings, const char* value);
3939 };
3940 const struct app_map amap[] = {
3941 { "program:", FreeRDP_RemoteApplicationProgram, parse_app_option_program },
3942 { "workdir:", FreeRDP_RemoteApplicationWorkingDir, nullptr },
3943 { "name:", FreeRDP_RemoteApplicationName, nullptr },
3944 { "icon:", FreeRDP_RemoteApplicationIcon, nullptr },
3945 { "cmd:", FreeRDP_RemoteApplicationCmdLine, nullptr },
3946 { "file:", FreeRDP_RemoteApplicationFile, nullptr },
3947 { "guid:", FreeRDP_RemoteApplicationGuid, nullptr },
3948 { "hidef:", FreeRDP_HiDefRemoteApp, nullptr }
3949 };
3950 for (size_t x = 0; x < count; x++)
3951 {
3952 BOOL handled = FALSE;
3953 const char* val = ptr[x];
3954
3955 for (size_t y = 0; y < ARRAYSIZE(amap); y++)
3956 {
3957 const struct app_map* cur = &amap[y];
3958 if (option_starts_with(cur->name, val))
3959 {
3960 const char* xval = &val[strlen(cur->name)];
3961 if (cur->fkt)
3962 rc = cur->fkt(settings, xval);
3963 else
3964 {
3965 const char* name = freerdp_settings_get_name_for_key(cur->id);
3966 if (!freerdp_settings_set_value_for_name(settings, name, xval))
3967 rc = COMMAND_LINE_ERROR_MEMORY;
3968 }
3969
3970 handled = TRUE;
3971 break;
3972 }
3973 }
3974
3975#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
3976 if (!handled && (count == 1))
3977 {
3978 /* Legacy path, allow /app:command and /app:||command syntax */
3979 rc = parse_app_option_program(settings, val);
3980 }
3981 else
3982#endif
3983 if (!handled)
3984 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
3985
3986 if (rc != 0)
3987 break;
3988 }
3989 }
3990
3991 CommandLineParserFree(ptr);
3992 return rc;
3993}
3994
3995static int parse_cache_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
3996{
3997 WINPR_ASSERT(settings);
3998 WINPR_ASSERT(arg);
3999
4000 int rc = CHANNEL_RC_OK;
4001 size_t count = 0;
4002 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
4003 if (!ptr || (count == 0))
4004 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
4005
4006 for (size_t x = 0; x < count; x++)
4007 {
4008 const char* val = ptr[x];
4009
4010 if (option_starts_with("codec:", val))
4011 {
4012 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheV3Enabled, TRUE))
4013 rc = COMMAND_LINE_ERROR;
4014 else if (option_equals(arg->Value, "rfx"))
4015 {
4016 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
4017 rc = COMMAND_LINE_ERROR;
4018 }
4019 else if (option_equals(arg->Value, "nsc"))
4020 {
4021 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, TRUE))
4022 rc = COMMAND_LINE_ERROR;
4023 }
4024
4025#if defined(WITH_JPEG)
4026 else if (option_equals(arg->Value, "jpeg"))
4027 {
4028 if (!freerdp_settings_set_bool(settings, FreeRDP_JpegCodec, TRUE))
4029 rc = COMMAND_LINE_ERROR;
4030
4031 if (freerdp_settings_get_uint32(settings, FreeRDP_JpegQuality) == 0)
4032 {
4033 if (!freerdp_settings_set_uint32(settings, FreeRDP_JpegQuality, 75))
4034 return COMMAND_LINE_ERROR;
4035 }
4036 }
4037
4038#endif
4039 }
4040 else if (option_starts_with("persist-file:", val))
4041 {
4042
4043 if (!freerdp_settings_set_string(settings, FreeRDP_BitmapCachePersistFile, &val[13]))
4044 rc = COMMAND_LINE_ERROR_MEMORY;
4045 else if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled, TRUE))
4046 rc = COMMAND_LINE_ERROR;
4047 }
4048 else
4049 {
4050 const PARSE_ON_OFF_RESULT bval = parse_on_off_option(val);
4051 if (bval == PARSE_FAIL)
4052 rc = COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
4053 else
4054 {
4055 if (option_starts_with("bitmap", val))
4056 {
4057 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled,
4058 bval != PARSE_OFF))
4059 rc = COMMAND_LINE_ERROR;
4060 }
4061 else if (option_starts_with("glyph", val))
4062 {
4063 if (!freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel,
4064 bval != PARSE_OFF ? GLYPH_SUPPORT_FULL
4065 : GLYPH_SUPPORT_NONE))
4066 rc = COMMAND_LINE_ERROR;
4067 }
4068 else if (option_starts_with("persist", val))
4069 {
4070 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled,
4071 bval != PARSE_OFF))
4072 rc = COMMAND_LINE_ERROR;
4073 }
4074 else if (option_starts_with("offscreen", val))
4075 {
4076 if (!freerdp_settings_set_uint32(settings, FreeRDP_OffscreenSupportLevel,
4077 bval != PARSE_OFF))
4078 rc = COMMAND_LINE_ERROR;
4079 }
4080 }
4081 }
4082 }
4083
4084 CommandLineParserFree(ptr);
4085 return rc;
4086}
4087
4088static BOOL parse_gateway_host_option(rdpSettings* settings, const char* host)
4089{
4090 WINPR_ASSERT(settings);
4091 WINPR_ASSERT(host);
4092
4093 char* name = nullptr;
4094 int port = -1;
4095 if (!freerdp_parse_hostname(host, &name, &port))
4096 return FALSE;
4097 const BOOL rc = freerdp_settings_set_string(settings, FreeRDP_GatewayHostname, name);
4098 free(name);
4099 if (!rc)
4100 return FALSE;
4101 if (port != -1)
4102 {
4103 if (!freerdp_settings_set_uint32(settings, FreeRDP_GatewayPort, (UINT32)port))
4104 return FALSE;
4105 }
4106 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayUseSameCredentials, TRUE))
4107 return FALSE;
4108 if (!freerdp_set_gateway_usage_method(settings, TSC_PROXY_MODE_DIRECT))
4109 return FALSE;
4110
4111 return TRUE;
4112}
4113
4114static BOOL parse_gateway_cred_option(rdpSettings* settings, const char* value,
4115 FreeRDP_Settings_Keys_String what)
4116{
4117 WINPR_ASSERT(settings);
4118 WINPR_ASSERT(value);
4119
4120 switch (what)
4121 {
4122 case FreeRDP_GatewayUsername:
4123 if (!freerdp_parse_username_settings(value, settings, FreeRDP_GatewayUsername,
4124 FreeRDP_GatewayDomain))
4125 return FALSE;
4126 break;
4127 default:
4128 if (!freerdp_settings_set_string(settings, what, value))
4129 return FALSE;
4130 break;
4131 }
4132
4133 return freerdp_settings_set_bool(settings, FreeRDP_GatewayUseSameCredentials, FALSE);
4134}
4135
4136static BOOL parse_gateway_type_option(rdpSettings* settings, const char* value)
4137{
4138 BOOL rc = FALSE;
4139
4140 WINPR_ASSERT(settings);
4141 WINPR_ASSERT(value);
4142
4143 if (option_equals(value, "rpc"))
4144 {
4145 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, TRUE) ||
4146 !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, FALSE) ||
4147 !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets, FALSE) ||
4148 !freerdp_settings_set_bool(settings, FreeRDP_GatewayArmTransport, FALSE))
4149 return FALSE;
4150 rc = TRUE;
4151 }
4152 else
4153 {
4154 if (option_equals(value, "http"))
4155 {
4156 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, FALSE) ||
4157 !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, TRUE) ||
4158 !freerdp_settings_set_bool(settings, FreeRDP_GatewayArmTransport, FALSE))
4159 return FALSE;
4160 rc = TRUE;
4161 }
4162 else if (option_equals(value, "auto"))
4163 {
4164 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, TRUE) ||
4165 !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, TRUE) ||
4166 !freerdp_settings_set_bool(settings, FreeRDP_GatewayArmTransport, FALSE))
4167 return FALSE;
4168 rc = TRUE;
4169 }
4170 else if (option_equals(value, "arm"))
4171 {
4172 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, FALSE) ||
4173 !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, FALSE) ||
4174 !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets, FALSE) ||
4175 !freerdp_settings_set_bool(settings, FreeRDP_GatewayArmTransport, TRUE))
4176 return FALSE;
4177 rc = TRUE;
4178 }
4179 }
4180 return rc;
4181}
4182
4183static BOOL parse_gateway_usage_option(rdpSettings* settings, const char* value)
4184{
4185 UINT32 type = 0;
4186
4187 WINPR_ASSERT(settings);
4188 WINPR_ASSERT(value);
4189
4190 if (option_equals(value, "none"))
4191 type = TSC_PROXY_MODE_NONE_DIRECT;
4192 else if (option_equals(value, "direct"))
4193 type = TSC_PROXY_MODE_DIRECT;
4194 else if (option_equals(value, "detect"))
4195 type = TSC_PROXY_MODE_DETECT;
4196 else if (option_equals(value, "default"))
4197 type = TSC_PROXY_MODE_DEFAULT;
4198 else
4199 {
4200 LONGLONG val = 0;
4201
4202 if (!value_to_int(value, &val, TSC_PROXY_MODE_NONE_DIRECT, TSC_PROXY_MODE_NONE_DETECT))
4203 return FALSE;
4204 }
4205
4206 return freerdp_set_gateway_usage_method(settings, type);
4207}
4208
4209static char* unescape(const char* str)
4210{
4211 char* copy = _strdup(str);
4212 if (!copy)
4213 return nullptr;
4214
4215 bool escaped = false;
4216 char* dst = copy;
4217 while (*str != '\0')
4218 {
4219 char cur = *str++;
4220
4221 switch (cur)
4222 {
4223 case '\\':
4224 if (!escaped)
4225 {
4226 escaped = true;
4227 continue;
4228 }
4229 // fallthrough
4230 WINPR_FALLTHROUGH
4231 default:
4232 *dst++ = cur;
4233 escaped = false;
4234 break;
4235 }
4236 }
4237
4238 *dst = '\0';
4239
4240 return copy;
4241}
4242
4243static BOOL parse_gateway_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
4244{
4245 char* argval = nullptr;
4246 BOOL rc = FALSE;
4247
4248 WINPR_ASSERT(settings);
4249 WINPR_ASSERT(arg);
4250
4251 size_t count = 0;
4252 char** ptr = CommandLineParseCommaSeparatedValues(arg->Value, &count);
4253 if (count == 0)
4254 return TRUE;
4255 WINPR_ASSERT(ptr);
4256
4257 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayEnabled, TRUE))
4258 goto fail;
4259
4260 {
4261 BOOL allowHttpOpts = FALSE;
4262 for (size_t x = 0; x < count; x++)
4263 {
4264 BOOL validOption = FALSE;
4265 free(argval);
4266 argval = unescape(ptr[x]);
4267 if (!argval)
4268 goto fail;
4269
4270 const char* gw = option_starts_with("g:", argval);
4271 if (gw)
4272 {
4273 if (!parse_gateway_host_option(settings, gw))
4274 goto fail;
4275 validOption = TRUE;
4276 allowHttpOpts = FALSE;
4277 }
4278
4279 const char* gu = option_starts_with("u:", argval);
4280 if (gu)
4281 {
4282 if (!parse_gateway_cred_option(settings, gu, FreeRDP_GatewayUsername))
4283 goto fail;
4284 validOption = TRUE;
4285 allowHttpOpts = FALSE;
4286 }
4287
4288 const char* gd = option_starts_with("d:", argval);
4289 if (gd)
4290 {
4291 if (!parse_gateway_cred_option(settings, gd, FreeRDP_GatewayDomain))
4292 goto fail;
4293 validOption = TRUE;
4294 allowHttpOpts = FALSE;
4295 }
4296
4297 const char* gp = option_starts_with("p:", argval);
4298 if (gp)
4299 {
4300 if (!parse_gateway_cred_option(settings, gp, FreeRDP_GatewayPassword))
4301 goto fail;
4302 validOption = TRUE;
4303 allowHttpOpts = FALSE;
4304 }
4305
4306 const char* gt = option_starts_with("type:", argval);
4307 if (gt)
4308 {
4309 if (!parse_gateway_type_option(settings, gt))
4310 goto fail;
4311 validOption = TRUE;
4312 allowHttpOpts = freerdp_settings_get_bool(settings, FreeRDP_GatewayHttpTransport);
4313 }
4314
4315 const char* gat = option_starts_with("access-token:", argval);
4316 if (gat)
4317 {
4318 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAccessToken, gat))
4319 goto fail;
4320 validOption = TRUE;
4321 allowHttpOpts = FALSE;
4322 }
4323
4324 const char* bearer = option_starts_with("bearer:", argval);
4325 if (bearer)
4326 {
4327 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayHttpExtAuthBearer,
4328 bearer))
4329 goto fail;
4330 validOption = TRUE;
4331 allowHttpOpts = FALSE;
4332 }
4333
4334 const char* gwurl = option_starts_with("url:", argval);
4335 if (gwurl)
4336 {
4337 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayUrl, gwurl))
4338 goto fail;
4339 if (!freerdp_set_gateway_usage_method(settings, TSC_PROXY_MODE_DIRECT))
4340 goto fail;
4341 validOption = TRUE;
4342 allowHttpOpts = FALSE;
4343 }
4344
4345 const char* um = option_starts_with("usage-method:", argval);
4346 if (um)
4347 {
4348 if (!parse_gateway_usage_option(settings, um))
4349 goto fail;
4350 validOption = TRUE;
4351 allowHttpOpts = FALSE;
4352 }
4353
4354 if (allowHttpOpts)
4355 {
4356 if (option_equals(argval, "no-websockets"))
4357 {
4358 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets,
4359 FALSE))
4360 goto fail;
4361 validOption = TRUE;
4362 }
4363 else if (option_equals(argval, "extauth-sspi-ntlm"))
4364 {
4365 if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpExtAuthSspiNtlm,
4366 TRUE))
4367 goto fail;
4368 validOption = TRUE;
4369 }
4370 }
4371
4372 if (!validOption)
4373 goto fail;
4374 }
4375 }
4376
4377 rc = TRUE;
4378fail:
4379 free(argval);
4380 CommandLineParserFree(ptr);
4381 return rc;
4382}
4383
4384static void fill_credential_string(COMMAND_LINE_ARGUMENT_A* args, const char* value)
4385{
4386 WINPR_ASSERT(args);
4387 WINPR_ASSERT(value);
4388
4389 const COMMAND_LINE_ARGUMENT_A* arg = CommandLineFindArgumentA(args, value);
4390 if (!arg)
4391 return;
4392
4393 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
4394 FillMemory(arg->Value, strlen(arg->Value), '*');
4395}
4396
4397static void fill_credential_strings(COMMAND_LINE_ARGUMENT_A* args)
4398{
4399 for (size_t x = 0; x < ARRAYSIZE(credential_args); x++)
4400 {
4401 const char* cred = credential_args[x];
4402 fill_credential_string(args, cred);
4403 }
4404
4405 const COMMAND_LINE_ARGUMENT_A* arg = CommandLineFindArgumentA(args, "gateway");
4406 if (arg && ((arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) != 0))
4407 {
4408 const char* gwcreds[] = { "p:", "access-token:" };
4409 char* saveptr = nullptr;
4410 char* tok = strtok_s(arg->Value, ",", &saveptr);
4411 while (tok)
4412 {
4413 for (size_t x = 0; x < ARRAYSIZE(gwcreds); x++)
4414 {
4415 const char* opt = gwcreds[x];
4416 if (option_starts_with(opt, tok))
4417 {
4418 char* val = &tok[strlen(opt)];
4419 FillMemory(val, strlen(val), '*');
4420 }
4421 }
4422 tok = strtok_s(nullptr, ",", &saveptr);
4423 }
4424 }
4425}
4426
4427static int parse_command_line_option_uint32(rdpSettings* settings,
4428 const COMMAND_LINE_ARGUMENT_A* arg,
4429 FreeRDP_Settings_Keys_UInt32 key, LONGLONG min,
4430 LONGLONG max)
4431{
4432 LONGLONG val = 0;
4433
4434 if (!value_to_int(arg->Value, &val, min, max))
4435 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4436
4437 if (!freerdp_settings_set_uint32(settings, key, (UINT32)val))
4438 return fail_at(arg, COMMAND_LINE_ERROR);
4439 return 0;
4440}
4441
4442#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
4443static int parse_deprecated_command_line(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg)
4444{
4445 int status = 0;
4446
4447 WINPR_ASSERT(settings);
4448 WINPR_ASSERT(arg);
4449
4450 BOOL enable = arg->Value ? TRUE : FALSE;
4451 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "gfx-thin-client")
4452 {
4453 WLog_WARN(TAG, "/gfx-thin-client is deprecated, use /gfx:thin-client[:on|off] instead");
4454 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxThinClient, enable))
4455 return fail_at(arg, COMMAND_LINE_ERROR);
4456
4457 if (freerdp_settings_get_bool(settings, FreeRDP_GfxThinClient))
4458 {
4459 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxSmallCache, TRUE))
4460 return fail_at(arg, COMMAND_LINE_ERROR);
4461 }
4462
4463 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE))
4464 return fail_at(arg, COMMAND_LINE_ERROR);
4465 }
4466 CommandLineSwitchCase(arg, "gfx-small-cache")
4467 {
4468 WLog_WARN(TAG, "/gfx-small-cache is deprecated, use /gfx:small-cache[:on|off] instead");
4469 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxSmallCache, enable))
4470 return fail_at(arg, COMMAND_LINE_ERROR);
4471
4472 if (enable)
4473 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE))
4474 return fail_at(arg, COMMAND_LINE_ERROR);
4475 }
4476 CommandLineSwitchCase(arg, "gfx-progressive")
4477 {
4478 WLog_WARN(TAG, "/gfx-progressive is deprecated, use /gfx:progressive[:on|off] instead");
4479 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, enable))
4480 return fail_at(arg, COMMAND_LINE_ERROR);
4481 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxThinClient, !enable))
4482 return fail_at(arg, COMMAND_LINE_ERROR);
4483
4484 if (enable)
4485 {
4486 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE))
4487 return fail_at(arg, COMMAND_LINE_ERROR);
4488 }
4489 }
4490#ifdef WITH_GFX_H264
4491 CommandLineSwitchCase(arg, "gfx-h264")
4492 {
4493 WLog_WARN(TAG, "/gfx-h264 is deprecated, use /gfx:avc420 instead");
4494 int rc = parse_gfx_options(settings, arg);
4495 if (rc != 0)
4496 return fail_at(arg, rc);
4497 }
4498#endif
4499 CommandLineSwitchCase(arg, "app-workdir")
4500 {
4501 WLog_WARN(TAG,
4502 "/app-workdir:<directory> is deprecated, use /app:workdir:<directory> instead");
4503 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationWorkingDir, arg->Value))
4504 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4505 }
4506 CommandLineSwitchCase(arg, "app-name")
4507 {
4508 WLog_WARN(TAG, "/app-name:<directory> is deprecated, use /app:name:<name> instead");
4509 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationName, arg->Value))
4510 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4511 }
4512 CommandLineSwitchCase(arg, "app-icon")
4513 {
4514 WLog_WARN(TAG, "/app-icon:<filename> is deprecated, use /app:icon:<filename> instead");
4515 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationIcon, arg->Value))
4516 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4517 }
4518 CommandLineSwitchCase(arg, "app-cmd")
4519 {
4520 WLog_WARN(TAG, "/app-cmd:<command> is deprecated, use /app:cmd:<command> instead");
4521 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationCmdLine, arg->Value))
4522 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4523 }
4524 CommandLineSwitchCase(arg, "app-file")
4525 {
4526 WLog_WARN(TAG, "/app-file:<filename> is deprecated, use /app:file:<filename> instead");
4527 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationFile, arg->Value))
4528 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4529 }
4530 CommandLineSwitchCase(arg, "app-guid")
4531 {
4532 WLog_WARN(TAG, "/app-guid:<guid> is deprecated, use /app:guid:<guid> instead");
4533 if (!freerdp_settings_set_string(settings, FreeRDP_RemoteApplicationGuid, arg->Value))
4534 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4535 }
4536 CommandLineSwitchCase(arg, "g")
4537 {
4538 if (!parse_gateway_host_option(settings, arg->Value))
4539 return fail_at(arg, COMMAND_LINE_ERROR);
4540 }
4541 CommandLineSwitchCase(arg, "gu")
4542 {
4543 if (!parse_gateway_cred_option(settings, arg->Value, FreeRDP_GatewayUsername))
4544 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4545 }
4546 CommandLineSwitchCase(arg, "gd")
4547 {
4548 if (!parse_gateway_cred_option(settings, arg->Value, FreeRDP_GatewayDomain))
4549 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4550 }
4551 CommandLineSwitchCase(arg, "gp")
4552 {
4553 if (!parse_gateway_cred_option(settings, arg->Value, FreeRDP_GatewayPassword))
4554 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4555 }
4556 CommandLineSwitchCase(arg, "gt")
4557 {
4558 if (!parse_gateway_type_option(settings, arg->Value))
4559 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4560 }
4561 CommandLineSwitchCase(arg, "gat")
4562 {
4563 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayAccessToken, arg->Value))
4564 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4565 }
4566 CommandLineSwitchCase(arg, "gateway-usage-method")
4567 {
4568 if (!parse_gateway_usage_option(settings, arg->Value))
4569 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4570 }
4571 CommandLineSwitchCase(arg, "kbd-remap")
4572 {
4573 WLog_WARN(TAG, "/kbd-remap:<key>=<value>,<key2>=<value2> is deprecated, use "
4574 "/kbd:remap:<key>=<value>,remap:<key2>=<value2>,... instead");
4575 if (!freerdp_settings_set_string(settings, FreeRDP_KeyboardRemappingList, arg->Value))
4576 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4577 }
4578 CommandLineSwitchCase(arg, "kbd-lang")
4579 {
4580 LONGLONG val = 0;
4581
4582 WLog_WARN(TAG, "/kbd-lang:<value> is deprecated, use /kbd:lang:<value> instead");
4583 if (!value_to_int(arg->Value, &val, 1, UINT32_MAX))
4584 {
4585 WLog_ERR(TAG, "Could not identify keyboard active language %s", arg->Value);
4586 WLog_ERR(TAG, "Use /list:kbd-lang to list available layouts");
4587 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4588 }
4589
4590 if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardCodePage, (UINT32)val))
4591 return fail_at(arg, COMMAND_LINE_ERROR);
4592 }
4593 CommandLineSwitchCase(arg, "kbd-type")
4594 {
4595 WLog_WARN(TAG, "/kbd-type:<value> is deprecated, use /kbd:type:<value> instead");
4596 const int rc =
4597 parse_command_line_option_uint32(settings, arg, FreeRDP_KeyboardType, 0, UINT32_MAX);
4598 if (rc != 0)
4599 return fail_at(arg, rc);
4600 }
4601 CommandLineSwitchCase(arg, "kbd-unicode")
4602 {
4603 WLog_WARN(TAG, "/kbd-unicode is deprecated, use /kbd:unicode[:on|off] instead");
4604 if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput, enable))
4605 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4606 }
4607 CommandLineSwitchCase(arg, "kbd-subtype")
4608 {
4609 WLog_WARN(TAG, "/kbd-subtype:<value> is deprecated, use /kbd:subtype:<value> instead");
4610 const int rc =
4611 parse_command_line_option_uint32(settings, arg, FreeRDP_KeyboardSubType, 0, UINT32_MAX);
4612 if (rc != 0)
4613 return fail_at(arg, rc);
4614 }
4615 CommandLineSwitchCase(arg, "kbd-fn-key")
4616 {
4617 WLog_WARN(TAG, "/kbd-fn-key:<value> is deprecated, use /kbd:fn-key:<value> instead");
4618 const int rc = parse_command_line_option_uint32(settings, arg, FreeRDP_KeyboardFunctionKey,
4619 0, UINT32_MAX);
4620 if (rc != 0)
4621 return fail_at(arg, rc);
4622 }
4623 CommandLineSwitchCase(arg, "bitmap-cache")
4624 {
4625 WLog_WARN(TAG, "/bitmap-cache is deprecated, use /cache:bitmap[:on|off] instead");
4626 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled, enable))
4627 return fail_at(arg, COMMAND_LINE_ERROR);
4628 }
4629 CommandLineSwitchCase(arg, "persist-cache")
4630 {
4631 WLog_WARN(TAG, "/persist-cache is deprecated, use /cache:persist[:on|off] instead");
4632 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled, enable))
4633 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4634 }
4635 CommandLineSwitchCase(arg, "persist-cache-file")
4636 {
4637 WLog_WARN(TAG, "/persist-cache-file:<filename> is deprecated, use "
4638 "/cache:persist-file:<filename> instead");
4639 if (!freerdp_settings_set_string(settings, FreeRDP_BitmapCachePersistFile, arg->Value))
4640 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4641
4642 if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled, TRUE))
4643 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4644 }
4645 CommandLineSwitchCase(arg, "offscreen-cache")
4646 {
4647 WLog_WARN(TAG, "/bitmap-cache is deprecated, use /cache:bitmap[:on|off] instead");
4648 if (!freerdp_settings_set_uint32(settings, FreeRDP_OffscreenSupportLevel, (UINT32)enable))
4649 return fail_at(arg, COMMAND_LINE_ERROR);
4650 }
4651 CommandLineSwitchCase(arg, "glyph-cache")
4652 {
4653 WLog_WARN(TAG, "/glyph-cache is deprecated, use /cache:glyph[:on|off] instead");
4654 if (!freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel,
4655 arg->Value ? GLYPH_SUPPORT_FULL : GLYPH_SUPPORT_NONE))
4656 return fail_at(arg, COMMAND_LINE_ERROR);
4657 }
4658 CommandLineSwitchCase(arg, "codec-cache")
4659 {
4660 WLog_WARN(TAG, "/codec-cache:<option> is deprecated, use /cache:codec:<option> instead");
4661 const int rc = parse_codec_cache_options(settings, arg);
4662 if (rc != 0)
4663 return fail_at(arg, rc);
4664 }
4665 CommandLineSwitchCase(arg, "sec-rdp")
4666 {
4667 WLog_WARN(TAG, "/sec-rdp is deprecated, use /sec:rdp[:on|off] instead");
4668 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, enable))
4669 return fail_at(arg, COMMAND_LINE_ERROR);
4670 }
4671 CommandLineSwitchCase(arg, "sec-tls")
4672 {
4673 WLog_WARN(TAG, "/sec-tls is deprecated, use /sec:tls[:on|off] instead");
4674 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, enable))
4675 return fail_at(arg, COMMAND_LINE_ERROR);
4676 }
4677 CommandLineSwitchCase(arg, "sec-nla")
4678 {
4679 WLog_WARN(TAG, "/sec-nla is deprecated, use /sec:nla[:on|off] instead");
4680 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, enable))
4681 return fail_at(arg, COMMAND_LINE_ERROR);
4682 }
4683 CommandLineSwitchCase(arg, "sec-ext")
4684 {
4685 WLog_WARN(TAG, "/sec-ext is deprecated, use /sec:ext[:on|off] instead");
4686 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, enable))
4687 return fail_at(arg, COMMAND_LINE_ERROR);
4688 }
4689 CommandLineSwitchCase(arg, "tls-ciphers")
4690 {
4691 WLog_WARN(TAG, "/tls-ciphers:<cipher list> is deprecated, use "
4692 "/tls:ciphers:<cipher list> instead");
4693 int rc = parse_tls_cipher_options(settings, arg);
4694 if (rc != 0)
4695 return fail_at(arg, rc);
4696 }
4697 CommandLineSwitchCase(arg, "tls-seclevel")
4698 {
4699 WLog_WARN(TAG, "/tls-seclevel:<level> is deprecated, use /tls:sec-level:<level> instead");
4700 int rc = parse_tls_cipher_options(settings, arg);
4701 if (rc != 0)
4702 return fail_at(arg, rc);
4703 }
4704 CommandLineSwitchCase(arg, "tls-secrets-file")
4705 {
4706 WLog_WARN(TAG, "/tls-secrets-file:<filename> is deprecated, use "
4707 "/tls:secrets-file:<filename> instead");
4708 int rc = parse_tls_cipher_options(settings, arg);
4709 if (rc != 0)
4710 return fail_at(arg, rc);
4711 }
4712 CommandLineSwitchCase(arg, "enforce-tlsv1_2")
4713 {
4714 WLog_WARN(TAG, "/enforce-tlsv1_2 is deprecated, use /tls:enforce:1.2 instead");
4715 int rc = parse_tls_cipher_options(settings, arg);
4716 if (rc != 0)
4717 return fail_at(arg, rc);
4718 }
4719 CommandLineSwitchCase(arg, "cert-name")
4720 {
4721 WLog_WARN(TAG, "/cert-name is deprecated, use /cert:name instead");
4722 if (!freerdp_settings_set_string(settings, FreeRDP_CertificateName, arg->Value))
4723 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4724 }
4725 CommandLineSwitchCase(arg, "cert-ignore")
4726 {
4727 WLog_WARN(TAG, "/cert-ignore is deprecated, use /cert:ignore instead");
4728 if (!freerdp_settings_set_bool(settings, FreeRDP_IgnoreCertificate, enable))
4729 return fail_at(arg, COMMAND_LINE_ERROR);
4730 }
4731 CommandLineSwitchCase(arg, "cert-tofu")
4732 {
4733 WLog_WARN(TAG, "/cert-tofu is deprecated, use /cert:tofu instead");
4734 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoAcceptCertificate, enable))
4735 return fail_at(arg, COMMAND_LINE_ERROR);
4736 }
4737 CommandLineSwitchCase(arg, "cert-deny")
4738 {
4739 WLog_WARN(TAG, "/cert-deny is deprecated, use /cert:deny instead");
4740 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoDenyCertificate, enable))
4741 return fail_at(arg, COMMAND_LINE_ERROR);
4742 }
4743 CommandLineSwitchDefault(arg)
4744 {
4745 status = -1;
4746 }
4747 CommandLineSwitchEnd(arg);
4748 return status;
4749}
4750#endif
4751
4752static int parse_command_line_option_timezone(rdpSettings* settings,
4753 const COMMAND_LINE_ARGUMENT_A* arg)
4754{
4755 BOOL found = FALSE;
4756 DWORD index = 0;
4757 DYNAMIC_TIME_ZONE_INFORMATION info = WINPR_C_ARRAY_INIT;
4758 char TimeZoneKeyName[ARRAYSIZE(info.TimeZoneKeyName) + 1] = WINPR_C_ARRAY_INIT;
4759 while (EnumDynamicTimeZoneInformation(index++, &info) != ERROR_NO_MORE_ITEMS)
4760 {
4761 (void)ConvertWCharNToUtf8(info.TimeZoneKeyName, ARRAYSIZE(info.TimeZoneKeyName),
4762 TimeZoneKeyName, ARRAYSIZE(TimeZoneKeyName));
4763
4764 WINPR_ASSERT(arg->Value);
4765 if (strncmp(TimeZoneKeyName, arg->Value, ARRAYSIZE(TimeZoneKeyName)) == 0)
4766 {
4767 found = TRUE;
4768 break;
4769 }
4770 }
4771 if (!found)
4772 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
4773
4774 if (!freerdp_settings_set_string(settings, FreeRDP_DynamicDSTTimeZoneKeyName, TimeZoneKeyName))
4775 return fail_at(arg, COMMAND_LINE_ERROR);
4776
4778 freerdp_settings_get_pointer_writable(settings, FreeRDP_ClientTimeZone);
4779 if (!tz)
4780 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4781
4782 tz->Bias = info.Bias;
4783 tz->DaylightBias = info.DaylightBias;
4784 tz->DaylightDate = info.DaylightDate;
4785 memcpy(tz->DaylightName, info.DaylightName, sizeof(tz->DaylightName));
4786 tz->StandardBias = info.StandardBias;
4787 tz->StandardDate = info.StandardDate;
4788 memcpy(tz->StandardName, info.StandardName, sizeof(tz->StandardName));
4789
4790 return 0;
4791}
4792
4793static int parse_command_line_option_window_pos(rdpSettings* settings,
4794 const COMMAND_LINE_ARGUMENT_A* arg)
4795{
4796 WINPR_ASSERT(settings);
4797 WINPR_ASSERT(arg);
4798
4799 unsigned long x = 0;
4800 unsigned long y = 0;
4801
4802 if (!arg->Value)
4803 return fail_at(arg, COMMAND_LINE_ERROR_MISSING_ARGUMENT);
4804
4805 if (!parseSizeValue(arg->Value, &x, &y) || x > UINT16_MAX || y > UINT16_MAX)
4806 {
4807 WLog_ERR(TAG, "invalid window-position argument");
4808 return fail_at(arg, COMMAND_LINE_ERROR_MISSING_ARGUMENT);
4809 }
4810
4811 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopPosX, (UINT32)x))
4812 return fail_at(arg, COMMAND_LINE_ERROR);
4813 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopPosY, (UINT32)y))
4814 return fail_at(arg, COMMAND_LINE_ERROR);
4815 return 0;
4816}
4817
4818static int parse_command_line(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* arg,
4819 freerdp_command_line_handle_option_t handle_option,
4820 void* handle_userdata, BOOL* promptForPassword, char** user)
4821{
4822 WINPR_ASSERT(promptForPassword);
4823 WINPR_ASSERT(user);
4824
4825 do
4826 {
4827 BOOL enable = (arg->Value != nullptr);
4828
4829 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
4830 continue;
4831
4832 CommandLineSwitchStart(arg)
4833
4834 CommandLineSwitchCase(arg, "v")
4835 {
4836 const int rc = parse_host_options(settings, arg);
4837 if (rc != 0)
4838 return fail_at(arg, rc);
4839 }
4840 CommandLineSwitchCase(arg, "spn-class")
4841 {
4842 if (!freerdp_settings_set_string(settings, FreeRDP_AuthenticationServiceClass,
4843 arg->Value))
4844 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4845 }
4846 CommandLineSwitchCase(arg, "sspi-module")
4847 {
4848 if (!freerdp_settings_set_string(settings, FreeRDP_SspiModule, arg->Value))
4849 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4850 }
4851 CommandLineSwitchCase(arg, "winscard-module")
4852 {
4853 if (!freerdp_settings_set_string(settings, FreeRDP_WinSCardModule, arg->Value))
4854 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4855 }
4856 CommandLineSwitchCase(arg, "redirect-prefer")
4857 {
4858 const int rc = parse_redirect_prefer_options(settings, arg);
4859 if (rc != 0)
4860 return fail_at(arg, rc);
4861 }
4862 CommandLineSwitchCase(arg, "credentials-delegation")
4863 {
4864 if (!freerdp_settings_set_bool(settings, FreeRDP_DisableCredentialsDelegation, !enable))
4865 return fail_at(arg, COMMAND_LINE_ERROR);
4866 }
4867 CommandLineSwitchCase(arg, "prevent-session-lock")
4868 {
4869 const int rc = parse_prevent_session_lock_options(settings, arg);
4870 if (rc != 0)
4871 return fail_at(arg, rc);
4872 }
4873 CommandLineSwitchCase(arg, "vmconnect")
4874 {
4875 const int rc = parse_vmconnect_options(settings, arg);
4876 if (rc != 0)
4877 return fail_at(arg, rc);
4878 }
4879 CommandLineSwitchCase(arg, "w")
4880 {
4881 const int rc = parse_command_line_option_uint32(settings, arg, FreeRDP_DesktopWidth, -1,
4882 UINT32_MAX);
4883 if (rc != 0)
4884 return fail_at(arg, rc);
4885 }
4886 CommandLineSwitchCase(arg, "h")
4887 {
4888 const int rc = parse_command_line_option_uint32(settings, arg, FreeRDP_DesktopHeight,
4889 -1, UINT32_MAX);
4890 if (rc != 0)
4891 return fail_at(arg, rc);
4892 }
4893 CommandLineSwitchCase(arg, "size")
4894 {
4895 const int rc = parse_size_options(settings, arg);
4896 if (rc != 0)
4897 return fail_at(arg, rc);
4898 }
4899 CommandLineSwitchCase(arg, "f")
4900 {
4901 if (!freerdp_settings_set_bool(settings, FreeRDP_Fullscreen, enable))
4902 return fail_at(arg, COMMAND_LINE_ERROR);
4903 }
4904 CommandLineSwitchCase(arg, "suppress-output")
4905 {
4906 if (!freerdp_settings_set_bool(settings, FreeRDP_SuppressOutput, enable))
4907 return fail_at(arg, COMMAND_LINE_ERROR);
4908 }
4909 CommandLineSwitchCase(arg, "multimon")
4910 {
4911 if (!freerdp_settings_set_bool(settings, FreeRDP_UseMultimon, TRUE))
4912 return fail_at(arg, COMMAND_LINE_ERROR);
4913
4914 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
4915 {
4916 if (option_equals(arg->Value, str_force))
4917 {
4918 if (!freerdp_settings_set_bool(settings, FreeRDP_ForceMultimon, TRUE))
4919 return fail_at(arg, COMMAND_LINE_ERROR);
4920 }
4921 }
4922 }
4923 CommandLineSwitchCase(arg, "span")
4924 {
4925 if (!freerdp_settings_set_bool(settings, FreeRDP_SpanMonitors, enable))
4926 return fail_at(arg, COMMAND_LINE_ERROR);
4927 }
4928 CommandLineSwitchCase(arg, "workarea")
4929 {
4930 if (!freerdp_settings_set_bool(settings, FreeRDP_Workarea, enable))
4931 return fail_at(arg, COMMAND_LINE_ERROR);
4932 }
4933 CommandLineSwitchCase(arg, "monitors")
4934 {
4935 const int rc = parse_monitors_options(settings, arg);
4936 if (rc != 0)
4937 return fail_at(arg, rc);
4938 }
4939 CommandLineSwitchCase(arg, "t")
4940 {
4941 if (!freerdp_settings_set_string(settings, FreeRDP_WindowTitle, arg->Value))
4942 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
4943 }
4944 CommandLineSwitchCase(arg, "decorations")
4945 {
4946 if (!freerdp_settings_set_bool(settings, FreeRDP_Decorations, enable))
4947 return fail_at(arg, COMMAND_LINE_ERROR);
4948 }
4949 CommandLineSwitchCase(arg, "dynamic-resolution")
4950 {
4951 const int rc = parse_dynamic_resolution_options(settings, arg);
4952 if (rc != 0)
4953 return fail_at(arg, rc);
4954 }
4955 CommandLineSwitchCase(arg, "smart-sizing")
4956 {
4957 const int rc = parse_smart_sizing_options(settings, arg);
4958 if (rc != 0)
4959 return fail_at(arg, rc);
4960 }
4961 CommandLineSwitchCase(arg, "bpp")
4962 {
4963 const int rc = parse_bpp_options(settings, arg);
4964 if (rc != 0)
4965 return fail_at(arg, rc);
4966 }
4967 CommandLineSwitchCase(arg, "admin")
4968 {
4969 if (!freerdp_settings_set_bool(settings, FreeRDP_ConsoleSession, enable))
4970 return fail_at(arg, COMMAND_LINE_ERROR);
4971 }
4972 CommandLineSwitchCase(arg, "relax-order-checks")
4973 {
4974 if (!freerdp_settings_set_bool(settings, FreeRDP_AllowUnanouncedOrdersFromServer,
4975 enable))
4976 return fail_at(arg, COMMAND_LINE_ERROR);
4977 }
4978 CommandLineSwitchCase(arg, "restricted-admin")
4979 {
4980 if (!freerdp_settings_set_bool(settings, FreeRDP_ConsoleSession, enable))
4981 return fail_at(arg, COMMAND_LINE_ERROR);
4982 if (!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeRequired, enable))
4983 return fail_at(arg, COMMAND_LINE_ERROR);
4984 }
4985#ifdef CHANNEL_RDPEAR_CLIENT
4986 CommandLineSwitchCase(arg, "remoteGuard")
4987 {
4988 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard, TRUE))
4989 return fail_at(arg, COMMAND_LINE_ERROR);
4990 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
4991 return fail_at(arg, COMMAND_LINE_ERROR);
4992 }
4993#endif
4994 CommandLineSwitchCase(arg, "pth")
4995 {
4996 if (!freerdp_settings_set_bool(settings, FreeRDP_ConsoleSession, TRUE))
4997 return fail_at(arg, COMMAND_LINE_ERROR);
4998 if (!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeRequired, TRUE))
4999 return fail_at(arg, COMMAND_LINE_ERROR);
5000
5001 if (!freerdp_settings_set_string(settings, FreeRDP_PasswordHash, arg->Value))
5002 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5003 }
5004 CommandLineSwitchCase(arg, "client-hostname")
5005 {
5006 if (!freerdp_settings_set_string(settings, FreeRDP_ClientHostname, arg->Value))
5007 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5008 }
5009 CommandLineSwitchCase(arg, "kbd")
5010 {
5011 int rc = parse_kbd_options(settings, arg);
5012 if (rc != 0)
5013 return fail_at(arg, rc);
5014 }
5015
5016 CommandLineSwitchCase(arg, "u")
5017 {
5018 WINPR_ASSERT(arg->Value);
5019 *user = arg->Value;
5020 }
5021 CommandLineSwitchCase(arg, "d")
5022 {
5023 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, arg->Value))
5024 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5025 }
5026 CommandLineSwitchCase(arg, "p")
5027 {
5028 /* In case of an optional password set that, if none provided set to empty string.
5029 * this way we know later on that we intentionally left the password blank. */
5030 const char* val = arg->Value;
5031 if (!val)
5032 val = "";
5033
5034 if (!freerdp_settings_set_string(settings, FreeRDP_Password, val))
5035 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5036 }
5037 CommandLineSwitchCase(arg, "gateway")
5038 {
5039 if (!parse_gateway_options(settings, arg))
5040 return fail_at(arg, COMMAND_LINE_ERROR);
5041 }
5042 CommandLineSwitchCase(arg, "proxy")
5043 {
5044 const int rc = parse_proxy_options(settings, arg);
5045 if (rc != 0)
5046 return fail_at(arg, rc);
5047 }
5048
5049 CommandLineSwitchCase(arg, "azure")
5050 {
5051 int rc = parse_aad_options(settings, arg);
5052 if (rc != 0)
5053 return fail_at(arg, rc);
5054 }
5055 CommandLineSwitchCase(arg, "app")
5056 {
5057 int rc = parse_app_options(settings, arg);
5058 if (rc != 0)
5059 return fail_at(arg, rc);
5060 }
5061 CommandLineSwitchCase(arg, "load-balance-info")
5062 {
5063 WINPR_ASSERT(arg->Value);
5064 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, arg->Value,
5065 strlen(arg->Value)))
5066 return fail_at(arg, COMMAND_LINE_ERROR);
5067 }
5068
5069 CommandLineSwitchCase(arg, "compression")
5070 {
5071 if (!freerdp_settings_set_bool(settings, FreeRDP_CompressionEnabled, enable))
5072 return fail_at(arg, COMMAND_LINE_ERROR);
5073 }
5074 CommandLineSwitchCase(arg, "compression-level")
5075 {
5076 const int rc = parse_command_line_option_uint32(settings, arg, FreeRDP_CompressionLevel,
5077 0, UINT32_MAX);
5078 if (rc != 0)
5079 return fail_at(arg, rc);
5080 }
5081 CommandLineSwitchCase(arg, "drives")
5082 {
5083 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectDrives, enable))
5084 return fail_at(arg, COMMAND_LINE_ERROR);
5085 }
5086 CommandLineSwitchCase(arg, "dump")
5087 {
5088 const int rc = parse_dump_options(settings, arg);
5089 if (rc != 0)
5090 return fail_at(arg, rc);
5091 }
5092 CommandLineSwitchCase(arg, "disable-output")
5093 {
5094 if (!freerdp_settings_set_bool(settings, FreeRDP_DeactivateClientDecoding, enable))
5095 return fail_at(arg, COMMAND_LINE_ERROR);
5096 }
5097 CommandLineSwitchCase(arg, "home-drive")
5098 {
5099 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectHomeDrive, enable))
5100 return fail_at(arg, COMMAND_LINE_ERROR);
5101 }
5102 CommandLineSwitchCase(arg, "ipv4")
5103 {
5104 if (arg->Value != nullptr && strncmp(arg->Value, str_force, ARRAYSIZE(str_force)) == 0)
5105 {
5106 if (!freerdp_settings_set_uint32(settings, FreeRDP_ForceIPvX, 4))
5107 return fail_at(arg, COMMAND_LINE_ERROR);
5108 }
5109 else
5110 {
5111 if (!freerdp_settings_set_bool(settings, FreeRDP_PreferIPv6OverIPv4, FALSE))
5112 return fail_at(arg, COMMAND_LINE_ERROR);
5113 }
5114 }
5115 CommandLineSwitchCase(arg, "ipv6")
5116 {
5117 if (arg->Value != nullptr && strncmp(arg->Value, str_force, ARRAYSIZE(str_force)) == 0)
5118 {
5119 if (!freerdp_settings_set_uint32(settings, FreeRDP_ForceIPvX, 6))
5120 return fail_at(arg, COMMAND_LINE_ERROR);
5121 }
5122 else
5123 {
5124 if (!freerdp_settings_set_bool(settings, FreeRDP_PreferIPv6OverIPv4, TRUE))
5125 return fail_at(arg, COMMAND_LINE_ERROR);
5126 }
5127 }
5128 CommandLineSwitchCase(arg, "clipboard")
5129 {
5130 const int rc = parse_clipboard_options(settings, arg);
5131 if (rc != 0)
5132 return fail_at(arg, rc);
5133 }
5134 CommandLineSwitchCase(arg, "server-name")
5135 {
5136 if (!freerdp_settings_set_string(settings, FreeRDP_UserSpecifiedServerName, arg->Value))
5137 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5138 }
5139 CommandLineSwitchCase(arg, "shell")
5140 {
5141 if (!freerdp_settings_set_string(settings, FreeRDP_AlternateShell, arg->Value))
5142 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5143 }
5144 CommandLineSwitchCase(arg, "shell-dir")
5145 {
5146 if (!freerdp_settings_set_string(settings, FreeRDP_ShellWorkingDirectory, arg->Value))
5147 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5148 }
5149 CommandLineSwitchCase(arg, "audio-mode")
5150 {
5151 const int rc = parse_audio_mode_options(settings, arg);
5152 if (rc != 0)
5153 return fail_at(arg, rc);
5154 }
5155 CommandLineSwitchCase(arg, "network")
5156 {
5157 const int rc = parse_network_options(settings, arg);
5158 if (rc != 0)
5159 return fail_at(arg, rc);
5160 }
5161 CommandLineSwitchCase(arg, "fonts")
5162 {
5163 if (!freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, enable))
5164 return fail_at(arg, COMMAND_LINE_ERROR);
5165 }
5166 CommandLineSwitchCase(arg, "wallpaper")
5167 {
5168 if (!freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, !enable))
5169 return fail_at(arg, COMMAND_LINE_ERROR);
5170 }
5171 CommandLineSwitchCase(arg, "window-drag")
5172 {
5173 if (!freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, !enable))
5174 return fail_at(arg, COMMAND_LINE_ERROR);
5175 }
5176 CommandLineSwitchCase(arg, "window-position")
5177 {
5178 const int rc = parse_command_line_option_window_pos(settings, arg);
5179 if (rc != 0)
5180 return fail_at(arg, rc);
5181 }
5182 CommandLineSwitchCase(arg, "menu-anims")
5183 {
5184 if (!freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, !enable))
5185 return fail_at(arg, COMMAND_LINE_ERROR);
5186 }
5187 CommandLineSwitchCase(arg, "themes")
5188 {
5189 if (!freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, !enable))
5190 return fail_at(arg, COMMAND_LINE_ERROR);
5191 }
5192 CommandLineSwitchCase(arg, "timeout")
5193 {
5194 const int rc =
5195 parse_command_line_option_uint32(settings, arg, FreeRDP_TcpAckTimeout, 0, 600000);
5196 if (rc != 0)
5197 return fail_at(arg, rc);
5198 }
5199 CommandLineSwitchCase(arg, "timezone")
5200 {
5201 const int rc = parse_command_line_option_timezone(settings, arg);
5202 if (rc != 0)
5203 return fail_at(arg, rc);
5204 }
5205 CommandLineSwitchCase(arg, "aero")
5206 {
5207 if (!freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, enable))
5208 return fail_at(arg, COMMAND_LINE_ERROR);
5209 }
5210 CommandLineSwitchCase(arg, "gdi")
5211 {
5212 if (option_equals(arg->Value, "sw"))
5213 {
5214 if (!freerdp_settings_set_bool(settings, FreeRDP_SoftwareGdi, TRUE))
5215 return fail_at(arg, COMMAND_LINE_ERROR);
5216 }
5217 else if (option_equals(arg->Value, "hw"))
5218 {
5219 if (!freerdp_settings_set_bool(settings, FreeRDP_SoftwareGdi, FALSE))
5220 return fail_at(arg, COMMAND_LINE_ERROR);
5221 }
5222 else
5223 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5224 }
5225 CommandLineSwitchCase(arg, "gfx")
5226 {
5227 int rc = parse_gfx_options(settings, arg);
5228 if (rc != 0)
5229 return fail_at(arg, rc);
5230 }
5231
5232 CommandLineSwitchCase(arg, "rfx")
5233 {
5234 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, enable))
5235 return fail_at(arg, COMMAND_LINE_ERROR);
5236 }
5237 CommandLineSwitchCase(arg, "rfx-mode")
5238 {
5239 if (!arg->Value)
5240 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5241
5242 if (option_equals(arg->Value, "video"))
5243 {
5244 if (!freerdp_settings_set_uint32(settings, FreeRDP_RemoteFxCodecMode, 0x00))
5245 return fail_at(arg, COMMAND_LINE_ERROR);
5246 }
5247 else if (option_equals(arg->Value, "image"))
5248 {
5249 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxImageCodec, TRUE))
5250 return fail_at(arg, COMMAND_LINE_ERROR);
5251 if (!freerdp_settings_set_uint32(settings, FreeRDP_RemoteFxCodecMode, 0x02))
5252 return fail_at(arg, COMMAND_LINE_ERROR);
5253 }
5254 }
5255 CommandLineSwitchCase(arg, "frame-ack")
5256 {
5257 const int rc = parse_command_line_option_uint32(settings, arg, FreeRDP_FrameAcknowledge,
5258 0, UINT32_MAX);
5259 if (rc != 0)
5260 return fail_at(arg, rc);
5261 }
5262 CommandLineSwitchCase(arg, "nsc")
5263 {
5264 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, enable))
5265 return fail_at(arg, COMMAND_LINE_ERROR);
5266 }
5267#if defined(WITH_JPEG)
5268 CommandLineSwitchCase(arg, "jpeg")
5269 {
5270 if (!freerdp_settings_set_bool(settings, FreeRDP_JpegCodec, enable))
5271 return fail_at(arg, COMMAND_LINE_ERROR);
5272 if (!freerdp_settings_set_uint32(settings, FreeRDP_JpegQuality, 75))
5273 return fail_at(arg, COMMAND_LINE_ERROR);
5274 }
5275 CommandLineSwitchCase(arg, "jpeg-quality")
5276 {
5277 LONGLONG val = 0;
5278
5279 if (!value_to_int(arg->Value, &val, 0, 100))
5280 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5281
5282 if (!freerdp_settings_set_uint32(settings, FreeRDP_JpegQuality, (UINT32)val))
5283 return fail_at(arg, COMMAND_LINE_ERROR);
5284 }
5285#endif
5286 CommandLineSwitchCase(arg, "nego")
5287 {
5288 if (!freerdp_settings_set_bool(settings, FreeRDP_NegotiateSecurityLayer, enable))
5289 return fail_at(arg, COMMAND_LINE_ERROR);
5290 }
5291 CommandLineSwitchCase(arg, "pcb")
5292 {
5293 if (!freerdp_settings_set_bool(settings, FreeRDP_SendPreconnectionPdu, TRUE))
5294 return fail_at(arg, COMMAND_LINE_ERROR);
5295
5296 if (!freerdp_settings_set_string(settings, FreeRDP_PreconnectionBlob, arg->Value))
5297 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5298 }
5299 CommandLineSwitchCase(arg, "pcid")
5300 {
5301 const int rc = parse_command_line_option_uint32(settings, arg, FreeRDP_PreconnectionId,
5302 0, UINT32_MAX);
5303 if (rc != 0)
5304 return fail_at(arg, rc);
5305 if (!freerdp_settings_set_bool(settings, FreeRDP_SendPreconnectionPdu, TRUE))
5306 return fail_at(arg, COMMAND_LINE_ERROR);
5307 }
5308#ifdef _WIN32
5309 CommandLineSwitchCase(arg, "connect-child-session")
5310 {
5311 if (!freerdp_settings_set_string(settings, FreeRDP_AuthenticationServiceClass,
5312 "vs-debug") ||
5313 !freerdp_settings_set_string(settings, FreeRDP_ServerHostname, "localhost") ||
5314 !freerdp_settings_set_string(settings, FreeRDP_AuthenticationPackageList, "ntlm") ||
5315 !freerdp_settings_set_string(settings, FreeRDP_ClientAddress, "0.0.0.0") ||
5316 !freerdp_settings_set_bool(settings, FreeRDP_NegotiateSecurityLayer, FALSE) ||
5317 !freerdp_settings_set_bool(settings, FreeRDP_VmConnectMode, TRUE) ||
5318 !freerdp_settings_set_bool(settings, FreeRDP_ConnectChildSession, TRUE) ||
5319 !freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE) ||
5320 !freerdp_settings_set_uint32(settings, FreeRDP_AuthenticationLevel, 0) ||
5321 !freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, TRUE) ||
5322 !freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, CONNECTION_TYPE_LAN))
5323 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5324 }
5325#endif
5326 CommandLineSwitchCase(arg, "sec")
5327 {
5328 const int rc = parse_sec_options(settings, arg);
5329 if (rc != 0)
5330 return fail_at(arg, rc);
5331 }
5332 CommandLineSwitchCase(arg, "encryption-methods")
5333 {
5334 const int rc = parse_encryption_methods_options(settings, arg);
5335 if (rc != 0)
5336 return fail_at(arg, rc);
5337 }
5338 CommandLineSwitchCase(arg, "args-from")
5339 {
5340 WLog_ERR(TAG, "/args-from:%s can not be used in combination with other arguments!",
5341 arg->Value);
5342 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5343 }
5344 CommandLineSwitchCase(arg, "from-stdin")
5345 {
5346 if (!freerdp_settings_set_bool(settings, FreeRDP_CredentialsFromStdin, TRUE))
5347 return fail_at(arg, COMMAND_LINE_ERROR);
5348
5349 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
5350 {
5351 if (!arg->Value)
5352 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5353 *promptForPassword = (option_equals(arg->Value, str_force));
5354
5355 if (!*promptForPassword)
5356 return fail_at(arg, COMMAND_LINE_ERROR);
5357 }
5358 }
5359 CommandLineSwitchCase(arg, "log-level")
5360 {
5361 wLog* root = WLog_GetRoot();
5362
5363 if (!WLog_SetStringLogLevel(root, arg->Value))
5364 return fail_at(arg, COMMAND_LINE_ERROR);
5365 }
5366 CommandLineSwitchCase(arg, "log-filters")
5367 {
5368 if (!WLog_AddStringLogFilters(arg->Value))
5369 return fail_at(arg, COMMAND_LINE_ERROR);
5370 }
5371 CommandLineSwitchCase(arg, "tls")
5372 {
5373 int rc = parse_tls_options(settings, arg);
5374 if (rc != 0)
5375 return fail_at(arg, rc);
5376 }
5377 CommandLineSwitchCase(arg, "cert")
5378 {
5379 const int rc = parse_cert_options(settings, arg);
5380 if (rc != 0)
5381 return fail_at(arg, rc);
5382 }
5383 CommandLineSwitchCase(arg, "authentication")
5384 {
5385 if (!freerdp_settings_set_bool(settings, FreeRDP_Authentication, enable))
5386 return fail_at(arg, COMMAND_LINE_ERROR);
5387 }
5388 CommandLineSwitchCase(arg, "encryption")
5389 {
5390 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, !enable))
5391 return fail_at(arg, COMMAND_LINE_ERROR);
5392 }
5393 CommandLineSwitchCase(arg, "grab-keyboard")
5394 {
5395 if (!freerdp_settings_set_bool(settings, FreeRDP_GrabKeyboard, enable))
5396 return fail_at(arg, COMMAND_LINE_ERROR);
5397 }
5398 CommandLineSwitchCase(arg, "grab-mouse")
5399 {
5400 if (!freerdp_settings_set_bool(settings, FreeRDP_GrabMouse, enable))
5401 return fail_at(arg, COMMAND_LINE_ERROR);
5402 }
5403 CommandLineSwitchCase(arg, "mouse-relative")
5404 {
5405 if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, enable))
5406 return fail_at(arg, COMMAND_LINE_ERROR);
5407 }
5408 CommandLineSwitchCase(arg, "mouse")
5409 {
5410 const int rc = parse_mouse_options(settings, arg);
5411 if (rc != 0)
5412 return fail_at(arg, rc);
5413 }
5414 CommandLineSwitchCase(arg, "unmap-buttons")
5415 {
5416 if (!freerdp_settings_set_bool(settings, FreeRDP_UnmapButtons, enable))
5417 return fail_at(arg, COMMAND_LINE_ERROR);
5418 }
5419 CommandLineSwitchCase(arg, "toggle-fullscreen")
5420 {
5421 if (!freerdp_settings_set_bool(settings, FreeRDP_ToggleFullscreen, enable))
5422 return fail_at(arg, COMMAND_LINE_ERROR);
5423 }
5424 CommandLineSwitchCase(arg, "force-console-callbacks")
5425 {
5426 if (!freerdp_settings_set_bool(settings, FreeRDP_UseCommonStdioCallbacks, enable))
5427 return fail_at(arg, COMMAND_LINE_ERROR);
5428 }
5429 CommandLineSwitchCase(arg, "floatbar")
5430 {
5431 const int rc = parse_floatbar_options(settings, arg);
5432 if (rc != 0)
5433 return fail_at(arg, rc);
5434 }
5435 CommandLineSwitchCase(arg, "mouse-motion")
5436 {
5437 if (!freerdp_settings_set_bool(settings, FreeRDP_MouseMotion, enable))
5438 return fail_at(arg, COMMAND_LINE_ERROR);
5439 }
5440 CommandLineSwitchCase(arg, "parent-window")
5441 {
5442 ULONGLONG val = 0;
5443
5444 if (!value_to_uint(arg->Value, &val, 0, UINT64_MAX))
5445 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5446
5447 if (!freerdp_settings_set_uint64(settings, FreeRDP_ParentWindowId, (UINT64)val))
5448 return fail_at(arg, COMMAND_LINE_ERROR);
5449 }
5450 CommandLineSwitchCase(arg, "client-build-number")
5451 {
5452 const int rc =
5453 parse_command_line_option_uint32(settings, arg, FreeRDP_ClientBuild, 0, UINT32_MAX);
5454 if (rc != 0)
5455 return fail_at(arg, rc);
5456 }
5457 CommandLineSwitchCase(arg, "cache")
5458 {
5459 int rc = parse_cache_options(settings, arg);
5460 if (rc != 0)
5461 return fail_at(arg, rc);
5462 }
5463
5464 CommandLineSwitchCase(arg, "max-fast-path-size")
5465 {
5466 const int rc = parse_command_line_option_uint32(
5467 settings, arg, FreeRDP_MultifragMaxRequestSize, 0, UINT32_MAX);
5468 if (rc != 0)
5469 return fail_at(arg, rc);
5470 }
5471 CommandLineSwitchCase(arg, "auto-request-control")
5472 {
5473 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl,
5474 enable))
5475 return fail_at(arg, COMMAND_LINE_ERROR);
5476 }
5477 CommandLineSwitchCase(arg, "async-update")
5478 {
5479 if (!freerdp_settings_set_bool(settings, FreeRDP_AsyncUpdate, enable))
5480 return fail_at(arg, COMMAND_LINE_ERROR);
5481 }
5482 CommandLineSwitchCase(arg, "async-channels")
5483 {
5484 if (!freerdp_settings_set_bool(settings, FreeRDP_AsyncChannels, enable))
5485 return fail_at(arg, COMMAND_LINE_ERROR);
5486 }
5487 CommandLineSwitchCase(arg, "wm-class")
5488 {
5489 if (!freerdp_settings_set_string(settings, FreeRDP_WmClass, arg->Value))
5490 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5491 }
5492 CommandLineSwitchCase(arg, "play-rfx")
5493 {
5494 if (!freerdp_settings_set_string(settings, FreeRDP_PlayRemoteFxFile, arg->Value))
5495 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5496
5497 if (!freerdp_settings_set_bool(settings, FreeRDP_PlayRemoteFx, TRUE))
5498 return fail_at(arg, COMMAND_LINE_ERROR);
5499 }
5500 CommandLineSwitchCase(arg, "auth-only")
5501 {
5502 if (!freerdp_settings_set_bool(settings, FreeRDP_AuthenticationOnly, enable))
5503 return fail_at(arg, COMMAND_LINE_ERROR);
5504 }
5505 CommandLineSwitchCase(arg, "auth-pkg-list")
5506 {
5507 if (!freerdp_settings_set_string(settings, FreeRDP_AuthenticationPackageList,
5508 arg->Value))
5509 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5510 }
5511 CommandLineSwitchCase(arg, "auto-reconnect")
5512 {
5513 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoReconnectionEnabled, enable))
5514 return fail_at(arg, COMMAND_LINE_ERROR);
5515 }
5516 CommandLineSwitchCase(arg, "auto-reconnect-max-retries")
5517 {
5518 const int rc = parse_command_line_option_uint32(
5519 settings, arg, FreeRDP_AutoReconnectMaxRetries, 0, 1000);
5520 if (rc != 0)
5521 return fail_at(arg, rc);
5522 }
5523 CommandLineSwitchCase(arg, "reconnect-cookie")
5524 {
5525 const int rc = parse_reconnect_cookie_options(settings, arg);
5526 if (rc != 0)
5527 return fail_at(arg, rc);
5528 }
5529 CommandLineSwitchCase(arg, "print-reconnect-cookie")
5530 {
5531 if (!freerdp_settings_set_bool(settings, FreeRDP_PrintReconnectCookie, enable))
5532 return fail_at(arg, COMMAND_LINE_ERROR);
5533 }
5534 CommandLineSwitchCase(arg, "pwidth")
5535 {
5536 const int rc = parse_command_line_option_uint32(
5537 settings, arg, FreeRDP_DesktopPhysicalWidth, 0, UINT32_MAX);
5538 if (rc != 0)
5539 return fail_at(arg, rc);
5540 }
5541 CommandLineSwitchCase(arg, "pheight")
5542 {
5543 const int rc = parse_command_line_option_uint32(
5544 settings, arg, FreeRDP_DesktopPhysicalHeight, 0, UINT32_MAX);
5545 if (rc != 0)
5546 return fail_at(arg, rc);
5547 }
5548 CommandLineSwitchCase(arg, "orientation")
5549 {
5550 LONGLONG val = 0;
5551
5552 if (!value_to_int(arg->Value, &val, 0, UINT16_MAX))
5553 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
5554
5555 if (!freerdp_settings_set_uint16(settings, FreeRDP_DesktopOrientation, (UINT16)val))
5556 return fail_at(arg, COMMAND_LINE_ERROR);
5557 if (!set_monitor_override(settings, FREERDP_MONITOR_OVERRIDE_ORIENTATION))
5558 return fail_at(arg, COMMAND_LINE_ERROR);
5559 }
5560 CommandLineSwitchCase(arg, "old-license")
5561 {
5562 if (!freerdp_settings_set_bool(settings, FreeRDP_OldLicenseBehaviour, TRUE))
5563 return fail_at(arg, COMMAND_LINE_ERROR);
5564 }
5565 CommandLineSwitchCase(arg, "scale")
5566 {
5567 const int rc = parse_scale_options(settings, arg);
5568 if (rc != 0)
5569 return fail_at(arg, rc);
5570 }
5571 CommandLineSwitchCase(arg, "scale-desktop")
5572 {
5573 const int rc = parse_command_line_option_uint32(settings, arg,
5574 FreeRDP_DesktopScaleFactor, 100, 500);
5575 if (rc != 0)
5576 return fail_at(arg, rc);
5577 if (!set_monitor_override(settings, FREERDP_MONITOR_OVERRIDE_DESKTOP_SCALE))
5578 return fail_at(arg, COMMAND_LINE_ERROR);
5579 }
5580 CommandLineSwitchCase(arg, "scale-device")
5581 {
5582 const int rc = parse_scale_device_options(settings, arg);
5583 if (rc != 0)
5584 return fail_at(arg, rc);
5585 }
5586 CommandLineSwitchCase(arg, "action-script")
5587 {
5588 if (!freerdp_settings_set_string(settings, FreeRDP_ActionScript, arg->Value))
5589 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5590 }
5591 CommandLineSwitchCase(arg, RDP2TCP_DVC_CHANNEL_NAME)
5592 {
5593 if (!freerdp_settings_set_string(settings, FreeRDP_RDP2TCPArgs, arg->Value))
5594 return fail_at(arg, COMMAND_LINE_ERROR_MEMORY);
5595 }
5596 CommandLineSwitchCase(arg, "fipsmode")
5597 {
5598 if (!freerdp_settings_set_bool(settings, FreeRDP_FIPSMode, enable))
5599 return fail_at(arg, COMMAND_LINE_ERROR);
5600 }
5601 CommandLineSwitchCase(arg, "smartcard-logon")
5602 {
5603 const int rc = parse_smartcard_logon_options(settings, arg);
5604 if (rc != 0)
5605 return fail_at(arg, rc);
5606 }
5607 CommandLineSwitchCase(arg, "tune")
5608 {
5609 const int rc = parse_tune_options(settings, arg);
5610 if (rc != 0)
5611 return fail_at(arg, rc);
5612 }
5613 CommandLineSwitchDefault(arg)
5614 {
5615#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
5616 const int status = parse_deprecated_command_line(settings, arg);
5617 /* option handled, continue with next */
5618 if (status != -1)
5619 continue;
5620#endif
5621 if (handle_option)
5622 {
5623 const int rc = handle_option(arg, handle_userdata);
5624 if (rc != 0)
5625 return fail_at(arg, rc);
5626 }
5627 }
5628 CommandLineSwitchEnd(arg)
5629 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
5630 return 0;
5631}
5632
5633static void warn_credential_args(const COMMAND_LINE_ARGUMENT_A* args)
5634{
5635 WINPR_ASSERT(args);
5636 bool insecureArgFound = false;
5637 for (size_t x = 0; x < ARRAYSIZE(credential_args); x++)
5638 {
5639 const char* cred = credential_args[x];
5640 const COMMAND_LINE_ARGUMENT_A* arg = CommandLineFindArgumentA(args, cred);
5641 if (!arg)
5642 continue;
5643 if ((arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT) == 0)
5644 continue;
5645
5646 WLog_WARN(TAG, "Using /%s is insecure", arg->Name);
5647 insecureArgFound = true;
5648 }
5649
5650 if (insecureArgFound)
5651 {
5652 WLog_WARN(TAG, "Passing credentials or secrets via command line might expose these in the "
5653 "process list");
5654 WLog_WARN(TAG, "Consider using one of the following (more secure) alternatives:");
5655 WLog_WARN(TAG, " - /args-from: pipe in arguments from stdin, file or file descriptor");
5656 WLog_WARN(TAG, " - /from-stdin pass the credential via stdin");
5657 WLog_WARN(TAG, " - set environment variable FREERDP_ASKPASS to have a gui tool query for "
5658 "credentials");
5659 }
5660}
5661
5662static int freerdp_client_settings_parse_command_line_arguments_int(
5663 rdpSettings* settings, int argc, char* argv[], BOOL allowUnknown,
5664 COMMAND_LINE_ARGUMENT_A* largs, WINPR_ATTR_UNUSED size_t count,
5665 freerdp_command_line_handle_option_t handle_option, void* handle_userdata, bool isArgsFrom)
5666{
5667 char* user = nullptr;
5668 int status = 0;
5669 BOOL ext = FALSE;
5670 BOOL assist = FALSE;
5671 DWORD flags = 0;
5672 BOOL promptForPassword = FALSE;
5673 BOOL compatibility = FALSE;
5674 const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
5675
5676 /* Command line detection fails if only a .rdp or .msrcIncident file
5677 * is supplied. Check this case first, only then try to detect
5678 * legacy command line syntax. */
5679 if (argc > 1)
5680 {
5681 ext = option_is_rdp_file(argv[1]);
5682 assist = option_is_incident_file(argv[1]);
5683 }
5684
5685 if (!ext && !assist)
5686 compatibility = freerdp_client_detect_command_line(argc, argv, &flags);
5687 else
5688 compatibility = freerdp_client_detect_command_line(argc - 1, &argv[1], &flags);
5689
5690 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname, nullptr))
5691 return -1;
5692 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername, nullptr))
5693 return -1;
5694 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword, nullptr))
5695 return -1;
5696
5697 if (compatibility)
5698 {
5699 WLog_WARN(TAG, "Unsupported command line syntax!");
5700 WLog_WARN(TAG, "%s 1.0 style syntax was dropped with version 3!",
5701 freerdp_getApplicationDetailsString());
5702 return -1;
5703 }
5704
5705 if (allowUnknown)
5706 flags |= COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
5707
5708 if (ext)
5709 {
5710 if (freerdp_client_settings_parse_connection_file(settings, argv[1]))
5711 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
5712 }
5713
5714 if (assist)
5715 {
5716 if (freerdp_client_settings_parse_assistance_file(settings, argc, argv) < 0)
5717 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
5718 }
5719
5720 CommandLineClearArgumentsA(largs);
5721 status = CommandLineParseArgumentsA(argc, argv, largs, flags, settings,
5722 freerdp_client_command_line_pre_filter,
5723 freerdp_client_command_line_post_filter);
5724
5725 if (status < 0)
5726 return status;
5727
5728 prepare_default_settings(settings, largs, ext);
5729 if (!isArgsFrom)
5730 warn_credential_args(largs);
5731
5732 arg = largs;
5733 errno = 0;
5734
5735 /* Disable unicode input unless requested. */
5736 if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput, FALSE))
5737 return COMMAND_LINE_ERROR_MEMORY;
5738
5739 status = parse_command_line(settings, arg, handle_option, handle_userdata, &promptForPassword,
5740 &user);
5741
5742 if (user)
5743 {
5744 if (!freerdp_settings_get_string(settings, FreeRDP_Domain) && user)
5745 {
5746 if (!freerdp_settings_set_string(settings, FreeRDP_Username, nullptr))
5747 return COMMAND_LINE_ERROR;
5748
5749 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, nullptr))
5750 return COMMAND_LINE_ERROR;
5751
5752 if (!freerdp_parse_username_settings(user, settings, FreeRDP_Username, FreeRDP_Domain))
5753 return COMMAND_LINE_ERROR;
5754 }
5755 else
5756 {
5757 if (!freerdp_settings_set_string(settings, FreeRDP_Username, user))
5758 return COMMAND_LINE_ERROR;
5759 }
5760 }
5761
5762 if (promptForPassword)
5763 {
5764 freerdp* instance = freerdp_settings_get_pointer_writable(settings, FreeRDP_instance);
5765 if (!freerdp_settings_get_string(settings, FreeRDP_Password))
5766 {
5767 char buffer[512 + 1] = WINPR_C_ARRAY_INIT;
5768
5769 if (!freerdp_passphrase_read(instance->context, "Password: ", buffer,
5770 ARRAYSIZE(buffer) - 1, 1))
5771 return COMMAND_LINE_ERROR;
5772 if (!freerdp_settings_set_string(settings, FreeRDP_Password, buffer))
5773 return COMMAND_LINE_ERROR;
5774 }
5775
5776 if (freerdp_settings_get_bool(settings, FreeRDP_GatewayEnabled) &&
5777 !freerdp_settings_get_bool(settings, FreeRDP_GatewayUseSameCredentials))
5778 {
5779 if (!freerdp_settings_get_string(settings, FreeRDP_GatewayPassword))
5780 {
5781 char buffer[512 + 1] = WINPR_C_ARRAY_INIT;
5782
5783 if (!freerdp_passphrase_read(instance->context, "Gateway Password: ", buffer,
5784 ARRAYSIZE(buffer) - 1, 1))
5785 return COMMAND_LINE_ERROR;
5786 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayPassword, buffer))
5787 return COMMAND_LINE_ERROR;
5788 }
5789 }
5790 }
5791
5792 freerdp_performance_flags_make(settings);
5793
5794 if (freerdp_settings_get_bool(settings, FreeRDP_RemoteFxCodec) ||
5795 freerdp_settings_get_bool(settings, FreeRDP_NSCodec) ||
5796 freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
5797 {
5798 if (!freerdp_settings_set_bool(settings, FreeRDP_FastPathOutput, TRUE))
5799 return COMMAND_LINE_ERROR;
5800 if (!freerdp_settings_set_bool(settings, FreeRDP_FrameMarkerCommandEnabled, TRUE))
5801 return COMMAND_LINE_ERROR;
5802 }
5803
5804 arg = CommandLineFindArgumentA(largs, "port");
5805 if (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT)
5806 {
5807 const int rc =
5808 parse_command_line_option_uint32(settings, arg, FreeRDP_ServerPort, 0, UINT16_MAX);
5809 if (rc != 0)
5810 return fail_at(arg, rc);
5811 }
5812
5813 if (freerdp_settings_get_bool(settings, FreeRDP_VmConnectMode))
5814 {
5815 const COMMAND_LINE_ARGUMENT_A* nego = CommandLineFindArgumentA(largs, "nego");
5816 if (nego && (nego->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
5817 return fail_at(arg, COMMAND_LINE_ERROR);
5818
5819 const UINT32 port = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
5820 WLog_INFO(TAG, "/vmconnect uses custom port %" PRIu32, port);
5821 }
5822
5823 fill_credential_strings(largs);
5824
5825 return status;
5826}
5827
5828static void argv_free(int* pargc, char** pargv[])
5829{
5830 WINPR_ASSERT(pargc);
5831 WINPR_ASSERT(pargv);
5832 const int argc = *pargc;
5833 char** argv = *pargv;
5834 *pargc = 0;
5835 *pargv = nullptr;
5836
5837 if (!argv)
5838 return;
5839 for (int x = 0; x < argc; x++)
5840 free(argv[x]);
5841 free((void*)argv);
5842}
5843
5844static BOOL argv_append(int* pargc, char** pargv[], char* what)
5845{
5846 WINPR_ASSERT(pargc);
5847 WINPR_ASSERT(pargv);
5848
5849 if (*pargc < 0)
5850 return FALSE;
5851
5852 if (!what)
5853 return FALSE;
5854
5855 int nargc = *pargc + 1;
5856 char** tmp = (char**)realloc((void*)*pargv, (size_t)nargc * sizeof(char*));
5857 if (!tmp)
5858 return FALSE;
5859
5860 tmp[*pargc] = what;
5861 *pargv = tmp;
5862 *pargc = nargc;
5863 return TRUE;
5864}
5865
5866static BOOL argv_append_dup(int* pargc, char** pargv[], const char* what)
5867{
5868 char* copy = nullptr;
5869 if (what)
5870 copy = _strdup(what);
5871
5872 const BOOL rc = argv_append(pargc, pargv, copy);
5873 if (!rc)
5874 free(copy);
5875 return rc;
5876}
5877
5878static BOOL args_from_fp(FILE* fp, int* aargc, char** aargv[], const char* file, const char* cmd)
5879{
5880 BOOL success = FALSE;
5881
5882 WINPR_ASSERT(aargc);
5883 WINPR_ASSERT(aargv);
5884 WINPR_ASSERT(cmd);
5885
5886 if (!fp)
5887 {
5888 WLog_ERR(TAG, "Failed to read command line options from file '%s'", file);
5889 return FALSE;
5890 }
5891 if (!argv_append_dup(aargc, aargv, cmd))
5892 goto fail;
5893 while (!feof(fp))
5894 {
5895 char* line = nullptr;
5896 size_t size = 0;
5897 INT64 rc = GetLine(&line, &size, fp);
5898 if ((rc < 0) || !line)
5899 {
5900 /* abort if GetLine failed due to reaching EOF */
5901 if (feof(fp))
5902 break;
5903 goto fail;
5904 }
5905
5906 while (rc > 0)
5907 {
5908 const char cur = (line[rc - 1]);
5909 if ((cur == '\n') || (cur == '\r'))
5910 {
5911 line[rc - 1] = '\0';
5912 rc--;
5913 }
5914 else
5915 break;
5916 }
5917 /* abort on empty lines */
5918 if (rc == 0)
5919 {
5920 free(line);
5921 break;
5922 }
5923 if (!argv_append(aargc, aargv, line))
5924 {
5925 free(line);
5926 goto fail;
5927 }
5928 }
5929
5930 success = TRUE;
5931fail:
5932 fclose(fp);
5933 if (!success)
5934 argv_free(aargc, aargv);
5935 return success;
5936}
5937
5938static BOOL args_from_env(const char* name, int* aargc, char** aargv[], const char* arg,
5939 const char* cmd)
5940{
5941 BOOL success = FALSE;
5942 char* env = nullptr;
5943
5944 WINPR_ASSERT(aargc);
5945 WINPR_ASSERT(aargv);
5946 WINPR_ASSERT(cmd);
5947
5948 if (!name)
5949 {
5950 WLog_ERR(TAG, "%s - environment variable name empty", arg);
5951 goto cleanup;
5952 }
5953
5954 {
5955 const DWORD size = GetEnvironmentVariableX(name, env, 0);
5956 if (size == 0)
5957 {
5958 WLog_ERR(TAG, "%s - no environment variable '%s'", arg, name);
5959 goto cleanup;
5960 }
5961 env = calloc(size + 1, sizeof(char));
5962 if (!env)
5963 goto cleanup;
5964
5965 {
5966 const DWORD rc = GetEnvironmentVariableX(name, env, size);
5967 if (rc != size - 1)
5968 goto cleanup;
5969 if (rc == 0)
5970 {
5971 WLog_ERR(TAG, "environment variable '%s' is empty", arg);
5972 goto cleanup;
5973 }
5974 }
5975 }
5976
5977 if (!argv_append_dup(aargc, aargv, cmd))
5978 goto cleanup;
5979
5980 {
5981 char* context = nullptr;
5982 char* tok = strtok_s(env, "\n", &context);
5983 while (tok)
5984 {
5985 if (!argv_append_dup(aargc, aargv, tok))
5986 goto cleanup;
5987 tok = strtok_s(nullptr, "\n", &context);
5988 }
5989 }
5990
5991 success = TRUE;
5992cleanup:
5993 free(env);
5994 if (!success)
5995 argv_free(aargc, aargv);
5996 return success;
5997}
5998
5999int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, int oargc,
6000 char* oargv[], BOOL allowUnknown)
6001{
6002 return freerdp_client_settings_parse_command_line_arguments_ex(
6003 settings, oargc, oargv, allowUnknown, nullptr, 0, nullptr, nullptr);
6004}
6005
6006int freerdp_client_settings_parse_command_line_arguments_ex(
6007 rdpSettings* settings, int oargc, char** oargv, BOOL allowUnknown,
6008 COMMAND_LINE_ARGUMENT_A* args, size_t count, freerdp_command_line_handle_option_t handle_option,
6009 void* handle_userdata)
6010{
6011 int argc = oargc;
6012 char** argv = oargv;
6013 int res = -1;
6014 int aargc = 0;
6015 char** aargv = nullptr;
6016
6017 bool isArgsFrom = false;
6018 if ((argc == 2) && option_starts_with("/args-from:", argv[1]))
6019 {
6020 isArgsFrom = true;
6021 BOOL success = FALSE;
6022 const char* file = strchr(argv[1], ':') + 1;
6023 FILE* fp = stdin;
6024
6025 if (option_starts_with("fd:", file))
6026 {
6027 ULONGLONG result = 0;
6028 const char* val = strchr(file, ':') + 1;
6029 if (!value_to_uint(val, &result, 0, INT_MAX))
6030 return -1;
6031 fp = fdopen((int)result, "r");
6032 success = args_from_fp(fp, &aargc, &aargv, file, oargv[0]);
6033 }
6034 else if (strncmp(file, "env:", 4) == 0)
6035 {
6036 const char* name = strchr(file, ':') + 1;
6037 success = args_from_env(name, &aargc, &aargv, oargv[1], oargv[0]);
6038 }
6039 else if (strcmp(file, "stdin") != 0)
6040 {
6041 fp = winpr_fopen(file, "r");
6042 success = args_from_fp(fp, &aargc, &aargv, file, oargv[0]);
6043 }
6044 else
6045 success = args_from_fp(fp, &aargc, &aargv, file, oargv[0]);
6046
6047 if (!success)
6048 return -1;
6049 argc = aargc;
6050 argv = aargv;
6051 }
6052
6053 WINPR_ASSERT(count <= SSIZE_MAX);
6054 size_t lcount = 0;
6055 COMMAND_LINE_ARGUMENT_A* largs = create_merged_args(args, (SSIZE_T)count, &lcount);
6056 if (!largs)
6057 goto fail;
6058
6059 res = freerdp_client_settings_parse_command_line_arguments_int(
6060 settings, argc, argv, allowUnknown, largs, lcount, handle_option, handle_userdata,
6061 isArgsFrom);
6062fail:
6063 free(largs);
6064 argv_free(&aargc, &aargv);
6065 return res;
6066}
6067
6068static BOOL freerdp_client_load_static_channel_addin(rdpChannels* channels, rdpSettings* settings,
6069 const char* name, void* data)
6070{
6071 PVIRTUALCHANNELENTRY entry = nullptr;
6072 PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(
6073 name, nullptr, nullptr, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
6074 PVIRTUALCHANNELENTRYEX pvceex = WINPR_FUNC_PTR_CAST(pvce, PVIRTUALCHANNELENTRYEX);
6075
6076 if (!pvceex)
6077 entry =
6078 freerdp_load_channel_addin_entry(name, nullptr, nullptr, FREERDP_ADDIN_CHANNEL_STATIC);
6079
6080 if (pvceex)
6081 {
6082 if (freerdp_channels_client_load_ex(channels, settings, pvceex, data) == 0)
6083 {
6084 WLog_DBG(TAG, "loading channelEx %s", name);
6085 return TRUE;
6086 }
6087 }
6088 else if (entry)
6089 {
6090 if (freerdp_channels_client_load(channels, settings, entry, data) == 0)
6091 {
6092 WLog_DBG(TAG, "loading channel %s", name);
6093 return TRUE;
6094 }
6095 }
6096
6097 return FALSE;
6098}
6099
6100typedef struct
6101{
6102 FreeRDP_Settings_Keys_Bool settingId;
6103 const char* channelName;
6104 void* args;
6105} ChannelToLoad;
6106
6107BOOL freerdp_client_load_addins(rdpChannels* channels, rdpSettings* settings)
6108{
6109 ChannelToLoad dynChannels[] = {
6110#if defined(CHANNEL_AINPUT_CLIENT)
6111 { FreeRDP_BOOL_UNUSED, AINPUT_CHANNEL_NAME, nullptr }, /* always loaded */
6112#endif
6113#ifdef CHANNEL_AUDIN_CLIENT
6114 { FreeRDP_AudioCapture, AUDIN_CHANNEL_NAME, nullptr },
6115#endif
6116#ifdef CHANNEL_RDPSND_CLIENT
6117 { FreeRDP_AudioPlayback, RDPSND_CHANNEL_NAME, nullptr },
6118#endif
6119#ifdef CHANNEL_RDPEI_CLIENT
6120 { FreeRDP_MultiTouchInput, RDPEI_CHANNEL_NAME, nullptr },
6121#endif
6122#ifdef CHANNEL_RDPGFX_CLIENT
6123 { FreeRDP_SupportGraphicsPipeline, RDPGFX_CHANNEL_NAME, nullptr },
6124#endif
6125#ifdef CHANNEL_ECHO_CLIENT
6126 { FreeRDP_SupportEchoChannel, ECHO_CHANNEL_NAME, nullptr },
6127#endif
6128#ifdef CHANNEL_SSHAGENT_CLIENT
6129 { FreeRDP_SupportSSHAgentChannel, "sshagent", nullptr },
6130#endif
6131#ifdef CHANNEL_DISP_CLIENT
6132 { FreeRDP_SupportDisplayControl, DISP_CHANNEL_NAME, nullptr },
6133#endif
6134#ifdef CHANNEL_GEOMETRY_CLIENT
6135 { FreeRDP_SupportGeometryTracking, GEOMETRY_CHANNEL_NAME, nullptr },
6136#endif
6137#ifdef CHANNEL_VIDEO_CLIENT
6138 { FreeRDP_SupportVideoOptimized, VIDEO_CHANNEL_NAME, nullptr },
6139#endif
6140#ifdef CHANNEL_RDPEAR_CLIENT
6141 { FreeRDP_RemoteCredentialGuard, RDPEAR_CHANNEL_NAME, nullptr },
6142#endif
6143#ifdef CHANNEL_RDPEWA_CLIENT
6144 { FreeRDP_RedirectWebAuthN, RDPEWA_CHANNEL_NAME, nullptr },
6145#endif
6146 };
6147
6148 ChannelToLoad staticChannels[] = {
6149#if defined(CHANNEL_RDPSND_CLIENT)
6150 { FreeRDP_AudioPlayback, RDPSND_CHANNEL_NAME, nullptr },
6151#endif
6152#if defined(CHANNEL_CLIPRDR_CLIENT)
6153 { FreeRDP_RedirectClipboard, CLIPRDR_SVC_CHANNEL_NAME, nullptr },
6154#endif
6155#if defined(CHANNEL_ENCOMSP_CLIENT)
6156 { FreeRDP_EncomspVirtualChannel, ENCOMSP_SVC_CHANNEL_NAME, settings },
6157#endif
6158#if defined(CHANNEL_REMDESK_CLIENT)
6159 { FreeRDP_RemdeskVirtualChannel, REMDESK_SVC_CHANNEL_NAME, settings },
6160#endif
6161#if defined(CHANNEL_RAIL_CLIENT)
6162 { FreeRDP_RemoteApplicationMode, RAIL_SVC_CHANNEL_NAME, settings }
6163#endif
6164 };
6165
6169 for (size_t i = 0; i < ARRAYSIZE(dynChannels); i++)
6170 {
6171 if ((dynChannels[i].settingId == FreeRDP_BOOL_UNUSED) ||
6172 freerdp_settings_get_bool(settings, dynChannels[i].settingId))
6173 {
6174 const char* const p[] = { dynChannels[i].channelName };
6175
6176 if (!freerdp_client_add_dynamic_channel(settings, ARRAYSIZE(p), p))
6177 return FALSE;
6178 }
6179 }
6180
6184 if ((freerdp_static_channel_collection_find(settings, RDPSND_CHANNEL_NAME)) ||
6185 (freerdp_dynamic_channel_collection_find(settings, RDPSND_CHANNEL_NAME))
6186#if defined(CHANNEL_TSMF_CLIENT)
6187 || (freerdp_dynamic_channel_collection_find(settings, "tsmf"))
6188#endif
6189 )
6190 {
6191 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
6192 return FALSE; /* rdpsnd requires rdpdr to be registered */
6193 if (!freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, TRUE))
6194 return FALSE; /* Both rdpsnd and tsmf require this flag to be set */
6195 }
6196
6197 if (freerdp_dynamic_channel_collection_find(settings, AUDIN_CHANNEL_NAME))
6198 {
6199 if (!freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, TRUE))
6200 return FALSE;
6201 }
6202
6203 if (freerdp_settings_get_bool(settings, FreeRDP_NetworkAutoDetect) ||
6204 freerdp_settings_get_bool(settings, FreeRDP_SupportHeartbeatPdu) ||
6205 freerdp_settings_get_bool(settings, FreeRDP_SupportMultitransport))
6206 {
6207 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
6208 return FALSE; /* these RDP8 features require rdpdr to be registered */
6209 }
6210
6211 const char* DrivesToRedirect = freerdp_settings_get_string(settings, FreeRDP_DrivesToRedirect);
6212
6213 if (DrivesToRedirect && (strlen(DrivesToRedirect) != 0))
6214 {
6215 /*
6216 * Drives to redirect:
6217 *
6218 * Very similar to DevicesToRedirect, but can contain a
6219 * comma-separated list of drive letters to redirect.
6220 */
6221 char* value = nullptr;
6222 char* tok = nullptr;
6223 char* context = nullptr;
6224
6225 value = _strdup(DrivesToRedirect);
6226 if (!value)
6227 return FALSE;
6228
6229 tok = strtok_s(value, ";", &context);
6230 if (!tok)
6231 {
6232 WLog_ERR(TAG, "DrivesToRedirect contains invalid data: '%s'", DrivesToRedirect);
6233 free(value);
6234 return FALSE;
6235 }
6236
6237 while (tok)
6238 {
6239 /* Syntax: Comma separated list of the following entries:
6240 * '*' ... Redirect all drives, including hotplug
6241 * 'DynamicDrives' ... hotplug
6242 * '%' ... user home directory
6243 * <label>(<path>) ... One or more paths to redirect.
6244 * <path>(<label>) ... One or more paths to redirect.
6245 * <path> ... One or more paths to redirect.
6246 */
6247 /* TODO: Need to properly escape labels and paths */
6248 BOOL success = 0;
6249 const char* name = nullptr;
6250 const char* drive = tok;
6251 char* subcontext = nullptr;
6252 char* start = strtok_s(tok, "(", &subcontext);
6253 char* end = strtok_s(nullptr, ")", &subcontext);
6254 if (start && end)
6255 name = end;
6256
6257 if (freerdp_path_valid(name, nullptr) && freerdp_path_valid(drive, nullptr))
6258 {
6259 success = freerdp_client_add_drive(settings, name, nullptr);
6260 if (success)
6261 success = freerdp_client_add_drive(settings, drive, nullptr);
6262 }
6263 else
6264 success = freerdp_client_add_drive(settings, drive, name);
6265
6266 if (!success)
6267 {
6268 free(value);
6269 return FALSE;
6270 }
6271
6272 tok = strtok_s(nullptr, ";", &context);
6273 }
6274 free(value);
6275
6276 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
6277 return FALSE;
6278 }
6279 else if (freerdp_settings_get_bool(settings, FreeRDP_RedirectDrives))
6280 {
6281 if (!freerdp_device_collection_find(settings, "drive"))
6282 {
6283 const char* const params[] = { "drive", "media", "*" };
6284
6285 if (!freerdp_client_add_device_channel(settings, ARRAYSIZE(params), params))
6286 return FALSE;
6287 }
6288 }
6289
6290 if (freerdp_settings_get_bool(settings, FreeRDP_RedirectDrives) ||
6291 freerdp_settings_get_bool(settings, FreeRDP_RedirectHomeDrive) ||
6292 freerdp_settings_get_bool(settings, FreeRDP_RedirectSerialPorts) ||
6293 freerdp_settings_get_bool(settings, FreeRDP_RedirectSmartCards) ||
6294 freerdp_settings_get_bool(settings, FreeRDP_RedirectPrinters))
6295 {
6296 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
6297 return FALSE; /* All of these features require rdpdr */
6298 }
6299
6300 if (freerdp_settings_get_bool(settings, FreeRDP_RedirectHomeDrive))
6301 {
6302 if (!freerdp_device_collection_find(settings, "drive"))
6303 {
6304 const char* params[] = { "drive", "home", "%" };
6305
6306 if (!freerdp_client_add_device_channel(settings, ARRAYSIZE(params), params))
6307 return FALSE;
6308 }
6309 }
6310
6311 if (freerdp_settings_get_bool(settings, FreeRDP_DeviceRedirection))
6312 {
6313 if (!freerdp_client_load_static_channel_addin(channels, settings, RDPDR_SVC_CHANNEL_NAME,
6314 settings))
6315 return FALSE;
6316
6317 if (!freerdp_static_channel_collection_find(settings, RDPSND_CHANNEL_NAME) &&
6318 !freerdp_dynamic_channel_collection_find(settings, RDPSND_CHANNEL_NAME))
6319 {
6320 const char* const params[] = { RDPSND_CHANNEL_NAME, "sys:fake" };
6321
6322 if (!freerdp_client_add_static_channel(settings, ARRAYSIZE(params), params))
6323 return FALSE;
6324
6325 if (!freerdp_client_add_dynamic_channel(settings, ARRAYSIZE(params), params))
6326 return FALSE;
6327 }
6328 }
6329
6330 if (freerdp_settings_get_bool(settings, FreeRDP_RedirectSmartCards))
6331 {
6332 if (!freerdp_device_collection_find_type(settings, RDPDR_DTYP_SMARTCARD))
6333 {
6334 RDPDR_DEVICE* smartcard = freerdp_device_new(RDPDR_DTYP_SMARTCARD, 0, nullptr);
6335
6336 if (!smartcard)
6337 return FALSE;
6338
6339 if (!freerdp_device_collection_add(settings, smartcard))
6340 {
6341 freerdp_device_free(smartcard);
6342 return FALSE;
6343 }
6344 }
6345 }
6346
6347 if (freerdp_settings_get_bool(settings, FreeRDP_RedirectPrinters))
6348 {
6349 if (!freerdp_device_collection_find_type(settings, RDPDR_DTYP_PRINT))
6350 {
6351 RDPDR_DEVICE* printer = freerdp_device_new(RDPDR_DTYP_PRINT, 0, nullptr);
6352
6353 if (!printer)
6354 return FALSE;
6355
6356 if (!freerdp_device_collection_add(settings, printer))
6357 {
6358 freerdp_device_free(printer);
6359 return FALSE;
6360 }
6361 }
6362 }
6363
6364 if (freerdp_settings_get_bool(settings, FreeRDP_LyncRdpMode))
6365 {
6366 if (!freerdp_settings_set_bool(settings, FreeRDP_EncomspVirtualChannel, TRUE))
6367 return FALSE;
6368 if (!freerdp_settings_set_bool(settings, FreeRDP_RemdeskVirtualChannel, TRUE))
6369 return FALSE;
6370 if (!freerdp_settings_set_bool(settings, FreeRDP_CompressionEnabled, FALSE))
6371 return FALSE;
6372 }
6373
6374 if (freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceMode))
6375 {
6376 if (!freerdp_settings_set_bool(settings, FreeRDP_EncomspVirtualChannel, TRUE))
6377 return FALSE;
6378 if (!freerdp_settings_set_bool(settings, FreeRDP_RemdeskVirtualChannel, TRUE))
6379 return FALSE;
6380 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
6381 return FALSE;
6382 }
6383
6384 /* step 3: schedule some static channels to load depending on the settings */
6385 for (size_t i = 0; i < ARRAYSIZE(staticChannels); i++)
6386 {
6387 if ((staticChannels[i].settingId == 0) ||
6388 freerdp_settings_get_bool(settings, staticChannels[i].settingId))
6389 {
6390 if (staticChannels[i].args)
6391 {
6392 if (!freerdp_client_load_static_channel_addin(
6393 channels, settings, staticChannels[i].channelName, staticChannels[i].args))
6394 return FALSE;
6395 }
6396 else
6397 {
6398 const char* const p[] = { staticChannels[i].channelName };
6399 if (!freerdp_client_add_static_channel(settings, ARRAYSIZE(p), p))
6400 return FALSE;
6401 }
6402 }
6403 }
6404
6405 {
6406 char* RDP2TCPArgs = freerdp_settings_get_string_writable(settings, FreeRDP_RDP2TCPArgs);
6407 if (RDP2TCPArgs)
6408 {
6409 const char* const p[] = { RDP2TCP_DVC_CHANNEL_NAME, RDP2TCPArgs };
6410 if (!freerdp_client_add_static_channel(settings, ARRAYSIZE(p), p))
6411 return FALSE;
6412 }
6413 }
6414
6415 /* step 4: do the static channels loading and init */
6416 for (UINT32 i = 0; i < freerdp_settings_get_uint32(settings, FreeRDP_StaticChannelCount); i++)
6417 {
6418 ADDIN_ARGV* _args =
6419 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_StaticChannelArray, i);
6420
6421 if (!freerdp_client_load_static_channel_addin(channels, settings, _args->argv[0], _args))
6422 return FALSE;
6423 }
6424
6425 if (freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelCount) > 0)
6426 {
6427 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDynamicChannels, TRUE))
6428 return FALSE;
6429 }
6430
6431 if (freerdp_settings_get_bool(settings, FreeRDP_SupportDynamicChannels))
6432 {
6433 if (!freerdp_client_load_static_channel_addin(channels, settings, DRDYNVC_SVC_CHANNEL_NAME,
6434 settings))
6435 return FALSE;
6436 }
6437
6438 return TRUE;
6439}
6440
6441void freerdp_client_warn_unmaintained(int argc, char* argv[])
6442{
6443 const char* app = (argc > 0) ? argv[0] : "INVALID_ARGV";
6444 const DWORD log_level = WLOG_WARN;
6445 wLog* log = WLog_Get(TAG);
6446 WINPR_ASSERT(log);
6447
6448 if (!WLog_IsLevelActive(log, log_level))
6449 return;
6450
6451 WLog_Print_unchecked(log, log_level, "[unmaintained] %s client is currently unmaintained!",
6452 app);
6453 WLog_Print_unchecked(
6454 log, log_level,
6455 " If problems occur please check https://github.com/FreeRDP/FreeRDP/issues for "
6456 "known issues!");
6457 WLog_Print_unchecked(
6458 log, log_level,
6459 "Be prepared to fix issues yourself though as nobody is actively working on this.");
6460 WLog_Print_unchecked(
6461 log, log_level,
6462 " Developers hang out in https://matrix.to/#/#FreeRDP:matrix.org?via=matrix.org "
6463 "- don't hesitate to ask some questions. (replies might take some time depending "
6464 "on your timezone) - if you intend using this component write us a message");
6465}
6466
6467void freerdp_client_warn_experimental(int argc, char* argv[])
6468{
6469 const char* app = (argc > 0) ? argv[0] : "INVALID_ARGV";
6470 const DWORD log_level = WLOG_WARN;
6471 wLog* log = WLog_Get(TAG);
6472 WINPR_ASSERT(log);
6473
6474 if (!WLog_IsLevelActive(log, log_level))
6475 return;
6476
6477 WLog_Print_unchecked(log, log_level, "[experimental] %s client is currently experimental!",
6478 app);
6479 WLog_Print_unchecked(
6480 log, log_level,
6481 " If problems occur please check https://github.com/FreeRDP/FreeRDP/issues for "
6482 "known issues or create a new one!");
6483 WLog_Print_unchecked(
6484 log, log_level,
6485 " Developers hang out in https://matrix.to/#/#FreeRDP:matrix.org?via=matrix.org "
6486 "- don't hesitate to ask some questions. (replies might take some time depending "
6487 "on your timezone)");
6488}
6489
6490void freerdp_client_warn_deprecated(int argc, char* argv[])
6491{
6492 const char* app = (argc > 0) ? argv[0] : "INVALID_ARGV";
6493 const DWORD log_level = WLOG_WARN;
6494 wLog* log = WLog_Get(TAG);
6495 WINPR_ASSERT(log);
6496
6497 if (!WLog_IsLevelActive(log, log_level))
6498 return;
6499
6500 WLog_Print_unchecked(log, log_level, "[deprecated] %s client has been deprecated", app);
6501 WLog_Print_unchecked(log, log_level, "As replacement there is a SDL3 based client available.");
6502 WLog_Print_unchecked(
6503 log, log_level,
6504 "If you are interested in keeping %s alive get in touch with the developers", app);
6505 WLog_Print_unchecked(
6506 log, log_level,
6507 "The project is hosted at https://github.com/freerdp/freerdp and "
6508 " developers hang out in https://matrix.to/#/#FreeRDP:matrix.org?via=matrix.org "
6509 "- don't hesitate to ask some questions. (replies might take some time depending "
6510 "on your timezone)");
6511}
WINPR_ATTR_NODISCARD FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API char * freerdp_settings_get_string_writable(rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a string settings value.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string_len(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val, size_t len)
Sets a string settings value. The val is copied.
WINPR_ATTR_NODISCARD FREERDP_API INT32 freerdp_settings_get_int32(const rdpSettings *settings, FreeRDP_Settings_Keys_Int32 id)
Returns a INT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API void * freerdp_settings_get_pointer_writable(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a mutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT64 freerdp_settings_get_uint64(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id)
Returns a UINT64 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint64(rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id, UINT64 val)
Sets a UINT64 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_append_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *separator, const char *param)
appends a string to a settings value. The param is copied. If the initial value of the setting was no...
WINPR_ATTR_NODISCARD FREERDP_API INT16 freerdp_settings_get_int16(const rdpSettings *settings, FreeRDP_Settings_Keys_Int16 id)
Returns a INT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API INT64 freerdp_settings_get_int64(const rdpSettings *settings, FreeRDP_Settings_Keys_Int64 id)
Returns a INT64 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint16(rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id, UINT16 val)
Sets a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_set_gateway_usage_method(rdpSettings *settings, UINT32 GatewayUsageMethod)
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_name_for_key(SSIZE_T key)
Returns the type name for a key.
WINPR_ATTR_NODISCARD FREERDP_API SSIZE_T freerdp_settings_get_type_for_key(SSIZE_T key)
Get a key type for the key index.