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