diff options
author | sss <sss@dark-alexandr.net> | 2023-01-17 00:38:19 +0300 |
---|---|---|
committer | sss <sss@dark-alexandr.net> | 2023-01-17 00:38:19 +0300 |
commit | cc3f33db7a8d3c4ad373e646b199808e01bc5d9b (patch) | |
tree | ec09d690c7656ab5f2cc72607e05fb359c24d8b2 /src/rdp |
added webrdp public code
Diffstat (limited to 'src/rdp')
l--------- | src/rdp/.clang-format | 1 | ||||
-rw-r--r-- | src/rdp/CMakeLists.txt | 82 | ||||
l--------- | src/rdp/include/.clang-format | 1 | ||||
-rw-r--r-- | src/rdp/include/rdp_backend_api.h | 17 | ||||
-rw-r--r-- | src/rdp/rdp.project | 138 | ||||
-rw-r--r-- | src/rdp/rdp_channels.c | 39 | ||||
-rw-r--r-- | src/rdp/rdp_channels.h | 17 | ||||
-rw-r--r-- | src/rdp/rdp_clipboard.c | 1524 | ||||
-rw-r--r-- | src/rdp/rdp_clipboard.h | 52 | ||||
-rw-r--r-- | src/rdp/rdp_display_output.c | 871 | ||||
-rw-r--r-- | src/rdp/rdp_display_output.h | 17 | ||||
-rw-r--r-- | src/rdp/rdp_ft.c | 129 | ||||
-rw-r--r-- | src/rdp/rdp_ft.h | 17 | ||||
-rw-r--r-- | src/rdp/rdp_impl.c | 637 | ||||
-rw-r--r-- | src/rdp/rdp_impl.h | 127 | ||||
-rw-r--r-- | src/rdp/rdp_io.c | 1492 | ||||
-rw-r--r-- | src/rdp/rdp_module.c | 168 | ||||
-rw-r--r-- | src/rdp/rdp_module.h | 7 | ||||
-rw-r--r-- | src/rdp/rdp_png.c | 89 | ||||
-rw-r--r-- | src/rdp/rdp_png.h | 22 | ||||
-rw-r--r-- | src/rdp/rdp_rail.c | 173 | ||||
-rw-r--r-- | src/rdp/rdp_rail.h | 14 | ||||
-rw-r--r-- | src/rdp/rdp_settings.c | 2278 | ||||
-rw-r--r-- | src/rdp/rdp_settings.h | 15 | ||||
-rw-r--r-- | src/rdp/rdp_user_input.c | 458 | ||||
-rw-r--r-- | src/rdp/rdp_user_input.h | 10 |
26 files changed, 8395 insertions, 0 deletions
diff --git a/src/rdp/.clang-format b/src/rdp/.clang-format new file mode 120000 index 0000000..2d11237 --- /dev/null +++ b/src/rdp/.clang-format @@ -0,0 +1 @@ +../../.clang-format
\ No newline at end of file diff --git a/src/rdp/CMakeLists.txt b/src/rdp/CMakeLists.txt new file mode 100644 index 0000000..29e7b9a --- /dev/null +++ b/src/rdp/CMakeLists.txt @@ -0,0 +1,82 @@ +# -*- CMakeLists.txt generated by CodeLite IDE. Do not edit by hand -*- + +cmake_minimum_required(VERSION 2.8.11) + +project(rdp) + +# Define some variables +set(PROJECT_rdp_PATH "${CMAKE_CURRENT_LIST_DIR}") +set(WORKSPACE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") + + + +#{{{{ User Code 1 +# Place your code here +#}}}} + +include_directories( + . + ./include + ../../3rdparty/FreeRDP/include + ../../3rdparty/FreeRDP/winpr + ../../3rdparty/FreeRDP/winpr/include + ../../3rdparty/FreeRDP/build/winpr/include + ../../3rdparty/FreeRDP/build/include + ../../3rdparty/libev + ../core/include +) + + +# Compiler options +add_definitions(-Wall) +add_definitions(-D_XOPEN_SOURCE=500) +add_definitions(-D_POSIX_C_SOURCE=200112L) + +# Linker options + + +if(WIN32) + # Resource options +endif(WIN32) + +# Library path +set(CMAKE_LDFLAGS "${CMAKE_LDFLAGS} -L. ") + +# Define the C sources +set ( C_SRCS + ${CMAKE_CURRENT_LIST_DIR}/rdp_module.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_impl.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_png.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_settings.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_channels.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_display_output.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_clipboard.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_ft.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_user_input.c + ${CMAKE_CURRENT_LIST_DIR}/rdp_rail.c +) + +set_source_files_properties( + ${C_SRCS} PROPERTIES COMPILE_FLAGS + " -Wall -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200112L -fPIC") + +if(WIN32) + enable_language(RC) + set(CMAKE_RC_COMPILE_OBJECT + "<CMAKE_RC_COMPILER> ${RC_OPTIONS} -O coff -i <SOURCE> -o <OBJECT>") +endif(WIN32) + + + +#{{{{ User Code 2 +# Place your code here +#}}}} + +add_library(rdp ${RC_SRCS} ${CXX_SRCS} ${C_SRCS}) + + + +#{{{{ User Code 3 +# Place your code here +#}}}} + diff --git a/src/rdp/include/.clang-format b/src/rdp/include/.clang-format new file mode 120000 index 0000000..3260daf --- /dev/null +++ b/src/rdp/include/.clang-format @@ -0,0 +1 @@ +../../../.clang-format
\ No newline at end of file diff --git a/src/rdp/include/rdp_backend_api.h b/src/rdp/include/rdp_backend_api.h new file mode 100644 index 0000000..4840765 --- /dev/null +++ b/src/rdp/include/rdp_backend_api.h @@ -0,0 +1,17 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +typedef enum +{ + rdp_conn_state_offline, + rdp_conn_state_connected +} rdp_connection_state; + +bool rdp_create(wrdp_core_exports *core, wrdp_backend_module *module); + +void rdp_backend_destroy(void *backend); diff --git a/src/rdp/rdp.project b/src/rdp/rdp.project new file mode 100644 index 0000000..30925d4 --- /dev/null +++ b/src/rdp/rdp.project @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CodeLite_Project Name="rdp" Version="11000" InternalType="Library"> + <Plugins> + <Plugin Name="qmake"> + <![CDATA[00010001N0005Debug000000000000]]> + </Plugin> + </Plugins> + <Description/> + <Dependencies/> + <VirtualDirectory Name="src"> + <File Name="rdp_rail.h"/> + <File Name="rdp_rail.c"/> + <File Name="rdp_ft.h"/> + <File Name="rdp_ft.c"/> + <File Name="rdp_display_output.h"/> + <File Name="rdp_display_output.c"/> + <File Name="rdp_user_input.h"/> + <File Name="rdp_user_input.c"/> + <File Name="rdp_channels.h"/> + <File Name="rdp_channels.c"/> + <File Name="rdp_clipboard.h"/> + <File Name="rdp_clipboard.c"/> + <File Name="rdp_settings.h"/> + <File Name="rdp_settings.c"/> + <File Name="rdp_png.c"/> + <File Name="rdp_png.h"/> + <File Name="rdp_impl.c"/> + <File Name="rdp_impl.h"/> + <File Name="rdp_module.c"/> + <File Name="rdp_module.h"/> + </VirtualDirectory> + <VirtualDirectory Name="include"> + <File Name="include/rdp_backend_api.h"/> + </VirtualDirectory> + <Settings Type="Static Library"> + <GlobalSettings> + <Compiler Options="-fPIC" C_Options="-D_XOPEN_SOURCE=500;-D_POSIX_C_SOURCE=200112L" Assembler=""> + <IncludePath Value="."/> + <IncludePath Value="./include"/> + <IncludePath Value="../../3rdparty/FreeRDP/include"/> + <IncludePath Value="../../3rdparty/libev"/> + <IncludePath Value="../../3rdparty/FreeRDP/winpr"/> + <IncludePath Value="../../3rdparty/FreeRDP/winpr/include"/> + <IncludePath Value="../../3rdparty/FreeRDP/build/winpr/include"/> + <IncludePath Value="../../3rdparty/FreeRDP/build/include"/> + <IncludePath Value="../core/include"/> + <IncludePath Value="/home/sss/.nix-profile/include"/> + </Compiler> + <Linker Options=""> + <LibraryPath Value="."/> + </Linker> + <ResourceCompiler Options=""/> + </GlobalSettings> + <Configuration Name="Debug" CompilerType="clang" DebuggerType="GNU gdb debugger" Type="Static Library" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append"> + <Compiler Options="-g;-Wall;-fsanitize=address;-O0" C_Options="-pg;-g;-Wall;-DDEBUG;-fsanitize=address;-O0" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0"/> + <Linker Options="-pg" Required="yes"/> + <ResourceCompiler Options="" Required="no"/> + <General OutputFile="$(IntermediateDirectory)/$(ProjectName).a" IntermediateDirectory="./Debug" Command="" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/> + <BuildSystem Name="Default"/> + <Environment EnvVarSetName="<Use Defaults>" DbgSetName="<Use Defaults>"> + <![CDATA[]]> + </Environment> + <Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath="" IsExtended="yes"> + <DebuggerSearchPaths/> + <PostConnectCommands/> + <StartupCommands/> + </Debugger> + <PreBuild/> + <PostBuild/> + <CustomBuild Enabled="no"> + <RebuildCommand/> + <CleanCommand/> + <BuildCommand/> + <PreprocessFileCommand/> + <SingleFileCommand/> + <MakefileGenerationCommand/> + <ThirdPartyToolName/> + <WorkingDirectory/> + </CustomBuild> + <AdditionalRules> + <CustomPostBuild/> + <CustomPreBuild/> + </AdditionalRules> + <Completion EnableCpp11="no" EnableCpp14="no"> + <ClangCmpFlagsC/> + <ClangCmpFlags/> + <ClangPP/> + <SearchPaths/> + </Completion> + </Configuration> + <Configuration Name="Release" CompilerType="clang" DebuggerType="GNU gdb debugger" Type="Static Library" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append"> + <Compiler Options="-Wall;-fPIC" C_Options="-std=c99;-Wall;-D_XOPEN_SOURCE=500;-D_POSIX_C_SOURCE=200112L" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0"> + <IncludePath Value="."/> + <IncludePath Value="./include"/> + <IncludePath Value="../../3rdparty/FreeRDP/include"/> + <IncludePath Value="../../3rdparty/FreeRDP/winpr/include"/> + <IncludePath Value="../../3rdparty/FreeRDP/build/winpr/include"/> + <IncludePath Value="../../3rdparty/FreeRDP/build/include"/> + <IncludePath Value="../core/include"/> + <Preprocessor Value="NDEBUG"/> + </Compiler> + <Linker Options="-O2" Required="yes"/> + <ResourceCompiler Options="" Required="no"/> + <General OutputFile="$(IntermediateDirectory)/$(ProjectName).a" IntermediateDirectory="./Release" Command="" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/> + <BuildSystem Name="Default"/> + <Environment EnvVarSetName="<Use Defaults>" DbgSetName="<Use Defaults>"> + <![CDATA[]]> + </Environment> + <Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath="" IsExtended="yes"> + <DebuggerSearchPaths/> + <PostConnectCommands/> + <StartupCommands/> + </Debugger> + <PreBuild/> + <PostBuild/> + <CustomBuild Enabled="no"> + <RebuildCommand/> + <CleanCommand/> + <BuildCommand/> + <PreprocessFileCommand/> + <SingleFileCommand/> + <MakefileGenerationCommand/> + <ThirdPartyToolName/> + <WorkingDirectory/> + </CustomBuild> + <AdditionalRules> + <CustomPostBuild/> + <CustomPreBuild/> + </AdditionalRules> + <Completion EnableCpp11="no" EnableCpp14="no"> + <ClangCmpFlagsC/> + <ClangCmpFlags/> + <ClangPP/> + <SearchPaths/> + </Completion> + </Configuration> + </Settings> +</CodeLite_Project> diff --git a/src/rdp/rdp_channels.c b/src/rdp/rdp_channels.c new file mode 100644 index 0000000..a4d95eb --- /dev/null +++ b/src/rdp/rdp_channels.c @@ -0,0 +1,39 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <string.h> +#include "rdp_channels.h" +#include "rdp_clipboard.h" +#include "rdp_rail.h" + +void +rdp_on_channel_connected(void *context, const ChannelConnectedEventArgs *e) +{ + if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) + { + rdp_cliprdr_init( + context, (CliprdrClientContext *)e->pInterface); + } + else if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) == 0) + { + rdp_rail_init(context, (RailClientContext *)e->pInterface); + } +} + +void +rdp_on_channel_disconnected( + void *context, const ChannelDisconnectedEventArgs *e) +{ + if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) + { + rdp_cliprdr_uninit( + context, (CliprdrClientContext *)e->pInterface); + } + else if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) == 0) + { + rdp_rail_uninit(context, (RailClientContext *)e->pInterface); + } +} diff --git a/src/rdp/rdp_channels.h b/src/rdp/rdp_channels.h new file mode 100644 index 0000000..2d895fb --- /dev/null +++ b/src/rdp/rdp_channels.h @@ -0,0 +1,17 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +#include <freerdp/client/channels.h> +#include <freerdp/client/cliprdr.h> +#include <freerdp/client/rail.h> + +void rdp_on_channel_connected( + void *context, const ChannelConnectedEventArgs *e); + +void rdp_on_channel_disconnected( + void *context, const ChannelDisconnectedEventArgs *e); diff --git a/src/rdp/rdp_clipboard.c b/src/rdp/rdp_clipboard.c new file mode 100644 index 0000000..e8ab882 --- /dev/null +++ b/src/rdp/rdp_clipboard.c @@ -0,0 +1,1524 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +/* this code mostly ported from XFreeRDP */ + +#include <arpa/inet.h> +#include <iconv.h> + +#include "rdp_clipboard.h" +#include "rdp_ft.h" + +static UINT +rdp_clip_ServerCapabilities( + CliprdrClientContext *context, const CLIPRDR_CAPABILITIES *capabilities) +{ + UINT32 i; + const CLIPRDR_CAPABILITY_SET *caps; + const CLIPRDR_GENERAL_CAPABILITY_SET *generalCaps; + const BYTE *capsPtr = (const BYTE *)capabilities->capabilitySets; + my_rdp_clipboard *clipboard = (my_rdp_clipboard *)context->custom; + clipboard->streams_supported = false; + + for (i = 0; i < capabilities->cCapabilitiesSets; i++) + { + caps = (const CLIPRDR_CAPABILITY_SET *)capsPtr; + + if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL) + { + generalCaps + = (const CLIPRDR_GENERAL_CAPABILITY_SET *)caps; + + if (generalCaps->generalFlags + & CB_STREAM_FILECLIP_ENABLED) + { + clipboard->streams_supported = true; + } + } + + capsPtr += caps->capabilitySetLength; + } + { + const char *msg + = "rdp_module: cliprdr: server capabilities received"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_trace, 0); + } + + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_ClientCapabilities(CliprdrClientContext* context, + CLIPRDR_CAPABILITIES* capabilities) +{ + return CHANNEL_RC_OK; +}*/ + +static UINT +rdp_cliprdr_send_client_capabilities(my_rdp_clipboard *clipboard) +{ + CLIPRDR_CAPABILITIES capabilities; + CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet; + capabilities.cCapabilitiesSets = 1; + capabilities.capabilitySets + = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet); + generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL; + generalCapabilitySet.capabilitySetLength = 12; + generalCapabilitySet.version = CB_CAPS_VERSION_2; + generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES; + /*|CB_CAN_LOCK_CLIPDATA; */ + + if (clipboard->streams_supported) + { + generalCapabilitySet.generalFlags |= CB_STREAM_FILECLIP_ENABLED + | CB_FILECLIP_NO_FILE_PATHS + | 0x00000020; + } + + { + const char *msg + = "rdp_module: cliprdr: client capabilities sent"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_trace, 0); + } + + return clipboard->clip_context->ClientCapabilities( + clipboard->clip_context, &capabilities); +} + +static UINT +rdp_cliprdr_send_client_format_list(my_rdp_clipboard *clipboard) +{ + const uint8_t format_count = 2; + CLIPRDR_FORMAT formats[format_count]; + CLIPRDR_FORMAT_LIST formatList; + memset(formats, 0, sizeof(CLIPRDR_FORMAT) * format_count); + formats[0].formatId = CF_RAW; + formats[1].formatId = CF_TEXT; + formatList.common.msgFlags = CB_RESPONSE_OK; + formatList.numFormats = format_count; + formatList.formats = formats; + + { + const char *msg + = "rdp_module: cliprdr: client format list sent"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_trace, 0); + } + + return clipboard->clip_context->ClientFormatList( + clipboard->clip_context, &formatList); +} + +static UINT +rdp_clip_MonitorReady( + CliprdrClientContext *context, const CLIPRDR_MONITOR_READY *monitorReady) +{ + my_rdp_clipboard *clipboard = context->custom; + UINT ret; + + if ((ret = rdp_cliprdr_send_client_capabilities(clipboard)) + != CHANNEL_RC_OK) + return ret; + + if ((ret = rdp_cliprdr_send_client_format_list(clipboard)) + != CHANNEL_RC_OK) + return ret; + + /* clipboard->sync = true; */ + { + const char *msg + = "rdp_module: cliprdr: monitor ready message handled"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_trace, 0); + } + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_TempDirectory(CliprdrClientContext* context, + CLIPRDR_TEMP_DIRECTORY* tempDirectory) +{ + return CHANNEL_RC_OK; +} */ + +/* unused for now + * static UINT +rdp_clip_ClientFormatList(CliprdrClientContext* context, + CLIPRDR_FORMAT_LIST* formatList) +{ + return CHANNEL_RC_OK; +}*/ + +static UINT +rdp_cliprdr_send_client_format_list_response( + my_rdp_clipboard *clipboard, BOOL status) +{ + CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse; + formatListResponse.common.msgType = CB_FORMAT_LIST_RESPONSE; + formatListResponse.common.msgFlags + = status ? CB_RESPONSE_OK : CB_RESPONSE_FAIL; + formatListResponse.common.dataLen = 0; + return clipboard->clip_context->ClientFormatListResponse( + clipboard->clip_context, &formatListResponse); +} + +static wrdp_enum_clip_format +clip_format_from_string(const char *fmt) +{ + if (!strcmp(fmt, "FileGroupDescriptorW")) + { + return clip_format_file_list; + } + /* + * unused + else if (!strcmp(fmt, "FileContents")) + { + } + else if (!strcmp(fmt, "Preffered DropEffect")) + { + } + */ + return clip_format_unsupported; +} + +static wrdp_enum_clip_format +clip_format_from_id(UINT32 id, my_rdp_clipboard *c) +{ + if (id == CF_TEXT || id == CF_OEMTEXT) + { + return clip_format_text; + } + else if (id == CF_RAW) + { + return clip_format_raw; + } + else if (id == CF_UNICODETEXT) + { + return clip_format_unicode; + } + else if (id == CB_FORMAT_TEXTURILIST) + { + return clip_format_file_list; + } + return clip_format_unsupported; +} + +static UINT +rdp_clip_ServerFormatList( + CliprdrClientContext *context, const CLIPRDR_FORMAT_LIST *formatList) +{ + UINT32 i; + uint8_t num_supported_fmts = 0, *out_fmts = 0, num_server_formats; + my_rdp_clipboard *clipboard = context->custom; + my_clip_format *server_formats = 0; + UINT ret; + + if (clipboard->my_rdp_context->ft_to_server->is_runing) + { + const char *msg + = "rdp_module: clipboard: new clipboard formats list " + "during runing filetransfer"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + /* return CHANNEL_RC_OK; */ + } + + num_server_formats = formatList->numFormats + 1; /* +1 for CF_RAW */ + + if (!(server_formats + = calloc(num_server_formats, sizeof(my_clip_format)))) + { + char buf[128]; + snprintf(buf, 127, + "failed to allocate %d my_clip_format structs", + num_server_formats); + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)buf, strlen(buf), + wrdp_log_level_error, 0); + return CHANNEL_RC_NO_MEMORY; + } + + if (clipboard->srv_fmts && clipboard->srv_fmts_count) + { + for (i = 0; i < clipboard->srv_fmts_count; ++i) + { + if (clipboard->srv_fmts[i].rdp_fmt.formatName) + { + free(clipboard->srv_fmts[i].rdp_fmt.formatName); + } + } + free(clipboard->srv_fmts); + } + + clipboard->srv_fmts_count = formatList->numFormats; + for (i = 0; i < formatList->numFormats; i++) + { + CLIPRDR_FORMAT *format = &formatList->formats[i]; + server_formats[i].rdp_fmt.formatId = format->formatId; + wrdp_enum_clip_format fmt + = clip_format_from_id(format->formatId, clipboard); + if (format->formatName) + { + server_formats[i].rdp_fmt.formatName + = strdup(format->formatName); + } + if (fmt == clip_format_unsupported && format->formatName) + { + fmt = clip_format_from_string(format->formatName); + } + if (fmt != clip_format_unsupported) + { + num_supported_fmts++; + } + server_formats[i].my_fmt = fmt; + } + /* CF_RAW is always implicitly supported by the server */ + { + my_clip_format *format + = &server_formats[formatList->numFormats]; + format->rdp_fmt.formatId = CF_RAW; + format->rdp_fmt.formatName = NULL; + format->my_fmt = clip_format_raw; + num_supported_fmts++; + } + { + out_fmts = calloc(num_supported_fmts, sizeof(uint8_t)); + if (!out_fmts) + { + perror("calloc"); + if (server_formats) + { + free(server_formats); + } + return CHANNEL_RC_NO_MEMORY; + } + uint8_t pos = 0; + for (i = 0; i < num_supported_fmts; ++i) + { + if (server_formats[i].my_fmt == clip_format_unsupported) + { + continue; + } + out_fmts[pos] = server_formats[i].my_fmt; + pos++; + } + clipboard->my_rdp_context->my_internals->core->api_clipboard + ->clipboard_changed(out_fmts, num_supported_fmts, + clipboard->my_rdp_context->my_internals->task_info); + free(out_fmts); + clipboard->srv_fmts = server_formats; + } + + { + const char *msg = "rdp_module: cliprdr: server format list" + " changed message received"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_trace, 0); + } + + ret = rdp_cliprdr_send_client_format_list_response(clipboard, TRUE); + return ret; +} + +/* unused for now + * static UINT +rdp_clip_ClientFormatListResponse(CliprdrClientContext* context, + CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse) +{ + return CHANNEL_RC_OK; +}*/ + +static UINT +rdp_clip_ServerFormatListResponse(CliprdrClientContext *context, + const CLIPRDR_FORMAT_LIST_RESPONSE *formatListResponse) +{ + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_ClientLockClipboardData(CliprdrClientContext* context, + CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData) +{ + return CHANNEL_RC_OK; +}*/ + +static UINT +rdp_clip_ServerLockClipboardData(CliprdrClientContext *context, + const CLIPRDR_LOCK_CLIPBOARD_DATA *lockClipboardData) +{ + /* my_rdp_clipboard* clipboard = context->custom; + clipboard->my_rdp_context->ft_to_server->clip_data_id = + lockClipboardData->clipDataId; */ + return CHANNEL_RC_OK; +} + +/* + * unused +static UINT +rdp_clip_ClientUnlockClipboardData(CliprdrClientContext* context, + CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData) +{ + return CHANNEL_RC_OK; +} +*/ + +static UINT +rdp_clip_ServerUnlockClipboardData(CliprdrClientContext *context, + const CLIPRDR_UNLOCK_CLIPBOARD_DATA *unlockClipboardData) +{ + /* TODO: */ + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_ClientFormatDataRequest(CliprdrClientContext* context, + CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest) +{ + return CHANNEL_RC_OK; +}*/ + +static UINT +rdp_clip_ServerFormatDataRequest(CliprdrClientContext *context, + const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest) +{ + my_rdp_clipboard *clipboard = context->custom; + wrdp_enum_clip_format fmt = clip_format_from_id( + formatDataRequest->requestedFormatId, clipboard); + clipboard->my_rdp_context->my_internals->core->api_clipboard + ->request_data( + fmt, clipboard->my_rdp_context->my_internals->task_info); + + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_ClientFormatDataResponse(CliprdrClientContext* context, + CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse) +{ + return CHANNEL_RC_OK; +}*/ + +/*static UINT rdp_cliprdr_send_data_response(my_rdp_clipboard* clipboard, + BYTE* data, int size) +{ + CLIPRDR_FORMAT_DATA_RESPONSE response; + ZeroMemory(&response, sizeof(CLIPRDR_FORMAT_DATA_RESPONSE)); + response.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL; + response.dataLen = size; + response.requestedFormatData = data; + return clipboard->clip_context->ClientFormatDataResponse( + clipboard->clip_context, &response); +} */ + +static UINT +rdp_clip_ServerFormatDataResponse(CliprdrClientContext *context, + const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse) +{ + UINT32 size = formatDataResponse->common.dataLen; + const BYTE *data = formatDataResponse->requestedFormatData; + my_rdp_clipboard *clipboard = context->custom; + + if (!data) + { + const char *msg + = "rdp_module: cliprdr: failed to get clipboard data"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + return CHANNEL_RC_OK; + } + + switch (clipboard->cli_req_fmt_id.my_fmt) + { + case clip_format_file_list: + { + /* TODO: check this */ + UINT error = NO_ERROR; + FILEDESCRIPTORW *file_array = 0; + UINT32 file_count = 0, pos = 0; + uint8_t *msg_struct = 0, *msg = 0; + wrdp_backend_ft_list_entry *list = 0; + size_t msg_size_result = 0; + error = cliprdr_parse_file_list( + data, size, &file_array, &file_count); + if (error) + { + char buf[128]; + snprintf(buf, 127, + "failed to deserialize" + " CLIPRDR_FILELIST: 0x%08X", + error); + clipboard->my_rdp_context->my_internals->core + ->api_utils->log_msg((const uint8_t *)buf, + strlen(buf), wrdp_log_level_error, 0); + return CHANNEL_RC_OK; + } + msg_struct = calloc( + file_count, sizeof(wrdp_backend_ft_list_entry)); + if (!msg_struct) + { + perror("calloc"); + if (file_count && file_array) + { + free(file_array); + } + return CHANNEL_RC_OK; + } + msg_size_result + = (file_count * sizeof(wrdp_backend_ft_list_entry)) + + 3; + char *filename_array[file_count]; + { + int i = 0; + for (; i < file_count; ++i) + { + filename_array[i] = 0; + } + } + list = (wrdp_backend_ft_list_entry *)msg_struct; + for (; file_array && pos < file_count; ++pos) + { + list[pos].file_id = pos; + if (file_array[pos].cFileName[0]) + { + iconv_t utf16_to_utf8 + = iconv_open("UTF-8", "UTF-16LE"); + char *ptr_w, *ptr_r, + *tmp_buf + = calloc(520, sizeof(char)); + size_t name_len_utf16 + = 260 * sizeof(WCHAR), + name_len_utf8 = 520, + name_len_result = 0; + if (!tmp_buf) + { + perror("calloc"); + iconv_close(utf16_to_utf8); + free(msg_struct); + if (file_count) + { + int i = 0; + for (; (i < file_count) + && filename_array + [i]; + ++i) + { + free( + filename_array + [i]); + } + free(file_array); + } + return CHANNEL_RC_OK; + } + ptr_w = tmp_buf; + ptr_r + = (char *)file_array[pos].cFileName; + while (name_len_utf16 && name_len_utf8) + { + iconv(utf16_to_utf8, &ptr_r, + &name_len_utf16, &ptr_w, + &name_len_utf8); + } +#ifdef DEBUG + if (!name_len_utf8 && name_len_utf16) + { + log_msg_info i = {0}; + i.level = wrdp_log_level_trace; + i.buf = (const uint8_t + *)"rdp_module: " + "cliprdr: iconv: not " + "sufficient space in " + "output buffer"; + i.task_info + = clipboard->my_internals + ->task_info; + clipboard->my_internals->core + ->api_utils->log_msg_ex(&i); + } +#endif + iconv(utf16_to_utf8, 0, 0, &ptr_w, + &name_len_utf8); + name_len_result = strlen(tmp_buf); + msg_size_result += name_len_result; + list[pos].filename_len + = name_len_result; + filename_array[pos] = strdup(tmp_buf); + iconv_close(utf16_to_utf8); + free(tmp_buf); + } + /* TODO: check/change endianness */ + memcpy(&(list[pos].file_size), + &file_array[pos].nFileSizeLow, 4); + memcpy(((uint8_t *)&(list[pos].file_size)) + 4, + &file_array[pos].nFileSizeHigh, 4); + } + msg = malloc(msg_size_result); + if (!msg) + { + perror("malloc"); + for (pos = 0; pos < file_count; ++pos) + { + free(filename_array[pos]); + } + free(msg_struct); + return CHANNEL_RC_OK; + } + memset(msg, 0, msg_size_result); + *((uint8_t *)msg) = clip_format_file_list; + memcpy(msg + 1, &file_count, 2); + { + size_t msg_pos = 3; + for (pos = 0; pos < file_count; ++pos) + { + memcpy(msg + msg_pos, + &list[pos].filename_len, 2); + msg_pos += 2; + memcpy(msg + msg_pos, + &list[pos].file_id, 4); + msg_pos += 4; + memcpy(msg + msg_pos, + &list[pos].file_size, 8); + msg_pos += 8; + /* TODO: looks like i have missed + * something ... "Null pointer passed as + * an argument to a 'nonnull' parameter" + * just added null ptr check for now */ + if (filename_array[pos]) + { + memcpy(msg + msg_pos, + filename_array[pos], + list[pos].filename_len); + free(filename_array[pos]); + } + msg_pos += list[pos].filename_len; + } + } + free(msg_struct); + clipboard->my_rdp_context->my_internals->core + ->api_clipboard->send_data((uint8_t *)msg, + msg_size_result, + clipboard->my_rdp_context->my_internals + ->task_info); + free(msg); + if (file_count && file_array) + { + free(file_array); + } + return CHANNEL_RC_OK; + } + break; + case clip_format_raw: + case clip_format_text: + { + size_t msg_len = size + 1; + uint8_t *msg = malloc(msg_len); + if (!msg) + { + perror("malloc"); + return CHANNEL_RC_OK; + } + *(uint8_t *)msg = clip_format_text; + memcpy(msg + 1, data, size); + clipboard->my_rdp_context->my_internals->core + ->api_clipboard->send_data(msg, msg_len, + clipboard->my_rdp_context->my_internals + ->task_info); + free(msg); + } + break; + case clip_format_unicode: + { + iconv_t utf16_to_utf8 = iconv_open("UTF-8", "UTF-16LE"); + size_t in_left = size, out_left = size; + uint8_t *tmp_buf = malloc(out_left), *tmp_buf_ptr; + uint8_t *msg = 0; + if (!tmp_buf) + { + perror("malloc"); + return CHANNEL_RC_OK; + } + tmp_buf_ptr = tmp_buf; + /* wcstombs((char*)msg + 1, tmp_buf, text_len); */ + while (in_left && out_left) + { + if (iconv(utf16_to_utf8, (char **)&data, + &in_left, (char **)&tmp_buf_ptr, + &out_left) + == (size_t)-1) + { + log_msg_info mi = {0}; + mi.level = wrdp_log_level_warning; + mi.buf = (const uint8_t + *)"rdp_module: cliprdr: iconv: " + "failed to encode outgoing " + "buffer from utf8 to utf16le"; + mi.task_info = clipboard->my_internals + ->task_info; + clipboard->my_internals->core->api_utils + ->log_msg_ex(&mi); + free(tmp_buf); + iconv_close(utf16_to_utf8); + return CHANNEL_RC_NO_BUFFER; + } + } +#ifdef DEBUG + if (!out_left && in_left) + { + log_msg_info i = {0}; + i.level = wrdp_log_level_trace; + i.buf = (const uint8_t + *)"rdp_module: cliprdr: iconv: not " + "sufficient space in output buffer"; + i.task_info + = clipboard->my_internals->task_info; + clipboard->my_internals->core->api_utils + ->log_msg_ex(&i); + } +#endif + iconv(utf16_to_utf8, 0, 0, (char **)&tmp_buf_ptr, + &out_left); + iconv_close(utf16_to_utf8); + msg = malloc(size - out_left + 1); + if (!msg) + { + perror("malloc"); + return CHANNEL_RC_OK; + } + *(uint8_t *)msg = clip_format_unicode; + memcpy(msg + 1, tmp_buf, size - out_left); +#ifdef DEBUG + { + log_msg_info i = {0}; + size_t len = 1024; + char msg_buf[len]; + snprintf(msg_buf, len - 1, + "rdp_module: cliprdr: sending encoded from " + "utf16le to utf8 text: %s", + tmp_buf); + i.level = wrdp_log_level_trace; + i.buf = (const uint8_t *)msg_buf; + i.task_info + = clipboard->my_internals->task_info; + clipboard->my_internals->core->api_utils + ->log_msg_ex(&i); + } +#endif + free(tmp_buf); + clipboard->my_rdp_context->my_internals->core + ->api_clipboard->send_data(msg, size - out_left + 1, + clipboard->my_rdp_context->my_internals + ->task_info); + free(msg); + } + break; + default: + { + const char *msg = "rdp_module: cliprdr: unsupported" + " clipboardformat requested"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + } + break; + } + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_ClientFileContentsRequest(CliprdrClientContext* context, + CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest) +{ + return CHANNEL_RC_OK; +}*/ + +static UINT +rdp_clip_ServerFileContentsRequest(CliprdrClientContext *context, + const CLIPRDR_FILE_CONTENTS_REQUEST *fileContentsRequest) +{ + my_rdp_clipboard *clipboard = context->custom; + if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE) + { + CLIPRDR_FILE_CONTENTS_RESPONSE resp; + if (fileContentsRequest->nPositionLow + || fileContentsRequest->nPositionHigh + || (fileContentsRequest->cbRequested != 0x00000008)) + { + const char *msg + = "rdp_module: ft: ServerFileContentsRequest: " + "malformed request"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + return CHANNEL_RC_OK; + } + memset(&resp, 0, sizeof(CLIPRDR_FILE_CONTENTS_RESPONSE)); + resp.common.msgType = CB_FILECONTENTS_RESPONSE; + resp.common.msgFlags = CB_RESPONSE_OK; + resp.streamId = fileContentsRequest->streamId; + resp.cbRequested = sizeof(uint64_t); + resp.requestedData = (BYTE *)&( + clipboard + ->client_filelist_cache[fileContentsRequest->listIndex] + .file_size); + clipboard->clip_context->ClientFileContentsResponse( + clipboard->clip_context, &resp); + } + else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE) + { + wrdp_backend_ft_file_request req; + memset(&req, 0, sizeof(wrdp_backend_ft_list_entry)); + /* TODO: better way to detect runing transfer */ + clipboard->my_rdp_context->ft_to_server->is_runing = true; + req.file_id = fileContentsRequest->listIndex; + req.req_size = fileContentsRequest->cbRequested; + clipboard->my_rdp_context->ft_to_server->transfer_id + = fileContentsRequest->streamId; + memcpy( + &req.file_offset, &(fileContentsRequest->nPositionLow), 4); + memcpy(((uint8_t *)&req.file_offset) + 4, + &(fileContentsRequest->nPositionHigh), 4); + clipboard->my_rdp_context->my_internals->core->api_filetransfers + ->ft_request(&req, + clipboard->my_rdp_context->my_internals->task_info); + } + return CHANNEL_RC_OK; +} + +/* unused for now + * static UINT +rdp_clip_ClientFileContentsResponse(CliprdrClientContext* context, + CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse) +{ + return CHANNEL_RC_OK; +} */ + +static UINT +rdp_clip_ServerFileContentsResponse( + CliprdrClientContext *context, const CLIPRDR_FILE_CONTENTS_RESPONSE *resp) +{ + /* TODO: somehow avoid memory copying here */ + my_rdp_clipboard *clipboard = context->custom; + wrdp_backend_ft_chunk chunk; + if (resp->common.msgFlags & CB_RESPONSE_FAIL) + { + const char *msg = "rdp_module: ft: ServerFileContentsResponse: " + "CB_RESPONSE_FAIL"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + goto transfer_failed; + } + if (!resp->cbRequested + && (clipboard->my_rdp_context->ft_to_client->transferred + != clipboard->my_rdp_context->ft_to_client->file_size)) + { + char msg[128]; + snprintf(msg, 127, + "rdp_module: ft: chunk with zero size received on" + " position %lu of %lu", + clipboard->my_rdp_context->ft_to_client->transferred, + clipboard->my_rdp_context->ft_to_client->file_size); + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + goto transfer_failed; + } + else if (clipboard->my_rdp_context->ft_to_client->transfer_id + != resp->streamId) + { + const char *msg + = "rdp_module: clipboard: ft: transfer_id mismatch in " + "ServerFileContentsResponse"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + goto transfer_failed; + } + else if (resp->cbRequested) + { + chunk.transfer_id = resp->streamId; + chunk.size = resp->cbRequested; + clipboard->my_rdp_context->my_internals->core->api_filetransfers + ->ft_send_chunk(&chunk, (uint8_t *)(resp->requestedData), + clipboard->my_rdp_context->my_internals->task_info); + clipboard->my_rdp_context->ft_to_client->transferred + += resp->cbRequested; + } + /* request next file chunk */ + if (clipboard->my_rdp_context->ft_to_client->transferred + < clipboard->my_rdp_context->ft_to_client->file_size) + { + my_rdp_context *c = clipboard->my_rdp_context; + CLIPRDR_FILE_CONTENTS_REQUEST file_req; + uint32_t chunk_len = 512000; + uint64_t left = 0; + memset(&file_req, 0, sizeof(CLIPRDR_FILE_CONTENTS_REQUEST)); + + left + = c->ft_to_client->file_size - c->ft_to_client->transferred; + if (left < chunk_len) + { + chunk_len = left; + } + file_req.cbRequested = chunk_len; + c->ft_to_client->transfer_id = rand(); + file_req.streamId = c->ft_to_client->transfer_id; + file_req.listIndex = c->ft_to_client->file_id; + file_req.dwFlags = FILECONTENTS_RANGE; + + /* TODO: check/change endianness */ + memcpy(&(file_req.nPositionLow), + &(c->ft_to_client->transferred), 4); + memcpy(&(file_req.nPositionHigh), + (((uint8_t *)&(c->ft_to_client->transferred)) + 4), 4); + c->clipboard->clip_context->ClientFileContentsRequest( + c->clipboard->clip_context, &file_req); + } + else if (clipboard->my_rdp_context->ft_to_client->is_runing == true) + { + my_rdp_context *c = clipboard->my_rdp_context; + wrdp_backend_ft_status status; + { + char msg[128]; + snprintf(msg, 127, + "rdp_module: ft: status: transfer finished," + " transfered size: %lu", + clipboard->my_rdp_context->ft_to_client + ->transferred); + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_trace, 0); + } + memset(&status, 0, sizeof(wrdp_backend_ft_status)); + status.status = ft_status_success; + status.file_id = c->ft_to_client->file_id; + status.transfer_id = c->ft_to_client->transfer_id; + c->my_internals->core->api_filetransfers->ft_finish( + &status, c->my_internals->task_info); + clipboard->my_rdp_context->ft_to_client->file_size = 0; + clipboard->my_rdp_context->ft_to_client->transfer_id = 0; + clipboard->my_rdp_context->ft_to_client->file_id = 0; + clipboard->my_rdp_context->ft_to_client->is_runing = false; + clipboard->my_rdp_context->ft_to_client->transferred = 0; + } + else + { + const char *msg = "rdp_module: clipboard: ft:" + " unexpected data chunk on finished transfer"; + clipboard->my_rdp_context->my_internals->core->api_utils + ->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + } + return CHANNEL_RC_OK; +transfer_failed: +{ + my_rdp_context *c = clipboard->my_rdp_context; + wrdp_backend_ft_status status; + memset(&status, 0, sizeof(wrdp_backend_ft_status)); + status.status = ft_status_failure; + status.file_id = c->ft_to_client->file_id; + status.transfer_id = c->ft_to_client->transfer_id; + c->my_internals->core->api_filetransfers->ft_finish( + &status, c->my_internals->task_info); + clipboard->my_rdp_context->ft_to_client->file_size = 0; + clipboard->my_rdp_context->ft_to_client->transfer_id = 0; + clipboard->my_rdp_context->ft_to_client->file_id = 0; + clipboard->my_rdp_context->ft_to_client->is_runing = false; + clipboard->my_rdp_context->ft_to_client->transferred = 0; +} + return CHANNEL_RC_OK; +} + +void +rdp_cliprdr_init(my_rdp_context *ctx, CliprdrClientContext *cliprdr) +{ + ctx->clipboard->clip_context = cliprdr; + cliprdr->custom = (void *)ctx->clipboard; + ctx->clipboard->my_internals = ctx->my_internals; + cliprdr->MonitorReady = rdp_clip_MonitorReady; + cliprdr->ServerCapabilities = rdp_clip_ServerCapabilities; + cliprdr->ServerFormatList = rdp_clip_ServerFormatList; + cliprdr->ServerFormatListResponse = rdp_clip_ServerFormatListResponse; + cliprdr->ServerFormatDataRequest = rdp_clip_ServerFormatDataRequest; + cliprdr->ServerFormatDataResponse = rdp_clip_ServerFormatDataResponse; + cliprdr->ServerFileContentsRequest = rdp_clip_ServerFileContentsRequest; + cliprdr->ServerFileContentsResponse + = rdp_clip_ServerFileContentsResponse; + cliprdr->ServerLockClipboardData = rdp_clip_ServerLockClipboardData; + cliprdr->ServerUnlockClipboardData = rdp_clip_ServerUnlockClipboardData; +} + +void +rdp_cliprdr_uninit(my_rdp_context *ctx, CliprdrClientContext *cliprdr) +{ + /* do not need to do it here, done during backend destruction */ + /* cliprdr->custom = NULL; + + if (ctx->clipboard) + ctx->clipboard->clip_context = NULL; + ctx->clipboard = NULL; */ +} + +/* unused for now + * static UINT +ClientRequestFileSize(wClipboardDelegate* delegate, + const wClipboardFileSizeRequest* request) +{ + return 0; +}*/ + +static UINT +ClipboardFileSizeSuccess(wClipboardDelegate *delegate, + const wClipboardFileSizeRequest *request, UINT64 fileSize) +{ + CLIPRDR_FILE_CONTENTS_RESPONSE response; + my_rdp_clipboard *clipboard = delegate->custom; + ZeroMemory(&response, sizeof(response)); + response.common.msgFlags = CB_RESPONSE_OK; + response.streamId = request->streamId; + response.cbRequested = sizeof(UINT64); + response.requestedData = (BYTE *)&fileSize; + return clipboard->clip_context->ClientFileContentsResponse( + clipboard->clip_context, &response); + // return 0; +} +static UINT +ClipboardFileSizeFailure(wClipboardDelegate *delegate, + const wClipboardFileSizeRequest *request, UINT errorCode) +{ + CLIPRDR_FILE_CONTENTS_RESPONSE response; + my_rdp_clipboard *clipboard = delegate->custom; + ZeroMemory(&response, sizeof(response)); + response.common.msgFlags = CB_RESPONSE_FAIL; + response.streamId = request->streamId; + return clipboard->clip_context->ClientFileContentsResponse( + clipboard->clip_context, &response); +} + +/* unused for now + * static UINT +ClientRequestFileRange(wClipboardDelegate* delegate, + const wClipboardFileRangeRequest* request) +{ + return 0; +} */ + +static UINT +ClipboardFileRangeSuccess(wClipboardDelegate *delegate, + const wClipboardFileRangeRequest *request, const BYTE *data, UINT32 size) +{ + CLIPRDR_FILE_CONTENTS_RESPONSE response; + my_rdp_clipboard *clipboard = delegate->custom; + ZeroMemory(&response, sizeof(response)); + response.common.msgFlags = CB_RESPONSE_OK; + response.streamId = request->streamId; + response.cbRequested = size; + response.requestedData = (BYTE *)data; + return clipboard->clip_context->ClientFileContentsResponse( + clipboard->clip_context, &response); +} + +static UINT +ClipboardFileRangeFailure(wClipboardDelegate *delegate, + const wClipboardFileRangeRequest *request, UINT errorCode) +{ + CLIPRDR_FILE_CONTENTS_RESPONSE response; + my_rdp_clipboard *clipboard = delegate->custom; + ZeroMemory(&response, sizeof(response)); + response.common.msgFlags = CB_RESPONSE_FAIL; + response.streamId = request->streamId; + return clipboard->clip_context->ClientFileContentsResponse( + clipboard->clip_context, &response); +} + +my_rdp_clipboard * +rdp_clipboard_new(my_rdp_context *context) +{ + my_rdp_clipboard *clip = calloc(1, sizeof(my_rdp_clipboard)); + if (!clip) + { + perror("calloc"); + return 0; + } + clip->my_rdp_context = context; + clip->clipboard = ClipboardCreate(); + clip->delegate = ClipboardGetDelegate(clip->clipboard); + clip->delegate->custom = clip; + + clip->delegate->ClipboardFileSizeSuccess = ClipboardFileSizeSuccess; + clip->delegate->ClipboardFileSizeFailure = ClipboardFileSizeFailure; + clip->delegate->ClipboardFileRangeSuccess = ClipboardFileRangeSuccess; + clip->delegate->ClipboardFileRangeFailure = ClipboardFileRangeFailure; + + context->clipboard = clip; + + return clip; +} + +static UINT +cliprdr_send_data_request(my_rdp_clipboard *clipboard) +{ + CLIPRDR_FORMAT_DATA_REQUEST request; + ZeroMemory(&request, sizeof(CLIPRDR_FORMAT_DATA_REQUEST)); + request.requestedFormatId = clipboard->cli_req_fmt_id.rdp_fmt.formatId; + return clipboard->clip_context->ClientFormatDataRequest( + clipboard->clip_context, &request); +} + +static bool +clip_api_handle_request_data( + const wrdp_backend_clipbrd_data_request *request, void *backend_internals) +{ + rdp_internals *i = backend_internals; + i->context->clipboard->cli_req_fmt_id.my_fmt = request->format; + switch (request->format) + { + case clip_format_raw: + { + /* i->context->clipboard->cli_req_fmt_id = CF_RAW; + * raw format does not work for some reason + */ + i->context->clipboard->cli_req_fmt_id.rdp_fmt.formatId + = CF_TEXT; + } + break; + case clip_format_text: + { + i->context->clipboard->cli_req_fmt_id.rdp_fmt.formatId + = CF_TEXT; + } + break; + case clip_format_file_list: + { + /* 4.5.3 Format Data Request PDU + The following is an annotated dump of a Format + Data Request PDU (section 2.2.5.1). The format being + requested is the File List that was advertised + in section 4.5.1 (the advertised ID in the Format + List PDU was 49273). + */ + uint8_t p; + bool format_found = false; + for (p = 0; p < i->context->clipboard->srv_fmts_count; + ++p) + { + if (!strcmp("FileGroupDescriptorW", + i->context->clipboard->srv_fmts[p] + .rdp_fmt.formatName)) + { + i->context->clipboard->cli_req_fmt_id + .rdp_fmt.formatId + = i->context->clipboard->srv_fmts[p] + .rdp_fmt.formatId; + format_found = true; + break; + } + } + if (!format_found) + { + const char *msg = "rdp_module: cliprdr: " + "requested data format" + " id not found"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + i->context->clipboard->cli_req_fmt_id.rdp_fmt + .formatId + = CF_RAW; + } + } + break; + case clip_format_unicode: + { + i->context->clipboard->cli_req_fmt_id.rdp_fmt.formatId + = CF_UNICODETEXT; + } + break; + default: + { + const char *msg + = "rdp_module: cliprdr: unsuported data format" + " requested"; + i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_warning, 0); + } + break; + } + cliprdr_send_data_request(i->context->clipboard); + return true; +} + +static bool +clip_api_handle_data_changed( + const wrdp_backend_clipbrd_fmts *fmts, void *backend_internals) +{ + rdp_internals *i = backend_internals; + my_rdp_clipboard *clip = i->context->clipboard; + uint8_t pos = 0; + + CLIPRDR_FORMAT *formats = 0; + CLIPRDR_FORMAT_LIST formatList; + + if (!fmts->count) + { + const char *msg = "received empty clipboard formats list"; + i->core->api_utils->log_msg((const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + return false; + } + formats = calloc(fmts->count, sizeof(CLIPRDR_FORMAT)); + if (!formats) + { + perror("calloc"); + return false; + } + + for (; pos < fmts->count; ++pos) + { + switch ((wrdp_enum_clip_format)fmts->formats[pos]) + { + case clip_format_raw: + { + formats[pos].formatId = CF_RAW; + } + break; + case clip_format_text: + { + formats[pos].formatId = CF_TEXT; + } + break; + case clip_format_unicode: + { + formats[pos].formatId = CF_UNICODETEXT; + } + break; + case clip_format_file_list: + { + formats[pos].formatId = CB_FORMAT_TEXTURILIST; + formats[pos].formatName + = "FileGroupDescriptorW"; + } + break; + default: + { + formats[pos].formatId = CF_RAW; + } + break; + } + } + formatList.common.msgFlags = CB_RESPONSE_OK; + formatList.numFormats = fmts->count; + formatList.formats = formats; + /* TODO: check return value of ClientFormatList */ + clip->clip_context->ClientFormatList(clip->clip_context, &formatList); + free(formats); + + return true; +} + +static bool +clip_api_handle_send_data( + const wrdp_backend_clipbrd_data *data, void *backend_internals) +{ + rdp_internals *i = backend_internals; + my_rdp_clipboard *clip = i->context->clipboard; + CLIPRDR_FORMAT_DATA_RESPONSE response; + + uint8_t type = data->data[0]; + memset(&response, 0, sizeof(CLIPRDR_FORMAT_DATA_RESPONSE)); + response.common.msgType = CB_FORMAT_DATA_RESPONSE; + response.common.msgFlags = CB_RESPONSE_FAIL; + response.common.dataLen = 0; + response.requestedFormatData = 0; +#ifdef DEBUG + { + const char *msg + = "rdp_module: clipboard: from client: raw clipboard" + " data:"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_trace, 0); + i->core->api_utils->log_msg(data->data, data->size, + wrdp_log_level_trace, wrdp_log_flag_binary); + } +#endif + switch (type) + { + case clip_format_raw: + case clip_format_text: + { + if (data->size > 1) + { + response.common.msgFlags = CB_RESPONSE_OK; + response.common.dataLen = data->size - 1; + response.requestedFormatData = data->data + 1; + } + clip->clip_context->ClientFormatDataResponse( + clip->clip_context, &response); + return true; + } + break; + case clip_format_unicode: + { + uint8_t *data_buf = 0; + BYTE *msg_buf = 0; + iconv_t utf8_to_utf16 = iconv_open("UTF-16LE", "UTF-8"); + char *ptr_w, *ptr_r; + size_t text_len_utf16 = (data->size - 1) * 2, + text_len_utf8 = data->size - 1, + text_len_result = text_len_utf16; + data_buf = calloc(text_len_utf16, sizeof(uint8_t)); + if (!data_buf) + { + perror("calloc"); + return true; + } + ptr_w = (char *)data_buf; + ptr_r = (char *)(data->data + 1); + while (text_len_utf8 && text_len_utf16) + { + if (iconv(utf8_to_utf16, &ptr_r, &text_len_utf8, + &ptr_w, &text_len_utf16) + == (size_t)-1) + { + log_msg_info mi = {0}; + free(data_buf); + clip->clip_context + ->ClientFormatDataResponse( + clip->clip_context, &response); + mi.level = wrdp_log_level_warning; + mi.buf = (const uint8_t + *)"rdp_module: cliprdr: iconv: " + "failed to encode outgoing " + "buffer from utf8 to utf16le"; + mi.task_info = i->task_info; + i->core->api_utils->log_msg_ex(&mi); + iconv_close(utf8_to_utf16); + return true; + } + } +#ifdef DEBUG + if (text_len_utf8 && !text_len_utf16) + { + log_msg_info mi = {0}; + mi.level = wrdp_log_level_trace; + mi.buf = (const uint8_t + *)"rdp_module: cliprdr: iconv: not " + "sufficient space in output buffer"; + mi.task_info = i->task_info; + i->core->api_utils->log_msg_ex(&mi); + } +#endif + iconv(utf8_to_utf16, 0, 0, &ptr_w, &text_len_utf16); + text_len_result = text_len_result - text_len_utf16; + iconv_close(utf8_to_utf16); + if (text_len_result) + { + msg_buf = calloc(text_len_result, sizeof(BYTE)); + memcpy(msg_buf, data_buf, text_len_result); + free(data_buf); + response.common.msgFlags = CB_RESPONSE_OK; + response.common.dataLen = text_len_result; + response.requestedFormatData = msg_buf; + } +#ifdef DEBUG + if (msg_buf) + { + log_msg_info mi = {0}; + mi.level = wrdp_log_level_trace; + mi.buf = (const uint8_t + *)"rdp_module: clipboard: to server:" + " utf16 encoded buffer"; + mi.task_info = i->task_info; + i->core->api_utils->log_msg_ex(&mi); + mi.flags = wrdp_log_flag_binary; + mi.buf = msg_buf; + mi.buf_size = text_len_result; + i->core->api_utils->log_msg_ex(&mi); + } +#endif + clip->clip_context->ClientFormatDataResponse( + clip->clip_context, &response); + if (msg_buf) + { + free(msg_buf); + } + return true; + } + break; + case clip_format_file_list: + { + uint16_t file_count; + bool broken_msg = false; + int _i; + uint64_t data_offset = 3; + FILEDESCRIPTORW *file_list = 0; + BYTE *format_data = 0; + UINT32 format_data_length = 0; + memcpy(&file_count, data->data + 1, 2); + if (!file_count) + { + const char *msg = "rdp_module: clipboard: ft: " + "empty file_list received"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_warning, 0); + return true; + } + if (clip->client_filelist_cache) + { + free(clip->client_filelist_cache); + } + clip->client_filelist_cache + = calloc(file_count, sizeof(file_list_cache_entry)); + if (!clip->client_filelist_cache) + { + perror("calloc"); + return false; + } + file_list = calloc(file_count, sizeof(FILEDESCRIPTOR)); + if (!file_list) + { + perror("calloc"); + return false; + } + for (_i = 0; _i < file_count; ++_i) + { + uint16_t filename_len; + uint32_t file_id; + uint64_t file_size; + char *filename, *ptr_w, *ptr_r; + iconv_t utf8_to_utf16 + = iconv_open("UTF-16LE", "UTF-8"); + size_t text_len_utf16 = 260 * sizeof(WCHAR), + text_len_utf8; + memcpy( + &filename_len, data->data + data_offset, 2); + data_offset += 2; + if (data_offset >= data->size) + { + broken_msg = true; + break; + } + text_len_utf8 = filename_len; + memcpy(&file_id, data->data + data_offset, 4); + clip->client_filelist_cache[_i].file_id + = file_id; + data_offset += 4; + if (data_offset >= data->size) + { + broken_msg = true; + break; + } + memcpy(&file_size, data->data + data_offset, 8); + clip->client_filelist_cache[_i].file_size + = file_size; + data_offset += 8; + if ((data_offset >= data->size) + && (_i < file_count)) + { + broken_msg = true; + break; + } + filename = (char *)data->data + data_offset; + ptr_r = filename; + ptr_w = (char *)(file_list[_i].cFileName); + iconv(utf8_to_utf16, &ptr_r, &text_len_utf8, + &ptr_w, &text_len_utf16); + iconv(utf8_to_utf16, 0, 0, &ptr_w, + &text_len_utf16); + iconv_close(utf8_to_utf16); + data_offset += filename_len; + file_list[_i].dwFileAttributes + = FILE_ATTRIBUTE_NORMAL; + file_list[_i].dwFlags = FD_ATTRIBUTES + | FD_FILESIZE + | FD_SHOWPROGRESSUI; + memcpy(&(file_list[_i].nFileSizeLow), + &file_size, 4); + memcpy(&(file_list[_i].nFileSizeHigh), + ((uint8_t *)&file_size) + 4, 4); + } + if (broken_msg) + { + char msg[128]; + snprintf(msg, 127, + "rdp_module: cliprdr: error: wrong file" + " list message size: %d, file count: %d\n", + data->size, file_count); + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + } + else + { + UINT error = NO_ERROR; + error = cliprdr_serialize_file_list(file_list, + file_count, &format_data, + &format_data_length); + if (error) + { + char buf[128]; + snprintf(buf, 127, + "failed to serialize " + "CLIPRDR_FILELIST:" + " 0x%08X", + error); + i->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), + wrdp_log_level_error, 0); + } + } + free(file_list); + if (format_data_length) + { + response.common.msgFlags = CB_RESPONSE_OK; + response.common.dataLen = format_data_length; + response.requestedFormatData = format_data; + } + clip->clip_context->ClientFormatDataResponse( + clip->clip_context, &response); + + free(format_data); + return true; + } + break; + default: + { + response.common.msgFlags = CB_RESPONSE_FAIL; + response.common.dataLen = 0; + response.requestedFormatData = 0; + clip->clip_context->ClientFormatDataResponse( + clip->clip_context, &response); + return true; + } + break; + } + /* TODO: check return code from ClientFormatDataResponse */ + return true; +} + +void +register_clipboard(wrdp_backend_module *backend) +{ + backend->callbacks_clipbrd->request_data = clip_api_handle_request_data; + backend->callbacks_clipbrd->send_data = clip_api_handle_send_data; + backend->callbacks_clipbrd->data_changed = clip_api_handle_data_changed; +} diff --git a/src/rdp/rdp_clipboard.h b/src/rdp/rdp_clipboard.h new file mode 100644 index 0000000..1536a2c --- /dev/null +++ b/src/rdp/rdp_clipboard.h @@ -0,0 +1,52 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "webrdp_core_api.h" +#include "webrdp_module_api.h" +#include "rdp_backend_api.h" +#include "rdp_impl.h" + +typedef struct +{ + CLIPRDR_FORMAT rdp_fmt; + wrdp_enum_clip_format my_fmt; +} my_clip_format; + +typedef struct +{ + uint32_t file_id; + uint64_t file_size; +} file_list_cache_entry; + +struct my_rdp_clipboard_s +{ + wClipboard *clipboard; + CliprdrClientContext *clip_context; + my_rdp_context *my_rdp_context; + wClipboardDelegate *delegate; + bool streams_supported; + uint8_t *data_raw; + /* we need to cache server side format list to know dynamic format id */ + my_clip_format *srv_fmts, cli_req_fmt_id, srv_req_fmt_id; + uint8_t srv_fmts_count; + file_list_cache_entry *client_filelist_cache; + + rdp_internals *my_internals; +}; + +my_rdp_clipboard *rdp_clipboard_new(my_rdp_context *context); + +void rdp_cliprdr_init(my_rdp_context *ctx, CliprdrClientContext *cliprdr); + +void rdp_cliprdr_uninit(my_rdp_context *ctx, CliprdrClientContext *cliprdr); + +void register_clipboard(wrdp_backend_module *backend); diff --git a/src/rdp/rdp_display_output.c b/src/rdp/rdp_display_output.c new file mode 100644 index 0000000..16f3c5d --- /dev/null +++ b/src/rdp/rdp_display_output.c @@ -0,0 +1,871 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <freerdp/client/cmdline.h> +#include <freerdp/client/file.h> +#include <freerdp/constants.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> +#include <freerdp/log.h> +#include <winpr/crt.h> +#include <winpr/synch.h> + +#include <webrdp_core_api.h> +#include <webrdp_module_api.h> + +#include "rdp_backend_api.h" +#include "rdp_impl.h" +#include "rdp_png.h" + +#define RGB_555_888(_r, _g, _b) \ + _r = (_r << 3 & ~0x7) | (_r >> 2); \ + _g = (_g << 3 & ~0x7) | (_g >> 2); \ + _b = (_b << 3 & ~0x7) | (_b >> 2); +#define RGB_565_888(_r, _g, _b) \ + _r = (_r << 3 & ~0x7) | (_r >> 2); \ + _g = (_g << 2 & ~0x3) | (_g >> 4); \ + _b = (_b << 3 & ~0x7) | (_b >> 2); +#define GetRGB_555(_r, _g, _b, _p) \ + _r = (_p & 0x7C00) >> 10; \ + _g = (_p & 0x3E0) >> 5; \ + _b = (_p & 0x1F); +#define GetRGB_565(_r, _g, _b, _p) \ + _r = (_p & 0xF800) >> 11; \ + _g = (_p & 0x7E0) >> 5; \ + _b = (_p & 0x1F); +#define GetRGB15(_r, _g, _b, _p) \ + GetRGB_555(_r, _g, _b, _p); \ + RGB_555_888(_r, _g, _b); +#define GetRGB16(_r, _g, _b, _p) \ + GetRGB_565(_r, _g, _b, _p); \ + RGB_565_888(_r, _g, _b); +#define RGB24(_r, _g, _b) ((_r << 16) | (_g << 8) | _b) +#define GetRGB24(_r, _g, _b, _p) \ + _r = (_p & 0xFF0000) >> 16; \ + _g = (_p & 0xFF00) >> 8; \ + _b = (_p & 0xFF); +#define GetRGB32(_r, _g, _b, _p) \ + _r = (_p & 0xFF0000) >> 16; \ + _g = (_p & 0xFF00) >> 8; \ + _b = (_p & 0xFF); +#define ARGB32(_a, _r, _g, _b) (_a << 24) | (_r << 16) | (_g << 8) | _b +#define RGB16(_r, _g, _b) \ + (((_r >> 3) & 0x1F) << 11) | (((_g >> 2) & 0x3F) << 5) \ + | ((_b >> 3) & 0x1F) +#define RGB15(_r, _g, _b) \ + (((_r >> 3) & 0x1F) << 10) | (((_g >> 3) & 0x1F) << 5) \ + | ((_b >> 3) & 0x1F) +#define GetARGB32(_a, _r, _g, _b, _p) \ + _a = (_p & 0xFF000000) >> 24; \ + _r = (_p & 0xFF0000) >> 16; \ + _g = (_p & 0xFF00) >> 8; \ + _b = (_p & 0xFF); + +HCLRCONV +freerdp_clrconv_new(UINT32 flags) +{ + HCLRCONV clrconv; + + clrconv = (CLRCONV *)calloc(1, sizeof(CLRCONV)); + + if (!clrconv) + { + perror("calloc"); + return 0; + } + + clrconv->alpha = (flags & CLRCONV_ALPHA) ? TRUE : FALSE; + clrconv->invert = (flags & CLRCONV_INVERT) ? TRUE : FALSE; + clrconv->rgb555 = (flags & CLRCONV_RGB555) ? TRUE : FALSE; + + clrconv->palette = (rdpPalette *)calloc(1, sizeof(rdpPalette)); + + if (!clrconv->palette) + { + perror("calloc"); + free(clrconv); + return 0; + } + + return clrconv; +} + +void +freerdp_clrconv_free(HCLRCONV clrconv) +{ + if (clrconv) + { + free(clrconv->palette); + free(clrconv); + } +} + +static int +freerdp_get_pixel(BYTE *data, int x, int y, int width, int height, int bpp) +{ + int start; + int shift; + UINT16 *src16; + UINT32 *src32; + int red, green, blue; + + switch (bpp) + { + case 1: + width = (width + 7) / 8; + start = (y * width) + x / 8; + shift = x % 8; + return (data[start] & (0x80 >> shift)) != 0; + case 8: + return data[y * width + x]; + case 15: + case 16: + src16 = (UINT16 *)data; + return src16[y * width + x]; + case 24: + data += y * width * 3; + data += x * 3; + red = data[0]; + green = data[1]; + blue = data[2]; + return RGB24(red, green, blue); + case 32: + src32 = (UINT32 *)data; + return src32[y * width + x]; + default: + break; + } + + return 0; +} + +static void +freerdp_set_pixel( + BYTE *data, int x, int y, int width, int height, int bpp, int pixel) +{ + int start; + int shift; + int *dst32; + + if (bpp == 1) + { + width = (width + 7) / 8; + start = (y * width) + x / 8; + shift = x % 8; + if (pixel) + data[start] = data[start] | (0x80 >> shift); + else + data[start] = data[start] & ~(0x80 >> shift); + } + else if (bpp == 32) + { + dst32 = (int *)data; + dst32[y * width + x] = pixel; + } +} + +static void +freerdp_color_split_rgb(UINT32 *color, int bpp, BYTE *red, BYTE *green, + BYTE *blue, BYTE *alpha, HCLRCONV clrconv) +{ + *red = *green = *blue = 0; + *alpha = (clrconv->alpha) ? 0xFF : 0x00; + + switch (bpp) + { + case 32: + if (clrconv->alpha) + { + GetARGB32(*alpha, *red, *green, *blue, *color); + } + else + { + GetRGB32(*red, *green, *blue, *color); + } + break; + + case 24: + GetRGB24(*red, *green, *blue, *color); + break; + + case 16: + GetRGB16(*red, *green, *blue, *color); + break; + + case 15: + GetRGB15(*red, *green, *blue, *color); + break; + + case 8: + *color &= 0xFF; + *red = clrconv->palette->entries[*color].red; + *green = clrconv->palette->entries[*color].green; + *blue = clrconv->palette->entries[*color].blue; + break; + + case 1: + if (*color != 0) + { + *red = 0xFF; + *green = 0xFF; + *blue = 0xFF; + } + break; + + default: + break; + } +} + +static void +freerdp_color_make_rgb(UINT32 *color, int bpp, BYTE *red, BYTE *green, + BYTE *blue, BYTE *alpha, HCLRCONV clrconv) +{ + switch (bpp) + { + case 32: + *color = ARGB32(*alpha, *red, *green, *blue); + break; + + case 24: + *color = RGB24(*red, *green, *blue); + break; + + case 16: + if (clrconv->rgb555) + { + *color = RGB15(*red, *green, *blue); + } + else + { + *color = RGB16(*red, *green, *blue); + } + break; + + case 15: + *color = RGB15(*red, *green, *blue); + break; + + case 8: + *color = RGB24(*red, *green, *blue); + break; + + case 1: + if ((*red != 0) || (*green != 0) || (*blue != 0)) + *color = 1; + break; + + default: + break; + } +} + +static UINT32 +freerdp_color_convert_rgb( + UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv) +{ + BYTE red = 0; + BYTE green = 0; + BYTE blue = 0; + BYTE alpha = 0xFF; + UINT32 dstColor = 0; + + freerdp_color_split_rgb( + &srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv); + freerdp_color_make_rgb( + &dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv); + + return dstColor; +} + +static void +freerdp_alpha_cursor_convert(BYTE *alphaData, BYTE *xorMask, BYTE *andMask, + int width, int height, int bpp, HCLRCONV clrconv) +{ + UINT32 xorPixel; + UINT32 andPixel; + UINT32 x, y, jj; + + for (y = 0; y < height; y++) + { + jj = (bpp == 1) ? y : (height - 1) - y; + + for (x = 0; x < width; x++) + { + xorPixel = freerdp_get_pixel( + xorMask, x, jj, width, height, bpp); + xorPixel = freerdp_color_convert_rgb( + xorPixel, bpp, 32, clrconv); + andPixel = freerdp_get_pixel( + andMask, x, jj, width, height, 1); + + if (andPixel) + { + if ((xorPixel & 0xFFFFFF) == 0xFFFFFF) + { + /* use pattern (not solid black) for xor + * area */ + xorPixel = (x & 1) == (y & 1); + xorPixel = xorPixel ? 0xFFFFFF : 0; + xorPixel |= 0xFF000000; + } + else if (xorPixel == 0xFF000000) + { + xorPixel = 0; + } + } + + freerdp_set_pixel( + alphaData, x, y, width, height, 32, xorPixel); + } + } +} + +static void +freerdp_bitmap_flip(BYTE *src, BYTE *dst, int scanLineSz, int height) +{ + int i; + + BYTE *bottomLine = dst + (scanLineSz * (height - 1)); + BYTE *topLine = src; + + /* Special processing if called for flip-in-place. */ + if (src == dst) + { + /* Allocate a scanline buffer. + * (FIXME: malloc / xfree below should be replaced by "get/put + * scanline buffer from a pool/Q of fixed buffers" to reuse + * fixed size buffers (of max scanline size (or adaptative?) ) + * -- would be much faster). + */ + BYTE *tmpBfr = (BYTE *)winpr_aligned_malloc(scanLineSz, 16); + if (!tmpBfr) + { + return; + } + int half = height / 2; + /* Flip buffer in place by line permutations through the temp + * scan line buffer. + * Note that if height has an odd number of line, we don't need + * to move the center scanline anyway. + * Also note that in place flipping takes three memcpy() calls + * to process two scanlines while src to distinct dest would + * only requires two memcpy() calls for two scanlines. + */ + height--; + for (i = 0; i < half; i++) + { + CopyMemory(tmpBfr, topLine, scanLineSz); + CopyMemory(topLine, bottomLine, scanLineSz); + CopyMemory(bottomLine, tmpBfr, scanLineSz); + topLine += scanLineSz; + bottomLine -= scanLineSz; + height--; + } + winpr_aligned_free(tmpBfr); + } + /* Flip from source buffer to destination buffer. */ + else + { + for (i = 0; i < height; i++) + { + if (bottomLine && topLine && scanLineSz) + { + CopyMemory(bottomLine, topLine, scanLineSz); + topLine += scanLineSz; + bottomLine -= scanLineSz; + } + } + } +} + +static BYTE * +freerdp_image_flip(BYTE *srcData, BYTE *dstData, int width, int height, int bpp) +{ + int scanline; + + scanline = width * ((bpp + 7) / 8); + + if (!dstData) + dstData = (BYTE *)winpr_aligned_malloc( + width * height * ((bpp + 7) / 8), 16); + + if (!dstData) + { + return NULL; + } + + freerdp_bitmap_flip(srcData, dstData, scanline, height); + + return dstData; +} + +/* outgoing rdp protocol to client implementation */ + +static BOOL +Pointer_New(rdpContext *context, rdpPointer *pointer) +{ + my_rdp_context *c = (my_rdp_context *)context; + HCLRCONV hclrconv = c->my_internals->clrconv; + size_t psize = pointer->width * pointer->height * 4; + + my_rdp_pointer *p = (my_rdp_pointer *)pointer; + p->id = c->my_internals->m_ptrId++; + uint8_t *pixels = malloc(sizeof(uint8_t) * psize); + if (!pixels) + { + perror("malloc"); + return FALSE; + } + memset(pixels, 0, psize); + if ((pointer->andMaskData != 0) && (pointer->xorMaskData != 0)) + { + //XXX + freerdp_alpha_cursor_convert(pixels, pointer->xorMaskData, + pointer->andMaskData, pointer->width, pointer->height, + pointer->xorBpp, hclrconv); + } + + //check if the cursor is fully transparent + bool transparent = TRUE; + for (int y = 0; y < pointer->height; y++) + { + for (int x = 0; x < pointer->width; x++) + { + if (pixels[0 + x * 4 + y * 4 * pointer->width] != 0) + { + transparent = FALSE; + } + } + } + if (transparent) + { + pixels[3] = 1; + } + + rdp_png_buf png_buf; + memset(&png_buf, 0, sizeof(png_buf)); + png_buf.buf_size + = (pointer->width * pointer->height * sizeof(uint32_t)) + + 8 /* allocating fullsize 32but argb pixels + png header */; + png_buf.buf = malloc(png_buf.buf_size); + if (!png_buf.buf) + { + free(pixels); + perror("malloc"); + return FALSE; + } + png_generate_from_argb( + pointer->width, pointer->height, pixels, &png_buf); + + wrdp_core_display_cursor cur = {0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + (unsigned char)pointer->width, (unsigned char)pointer->height, 0x00, + 0x00, (unsigned char)pointer->xPos, + (unsigned char)(pointer->xPos >> 8), (unsigned char)pointer->yPos, + (unsigned char)(pointer->yPos >> 8), (unsigned char)png_buf.written, + (unsigned char)(png_buf.written >> 8), + (unsigned char)(png_buf.written >> 16), + (unsigned char)(png_buf.written >> 24), 0x16, 0x00, 0x00, 0x00}; + + cur.data = png_buf.buf; + wrdp_core_display_cursor_info cursor_info + = {p->id, pointer->xPos, pointer->yPos, png_buf.written, &cur}; + + c->my_internals->core->api_paint->send_ptr_new( + &cursor_info, c->my_internals->task_info); + free(png_buf.buf); + free(pixels); + return TRUE; +} + +static void +Pointer_Free(rdpContext *context, rdpPointer *pointer) +{ + my_rdp_pointer *p = (my_rdp_pointer *)pointer; + my_rdp_context *c = (my_rdp_context *)context; + if (p->id) + { + c->my_internals->core->api_paint->send_ptr_free( + p->id, c->my_internals->task_info); + } +} + +static BOOL +Pointer_Set(rdpContext *context, rdpPointer *pointer) +{ + my_rdp_pointer *p = (my_rdp_pointer *)pointer; + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->api_paint->send_ptr_set( + p->id, c->my_internals->task_info); + return TRUE; +} + +static BOOL +Pointer_SetNull(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->api_paint->send_ptr_set_null( + c->my_internals->task_info); + return TRUE; +} + +static BOOL +Pointer_SetDefault(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->api_paint->send_ptr_set_default( + c->my_internals->task_info); + return TRUE; +} + +void +register_pointer(rdpPointer *p) +{ + p->New = Pointer_New; + p->Free = Pointer_Free; + p->Set = Pointer_Set; + p->SetNull = Pointer_SetNull; + p->SetDefault = Pointer_SetDefault; +} + +static BOOL +BeginPaint(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->api_paint->send_begin_paint( + c->my_internals->task_info); + return TRUE; +} + +static BOOL +EndPaint(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->api_paint->send_end_paint( + c->my_internals->task_info); + return TRUE; +} + +static BOOL +SetBounds(rdpContext *context, const rdpBounds *bounds) +{ + my_rdp_context *c = (my_rdp_context *)context; + wrdp_core_display_bounds lB; + if (bounds) + { + lB.left = bounds->left; + lB.right = bounds->right + 1; + lB.top = bounds->top; + lB.bottom = bounds->bottom + 1; + } + else + { + memset(&lB, 0, sizeof(wrdp_core_display_bounds)); + } + c->my_internals->core->api_paint->send_set_bounds( + &lB, c->my_internals->task_info); + return TRUE; +} + +static BOOL +Synchronize(rdpContext *context) +{ + return TRUE; +} + +static BOOL +DesktopResize(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + char buf[64]; + snprintf(buf, 63, "R:%dx%d", context->settings->DesktopWidth, + context->settings->DesktopHeight); + c->my_internals->core->api_msgs->send_text_msg( + buf, c->my_internals->task_info); + return TRUE; +} + +static BOOL +BitmapUpdate(rdpContext *context, const BITMAP_UPDATE *bitmap) +{ + my_rdp_context *c = (my_rdp_context *)context; + int i; + BITMAP_DATA *bmd; + for (i = 0; i < (int)bitmap->number; i++) + { + bmd = &bitmap->rectangles[i]; + wrdp_core_display_bmp bmp = {bmd->destLeft, bmd->destTop, + bmd->width, bmd->height, bmd->destRight - bmd->destLeft + 1, + bmd->destBottom - bmd->destTop + 1, bmd->bitsPerPixel, + bmd->compressed, bmd->bitmapLength}; + if (!bmd->compressed) + { + freerdp_image_flip(bmd->bitmapDataStream, + bmd->bitmapDataStream, bmd->width, bmd->height, + bmd->bitsPerPixel); + } + c->my_internals->core->api_paint->send_bitmap( + &bmp, bmd->bitmapDataStream, c->my_internals->task_info); + } + return TRUE; +} + +static BOOL +Palette(rdpContext *c, const PALETTE_UPDATE *p) +{ + return TRUE; +} + +static BOOL +PlaySound(rdpContext *c, const PLAY_SOUND_UPDATE *s) +{ + return TRUE; +} + +static BOOL +RefreshRect(rdpContext *c, UINT8 hz, const RECTANGLE_16 *r) +{ + return TRUE; +} + +static BOOL +SuppressOutput(rdpContext *c, UINT8 hz, const RECTANGLE_16 *r) +{ + return TRUE; +} + +/*static BOOL +SurfaceCommand(rdpContext* c, wStream* s) +{ + return TRUE; +}*/ + +static BOOL +SurfaceBits(rdpContext *c, const SURFACE_BITS_COMMAND *sbc) +{ + return TRUE; +} + +/*static BOOL +SurfaceFrameMarker(rdpContext* c, SURFACE_FRAME_MARKER* sfm) +{ + return TRUE; +} */ + +void +RegisterUpdate(freerdp *rdp) +{ + rdpUpdate *u = rdp->context->update; + u->BeginPaint = BeginPaint; + u->EndPaint = EndPaint; + u->SetBounds = SetBounds; + u->Synchronize = Synchronize; + u->DesktopResize = DesktopResize; + u->BitmapUpdate = BitmapUpdate; + u->Palette = Palette; + u->PlaySound = PlaySound; + u->SurfaceBits = SurfaceBits; + + u->RefreshRect = RefreshRect; + u->SuppressOutput = SuppressOutput; +} + +static BOOL +DstBlt(rdpContext *c, const DSTBLT_ORDER *_do) +{ + return TRUE; +} + +static BOOL +PatBlt(rdpContext *ctx, PATBLT_ORDER *po) +{ + my_rdp_context *c = (my_rdp_context *)ctx; + uint32_t rop3 = gdi_rop3_code(po->bRop); + if (GDI_BS_SOLID == po->brush.style) + { + wrdp_core_display_patblt_order patblt + = {po->nLeftRect, po->nTopRect, po->nWidth, po->nHeight, + FreeRDPConvertColor(po->foreColor, PIXEL_FORMAT_BGR16, + PIXEL_FORMAT_ABGR32, + NULL), //XXX + rop3}; + c->my_internals->core->api_paint->send_pat_blt( + &patblt, c->my_internals->task_info); + } + return TRUE; +} + +static BOOL +ScrBlt(rdpContext *ctx, const SCRBLT_ORDER *sbo) +{ + my_rdp_context *c = (my_rdp_context *)ctx; + uint32_t rop3 = gdi_rop3_code(sbo->bRop); + wrdp_core_display_scr_blt scr_blt = {rop3, sbo->nLeftRect, + sbo->nTopRect, sbo->nWidth, sbo->nHeight, sbo->nXSrc, sbo->nYSrc}; + c->my_internals->core->api_paint->send_scr_blt( + &scr_blt, c->my_internals->task_info); + return TRUE; +} + +static BOOL +OpaqueRect(rdpContext *context, const OPAQUE_RECT_ORDER *oro) +{ + my_rdp_context *c = (my_rdp_context *)context; + wrdp_core_display_opaque_rect_order oro2; + oro2.color = oro->color; + oro2.nHeight = oro->nHeight; + oro2.nLeftRect = oro->nLeftRect; + oro2.nTopRect = oro->nTopRect; + oro2.nWidth = oro->nWidth; + oro2.color = FreeRDPConvertColor( + oro2.color, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL); + c->my_internals->core->api_paint->send_opaque_rect_order( + &oro2, c->my_internals->task_info); + return TRUE; +} + +static BOOL +DrawNineGrid(rdpContext *c, const DRAW_NINE_GRID_ORDER *dngo) +{ + return TRUE; +} + +static BOOL +MultiDstBlt(rdpContext *c, const MULTI_DSTBLT_ORDER *mdo) +{ + return TRUE; +} + +static BOOL +MultiPatBlt(rdpContext *c, const MULTI_PATBLT_ORDER *mpo) +{ + return TRUE; +} + +static BOOL +MultiScrBlt(rdpContext *c, const MULTI_SCRBLT_ORDER *mso) +{ + return TRUE; +} + +static BOOL +MultiOpaqueRect(rdpContext *context, const MULTI_OPAQUE_RECT_ORDER *moro) +{ + my_rdp_context *c = (my_rdp_context *)context; + wrdp_core_display_m_opaque_rect mrect; + mrect.color = FreeRDPConvertColor( + moro->color, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL); + mrect.num_rect = moro->numRectangles; + mrect.rects = (wrdp_core_display_delta_rect *)(moro->rectangles); + c->my_internals->core->api_paint->send_multi_opaque_rect( + &mrect, c->my_internals->task_info); + return TRUE; +} + +static BOOL +MultiDrawNineGrid(rdpContext *c, const MULTI_DRAW_NINE_GRID_ORDER *mdngo) +{ + return TRUE; +} + +static BOOL +LineTo(rdpContext *c, const LINE_TO_ORDER *lto) +{ + return TRUE; +} + +static BOOL +Polyline(rdpContext *c, const POLYLINE_ORDER *po) +{ + return TRUE; +} + +static BOOL +MemBlt(rdpContext *c, MEMBLT_ORDER *mo) +{ + return TRUE; +} + +static BOOL +Mem3Blt(rdpContext *c, MEM3BLT_ORDER *mo) +{ + return TRUE; +} + +static BOOL +SaveBitmap(rdpContext *c, const SAVE_BITMAP_ORDER *sbo) +{ + return TRUE; +} + +static BOOL +GlyphIndex(rdpContext *c, GLYPH_INDEX_ORDER *gio) +{ + return TRUE; +} + +static BOOL +FastIndex(rdpContext *c, const FAST_INDEX_ORDER *fio) +{ + return TRUE; +} + +static BOOL +FastGlyph(rdpContext *c, const FAST_GLYPH_ORDER *fgo) +{ + return TRUE; +} + +static BOOL +PolygonSC(rdpContext *c, const POLYGON_SC_ORDER *pso) +{ + return TRUE; +} + +static BOOL +PolygonCB(rdpContext *c, POLYGON_CB_ORDER *pco) +{ + return TRUE; +} + +static BOOL +EllipseSC(rdpContext *c, const ELLIPSE_SC_ORDER *eso) +{ + return TRUE; +} + +static BOOL +EllipseCB(rdpContext *c, const ELLIPSE_CB_ORDER *eco) +{ + return TRUE; +} +void +RegisterPrimary(freerdp *rdp) +{ + rdpPrimaryUpdate *p = rdp->context->update->primary; + p->DstBlt = DstBlt; + p->PatBlt = PatBlt; + p->ScrBlt = ScrBlt; + p->OpaqueRect = OpaqueRect; + p->DrawNineGrid = DrawNineGrid; + p->MultiDstBlt = MultiDstBlt; + p->MultiPatBlt = MultiPatBlt; + p->MultiScrBlt = MultiScrBlt; + p->MultiOpaqueRect = MultiOpaqueRect; + p->MultiDrawNineGrid = MultiDrawNineGrid; + p->LineTo = LineTo; + p->Polyline = Polyline; + p->MemBlt = MemBlt; + p->Mem3Blt = Mem3Blt; + p->SaveBitmap = SaveBitmap; + p->GlyphIndex = GlyphIndex; + p->FastIndex = FastIndex; + p->FastGlyph = FastGlyph; + p->PolygonSC = PolygonSC; + p->PolygonCB = PolygonCB; + p->EllipseSC = EllipseSC; + p->EllipseCB = EllipseCB; +} diff --git a/src/rdp/rdp_display_output.h b/src/rdp/rdp_display_output.h new file mode 100644 index 0000000..14ae7f3 --- /dev/null +++ b/src/rdp/rdp_display_output.h @@ -0,0 +1,17 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +void RegisterUpdate(freerdp *rdp); +void RegisterPrimary(freerdp *rdp); + +void rdp_register_base(freerdp *r); + +HCLRCONV +freerdp_clrconv_new(UINT32 flags); + +void freerdp_clrconv_free(HCLRCONV clrconv); diff --git a/src/rdp/rdp_ft.c b/src/rdp/rdp_ft.c new file mode 100644 index 0000000..0c5575e --- /dev/null +++ b/src/rdp/rdp_ft.c @@ -0,0 +1,129 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <stddef.h> +#include <stdbool.h> + +#include "webrdp_module_api.h" +#include "webrdp_core_api.h" +#include "rdp_backend_api.h" +#include "rdp_impl.h" +#include "rdp_ft.h" +#include "rdp_clipboard.h" + +static bool +ft_api_handle_request( + const wrdp_backend_ft_file_request *request, void *backend_internals) +{ + rdp_internals *i = backend_internals; + CLIPRDR_FILE_CONTENTS_REQUEST file_req; + uint32_t chunk_len = 512000; + size_t left = 0; + if (i->context->ft_to_client->is_runing) + { + const char *msg + = "rdp_module: clipboard: filetransfers: transfer " + "already runing, cannot start new one"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + return true; + } + memset(&file_req, 0, sizeof(CLIPRDR_FILE_CONTENTS_REQUEST)); + i->context->ft_to_client->transfer_id = rand(); + i->context->ft_to_client->file_size = request->req_size; + i->context->ft_to_client->file_id = request->file_id; + + i->context->ft_to_client->transferred = request->file_offset; + i->context->ft_to_client->is_runing = true; + + left = i->context->ft_to_client->file_size + - i->context->ft_to_client->transferred; + if (left < chunk_len) + { + chunk_len = left; + } + file_req.cbRequested = chunk_len; + file_req.listIndex = request->file_id; + file_req.streamId = i->context->ft_to_client->transfer_id; + file_req.dwFlags = FILECONTENTS_RANGE; + /* file_req.haveClipDataId = TRUE; + file_req.clipDataId = + i->context->ft_to_client->clipboard_data_id; */ + + /* TODO: check/change endianness */ + memcpy(&(file_req.nPositionLow), + &(i->context->ft_to_client->transferred), 4); + memcpy(&(file_req.nPositionHigh), + (((uint8_t *)&(i->context->ft_to_client->transferred)) + 4), 4); + i->context->clipboard->clip_context->ClientFileContentsRequest( + i->context->clipboard->clip_context, &file_req); + + return true; +} + +static bool +ft_api_handle_chunk(const wrdp_backend_ft_chunk *chunk, const uint8_t *data, + void *backend_internals) +{ + rdp_internals *i = backend_internals; + CLIPRDR_FILE_CONTENTS_RESPONSE resp; + memset(&resp, 0, sizeof(CLIPRDR_FILE_CONTENTS_RESPONSE)); + resp.cbRequested = chunk->size; + resp.streamId = i->context->ft_to_server->transfer_id; + resp.common.msgFlags = CB_RESPONSE_OK; + resp.common.msgType = CB_FILECONTENTS_RESPONSE; + resp.requestedData = data; + i->context->clipboard->clip_context->ClientFileContentsResponse( + i->context->clipboard->clip_context, &resp); + i->context->ft_to_server->transferred += chunk->size; + if (i->context->ft_to_server->transferred + == i->context->ft_to_server->file_size) + { + i->context->ft_to_server->is_runing = false; + i->context->ft_to_server->transfer_id = 0; + i->context->ft_to_server->file_id = 0; + i->context->ft_to_server->file_size = 0; + i->context->ft_to_server->transferred = 0; + } + return true; +} + +static bool +ft_api_handle_finish( + const wrdp_backend_ft_status *status, void *backend_internals) +{ + /* TODO: handle errors */ + rdp_internals *i = backend_internals; + if (status->status != ft_status_success) + { + /* TODO: test it */ + CLIPRDR_FILE_CONTENTS_RESPONSE resp; + memset(&resp, 0, sizeof(CLIPRDR_FILE_CONTENTS_RESPONSE)); + const char *msg = "filetransfer finished with error"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + resp.common.msgType = CB_FILECONTENTS_RESPONSE; + resp.common.msgFlags = CB_RESPONSE_FAIL; + resp.streamId = i->context->ft_to_server->transfer_id; + resp.cbRequested = 0; + resp.requestedData = 0; + i->context->clipboard->clip_context->ClientFileContentsResponse( + i->context->clipboard->clip_context, &resp); + } + i->context->ft_to_server->is_runing = false; + i->context->ft_to_server->transfer_id = 0; + i->context->ft_to_server->file_size + = i->context->ft_to_server->transferred = 0; + return true; +} + +void +register_ft(wrdp_backend_module *backend) +{ + backend->callbacks_ft->chunk = ft_api_handle_chunk; + backend->callbacks_ft->finish = ft_api_handle_finish; + backend->callbacks_ft->request = ft_api_handle_request; +} diff --git a/src/rdp/rdp_ft.h b/src/rdp/rdp_ft.h new file mode 100644 index 0000000..38b6a34 --- /dev/null +++ b/src/rdp/rdp_ft.h @@ -0,0 +1,17 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +struct my_rdp_ft_s +{ + uint32_t transfer_id, file_id; + bool is_runing; + uint64_t file_size, transferred; +}; + +void register_ft(wrdp_backend_module *backend); + diff --git a/src/rdp/rdp_impl.c b/src/rdp/rdp_impl.c new file mode 100644 index 0000000..3aa47e2 --- /dev/null +++ b/src/rdp/rdp_impl.c @@ -0,0 +1,637 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <errno.h> +#include <locale.h> +#include <pthread.h> +#include <stdlib.h> +#include <sys/select.h> + +#include <freerdp/client/channels.h> +#include <freerdp/client/cliprdr.h> +#include <freerdp/client/cmdline.h> +#include <freerdp/client/file.h> +#include <freerdp/constants.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> +#include <winpr/crt.h> +#include <winpr/synch.h> +#include <freerdp/log.h> + +#include <webrdp_core_api.h> +#include <webrdp_module_api.h> + +#include "rdp_backend_api.h" +#include "rdp_impl.h" +#include "rdp_display_output.h" +#include "rdp_channels.h" +#include "rdp_clipboard.h" +#include "rdp_ft.h" +#include "rdp_user_input.h" + +/* from winpr */ + +typedef BOOL (*pcIsHandled)(HANDLE handle); +typedef BOOL (*pcCloseHandle)(HANDLE handle); +typedef int (*pcGetFd)(HANDLE handle); +typedef DWORD (*pcCleanupHandle)(HANDLE handle); +typedef BOOL (*pcReadFile)(PVOID Object, LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcReadFileEx)(HANDLE hFile, LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +typedef BOOL (*pcReadFileScatter)(HANDLE hFile, + FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead, + LPDWORD lpReserved, LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcWriteFile)(PVOID Object, LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcWriteFileEx)(HANDLE hFile, LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +typedef BOOL (*pcWriteFileGather)(HANDLE hFile, + FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToWrite, + LPDWORD lpReserved, LPOVERLAPPED lpOverlapped); +typedef DWORD (*pcGetFileSize)(HANDLE handle, LPDWORD lpFileSizeHigh); +typedef BOOL (*pcFlushFileBuffers)(HANDLE hFile); +typedef BOOL (*pcSetEndOfFile)(HANDLE handle); +typedef DWORD (*pcSetFilePointer)(HANDLE handle, LONG lDistanceToMove, + PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); +typedef BOOL (*pcSetFilePointerEx)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, + PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod); +typedef BOOL (*pcLockFile)(HANDLE hFile, DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh); +typedef BOOL (*pcLockFileEx)(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcUnlockFile)(HANDLE hFile, DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh); +typedef BOOL (*pcUnlockFileEx)(HANDLE hFile, DWORD dwReserved, + DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcSetFileTime)(HANDLE hFile, const FILETIME *lpCreationTime, + const FILETIME *lpLastAccessTime, const FILETIME *lpLastWriteTime); + +typedef struct _HANDLE_OPS +{ + pcIsHandled IsHandled; + pcCloseHandle CloseHandle; + pcGetFd GetFd; + pcCleanupHandle CleanupHandle; + pcReadFile ReadFile; + pcReadFileEx ReadFileEx; + pcReadFileScatter ReadFileScatter; + pcWriteFile WriteFile; + pcWriteFileEx WriteFileEx; + pcWriteFileGather WriteFileGather; + pcGetFileSize GetFileSize; + pcFlushFileBuffers FlushFileBuffers; + pcSetEndOfFile SetEndOfFile; + pcSetFilePointer SetFilePointer; + pcSetFilePointerEx SetFilePointerEx; + pcLockFile LockFile; + pcLockFileEx LockFileEx; + pcUnlockFile UnlockFile; + pcUnlockFileEx UnlockFileEx; + pcSetFileTime SetFileTime; +} HANDLE_OPS; + +#define WINPR_HANDLE_DEF() \ + ULONG Type; \ + ULONG Mode; \ + HANDLE_OPS *ops + +struct winpr_handle +{ + WINPR_HANDLE_DEF(); +}; +typedef struct winpr_handle WINPR_HANDLE; + +static INLINE BOOL +winpr_Handle_GetInfo(HANDLE handle, ULONG *pType, WINPR_HANDLE **pObject) +{ + WINPR_HANDLE *wHandle; + + if (handle == NULL || handle == INVALID_HANDLE_VALUE) + return FALSE; + + wHandle = (WINPR_HANDLE *)handle; + + *pType = wHandle->Type; + *pObject = handle; + + return TRUE; +} + +static INLINE int +winpr_Handle_getFd(HANDLE handle) +{ + WINPR_HANDLE *hdl; + ULONG type; + + if (!winpr_Handle_GetInfo(handle, &type, &hdl)) + return -1; + + if (!hdl || !hdl->ops || !hdl->ops->GetFd) + return -1; + + return hdl->ops->GetFd(handle); +} + +/* end from winpr */ + +bool +rdp_init_internals(void *internals) +{ + setlocale(LC_ALL, ""); +#ifdef DEBUG + setenv("WLOG_LEVEL", "DEBUG", 1); +#else + setenv("WLOG_LEVEL", "ERROR", 1); +#endif + rdp_internals *i = internals; + i->instance = freerdp_new(); + if (!i->instance) + { + const char *msg + = "rdp_module: Failed to create FreeRDP instance"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + goto error; + } + rdp_register_base(i->instance); + i->instance->ContextSize = sizeof(my_rdp_context); + if (!freerdp_context_new(i->instance)) + { + const char *msg + = "rdp_module: Failed to create FreeRDP context"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + goto error; + } + i->context = (my_rdp_context *)i->instance->context; + ((my_rdp_context *)i->instance->context)->my_internals = internals; + { + char **argv = malloc(sizeof(char *)); + if (!argv) + { + perror("malloc"); + goto error; + } + argv[0] = strdup("./core"); + rdpSettings *settings = i->instance->context->settings; + freerdp_client_settings_parse_command_line( + settings, 1, argv, FALSE); + free(argv[0]); + free(argv); + if (!settings) + { + const char *msg + = "rdp_module: Failed to create FreeRDP settings"; + i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_error, 0); + goto error; + } + settings->ThreadingFlags = THREADING_FLAGS_DISABLE_THREADS; + + settings->OsMajorType = OSMAJORTYPE_UNIX; + settings->OsMinorType = OSMINORTYPE_UNSPECIFIED; + + /* unsupported */ + settings->RemoteFxCodec = FALSE; + settings->NSCodec = FALSE; + + /* turn on clipboard redirect */ + settings->RedirectClipboard = TRUE; + } + i->context->ft_to_server = calloc(1, sizeof(my_rdp_ft)); + if (!i->context->ft_to_server) + { + perror("calloc"); + goto error; + } + i->context->ft_to_client = calloc(1, sizeof(my_rdp_ft)); + if (!i->context->ft_to_client) + { + perror("calloc"); + goto error; + } + freerdp_register_addin_provider( + freerdp_channels_load_static_addin_entry, 0); + i->settings = i->instance->context->settings; + //i->instance->settings = i->settings; + i->settings->instance = i->instance; + i->instance->context->settings = i->settings; + return true; +error: + if (i->instance) + { + if (i->instance->context) + { + freerdp_context_free(i->instance); + } + freerdp_free(i->instance); + i->instance = 0; + } + if (i->context->ft_to_client) + { + free(i->context->ft_to_client); + } + if (i->context->ft_to_server) + { + free(i->context->ft_to_server); + } + { + const char *msg = "rdp_module: internal error"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + } + i->core->api_msgs->send_error_msg( + "rdp_module_internal_error", i->task_info); + return false; +} + +static void +rdp_check_all_fds(struct ev_loop *loop, ev_io *w) +{ + rdp_internals *i = w->data; + bool failure = false; + if (i->backend->stopped) + return; + if ((freerdp_check_event_handles(i->instance->context)) != TRUE) + { + uint32_t e; + e = freerdp_error_info(i->instance); + switch (e) + { + case 1: + case 2: + case 7: + case 9: + /* case 12: */ + /* No really an error + * (Happens when you invoke Disconnect + * in Start-Menu) */ + break; + case 5: + { + const char *msg + = "rdp_module: Another user connected to " + "the server,\nforcing the " + "disconnection of the " + "current connection."; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_debug, 0); + break; + } + default: + { + failure = true; + } + break; + } + if (failure) + { + char buf[512]; + snprintf(buf, 511, "%s", + freerdp_get_last_error_name( + freerdp_get_last_error(i->instance->context))); + i->core->api_msgs->send_error_msg(buf, i->task_info); + i->core->api_utils->log_msg((const uint8_t *)buf, + strlen(buf), wrdp_log_level_error, 0); + goto cleanup; + } + } + if (freerdp_shall_disconnect_context(i->instance->context)) + { + { + const char *msg = "rdp_module: disconnect, no error"; + i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_trace, 0); + } + freerdp_disconnect(i->instance); + i->conn_state = rdp_conn_state_offline; + i->core->api_msgs->send_error_msg( + "rdp_module_disconnect_no_error", i->task_info); + goto cleanup; + } + return; +cleanup: + ev_io_stop(loop, w); + if (!failure) + { + i->core->api_msgs->send_termination_msg(i->task_info); + } + i->core->api_core->task_finished(true, i->task_info); +} + +static void +rdp_fd_readable_cb(struct ev_loop *loop, ev_io *w, int revents) +{ + rdp_check_all_fds(loop, w); +} + +bool +rdp_connect(void *internals) +{ + rdp_internals *_i = internals; +#ifdef DEBUG + { + /* print settings before connect for debug */ + char buf[512]; + snprintf(buf, 511, + "rdp_module: settings:\n\tfntlm: %ld\n\t" + "height: %ld\n\thost: %s\n\tnomani: %ld\n\tnonla: %ld\n\t" + "notheme: %ld\n\tnotls: %ld\n\tnowallp: %ld\n\tnowdrag: " + "%ld\n\t" + "password: %s\n\tpcb: %s\n\tperf: %ld\n\tport: " + "%ld\n\tuser: %s\n\t" + "width: %ld", + _i->my_settings.fntlm.value, _i->my_settings.height.value, + _i->my_settings.host.value, _i->my_settings.nomani.value, + _i->my_settings.nonla.value, _i->my_settings.notheme.value, + _i->my_settings.notls.value, _i->my_settings.nowallp.value, + _i->my_settings.nowdrag.value, + _i->my_settings.password.value, _i->my_settings.pcb.value, + _i->my_settings.perf.value, _i->my_settings.port.value, + _i->my_settings.user.value, _i->my_settings.width.value); + _i->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), wrdp_log_level_trace, 0); + } +#endif /* DEBUG */ + if (!_i->my_settings.host.value || !_i->my_settings.host.value[0]) + { + const char *msg = "rdp_module: host is not set"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_param_error", _i->task_info); + return false; + } + if (!_i->my_settings.port.value) + { + const char *msg = "rdp_module: port is not set"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_param_error", _i->task_info); + return false; + } + if (!_i->my_settings.user.value || !_i->my_settings.user.value[0]) + { + const char *msg = "rdp_module: user is not set"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_param_error", _i->task_info); + return false; + } + /* password can be optional ? */ + if (freerdp_connect(_i->instance)) + { + int fdcount = 0, i; + HANDLE *fds[256]; + ZeroMemory(fds, sizeof(fds)); + + struct ev_loop *loop + = _i->core->api_core->get_libev_loop(_i->task_info); + fdcount = freerdp_get_event_handles( + _i->instance->context, (HANDLE *)fds, 256); + if (!fdcount) + { + const char *msg + = "Failed to get FreeRDP file descriptors"; + _i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_internal_error", _i->task_info); + return false; + } + _i->rdp_fd_count = fdcount; + for (i = 0; i < fdcount; ++i) + { + ULONG type; + WINPR_HANDLE *phandle = 0; + int fd = 0; + if (!winpr_Handle_GetInfo(fds[i], &type, &phandle)) + { + /* TODO: assume what this is not fatal */ + const char *msg = "winpr_Handle_GetInfo failed"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + continue; + } + fd = winpr_Handle_getFd(phandle); + + /* TODO: check type here, and set EV_WRITE if needed + * for now watch only for read available event*/ + ev_io_init( + &(_i->rdp_fd[i]), rdp_fd_readable_cb, fd, EV_READ); + _i->rdp_fd[i].data = internals; + ev_io_start(loop, &(_i->rdp_fd[i])); + } + return true; + } + { + const char *msg = freerdp_get_last_error_name( + freerdp_get_last_error(_i->instance->context)); + _i->core->api_msgs->send_error_msg(msg, _i->task_info); + } + + return false; +} + +static BOOL +rdp_pre_connect(freerdp *instance) +{ + rdpSettings *settings = instance->context->settings; + my_rdp_context *c = (my_rdp_context *)instance->context; + + RegisterUpdate(instance); + RegisterPrimary(instance); + + /* looks like this settings reducing artifacts level */ + settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_SCRBLT_INDEX] = FALSE; + + /* causing random disconnects */ + settings->OrderSupport[NEG_ATEXTOUT_INDEX] = FALSE; + settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; + settings->NoBitmapCompressionHeader = TRUE; + settings->BitmapCompressionDisabled = TRUE; + settings->BitmapCacheEnabled = FALSE; + settings->BrushSupportLevel = 0; + + /* causing connection failure on recent versions of freerdp */ + settings->OffscreenSupportLevel = 0; + settings->OffscreenCacheSize = 0; + + /* following required at least on windows 7 */ + settings->OrderSupport[NEG_MEMBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MEM3BLT_INDEX] = FALSE; + + /* following required for windows 2012,2016 */ + settings->FastPathOutput = 1; + + /* testing */ + /* wsgate settings */ + // settings->ColorDepth = 16; + // settings->FrameAcknowledge = 1; + // settings->LargePointerFlag = 1; + // settings->BitmapCacheV3Enabled = FALSE; + // settings->BitmapCachePersistEnabled = FALSE; + // settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; + // settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; // XXX + // settings->OrderSupport[NEG_SCRBLT_INDEX] = FALSE; // XXX + // settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; + // settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; + // settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; + // settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; + // settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = FALSE; // + //XXX settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; + // settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; + // settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; + // settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = FALSE; + // settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; + // settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; + // settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; + // settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; + // settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; + // settings->OrderSupport[NEG_POLYGON_SC_INDEX] = FALSE; + // settings->OrderSupport[NEG_POLYGON_CB_INDEX] = FALSE; + // settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; + // settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; + // settings->GlyphSupportLevel = GLYPH_SUPPORT_NONE; + + PubSub_SubscribeChannelConnected( + instance->context->pubSub, rdp_on_channel_connected); + PubSub_SubscribeChannelDisconnected( + instance->context->pubSub, rdp_on_channel_disconnected); + + freerdp_client_load_addins(instance->context->channels, settings); + instance->context->cache = cache_new(instance->context); + + c->my_internals->clrconv + = freerdp_clrconv_new(CLRCONV_ALPHA | CLRCONV_INVERT); + + if (!instance->context->cache) + { + return FALSE; + } + return TRUE; +} + +static BOOL +rdp_post_connect(freerdp *instance) +{ + my_rdp_context *c = (my_rdp_context *)instance->context; + my_rdp_clipboard *clip = 0; + rdpPointer p; + memset(&p, 0, sizeof(p)); + p.size = sizeof(my_rdp_pointer); + register_pointer(&p); + graphics_register_pointer(instance->context->graphics, &p); + pointer_cache_register_callbacks(instance->context->update); + + clip = rdp_clipboard_new(c); + if (!clip) + { + const char *msg = "rdp_module: \"rdp_clipboard_new\" failed"; + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + return FALSE; + } + c->clipboard = clip; + + c->my_internals->conn_state = rdp_conn_state_connected; + + { + const char *msg + = "rdp_module: \"C:RDP session connection started.\""; + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_trace, 0); + } + + c->my_internals->core->api_msgs->send_text_msg( + "C:RDP session connection started.", c->my_internals->task_info); + + instance->context->update->DesktopResize(instance->context); + + return TRUE; +} + +static DWORD +rdp_verify_certificate(freerdp *instance, const char *host, UINT16 port, + const char *common_name, const char *subject, const char *issuer, + const char *fingerprint, DWORD flags) +{ + my_rdp_context *c = (my_rdp_context *)instance->context; + char buf[512]; + snprintf(buf, 511, + "Certificate details:\n\tSubject: %s\n\tIssuer:" + " %s\n\tThumbprint: %s\n" + "The above X.509 certificate could not be verified, possibly " + "because you do not have " + "the CA certificate in your certificate store, or the " + "certificate has expired. " + "Please look at the OpenSSL documentation on how to add a " + "private CA to the store.", + subject, issuer, fingerprint); + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), wrdp_log_level_debug, 0); + + return TRUE; /* ?? */ +} + +/*static int +rdp_receive_channel_data (freerdp* instance, + UINT16 channelId, + BYTE* data, + int size, + int flags, + int total_size) +{ + return freerdp_channels_data ( + instance, channelId, data, size, flags, total_size); +}*/ + +static BOOL +rdp_context_new(freerdp *instance, rdpContext *context) +{ + return TRUE; +} + +static void +rdp_context_free(freerdp *instance, rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + if (context->cache) + { + cache_free(context->cache); + context->cache = NULL; + } + if (c->my_internals->clrconv) + { + freerdp_clrconv_free(c->my_internals->clrconv); + c->my_internals->clrconv = NULL; + } +} + +void +rdp_register_base(freerdp *r) +{ + r->PreConnect = rdp_pre_connect; + r->PostConnect = rdp_post_connect; + r->VerifyCertificateEx = rdp_verify_certificate; + // r->ReceiveChannelData = rdp_receive_channel_data; + r->ContextNew = rdp_context_new; + r->ContextFree = rdp_context_free; +} diff --git a/src/rdp/rdp_impl.h b/src/rdp/rdp_impl.h new file mode 100644 index 0000000..0e561ed --- /dev/null +++ b/src/rdp/rdp_impl.h @@ -0,0 +1,127 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +#include <ev.h> + +#include <freerdp/freerdp.h> +#include <winpr/clipboard.h> +#include <freerdp/client/cliprdr.h> + +#define CLRCONV_ALPHA 1 +#define CLRCONV_INVERT 2 +/* if defined RGB555 format is used when rendering with a 16-bit frame buffer + */ +#define CLRCONV_RGB555 4 +/* Supported Internal Buffer Formats */ +#define CLRBUF_16BPP 8 +#define CLRBUF_32BPP 32 + +struct _CLRCONV +{ + int alpha; + int invert; + int rgb555; + rdpPalette *palette; +}; +typedef struct _CLRCONV CLRCONV; +typedef CLRCONV *HCLRCONV; + +/* #define TAG CLIENT_TAG ("RDP")*/ + +typedef struct +{ + /* TODO: make variables list instead of static set */ + backend_setting_str host, pcb, user, password, + /* new settings from freerdp header:*/ + Domain, PasswordHash, AcceptedCert, ClientHostname, ClientProductId, + AlternateShell, ShellWorkingDirectory, ClientAddress, ClientDir, + DynamicDSTTimeZoneKeyName, RemoteAssistanceSessionId, + RemoteAssistancePassStub, RemoteAssistancePassword, + RemoteAssistanceRCTicket, AuthenticationServiceClass, + AllowedTlsCiphers, NtlmSamFile, PreconnectionBlob, + RedirectionAcceptedCert, KerberosKdc, KerberosRealm, + CertificateName, CertificateFile, PrivateKeyFile, + CertificateContent, PrivateKeyContent, WindowTitle, WmClass, + ComputerName, ConnectionFile, AssistanceFile, HomePath, ConfigPath, + CurrentPath, DumpRemoteFxFile, PlayRemoteFxFile, GatewayHostname, + GatewayUsername, GatewayPassword, GatewayDomain, GatewayAccessToken, + GatewayAcceptedCert, ProxyHostname, RemoteApplicationName, + RemoteApplicationIcon, RemoteApplicationProgram, + RemoteApplicationFile, RemoteApplicationGuid, + RemoteApplicationCmdLine, ImeFileName, DrivesToRedirect, + ActionScript; + backend_setting_int port, perf, fntlm, nowallp, nowdrag, nomani, + notheme, nonla, notls, width, height, + /* new settings from freerdp header:*/ + ServerMode, WaitForOutputBufferFlush, MaxTimeInCheckLoop, + DesktopWidth, DesktopHeight, Workarea, Fullscreen, GrabKeyboard, + Decorations, RdpVersion, ColorDepth, ExtSecurity, NlaSecurity, + TlsSecurity, RdpSecurity, NegotiateSecurityLayer, + RestrictedAdminModeRequired, MstscCookieMode, CookieMaxLength, + ClientBuild, KeyboardType, KeyboardSubType, KeyboardFunctionKey, + KeyboardLayout, UseRdpSecurityLayer, SaltedChecksum, ServerPort, + GatewayPort, DesktopResize, ToggleFullscreen, Floatbar, DesktopPosX, + DesktopPosY, SoftwareGdi, UnmapButtons, PerformanceFlags, + AllowFontSmoothing, AllowDesktopComposition, DisableWallpaper, + DisableFullWindowDrag, DisableMenuAnims, DisableThemes, + ConnectionType, EncryptionMethods, EncryptionLevel, FIPSMode, + CompressionEnabled, LogonNotify, BrushSupportLevel, + RemoteApplicationMode, TcpAckTimeout; +} my_rdp_settings; + +typedef struct +{ + rdpPointer pointer; + uint32_t id; +} my_rdp_pointer; + +struct rdp_internals_s; +typedef struct rdp_internals_s rdp_internals; + +struct my_rdp_clipboard_s; +typedef struct my_rdp_clipboard_s my_rdp_clipboard; + +struct my_rdp_ft_s; +typedef struct my_rdp_ft_s my_rdp_ft; + +typedef struct +{ + rdpContext context; + my_rdp_clipboard *clipboard; + my_rdp_ft *ft_to_client, *ft_to_server; + rdp_internals *my_internals; +} my_rdp_context; + +struct rdp_internals_s +{ + freerdp *instance; + my_rdp_context *context; + + rdpSettings *settings; + + void *task_info; + wrdp_core_exports *core; + wrdp_backend_module *backend; + + my_rdp_settings my_settings; + rdp_connection_state conn_state; + + /* libev watchers */ + /* TODO: dynamic allocation */ + ev_io rdp_fd[32]; + int rdp_fd_count; + + HCLRCONV clrconv; + uint32_t m_ptrId; +}; + +bool rdp_init_internals(void *internals); + +bool rdp_connect(void *internals); + +bool rdp_init_settings(void *internals); diff --git a/src/rdp/rdp_io.c b/src/rdp/rdp_io.c new file mode 100644 index 0000000..5754997 --- /dev/null +++ b/src/rdp/rdp_io.c @@ -0,0 +1,1492 @@ +#include <freerdp/client/channels.h> +#include <freerdp/client/cliprdr.h> +#include <freerdp/client/cmdline.h> +#include <freerdp/client/file.h> +#include <freerdp/constants.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> +#include <freerdp/log.h> +#include <winpr/crt.h> +#include <winpr/synch.h> + +#include <webrdp_core_api.h> +#include <webrdp_module_api.h> + +#include "rdp_backend_api.h" +#include "rdp_impl.h" +#include "rdp_png.h" + +const UINT32 ASCII_TO_SCANCODE[256] = { + 0, /* 0 */ + 0, /* 1 */ + 0, /* 2 */ + 0, /* 3 */ + 0, /* 4 */ + 0, /* 5 */ + 0, /* 6 */ + 0, /* 7 */ + RDP_SCANCODE_BACKSPACE, /* 8 */ + RDP_SCANCODE_TAB, /* 9 */ + VK_KEY_D, /* 10 */ + VK_KEY_F, /* 11 */ + VK_KEY_H, /* 12 */ + RDP_SCANCODE_RETURN, /* 13 */ + RDP_SCANCODE_BACKSPACE, /* 14 */ + VK_KEY_X, /* 15 */ + RDP_SCANCODE_LSHIFT, /* 16 */ + RDP_SCANCODE_LCONTROL, /* 17 */ + RDP_SCANCODE_LMENU, /* 18 */ + RDP_SCANCODE_PAUSE, /* 19 */ + RDP_SCANCODE_CAPSLOCK, /* 20 */ + VK_KEY_W, /* 21 */ + VK_KEY_E, /* 22 */ + VK_KEY_R, /* 23 */ + VK_KEY_Y, /* 24 */ + VK_KEY_A, /* 25 */ + VK_KEY_1, /* 26 */ + RDP_SCANCODE_ESCAPE, /* 27 */ + VK_KEY_3, /* 28 */ + VK_KEY_4, /* 29 */ + VK_KEY_6, /* 30 */ + VK_KEY_5, /* 31 */ + RDP_SCANCODE_SPACE, /* 32 */ + RDP_SCANCODE_PRIOR, /* 33 */ + RDP_SCANCODE_NEXT, /* 34 */ + RDP_SCANCODE_END, /* 35 */ + RDP_SCANCODE_HOME, /* 36 */ + RDP_SCANCODE_LEFT, /* 37 */ + RDP_SCANCODE_UP, /* 38 */ + RDP_SCANCODE_RIGHT, /* 39 */ + RDP_SCANCODE_DOWN, /* 40 */ + RDP_SCANCODE_KEY_0, /* 41 */ + RDP_SCANCODE_MULTIPLY, /* 42 */ + RDP_SCANCODE_ADD, /* 43 */ + RDP_SCANCODE_PRINTSCREEN, /* 44 */ + RDP_SCANCODE_INSERT, /* 45 */ + RDP_SCANCODE_DELETE, /* 46 */ + RDP_SCANCODE_DIVIDE, /* 47 */ + RDP_SCANCODE_KEY_0, /* 48 */ + RDP_SCANCODE_KEY_1, /* 49 */ + RDP_SCANCODE_KEY_2, /* 50 */ + RDP_SCANCODE_KEY_3, /* 51 */ + RDP_SCANCODE_KEY_4, /* 52 */ + RDP_SCANCODE_KEY_5, /* 53 */ + RDP_SCANCODE_KEY_6, /* 54 */ + RDP_SCANCODE_KEY_7, /* 55 */ + RDP_SCANCODE_KEY_8, /* 56 */ + RDP_SCANCODE_KEY_9, /* 57 */ + RDP_SCANCODE_OEM_1, /* 58 */ + RDP_SCANCODE_OEM_1, /* 59 */ + RDP_SCANCODE_OEM_COMMA, /* 60 */ + RDP_SCANCODE_OEM_PLUS, /* 61 */ + RDP_SCANCODE_OEM_PERIOD, /* 62 */ + RDP_SCANCODE_DIVIDE, /* 63 */ + RDP_SCANCODE_KEY_2, /* 64 */ + RDP_SCANCODE_KEY_A, /* 65 */ + RDP_SCANCODE_KEY_B, /* 66 */ + RDP_SCANCODE_KEY_C, /* 67 */ + RDP_SCANCODE_KEY_D, /* 68 */ + RDP_SCANCODE_KEY_E, /* 69 */ + RDP_SCANCODE_KEY_F, /* 70 */ + RDP_SCANCODE_KEY_G, /* 71 */ + RDP_SCANCODE_KEY_H, /* 72 */ + RDP_SCANCODE_KEY_I, /* 73 */ + RDP_SCANCODE_KEY_J, /* 74 */ + RDP_SCANCODE_KEY_K, /* 75 */ + RDP_SCANCODE_KEY_L, /* 76 */ + RDP_SCANCODE_KEY_M, /* 77 */ + RDP_SCANCODE_KEY_N, /* 78 */ + RDP_SCANCODE_KEY_O, /* 79 */ + RDP_SCANCODE_KEY_P, /* 80 */ + RDP_SCANCODE_KEY_Q, /* 81 */ + RDP_SCANCODE_KEY_R, /* 82 */ + RDP_SCANCODE_KEY_S, /* 83 */ + RDP_SCANCODE_KEY_T, /* 84 */ + RDP_SCANCODE_KEY_U, /* 85 */ + RDP_SCANCODE_KEY_V, /* 86 */ + RDP_SCANCODE_KEY_W, /* 87 */ + RDP_SCANCODE_KEY_X, /* 88 */ + RDP_SCANCODE_KEY_Y, /* 89 */ + RDP_SCANCODE_KEY_Z, /* 90 */ + RDP_SCANCODE_LWIN, /* 91 */ + RDP_SCANCODE_RWIN, /* 92 */ + RDP_SCANCODE_APPS, /* 93 */ + RDP_SCANCODE_KEY_6, /* 94 */ + RDP_SCANCODE_OEM_MINUS, /* 95 */ + RDP_SCANCODE_NUMPAD0, /* 96 */ + RDP_SCANCODE_NUMPAD1, /* 97 */ + RDP_SCANCODE_NUMPAD2, /* 98 */ + RDP_SCANCODE_NUMPAD3, /* 99 */ + RDP_SCANCODE_NUMPAD4, /* 100 */ + RDP_SCANCODE_NUMPAD5, /* 101 */ + RDP_SCANCODE_NUMPAD6, /* 102 */ + RDP_SCANCODE_NUMPAD7, /* 103 */ + RDP_SCANCODE_NUMPAD8, /* 104 */ + RDP_SCANCODE_NUMPAD9, /* 105 */ + RDP_SCANCODE_MULTIPLY, /* 106 */ + RDP_SCANCODE_ADD, /* 107 */ + 0, /* 108 */ + RDP_SCANCODE_SUBTRACT, /* 109 */ + RDP_SCANCODE_DELETE, /* 110 */ + RDP_SCANCODE_DIVIDE, /* 111 */ + RDP_SCANCODE_F1, /* 112 */ + RDP_SCANCODE_F2, /* 113 */ + RDP_SCANCODE_F3, /* 114 */ + RDP_SCANCODE_F4, /* 115 */ + RDP_SCANCODE_F5, /* 116 */ + RDP_SCANCODE_F6, /* 117 */ + RDP_SCANCODE_F7, /* 118 */ + RDP_SCANCODE_F8, /* 119 */ + RDP_SCANCODE_F9, /* 120 */ + RDP_SCANCODE_F10, /* 121 */ + RDP_SCANCODE_F11, /* 122 */ + RDP_SCANCODE_F12, /* 123 */ + RDP_SCANCODE_OEM_5, /* 124 */ + RDP_SCANCODE_OEM_6, /* 125 */ + VK_F4, /* 126 */ + VK_END, /* 127 */ + VK_F2, /* 128 */ + VK_NEXT, /* 129 */ + VK_F1, /* 130 */ + VK_LEFT, /* 131 */ + VK_RIGHT, /* 132 */ + VK_DOWN, /* 133 */ + VK_UP, /* 134 */ + 0, /* 135 */ + 0, /* 136 */ + 0, /* 137 */ + 0, /* 138 */ + 0, /* 139 */ + 0, /* 140 */ + 0, /* 141 */ + 0, /* 142 */ + 0, /* 143 */ + RDP_SCANCODE_NUMLOCK, /* 144 */ + RDP_SCANCODE_SCROLLLOCK, /* 145 */ + 0, /* 146 */ + 0, /* 147 */ + 0, /* 148 */ + 0, /* 149 */ + 0, /* 150 */ + 0, /* 151 */ + 0, /* 152 */ + 0, /* 153 */ + 0, /* 154 */ + 0, /* 155 */ + 0, /* 156 */ + 0, /* 157 */ + 0, /* 158 */ + 0, /* 159 */ + 0, /* 160 */ + 0, /* 161 */ + 0, /* 162 */ + 0, /* 163 */ + 0, /* 164 */ + 0, /* 165 */ + 0, /* 166 */ + 0, /* 167 */ + 0, /* 168 */ + 0, /* 169 */ + 0, /* 170 */ + 0, /* 171 */ + 0, /* 172 */ + RDP_SCANCODE_OEM_MINUS, /* 173 */ + 0, /* 174 */ + 0, /* 175 */ + 0, /* 176 */ + 0, /* 177 */ + 0, /* 178 */ + 0, /* 179 */ + 0, /* 180 */ + 0, /* 181 */ + 0, /* 182 */ + 0, /* 183 */ + 0, /* 184 */ + 0, /* 185 */ + RDP_SCANCODE_OEM_1, /* 186 */ + RDP_SCANCODE_OEM_PLUS, /* 187 */ + RDP_SCANCODE_OEM_COMMA, /* 188 */ + RDP_SCANCODE_OEM_MINUS, /* 189 */ + RDP_SCANCODE_OEM_PERIOD, /* 190 */ + RDP_SCANCODE_OEM_2, /* 191 */ + RDP_SCANCODE_OEM_3, /* 192 */ + 0, /* 193 */ + 0, /* 194 */ + 0, /* 195 */ + 0, /* 196 */ + 0, /* 197 */ + 0, /* 198 */ + 0, /* 199 */ + 0, /* 200 */ + 0, /* 201 */ + 0, /* 202 */ + 0, /* 203 */ + 0, /* 204 */ + 0, /* 205 */ + 0, /* 206 */ + 0, /* 207 */ + 0, /* 208 */ + 0, /* 209 */ + 0, /* 210 */ + 0, /* 211 */ + 0, /* 212 */ + 0, /* 213 */ + 0, /* 214 */ + 0, /* 215 */ + 0, /* 216 */ + 0, /* 217 */ + 0, /* 218 */ + RDP_SCANCODE_OEM_4, /* 219 */ + RDP_SCANCODE_OEM_5, /* 220 */ + RDP_SCANCODE_OEM_6, /* 221 */ + RDP_SCANCODE_OEM_7, /* 222 */ + 0, /* 223 */ + 0, /* 224 */ + 0, /* 225 */ + 0, /* 226 */ + 0, /* 227 */ + 0, /* 228 */ + 0, /* 229 */ + 0, /* 230 */ + 0, /* 231 */ + 0, /* 232 */ + 0, /* 233 */ + 0, /* 234 */ + 0, /* 235 */ + 0, /* 236 */ + 0, /* 237 */ + 0, /* 238 */ + 0, /* 239 */ + 0, /* 240 */ + 0, /* 241 */ + 0, /* 242 */ + 0, /* 243 */ + 0, /* 244 */ + 0, /* 245 */ + 0, /* 246 */ + 0, /* 247 */ + 0, /* 248 */ + 0, /* 249 */ + 0, /* 250 */ + 0, /* 251 */ + 0, /* 252 */ + 0, /* 253 */ + 0, /* 254 */ + 0 /* 255 */ +}; + +#define RGB_555_888(_r, _g, _b) \ + _r = (_r << 3 & ~0x7) | (_r >> 2); \ + _g = (_g << 3 & ~0x7) | (_g >> 2); \ + _b = (_b << 3 & ~0x7) | (_b >> 2); +#define RGB_565_888(_r, _g, _b) \ + _r = (_r << 3 & ~0x7) | (_r >> 2); \ + _g = (_g << 2 & ~0x3) | (_g >> 4); \ + _b = (_b << 3 & ~0x7) | (_b >> 2); +#define GetRGB_555(_r, _g, _b, _p) \ + _r = (_p & 0x7C00) >> 10; \ + _g = (_p & 0x3E0) >> 5; \ + _b = (_p & 0x1F); +#define GetRGB_565(_r, _g, _b, _p) \ + _r = (_p & 0xF800) >> 11; \ + _g = (_p & 0x7E0) >> 5; \ + _b = (_p & 0x1F); +#define GetRGB15(_r, _g, _b, _p) \ + GetRGB_555(_r, _g, _b, _p); \ + RGB_555_888(_r, _g, _b); +#define GetRGB16(_r, _g, _b, _p) \ + GetRGB_565(_r, _g, _b, _p); \ + RGB_565_888(_r, _g, _b); +#define RGB24(_r, _g, _b) ((_r << 16) | (_g << 8) | _b) +#define GetRGB24(_r, _g, _b, _p) \ + _r = (_p & 0xFF0000) >> 16; \ + _g = (_p & 0xFF00) >> 8; \ + _b = (_p & 0xFF); +#define GetRGB32(_r, _g, _b, _p) \ + _r = (_p & 0xFF0000) >> 16; \ + _g = (_p & 0xFF00) >> 8; \ + _b = (_p & 0xFF); +#define ARGB32(_a, _r, _g, _b) (_a << 24) | (_r << 16) | (_g << 8) | _b +#define RGB16(_r, _g, _b) \ + (((_r >> 3) & 0x1F) << 11) | (((_g >> 2) & 0x3F) << 5) \ + | ((_b >> 3) & 0x1F) +#define RGB15(_r, _g, _b) \ + (((_r >> 3) & 0x1F) << 10) | (((_g >> 3) & 0x1F) << 5) \ + | ((_b >> 3) & 0x1F) +#define GetARGB32(_a, _r, _g, _b, _p) \ + _a = (_p & 0xFF000000) >> 24; \ + _r = (_p & 0xFF0000) >> 16; \ + _g = (_p & 0xFF00) >> 8; \ + _b = (_p & 0xFF); + +static HCLRCONV +freerdp_clrconv_new(UINT32 flags) +{ + HCLRCONV clrconv; + + clrconv = (CLRCONV *)calloc(1, sizeof(CLRCONV)); + + if (!clrconv) + return NULL; + + clrconv->alpha = (flags & CLRCONV_ALPHA) ? TRUE : FALSE; + clrconv->invert = (flags & CLRCONV_INVERT) ? TRUE : FALSE; + clrconv->rgb555 = (flags & CLRCONV_RGB555) ? TRUE : FALSE; + + clrconv->palette = (rdpPalette *)calloc(1, sizeof(rdpPalette)); + + if (!clrconv->palette) + { + free(clrconv); + return NULL; + } + + return clrconv; +} + +static void +freerdp_clrconv_free(HCLRCONV clrconv) +{ + if (clrconv) + { + free(clrconv->palette); + free(clrconv); + } +} + +static int +freerdp_get_pixel(BYTE *data, int x, int y, int width, int height, int bpp) +{ + int start; + int shift; + UINT16 *src16; + UINT32 *src32; + int red, green, blue; + + switch (bpp) + { + case 1: + width = (width + 7) / 8; + start = (y * width) + x / 8; + shift = x % 8; + return (data[start] & (0x80 >> shift)) != 0; + case 8: + return data[y * width + x]; + case 15: + case 16: + src16 = (UINT16 *)data; + return src16[y * width + x]; + case 24: + data += y * width * 3; + data += x * 3; + red = data[0]; + green = data[1]; + blue = data[2]; + return RGB24(red, green, blue); + case 32: + src32 = (UINT32 *)data; + return src32[y * width + x]; + default: + break; + } + + return 0; +} + +static void +freerdp_set_pixel( + BYTE *data, int x, int y, int width, int height, int bpp, int pixel) +{ + int start; + int shift; + int *dst32; + + if (bpp == 1) + { + width = (width + 7) / 8; + start = (y * width) + x / 8; + shift = x % 8; + if (pixel) + data[start] = data[start] | (0x80 >> shift); + else + data[start] = data[start] & ~(0x80 >> shift); + } + else if (bpp == 32) + { + dst32 = (int *)data; + dst32[y * width + x] = pixel; + } +} + +static void +freerdp_color_split_rgb(UINT32 *color, int bpp, BYTE *red, BYTE *green, + BYTE *blue, BYTE *alpha, HCLRCONV clrconv) +{ + *red = *green = *blue = 0; + *alpha = (clrconv->alpha) ? 0xFF : 0x00; + + switch (bpp) + { + case 32: + if (clrconv->alpha) + { + GetARGB32(*alpha, *red, *green, *blue, *color); + } + else + { + GetRGB32(*red, *green, *blue, *color); + } + break; + + case 24: + GetRGB24(*red, *green, *blue, *color); + break; + + case 16: + GetRGB16(*red, *green, *blue, *color); + break; + + case 15: + GetRGB15(*red, *green, *blue, *color); + break; + + case 8: + *color &= 0xFF; + *red = clrconv->palette->entries[*color].red; + *green = clrconv->palette->entries[*color].green; + *blue = clrconv->palette->entries[*color].blue; + break; + + case 1: + if (*color != 0) + { + *red = 0xFF; + *green = 0xFF; + *blue = 0xFF; + } + break; + + default: + break; + } +} + +static void +freerdp_color_make_rgb(UINT32 *color, int bpp, BYTE *red, BYTE *green, + BYTE *blue, BYTE *alpha, HCLRCONV clrconv) +{ + switch (bpp) + { + case 32: + *color = ARGB32(*alpha, *red, *green, *blue); + break; + + case 24: + *color = RGB24(*red, *green, *blue); + break; + + case 16: + if (clrconv->rgb555) + { + *color = RGB15(*red, *green, *blue); + } + else + { + *color = RGB16(*red, *green, *blue); + } + break; + + case 15: + *color = RGB15(*red, *green, *blue); + break; + + case 8: + *color = RGB24(*red, *green, *blue); + break; + + case 1: + if ((*red != 0) || (*green != 0) || (*blue != 0)) + *color = 1; + break; + + default: + break; + } +} + +static UINT32 +freerdp_color_convert_rgb( + UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv) +{ + BYTE red = 0; + BYTE green = 0; + BYTE blue = 0; + BYTE alpha = 0xFF; + UINT32 dstColor = 0; + + freerdp_color_split_rgb( + &srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv); + freerdp_color_make_rgb( + &dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv); + + return dstColor; +} + +static void +freerdp_alpha_cursor_convert(BYTE *alphaData, BYTE *xorMask, BYTE *andMask, + int width, int height, int bpp, HCLRCONV clrconv) +{ + UINT32 xorPixel; + UINT32 andPixel; + UINT32 x, y, jj; + + for (y = 0; y < height; y++) + { + jj = (bpp == 1) ? y : (height - 1) - y; + + for (x = 0; x < width; x++) + { + xorPixel = freerdp_get_pixel( + xorMask, x, jj, width, height, bpp); + xorPixel = freerdp_color_convert_rgb( + xorPixel, bpp, 32, clrconv); + andPixel = freerdp_get_pixel( + andMask, x, jj, width, height, 1); + + if (andPixel) + { + if ((xorPixel & 0xFFFFFF) == 0xFFFFFF) + { + /* use pattern (not solid black) for xor + * area */ + xorPixel = (x & 1) == (y & 1); + xorPixel = xorPixel ? 0xFFFFFF : 0; + xorPixel |= 0xFF000000; + } + else if (xorPixel == 0xFF000000) + { + xorPixel = 0; + } + } + + freerdp_set_pixel( + alphaData, x, y, width, height, 32, xorPixel); + } + } +} + +static void +freerdp_bitmap_flip(BYTE *src, BYTE *dst, int scanLineSz, int height) +{ + int i; + + BYTE *bottomLine = dst + (scanLineSz * (height - 1)); + BYTE *topLine = src; + + /* Special processing if called for flip-in-place. */ + if (src == dst) + { + /* Allocate a scanline buffer. + * (FIXME: malloc / xfree below should be replaced by "get/put + * scanline buffer from a pool/Q of fixed buffers" to reuse + * fixed size buffers (of max scanline size (or adaptative?) ) + * -- would be much faster). + */ + BYTE *tmpBfr = (BYTE *)_aligned_malloc(scanLineSz, 16); + int half = height / 2; + /* Flip buffer in place by line permutations through the temp + * scan line buffer. + * Note that if height has an odd number of line, we don't need + * to move the center scanline anyway. + * Also note that in place flipping takes three memcpy() calls + * to process two scanlines while src to distinct dest would + * only requires two memcpy() calls for two scanlines. + */ + height--; + for (i = 0; i < half; i++) + { + CopyMemory(tmpBfr, topLine, scanLineSz); + CopyMemory(topLine, bottomLine, scanLineSz); + CopyMemory(bottomLine, tmpBfr, scanLineSz); + topLine += scanLineSz; + bottomLine -= scanLineSz; + height--; + } + _aligned_free(tmpBfr); + } + /* Flip from source buffer to destination buffer. */ + else + { + + for (i = 0; i < height; i++) + { + CopyMemory(bottomLine, topLine, scanLineSz); + topLine += scanLineSz; + bottomLine -= scanLineSz; + } + } +} + +static BYTE * +freerdp_image_flip(BYTE *srcData, BYTE *dstData, int width, int height, int bpp) +{ + int scanline; + + scanline = width * ((bpp + 7) / 8); + + if (!dstData) + dstData = (BYTE *)_aligned_malloc( + width * height * ((bpp + 7) / 8), 16); + + if (!dstData) + { + return NULL; + } + + freerdp_bitmap_flip(srcData, dstData, scanline, height); + + return dstData; +} + +/* outgoing rdp protocol to client implementation */ + +static BOOL +Pointer_New(rdpContext *context, rdpPointer *pointer) +{ + my_rdp_context *c = (my_rdp_context *)context; + HCLRCONV hclrconv = c->my_internals->clrconv; + size_t psize = pointer->width * pointer->height * 4; + + my_rdp_pointer *p = (my_rdp_pointer *)pointer; + p->id = c->my_internals->m_ptrId++; + uint8_t *pixels = malloc(sizeof(uint8_t) * psize); + memset(pixels, 0, psize); + if ((pointer->andMaskData != 0) && (pointer->xorMaskData != 0)) + { + //XXX + freerdp_alpha_cursor_convert(pixels, pointer->xorMaskData, + pointer->andMaskData, pointer->width, pointer->height, + pointer->xorBpp, hclrconv); + } + + //check if the cursor is fully transparent + bool transparent = TRUE; + for (int y = 0; y < pointer->height; y++) + { + for (int x = 0; x < pointer->width; x++) + { + if (pixels[0 + x * 4 + y * 4 * pointer->width] != 0) + { + transparent = FALSE; + } + } + } + if (transparent) + { + pixels[3] = 1; + } + + rdp_png_buf png_buf; + memset(&png_buf, 0, sizeof(png_buf)); + png_buf.buf_size + = (pointer->width * pointer->height * sizeof(uint32_t)) + + 8 /* allocating fullsize 32but argb pixels + png header */; + png_buf.buf = malloc(png_buf.buf_size); + png_generate_from_argb( + pointer->width, pointer->height, pixels, &png_buf); + + struct + { + unsigned char resL; //2 bytes reserved must always be 0 + unsigned char resH; + unsigned char imgTypeL; //2 bytes image type. 1 = ICO, 2 = CUR + unsigned char imgTypeH; + unsigned char nbImgL; //2 bytes number of images + unsigned char nbImgH; + unsigned char width; //1 byte image width in pixels + unsigned char height; //1 byte image height in pixels + unsigned char nbColors; //1 bytenumber of colors. 0 if not using + //a color pallete + unsigned char res; //1 byte reserved, should be 0 + unsigned char xPosL; //2 bytes of hot spot x (for ICO, color + //planes, 0 or 1) + unsigned char xPosH; + unsigned char + yPosL; //2 bytes of hot spot y (for ICO bits per pixel) + unsigned char yPosH; + unsigned char sizeLL; //4 bytes of image size + unsigned char sizeLH; + unsigned char sizeHL; + unsigned char sizeHH; + unsigned char offsetLL; //4 bytes of offset of the data + unsigned char offsetLH; + unsigned char offsetHL; + unsigned char offsetHH; + } curHeader = {0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + (unsigned char)pointer->width, (unsigned char)pointer->height, 0x00, + 0x00, (unsigned char)pointer->xPos, + (unsigned char)(pointer->xPos >> 8), (unsigned char)pointer->yPos, + (unsigned char)(pointer->yPos >> 8), (unsigned char)png_buf.written, + (unsigned char)(png_buf.written >> 8), + (unsigned char)(png_buf.written >> 16), + (unsigned char)(png_buf.written >> 24), 0x16, 0x00, 0x00, 0x00}; + + size_t cursor_buf_size = png_buf.written + sizeof(curHeader); + uint8_t *cursor_buf = malloc(cursor_buf_size); + + memcpy(cursor_buf, &curHeader, sizeof(curHeader)); + memcpy(cursor_buf + sizeof(curHeader), png_buf.buf, png_buf.written); + free(png_buf.buf); + + /* rdp_cursor_add (p->id, cursor_buf, cursor_buf_size, + * c->my_internals); */ + + free(pixels); + struct + { + uint32_t id; + uint32_t hx; + uint32_t hy; + } tmp = {p->id, pointer->xPos, pointer->yPos}; + size_t send_buf_size = sizeof(tmp) + cursor_buf_size; + uint8_t *send_buf = malloc(send_buf_size); + memcpy(send_buf, &tmp, sizeof(tmp)); + memcpy(send_buf + sizeof(tmp), cursor_buf, cursor_buf_size); + free(cursor_buf); + c->my_internals->core->send_ptr_new( + send_buf, send_buf_size, c->my_internals->task_info); + return TRUE; +} + +static void +Pointer_Free(rdpContext *context, rdpPointer *pointer) +{ + my_rdp_pointer *p = (my_rdp_pointer *)pointer; + my_rdp_context *c = (my_rdp_context *)context; + if (p->id) + { + /* rdp_cursor_erase (p->id, c->my_internals); */ + c->my_internals->core->send_ptr_free((uint8_t *)&(p->id), + sizeof(uint32_t), c->my_internals->task_info); + } +} + +static BOOL +Pointer_Set(rdpContext *context, const rdpPointer *pointer) +{ + my_rdp_pointer *p = (my_rdp_pointer *)pointer; + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->send_ptr_set( + (uint8_t *)&(p->id), sizeof(uint32_t), c->my_internals->task_info); + return TRUE; +} + +static BOOL +Pointer_SetNull(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->send_ptr_set_null(c->my_internals->task_info); + return TRUE; +} + +static BOOL +Pointer_SetDefault(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->send_ptr_set_default(c->my_internals->task_info); + return TRUE; +} + +void +register_pointer(rdpPointer *p) +{ + p->New = Pointer_New; + p->Free = Pointer_Free; + p->Set = Pointer_Set; + p->SetNull = Pointer_SetNull; + p->SetDefault = Pointer_SetDefault; +} + +static BOOL +rdp_context_new(freerdp *instance, rdpContext *context) +{ + return TRUE; +} + +static void +rdp_context_free(freerdp *instance, rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + if (context->cache) + { + cache_free(context->cache); + context->cache = NULL; + } + if (c->my_internals->clrconv) + { + freerdp_clrconv_free(c->my_internals->clrconv); + c->my_internals->clrconv = NULL; + } +} + +static BOOL +BeginPaint(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->send_begin_paint(c->my_internals->task_info); + return TRUE; +} + +static BOOL +EndPaint(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + c->my_internals->core->send_end_paint(c->my_internals->task_info); + return TRUE; +} + +static BOOL +SetBounds(rdpContext *context, const rdpBounds *bounds) +{ + my_rdp_context *c = (my_rdp_context *)context; + rdpBounds lB; + if (bounds) + { + memcpy(&lB, bounds, sizeof(rdpBounds)); + lB.right++; + lB.bottom++; + } + else + { + memset(&lB, 0, sizeof(rdpBounds)); + } + c->my_internals->core->send_set_bounds( + (uint8_t *)&lB, sizeof(lB), c->my_internals->task_info); + return TRUE; +} + +static BOOL +Synchronize(rdpContext *context) +{ + return TRUE; +} + +static BOOL +DesktopResize(rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + char buf[64]; + snprintf(buf, 63, "R:%dx%d", context->settings->DesktopWidth, + context->settings->DesktopHeight); + c->my_internals->core->send_text_msg(buf, c->my_internals->task_info); + return TRUE; +} + +static BOOL +BitmapUpdate(rdpContext *context, const BITMAP_UPDATE *bitmap) +{ + my_rdp_context *c = (my_rdp_context *)context; + int i; + BITMAP_DATA *bmd; + for (i = 0; i < (int)bitmap->number; i++) + { + bmd = &bitmap->rectangles[i]; + struct + { + uint32_t x; + uint32_t y; + uint32_t w; + uint32_t h; + uint32_t dw; + uint32_t dh; + uint32_t bpp; + uint32_t cf; + uint32_t sz; + } wxbm = {bmd->destLeft, bmd->destTop, bmd->width, bmd->height, + bmd->destRight - bmd->destLeft + 1, + bmd->destBottom - bmd->destTop + 1, bmd->bitsPerPixel, + (uint32_t)bmd->compressed, bmd->bitmapLength}; + if (!bmd->compressed) + { + freerdp_image_flip(bmd->bitmapDataStream, + bmd->bitmapDataStream, bmd->width, bmd->height, + bmd->bitsPerPixel); + } + size_t buf_size = sizeof(wxbm) + bmd->bitmapLength; + uint8_t *buf = malloc(buf_size); + memcpy(buf, &wxbm, sizeof(wxbm)); + memcpy(buf + sizeof(wxbm), bmd->bitmapDataStream, + bmd->bitmapLength); + c->my_internals->core->send_bitmap( + buf, buf_size, c->my_internals->task_info); + free(buf); + } + return TRUE; +} + +static BOOL +Palette(rdpContext *c, const PALETTE_UPDATE *p) +{ + return TRUE; +} + +static BOOL +PlaySound(rdpContext *c, const PLAY_SOUND_UPDATE *s) +{ + return TRUE; +} + +static BOOL +RefreshRect(rdpContext *c, UINT8 hz, const RECTANGLE_16 *r) +{ + return TRUE; +} + +static BOOL +SuppressOutput(rdpContext *c, UINT8 hz, const RECTANGLE_16 *r) +{ + return TRUE; +} + +/*static BOOL +SurfaceCommand(rdpContext* c, wStream* s) +{ + return TRUE; +}*/ + +static BOOL +SurfaceBits(rdpContext *c, const SURFACE_BITS_COMMAND *sbc) +{ + return TRUE; +} + +/*static BOOL +SurfaceFrameMarker(rdpContext* c, SURFACE_FRAME_MARKER* sfm) +{ + return TRUE; +} */ + +void +RegisterUpdate(freerdp *rdp) +{ + rdp->update->BeginPaint = BeginPaint; + rdp->update->EndPaint = EndPaint; + rdp->update->SetBounds = SetBounds; + rdp->update->Synchronize = Synchronize; + rdp->update->DesktopResize = DesktopResize; + rdp->update->BitmapUpdate = BitmapUpdate; + rdp->update->Palette = Palette; + rdp->update->PlaySound = PlaySound; + rdp->update->SurfaceBits = SurfaceBits; + + rdp->update->RefreshRect = RefreshRect; + rdp->update->SuppressOutput = SuppressOutput; +} + +static BOOL +DstBlt(rdpContext *c, const DSTBLT_ORDER *_do) +{ + return TRUE; +} + +static BOOL +PatBlt(rdpContext *ctx, PATBLT_ORDER *po) +{ + my_rdp_context *c = (my_rdp_context *)ctx; + uint32_t rop3 = gdi_rop3_code(po->bRop); + if (GDI_BS_SOLID == po->brush.style) + { + struct + { + int32_t x; + int32_t y; + int32_t w; + int32_t h; + uint32_t fg; + uint32_t rop; + } tmp = {po->nLeftRect, po->nTopRect, po->nWidth, po->nHeight, + ConvertColor(po->foreColor, PIXEL_FORMAT_BGR16, + PIXEL_FORMAT_ABGR32, + NULL), //XXX + rop3}; + c->my_internals->core->send_pat_blt( + (uint8_t *)&tmp, sizeof(tmp), c->my_internals->task_info); + } + return TRUE; +} + +static BOOL +ScrBlt(rdpContext *ctx, const SCRBLT_ORDER *sbo) +{ + my_rdp_context *c = (my_rdp_context *)ctx; + uint32_t rop3 = gdi_rop3_code(sbo->bRop); + struct + { + uint32_t rop; + int32_t x; + int32_t y; + int32_t w; + int32_t h; + int32_t sx; + int32_t sy; + } tmp = {rop3, sbo->nLeftRect, sbo->nTopRect, sbo->nWidth, sbo->nHeight, + sbo->nXSrc, sbo->nYSrc}; + c->my_internals->core->send_scr_blt( + (uint8_t *)&tmp, sizeof(tmp), c->my_internals->task_info); + return TRUE; +} + +static BOOL +OpaqueRect(rdpContext *context, const OPAQUE_RECT_ORDER *oro) +{ + my_rdp_context *c = (my_rdp_context *)context; + OPAQUE_RECT_ORDER oro2 = *oro; + oro2.color = ConvertColor( + oro2.color, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL); + c->my_internals->core->send_opaque_rect_order((uint8_t *)&oro2, + sizeof(OPAQUE_RECT_ORDER), c->my_internals->task_info); + return TRUE; +} + +static BOOL +DrawNineGrid(rdpContext *c, const DRAW_NINE_GRID_ORDER *dngo) +{ + return TRUE; +} + +static BOOL +MultiDstBlt(rdpContext *c, const MULTI_DSTBLT_ORDER *mdo) +{ + return TRUE; +} + +static BOOL +MultiPatBlt(rdpContext *c, const MULTI_PATBLT_ORDER *mpo) +{ + return TRUE; +} + +static BOOL +MultiScrBlt(rdpContext *c, const MULTI_SCRBLT_ORDER *mso) +{ + return TRUE; +} + +static BOOL +MultiOpaqueRect(rdpContext *context, const MULTI_OPAQUE_RECT_ORDER *moro) +{ + my_rdp_context *c = (my_rdp_context *)context; + uint32_t color = ConvertColor( + moro->color, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_ABGR32, NULL); + uint32_t nr = moro->numRectangles; + size_t buf_size + = (sizeof(color) + sizeof(nr)) + (sizeof(DELTA_RECT) * nr); + uint8_t *buf = malloc(buf_size); + memcpy(buf, (uint8_t *)&color, sizeof(color)); + memcpy(buf + sizeof(color), (uint8_t *)&nr, sizeof(nr)); + memcpy(buf + (sizeof(color) + sizeof(color)), + (uint8_t *)&moro->rectangles[1], sizeof(DELTA_RECT) * nr); + c->my_internals->core->send_multi_opaque_rect( + buf, buf_size, c->my_internals->task_info); + free(buf); + return TRUE; +} + +static BOOL +MultiDrawNineGrid(rdpContext *c, const MULTI_DRAW_NINE_GRID_ORDER *mdngo) +{ + return TRUE; +} + +static BOOL +LineTo(rdpContext *c, const LINE_TO_ORDER *lto) +{ + return TRUE; +} + +static BOOL +Polyline(rdpContext *c, const POLYLINE_ORDER *po) +{ + return TRUE; +} + +static BOOL +MemBlt(rdpContext *c, MEMBLT_ORDER *mo) +{ + return TRUE; +} + +static BOOL +Mem3Blt(rdpContext *c, MEM3BLT_ORDER *mo) +{ + return TRUE; +} + +static BOOL +SaveBitmap(rdpContext *c, const SAVE_BITMAP_ORDER *sbo) +{ + return TRUE; +} + +static BOOL +GlyphIndex(rdpContext *c, GLYPH_INDEX_ORDER *gio) +{ + return TRUE; +} + +static BOOL +FastIndex(rdpContext *c, const FAST_INDEX_ORDER *fio) +{ + return TRUE; +} + +static BOOL +FastGlyph(rdpContext *c, const FAST_GLYPH_ORDER *fgo) +{ + return TRUE; +} + +static BOOL +PolygonSC(rdpContext *c, const POLYGON_SC_ORDER *pso) +{ + return TRUE; +} + +static BOOL +PolygonCB(rdpContext *c, POLYGON_CB_ORDER *pco) +{ + return TRUE; +} + +static BOOL +EllipseSC(rdpContext *c, const ELLIPSE_SC_ORDER *eso) +{ + return TRUE; +} + +static BOOL +EllipseCB(rdpContext *c, const ELLIPSE_CB_ORDER *eco) +{ + return TRUE; +} +void +RegisterPrimary(freerdp *rdp) +{ + rdp->update->primary->DstBlt = DstBlt; + rdp->update->primary->PatBlt = PatBlt; + rdp->update->primary->ScrBlt = ScrBlt; + rdp->update->primary->OpaqueRect = OpaqueRect; + rdp->update->primary->DrawNineGrid = DrawNineGrid; + rdp->update->primary->MultiDstBlt = MultiDstBlt; + rdp->update->primary->MultiPatBlt = MultiPatBlt; + rdp->update->primary->MultiScrBlt = MultiScrBlt; + rdp->update->primary->MultiOpaqueRect = MultiOpaqueRect; + rdp->update->primary->MultiDrawNineGrid = MultiDrawNineGrid; + rdp->update->primary->LineTo = LineTo; + rdp->update->primary->Polyline = Polyline; + rdp->update->primary->MemBlt = MemBlt; + rdp->update->primary->Mem3Blt = Mem3Blt; + rdp->update->primary->SaveBitmap = SaveBitmap; + rdp->update->primary->GlyphIndex = GlyphIndex; + rdp->update->primary->FastIndex = FastIndex; + rdp->update->primary->FastGlyph = FastGlyph; + rdp->update->primary->PolygonSC = PolygonSC; + rdp->update->primary->PolygonCB = PolygonCB; + rdp->update->primary->EllipseSC = EllipseSC; + rdp->update->primary->EllipseCB = EllipseCB; +} + +/* incomming input from client handlers */ + +static bool +rdp_backend_handle_input_mouse(ws_input_mouse input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->input; + _i->core->reset_idle(_i->task_info); + inp->MouseEvent(inp, input.flags, input.x, input.y); + return true; +} + +static bool +rdp_backend_handle_input_kupdown(ws_input_kupdown input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->input; + if (0 < input.code) + { + _i->core->reset_idle(_i->task_info); + /*log::info << "257 >> tcode: " << input.code << "\n"; */ + /* make byte */ + input.code = RDP_SCANCODE_CODE(input.code); + /* apply extended */ + input.code = ASCII_TO_SCANCODE[input.code]; + /* extract extended sepparatelly in tflag */ + uint32_t tflag = RDP_SCANCODE_EXTENDED(input.code) ? + KBD_FLAGS_EXTENDED : + 0; + /* now make the call */ + freerdp_input_send_keyboard_event(inp, + (input.down ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE) | tflag, + input.code); + } + return true; +} + +static bool +rdp_backend_handle_input_kpress(ws_input_kpress input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->input; + _i->core->reset_idle(_i->task_info); + if (0x20 < input.code) + { + if (input.shiftstate & 6) + { + //Control and or Alt: Must use scan-codes since + //unicode-event can't handle these + if (((64 < input.code) && (91 > input.code)) + || ((96 < input.code) && (123 > input.code))) + { + input.code -= (input.shiftstate & 1) ? 0 : 32; + input.code + = GetVirtualScanCodeFromVirtualKeyCode( + input.code, 4); + if (0 < input.code) + { + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_DOWN, + ASCII_TO_SCANCODE[input.code]); + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_RELEASE, + ASCII_TO_SCANCODE[input.code]); + } + } + } + else + { + if (0 < input.code) + { + if (input.code == 96) + { + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, + RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, + RDP_SCANCODE_DELETE); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + RDP_SCANCODE_DELETE); + } + + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, + ASCII_TO_SCANCODE[input.code]); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + ASCII_TO_SCANCODE[input.code]); + } + } + } + else + { + if (0 < input.code) + { + input.code = RDP_SCANCODE_CODE(input.code); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, ASCII_TO_SCANCODE[input.code]); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, ASCII_TO_SCANCODE[input.code]); + } + } + return true; +} + +static bool +rdp_backend_handle_input_kcomb(ws_input_keycomb input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->input; + _i->core->reset_idle(_i->task_info); + switch (input) + { + case ws_keycomb_ctrlaltdel_press: + { + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_DELETE); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_DELETE); + } + break; + case ws_keycomb_alttab_press: + { + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_TAB); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_TAB); + } + break; + case ws_keycomb_alttab_release: + { + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_LMENU); + } + break; + default: + break; + } + return true; +} + +static bool +rdp_backend_handle_input_unicode(ws_input_unicode input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->input; + int i; + _i->core->reset_idle(_i->task_info); + for (i = 0; i < input.length; ++i) + { + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_DOWN, (UINT16)input.input[i]); + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_RELEASE, (UINT16)input.input[i]); + } + return true; +} + +void +register_input(wrdp_backend_module *backend) +{ + backend->backend_handle_input_kcomb = rdp_backend_handle_input_kcomb; + backend->backend_handle_input_kupdown + = rdp_backend_handle_input_kupdown; + backend->backend_handle_input_kpress = rdp_backend_handle_input_kpress; + backend->backend_handle_input_mouse = rdp_backend_handle_input_mouse; + backend->backend_handle_input_unicode + = rdp_backend_handle_input_unicode; +} + +static BOOL +rdp_pre_connect(freerdp *instance) +{ + rdpSettings *settings = instance->settings; + my_rdp_context *c = (my_rdp_context *)instance->context; + + RegisterUpdate(instance); + RegisterPrimary(instance); + + /* looks like this settings reducing artifacts level */ + settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_SCRBLT_INDEX] = FALSE; + + /* causing random disconnects */ + settings->OrderSupport[NEG_ATEXTOUT_INDEX] = FALSE; + settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; + settings->NoBitmapCompressionHeader = TRUE; + settings->BitmapCompressionDisabled = TRUE; + settings->FastPathOutput = FALSE; + settings->BitmapCacheEnabled = FALSE; + settings->BrushSupportLevel = 0; + + /* causing connection failure on recent versions of freerdp */ + settings->OffscreenSupportLevel = 0; + settings->OffscreenCacheSize = 0; + + /* following required at least on windows 7 */ + settings->OrderSupport[NEG_MEMBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MEM3BLT_INDEX] = FALSE; + + instance->context->cache = cache_new(settings); + + c->my_internals->clrconv + = freerdp_clrconv_new(CLRCONV_ALPHA | CLRCONV_INVERT); + + if (!instance->context->cache) + { + return FALSE; + } + return TRUE; +} + +static BOOL +rdp_post_connect(freerdp *instance) +{ + my_rdp_context *c = (my_rdp_context *)instance->context; + c->my_internals->conn_state = rdp_conn_state_connected; + rdpPointer p; + memset(&p, 0, sizeof(p)); + p.size = sizeof(my_rdp_pointer); + register_pointer(&p); + graphics_register_pointer(instance->context->graphics, &p); + pointer_cache_register_callbacks(instance->update); + + c->my_internals->core->send_text_msg( + "C:RDP session connection started.", c->my_internals->task_info); + + instance->update->DesktopResize(instance->context); + + return TRUE; +} + +static DWORD +rdp_verify_certificate(freerdp *instance, const char *common_name, + const char *subject, const char *issuer, const char *fingerprint, + BOOL host_mismatch) +{ + WLog_INFO(TAG, "Certificate details:"); + WLog_INFO(TAG, "\tSubject: %s", subject); + WLog_INFO(TAG, "\tIssuer: %s", issuer); + WLog_INFO(TAG, "\tThumbprint: %s", fingerprint); + WLog_INFO(TAG, + "The above X.509 certificate could not be verified, possibly " + "because you do not have " + "the CA certificate in your certificate store, or the " + "certificate has expired. " + "Please look at the OpenSSL documentation on how to add a " + "private CA to the store."); + + return TRUE; /* ?? */ +} + +/*static int +rdp_receive_channel_data (freerdp* instance, + UINT16 channelId, + BYTE* data, + int size, + int flags, + int total_size) +{ + return freerdp_channels_data ( + instance, channelId, data, size, flags, total_size); +}*/ + +void +rdp_register_base(freerdp *r) +{ + r->PreConnect = rdp_pre_connect; + r->PostConnect = rdp_post_connect; + r->VerifyCertificate = rdp_verify_certificate; + // r->ReceiveChannelData = rdp_receive_channel_data; + r->ContextNew = rdp_context_new; + r->ContextFree = rdp_context_free; +} diff --git a/src/rdp/rdp_module.c b/src/rdp/rdp_module.c new file mode 100644 index 0000000..a315325 --- /dev/null +++ b/src/rdp/rdp_module.c @@ -0,0 +1,168 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <unistd.h> + +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <webrdp_core_api.h> +#include <webrdp_module_api.h> + +#include "rdp_backend_api.h" +#include "rdp_impl.h" +#include "rdp_display_output.h" +#include "rdp_user_input.h" +#include "rdp_clipboard.h" +#include "rdp_ft.h" +#include "rdp_module.h" +#include "rdp_settings.h" + +/* static void +send_termination_message (rdp_internals* i) +{ + i->core->send_termination_msg (i->task_info); +} */ + +static bool +rdp_init(void *internals) +{ + bool ret = true; + if (!rdp_init_internals(internals)) + { + /* TODO: handle error */ + return false; + } + if (!((rdp_internals *)internals)->instance) + { + /* TODO: handle error */ + ret = false; + } + if (!rdp_init_settings(internals)) + { + /* TODO: handle error */ + ret = false; + } + if (!rdp_connect(internals)) + { + /* TODO: handle error */ + ret = false; + } + if (!ret) + { + rdp_internals *i = internals; + i->core->api_msgs->send_error_msg( + "rdp_module_internal_error", i->task_info); + /*send_termination_message (i);*/ + i->core->api_core->task_finished(false, i->task_info); + } + return ret; +} + +static void +destroy_rdp(void *internals) +{ + rdp_internals *_i = internals; + /* stop all rdp fd's watchers */ + int i; + for (i = 0; i < _i->rdp_fd_count; ++i) + { + if (ev_is_active(&(_i->rdp_fd[i]))) + { + ev_io_stop( + _i->core->api_core->get_libev_loop(_i->task_info), + &(_i->rdp_fd[i])); + } + } + if (_i->conn_state == rdp_conn_state_connected) + { + freerdp_disconnect(_i->instance); + _i->conn_state = rdp_conn_state_offline; + } + _i->rdp_fd_count = 0; + if (_i->context) + { + if (_i->context->clipboard && _i->context->clipboard->srv_fmts + && _i->context->clipboard->srv_fmts_count) + { + for (i = 0; i < _i->context->clipboard->srv_fmts_count; + ++i) + { + if (_i->context->clipboard->srv_fmts[i] + .rdp_fmt.formatName) + { + free(_i->context->clipboard->srv_fmts[i] + .rdp_fmt.formatName); + } + } + free(_i->context->clipboard->srv_fmts); + } + if (_i->context->clipboard && _i->context->clipboard->clipboard) + { + ClipboardDestroy(_i->context->clipboard->clipboard); + } + if (_i->context->clipboard + && _i->context->clipboard->client_filelist_cache) + { + free(_i->context->clipboard->client_filelist_cache); + _i->context->clipboard->client_filelist_cache = 0; + } + free(_i->context->ft_to_client); + free(_i->context->ft_to_server); + } + if (_i->instance) + { + freerdp_context_free(_i->instance); + freerdp_free(_i->instance); + _i->instance = 0; + } + if (internals) + { + rdp_settings_free(internals); + internals = 0; + } +} + +static void +rdp_set_task_info(void *task_info, void *internals) +{ + rdp_internals *i = internals; + i->task_info = task_info; +} + +bool +rdp_create(wrdp_core_exports *core, wrdp_backend_module *module) +{ + wrdp_backend_module *backend = module; + if (!backend) + { + return false; + } + backend->callbacks_module->init = rdp_init; + backend->callbacks_module->destroy = destroy_rdp; + backend->callbacks_module->set_setting_int = rdp_set_setting_int; + backend->callbacks_module->set_setting_str = rdp_set_setting_str; + backend->callbacks_module->set_task_info = rdp_set_task_info; + register_input(backend); + register_clipboard(backend); + register_ft(backend); + backend->backend_internals = calloc(1, sizeof(rdp_internals)); + if (!backend->backend_internals) + { + perror("calloc"); + return false; + } + memset(&(((rdp_internals *)backend->backend_internals)->my_settings), 0, + sizeof(my_rdp_settings)); + /* TODO: do we need to zero each option structure in settings ? */ + { + rdp_internals *i = backend->backend_internals; + i->core = core; + i->backend = backend; + } + + return true; +} diff --git a/src/rdp/rdp_module.h b/src/rdp/rdp_module.h new file mode 100644 index 0000000..59842d3 --- /dev/null +++ b/src/rdp/rdp_module.h @@ -0,0 +1,7 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once
\ No newline at end of file diff --git a/src/rdp/rdp_png.c b/src/rdp/rdp_png.c new file mode 100644 index 0000000..09f01a2 --- /dev/null +++ b/src/rdp/rdp_png.c @@ -0,0 +1,89 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include "rdp_png.h" +#include <stdlib.h> +#include <string.h> + +static void +cbPngError(png_structp png, png_const_charp msg) +{ + printf("Png error: %s\n", (msg ? msg : "unknown error")); +} + +static void +cbPngWarn(png_structp png, png_const_charp msg) +{ + printf("Png warning: %s\n", (msg ? msg : "unknown error")); +} + +static void +cbPngWrite(png_structp png, png_bytep data, png_size_t len) +{ + rdp_png_buf *out_buf = (rdp_png_buf *)png_get_io_ptr(png); + uint8_t *ptr = out_buf->buf; + if (out_buf->written + len > out_buf->buf_size) + { + printf("error: rdp_png_buf is too small\n"); + return; + } + ptr += out_buf->written; + memcpy(ptr, data, len); + out_buf->written += len; +} + +static void +cbPngFlush(png_structp png) +{ +} + +bool +png_generate_from_argb( + int width, int height, uint8_t *data, rdp_png_buf *out_buf) +{ + png_infop info_ptr; + png_structp png_ptr = png_create_write_struct( + PNG_LIBPNG_VER_STRING, 0, cbPngError, cbPngWarn); + if (!png_ptr) + { + printf("Could not allocate png_struct\n"); + return false; + } + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + printf("Could not allocate png_info_struct\n"); + return false; + } + png_set_write_fn(png_ptr, out_buf, cbPngWrite, cbPngFlush); + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_bytep *rows = malloc(sizeof(png_bytep) * height); + if (!rows) + { + perror("malloc"); + return false; + } + for (int i = 0; i < height; ++i) + { + rows[i] = (png_bytep)data; + data += width * 4; + } + png_set_rows(png_ptr, info_ptr, rows); + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + + free(rows); + + return true; +} + +void +png_destroy(png_structp *p, png_infop *i) +{ + png_destroy_write_struct(p, i); +} diff --git a/src/rdp/rdp_png.h b/src/rdp/rdp_png.h new file mode 100644 index 0000000..a3a3bfc --- /dev/null +++ b/src/rdp/rdp_png.h @@ -0,0 +1,22 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +#include <png.h> +#include <stdbool.h> +#include <stdint.h> + +typedef struct +{ + uint8_t *buf; + size_t buf_size, written; +} rdp_png_buf; + +void png_destroy(png_structp *p, png_infop *i); + +bool png_generate_from_argb( + int width, int height, uint8_t *data, rdp_png_buf *out_buf); diff --git a/src/rdp/rdp_rail.c b/src/rdp/rdp_rail.c new file mode 100644 index 0000000..bde1950 --- /dev/null +++ b/src/rdp/rdp_rail.c @@ -0,0 +1,173 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#include "webrdp_core_api.h" +#include "webrdp_module_api.h" +#include "rdp_backend_api.h" +#include "rdp_rail.h" + +static UINT +RailServerSystemParam( + RailClientContext *context, const RAIL_SYSPARAM_ORDER *sysparam) +{ + return CHANNEL_RC_OK; +} + +static UINT +RailServerHandshake( + RailClientContext *context, const RAIL_HANDSHAKE_ORDER *handshake) +{ + return client_rail_server_start_cmd(context); +} + +static UINT +RailServerHandshakeEx( + RailClientContext *context, const RAIL_HANDSHAKE_EX_ORDER *handshakeEx) +{ + return client_rail_server_start_cmd(context); +} + +static UINT +RailServerLocalMoveSize( + RailClientContext *context, const RAIL_LOCALMOVESIZE_ORDER *localMoveSize) +{ + return CHANNEL_RC_OK; +} +static UINT +RailServerMinMaxInfo( + RailClientContext *context, const RAIL_MINMAXINFO_ORDER *minMaxInfo) +{ + return CHANNEL_RC_OK; +} + +static UINT +RailServerLanguageBarInfo( + RailClientContext *context, const RAIL_LANGBAR_INFO_ORDER *langBarInfo) +{ + return CHANNEL_RC_OK; +} +static UINT +RailServerExecuteResult( + RailClientContext *context, const RAIL_EXEC_RESULT_ORDER *execResult) +{ + my_rdp_context *c = (my_rdp_context *)context; + if (execResult->execResult != RAIL_EXEC_S_OK) + { + const char *msg + = "rdp_module: rail: RailServerExecuteResult failure"; + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + } + return CHANNEL_RC_OK; +} + +static UINT +RailServerGetAppIdResponse( + RailClientContext *context, const RAIL_GET_APPID_RESP_ORDER *getAppIdResp) +{ + return CHANNEL_RC_OK; +} + +static BOOL +WindowCreate(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const WINDOW_STATE_ORDER *window_state) +{ + return TRUE; +} +static BOOL +WindowUpdate(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const WINDOW_STATE_ORDER *window_state) +{ + return TRUE; +} +static BOOL +WindowIcon(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const WINDOW_ICON_ORDER *window_icon) +{ + return TRUE; +} +static BOOL +WindowCachedIcon(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const WINDOW_CACHED_ICON_ORDER *window_cached_icon) +{ + return TRUE; +} +static BOOL +WindowDelete(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo) +{ + return TRUE; +} +static BOOL +NotifyIconCreate(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const NOTIFY_ICON_STATE_ORDER *notify_icon_state) +{ + return TRUE; +} +static BOOL +NotifyIconUpdate(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const NOTIFY_ICON_STATE_ORDER *notify_icon_state) +{ + return TRUE; +} +static BOOL +NotifyIconDelete(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo) +{ + return TRUE; +} +static BOOL +MonitoredDesktop(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo, + const MONITORED_DESKTOP_ORDER *monitored_desktop) +{ + return TRUE; +} +static BOOL +NonMonitoredDesktop(rdpContext *context, const WINDOW_ORDER_INFO *orderInfo) +{ + return TRUE; +} + +static void +rdp_rail_register_update_callbacks(rdpUpdate *update) +{ + rdpWindowUpdate *window = update->window; + window->WindowCreate = WindowCreate; + window->WindowUpdate = WindowUpdate; + window->WindowDelete = WindowDelete; + window->WindowIcon = WindowIcon; + window->WindowCachedIcon = WindowCachedIcon; + window->NotifyIconCreate = NotifyIconCreate; + window->NotifyIconUpdate = NotifyIconUpdate; + window->NotifyIconDelete = NotifyIconDelete; + window->MonitoredDesktop = MonitoredDesktop; + window->NonMonitoredDesktop = NonMonitoredDesktop; +} + +void +rdp_rail_init(my_rdp_context *context, RailClientContext *rail) +{ + if (!context || !rail) + return; + + rdp_rail_register_update_callbacks(context->context.update); + rail->custom = (void *)context; + rail->ServerExecuteResult = RailServerExecuteResult; + rail->ServerSystemParam = RailServerSystemParam; + rail->ServerHandshake = RailServerHandshake; + rail->ServerHandshakeEx = RailServerHandshakeEx; + rail->ServerLocalMoveSize = RailServerLocalMoveSize; + rail->ServerMinMaxInfo = RailServerMinMaxInfo; + rail->ServerLanguageBarInfo = RailServerLanguageBarInfo; + rail->ServerGetAppIdResponse = RailServerGetAppIdResponse; +} + +void +rdp_rail_uninit(my_rdp_context *context, RailClientContext *rail) +{ +} diff --git a/src/rdp/rdp_rail.h b/src/rdp/rdp_rail.h new file mode 100644 index 0000000..0d3d7ba --- /dev/null +++ b/src/rdp/rdp_rail.h @@ -0,0 +1,14 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +#include "rdp_channels.h" +#include "rdp_impl.h" + +void rdp_rail_init(my_rdp_context *context, RailClientContext *rail); + +void rdp_rail_uninit(my_rdp_context *context, RailClientContext *rail); diff --git a/src/rdp/rdp_settings.c b/src/rdp/rdp_settings.c new file mode 100644 index 0000000..ecb1dcc --- /dev/null +++ b/src/rdp/rdp_settings.c @@ -0,0 +1,2278 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <stdbool.h> +#include <stddef.h> + +#include <webrdp_module_api.h> +#include <webrdp_core_api.h> + +#include "rdp_backend_api.h" +#include "rdp_impl.h" + +/* this variable used in integer settng name field to indicate used setting */ +static const char *setting_used = "1"; + +bool +rdp_set_setting_str(backend_setting_str *setting, void *internals) +{ + rdp_internals *i = internals; +#ifdef DEBUG + { + char buf[128]; + snprintf(buf, 127, + "rdp backend handling setting string: \"%s: %s\"", + setting->name, setting->value); + i->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), wrdp_log_level_trace, 0); + } +#endif /* DEBUG */ + if (!strcmp(setting->name, "host")) + { + free(i->my_settings.host.value); + i->my_settings.host.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "pcb")) + { + /* + free (i->my_settings.pcb.value); + i->my_settings.pcb.value = strdup (setting->value); */ + return true; + } + else if (!strcmp(setting->name, "user") && setting->value) + { + free(i->my_settings.user.value); + i->my_settings.user.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "password") && setting->value) + { + free(i->my_settings.password.value); + i->my_settings.password.value = strdup(setting->value); + return true; + } + + /* handle int settings passed as strings */ + if (!strcmp(setting->name, "port")) + { + i->my_settings.port.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.port.value = atoll(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "width")) + { + i->my_settings.width.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.width.value = atoll(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "height")) + { + i->my_settings.height.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.height.value = atoll(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "perf")) + { + i->my_settings.perf.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.perf.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "fntlm")) + { + i->my_settings.fntlm.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.fntlm.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "nowallp")) + { + i->my_settings.nowallp.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.nowallp.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "nowdrag")) + { + i->my_settings.nowdrag.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.nowdrag.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "nomani")) + { + i->my_settings.nomani.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.nomani.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "notheme")) + { + i->my_settings.notheme.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.notheme.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "nonla")) + { + i->my_settings.nonla.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.nonla.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "notls")) + { + i->my_settings.notls.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.notls.value = atoi(setting->value); + } + return true; + } + + /*new settings from freerdp header (strings) begin */ + else if (!strcmp(setting->name, "Domain") && setting->value) + { + free(i->my_settings.Domain.value); + i->my_settings.Domain.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "PasswordHash") && setting->value) + { + free(i->my_settings.PasswordHash.value); + i->my_settings.PasswordHash.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "AcceptedCert") && setting->value) + { + free(i->my_settings.AcceptedCert.value); + i->my_settings.AcceptedCert.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ClientHostname") && setting->value) + { + free(i->my_settings.ClientHostname.value); + i->my_settings.ClientHostname.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ClientProductId") && setting->value) + { + free(i->my_settings.ClientProductId.value); + i->my_settings.ClientProductId.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "AlternateShell") && setting->value) + { + free(i->my_settings.AlternateShell.value); + i->my_settings.AlternateShell.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ShellWorkingDirectory") + && setting->value) + { + free(i->my_settings.ShellWorkingDirectory.value); + i->my_settings.ShellWorkingDirectory.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ClientAddress") && setting->value) + { + free(i->my_settings.ClientAddress.value); + i->my_settings.ClientAddress.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ClientDir") && setting->value) + { + free(i->my_settings.ClientDir.value); + i->my_settings.ClientDir.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "DynamicDSTTimeZoneKeyName") + && setting->value) + { + free(i->my_settings.DynamicDSTTimeZoneKeyName.value); + i->my_settings.DynamicDSTTimeZoneKeyName.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteAssistanceSessionId") + && setting->value) + { + free(i->my_settings.RemoteAssistanceSessionId.value); + i->my_settings.RemoteAssistanceSessionId.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteAssistancePassStub") + && setting->value) + { + free(i->my_settings.RemoteAssistancePassStub.value); + i->my_settings.RemoteAssistancePassStub.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteAssistancePassword") + && setting->value) + { + free(i->my_settings.RemoteAssistancePassword.value); + i->my_settings.RemoteAssistancePassword.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteAssistanceRCTicket") + && setting->value) + { + free(i->my_settings.RemoteAssistanceRCTicket.value); + i->my_settings.RemoteAssistanceRCTicket.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "AuthenticationServiceClass") + && setting->value) + { + free(i->my_settings.AuthenticationServiceClass.value); + i->my_settings.AuthenticationServiceClass.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "AllowedTlsCiphers") && setting->value) + { + free(i->my_settings.AllowedTlsCiphers.value); + i->my_settings.AllowedTlsCiphers.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "NtlmSamFile") && setting->value) + { + free(i->my_settings.NtlmSamFile.value); + i->my_settings.NtlmSamFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "PreconnectionBlob") && setting->value) + { + free(i->my_settings.PreconnectionBlob.value); + i->my_settings.PreconnectionBlob.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RedirectionAcceptedCert") + && setting->value) + { + free(i->my_settings.RedirectionAcceptedCert.value); + i->my_settings.RedirectionAcceptedCert.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "KerberosKdc") && setting->value) + { + free(i->my_settings.KerberosKdc.value); + i->my_settings.KerberosKdc.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "KerberosRealm") && setting->value) + { + free(i->my_settings.KerberosRealm.value); + i->my_settings.KerberosRealm.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "CertificateName") && setting->value) + { + free(i->my_settings.CertificateName.value); + i->my_settings.CertificateName.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "CertificateFile") && setting->value) + { + free(i->my_settings.CertificateFile.value); + i->my_settings.CertificateFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "PrivateKeyFile") && setting->value) + { + free(i->my_settings.PrivateKeyFile.value); + i->my_settings.PrivateKeyFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "CertificateContent") && setting->value) + { + free(i->my_settings.CertificateContent.value); + i->my_settings.CertificateContent.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "PrivateKeyContent") && setting->value) + { + free(i->my_settings.PrivateKeyContent.value); + i->my_settings.PrivateKeyContent.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "WindowTitle") && setting->value) + { + free(i->my_settings.WindowTitle.value); + i->my_settings.WindowTitle.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "WmClass") && setting->value) + { + free(i->my_settings.WmClass.value); + i->my_settings.WmClass.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ComputerName") && setting->value) + { + free(i->my_settings.ComputerName.value); + i->my_settings.ComputerName.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ConnectionFile") && setting->value) + { + free(i->my_settings.ConnectionFile.value); + i->my_settings.ConnectionFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "AssistanceFile") && setting->value) + { + free(i->my_settings.AssistanceFile.value); + i->my_settings.AssistanceFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "HomePath") && setting->value) + { + free(i->my_settings.HomePath.value); + i->my_settings.HomePath.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ConfigPath") && setting->value) + { + free(i->my_settings.ConfigPath.value); + i->my_settings.ConfigPath.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "CurrentPath") && setting->value) + { + free(i->my_settings.CurrentPath.value); + i->my_settings.CurrentPath.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "DumpRemoteFxFile") && setting->value) + { + free(i->my_settings.DumpRemoteFxFile.value); + i->my_settings.DumpRemoteFxFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "PlayRemoteFxFile") && setting->value) + { + free(i->my_settings.PlayRemoteFxFile.value); + i->my_settings.PlayRemoteFxFile.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "GatewayHostname") && setting->value) + { + free(i->my_settings.GatewayHostname.value); + i->my_settings.GatewayHostname.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "GatewayUsername") && setting->value) + { + free(i->my_settings.GatewayUsername.value); + i->my_settings.GatewayUsername.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "GatewayPassword") && setting->value) + { + free(i->my_settings.GatewayPassword.value); + i->my_settings.GatewayPassword.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "GatewayDomain") && setting->value) + { + free(i->my_settings.GatewayDomain.value); + i->my_settings.GatewayDomain.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "GatewayAccessToken") && setting->value) + { + free(i->my_settings.GatewayAccessToken.value); + i->my_settings.GatewayAccessToken.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "GatewayAcceptedCert") + && setting->value) + { + free(i->my_settings.GatewayAcceptedCert.value); + i->my_settings.GatewayAcceptedCert.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ProxyHostname") && setting->value) + { + free(i->my_settings.ProxyHostname.value); + i->my_settings.ProxyHostname.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationName") + && setting->value) + { + free(i->my_settings.RemoteApplicationName.value); + i->my_settings.RemoteApplicationName.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationIcon") + && setting->value) + { + free(i->my_settings.RemoteApplicationIcon.value); + i->my_settings.RemoteApplicationIcon.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationProgram") + && setting->value) + { + free(i->my_settings.RemoteApplicationProgram.value); + i->my_settings.RemoteApplicationProgram.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationFile") + && setting->value) + { + free(i->my_settings.RemoteApplicationFile.value); + i->my_settings.RemoteApplicationFile.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationGuid") + && setting->value) + { + free(i->my_settings.RemoteApplicationGuid.value); + i->my_settings.RemoteApplicationGuid.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationCmdLine") + && setting->value) + { + free(i->my_settings.RemoteApplicationCmdLine.value); + i->my_settings.RemoteApplicationCmdLine.value + = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ImeFileName") && setting->value) + { + free(i->my_settings.ImeFileName.value); + i->my_settings.ImeFileName.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "DrivesToRedirect") && setting->value) + { + free(i->my_settings.DrivesToRedirect.value); + i->my_settings.DrivesToRedirect.value = strdup(setting->value); + return true; + } + else if (!strcmp(setting->name, "ActionScript") && setting->value) + { + free(i->my_settings.ActionScript.value); + i->my_settings.ActionScript.value = strdup(setting->value); + return true; + } + /*new settings from freerdp header (strings) end */ + + /* TODO: handle also integer settings passed as strings */ + + /* new settings from freerdp header (integers as string) begin */ + + else if (!strcmp(setting->name, "ServerMode")) + { + i->my_settings.ServerMode.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ServerMode.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "WaitForOutputBufferFlush")) + { + i->my_settings.WaitForOutputBufferFlush.name + = (char *)setting_used; + if (setting->value) + { + i->my_settings.WaitForOutputBufferFlush.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "MaxTimeInCheckLoop")) + { + i->my_settings.MaxTimeInCheckLoop.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.MaxTimeInCheckLoop.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DesktopWidth")) + { + i->my_settings.DesktopWidth.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DesktopWidth.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DesktopHeight")) + { + i->my_settings.DesktopHeight.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DesktopHeight.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "Workarea")) + { + i->my_settings.Workarea.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.Workarea.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "Fullscreen")) + { + i->my_settings.Fullscreen.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.Fullscreen.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "GrabKeyboard")) + { + i->my_settings.GrabKeyboard.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.GrabKeyboard.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "Decorations")) + { + i->my_settings.Decorations.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.Decorations.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "RdpVersion")) + { + i->my_settings.RdpVersion.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.RdpVersion.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "ColorDepth")) + { + i->my_settings.ColorDepth.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ColorDepth.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "ExtSecurity")) + { + i->my_settings.ExtSecurity.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ExtSecurity.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "NlaSecurity")) + { + i->my_settings.NlaSecurity.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.NlaSecurity.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "TlsSecurity")) + { + i->my_settings.TlsSecurity.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.TlsSecurity.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "RdpSecurity")) + { + i->my_settings.RdpSecurity.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.RdpSecurity.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "NegotiateSecurityLayer")) + { + i->my_settings.NegotiateSecurityLayer.name + = (char *)setting_used; + if (setting->value) + { + i->my_settings.NegotiateSecurityLayer.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "RestrictedAdminModeRequired")) + { + i->my_settings.RestrictedAdminModeRequired.name + = (char *)setting_used; + if (setting->value) + { + i->my_settings.RestrictedAdminModeRequired.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "MstscCookieMode")) + { + i->my_settings.MstscCookieMode.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.MstscCookieMode.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "CookieMaxLength")) + { + i->my_settings.CookieMaxLength.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.CookieMaxLength.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "ClientBuild")) + { + i->my_settings.ClientBuild.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ClientBuild.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "KeyboardType")) + { + i->my_settings.KeyboardType.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.KeyboardType.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "KeyboardSubType")) + { + i->my_settings.KeyboardSubType.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.KeyboardSubType.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "KeyboardFunctionKey")) + { + i->my_settings.KeyboardFunctionKey.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.KeyboardFunctionKey.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "KeyboardLayout")) + { + i->my_settings.KeyboardLayout.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.KeyboardLayout.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "UseRdpSecurityLayer")) + { + i->my_settings.UseRdpSecurityLayer.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.UseRdpSecurityLayer.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "SaltedChecksum")) + { + i->my_settings.SaltedChecksum.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.SaltedChecksum.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "ServerPort")) + { + i->my_settings.ServerPort.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ServerPort.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "GatewayPort")) + { + i->my_settings.GatewayPort.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.GatewayPort.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DesktopResize")) + { + i->my_settings.DesktopResize.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DesktopResize.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "ToggleFullscreen")) + { + i->my_settings.ToggleFullscreen.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ToggleFullscreen.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "Floatbar")) + { + i->my_settings.Floatbar.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.Floatbar.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DesktopPosX")) + { + i->my_settings.DesktopPosX.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DesktopPosX.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DesktopPosY")) + { + i->my_settings.DesktopPosY.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DesktopPosY.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "SoftwareGdi")) + { + i->my_settings.SoftwareGdi.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.SoftwareGdi.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "UnmapButtons")) + { + i->my_settings.UnmapButtons.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.UnmapButtons.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "PerformanceFlags")) + { + i->my_settings.PerformanceFlags.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.PerformanceFlags.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "AllowFontSmoothing")) + { + i->my_settings.AllowFontSmoothing.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.AllowFontSmoothing.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "AllowDesktopComposition")) + { + i->my_settings.AllowDesktopComposition.name + = (char *)setting_used; + if (setting->value) + { + i->my_settings.AllowDesktopComposition.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DisableWallpaper")) + { + i->my_settings.DisableWallpaper.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DisableWallpaper.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DisableFullWindowDrag")) + { + i->my_settings.DisableFullWindowDrag.name + = (char *)setting_used; + if (setting->value) + { + i->my_settings.DisableFullWindowDrag.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DisableMenuAnims")) + { + i->my_settings.DisableMenuAnims.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DisableMenuAnims.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "DisableThemes")) + { + i->my_settings.DisableThemes.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.DisableThemes.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "ConnectionType")) + { + i->my_settings.ConnectionType.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.ConnectionType.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "EncryptionMethods")) + { + i->my_settings.EncryptionMethods.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.EncryptionMethods.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "EncryptionLevel")) + { + i->my_settings.EncryptionLevel.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.EncryptionLevel.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "FIPSMode")) + { + i->my_settings.FIPSMode.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.FIPSMode.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "CompressionEnabled")) + { + i->my_settings.CompressionEnabled.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.CompressionEnabled.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "LogonNotify")) + { + i->my_settings.LogonNotify.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.LogonNotify.value = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "BrushSupportLevel")) + { + i->my_settings.BrushSupportLevel.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.BrushSupportLevel.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationMode")) + { + i->my_settings.RemoteApplicationMode.name + = (char *)setting_used; + if (setting->value) + { + i->my_settings.RemoteApplicationMode.value + = atoi(setting->value); + } + return true; + } + else if (!strcmp(setting->name, "TcpAckTimeout")) + { + i->my_settings.TcpAckTimeout.name = (char *)setting_used; + if (setting->value) + { + i->my_settings.TcpAckTimeout.value + = atoi(setting->value); + } + return true; + } + /* new settings from freerdp header (integers as string) end */ + + /* error: unhandled setting */ + { + char buf[128]; + snprintf(buf, 127, "rdp_module: unknown setting string: %s", + setting->name); + i->core->api_utils->log_msg((const uint8_t *)buf, strlen(buf), + wrdp_log_level_warning, 0); + } + return false; +} + +bool +rdp_set_setting_int(backend_setting_int *setting, void *internals) +{ + rdp_internals *i = internals; +#ifdef DEBUG + { + char buf[128]; + snprintf(buf, 127, + "rdp backend handling setting int: \"%s: %ld\"", + setting->name, setting->value); + i->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), wrdp_log_level_trace, 0); + } +#endif + if (!strcmp(setting->name, "port")) + { + i->my_settings.port.name = (char *)setting_used; + i->my_settings.port.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "width")) + { + i->my_settings.width.name = (char *)setting_used; + i->my_settings.width.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "height")) + { + i->my_settings.height.name = (char *)setting_used; + i->my_settings.height.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "perf")) + { + i->my_settings.perf.name = (char *)setting_used; + i->my_settings.perf.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "fntlm")) + { + i->my_settings.fntlm.name = (char *)setting_used; + i->my_settings.fntlm.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "nowallp")) + { + i->my_settings.nowallp.name = (char *)setting_used; + i->my_settings.nowallp.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "nowdrag")) + { + i->my_settings.nowdrag.name = (char *)setting_used; + i->my_settings.nowdrag.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "nomani")) + { + i->my_settings.nomani.name = (char *)setting_used; + i->my_settings.nomani.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "notheme")) + { + i->my_settings.notheme.name = (char *)setting_used; + i->my_settings.notheme.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "nonla")) + { + i->my_settings.nonla.name = (char *)setting_used; + i->my_settings.nonla.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "notls")) + { + i->my_settings.notls.name = (char *)setting_used; + i->my_settings.notls.value = setting->value; + return true; + } + + /* new settings from freerdp header (integers) begin */ + else if (!strcmp(setting->name, "ServerMode")) + { + i->my_settings.ServerMode.name = (char *)setting_used; + i->my_settings.ServerMode.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "WaitForOutputBufferFlush")) + { + i->my_settings.WaitForOutputBufferFlush.name + = (char *)setting_used; + i->my_settings.WaitForOutputBufferFlush.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "MaxTimeInCheckLoop")) + { + i->my_settings.MaxTimeInCheckLoop.name = (char *)setting_used; + i->my_settings.MaxTimeInCheckLoop.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DesktopWidth")) + { + i->my_settings.DesktopWidth.name = (char *)setting_used; + i->my_settings.DesktopWidth.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DesktopHeight")) + { + i->my_settings.DesktopHeight.name = (char *)setting_used; + i->my_settings.DesktopHeight.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "Workarea")) + { + i->my_settings.Workarea.name = (char *)setting_used; + i->my_settings.Workarea.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "Fullscreen")) + { + i->my_settings.Fullscreen.name = (char *)setting_used; + i->my_settings.Fullscreen.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "GrabKeyboard")) + { + i->my_settings.GrabKeyboard.name = (char *)setting_used; + i->my_settings.GrabKeyboard.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "Decorations")) + { + i->my_settings.Decorations.name = (char *)setting_used; + i->my_settings.Decorations.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "RdpVersion")) + { + i->my_settings.RdpVersion.name = (char *)setting_used; + i->my_settings.RdpVersion.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "ColorDepth")) + { + i->my_settings.ColorDepth.name = (char *)setting_used; + i->my_settings.ColorDepth.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "ExtSecurity")) + { + i->my_settings.ExtSecurity.name = (char *)setting_used; + i->my_settings.ExtSecurity.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "NlaSecurity")) + { + i->my_settings.NlaSecurity.name = (char *)setting_used; + i->my_settings.NlaSecurity.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "TlsSecurity")) + { + i->my_settings.TlsSecurity.name = (char *)setting_used; + i->my_settings.TlsSecurity.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "RdpSecurity")) + { + i->my_settings.RdpSecurity.name = (char *)setting_used; + i->my_settings.RdpSecurity.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "NegotiateSecurityLayer")) + { + i->my_settings.NegotiateSecurityLayer.name + = (char *)setting_used; + i->my_settings.NegotiateSecurityLayer.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "RestrictedAdminModeRequired")) + { + i->my_settings.RestrictedAdminModeRequired.name + = (char *)setting_used; + i->my_settings.RestrictedAdminModeRequired.value + = setting->value; + return true; + } + else if (!strcmp(setting->name, "MstscCookieMode")) + { + i->my_settings.MstscCookieMode.name = (char *)setting_used; + i->my_settings.MstscCookieMode.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "CookieMaxLength")) + { + i->my_settings.CookieMaxLength.name = (char *)setting_used; + i->my_settings.CookieMaxLength.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "ClientBuild")) + { + i->my_settings.ClientBuild.name = (char *)setting_used; + i->my_settings.ClientBuild.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "KeyboardType")) + { + i->my_settings.KeyboardType.name = (char *)setting_used; + i->my_settings.KeyboardType.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "KeyboardSubType")) + { + i->my_settings.KeyboardSubType.name = (char *)setting_used; + i->my_settings.KeyboardSubType.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "KeyboardFunctionKey")) + { + i->my_settings.KeyboardFunctionKey.name = (char *)setting_used; + i->my_settings.KeyboardFunctionKey.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "KeyboardLayout")) + { + i->my_settings.KeyboardLayout.name = (char *)setting_used; + i->my_settings.KeyboardLayout.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "UseRdpSecurityLayer")) + { + i->my_settings.UseRdpSecurityLayer.name = (char *)setting_used; + i->my_settings.UseRdpSecurityLayer.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "SaltedChecksum")) + { + i->my_settings.SaltedChecksum.name = (char *)setting_used; + i->my_settings.SaltedChecksum.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "ServerPort")) + { + i->my_settings.ServerPort.name = (char *)setting_used; + i->my_settings.ServerPort.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "GatewayPort")) + { + i->my_settings.GatewayPort.name = (char *)setting_used; + i->my_settings.GatewayPort.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DesktopResize")) + { + i->my_settings.DesktopResize.name = (char *)setting_used; + i->my_settings.DesktopResize.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "ToggleFullscreen")) + { + i->my_settings.ToggleFullscreen.name = (char *)setting_used; + i->my_settings.ToggleFullscreen.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "Floatbar")) + { + i->my_settings.Floatbar.name = (char *)setting_used; + i->my_settings.Floatbar.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DesktopPosX")) + { + i->my_settings.DesktopPosX.name = (char *)setting_used; + i->my_settings.DesktopPosX.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DesktopPosY")) + { + i->my_settings.DesktopPosY.name = (char *)setting_used; + i->my_settings.DesktopPosY.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "SoftwareGdi")) + { + i->my_settings.SoftwareGdi.name = (char *)setting_used; + i->my_settings.SoftwareGdi.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "UnmapButtons")) + { + i->my_settings.UnmapButtons.name = (char *)setting_used; + i->my_settings.UnmapButtons.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "PerformanceFlags")) + { + i->my_settings.PerformanceFlags.name = (char *)setting_used; + i->my_settings.PerformanceFlags.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "AllowFontSmoothing")) + { + i->my_settings.AllowFontSmoothing.name = (char *)setting_used; + i->my_settings.AllowFontSmoothing.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "AllowDesktopComposition")) + { + i->my_settings.AllowDesktopComposition.name + = (char *)setting_used; + i->my_settings.AllowDesktopComposition.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DisableWallpaper")) + { + i->my_settings.DisableWallpaper.name = (char *)setting_used; + i->my_settings.DisableWallpaper.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DisableFullWindowDrag")) + { + i->my_settings.DisableFullWindowDrag.name + = (char *)setting_used; + i->my_settings.DisableFullWindowDrag.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DisableMenuAnims")) + { + i->my_settings.DisableMenuAnims.name = (char *)setting_used; + i->my_settings.DisableMenuAnims.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "DisableThemes")) + { + i->my_settings.DisableThemes.name = (char *)setting_used; + i->my_settings.DisableThemes.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "ConnectionType")) + { + i->my_settings.ConnectionType.name = (char *)setting_used; + i->my_settings.ConnectionType.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "EncryptionMethods")) + { + i->my_settings.EncryptionMethods.name = (char *)setting_used; + i->my_settings.EncryptionMethods.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "EncryptionLevel")) + { + i->my_settings.EncryptionLevel.name = (char *)setting_used; + i->my_settings.EncryptionLevel.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "FIPSMode")) + { + i->my_settings.FIPSMode.name = (char *)setting_used; + i->my_settings.FIPSMode.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "CompressionEnabled")) + { + i->my_settings.CompressionEnabled.name = (char *)setting_used; + i->my_settings.CompressionEnabled.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "LogonNotify")) + { + i->my_settings.LogonNotify.name = (char *)setting_used; + i->my_settings.LogonNotify.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "BrushSupportLevel")) + { + i->my_settings.BrushSupportLevel.name = (char *)setting_used; + i->my_settings.BrushSupportLevel.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "RemoteApplicationMode")) + { + i->my_settings.RemoteApplicationMode.name + = (char *)setting_used; + i->my_settings.RemoteApplicationMode.value = setting->value; + return true; + } + else if (!strcmp(setting->name, "TcpAckTimeout")) + { + i->my_settings.TcpAckTimeout.name = (char *)setting_used; + i->my_settings.TcpAckTimeout.value = setting->value; + return true; + } + + /* new settings from freerdp header (integers) end */ + + /* error: unhandled setting */ + { + char buf[128]; + snprintf(buf, 127, "rdp_module: unknown setting int: %s", + setting->name); + i->core->api_utils->log_msg((const uint8_t *)buf, strlen(buf), + wrdp_log_level_warning, 0); + } + return false; +} + +void +rdp_settings_free(rdp_internals *internals) +{ + rdp_internals *i = internals; + if (i->my_settings.host.value) + { + free((void *)i->my_settings.host.value); + } + if (i->my_settings.pcb.value) + { + free((void *)i->my_settings.pcb.value); + } + if (i->my_settings.user.value) + { + free((void *)i->my_settings.user.value); + } + if (i->my_settings.password.value) + { + free((void *)i->my_settings.password.value); + } + /* new */ + if (i->my_settings.Domain.value) + { + free((void *)i->my_settings.Domain.value); + } + if (i->my_settings.PasswordHash.value) + { + free((void *)i->my_settings.PasswordHash.value); + } + if (i->my_settings.AcceptedCert.value) + { + free((void *)i->my_settings.AcceptedCert.value); + } + if (i->my_settings.ClientHostname.value) + { + free((void *)i->my_settings.ClientHostname.value); + } + if (i->my_settings.ClientProductId.value) + { + free((void *)i->my_settings.ClientProductId.value); + } + if (i->my_settings.AlternateShell.value) + { + free((void *)i->my_settings.AlternateShell.value); + } + if (i->my_settings.ShellWorkingDirectory.value) + { + free((void *)i->my_settings.ShellWorkingDirectory.value); + } + if (i->my_settings.ClientAddress.value) + { + free((void *)i->my_settings.ClientAddress.value); + } + if (i->my_settings.ClientDir.value) + { + free((void *)i->my_settings.ClientDir.value); + } + if (i->my_settings.DynamicDSTTimeZoneKeyName.value) + { + free((void *)i->my_settings.DynamicDSTTimeZoneKeyName.value); + } + if (i->my_settings.RemoteAssistanceSessionId.value) + { + free((void *)i->my_settings.RemoteAssistanceSessionId.value); + } + if (i->my_settings.RemoteAssistancePassStub.value) + { + free((void *)i->my_settings.RemoteAssistancePassStub.value); + } + if (i->my_settings.RemoteAssistancePassword.value) + { + free((void *)i->my_settings.RemoteAssistancePassword.value); + } + if (i->my_settings.RemoteAssistanceRCTicket.value) + { + free((void *)i->my_settings.RemoteAssistanceRCTicket.value); + } + if (i->my_settings.AuthenticationServiceClass.value) + { + free((void *)i->my_settings.AuthenticationServiceClass.value); + } + if (i->my_settings.AllowedTlsCiphers.value) + { + free((void *)i->my_settings.AllowedTlsCiphers.value); + } + if (i->my_settings.NtlmSamFile.value) + { + free((void *)i->my_settings.NtlmSamFile.value); + } + if (i->my_settings.PreconnectionBlob.value) + { + free((void *)i->my_settings.PreconnectionBlob.value); + } + if (i->my_settings.RedirectionAcceptedCert.value) + { + free((void *)i->my_settings.RedirectionAcceptedCert.value); + } + if (i->my_settings.KerberosKdc.value) + { + free((void *)i->my_settings.KerberosKdc.value); + } + if (i->my_settings.KerberosRealm.value) + { + free((void *)i->my_settings.KerberosRealm.value); + } + if (i->my_settings.CertificateName.value) + { + free((void *)i->my_settings.CertificateName.value); + } + if (i->my_settings.CertificateFile.value) + { + free((void *)i->my_settings.CertificateFile.value); + } + if (i->my_settings.PrivateKeyFile.value) + { + free((void *)i->my_settings.PrivateKeyFile.value); + } + if (i->my_settings.CertificateContent.value) + { + free((void *)i->my_settings.CertificateContent.value); + } + if (i->my_settings.PrivateKeyContent.value) + { + free((void *)i->my_settings.PrivateKeyContent.value); + } + if (i->my_settings.WindowTitle.value) + { + free((void *)i->my_settings.WindowTitle.value); + } + if (i->my_settings.WmClass.value) + { + free((void *)i->my_settings.WmClass.value); + } + if (i->my_settings.ComputerName.value) + { + free((void *)i->my_settings.ComputerName.value); + } + if (i->my_settings.ConnectionFile.value) + { + free((void *)i->my_settings.ConnectionFile.value); + } + if (i->my_settings.AssistanceFile.value) + { + free((void *)i->my_settings.AssistanceFile.value); + } + if (i->my_settings.HomePath.value) + { + free((void *)i->my_settings.HomePath.value); + } + if (i->my_settings.ConfigPath.value) + { + free((void *)i->my_settings.ConfigPath.value); + } + if (i->my_settings.CurrentPath.value) + { + free((void *)i->my_settings.CurrentPath.value); + } + if (i->my_settings.DumpRemoteFxFile.value) + { + free((void *)i->my_settings.DumpRemoteFxFile.value); + } + if (i->my_settings.PlayRemoteFxFile.value) + { + free((void *)i->my_settings.PlayRemoteFxFile.value); + } + if (i->my_settings.GatewayHostname.value) + { + free((void *)i->my_settings.GatewayHostname.value); + } + if (i->my_settings.GatewayUsername.value) + { + free((void *)i->my_settings.GatewayUsername.value); + } + if (i->my_settings.GatewayPassword.value) + { + free((void *)i->my_settings.GatewayPassword.value); + } + if (i->my_settings.GatewayDomain.value) + { + free((void *)i->my_settings.GatewayDomain.value); + } + if (i->my_settings.GatewayAccessToken.value) + { + free((void *)i->my_settings.GatewayAccessToken.value); + } + if (i->my_settings.GatewayAcceptedCert.value) + { + free((void *)i->my_settings.GatewayAcceptedCert.value); + } + if (i->my_settings.ProxyHostname.value) + { + free((void *)i->my_settings.ProxyHostname.value); + } + if (i->my_settings.RemoteApplicationName.value) + { + free((void *)i->my_settings.RemoteApplicationName.value); + } + if (i->my_settings.RemoteApplicationIcon.value) + { + free((void *)i->my_settings.RemoteApplicationIcon.value); + } + if (i->my_settings.RemoteApplicationProgram.value) + { + free((void *)i->my_settings.RemoteApplicationProgram.value); + } + if (i->my_settings.RemoteApplicationFile.value) + { + free((void *)i->my_settings.RemoteApplicationFile.value); + } + if (i->my_settings.RemoteApplicationGuid.value) + { + free((void *)i->my_settings.RemoteApplicationGuid.value); + } + if (i->my_settings.RemoteApplicationCmdLine.value) + { + free((void *)i->my_settings.RemoteApplicationCmdLine.value); + } + if (i->my_settings.ImeFileName.value) + { + free((void *)i->my_settings.ImeFileName.value); + } + if (i->my_settings.DrivesToRedirect.value) + { + free((void *)i->my_settings.DrivesToRedirect.value); + } + if (i->my_settings.ActionScript.value) + { + free((void *)i->my_settings.ActionScript.value); + } +} + +bool +rdp_init_settings(void *internals) +{ + rdp_internals *i = internals; + rdpSettings *s = i->instance->context->settings; + + /* via are not server here ) */ + s->ServerMode = FALSE; + + s->TlsSecurity = !(i->my_settings.notls.value); + if (i->my_settings.host.value && i->my_settings.host.value[0]) + { + s->ServerHostname = strdup((char *)i->my_settings.host.value); + } + else + { + const char *msg = "rdp_module: server hostname does not set."; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + return FALSE; + } + if (i->my_settings.port.name && i->my_settings.port.name[0] == '1') + { + s->ServerPort = i->my_settings.port.value; + } + if (i->my_settings.user.value && i->my_settings.user.value[0]) + { + s->Username = strdup((char *)i->my_settings.user.value); + } + if (i->my_settings.password.value && i->my_settings.password.value[0]) + { + s->Password = strdup((char *)i->my_settings.password.value); + } + if (!i->my_settings.user.value && !i->my_settings.password.value) + { + /* TODO: disable authentication */ + } + if (i->my_settings.nowallp.name + && i->my_settings.nowallp.name[0] == '1') + { + s->DisableWallpaper = i->my_settings.nowallp.value; + s->PerformanceFlags |= PERF_DISABLE_WALLPAPER; + } + if (i->my_settings.nowdrag.name + && i->my_settings.nowdrag.name[0] == '1') + { + s->DisableFullWindowDrag = i->my_settings.nowdrag.value; + s->PerformanceFlags |= PERF_DISABLE_FULLWINDOWDRAG; + } + if (i->my_settings.nomani.name && i->my_settings.nomani.name[0] == '1') + { + s->DisableMenuAnims = i->my_settings.nomani.value; + s->PerformanceFlags |= PERF_DISABLE_MENUANIMATIONS; + } + if (i->my_settings.notheme.name + && i->my_settings.notheme.name[0] == '1') + { + s->DisableThemes = i->my_settings.notheme.value; + s->PerformanceFlags |= PERF_DISABLE_THEMING; + } + if (i->my_settings.nonla.name && i->my_settings.nonla.name[0] == '1') + { + s->NlaSecurity = !(i->my_settings.nonla.value); + } + if (i->my_settings.width.name && i->my_settings.width.name[0] == '1') + { + s->DesktopWidth = i->my_settings.width.value; + } + if (i->my_settings.height.name && i->my_settings.height.name[0] == '1') + { + s->DesktopHeight = i->my_settings.height.value; + } + if (i->my_settings.pcb.value && i->my_settings.pcb.value[0]) + { + s->SendPreconnectionPdu = TRUE; + s->PreconnectionBlob = strdup(i->my_settings.pcb.value); + } + + s->IgnoreCertificate = TRUE; + + if (i->my_settings.perf.name && i->my_settings.perf.name[0] == '1') + { + switch (i->my_settings.perf.value) + { + case 0: + //LAN + s->PerformanceFlags = PERF_FLAG_NONE; + s->ConnectionType = CONNECTION_TYPE_LAN; + s->AllowFontSmoothing = TRUE; + break; + case 1: + //Broadband + s->PerformanceFlags = PERF_DISABLE_WALLPAPER; + s->ConnectionType + = CONNECTION_TYPE_BROADBAND_HIGH; + break; + case 2: + //Modem + s->PerformanceFlags + = PERF_DISABLE_WALLPAPER + | PERF_DISABLE_FULLWINDOWDRAG + | PERF_DISABLE_MENUANIMATIONS + | PERF_DISABLE_THEMING; + s->ConnectionType = CONNECTION_TYPE_MODEM; + break; + } + } + + /* new settings from freerdp header (strings) begin */ + + if (i->my_settings.Domain.value) + { + s->Domain = strdup(i->my_settings.Domain.value); + } + if (i->my_settings.PasswordHash.value) + { + s->PasswordHash = strdup(i->my_settings.PasswordHash.value); + } + if (i->my_settings.AcceptedCert.value) + { + s->AcceptedCert = strdup(i->my_settings.AcceptedCert.value); + } + if (i->my_settings.ClientHostname.value) + { + s->ClientHostname = strdup(i->my_settings.ClientHostname.value); + } + if (i->my_settings.ClientProductId.value) + { + s->ClientProductId + = strdup(i->my_settings.ClientProductId.value); + } + if (i->my_settings.AlternateShell.value) + { + s->AlternateShell = strdup(i->my_settings.AlternateShell.value); + } + if (i->my_settings.ShellWorkingDirectory.value) + { + s->ShellWorkingDirectory + = strdup(i->my_settings.ShellWorkingDirectory.value); + } + if (i->my_settings.ClientAddress.value) + { + s->ClientAddress = strdup(i->my_settings.ClientAddress.value); + } + if (i->my_settings.ClientDir.value) + { + s->ClientDir = strdup(i->my_settings.ClientDir.value); + } + if (i->my_settings.DynamicDSTTimeZoneKeyName.value) + { + s->DynamicDSTTimeZoneKeyName + = strdup(i->my_settings.DynamicDSTTimeZoneKeyName.value); + } + if (i->my_settings.RemoteAssistanceSessionId.value) + { + s->RemoteAssistanceSessionId + = strdup(i->my_settings.RemoteAssistanceSessionId.value); + } + if (i->my_settings.RemoteAssistancePassStub.value) + { + s->RemoteAssistancePassStub + = strdup(i->my_settings.RemoteAssistancePassStub.value); + } + if (i->my_settings.RemoteAssistancePassword.value) + { + s->RemoteAssistancePassword + = strdup(i->my_settings.RemoteAssistancePassword.value); + } + if (i->my_settings.RemoteAssistanceRCTicket.value) + { + s->RemoteAssistanceRCTicket + = strdup(i->my_settings.RemoteAssistanceRCTicket.value); + } + if (i->my_settings.AuthenticationServiceClass.value) + { + s->AuthenticationServiceClass + = strdup(i->my_settings.AuthenticationServiceClass.value); + } + if (i->my_settings.AllowedTlsCiphers.value) + { + s->AllowedTlsCiphers + = strdup(i->my_settings.AllowedTlsCiphers.value); + } + if (i->my_settings.NtlmSamFile.value) + { + s->NtlmSamFile = strdup(i->my_settings.NtlmSamFile.value); + } + if (i->my_settings.PreconnectionBlob.value) + { + s->PreconnectionBlob + = strdup(i->my_settings.PreconnectionBlob.value); + } + if (i->my_settings.RedirectionAcceptedCert.value) + { + s->RedirectionAcceptedCert + = strdup(i->my_settings.RedirectionAcceptedCert.value); + } + if (i->my_settings.KerberosKdc.value) + { + s->KerberosKdc = strdup(i->my_settings.KerberosKdc.value); + } + if (i->my_settings.KerberosRealm.value) + { + s->KerberosRealm = strdup(i->my_settings.KerberosRealm.value); + } + if (i->my_settings.CertificateName.value) + { + s->CertificateName + = strdup(i->my_settings.CertificateName.value); + } + if (i->my_settings.CertificateFile.value) + { + s->CertificateFile + = strdup(i->my_settings.CertificateFile.value); + } + if (i->my_settings.PrivateKeyFile.value) + { + s->PrivateKeyFile = strdup(i->my_settings.PrivateKeyFile.value); + } + if (i->my_settings.CertificateContent.value) + { + s->CertificateContent + = strdup(i->my_settings.CertificateContent.value); + } + if (i->my_settings.PrivateKeyContent.value) + { + s->PrivateKeyContent + = strdup(i->my_settings.PrivateKeyContent.value); + } + if (i->my_settings.WindowTitle.value) + { + s->WindowTitle = strdup(i->my_settings.WindowTitle.value); + } + if (i->my_settings.WmClass.value) + { + s->WmClass = strdup(i->my_settings.WmClass.value); + } + if (i->my_settings.ComputerName.value) + { + s->ComputerName = strdup(i->my_settings.ComputerName.value); + } + if (i->my_settings.ConnectionFile.value) + { + s->ConnectionFile = strdup(i->my_settings.ConnectionFile.value); + } + if (i->my_settings.AssistanceFile.value) + { + s->AssistanceFile = strdup(i->my_settings.AssistanceFile.value); + } + if (i->my_settings.HomePath.value) + { + s->HomePath = strdup(i->my_settings.HomePath.value); + } + if (i->my_settings.ConfigPath.value) + { + s->ConfigPath = strdup(i->my_settings.ConfigPath.value); + } + if (i->my_settings.CurrentPath.value) + { + s->CurrentPath = strdup(i->my_settings.CurrentPath.value); + } + if (i->my_settings.DumpRemoteFxFile.value) + { + s->DumpRemoteFxFile + = strdup(i->my_settings.DumpRemoteFxFile.value); + } + if (i->my_settings.PlayRemoteFxFile.value) + { + s->PlayRemoteFxFile + = strdup(i->my_settings.PlayRemoteFxFile.value); + } + if (i->my_settings.GatewayHostname.value) + { + s->GatewayHostname + = strdup(i->my_settings.GatewayHostname.value); + } + if (i->my_settings.GatewayUsername.value) + { + s->GatewayUsername + = strdup(i->my_settings.GatewayUsername.value); + } + if (i->my_settings.GatewayPassword.value) + { + s->GatewayPassword + = strdup(i->my_settings.GatewayPassword.value); + } + if (i->my_settings.GatewayDomain.value) + { + s->GatewayDomain = strdup(i->my_settings.GatewayDomain.value); + } + if (i->my_settings.GatewayAccessToken.value) + { + s->GatewayAccessToken + = strdup(i->my_settings.GatewayAccessToken.value); + } + if (i->my_settings.GatewayAcceptedCert.value) + { + s->GatewayAcceptedCert + = strdup(i->my_settings.GatewayAcceptedCert.value); + } + if (i->my_settings.ProxyHostname.value) + { + s->ProxyHostname = strdup(i->my_settings.ProxyHostname.value); + } + if (i->my_settings.RemoteApplicationName.value) + { + s->RemoteApplicationName + = strdup(i->my_settings.RemoteApplicationName.value); + } + if (i->my_settings.RemoteApplicationIcon.value) + { + s->RemoteApplicationIcon + = strdup(i->my_settings.RemoteApplicationIcon.value); + } + if (i->my_settings.RemoteApplicationProgram.value) + { + s->RemoteApplicationProgram + = strdup(i->my_settings.RemoteApplicationProgram.value); + } + if (i->my_settings.RemoteApplicationFile.value) + { + s->RemoteApplicationFile + = strdup(i->my_settings.RemoteApplicationFile.value); + } + if (i->my_settings.RemoteApplicationGuid.value) + { + s->RemoteApplicationGuid + = strdup(i->my_settings.RemoteApplicationGuid.value); + } + if (i->my_settings.RemoteApplicationCmdLine.value) + { + s->RemoteApplicationCmdLine + = strdup(i->my_settings.RemoteApplicationCmdLine.value); + } + if (i->my_settings.ImeFileName.value) + { + s->ImeFileName = strdup(i->my_settings.ImeFileName.value); + } + if (i->my_settings.DrivesToRedirect.value) + { + s->DrivesToRedirect + = strdup(i->my_settings.DrivesToRedirect.value); + } + if (i->my_settings.ActionScript.value) + { + s->ActionScript = strdup(i->my_settings.ActionScript.value); + } + + /* new settings from freerdp header (strings) end */ + + /* new settings from freerdp header (integers) begin */ + + if (i->my_settings.ServerMode.name + && i->my_settings.ServerMode.name[0] == '1') + { + s->ServerMode = i->my_settings.ServerMode.value; + } + if (i->my_settings.WaitForOutputBufferFlush.name + && i->my_settings.WaitForOutputBufferFlush.name[0] == '1') + { + s->WaitForOutputBufferFlush + = i->my_settings.WaitForOutputBufferFlush.value; + } + if (i->my_settings.MaxTimeInCheckLoop.name + && i->my_settings.MaxTimeInCheckLoop.name[0] == '1') + { + s->MaxTimeInCheckLoop = i->my_settings.MaxTimeInCheckLoop.value; + } + if (i->my_settings.DesktopWidth.name + && i->my_settings.DesktopWidth.name[0] == '1') + { + s->DesktopWidth = i->my_settings.DesktopWidth.value; + } + if (i->my_settings.Workarea.name + && i->my_settings.Workarea.name[0] == '1') + { + s->Workarea = i->my_settings.Workarea.value; + } + if (i->my_settings.Fullscreen.name + && i->my_settings.Fullscreen.name[0] == '1') + { + s->Fullscreen = i->my_settings.Fullscreen.value; + } + if (i->my_settings.GrabKeyboard.name + && i->my_settings.GrabKeyboard.name[0] == '1') + { + s->GrabKeyboard = i->my_settings.GrabKeyboard.value; + } + if (i->my_settings.Decorations.name + && i->my_settings.Decorations.name[0] == '1') + { + s->Decorations = i->my_settings.Decorations.value; + } + if (i->my_settings.RdpVersion.name + && i->my_settings.RdpVersion.name[0] == '1') + { + s->RdpVersion = i->my_settings.RdpVersion.value; + } + if (i->my_settings.ColorDepth.name + && i->my_settings.ColorDepth.name[0] == '1') + { + s->ColorDepth = i->my_settings.ColorDepth.value; + } + if (i->my_settings.ExtSecurity.name + && i->my_settings.ExtSecurity.name[0] == '1') + { + s->ExtSecurity = i->my_settings.ExtSecurity.value; + } + if (i->my_settings.NlaSecurity.name + && i->my_settings.NlaSecurity.name[0] == '1') + { + s->NlaSecurity = i->my_settings.NlaSecurity.value; + } + if (i->my_settings.TlsSecurity.name + && i->my_settings.TlsSecurity.name[0] == '1') + { + s->TlsSecurity = i->my_settings.TlsSecurity.value; + } + if (i->my_settings.RdpSecurity.name + && i->my_settings.RdpSecurity.name[0] == '1') + { + s->RdpSecurity = i->my_settings.RdpSecurity.value; + } + if (i->my_settings.NegotiateSecurityLayer.name + && i->my_settings.NegotiateSecurityLayer.name[0] == '1') + { + s->NegotiateSecurityLayer + = i->my_settings.NegotiateSecurityLayer.value; + } + if (i->my_settings.RestrictedAdminModeRequired.name + && i->my_settings.RestrictedAdminModeRequired.name[0] == '1') + { + s->RestrictedAdminModeRequired + = i->my_settings.RestrictedAdminModeRequired.value; + } + if (i->my_settings.MstscCookieMode.name + && i->my_settings.MstscCookieMode.name[0] == '1') + { + s->MstscCookieMode = i->my_settings.MstscCookieMode.value; + } + if (i->my_settings.CookieMaxLength.name + && i->my_settings.CookieMaxLength.name[0] == '1') + { + s->CookieMaxLength = i->my_settings.CookieMaxLength.value; + } + if (i->my_settings.ClientBuild.name + && i->my_settings.ClientBuild.name[0] == '1') + { + s->ClientBuild = i->my_settings.ClientBuild.value; + } + if (i->my_settings.KeyboardType.name + && i->my_settings.KeyboardType.name[0] == '1') + { + s->KeyboardType = i->my_settings.KeyboardType.value; + } + if (i->my_settings.KeyboardSubType.name + && i->my_settings.KeyboardSubType.name[0] == '1') + { + s->KeyboardSubType = i->my_settings.KeyboardSubType.value; + } + if (i->my_settings.KeyboardFunctionKey.name + && i->my_settings.KeyboardFunctionKey.name[0] == '1') + { + s->KeyboardFunctionKey + = i->my_settings.KeyboardFunctionKey.value; + } + if (i->my_settings.KeyboardLayout.name + && i->my_settings.KeyboardLayout.name[0] == '1') + { + s->KeyboardLayout = i->my_settings.KeyboardLayout.value; + } + if (i->my_settings.UseRdpSecurityLayer.name + && i->my_settings.UseRdpSecurityLayer.name[0] == '1') + { + s->UseRdpSecurityLayer + = i->my_settings.UseRdpSecurityLayer.value; + } + if (i->my_settings.SaltedChecksum.name + && i->my_settings.SaltedChecksum.name[0] == '1') + { + s->SaltedChecksum = i->my_settings.SaltedChecksum.value; + } + if (i->my_settings.ServerPort.name + && i->my_settings.ServerPort.name[0] == '1') + { + s->ServerPort = i->my_settings.ServerPort.value; + } + if (i->my_settings.GatewayPort.name + && i->my_settings.GatewayPort.name[0] == '1') + { + s->GatewayPort = i->my_settings.GatewayPort.value; + } + if (i->my_settings.DesktopResize.name + && i->my_settings.DesktopResize.name[0] == '1') + { + s->DesktopResize = i->my_settings.DesktopResize.value; + } + if (i->my_settings.ToggleFullscreen.name + && i->my_settings.ToggleFullscreen.name[0] == '1') + { + s->ToggleFullscreen = i->my_settings.ToggleFullscreen.value; + } + if (i->my_settings.Floatbar.name + && i->my_settings.Floatbar.name[0] == '1') + { + s->Floatbar = i->my_settings.Floatbar.value; + } + if (i->my_settings.DesktopPosX.name + && i->my_settings.DesktopPosX.name[0] == '1') + { + s->DesktopPosX = i->my_settings.DesktopPosX.value; + } + if (i->my_settings.DesktopPosY.name + && i->my_settings.DesktopPosY.name[0] == '1') + { + s->DesktopPosY = i->my_settings.DesktopPosY.value; + } + if (i->my_settings.SoftwareGdi.name + && i->my_settings.SoftwareGdi.name[0] == '1') + { + s->SoftwareGdi = i->my_settings.SoftwareGdi.value; + } + if (i->my_settings.UnmapButtons.name + && i->my_settings.UnmapButtons.name[0] == '1') + { + s->UnmapButtons = i->my_settings.UnmapButtons.value; + } + if (i->my_settings.PerformanceFlags.name + && i->my_settings.PerformanceFlags.name[0] == '1') + { + s->PerformanceFlags = i->my_settings.PerformanceFlags.value; + } + if (i->my_settings.AllowFontSmoothing.name + && i->my_settings.AllowFontSmoothing.name[0] == '1') + { + s->AllowFontSmoothing = i->my_settings.AllowFontSmoothing.value; + } + if (i->my_settings.AllowDesktopComposition.name + && i->my_settings.AllowDesktopComposition.name[0] == '1') + { + s->AllowDesktopComposition + = i->my_settings.AllowDesktopComposition.value; + } + if (i->my_settings.DisableWallpaper.name + && i->my_settings.DisableWallpaper.name[0] == '1') + { + s->DisableWallpaper = i->my_settings.DisableWallpaper.value; + } + if (i->my_settings.DisableFullWindowDrag.name + && i->my_settings.DisableFullWindowDrag.name[0] == '1') + { + s->DisableFullWindowDrag + = i->my_settings.DisableFullWindowDrag.value; + } + if (i->my_settings.DisableMenuAnims.name + && i->my_settings.DisableMenuAnims.name[0] == '1') + { + s->DisableMenuAnims = i->my_settings.DisableMenuAnims.value; + } + if (i->my_settings.DisableThemes.name + && i->my_settings.DisableThemes.name[0] == '1') + { + s->DisableThemes = i->my_settings.DisableThemes.value; + } + if (i->my_settings.ConnectionType.name + && i->my_settings.ConnectionType.name[0] == '1') + { + s->ConnectionType = i->my_settings.ConnectionType.value; + } + if (i->my_settings.EncryptionMethods.name + && i->my_settings.EncryptionMethods.name[0] == '1') + { + s->EncryptionMethods = i->my_settings.EncryptionMethods.value; + } + if (i->my_settings.EncryptionLevel.name + && i->my_settings.EncryptionLevel.name[0] == '1') + { + s->EncryptionLevel = i->my_settings.EncryptionLevel.value; + } + if (i->my_settings.FIPSMode.name + && i->my_settings.FIPSMode.name[0] == '1') + { + s->FIPSMode = i->my_settings.FIPSMode.value; + } + if (i->my_settings.CompressionEnabled.name + && i->my_settings.CompressionEnabled.name[0] == '1') + { + s->CompressionEnabled = i->my_settings.CompressionEnabled.value; + } + if (i->my_settings.LogonNotify.name + && i->my_settings.LogonNotify.name[0] == '1') + { + s->LogonNotify = i->my_settings.LogonNotify.value; + } + if (i->my_settings.BrushSupportLevel.name + && i->my_settings.BrushSupportLevel.name[0] == '1') + { + s->BrushSupportLevel = i->my_settings.BrushSupportLevel.value; + } + if (i->my_settings.RemoteApplicationMode.name + && i->my_settings.RemoteApplicationMode.name[0] == '1') + { + s->RemoteApplicationMode + = i->my_settings.RemoteApplicationMode.value; + } + if (i->my_settings.TcpAckTimeout.name + && i->my_settings.TcpAckTimeout.name[0] == '1') + { + s->TcpAckTimeout = i->my_settings.TcpAckTimeout.value; + } + /* new settings from freerdp header (integers) end */ + + /* TODO: handle rest of options: + * fntlm + */ + + return true; +} diff --git a/src/rdp/rdp_settings.h b/src/rdp/rdp_settings.h new file mode 100644 index 0000000..3fca325 --- /dev/null +++ b/src/rdp/rdp_settings.h @@ -0,0 +1,15 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +bool rdp_set_setting_str(backend_setting_str *setting, void *internals); + +bool rdp_set_setting_int(backend_setting_int *setting, void *internals); + +void rdp_settings_free(rdp_internals *internals); + +bool rdp_init_settings(void *internals); diff --git a/src/rdp/rdp_user_input.c b/src/rdp/rdp_user_input.c new file mode 100644 index 0000000..c79e5b1 --- /dev/null +++ b/src/rdp/rdp_user_input.c @@ -0,0 +1,458 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "webrdp_core_api.h" +#include "webrdp_module_api.h" + +#include "rdp_backend_api.h" +#include "rdp_impl.h" + +const UINT32 ASCII_TO_SCANCODE[256] = { + 0, /* 0 */ + 0, /* 1 */ + 0, /* 2 */ + 0, /* 3 */ + 0, /* 4 */ + 0, /* 5 */ + 0, /* 6 */ + 0, /* 7 */ + RDP_SCANCODE_BACKSPACE, /* 8 */ + RDP_SCANCODE_TAB, /* 9 */ + VK_KEY_D, /* 10 */ + VK_KEY_F, /* 11 */ + VK_KEY_H, /* 12 */ + RDP_SCANCODE_RETURN, /* 13 */ + RDP_SCANCODE_BACKSPACE, /* 14 */ + VK_KEY_X, /* 15 */ + RDP_SCANCODE_LSHIFT, /* 16 */ + RDP_SCANCODE_LCONTROL, /* 17 */ + RDP_SCANCODE_LMENU, /* 18 */ + RDP_SCANCODE_PAUSE, /* 19 */ + RDP_SCANCODE_CAPSLOCK, /* 20 */ + VK_KEY_W, /* 21 */ + VK_KEY_E, /* 22 */ + VK_KEY_R, /* 23 */ + VK_KEY_Y, /* 24 */ + VK_KEY_A, /* 25 */ + VK_KEY_1, /* 26 */ + RDP_SCANCODE_ESCAPE, /* 27 */ + VK_KEY_3, /* 28 */ + VK_KEY_4, /* 29 */ + VK_KEY_6, /* 30 */ + VK_KEY_5, /* 31 */ + RDP_SCANCODE_SPACE, /* 32 */ + RDP_SCANCODE_PRIOR, /* 33 */ + RDP_SCANCODE_NEXT, /* 34 */ + RDP_SCANCODE_END, /* 35 */ + RDP_SCANCODE_HOME, /* 36 */ + RDP_SCANCODE_LEFT, /* 37 */ + RDP_SCANCODE_UP, /* 38 */ + RDP_SCANCODE_RIGHT, /* 39 */ + RDP_SCANCODE_DOWN, /* 40 */ + RDP_SCANCODE_KEY_0, /* 41 */ + RDP_SCANCODE_MULTIPLY, /* 42 */ + RDP_SCANCODE_ADD, /* 43 */ + RDP_SCANCODE_PRINTSCREEN, /* 44 */ + RDP_SCANCODE_INSERT, /* 45 */ + RDP_SCANCODE_DELETE, /* 46 */ + RDP_SCANCODE_DIVIDE, /* 47 */ + RDP_SCANCODE_KEY_0, /* 48 */ + RDP_SCANCODE_KEY_1, /* 49 */ + RDP_SCANCODE_KEY_2, /* 50 */ + RDP_SCANCODE_KEY_3, /* 51 */ + RDP_SCANCODE_KEY_4, /* 52 */ + RDP_SCANCODE_KEY_5, /* 53 */ + RDP_SCANCODE_KEY_6, /* 54 */ + RDP_SCANCODE_KEY_7, /* 55 */ + RDP_SCANCODE_KEY_8, /* 56 */ + RDP_SCANCODE_KEY_9, /* 57 */ + RDP_SCANCODE_OEM_1, /* 58 */ + RDP_SCANCODE_OEM_1, /* 59 */ + RDP_SCANCODE_OEM_COMMA, /* 60 */ + RDP_SCANCODE_OEM_PLUS, /* 61 */ + RDP_SCANCODE_OEM_PERIOD, /* 62 */ + RDP_SCANCODE_DIVIDE, /* 63 */ + RDP_SCANCODE_KEY_2, /* 64 */ + RDP_SCANCODE_KEY_A, /* 65 */ + RDP_SCANCODE_KEY_B, /* 66 */ + RDP_SCANCODE_KEY_C, /* 67 */ + RDP_SCANCODE_KEY_D, /* 68 */ + RDP_SCANCODE_KEY_E, /* 69 */ + RDP_SCANCODE_KEY_F, /* 70 */ + RDP_SCANCODE_KEY_G, /* 71 */ + RDP_SCANCODE_KEY_H, /* 72 */ + RDP_SCANCODE_KEY_I, /* 73 */ + RDP_SCANCODE_KEY_J, /* 74 */ + RDP_SCANCODE_KEY_K, /* 75 */ + RDP_SCANCODE_KEY_L, /* 76 */ + RDP_SCANCODE_KEY_M, /* 77 */ + RDP_SCANCODE_KEY_N, /* 78 */ + RDP_SCANCODE_KEY_O, /* 79 */ + RDP_SCANCODE_KEY_P, /* 80 */ + RDP_SCANCODE_KEY_Q, /* 81 */ + RDP_SCANCODE_KEY_R, /* 82 */ + RDP_SCANCODE_KEY_S, /* 83 */ + RDP_SCANCODE_KEY_T, /* 84 */ + RDP_SCANCODE_KEY_U, /* 85 */ + RDP_SCANCODE_KEY_V, /* 86 */ + RDP_SCANCODE_KEY_W, /* 87 */ + RDP_SCANCODE_KEY_X, /* 88 */ + RDP_SCANCODE_KEY_Y, /* 89 */ + RDP_SCANCODE_KEY_Z, /* 90 */ + RDP_SCANCODE_LWIN, /* 91 */ + RDP_SCANCODE_RWIN, /* 92 */ + RDP_SCANCODE_APPS, /* 93 */ + RDP_SCANCODE_KEY_6, /* 94 */ + RDP_SCANCODE_OEM_MINUS, /* 95 */ + RDP_SCANCODE_NUMPAD0, /* 96 */ + RDP_SCANCODE_NUMPAD1, /* 97 */ + RDP_SCANCODE_NUMPAD2, /* 98 */ + RDP_SCANCODE_NUMPAD3, /* 99 */ + RDP_SCANCODE_NUMPAD4, /* 100 */ + RDP_SCANCODE_NUMPAD5, /* 101 */ + RDP_SCANCODE_NUMPAD6, /* 102 */ + RDP_SCANCODE_NUMPAD7, /* 103 */ + RDP_SCANCODE_NUMPAD8, /* 104 */ + RDP_SCANCODE_NUMPAD9, /* 105 */ + RDP_SCANCODE_MULTIPLY, /* 106 */ + RDP_SCANCODE_ADD, /* 107 */ + 0, /* 108 */ + RDP_SCANCODE_SUBTRACT, /* 109 */ + RDP_SCANCODE_DELETE, /* 110 */ + RDP_SCANCODE_DIVIDE, /* 111 */ + RDP_SCANCODE_F1, /* 112 */ + RDP_SCANCODE_F2, /* 113 */ + RDP_SCANCODE_F3, /* 114 */ + RDP_SCANCODE_F4, /* 115 */ + RDP_SCANCODE_F5, /* 116 */ + RDP_SCANCODE_F6, /* 117 */ + RDP_SCANCODE_F7, /* 118 */ + RDP_SCANCODE_F8, /* 119 */ + RDP_SCANCODE_F9, /* 120 */ + RDP_SCANCODE_F10, /* 121 */ + RDP_SCANCODE_F11, /* 122 */ + RDP_SCANCODE_F12, /* 123 */ + RDP_SCANCODE_OEM_5, /* 124 */ + RDP_SCANCODE_OEM_6, /* 125 */ + VK_F4, /* 126 */ + VK_END, /* 127 */ + VK_F2, /* 128 */ + VK_NEXT, /* 129 */ + VK_F1, /* 130 */ + VK_LEFT, /* 131 */ + VK_RIGHT, /* 132 */ + VK_DOWN, /* 133 */ + VK_UP, /* 134 */ + 0, /* 135 */ + 0, /* 136 */ + 0, /* 137 */ + 0, /* 138 */ + 0, /* 139 */ + 0, /* 140 */ + 0, /* 141 */ + 0, /* 142 */ + 0, /* 143 */ + RDP_SCANCODE_NUMLOCK, /* 144 */ + RDP_SCANCODE_SCROLLLOCK, /* 145 */ + 0, /* 146 */ + 0, /* 147 */ + 0, /* 148 */ + 0, /* 149 */ + 0, /* 150 */ + 0, /* 151 */ + 0, /* 152 */ + 0, /* 153 */ + 0, /* 154 */ + 0, /* 155 */ + 0, /* 156 */ + 0, /* 157 */ + 0, /* 158 */ + 0, /* 159 */ + 0, /* 160 */ + 0, /* 161 */ + 0, /* 162 */ + 0, /* 163 */ + 0, /* 164 */ + 0, /* 165 */ + 0, /* 166 */ + 0, /* 167 */ + 0, /* 168 */ + 0, /* 169 */ + 0, /* 170 */ + 0, /* 171 */ + 0, /* 172 */ + RDP_SCANCODE_OEM_MINUS, /* 173 */ + 0, /* 174 */ + 0, /* 175 */ + 0, /* 176 */ + 0, /* 177 */ + 0, /* 178 */ + 0, /* 179 */ + 0, /* 180 */ + 0, /* 181 */ + 0, /* 182 */ + 0, /* 183 */ + 0, /* 184 */ + 0, /* 185 */ + RDP_SCANCODE_OEM_1, /* 186 */ + RDP_SCANCODE_OEM_PLUS, /* 187 */ + RDP_SCANCODE_OEM_COMMA, /* 188 */ + RDP_SCANCODE_OEM_MINUS, /* 189 */ + RDP_SCANCODE_OEM_PERIOD, /* 190 */ + RDP_SCANCODE_OEM_2, /* 191 */ + RDP_SCANCODE_OEM_3, /* 192 */ + 0, /* 193 */ + 0, /* 194 */ + 0, /* 195 */ + 0, /* 196 */ + 0, /* 197 */ + 0, /* 198 */ + 0, /* 199 */ + 0, /* 200 */ + 0, /* 201 */ + 0, /* 202 */ + 0, /* 203 */ + 0, /* 204 */ + 0, /* 205 */ + 0, /* 206 */ + 0, /* 207 */ + 0, /* 208 */ + 0, /* 209 */ + 0, /* 210 */ + 0, /* 211 */ + 0, /* 212 */ + 0, /* 213 */ + 0, /* 214 */ + 0, /* 215 */ + 0, /* 216 */ + 0, /* 217 */ + 0, /* 218 */ + RDP_SCANCODE_OEM_4, /* 219 */ + RDP_SCANCODE_OEM_5, /* 220 */ + RDP_SCANCODE_OEM_6, /* 221 */ + RDP_SCANCODE_OEM_7, /* 222 */ + 0, /* 223 */ + 0, /* 224 */ + 0, /* 225 */ + 0, /* 226 */ + 0, /* 227 */ + 0, /* 228 */ + 0, /* 229 */ + 0, /* 230 */ + 0, /* 231 */ + 0, /* 232 */ + 0, /* 233 */ + 0, /* 234 */ + 0, /* 235 */ + 0, /* 236 */ + 0, /* 237 */ + 0, /* 238 */ + 0, /* 239 */ + 0, /* 240 */ + 0, /* 241 */ + 0, /* 242 */ + 0, /* 243 */ + 0, /* 244 */ + 0, /* 245 */ + 0, /* 246 */ + 0, /* 247 */ + 0, /* 248 */ + 0, /* 249 */ + 0, /* 250 */ + 0, /* 251 */ + 0, /* 252 */ + 0, /* 253 */ + 0, /* 254 */ + 0 /* 255 */ +}; + +static bool +rdp_backend_handle_input_mouse(ws_input_mouse input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->context->input; + _i->core->api_core->reset_idle(_i->task_info); + inp->MouseEvent(inp, input.flags, input.x, input.y); + return true; +} + +static bool +rdp_backend_handle_input_kupdown(ws_input_kupdown input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->context->input; + if (0 < input.code) + { + _i->core->api_core->reset_idle(_i->task_info); + /* make byte */ + input.code = RDP_SCANCODE_CODE(input.code); + /* apply extended */ + input.code = ASCII_TO_SCANCODE[input.code]; + /* extract extended sepparatelly in tflag */ + uint32_t tflag = RDP_SCANCODE_EXTENDED(input.code) ? + KBD_FLAGS_EXTENDED : + 0; + freerdp_input_send_keyboard_event(inp, + (input.down ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE) | tflag, + input.code); + } + return true; +} + +static bool +rdp_backend_handle_input_kpress(ws_input_kpress input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->context->input; + _i->core->api_core->reset_idle(_i->task_info); + if (0x20 < input.code) + { + if (input.shiftstate & 6) + { + //Control and or Alt: Must use scan-codes since + //unicode-event can't handle these + if (((64 < input.code) && (91 > input.code)) + || ((96 < input.code) && (123 > input.code))) + { + input.code -= (input.shiftstate & 1) ? 0 : 32; + input.code + = GetVirtualScanCodeFromVirtualKeyCode( + input.code, 4); + if (0 < input.code) + { + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_DOWN, + ASCII_TO_SCANCODE[input.code]); + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_RELEASE, + ASCII_TO_SCANCODE[input.code]); + } + } + } + else + { + if (0 < input.code) + { + if (input.code == 96) + { + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, + RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, + (UINT8)RDP_SCANCODE_DELETE); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + (UINT8)RDP_SCANCODE_DELETE); + } + + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_DOWN, + ASCII_TO_SCANCODE[input.code]); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, + ASCII_TO_SCANCODE[input.code]); + } + } + } + else + { + if (0 < input.code) + { + input.code = RDP_SCANCODE_CODE(input.code); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, ASCII_TO_SCANCODE[input.code]); + freerdp_input_send_keyboard_event(inp, + KBD_FLAGS_RELEASE, ASCII_TO_SCANCODE[input.code]); + } + } + return true; +} + +static bool +rdp_backend_handle_input_kcomb(ws_input_keycomb input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->context->input; + _i->core->api_core->reset_idle(_i->task_info); + switch (input) + { + case ws_keycomb_ctrlaltdel_press: + { + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, (UINT8)RDP_SCANCODE_DELETE); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_LCONTROL); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, (UINT8)RDP_SCANCODE_DELETE); + } + break; + case ws_keycomb_alttab_press: + { + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_LMENU); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_DOWN, RDP_SCANCODE_TAB); + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_TAB); + } + break; + case ws_keycomb_alttab_release: + { + freerdp_input_send_keyboard_event( + inp, KBD_FLAGS_RELEASE, RDP_SCANCODE_LMENU); + } + break; + default: + break; + } + return true; +} + +static bool +rdp_backend_handle_input_unicode(ws_input_unicode input, void *internals) +{ + rdp_internals *_i = internals; + rdpInput *inp = _i->instance->context->input; + int i; + _i->core->api_core->reset_idle(_i->task_info); + for (i = 0; i < input.length; ++i) + { + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_DOWN, (UINT16)input.input[i]); + freerdp_input_send_unicode_keyboard_event( + inp, KBD_FLAGS_RELEASE, (UINT16)input.input[i]); + } + return true; +} + +void +register_input(wrdp_backend_module *backend) +{ + backend->callbacks_input->kcomb = rdp_backend_handle_input_kcomb; + backend->callbacks_input->kupdown = rdp_backend_handle_input_kupdown; + backend->callbacks_input->kpress = rdp_backend_handle_input_kpress; + backend->callbacks_input->mouse = rdp_backend_handle_input_mouse; + backend->callbacks_input->unicode = rdp_backend_handle_input_unicode; +} diff --git a/src/rdp/rdp_user_input.h b/src/rdp/rdp_user_input.h new file mode 100644 index 0000000..c7851e4 --- /dev/null +++ b/src/rdp/rdp_user_input.h @@ -0,0 +1,10 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#pragma once + +void register_input(wrdp_backend_module *backend); +void register_pointer(rdpPointer *p); |