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