Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,127 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP Client Common
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2025 Siemens
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "freerdp-client")
set(MODULE_PREFIX "FREERDP_CLIENT")
set(SRCS
client.c
cmdline.c
cmdline.h
file.c
client_cliprdr_file.c
geometry.c
smartcard_cli.c
)
foreach(FREERDP_CHANNELS_CLIENT_SRC ${FREERDP_CHANNELS_CLIENT_SRCS})
get_filename_component(NINC ${FREERDP_CHANNELS_CLIENT_SRC} PATH)
include_directories(${NINC})
list(APPEND SRCS "${FREERDP_CHANNELS_CLIENT_SRC}")
endforeach()
if(UNIX)
option(WITH_SSO_MIB "Build with sso-mib support" OFF)
else()
set(WITH_SSO_MIB OFF CACHE INTERNAL "unsupported platform")
endif()
if(WITH_SSO_MIB)
find_package(SSO_MIB REQUIRED)
if(SSO_MIB_INSTALL_LIBRARIES)
install(FILES ${SSO_MIB_INSTALL_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
endif()
if(NOT APPLE AND NOT WIN32 AND NOT ANDROID)
set(OPT_FUSE_DEFAULT ON)
else()
set(OPT_FUSE_DEFAULT OFF)
endif()
option(WITH_FUSE "Build clipboard with FUSE file copy support" ${OPT_FUSE_DEFAULT})
if(WITH_FUSE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(FUSE3 REQUIRED fuse3)
freerdp_client_pc_add_requires_private("fuse3")
include_directories(SYSTEM ${FUSE3_INCLUDE_DIRS})
add_compile_definitions(WITH_FUSE)
list(APPEND LIBS ${FUSE3_LIBRARIES})
add_compile_definitions(_FILE_OFFSET_BITS=64)
endif()
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
if(WITH_SSO_MIB)
list(APPEND SRCS sso_mib_tokens.c)
endif()
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" SRCS)
if(WITH_SSO_MIB)
if(SSO_MIB_EXTERNAL_DIR)
add_dependencies(${MODULE_NAME} sso-mib-external)
endif()
include_directories(${SSO_MIB_INCLUDE_DIRS})
add_compile_definitions(WITH_SSO_MIB)
list(APPEND LIBS ${SSO_MIB_LIBRARIES})
endif()
list(APPEND LIBS freerdp winpr)
include(CheckLibraryExists)
check_library_exists(m lround "" HAVE_LIB_M)
if(HAVE_LIB_M)
list(APPEND LIBS m)
endif()
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
target_link_libraries(${MODULE_NAME} PRIVATE ${FREERDP_CHANNELS_CLIENT_LIBS})
target_link_libraries(${MODULE_NAME} PUBLIC ${LIBS})
installwithrpath(
TARGETS
${MODULE_NAME}
COMPONENT
libraries
EXPORT
FreeRDP-ClientTargets
ARCHIVE
DESTINATION
${CMAKE_INSTALL_LIBDIR}
LIBRARY
DESTINATION
${CMAKE_INSTALL_LIBDIR}
RUNTIME
DESTINATION
${CMAKE_INSTALL_BINDIR}
)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Common")
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()
if(WITH_MANPAGES)
add_subdirectory(man)
endif()

2598
third_party/FreeRDP/client/common/client.c vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,556 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Client Command-Line Interface
*
* Copyright 2018 Bernhard Miklautz <bernhard.miklautz@thincast.com>
* Copyright 2018 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CLIENT_COMMON_CMDLINE_H
#define CLIENT_COMMON_CMDLINE_H
#include <freerdp/config.h>
#include <winpr/cmdline.h>
static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
{ "a", COMMAND_LINE_VALUE_REQUIRED, "<addin>[,<options>]", nullptr, nullptr, -1, "addin",
"Addin" },
{ "azure", COMMAND_LINE_VALUE_REQUIRED,
"[tenantid:<id>],[use-tenantid[:[on|off]],[ad:<url>]"
"[avd-access:<format string>],[avd-token:<format string>],[avd-scope:<format string>]",
nullptr, nullptr, -1, nullptr, "AzureAD options" },
{ "action-script", COMMAND_LINE_VALUE_REQUIRED, "<file-name>", "~/.config/freerdp/action.sh",
nullptr, -1, nullptr, "Action script" },
{ "admin", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "console",
"Admin (or console) session" },
{ "aero", COMMAND_LINE_VALUE_BOOL, nullptr, nullptr, BoolValueFalse, -1, nullptr,
"desktop composition" },
{ "app", COMMAND_LINE_VALUE_REQUIRED,
"program:[<path>|<||alias>],cmd:<command>,file:<filename>,guid:<guid>,icon:<filename>,name:<"
"name>,workdir:<directory>,hidef:[on|off]",
nullptr, nullptr, -1, nullptr, "Remote application program" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "app-cmd", COMMAND_LINE_VALUE_REQUIRED, "<parameters>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /app:cmd:<command>] Remote application command-line parameters" },
{ "app-file", COMMAND_LINE_VALUE_REQUIRED, "<file-name>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /app:file:<filename>] File to open with remote application" },
{ "app-guid", COMMAND_LINE_VALUE_REQUIRED, "<app-guid>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /app:guid:<guid>] Remote application GUID" },
{ "app-icon", COMMAND_LINE_VALUE_REQUIRED, "<icon-path>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /app:icon:<filename>] Remote application icon for user interface" },
{ "app-name", COMMAND_LINE_VALUE_REQUIRED, "<app-name>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /app:name:<name>] Remote application name for user interface" },
{ "app-workdir", COMMAND_LINE_VALUE_REQUIRED, "<workspace path>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /app:workdir:<directory>] Remote application workspace path" },
#endif
{ "assistance", COMMAND_LINE_VALUE_REQUIRED, "<password>", nullptr, nullptr, -1, nullptr,
"Remote assistance password" },
{ "auto-request-control", COMMAND_LINE_VALUE_FLAG, "", nullptr, nullptr, -1, nullptr,
"Automatically request remote assistance input control" },
{ "async-channels", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Asynchronous channels (experimental)" },
{ "async-update", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Asynchronous update" },
{ "audio-mode", COMMAND_LINE_VALUE_REQUIRED, "<mode>", nullptr, nullptr, -1, nullptr,
"Audio output mode" },
{ "auth-only", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Authenticate only" },
{ "auth-pkg-list", COMMAND_LINE_VALUE_REQUIRED, "[[none],]<!ntlm,kerberos,!u2u>", nullptr,
nullptr, -1, nullptr,
"Authentication package filter (comma-separated list, use '!' to disable). By default "
"all methods are enabled. Use explicit 'none' as first argument to disable all methods, "
"selectively enabling only the ones following." },
{ "authentication", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Authentication (experimental)" },
{ "auto-reconnect", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Automatic reconnection" },
{ "auto-reconnect-max-retries", COMMAND_LINE_VALUE_REQUIRED, "<retries>", nullptr, nullptr, -1,
nullptr, "Automatic reconnection maximum retries, 0 for unlimited [0,1000]" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "bitmap-cache", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /cache:bitmap[:on|off]] bitmap cache" },
{ "persist-cache", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /cache:persist[:on|off]] persistent bitmap cache" },
{ "persist-cache-file", COMMAND_LINE_VALUE_REQUIRED, "<filename>", nullptr, nullptr, -1,
nullptr, "[DEPRECATED, use /cache:persist-file:<filename>] persistent bitmap cache file" },
#endif
{ "bpp", COMMAND_LINE_VALUE_REQUIRED, "<depth>", "16", nullptr, -1, nullptr,
"Session bpp (color depth)" },
{ "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, nullptr, nullptr,
nullptr, -1, nullptr, "Print the build configuration" },
{ "cache", COMMAND_LINE_VALUE_REQUIRED,
"[bitmap[:on|off],codec[:rfx|nsc],glyph[:on|off],offscreen[:on|off],persist,persist-file:<"
"filename>]",
nullptr, nullptr, -1, nullptr, "" },
{ "cert", COMMAND_LINE_VALUE_REQUIRED,
"[deny,ignore,name:<name>,tofu,fingerprint:<hash>:<hash as hex>[,fingerprint:<hash>:<another "
"hash>]]",
nullptr, nullptr, -1, nullptr,
"Certificate accept options. Use with care!\n"
" * deny ... Automatically abort connection if the certificate does not match, no "
"user interaction.\n"
" * ignore ... Ignore the certificate checks altogether (overrules all other options)\n"
" * name ... Use the alternate <name> instead of the certificate subject to match "
"locally stored certificates\n"
" * tofu ... Accept certificate unconditionally on first connect and deny on "
"subsequent connections if the certificate does not match\n"
" * fingerprints ... A list of certificate hashes that are accepted unconditionally for a "
"connection" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "cert-deny", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /cert:deny] Automatically abort connection for any certificate that can "
"not be validated." },
{ "cert-ignore", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /cert:ignore] Ignore certificate" },
{ "cert-name", COMMAND_LINE_VALUE_REQUIRED, "<name>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /cert:name:<name>] Certificate name" },
{ "cert-tofu", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /cert:tofu] Automatically accept certificate on first connect" },
#endif
#ifdef _WIN32
{ "connect-child-session", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, "",
"connect to child session (win32)" },
#endif
{ "client-build-number", COMMAND_LINE_VALUE_REQUIRED, "<number>", nullptr, nullptr, -1, nullptr,
"Client Build Number sent to server (influences smartcard behaviour, see [MS-RDPESC])" },
{ "client-hostname", COMMAND_LINE_VALUE_REQUIRED, "<name>", nullptr, nullptr, -1, nullptr,
"Client Hostname to send to server" },
{ "clipboard", COMMAND_LINE_VALUE_BOOL | COMMAND_LINE_VALUE_OPTIONAL,
"[[use-selection:<atom>],[direction-to:[all|local|remote|off]],[files-to[:all|local|remote|"
"off]]]",
BoolValueTrue, nullptr, -1, nullptr,
"Redirect clipboard:\n"
" * use-selection:<atom> ... (X11) Specify which X selection to access. Default is "
"CLIPBOARD. PRIMARY is the X-style middle-click selection.\n"
" * direction-to:[all|local|remote|off] control enabled clipboard direction\n"
" * files-to:[all|local|remote|off] control enabled file clipboard direction" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "codec-cache", COMMAND_LINE_VALUE_REQUIRED, "[rfx|nsc|jpeg]", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /cache:codec:[rfx|nsc|jpeg]] Bitmap codec cache" },
#endif
{ "compression", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, "z",
"compression" },
{ "compression-level", COMMAND_LINE_VALUE_REQUIRED, "<level>", nullptr, nullptr, -1, nullptr,
"Compression level (0,1,2)" },
{ "credentials-delegation", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1,
nullptr, "credentials delegation" },
{ "d", COMMAND_LINE_VALUE_REQUIRED, "<domain>", nullptr, nullptr, -1, nullptr, "Domain" },
{ "decorations", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Window decorations" },
{ "disp", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr, "Display control" },
{ "drive", COMMAND_LINE_VALUE_REQUIRED, "<name>,<path>", nullptr, nullptr, -1, nullptr,
"Redirect directory <path> as named share <name>. Hotplug support is enabled with "
"/drive:hotplug,*. This argument provides the same function as \"Drives that I plug in "
"later\" option in MSTSC." },
{ "drives", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Redirect all mount points as shares" },
{ "dump", COMMAND_LINE_VALUE_REQUIRED, "<record|replay>,file:<file>[,nodelay]", nullptr,
nullptr, -1, nullptr, "record or replay dump" },
{ "dvc", COMMAND_LINE_VALUE_REQUIRED, "<channel>[,<options>]", nullptr, nullptr, -1, nullptr,
"Dynamic virtual channel" },
{ "dynamic-resolution", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Send resolution updates when the window is resized" },
{ "echo", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "echo", "Echo channel" },
{ "encryption", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Encryption (experimental)" },
{ "encryption-methods", COMMAND_LINE_VALUE_REQUIRED, "[40,][56,][128,][FIPS]", nullptr, nullptr,
-1, nullptr, "RDP standard security encryption methods" },
{ "f", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"Fullscreen mode (<Ctrl>+<Alt>+<Enter> toggles fullscreen)" },
{ "fipsmode", COMMAND_LINE_VALUE_BOOL, nullptr, nullptr, nullptr, -1, nullptr, "FIPS mode" },
{ "floatbar", COMMAND_LINE_VALUE_OPTIONAL,
"sticky:[on|off],default:[visible|hidden],show:[always|fullscreen|window]", nullptr, nullptr,
-1, nullptr,
"floatbar is disabled by default (when enabled defaults to sticky in fullscreen mode)" },
{ "fonts", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"smooth fonts (ClearType)" },
{ "force-console-callbacks", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1,
nullptr, "Use default callbacks (console) for certificate/credential/..." },
{ "frame-ack", COMMAND_LINE_VALUE_REQUIRED, "<number>", nullptr, nullptr, -1, nullptr,
"Number of frame acknowledgement" },
{ "args-from", COMMAND_LINE_VALUE_REQUIRED, "<file>|stdin|fd:<number>|env:<name>", nullptr,
nullptr, -1, nullptr,
"Read command line from a file, stdin or file descriptor. This argument can not be combined "
"with any other. "
"Provide one argument per line." },
{ "from-stdin", COMMAND_LINE_VALUE_OPTIONAL, "force", nullptr, nullptr, -1, nullptr,
"Read credentials from stdin. With <force> the prompt is done before connection, otherwise "
"on server request." },
{ "gateway", COMMAND_LINE_VALUE_REQUIRED,
"g:<gateway>[:<port>],u:<user>,d:<domain>,p:<password>,usage-method:["
"direct|detect],access-token:<"
"token>,type:[rpc|http[,no-websockets][,extauth-sspi-ntlm]|auto[,no-websockets][,extauth-"
"sspi-ntlm]]|arm,url:<wss://url>,bearer:<oauth2-bearer-token>",
nullptr, nullptr, -1, "gw", "Gateway Hostname" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "g", COMMAND_LINE_VALUE_REQUIRED, "<gateway>[:<port>]", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /gateway:g:<url>] Gateway Hostname" },
{ "gateway-usage-method", COMMAND_LINE_VALUE_REQUIRED, "[direct|detect]", nullptr, nullptr, -1,
"gum", "[DEPRECATED, use /gateway:usage-method:<method>] Gateway usage method" },
{ "gd", COMMAND_LINE_VALUE_REQUIRED, "<domain>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /gateway:d:<domain>] Gateway domain" },
#endif
{ "gdi", COMMAND_LINE_VALUE_REQUIRED, "sw|hw", nullptr, nullptr, -1, nullptr, "GDI rendering" },
{ "geometry", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"Geometry tracking channel" },
{ "gestures", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Consume multitouch input locally" },
#ifdef WITH_GFX_H264
{ "gfx", COMMAND_LINE_VALUE_OPTIONAL,
"[[progressive[:on|off]|RFX[:on|off]|AVC420[:on|off]AVC444[:on|off]],mask:<value>,small-"
"cache[:on|off],thin-client[:on|off],progressive[:on|"
"off],frame-ack[:on|off]]",
nullptr, nullptr, -1, nullptr, "RDP8 graphics pipeline" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "gfx-h264", COMMAND_LINE_VALUE_OPTIONAL, "[[AVC420|AVC444],mask:<value>]", nullptr, nullptr,
-1, nullptr, "[DEPRECATED, use /gfx:avc420] RDP8.1 graphics pipeline using H264 codec" },
#endif
#else
{ "gfx", COMMAND_LINE_VALUE_OPTIONAL,
"[progressive[:on|off]|RFX[:on|off]|AVC420[:on|off]AVC444[:on|off]],mask:<value>,small-cache["
":on|off],thin-client[:on|off],progressive[:on|off]]",
nullptr, nullptr, -1, nullptr, "RDP8 graphics pipeline" },
#endif
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "gfx-progressive", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /gfx:progressive] RDP8 graphics pipeline using progressive codec" },
{ "gfx-small-cache", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"[DEPRECATED, use /gfx:small-cache] RDP8 graphics pipeline using small cache mode" },
{ "gfx-thin-client", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /gfx:thin-client] RDP8 graphics pipeline using thin client mode" },
{ "glyph-cache", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /cache:glyph[:on|off]] Glyph cache (experimental)" },
#endif
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "gp", COMMAND_LINE_VALUE_REQUIRED, "<password>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /gateway:p:<password>] Gateway password" },
#endif
{ "grab-keyboard", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Grab keyboard focus, forward all keys to remote" },
{ "grab-mouse", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Grab mouse focus, forward all events to remote" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "gt", COMMAND_LINE_VALUE_REQUIRED,
"[rpc|http[,no-websockets][,extauth-sspi-ntlm]|auto[,no-websockets][,extauth-sspi-ntlm]]",
nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /gateway:type:<type>] Gateway transport type" },
{ "gu", COMMAND_LINE_VALUE_REQUIRED, "[[<domain>\\]<user>|<user>[@<domain>]]", nullptr, nullptr,
-1, nullptr, "[DEPRECATED, use /gateway:u:<user>] Gateway username" },
{ "gat", COMMAND_LINE_VALUE_REQUIRED, "<access token>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /gateway:access-token:<token>] Gateway Access Token" },
#endif
{ "h", COMMAND_LINE_VALUE_REQUIRED, "<height>", "768", nullptr, -1, nullptr, "Height" },
{ "heartbeat", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Support heartbeat PDUs" },
{ "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, nullptr, nullptr, nullptr, -1, "?",
"Print help" },
{ "home-drive", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Redirect user home as share" },
{ "ipv4", COMMAND_LINE_VALUE_OPTIONAL, "[:force]", nullptr, nullptr, -1, "4",
"Prefer IPv4 A record over IPv6 AAAA record" },
{ "ipv6", COMMAND_LINE_VALUE_OPTIONAL, "[:force]", nullptr, nullptr, -1, "6",
"Prefer IPv6 AAAA record over IPv4 A record" },
#if defined(WITH_JPEG)
{ "jpeg", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"JPEG codec support" },
{ "jpeg-quality", COMMAND_LINE_VALUE_REQUIRED, "<percentage>", nullptr, nullptr, -1, nullptr,
"JPEG quality" },
#endif
{ "kbd", COMMAND_LINE_VALUE_REQUIRED,
"[layout:[0x<id>|<name>],lang:<0x<id>>,fn-key:<value>,type:<value>,subtype:<value>,unicode[:"
"on|off],remap:<key1>=<value1>,remap:<key2>=<value2>,pipe:<filename>]",
nullptr, nullptr, -1, nullptr,
"Keyboard related options:\n"
" * layout: set the keybouard layout announced to the server\n"
" * lang: set the keyboard language identifier sent to the server\n"
" * fn-key: Function key value\n"
" * remap: RDP scancode to another one. Use /list:kbd-scancode to get the mapping. Example: "
"To switch "
"'a' and 's' on a US keyboard: /kbd:remap:0x1e=0x1f,remap:0x1f=0x1e\n"
" * pipe: Name of a named pipe that can be used to type text into the RDP session\n" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "kbd-lang", COMMAND_LINE_VALUE_REQUIRED, "0x<id>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use / kbd:lang:<value>] Keyboard active language identifier" },
{ "kbd-fn-key", COMMAND_LINE_VALUE_REQUIRED, "<value>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /kbd:fn-key:<value>] Function key value" },
{ "kbd-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, nullptr, nullptr, nullptr, -1,
nullptr, "[DEPRECATED, use /list:kbd] List keyboard layouts" },
{ "kbd-scancode-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, nullptr, nullptr, nullptr,
-1, nullptr, "[DEPRECATED, use list:kbd-scancode] List keyboard RDP scancodes" },
{ "kbd-lang-list", COMMAND_LINE_VALUE_OPTIONAL | COMMAND_LINE_PRINT, nullptr, nullptr, nullptr,
-1, nullptr, "[DEPRECATED, use /list:kbd-lang] List keyboard languages" },
{ "kbd-remap", COMMAND_LINE_VALUE_REQUIRED,
"[DEPRECATED, use /kbd:remap] List of <key>=<value>,... pairs to remap scancodes", nullptr,
nullptr, -1, nullptr, "Keyboard scancode remapping" },
{ "kbd-subtype", COMMAND_LINE_VALUE_REQUIRED, "<id>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /kbd:subtype]Keyboard subtype" },
{ "kbd-type", COMMAND_LINE_VALUE_REQUIRED, "<id>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /kbd:type] Keyboard type" },
{ "kbd-unicode", COMMAND_LINE_VALUE_FLAG, "", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /kbd:unicode[:on|off]] Send unicode symbols, e.g. use the local "
"keyboard map. ATTENTION: Does not work with every "
"RDP server!" },
#endif
{ "kerberos", COMMAND_LINE_VALUE_REQUIRED,
"[kdc-url:<url>,lifetime:<time>,start-time:<time>,renewable-lifetime:<time>,cache:<path>,"
"armor:<path>,pkinit-anchors:<path>,pkcs11-module:<name>]",
nullptr, nullptr, -1, nullptr, "Kerberos options" },
{ "load-balance-info", COMMAND_LINE_VALUE_REQUIRED, "<info-string>", nullptr, nullptr, -1,
nullptr, "Load balance info" },
{ "list", COMMAND_LINE_VALUE_REQUIRED | COMMAND_LINE_PRINT,
"[kbd|kbd-scancode|kbd-lang[:<value>]|smartcard[:[pkinit-anchors:<path>][,pkcs11-module:<"
"name>]]|"
"monitor|tune|timezones]",
"List available options for subcommand", nullptr, -1, nullptr,
"List available options for subcommand" },
{ "log-filters", COMMAND_LINE_VALUE_REQUIRED, "<tag>:<level>[,<tag>:<level>[,...]]", nullptr,
nullptr, -1, nullptr, "Set logger filters, see wLog(7) for details" },
{ "log-level", COMMAND_LINE_VALUE_REQUIRED, "[OFF|FATAL|ERROR|WARN|INFO|DEBUG|TRACE]", nullptr,
nullptr, -1, nullptr, "Set the default log level, see wLog(7) for details" },
{ "max-fast-path-size", COMMAND_LINE_VALUE_REQUIRED, "<size>", nullptr, nullptr, -1, nullptr,
"Specify maximum fast-path update size" },
{ "max-loop-time", COMMAND_LINE_VALUE_REQUIRED, "<time>", nullptr, nullptr, -1, nullptr,
"Specify maximum time in milliseconds spend treating packets" },
{ "menu-anims", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"menu animations" },
{ "microphone", COMMAND_LINE_VALUE_OPTIONAL,
"[sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,][channel:<channel>]", nullptr,
nullptr, -1, "mic", "Audio input (microphone)" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "smartcard-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, nullptr, nullptr, nullptr, -1,
nullptr, "[DEPRECATED, use /list:smartcard] List smartcard information" },
{ "monitor-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, nullptr, nullptr, nullptr, -1,
nullptr, "[DEPRECATED, use /list:monitor] List detected monitors" },
#endif
{ "monitors", COMMAND_LINE_VALUE_REQUIRED, "<id>[,<id>[,...]]", nullptr, nullptr, -1, nullptr,
"Select monitors to use (only effective in fullscreen or multimonitor mode)" },
{ "mouse-motion", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Send mouse motion events" },
{ "mouse-relative", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Send mouse motion with relative addressing" },
{ "mouse", COMMAND_LINE_VALUE_REQUIRED, "[relative:[on|off],grab:[on|off]]", nullptr, nullptr,
-1, nullptr,
"Mouse related options:\n"
" * relative: send relative mouse movements if supported by server\n"
" * grab: grab the mouse if within the window" },
#if defined(CHANNEL_TSMF_CLIENT)
{ "multimedia", COMMAND_LINE_VALUE_OPTIONAL, "[sys:<sys>,][dev:<dev>,][decoder:<decoder>]",
nullptr, nullptr, -1, "mmr", "[DEPRECATED], use /video] Redirect multimedia (video)" },
#endif
{ "multimon", COMMAND_LINE_VALUE_OPTIONAL, "force", nullptr, nullptr, -1, nullptr,
"Use multiple monitors" },
{ "multitouch", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Redirect multitouch input" },
{ "multitransport", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Support multitransport protocol" },
{ "nego", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"protocol security negotiation" },
{ "network", COMMAND_LINE_VALUE_REQUIRED,
"[invalid|modem|broadband|broadband-low|broadband-high|wan|lan|auto]", nullptr, nullptr, -1,
nullptr, "Network connection type" },
{ "nsc", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "nscodec", "NSCodec support" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "offscreen-cache", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /cache:offscreen[:on|off]] offscreen bitmap cache" },
#endif
{ "orientation", COMMAND_LINE_VALUE_REQUIRED, "[0|90|180|270]", nullptr, nullptr, -1, nullptr,
"Orientation of display in degrees" },
{ "old-license", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Use the old license workflow (no CAL and hwId set to 0)" },
{ "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", nullptr, nullptr, -1, nullptr, "Password" },
#if defined(CHANNEL_PARALLEL_CLIENT)
{ "parallel", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<path>]", nullptr, nullptr, -1, nullptr,
"Redirect parallel device" },
#endif
{ "parent-window", COMMAND_LINE_VALUE_REQUIRED, "<window-id>", nullptr, nullptr, -1, nullptr,
"Parent window id" },
{ "pcb", COMMAND_LINE_VALUE_REQUIRED, "<blob>", nullptr, nullptr, -1, nullptr,
"Preconnection Blob" },
{ "pcid", COMMAND_LINE_VALUE_REQUIRED, "<id>", nullptr, nullptr, -1, nullptr,
"Preconnection Id" },
{ "pheight", COMMAND_LINE_VALUE_REQUIRED, "<height>", nullptr, nullptr, -1, nullptr,
"Physical height of display (in millimeters)" },
{ "play-rfx", COMMAND_LINE_VALUE_REQUIRED, "<pcap-file>", nullptr, nullptr, -1, nullptr,
"Replay rfx pcap file" },
{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", nullptr, nullptr, -1, nullptr,
"Server port" },
{ "suppress-output", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"suppress output when minimized" },
{ "print-reconnect-cookie", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1,
nullptr, "Print base64 reconnect cookie after connecting" },
{ "printer", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<driver>[,default]]", nullptr, nullptr, -1,
nullptr, "Redirect printer device" },
{ "proxy", COMMAND_LINE_VALUE_REQUIRED, "[<proto>://][<user>:<password>@]<host>[:<port>]",
nullptr, nullptr, -1, nullptr,
"Proxy settings: override env. var (see also environment variable below). Protocol "
"\"socks5\" should be given explicitly where \"http\" is default." },
{ "pth", COMMAND_LINE_VALUE_REQUIRED, "<password-hash>", nullptr, nullptr, -1, "pass-the-hash",
"Pass the hash (restricted admin mode)" },
{ "pwidth", COMMAND_LINE_VALUE_REQUIRED, "<width>", nullptr, nullptr, -1, nullptr,
"Physical width of display (in millimeters)" },
{ "rdp2tcp", COMMAND_LINE_VALUE_REQUIRED, "<executable path[:arg...]>", nullptr, nullptr, -1,
nullptr, "TCP redirection" },
{ "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "<base64-cookie>", nullptr, nullptr, -1,
nullptr, "Pass base64 reconnect cookie to the connection" },
{ "redirect-prefer", COMMAND_LINE_VALUE_REQUIRED, "<FQDN|IP|NETBIOS>,[...]", nullptr, nullptr,
-1, nullptr, "Override the preferred redirection order" },
{ "relax-order-checks", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1,
"relax-order-checks",
"Do not check if a RDP order was announced during capability exchange, only use when "
"connecting to a buggy server" },
{ "restricted-admin", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "restrictedAdmin",
"Restricted admin mode" },
#ifdef CHANNEL_RDPEAR_CLIENT
{ "remoteGuard", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "remoteGuard",
"Remote guard credentials" },
#endif
{ "rfx", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr, "RemoteFX" },
{ "rfx-mode", COMMAND_LINE_VALUE_REQUIRED, "[image|video]", nullptr, nullptr, -1, nullptr,
"RemoteFX mode" },
{ "scale", COMMAND_LINE_VALUE_REQUIRED, "[100|140|180]", "100", nullptr, -1, nullptr,
"Scaling factor of the display" },
{ "scale-desktop", COMMAND_LINE_VALUE_REQUIRED, "<percentage>", "100", nullptr, -1, nullptr,
"Scaling factor for desktop applications (value between 100 and 500)" },
{ "scale-device", COMMAND_LINE_VALUE_REQUIRED, "100|140|180", "100", nullptr, -1, nullptr,
"Scaling factor for app store applications" },
{ "sec", COMMAND_LINE_VALUE_REQUIRED,
"rdp[:[on|off]]|tls[:[on|off]]|nla[:[on|off]]|ext[:[on|off]]|aad[:[on|off]]", nullptr,
nullptr, -1, nullptr,
"Force specific protocol security. e.g. /sec:nla enables NLA and disables all others, while "
"/sec:nla:[on|off] just toggles NLA" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "sec-ext", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /sec:ext] NLA extended protocol security" },
{ "sec-nla", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"[DEPRECATED, use /sec:nla] NLA protocol security" },
{ "sec-rdp", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"[DEPRECATED, use /sec:rdp] RDP protocol security" },
{ "sec-tls", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"[DEPRECATED, use /sec:tls] TLS protocol security" },
#endif
#if defined(CHANNEL_SERIAL_CLIENT)
{ "serial", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<path>[,<driver>[,permissive]]]", nullptr,
nullptr, -1, "tty", "Redirect serial device" },
#endif
{ "server-name", COMMAND_LINE_VALUE_REQUIRED, "<name>", nullptr, nullptr, -1, nullptr,
"User-specified server name to use for validation (TLS, Kerberos)" },
{ "shell", COMMAND_LINE_VALUE_REQUIRED, "<shell>", nullptr, nullptr, -1, nullptr,
"Alternate shell" },
{ "shell-dir", COMMAND_LINE_VALUE_REQUIRED, "<dir>", nullptr, nullptr, -1, nullptr,
"Shell working directory" },
{ "size", COMMAND_LINE_VALUE_REQUIRED, "<width>x<height> or <percent>%[wh]", "1024x768",
nullptr, -1, nullptr, "Screen size" },
{ "smart-sizing", COMMAND_LINE_VALUE_OPTIONAL, "<width>x<height>", nullptr, nullptr, -1,
nullptr, "Scale remote desktop to window size" },
{ "smartcard", COMMAND_LINE_VALUE_OPTIONAL, "<str>[,<str>...]", nullptr, nullptr, -1, nullptr,
"Redirect the smartcard devices containing any of the <str> in their names." },
{ "smartcard-logon", COMMAND_LINE_VALUE_OPTIONAL,
"[cert:<path>,key:<key>,pin:<pin>,csp:<csp name>,reader:<reader>,card:<card>]", nullptr,
nullptr, -1, nullptr, "Activates Smartcard (optional certificate) Logon authentication." },
{ "sound", COMMAND_LINE_VALUE_OPTIONAL,
"[sys:<sys>,][dev:<dev>,][format:<format>,][rate:<rate>,][channel:<channel>,][latency:<"
"latency>,][quality:<quality>]",
nullptr, nullptr, -1, "audio", "Audio output (sound)" },
{ "span", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"Span screen over multiple monitors" },
{ "spn-class", COMMAND_LINE_VALUE_REQUIRED, "<service-class>", nullptr, nullptr, -1, nullptr,
"SPN authentication service class" },
{ "ssh-agent", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "ssh-agent",
"SSH Agent forwarding channel" },
{ "sspi-module", COMMAND_LINE_VALUE_REQUIRED, "<SSPI module path>", nullptr, nullptr, -1,
nullptr, "SSPI shared library module file path" },
{ "winscard-module", COMMAND_LINE_VALUE_REQUIRED, "<WinSCard module path>", nullptr, nullptr,
-1, nullptr, "WinSCard shared library module file path" },
{ "disable-output", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"Deactivate all graphics decoding in the client session. Useful for load tests with many "
"simultaneous connections" },
{ "t", COMMAND_LINE_VALUE_REQUIRED, "<title>", nullptr, nullptr, -1, "title", "Window title" },
{ "themes", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr, "themes" },
{ "timeout", COMMAND_LINE_VALUE_REQUIRED, "<time in ms>", "9000", nullptr, -1, "timeout",
"Advanced setting for high latency links: Adjust connection timeout, use if you encounter "
"timeout failures with your connection" },
{ "timezone", COMMAND_LINE_VALUE_REQUIRED, "<windows timezone>", nullptr, nullptr, -1, nullptr,
"Use supplied windows timezone for connection (requires server support), see /list:timezones "
"for allowed values" },
{ "tls", COMMAND_LINE_VALUE_REQUIRED, "[ciphers|seclevel|secrets-file|enforce]", nullptr,
nullptr, -1, nullptr,
"TLS configuration options:"
" * ciphers:[netmon|ma|<cipher names>]\n"
" * seclevel:<level>, default: 1, range: [0-5] Override the default TLS security level, "
"might be required for older target servers\n"
" * secrets-file:<filename>\n"
" * enforce[:[ssl3|1.0|1.1|1.2|1.3]] Force use of SSL/TLS version for a connection. Some "
"servers have a buggy TLS "
"version negotiation and might fail without this. Defaults to TLS 1.2 if no argument is "
"supplied. Use 1.0 for windows 7" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "tls-ciphers", COMMAND_LINE_VALUE_REQUIRED, "[netmon|ma|ciphers]", nullptr, nullptr, -1,
nullptr, "[DEPRECATED, use /tls:ciphers] Allowed TLS ciphers" },
{ "tls-seclevel", COMMAND_LINE_VALUE_REQUIRED, "<level>", "1", nullptr, -1, nullptr,
"[DEPRECATED, use /tls:seclevel] TLS security level - defaults to 1" },
{ "tls-secrets-file", COMMAND_LINE_VALUE_REQUIRED, "<filename>", nullptr, nullptr, -1, nullptr,
"[DEPRECATED, use /tls:secrets:file] File were TLS secrets will be stored in the "
"SSLKEYLOGFILE format" },
{ "enforce-tlsv1_2", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"[DEPRECATED, use /tls:enforce:1.2] Force use of TLS1.2 for connection. Some "
"servers have a buggy TLS version negotiation and "
"might fail without this" },
#endif
{ "toggle-fullscreen", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Alt+Ctrl+Enter to toggle fullscreen" },
{ "tune", COMMAND_LINE_VALUE_REQUIRED, "<setting:value>,<setting:value>", "", nullptr, -1,
nullptr, "[experimental] directly manipulate freerdp settings, use with extreme caution!" },
#if defined(WITH_FREERDP_DEPRECATED_COMMANDLINE)
{ "tune-list", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT, nullptr, nullptr, nullptr, -1,
nullptr, "[DEPRECATED, use /list:tune] Print options allowed for /tune" },
#endif
{ "u", COMMAND_LINE_VALUE_REQUIRED, "[[<domain>\\]<user>|<user>[@<domain>]]", nullptr, nullptr,
-1, nullptr, "Username" },
{ "unmap-buttons", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Let server see real physical pointer button" },
#ifdef CHANNEL_URBDRC_CLIENT
{ "usb", COMMAND_LINE_VALUE_REQUIRED,
"[dbg,][id:<vid>:<pid>#...,][addr:<bus>:<addr>#...,][auto]", nullptr, nullptr, -1, nullptr,
"Redirect USB device" },
#endif
{ "v", COMMAND_LINE_VALUE_REQUIRED, "<server>[:port]", nullptr, nullptr, -1, nullptr,
"Server hostname|URL|IPv4|IPv6 or vsock://<number> or /some/path/to/pipe or |:1234 to pass a "
"TCP socket to use" },
{ "vc", COMMAND_LINE_VALUE_REQUIRED, "<channel>[,<options>]", nullptr, nullptr, -1, nullptr,
"Static virtual channel" },
{ "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, nullptr, nullptr, nullptr,
-1, nullptr, "Print version" },
{ "video", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"Video optimized remoting channel" },
{ "prevent-session-lock", COMMAND_LINE_VALUE_OPTIONAL, "<time in sec>", nullptr, nullptr, -1,
nullptr,
"Prevent session locking by injecting fake mouse motion events to the server "
"when the connection is idle (default interval: 180 seconds)" },
{ "vmconnect", COMMAND_LINE_VALUE_OPTIONAL, "<vmid>", nullptr, nullptr, -1, nullptr,
"Hyper-V console (use port 2179, disable negotiation)" },
{ "w", COMMAND_LINE_VALUE_REQUIRED, "<width>", "1024", nullptr, -1, nullptr, "Width" },
{ "wallpaper", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"wallpaper" },
{ "window-drag", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"full window drag" },
{ "window-position", COMMAND_LINE_VALUE_REQUIRED, "<xpos>x<ypos>", nullptr, nullptr, -1,
nullptr, "window position" },
{ "wm-class", COMMAND_LINE_VALUE_REQUIRED, "<class-name>", nullptr, nullptr, -1, nullptr,
"Set the WM_CLASS hint for the window instance" },
{ "workarea", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"Use available work area" },
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
};
#endif /* CLIENT_COMMON_CMDLINE_H */

2908
third_party/FreeRDP/client/common/file.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Geometry tracking Virtual Channel Extension
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <freerdp/client/geometry.h>
#include <winpr/interlocked.h>
void mappedGeometryRef(MAPPED_GEOMETRY* g)
{
InterlockedIncrement(&g->refCounter);
}
void mappedGeometryUnref(MAPPED_GEOMETRY* g)
{
if (!g)
return;
if (InterlockedDecrement(&g->refCounter))
return;
g->MappedGeometryUpdate = nullptr;
g->MappedGeometryClear = nullptr;
g->custom = nullptr;
free(g->geometry.rects);
free(g);
}

View File

@@ -0,0 +1,31 @@
if(CMAKE_CROSSCOMPILING)
find_package(GenerateArgumentManpage)
else()
include_directories(${CMAKE_BINARY_DIR}/include/)
add_executable(generate_argument_manpage generate_argument_manpage.c ../cmdline.h)
include(CompilerDetect)
if(CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_GNUCC)
target_compile_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
target_compile_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
target_link_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
target_link_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
endif()
export(TARGETS generate_argument_manpage FILE "${CMAKE_BINARY_DIR}/GenerateArgumentManpageConfig.cmake")
endif()
set(MAN_OPTIONS_FILE "${CMAKE_CURRENT_BINARY_DIR}/freerdp-global-options.1")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${MAN_OPTIONS_FILE})
add_custom_command(
OUTPUT ${MAN_OPTIONS_FILE} COMMAND $<TARGET_FILE:generate_argument_manpage> "${MAN_OPTIONS_FILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS generate_argument_manpage
)
add_custom_target(
generate_argument_manpage.target DEPENDS ${MAN_OPTIONS_FILE} ${CMAKE_BINARY_DIR}/include/freerdp/config.h
$<TARGET_FILE:generate_argument_manpage>
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

View File

@@ -0,0 +1,82 @@
.SH "GLOBAL CONFIGURATION (client common)"
.PP
Format and Location:
.RS 4
The configuration files are stored in global system configuration\&.
.br
The common certificate settings location is
\fI@SYSCONF_DIR@/certificates\&.json\fR
.br
File format is JSON
.RE
.PP
Supported options:
.RS 4
.PP
\fIdeny\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
Deny the certificate if the check against system SSL store was not successful
.RE
.RE
.PP
\fIignore\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
Ignore certificate failures, just ignore the certificate
.RE
.RE
.PP
\fIdeny\-userconfig\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
If the checks in the global configuration do not accept the certificate do not ask the user
.RE
.RE
.PP
\fIcertificate\-db\fR
.RS 4
.PP
\fIJSON array\fR
.RS 4
An array of
\fIJSON objects\fR
with:
.PP
\fItype\fR
.RS 4
\fIJSON string\fR
.br
a string identifying the hash algorithm used, e\&.g\&.
\fIsha256\fR
.br
.RE
.PP
\fIhash\fR
.RS 4
\fIJSON string\fR
.br
a string of hex integer values representing the certificate hash, e\&.g\&.
\fI0123456789abcdef\fR
.RE
.RE
.RE
.RE

View File

@@ -0,0 +1,17 @@
.SH "ENVIRONMENT VARIABLES"
.PP
wlog environment variables
.RS 4
@MANPAGE_NAME@ uses wLog as its log facility, you can refer to the corresponding man page (wlog(7)) for more information\&. Arguments passed via the
\fI/log\-level\fR
or
\fI/log\-filters\fR
have precedence over the environment variables\&.
.br
.RE
.PP
FREERDP_ASKPASS
.RS 4
\fISet a program to ask for passwords similar to SSH_ASKPASS\fR
.RE

View File

@@ -0,0 +1,8 @@
.SH "LINKS"
.PP
\m[blue]\fBhttp://www\&.freerdp\&.com/\fR\m[]
.SH "AUTHOR"
.br
.PP
The FreeRDP Team

View File

@@ -0,0 +1,262 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "../cmdline.h"
static char* resize(char** buffer, size_t* size, size_t increment)
{
const size_t nsize = *size + increment;
char* tmp = realloc(*buffer, nsize);
if (!tmp)
{
(void)fprintf(stderr,
"Could not reallocate string buffer from %" PRIuz " to %" PRIuz " bytes.\n",
*size, nsize);
free(*buffer);
return nullptr;
}
memset(&tmp[*size], '\0', increment);
*size = nsize;
*buffer = tmp;
return tmp;
}
static char* append(char** buffer, size_t* size, const char* str)
{
const size_t len = strnlen(*buffer, *size);
const size_t add = strlen(str);
const size_t required = len + add + 1;
if (required > *size)
{
if (!resize(buffer, size, required - *size))
return nullptr;
}
strncpy(&(*buffer)[len], str, add);
return *buffer;
}
static LPSTR tr_esc_str(LPCSTR arg, bool format, int* failed)
{
const char* str = nullptr;
LPSTR tmp = nullptr;
size_t ds = 0;
assert(failed);
if (nullptr == arg)
return nullptr;
const size_t s = strlen(arg) + 1;
if (!resize(&tmp, &ds, s))
{
*failed = -2;
return nullptr;
}
for (size_t x = 0; x < s; x++)
{
char data[2] = WINPR_C_ARRAY_INIT;
switch (arg[x])
{
case '-':
str = "\\-";
if (!append(&tmp, &ds, str))
{
*failed = -3;
return nullptr;
}
break;
case '<':
if (format)
str = "\\fI";
else
str = "<";
if (!append(&tmp, &ds, str))
{
*failed = -4;
return nullptr;
}
break;
case '>':
if (format)
str = "\\fR";
else
str = ">";
if (!append(&tmp, &ds, str))
{
*failed = -5;
return nullptr;
}
break;
case '\'':
str = "\\*(Aq";
if (!append(&tmp, &ds, str))
{
*failed = -6;
return nullptr;
}
break;
case '.':
if (!append(&tmp, &ds, "\\&."))
{
*failed = -7;
return nullptr;
}
break;
case '\r':
case '\n':
if (!append(&tmp, &ds, "\n.br\n"))
{
*failed = -8;
return nullptr;
}
break;
default:
data[0] = arg[x];
if (!append(&tmp, &ds, data))
{
*failed = -9;
return nullptr;
}
break;
}
}
return tmp;
}
int main(int argc, char* argv[])
{
int rc = -3;
size_t elements = sizeof(global_cmd_args) / sizeof(global_cmd_args[0]);
if (argc != 2)
{
(void)fprintf(stderr, "Usage: %s <output file name>\n", argv[0]);
return -1;
}
const char* fname = argv[1];
(void)fprintf(stdout, "Generating manpage file '%s'\n", fname);
FILE* fp = fopen(fname, "w");
if (nullptr == fp)
{
(void)fprintf(stderr, "Could not open '%s' for writing.\n", fname);
return -1;
}
/* The tag used as header in the manpage */
(void)fprintf(fp, ".SH \"OPTIONS\"\n");
if (elements < 2)
{
(void)fprintf(stderr, "The argument array 'args' is empty, writing an empty file.\n");
elements = 1;
}
for (size_t x = 0; x < elements - 1; x++)
{
int failed = 0;
const COMMAND_LINE_ARGUMENT_A* arg = &global_cmd_args[x];
char* name = tr_esc_str(arg->Name, FALSE, &failed);
char* alias = tr_esc_str(arg->Alias, FALSE, &failed);
char* format = tr_esc_str(arg->Format, TRUE, &failed);
char* text = tr_esc_str(arg->Text, FALSE, &failed);
if (failed != 0)
{
free(name);
free(alias);
free(format);
free(text);
rc = failed;
goto fail;
}
(void)fprintf(fp, ".PP\n");
bool first = true;
do
{
(void)fprintf(fp, "%s\\fB", first ? "" : ", ");
first = false;
if (arg->Flags == COMMAND_LINE_VALUE_BOOL)
(void)fprintf(fp, "%s", arg->Default ? "\\-" : "+");
else
(void)fprintf(fp, "/");
(void)fprintf(fp, "%s\\fR", name);
if (format)
{
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
(void)fprintf(fp, "[");
(void)fprintf(fp, ":%s", format);
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
(void)fprintf(fp, "]");
}
if (alias == name)
break;
free(name);
name = alias;
} while (alias);
(void)fprintf(fp, "\n");
if (text)
{
(void)fprintf(fp, ".RS 4\n");
const int hasText = text && (strnlen(text, 2) > 0);
if (hasText)
(void)fprintf(fp, "%s", text);
if (arg->Flags & COMMAND_LINE_VALUE_BOOL &&
(!arg->Default || arg->Default == BoolValueTrue))
(void)fprintf(fp, " (default:%s)\n", arg->Default ? "on" : "off");
else if (arg->Default)
{
char* value = tr_esc_str(arg->Default, FALSE, &failed);
if (failed != 0)
{
rc = failed;
goto fail;
}
(void)fprintf(fp, " (default:%s)\n", value);
free(value);
}
else if (hasText)
(void)fprintf(fp, "\n");
}
(void)fprintf(fp, ".RE\n");
free(name);
free(format);
free(text);
}
rc = 0;
fail:
(void)fclose(fp);
if (rc == 0)
(void)fprintf(stdout, "successfully generated '%s'\n", fname);
else
(void)fprintf(stdout, "failed to generate '%s'\n", fname);
return rc;
}

View File

@@ -0,0 +1,60 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Smartcard client functions
*
* Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/assert.h>
#include <freerdp/client/utils/smartcard_cli.h>
#include <freerdp/utils/smartcardlogon.h>
BOOL freerdp_smartcard_list(const rdpSettings* settings)
{
SmartcardCertInfo** certs = nullptr;
size_t count = 0;
if (!smartcard_enumerateCerts(settings, &certs, &count, FALSE))
return FALSE;
printf("smartcard reader detected, listing %" PRIuz " certificates:\n", count);
for (size_t i = 0; i < count; i++)
{
const SmartcardCertInfo* info = certs[i];
char asciiStr[256] = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(info);
printf("%" PRIuz ": %s\n", i, info->subject);
if (ConvertWCharToUtf8(info->csp, asciiStr, ARRAYSIZE(asciiStr)))
printf("\t* CSP: %s\n", asciiStr);
if (ConvertWCharToUtf8(info->reader, asciiStr, ARRAYSIZE(asciiStr)))
printf("\t* reader: %s\n", asciiStr);
#ifndef _WIN32
printf("\t* slotId: %" PRIu32 "\n", info->slotId);
printf("\t* pkinitArgs: %s\n", info->pkinitArgs);
#endif
if (ConvertWCharToUtf8(info->containerName, asciiStr, ARRAYSIZE(asciiStr)))
printf("\t* containerName: %s\n", asciiStr);
if (info->upn)
printf("\t* UPN: %s\n", info->upn);
}
smartcardCertList_Free(certs, count);
return TRUE;
}

View File

@@ -0,0 +1,245 @@
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright 2025 Siemens
*/
#include <sso-mib/sso-mib.h>
#include <freerdp/crypto/crypto.h>
#include <winpr/json.h>
#include "sso_mib_tokens.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("common.sso")
enum sso_mib_state
{
SSO_MIB_STATE_INIT = 0,
SSO_MIB_STATE_FAILED = 1,
SSO_MIB_STATE_SUCCESS = 2,
};
struct MIBClientWrapper
{
MIBClientApp* app;
enum sso_mib_state state;
pGetCommonAccessToken GetCommonAccessToken;
};
static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
{
WINPR_ASSERT(client_context);
WINPR_ASSERT(client_context->mibClientWrapper);
WINPR_ASSERT(client_context->mibClientWrapper->app);
WINPR_ASSERT(token);
MIBAccount* account = nullptr;
GSList* scopes = nullptr;
BOOL rc = FALSE;
*token = nullptr;
account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, nullptr);
if (!account)
{
goto cleanup;
}
scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
account, scopes, nullptr, nullptr, nullptr);
if (prt)
{
const char* access_token = mib_prt_get_access_token(prt);
if (access_token)
{
*token = strdup(access_token);
}
g_object_unref(prt);
}
rc = TRUE && *token != nullptr;
cleanup:
if (account)
g_object_unref(account);
g_slist_free_full(scopes, g_free);
return rc;
}
static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
const char* req_cnf, char** token)
{
WINPR_ASSERT(client_context);
WINPR_ASSERT(client_context->mibClientWrapper);
WINPR_ASSERT(client_context->mibClientWrapper->app);
WINPR_ASSERT(scope);
WINPR_ASSERT(token);
WINPR_ASSERT(req_cnf);
GSList* scopes = nullptr;
WINPR_JSON* json = nullptr;
MIBPopParams* params = nullptr;
BOOL rc = FALSE;
*token = nullptr;
BYTE* req_cnf_dec = nullptr;
size_t req_cnf_dec_len = 0;
scopes = g_slist_append(scopes, g_strdup(scope));
// Parse the "kid" element from req_cnf
crypto_base64_decode(req_cnf, strlen(req_cnf) + 1, &req_cnf_dec, &req_cnf_dec_len);
if (!req_cnf_dec)
{
goto cleanup;
}
json = WINPR_JSON_Parse((const char*)req_cnf_dec);
if (!json)
{
goto cleanup;
}
WINPR_JSON* prop = WINPR_JSON_GetObjectItemCaseSensitive(json, "kid");
if (!prop)
{
goto cleanup;
}
const char* kid = WINPR_JSON_GetStringValue(prop);
if (!kid)
{
goto cleanup;
}
params = mib_pop_params_new(MIB_AUTH_SCHEME_POP, MIB_REQUEST_METHOD_GET, "");
mib_pop_params_set_kid(params, kid);
MIBPrt* prt = mib_client_app_acquire_token_interactive(client_context->mibClientWrapper->app,
scopes, MIB_PROMPT_NONE, nullptr,
nullptr, nullptr, params);
if (prt)
{
*token = strdup(mib_prt_get_access_token(prt));
rc = TRUE;
g_object_unref(prt);
}
cleanup:
if (params)
g_object_unref(params);
WINPR_JSON_Delete(json);
free(req_cnf_dec);
g_slist_free_full(scopes, g_free);
return rc;
}
static BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
size_t count, ...)
{
BOOL rc = FALSE;
rdpClientContext* client_context = (rdpClientContext*)context;
WINPR_ASSERT(client_context);
WINPR_ASSERT(client_context->mibClientWrapper);
if (!client_context->mibClientWrapper->app)
{
const char* client_id =
freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
client_context->mibClientWrapper->app =
mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, nullptr, nullptr);
}
if (!client_context->mibClientWrapper->app)
return FALSE;
const char* scope = nullptr;
const char* req_cnf = nullptr;
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, count);
if (tokenType == ACCESS_TOKEN_TYPE_AAD)
{
scope = va_arg(ap, const char*);
req_cnf = va_arg(ap, const char*);
}
if ((client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT) ||
(client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS))
{
switch (tokenType)
{
case ACCESS_TOKEN_TYPE_AVD:
{
rc = sso_mib_get_avd_access_token(client_context, token);
if (rc)
client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
else
{
WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
"browser-based authentication.");
client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
}
}
break;
case ACCESS_TOKEN_TYPE_AAD:
{
// Setup scope without URL encoding for sso-mib
char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
if (!scope_copy)
WLog_ERR(TAG, "Failed to decode scope");
else
{
rc =
sso_mib_get_rdsaad_access_token(client_context, scope_copy, req_cnf, token);
free(scope_copy);
if (rc)
client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
else
{
WLog_WARN(TAG,
"Getting RDS token from identity broker failed, falling back to "
"browser-based authentication.");
client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
}
}
}
break;
default:
break;
}
}
if (!rc && client_context->mibClientWrapper->GetCommonAccessToken)
rc = client_context->mibClientWrapper->GetCommonAccessToken(context, tokenType, token,
count, scope, req_cnf);
va_end(ap);
return rc;
}
MIBClientWrapper* sso_mib_new(rdpContext* context)
{
MIBClientWrapper* mibClientWrapper = (MIBClientWrapper*)calloc(1, sizeof(MIBClientWrapper));
if (!mibClientWrapper)
return nullptr;
mibClientWrapper->GetCommonAccessToken = freerdp_get_common_access_token(context);
if (!freerdp_set_common_access_token(context, sso_mib_get_access_token))
{
sso_mib_free(mibClientWrapper);
return nullptr;
}
mibClientWrapper->state = SSO_MIB_STATE_INIT;
return mibClientWrapper;
}
void sso_mib_free(MIBClientWrapper* sso)
{
if (!sso)
return;
if (sso->app)
g_object_unref(sso->app);
free(sso);
}

View File

@@ -0,0 +1,17 @@
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright 2025 Siemens
*/
#ifndef FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
#define FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H
#include <freerdp/freerdp.h>
void sso_mib_free(MIBClientWrapper* sso);
WINPR_ATTR_MALLOC(sso_mib_free, 1)
WINPR_ATTR_NODISCARD
MIBClientWrapper* sso_mib_new(rdpContext* context);
#endif /* FREERDP_CLIENT_COMMON_SSO_MIB_TOKENS_H */

View File

@@ -0,0 +1,26 @@
set(MODULE_NAME "TestClient")
set(MODULE_PREFIX "TEST_CLIENT")
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_TESTS TestClientRdpFile.c TestClientChannels.c TestClientCmdLine.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_compile_definitions(${MODULE_NAME} PRIVATE TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Client/Test")

View File

@@ -0,0 +1,87 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/windows.h>
#include <freerdp/client/channels.h>
#include <freerdp/channels/rdpsnd.h>
int TestClientChannels(int argc, char* argv[])
{
DWORD dwFlags = 0;
FREERDP_ADDIN** ppAddins = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
dwFlags = FREERDP_ADDIN_DYNAMIC;
printf("Enumerate all\n");
ppAddins = freerdp_channels_list_addins(nullptr, nullptr, nullptr, dwFlags);
for (size_t index = 0; ppAddins[index] != nullptr; index++)
{
FREERDP_ADDIN* pAddin = ppAddins[index];
printf("Addin: Name: %s Subsystem: %s Type: %s\n", pAddin->cName, pAddin->cSubsystem,
pAddin->cType);
}
freerdp_channels_addin_list_free(ppAddins);
printf("Enumerate rdpsnd\n");
ppAddins = freerdp_channels_list_addins(RDPSND_CHANNEL_NAME, nullptr, nullptr, dwFlags);
for (size_t index = 0; ppAddins[index] != nullptr; index++)
{
FREERDP_ADDIN* pAddin = ppAddins[index];
printf("Addin: Name: %s Subsystem: %s Type: %s\n", pAddin->cName, pAddin->cSubsystem,
pAddin->cType);
}
freerdp_channels_addin_list_free(ppAddins);
#if defined(CHANNEL_TSMF_CLIENT)
printf("Enumerate tsmf video\n");
ppAddins = freerdp_channels_list_addins("tsmf", nullptr, "video", dwFlags);
for (size_t index = 0; ppAddins[index] != nullptr; index++)
{
FREERDP_ADDIN* pAddin = ppAddins[index];
printf("Addin: Name: %s Subsystem: %s Type: %s\n", pAddin->cName, pAddin->cSubsystem,
pAddin->cType);
}
freerdp_channels_addin_list_free(ppAddins);
#endif
ppAddins = freerdp_channels_list_addins("unknown", nullptr, nullptr, dwFlags);
for (size_t index = 0; ppAddins[index] != nullptr; index++)
{
FREERDP_ADDIN* pAddin = ppAddins[index];
printf("Addin: Name: %s Subsystem: %s Type: %s\n", pAddin->cName, pAddin->cSubsystem,
pAddin->cType);
}
freerdp_channels_addin_list_free(ppAddins);
printf("Enumerate static addins\n");
dwFlags = FREERDP_ADDIN_STATIC;
ppAddins = freerdp_channels_list_addins(nullptr, nullptr, nullptr, dwFlags);
for (size_t index = 0; ppAddins[index] != nullptr; index++)
{
FREERDP_ADDIN* pAddin = ppAddins[index];
printf("Addin: Name: %s Subsystem: %s Type: %s\n", pAddin->cName, pAddin->cSubsystem,
pAddin->cType);
}
freerdp_channels_addin_list_free(ppAddins);
return 0;
}

View File

@@ -0,0 +1,263 @@
#include <freerdp/client.h>
#include <freerdp/client/cmdline.h>
#include <freerdp/settings.h>
#include <winpr/cmdline.h>
#include <winpr/spec.h>
#include <winpr/strlst.h>
#include <winpr/collections.h>
typedef BOOL (*validate_settings_pr)(rdpSettings* settings);
#define printref() printf("%s:%d: in function %-40s:", __FILE__, __LINE__, __func__)
#define TEST_ERROR(format, ...) \
do \
{ \
(void)fprintf(stderr, format, ##__VA_ARGS__); \
printref(); \
(void)printf(format, ##__VA_ARGS__); \
(void)fflush(stdout); \
} while (0)
#define TEST_FAILURE(format, ...) \
do \
{ \
printref(); \
(void)printf(" FAILURE "); \
(void)printf(format, ##__VA_ARGS__); \
(void)fflush(stdout); \
} while (0)
static void print_test_title(int argc, char** argv)
{
printf("Running test:");
for (int i = 0; i < argc; i++)
{
printf(" %s", argv[i]);
}
printf("\n");
}
static inline BOOL testcase(const char* name, char** argv, size_t argc, int expected_return,
validate_settings_pr validate_settings)
{
int status = 0;
BOOL valid_settings = TRUE;
rdpSettings* settings = freerdp_settings_new(0);
WINPR_ASSERT(argc <= INT_MAX);
print_test_title((int)argc, argv);
if (!settings)
{
TEST_ERROR("Test %s could not allocate settings!\n", name);
return FALSE;
}
status = freerdp_client_settings_parse_command_line(settings, (int)argc, argv, FALSE);
if (validate_settings)
{
valid_settings = validate_settings(settings);
}
freerdp_settings_free(settings);
if (status == expected_return)
{
if (!valid_settings)
{
return FALSE;
}
}
else
{
TEST_FAILURE("Expected status %d, got status %d\n", expected_return, status);
return FALSE;
}
return TRUE;
}
#if defined(_WIN32)
#define DRIVE_REDIRECT_PATH "c:\\Windows"
#else
#define DRIVE_REDIRECT_PATH "/tmp"
#endif
static BOOL check_settings_smartcard_no_redirection(rdpSettings* settings)
{
BOOL result = TRUE;
if (freerdp_settings_get_bool(settings, FreeRDP_RedirectSmartCards))
{
TEST_FAILURE("Expected RedirectSmartCards = FALSE, but RedirectSmartCards = TRUE!\n");
result = FALSE;
}
if (freerdp_device_collection_find_type(settings, RDPDR_DTYP_SMARTCARD))
{
TEST_FAILURE("Expected no SMARTCARD device, but found at least one!\n");
result = FALSE;
}
return result;
}
typedef struct
{
int expected_status;
validate_settings_pr validate_settings;
const char* command_line[128];
struct
{
int index;
const char* expected_value;
} modified_arguments[8];
} test;
// NOLINTBEGIN(bugprone-suspicious-missing-comma)
static const test tests[] = {
{ COMMAND_LINE_STATUS_PRINT_HELP,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "--help", nullptr },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT_HELP,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/help", nullptr },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT_HELP,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "-help", nullptr },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT_VERSION,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "--version", nullptr },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT_VERSION,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/version", nullptr },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT_VERSION,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "-version", nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "-v", "test.freerdp.com", nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "--v", "test.freerdp.com", nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/v:test.freerdp.com", nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/sound", "/drive:media," DRIVE_REDIRECT_PATH, "/v:test.freerdp.com",
nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "-u", "test", "-p", "test", "-v", "test.freerdp.com", nullptr },
{ { 4, "****" }, { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/u:test", "/p:test", "/v:test.freerdp.com", nullptr },
{ { 2, "/p:****" }, { 0 } } },
{ COMMAND_LINE_ERROR_NO_KEYWORD,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "-invalid", nullptr },
{ { 0 } } },
{ COMMAND_LINE_ERROR_NO_KEYWORD,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "--invalid", nullptr },
{ { 0 } } },
#if defined(WITH_FREERDP_DEPRECATED_CMDLINE)
{ COMMAND_LINE_STATUS_PRINT,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/kbd-list", 0 },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/monitor-list", 0 },
{ { 0 } } },
#endif
{ COMMAND_LINE_STATUS_PRINT,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/list:kbd", nullptr },
{ { 0 } } },
{ COMMAND_LINE_STATUS_PRINT,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/list:monitor", nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/sound", "/drive:media:" DRIVE_REDIRECT_PATH, "/v:test.freerdp.com",
nullptr },
{ { 0 } } },
{ 0,
check_settings_smartcard_no_redirection,
{ "testfreerdp", "/sound", "/drive:media,/foo/bar/blabla", "/v:test.freerdp.com", nullptr },
{ { 0 } } },
};
// NOLINTEND(bugprone-suspicious-missing-comma)
static void check_modified_arguments(const test* test, char** command_line, int* rc)
{
const char* expected_argument = nullptr;
for (int k = 0; (expected_argument = test->modified_arguments[k].expected_value); k++)
{
int index = test->modified_arguments[k].index;
char* actual_argument = command_line[index];
if (0 != strcmp(actual_argument, expected_argument))
{
printref();
printf("Failure: overridden argument %d is %s but it should be %s\n", index,
actual_argument, expected_argument);
(void)fflush(stdout);
*rc = -1;
}
}
}
int TestClientCmdLine(int argc, char* argv[])
{
int rc = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
for (size_t i = 0; i < ARRAYSIZE(tests); i++)
{
const test* current = &tests[i];
int failure = 0;
char** command_line = string_list_copy(current->command_line);
const int len = string_list_length((const char* const*)command_line);
if (!testcase(__func__, command_line, WINPR_ASSERTING_INT_CAST(size_t, len),
current->expected_status, current->validate_settings))
{
TEST_FAILURE("parsing arguments.\n");
failure = 1;
}
check_modified_arguments(current, command_line, &failure);
if (failure)
{
string_list_print(stdout, (const char* const*)command_line);
rc = -1;
}
string_list_free(command_line);
}
return rc;
}

View File

@@ -0,0 +1,838 @@
#include <freerdp/config.h>
#include <stdio.h>
#include <errno.h>
#include <winpr/crt.h>
#include <winpr/windows.h>
#include <winpr/path.h>
#include <winpr/crypto.h>
#include <freerdp/client/file.h>
#include <freerdp/channels/rdpecam.h>
static const BYTE testRdpFileUTF16[] = {
0xff, 0xfe, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x20, 0x00,
0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x64, 0x00, 0x3a, 0x00,
0x69, 0x00, 0x3a, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00,
0x20, 0x00, 0x6d, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00,
0x6e, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00,
0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x77, 0x00, 0x69, 0x00,
0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x39, 0x00,
0x32, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00,
0x74, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x68, 0x00, 0x65, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00,
0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00,
0x0d, 0x00, 0x0a, 0x00, 0x73, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
0x6e, 0x00, 0x20, 0x00, 0x62, 0x00, 0x70, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x33, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x70, 0x00,
0x6f, 0x00, 0x73, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00,
0x30, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x35, 0x00, 0x35, 0x00, 0x33, 0x00, 0x2c, 0x00,
0x32, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x33, 0x00,
0x2c, 0x00, 0x38, 0x00, 0x31, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x70, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00,
0x6e, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6b, 0x00,
0x65, 0x00, 0x79, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x68, 0x00,
0x6f, 0x00, 0x6f, 0x00, 0x6b, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x32, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x61, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00,
0x70, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00,
0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x76, 0x00,
0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00,
0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00,
0x20, 0x00, 0x74, 0x00, 0x79, 0x00, 0x70, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x37, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x77, 0x00, 0x6f, 0x00,
0x72, 0x00, 0x6b, 0x00, 0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00,
0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00,
0x0d, 0x00, 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x77, 0x00, 0x69, 0x00,
0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x64, 0x00,
0x65, 0x00, 0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x70, 0x00, 0x6c, 0x00,
0x61, 0x00, 0x79, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00,
0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x72, 0x00, 0x3a, 0x00,
0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00,
0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x73, 0x00,
0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00,
0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x77, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x70, 0x00,
0x61, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00,
0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00,
0x66, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x6f, 0x00,
0x6f, 0x00, 0x74, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x3a, 0x00, 0x69, 0x00,
0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00,
0x77, 0x00, 0x20, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x6f, 0x00,
0x70, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x73, 0x00,
0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00,
0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x66, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00,
0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00, 0x64, 0x00,
0x72, 0x00, 0x61, 0x00, 0x67, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00,
0x20, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00,
0x69, 0x00, 0x6d, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00,
0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x73, 0x00, 0x3a, 0x00,
0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00,
0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x75, 0x00, 0x72, 0x00,
0x73, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00,
0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x70, 0x00, 0x63, 0x00,
0x61, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00,
0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00,
0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x66, 0x00,
0x75, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x61, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00,
0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x41, 0x00,
0x42, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x44, 0x00, 0x4d, 0x00,
0x2d, 0x00, 0x30, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x62, 0x00, 0x31, 0x00,
0x2e, 0x00, 0x61, 0x00, 0x77, 0x00, 0x61, 0x00, 0x6b, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x6c, 0x00,
0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x75, 0x00,
0x64, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x3a, 0x00,
0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00,
0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00,
0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00,
0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6f, 0x00,
0x72, 0x00, 0x74, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00,
0x74, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x63, 0x00, 0x61, 0x00,
0x72, 0x00, 0x64, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00,
0x74, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x70, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x61, 0x00,
0x72, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00,
0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00,
0x70, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x64, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00,
0x65, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00,
0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00,
0x20, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x64, 0x00,
0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x75, 0x00,
0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00,
0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x76, 0x00,
0x65, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00,
0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00,
0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00,
0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6f, 0x00,
0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00,
0x63, 0x00, 0x75, 0x00, 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x6c, 0x00,
0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00,
0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00,
0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00,
0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x3a, 0x00,
0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x74, 0x00,
0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00,
0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x0d, 0x00,
0x0a, 0x00, 0x73, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00,
0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x64, 0x00,
0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x79, 0x00,
0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x61, 0x00, 0x74, 0x00,
0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x74, 0x00,
0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x4c, 0x00,
0x41, 0x00, 0x42, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00,
0x52, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x47, 0x00, 0x57, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x61, 0x00,
0x62, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x77, 0x00, 0x61, 0x00, 0x6b, 0x00, 0x65, 0x00,
0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x0d, 0x00, 0x0a, 0x00,
0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x75, 0x00,
0x73, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x74, 0x00, 0x68, 0x00,
0x6f, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00,
0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x63, 0x00,
0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00,
0x6c, 0x00, 0x73, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00,
0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x61, 0x00,
0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00,
0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x75, 0x00, 0x73, 0x00, 0x61, 0x00, 0x67, 0x00,
0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x3a, 0x00,
0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00,
0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x63, 0x00,
0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x75, 0x00,
0x73, 0x00, 0x65, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00,
0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x73, 0x00,
0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00,
0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00,
0x72, 0x00, 0x64, 0x00, 0x67, 0x00, 0x69, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x64, 0x00, 0x63, 0x00,
0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x79, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00,
0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6b, 0x00, 0x64, 0x00, 0x63, 0x00, 0x70, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x78, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00,
0x73, 0x00, 0x3a, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00,
0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00,
0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x2a, 0x00,
0x0d, 0x00, 0x0a, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00,
0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x42, 0x00,
0x31, 0x00, 0x5c, 0x00, 0x4a, 0x00, 0x6f, 0x00, 0x68, 0x00, 0x6e, 0x00, 0x44, 0x00, 0x6f, 0x00,
0x65, 0x00, 0x0d, 0x00, 0x0a, 0x00
};
#if defined(CHANNEL_RDPECAM_CLIENT)
static const char* camera_args[] = { RDPECAM_DVC_CHANNEL_NAME,
"device:*",
"device:\\?\\usb#vid_0bda&pid_58b0&mi",
"device:-\\?\\usb#vid_0bdc&pid_58b1&mi",
"encode:1",
"quality:2" };
#endif
#if defined(CHANNEL_URBDRC_CLIENT)
static const char* urbdrc_args[] = { "urbdrc", "device:*", "device:USBInstanceID:someid",
"device:{72631e54-78a4-11d0-bcf7-00aa00b7b32a}" };
#endif
static char testRdpFileUTF8[] =
"screen mode id:i:2\n"
"use multimon:i:0\n"
"desktopwidth:i:1920\n"
"desktopheight:i:1080\n"
"dynamic resolution:i:1080\n"
"desktopscalefactor:i:1080\n"
"redirected video capture encoding quality:i:2\n"
"encode redirected video capture:i:1\n"
"camerastoredirect:s:*,\\?\\usb#vid_0bda&pid_58b0&mi,-\\?\\usb#vid_0bdc&pid_58b1&mi\n"
"usbdevicestoredirect:s:*,USBInstanceID:someid,{72631e54-78a4-11d0-bcf7-00aa00b7b32a}\n"
"selectedmonitors:s:3,2,42,23"
"session bpp:i:32\n"
"winposstr:s:0,1,553,211,1353,811\n"
"compression:i:1\n"
"keyboardhook:i:2\n"
"audiocapturemode:i:0\n"
"videoplaybackmode:i:2\n"
"connection type:i:7\n"
"networkautodetect:i:1\n"
"bandwidthautodetect:i:1\n"
"displayconnectionbar:i:1\n"
"enableworkspacereconnect:i:0\n"
"disable wallpaper:i:0\n"
"allow font smoothing:i:0\n"
"allow desktop composition:i:0\n"
"disable full window drag:i:1\n"
"disable menu anims:i:1\n"
"disable themes:i:0\n"
"disable cursor setting:i:0\n"
"bitmapcachepersistenable:i:1\n"
"full address:s:LAB1-W7-DM-01.lab1.awake.local\n"
"alternate full address:s:LAB1-W7-DM-01.lab1.awake.global\n"
"audiomode:i:0\n"
"redirectprinters:i:1\n"
"redirectcomports:i:0\n"
"redirectsmartcards:i:1\n"
"redirectclipboard:i:1\n"
"redirectposdevices:i:0\n"
"autoreconnection enabled:i:1\n"
"authentication level:i:2\n"
"prompt for credentials:i:0\n"
"negotiate security layer:i:1\n"
"remoteapplicationmode:i:0\n"
"alternate shell:s:\n"
"shell working directory:s:\n"
"gatewayhostname:s:LAB1-W2K8R2-GW.lab1.awake.local\n"
"gatewayusagemethod:i:1\n"
"gatewaycredentialssource:i:0\n"
"gatewayprofileusagemethod:i:1\n"
"promptcredentialonce:i:1\n"
"use redirection server name:i:0\n"
"rdgiskdcproxy:i:0\n"
"kdcproxyname:s:\n"
"drivestoredirect:s:*\n"
"username:s:LAB1\\JohnDoe\n"
"vendor integer:i:123\n"
"vendor string:s:microsoft\n";
static char* append(const char* fmt, ...)
{
int rc = 0;
char* dst = nullptr;
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, fmt);
rc = vsnprintf(nullptr, 0, fmt, ap);
va_end(ap);
if (rc < 0)
return nullptr;
dst = malloc((size_t)rc + 1);
if (!dst)
return nullptr;
va_start(ap, fmt);
rc = vsnprintf(dst, (size_t)rc + 1, fmt, ap);
va_end(ap);
if (rc < 0)
{
free(dst);
return nullptr;
}
return dst;
}
static FILE* test_fopen(const char* name, const char* mode)
{
#ifndef TEST_SOURCE_DIR
#error "TEST_SOURCE_DIR must be defined to the test source directory"
#endif
char* path = GetCombinedPath(TEST_SOURCE_DIR, name);
FILE* fp = winpr_fopen(path, mode);
free(path);
return fp;
}
static void* read_rdp_data(const char* name, size_t* plen)
{
BOOL success = FALSE;
char* json = nullptr;
FILE* fp = test_fopen(name, "r");
if (!fp)
goto fail;
if (fseek(fp, 0, SEEK_END) != 0)
goto fail;
const INT64 pos = _ftelli64(fp);
if (pos < 0)
goto fail;
if (fseek(fp, 0, SEEK_SET) != 0)
goto fail;
const size_t upos = WINPR_CXX_COMPAT_CAST(size_t, pos);
json = calloc(1ULL + upos, sizeof(char));
if (!json)
goto fail;
if (fread(json, 1, pos, fp) != pos)
goto fail;
*plen = upos;
success = TRUE;
fail:
if (!success)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WLog_ERR(__func__, "failed to read data from '%s': %s", name,
winpr_strerror(errno, buffer, sizeof(buffer)));
free(json);
json = nullptr;
}
if (fp)
(void)fclose(fp);
return json;
}
static bool save_settings(const rdpSettings* settings, const char* name)
{
bool rc = false;
size_t datalen = 0;
char* data = freerdp_settings_serialize(settings, TRUE, &datalen);
if (!data)
return false;
FILE* fp = test_fopen(name, "w");
if (fp)
{
const size_t res = fwrite(data, 1, datalen, fp);
(void)fclose(fp);
rc = res == datalen;
}
free(data);
return rc;
}
static char* get_json_name(const char* base, bool unchecked)
{
size_t namelen = 0;
char* name = nullptr;
winpr_asprintf(&name, &namelen, "%s%s.json", base, unchecked ? ".unchecked" : "");
return name;
}
static rdpSettings* read_json(const char* name)
{
size_t datalen = 0;
void* data = read_rdp_data(name, &datalen);
rdpSettings* settings = freerdp_settings_deserialize(data, datalen);
fail:
free(data);
return settings;
}
static rdpSettings* load_from(const void* data, size_t len, bool unchecked)
{
BOOL rc = false;
rdpFile* file = freerdp_client_rdp_file_new();
rdpSettings* settings = read_json("default-settings.json");
if (!settings)
{
settings = freerdp_settings_new(0);
if (settings)
{
save_settings(settings, "default-settings.json");
}
}
if (!file || !settings)
goto fail;
if (!freerdp_client_parse_rdp_file_buffer(file, data, len))
goto fail;
if (unchecked)
{
if (!freerdp_client_populate_settings_from_rdp_file_unchecked(file, settings))
goto fail;
}
else
{
if (!freerdp_client_populate_settings_from_rdp_file(file, settings))
goto fail;
}
rc = true;
fail:
freerdp_client_rdp_file_free(file);
if (!rc)
{
freerdp_settings_free(settings);
return nullptr;
}
return settings;
}
static rdpSettings* load_from_file(const char* name, bool unchecked)
{
size_t datalen = 0;
void* data = read_rdp_data(name, &datalen);
if (!data)
return nullptr;
rdpSettings* settings = load_from(data, datalen, unchecked);
free(data);
return settings;
}
static bool test_data(const char* json, const void* data, size_t len, bool unchecked)
{
bool rc = false;
rdpSettings* settings = load_from(data, len, unchecked);
rdpSettings* expect = read_json(json);
if (!settings || !expect)
goto fail;
#ifndef WITH_GFX_H264
if (!freerdp_settings_set_bool(expect, FreeRDP_GfxH264, FALSE) ||
!freerdp_settings_set_bool(expect, FreeRDP_GfxAVC444, FALSE) ||
!freerdp_settings_set_bool(expect, FreeRDP_GfxAVC444v2, FALSE))
goto fail;
#endif
wLog* log = WLog_Get(__func__);
WLog_Print(log, WLOG_INFO, "Test cast '%s'", json);
if (freerdp_settings_print_diff(log, WLOG_ERROR, expect, settings))
goto fail;
rc = true;
fail:
freerdp_settings_free(settings);
freerdp_settings_free(expect);
return rc;
}
static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, WIN32_FIND_DATAW* FindData)
{
HANDLE hdl = INVALID_HANDLE_VALUE;
if (!pszSearchPath)
return hdl;
WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, nullptr);
if (!wpath)
return hdl;
hdl = FindFirstFileW(wpath, FindData);
free(wpath);
return hdl;
}
static bool test_rdp_file(const char* base, bool allowCreate, bool unchecked)
{
bool rc = false;
size_t rdplen = 0;
char* rdp = nullptr;
winpr_asprintf(&rdp, &rdplen, "%s.rdp", base);
char* json = get_json_name(base, unchecked);
size_t datalen = 0;
void* data = read_rdp_data(rdp, &datalen);
if (!data)
goto fail;
rc = test_data(json, data, datalen, unchecked);
if (!rc && allowCreate)
{
rdpSettings* expect = read_json(json);
if (expect)
{
freerdp_settings_free(expect);
goto fail;
}
rdpSettings* settings = load_from_file(rdp, unchecked);
if (!settings)
goto fail;
rc = save_settings(settings, json);
}
fail:
free(data);
free(json);
free(rdp);
return rc;
}
static bool test_rdp_files(bool allowCreate)
{
bool rc = false;
/* Load RDP files from directory, compare to JSON in same directory.
* If JSON does not exist, create it.
*/
#ifndef TEST_SOURCE_DIR
#error "TEST_SOURCE_DIR must be defined to the test source directory"
#endif
HANDLE hdl = INVALID_HANDLE_VALUE;
char* path = GetCombinedPath(TEST_SOURCE_DIR, "rdp-testcases/*.rdp");
if (!path)
goto fail;
WIN32_FIND_DATAW FindData = WINPR_C_ARRAY_INIT;
hdl = FindFirstFileUTF8(path, &FindData);
if (hdl == INVALID_HANDLE_VALUE)
{
WLog_INFO(
__func__,
"no RDP files found in %s. Add RDP files to generate settings JSON for comparison.",
path);
rc = true;
goto fail;
}
do
{
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
{
char cFileName[6 * MAX_PATH] = WINPR_C_ARRAY_INIT;
char rdp[6 * MAX_PATH] = WINPR_C_ARRAY_INIT;
ConvertWCharToUtf8(FindData.cFileName, cFileName, sizeof(cFileName));
const size_t len = strnlen(cFileName, sizeof(cFileName));
if (len < 4)
continue;
strcpy(rdp, "rdp-testcases/");
strncpy(&rdp[14], cFileName, len - 4);
if (!test_rdp_file(rdp, allowCreate, false))
goto fail;
if (!test_rdp_file(rdp, allowCreate, true))
goto fail;
}
} while (FindNextFileW(hdl, &FindData));
rc = true;
fail:
FindClose(hdl);
free(path);
return rc;
}
int TestClientRdpFile(int argc, char* argv[])
{
int rc = -1;
int iValue = 0;
UINT32 uValue = 0;
const UINT32* puValue = nullptr;
const char* sValue = nullptr;
char* utfname = nullptr;
char* uniname = nullptr;
char* base = nullptr;
char* tmp = nullptr;
UINT64 id = 0;
rdpFile* file = nullptr;
rdpSettings* settings = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (winpr_RAND(&id, sizeof(id)) < 0)
return -1;
/* UTF8 */
#if defined(CHANNEL_URBDRC_CLIENT) && defined(CHANNEL_RDPECAM_CLIENT)
if (!test_data("testRdpFileUTF8.json", testRdpFileUTF8, sizeof(testRdpFileUTF8), false))
return -1;
if (!test_data("testRdpFileUTF8.unchecked.json", testRdpFileUTF8, sizeof(testRdpFileUTF8),
true))
return -1;
#endif
/* Unicode */
#if defined(CHANNEL_URBDRC_CLIENT)
if (!test_data("testRdpFileUTF16.json", testRdpFileUTF16, sizeof(testRdpFileUTF16), false))
return -1;
if (!test_data("testRdpFileUTF16.unchecked.json", testRdpFileUTF16, sizeof(testRdpFileUTF16),
true))
return -1;
#endif
#if defined(CHANNEL_URBDRC_CLIENT) && defined(CHANNEL_RDPECAM_CLIENT)
if (!test_rdp_files(argc > 1))
return -1;
#endif
/* Ascii */
file = freerdp_client_rdp_file_new();
settings = freerdp_settings_new(0);
if (!file || !settings)
{
printf("rdp_file_new failed\n");
goto fail;
}
if (!freerdp_client_parse_rdp_file_buffer(file, (BYTE*)testRdpFileUTF8,
sizeof(testRdpFileUTF8)))
goto fail;
if (!freerdp_client_populate_settings_from_rdp_file(file, settings))
goto fail;
if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
{
printf("UseMultiMon mismatch: Actual: %" PRIu32 ", Expected: 0\n",
freerdp_settings_get_bool(settings, FreeRDP_UseMultimon));
return -1;
}
if (!freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
{
printf("ScreenModeId mismatch: Actual: %" PRIu32 ", Expected: TRUE\n",
freerdp_settings_get_bool(settings, FreeRDP_Fullscreen));
return -1;
}
if (strcmp(freerdp_settings_get_string(settings, FreeRDP_ServerHostname),
"LAB1-W7-DM-01.lab1.awake.global") != 0)
{
printf("ServerHostname mismatch: Actual: %s, Expected: %s\n",
freerdp_settings_get_string(settings, FreeRDP_ServerHostname),
"LAB1-W7-DM-01.lab1.awake.global");
goto fail;
}
if (strcmp(freerdp_settings_get_string(settings, FreeRDP_GatewayHostname),
"LAB1-W2K8R2-GW.lab1.awake.local") != 0)
{
printf("GatewayHostname mismatch: Actual: %s, Expected: %s\n",
freerdp_settings_get_string(settings, FreeRDP_GatewayHostname),
"LAB1-W2K8R2-GW.lab1.awake.local");
goto fail;
}
iValue = freerdp_client_rdp_file_get_integer_option(file, "dynamic resolution");
if (iValue != 1080)
{
printf("dynamic resolution uses invalid default value %d", iValue);
goto fail;
}
if (!freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
{
printf("FreeRDP_DynamicResolutionUpdate has invalid value");
goto fail;
}
iValue = freerdp_client_rdp_file_get_integer_option(file, "desktopscalefactor");
if (iValue != 1080)
{
printf("desktopscalefactor uses invalid default value %d", iValue);
goto fail;
}
if ((INT64)freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor) != iValue)
{
printf("FreeRDP_DesktopScaleFactor has invalid value");
goto fail;
}
/* Check [MS-RDPECAM] related options */
#if defined(CHANNEL_RDPECAM_CLIENT)
{
ADDIN_ARGV* args = nullptr;
iValue =
freerdp_client_rdp_file_get_integer_option(file, "encode redirected video capture");
if (iValue != 1)
{
printf("encode redirected video capture uses invalid default value %d", iValue);
goto fail;
}
iValue = freerdp_client_rdp_file_get_integer_option(
file, "redirected video capture encoding quality");
if (iValue != 2)
{
printf("redirected video capture encoding quality uses invalid default value %d",
iValue);
goto fail;
}
args = freerdp_dynamic_channel_collection_find(settings, RDPECAM_DVC_CHANNEL_NAME);
if (!args)
{
printf("rdpecam channel was not loaded");
goto fail;
}
if (args->argc != 6)
{
printf("rdpecam channel was not loaded");
goto fail;
}
for (int x = 0; x < args->argc; x++)
{
if (strcmp(args->argv[x], camera_args[x]) != 0)
{
printf("rdpecam invalid argument argv[%d]: %s", x, args->argv[x]);
goto fail;
}
}
}
#endif
/* Check [URBDRC] related options */
#if defined(CHANNEL_URBDRC_CLIENT)
{
ADDIN_ARGV* args = freerdp_dynamic_channel_collection_find(settings, "urbdrc");
if (!args)
{
printf("urbdrc channel was not loaded");
goto fail;
}
if (args->argc != 4)
{
printf("urbdrc channel was not loaded");
goto fail;
}
for (int x = 0; x < args->argc; x++)
{
if (strcmp(args->argv[x], urbdrc_args[x]) != 0)
{
printf("urbdrc invalid argument argv[%d]: %s", x, args->argv[x]);
goto fail;
}
}
}
#endif
/* Validate selectedmonitors:s:3,2,42,23 */
uValue = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds);
if (uValue != 4)
{
printf("FreeRDP_NumMonitorIds has invalid value %" PRIu32, uValue);
goto fail;
}
puValue = (const UINT32*)freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, 0);
if (!puValue)
{
printf("FreeRDP_MonitorIds has invalid value %p", (const void*)puValue);
goto fail;
}
if ((puValue[0] != 3) || (puValue[1] != 2) || (puValue[2] != 42) || (puValue[3] != 23))
{
printf("FreeRDP_MonitorIds has invalid values: [%" PRIu32 ",%" PRIu32 ",%" PRIu32
",%" PRIu32 "]",
puValue[0], puValue[1], puValue[2], puValue[3]);
goto fail;
}
iValue = freerdp_client_rdp_file_get_integer_option(file, "videoplaybackmode");
if (iValue != 2)
{
printf("videoplaybackmode uses invalid default value %d", iValue);
goto fail;
}
if (!freerdp_settings_get_bool(settings, FreeRDP_SupportVideoOptimized))
{
printf("FreeRDP_SupportVideoOptimized has invalid value");
goto fail;
}
if (!freerdp_settings_get_bool(settings, FreeRDP_SupportGeometryTracking))
{
printf("FreeRDP_SupportGeometryTracking has invalid value");
goto fail;
}
iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer");
if (iValue != 123)
goto fail;
if (freerdp_client_rdp_file_set_integer_option(file, "vendor integer", 456) == -1)
{
printf("failed to set integer: vendor integer");
goto fail;
}
iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer");
if (iValue != 456)
return -1;
const char microsoft[] = "microsoft";
sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string");
if (strncmp(sValue, microsoft, sizeof(microsoft)) != 0)
goto fail;
const char apple[] = "apple";
freerdp_client_rdp_file_set_string_option(file, "vendor string", "apple");
sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string");
if (strncmp(sValue, apple, sizeof(apple)) != 0)
goto fail;
freerdp_client_rdp_file_set_string_option(file, "fruits", "banana,oranges");
if (freerdp_client_rdp_file_set_integer_option(file, "numbers", 123456789) == -1)
{
printf("failed to set integer: numbers");
return -1;
}
freerdp_client_rdp_file_free(file);
tmp = GetKnownPath(KNOWN_PATH_TEMP);
if (!tmp)
goto fail;
base = append("%s/rdp-file-test-%" PRIx64, tmp, id);
if (!base)
goto fail;
if (!CreateDirectoryA(base, nullptr))
goto fail;
utfname = append("%s/utfname", base);
uniname = append("%s/uniname", base);
file = freerdp_client_rdp_file_new();
if (!file || !utfname || !uniname)
goto fail;
if (!freerdp_client_populate_rdp_file_from_settings(file, settings))
goto fail;
if (!freerdp_client_write_rdp_file(file, utfname, FALSE))
goto fail;
if (!freerdp_client_write_rdp_file(file, uniname, TRUE))
goto fail;
rc = 0;
fail:
if (utfname)
winpr_DeleteFile(utfname);
if (uniname)
winpr_DeleteFile(uniname);
if (base)
winpr_RemoveDirectory(base);
free(utfname);
free(uniname);
free(base);
free(tmp);
freerdp_client_rdp_file_free(file);
freerdp_settings_free(settings);
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
This directory should contain RDP files that are checked by TestClientRdpFile
Adding new test works like this:
1. place a .rdp file in this folder
2. run TestClient TestClientRdpFile generate to generate a settings JSON from the RDP file
3. git add .rdp .json && git commit .
4. now the unit test is run and the result is compared

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
gatewayusagemethod:i:1
gatewayprofileusagemethod:i:1
authentication level:i:1
gatewaybrokeringtype:i:1
wvd endpoint pool:s:11112222-0815-1234-abcd-123456789abc
geo:s:EU
armpath:s:/subscriptions/584e4430-82fd-41aa-b87a-efc8b62f890c/resourcegroups/f6cfbbf2-efed-4756-a9c8-3c547bc4eb50/providers/Microsoft.DesktopVirtualization/hostpools/3367af18e772425b82d958c4f06d8023
aadtenantid:s:77064fd5-2634-4a0d-b310-2fa3c1d0472d
full address:s:rdgateway-r1.wvd.microsoft.com
alternate full address:s:rdgateway-r1.wvd.microsoft.com
diagnosticserviceurl:s:https://rdweb-g-eu-r1.wvd.microsoft.com/api/arm/DiagnosticEvents/v1
hubdiscoverygeourl:s:https://rdweb-g-eu-r1.wvd.microsoft.com/api/arm/hubdiscovery?resourceId=16e1fb18-0f41-4e0c-acf8-5046124affe9
resourceprovider:s:arm
gatewayhostname:s:afdfp-rdgateway-r1.wvd.microsoft.com:443
loadbalanceinfo:s:mth://localhost/b47b47c1-b81e-4dfa-ac1b-a8a195d43f26/16e1fb18-0f41-4e0c-acf8-5046124affe9
workspace id:s:cd2471f8-135b-405b-ac04-c2e1564ef354
activityhint:s:ms-wvd-ep:16e1fb18-0f41-4e0c-acf8-5046124affe9?ScaleUnitPath={"Geo"%3a"EU"%2c"Ring"%3a1%2c"Region"%3a"westeurope"%2c"ScaleUnit"%3a100}
promptcredentialonce:i:1
gatewaycredentialssource:i:0
remoteapplicationprogram:s:||40d51148-8d2c-4222-aeb3-7ad10be11650
remotedesktopname:s:Cloud PC Enterprise 8vCPU/32GB/512GB
remoteapplicationmode:i:0
audiomode:i:0
redirectclipboard:i:1
redirectprinters:i:1
redirectsmartcards:i:1
dynamic resolution:i:1
audiocapturemode:i:1
camerastoredirect:s:*
devicestoredirect:s:*
redirectcomports:i:1
drivestoredirect:s:*
usbdevicestoredirect:s:*
singlemoninwindowedmode:i:1
redirectlocation:i:1
targetisaadjoined:i:1
enablerdsaadauth:i:0
clientrejectinjectedinput:i:0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
gatewayusagemethod:i:1
gatewayprofileusagemethod:i:1
authentication level:i:1
gatewaybrokeringtype:i:1
wvd endpoint pool:s:11112222-0815-1234-abcd-123456789abc
geo:s:EU
armpath:s:/subscriptions/584e4430-82fd-41aa-b87a-efc8b62f890c/resourcegroups/f6cfbbf2-efed-4756-a9c8-3c547bc4eb50/providers/Microsoft.DesktopVirtualization/hostpools/3367af18e772425b82d958c4f06d8023
aadtenantid:s:77064fd5-2634-4a0d-b310-2fa3c1d0472d
full address:s:rdgateway-r1.wvd.microsoft.com
rdgiskdcproxy:i:1
alternate full address:s:rdgateway-r1.wvd.microsoft.com
diagnosticserviceurl:s:https://rdweb-g-eu-r1.wvd.microsoft.com/api/arm/DiagnosticEvents/v1
hubdiscoverygeourl:s:https://rdweb-g-eu-r1.wvd.microsoft.com/api/arm/hubdiscovery?resourceId=16e1fb18-0f41-4e0c-acf8-5046124affe9
resourceprovider:s:arm
gatewayhostname:s:afdfp-rdgateway-r1.wvd.microsoft.com:443
loadbalanceinfo:s:mth://localhost/b47b47c1-b81e-4dfa-ac1b-a8a195d43f26/16e1fb18-0f41-4e0c-acf8-5046124affe9
workspace id:s:cd2471f8-135b-405b-ac04-c2e1564ef354
activityhint:s:ms-wvd-ep:16e1fb18-0f41-4e0c-acf8-5046124affe9?ScaleUnitPath={"Geo"%3a"EU"%2c"Ring"%3a1%2c"Region"%3a"westeurope"%2c"ScaleUnit"%3a100}
promptcredentialonce:i:1
gatewaycredentialssource:i:0
remoteapplicationprogram:s:||40d51148-8d2c-4222-aeb3-7ad10be11650
remotedesktopname:s:Cloud PC Enterprise 8vCPU/32GB/512GB
remoteapplicationmode:i:0
audiomode:i:0
redirectclipboard:i:1
redirectprinters:i:1
redirectsmartcards:i:1
dynamic resolution:i:1
audiocapturemode:i:1
camerastoredirect:s:*
devicestoredirect:s:*
redirectcomports:i:1
drivestoredirect:s:*
usbdevicestoredirect:s:*
singlemoninwindowedmode:i:1
redirectlocation:i:1
targetisaadjoined:i:1
enablerdsaadauth:i:0
clientrejectinjectedinput:i:0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
gatewayusagemethod:i:1
gatewayprofileusagemethod:i:1
authentication level:i:1
gatewaybrokeringtype:i:1
wvd endpoint pool:s:11112222-0815-1234-abcd-123456789abc
geo:s:EU
armpath:s:/subscriptions/584e4430-82fd-41aa-b87a-efc8b62f890c/resourcegroups/f6cfbbf2-efed-4756-a9c8-3c547bc4eb50/providers/Microsoft.DesktopVirtualization/hostpools/3367af18e772425b82d958c4f06d8023
aadtenantid:s:77064fd5-2634-4a0d-b310-2fa3c1d0472d
full address:s:rdgateway-r1.wvd.microsoft.com
rdgiskdcproxy:i:0
alternate full address:s:rdgateway-r1.wvd.microsoft.com
diagnosticserviceurl:s:https://rdweb-g-eu-r1.wvd.microsoft.com/api/arm/DiagnosticEvents/v1
hubdiscoverygeourl:s:https://rdweb-g-eu-r1.wvd.microsoft.com/api/arm/hubdiscovery?resourceId=16e1fb18-0f41-4e0c-acf8-5046124affe9
resourceprovider:s:arm
gatewayhostname:s:afdfp-rdgateway-r1.wvd.microsoft.com:443
loadbalanceinfo:s:mth://localhost/b47b47c1-b81e-4dfa-ac1b-a8a195d43f26/16e1fb18-0f41-4e0c-acf8-5046124affe9
workspace id:s:cd2471f8-135b-405b-ac04-c2e1564ef354
activityhint:s:ms-wvd-ep:16e1fb18-0f41-4e0c-acf8-5046124affe9?ScaleUnitPath={"Geo"%3a"EU"%2c"Ring"%3a1%2c"Region"%3a"westeurope"%2c"ScaleUnit"%3a100}
promptcredentialonce:i:1
gatewaycredentialssource:i:0
remoteapplicationprogram:s:||40d51148-8d2c-4222-aeb3-7ad10be11650
remotedesktopname:s:Cloud PC Enterprise 8vCPU/32GB/512GB
remoteapplicationmode:i:0
audiomode:i:0
redirectclipboard:i:1
redirectprinters:i:1
redirectsmartcards:i:1
dynamic resolution:i:1
audiocapturemode:i:1
camerastoredirect:s:*
devicestoredirect:s:*
redirectcomports:i:1
drivestoredirect:s:*
usbdevicestoredirect:s:*
singlemoninwindowedmode:i:1
redirectlocation:i:1
targetisaadjoined:i:1
enablerdsaadauth:i:0
clientrejectinjectedinput:i:0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff