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