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