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