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