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