From b782189569e61acca24f7820564d9f193bd87089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Tue, 9 Aug 2011 17:42:10 -0400 Subject: [PATCH] rail: started integrating Roman's RAIL virtual channel plugin --- channels/CMakeLists.txt | 1 + channels/rail/CMakeLists.txt | 35 ++ channels/rail/rail_channel_orders.c | 646 ++++++++++++++++++++++++++++ channels/rail/rail_channel_orders.h | 40 ++ channels/rail/rail_core.c | 471 ++++++++++++++++++++ channels/rail/rail_core.h | 137 ++++++ channels/rail/rail_main.c | 149 +++++++ channels/rail/rail_main.h | 34 ++ channels/rdpdr/disk/disk_file.c | 3 +- include/freerdp/constants.h | 5 +- include/freerdp/rail.h | 341 +++++++++++++++ libfreerdp-utils/event.c | 4 + 12 files changed, 1864 insertions(+), 2 deletions(-) create mode 100644 channels/rail/CMakeLists.txt create mode 100644 channels/rail/rail_channel_orders.c create mode 100644 channels/rail/rail_channel_orders.h create mode 100644 channels/rail/rail_core.c create mode 100644 channels/rail/rail_core.h create mode 100644 channels/rail/rail_main.c create mode 100644 channels/rail/rail_main.h create mode 100644 include/freerdp/rail.h diff --git a/channels/CMakeLists.txt b/channels/CMakeLists.txt index 75e5b1c20..a5a55738d 100644 --- a/channels/CMakeLists.txt +++ b/channels/CMakeLists.txt @@ -21,4 +21,5 @@ add_subdirectory(cliprdr) add_subdirectory(drdynvc) add_subdirectory(rdpdbg) add_subdirectory(rdpdr) +add_subdirectory(rail) diff --git a/channels/rail/CMakeLists.txt b/channels/rail/CMakeLists.txt new file mode 100644 index 000000000..e4e2e214f --- /dev/null +++ b/channels/rail/CMakeLists.txt @@ -0,0 +1,35 @@ +# FreeRDP: A Remote Desktop Protocol Client +# FreeRDP cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(RAIL_SRCS + rail_main.c + rail_main.h + rail_core.c + rail_core.h + rail_channel_orders.c + rail_channel_orders.h +) + +add_library(rail SHARED ${RAIL_SRCS}) +set_target_properties(rail PROPERTIES PREFIX "") + +target_link_libraries(rail freerdp-utils) + +install(TARGETS rail DESTINATION ${FREERDP_PLUGIN_PATH}) + diff --git a/channels/rail/rail_channel_orders.c b/channels/rail/rail_channel_orders.c new file mode 100644 index 000000000..163af8c46 --- /dev/null +++ b/channels/rail/rail_channel_orders.c @@ -0,0 +1,646 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Remote Applications Integrated Locally (RAIL) + + Copyright 2009 Marc-Andre Moreau + Copyright 2011 Roman Barabanov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rail_core.h" +#include "rail_main.h" + +/* + * RAIL_PDU_HEADER + * begin + * orderType = 2 bytes + * orderLength = 2 bytes + end + */ + +static size_t RAIL_PDU_HEADER_SIZE = 4; + +void stream_init_by_allocated_data(STREAM* s, void* data, size_t size) +{ + s->data = data; + s->size = size; + s->p = s->data; +} + +void* rail_alloc_order_data(size_t length) +{ + uint8 * order_start = xmalloc(length + RAIL_PDU_HEADER_SIZE); + return (order_start + RAIL_PDU_HEADER_SIZE); +} + +void write_rail_unicode_string_content(STREAM* s, RAIL_UNICODE_STRING* string) +{ + if (string->length > 0) + stream_write(s, string->buffer, string->length); +} + +void write_rail_unicode_string(STREAM* s, RAIL_UNICODE_STRING* string) +{ + stream_write_uint16(s, string->length); + + if (string->length > 0) + stream_write(s, string->buffer, string->length); +} + +void write_rail_rect_16(STREAM* s, RAIL_RECT_16* rect) +{ + stream_write_uint16(s, rect->left); /*Left*/ + stream_write_uint16(s, rect->top); /*Top*/ + stream_write_uint16(s, rect->right); /*Right*/ + stream_write_uint16(s, rect->bottom); /*Bottom*/ +} + +void read_rail_unicode_string(STREAM* s, RAIL_UNICODE_STRING * string) +{ + stream_read_uint16(s, string->length); + + string->buffer = NULL; + if (string->length > 0) + { + string->buffer = xmalloc(string->length); + stream_read(s, string->buffer, string->length); + } +} + +void free_rail_unicode_string(RAIL_UNICODE_STRING * string) +{ + if (string->buffer != NULL) + { + xfree(string->buffer); + string->buffer = NULL; + string->length = 0; + } +} + +// Used by 'rail_vchannel_send_' routines for sending constructed RAIL PDU to +// the 'rail' channel +static void rail_vchannel_send_order_data(RAIL_SESSION* session, uint16 order_type, void* allocated_order_data, uint16 data_length) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint8* header_start = ((uint8*)allocated_order_data - RAIL_PDU_HEADER_SIZE); + + + data_length += RAIL_PDU_HEADER_SIZE; + + stream_init_by_allocated_data(s, header_start, RAIL_PDU_HEADER_SIZE); + + stream_write_uint16(s, order_type); + stream_write_uint16(s, data_length); + + session->data_sender->send_rail_vchannel_data( + session->data_sender->data_sender_object, + header_start, data_length); + + // In there we free memory which we allocated in rail_alloc_order_data(..) + xfree(header_start); +} + +/* + * The Handshake PDU is exchanged between the server and the client to + * establish that both endpoints are ready to begin RAIL mode. + * The server sends the Handshake PDU and the client responds + * with the Handshake PDU. + */ +void rail_vchannel_send_handshake_order(RAIL_SESSION * session, uint32 build_number) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, build_number); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_HANDSHAKE, data, data_length); +} + +/* + * The Client Activate PDU is sent from client to server + * when a local RAIL window on the client is activated or deactivated. + */ +void rail_vchannel_send_activate_order(RAIL_SESSION* session, uint32 window_id, uint8 enabled) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 5; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, window_id); + stream_write_uint8(s, enabled); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_ACTIVATE, data, data_length); +} + +/* + * Indicates a Client Execute PDU from client to server to request that a + * remote application launch on the server. + * */ +void rail_vchannel_send_exec_order(RAIL_SESSION* session, uint16 flags, + RAIL_UNICODE_STRING* exe_or_file, RAIL_UNICODE_STRING* working_directory, RAIL_UNICODE_STRING* arguments) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + + uint16 exe_or_file_length = exe_or_file->length; + uint16 working_directory_length = working_directory->length; + uint16 arguments_length = arguments->length; + + size_t data_length = + 2 + /*Flags (2 bytes)*/ + 2 + /*ExeOrFileLength (2 bytes)*/ + 2 + /*WorkingDirLength (2 bytes)*/ + 2 + /*ArgumentsLen (2 bytes)*/ + exe_or_file_length + /*ExeOrFile (variable)*/ + working_directory_length + /*WorkingDir (variable)*/ + arguments_length; /*Arguments (variable):*/ + + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint16(s, flags); + stream_write_uint16(s, exe_or_file_length); + stream_write_uint16(s, working_directory_length); + stream_write_uint16(s, arguments_length); + + write_rail_unicode_string_content(s, exe_or_file); + write_rail_unicode_string_content(s, working_directory); + write_rail_unicode_string_content(s, arguments); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_EXEC, data, data_length); +} + +size_t get_sysparam_size_in_rdp_stream(RAIL_CLIENT_SYSPARAM * sysparam) +{ + switch (sysparam->type) + { + case SPI_SETDRAGFULLWINDOWS: {return 1;} + case SPI_SETKEYBOARDCUES: {return 1;} + case SPI_SETKEYBOARDPREF: {return 1;} + case SPI_SETMOUSEBUTTONSWAP: {return 1;} + case SPI_SETWORKAREA: {return 8;} + case RAIL_SPI_DISPLAYCHANGE: {return 8;} + case RAIL_SPI_TASKBARPOS: {return 8;} + case SPI_SETHIGHCONTRAST: + { + return (4 + /*Flags (4 bytes)*/ + 4 + /*ColorSchemeLength (4 bytes)*/ + 2 + /*UNICODE_STRING.cbString (2 bytes)*/ + sysparam->value.high_contrast_system_info.color_scheme.length); + } + }; + + assert(!"Unknown sysparam type"); + return 0; +} + +/* + * Indicates a Client System Parameters Update PDU from client to server to + * synchronize system parameters on the server with those on the client. + */ +void rail_vchannel_send_client_sysparam_update_order(RAIL_SESSION* session, RAIL_CLIENT_SYSPARAM* sysparam) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + size_t data_length = 4; /*SystemParam (4 bytes)*/ + void* data = 0; + + data_length += get_sysparam_size_in_rdp_stream(sysparam); + + data = rail_alloc_order_data(data_length); + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, sysparam->type); + + switch (sysparam->type) + { + case SPI_SETDRAGFULLWINDOWS: + stream_write_uint8(s, sysparam->value.full_window_drag_enabled); + break; + + case SPI_SETKEYBOARDCUES: + stream_write_uint8(s, sysparam->value.menu_access_key_always_underlined); + break; + + case SPI_SETKEYBOARDPREF: + stream_write_uint8(s, sysparam->value.keyboard_for_user_prefered); + break; + + case SPI_SETMOUSEBUTTONSWAP: + stream_write_uint8(s, sysparam->value.left_right_mouse_buttons_swapped); + break; + + case SPI_SETWORKAREA: + write_rail_rect_16(s, &sysparam->value.work_area); + break; + + case RAIL_SPI_DISPLAYCHANGE: + write_rail_rect_16(s, &sysparam->value.display_resolution); + break; + + case RAIL_SPI_TASKBARPOS: + write_rail_rect_16(s, &sysparam->value.taskbar_size); + break; + + case SPI_SETHIGHCONTRAST: + { + uint32 color_scheme_length = 2 + + sysparam->value.high_contrast_system_info.color_scheme.length; + + stream_write_uint32(s, sysparam->value.high_contrast_system_info.flags); + stream_write_uint32(s, color_scheme_length); + write_rail_unicode_string(s, &sysparam->value.high_contrast_system_info.color_scheme); + break; + } + + default: + assert(!"Unknown sysparam type"); + break; + } + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_SYSPARAM, data, data_length); +} + +/* + * Indicates a Client System Command PDU from client to server when a local + * RAIL window on the client receives a command to perform an action on the + * window, such as minimize or maximize. + */ +void rail_vchannel_send_syscommand_order(RAIL_SESSION* session, uint32 window_id, uint16 command) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4 + 2; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, window_id); + stream_write_uint16(s, command); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_SYSCOMMAND, data, data_length); +} + +/* + * The Client Notify Event PDU packet is sent from a client to a server when + * a local RAIL Notification Icon on the client receives a keyboard or mouse + * message from the user. This notification is forwarded to the server via + * the Notify Event PDU. + * */ +void rail_vchannel_send_notify_event_order(RAIL_SESSION * session, uint32 window_id, uint32 notify_icon_id, uint32 message) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4 * 3; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, window_id); + stream_write_uint32(s, notify_icon_id); + stream_write_uint32(s, message); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_NOTIFY_EVENT, data, data_length); +} + +/* + * The Client Window Move PDU packet is sent from the client to the server + * when a local window is ending a move or resize. The client communicates the + * locally moved or resized window's position to the server by using this packet. + * The server uses this information to reposition its window.*/ +void rail_vchannel_send_client_windowmove_order(RAIL_SESSION* session, uint32 window_id, RAIL_RECT_16* new_position) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4 + 2 * 4; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, window_id); + stream_write_uint16(s, new_position->left); + stream_write_uint16(s, new_position->top); + stream_write_uint16(s, new_position->right); + stream_write_uint16(s, new_position->bottom); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_WINDOWMOVE, data, data_length); +} + +/* + * The Client Information PDU is sent from client to server and contains + * information about RAIL client state and features supported by the client. + * */ +void rail_vchannel_send_client_information_order(RAIL_SESSION* session, uint32 flags) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, flags); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_CLIENTSTATUS, data, data_length); +} + +/* + * The Client System Menu PDU packet is sent from the client to the server + * when a local RAIL window on the client receives a command to display its + * System menu. This command is forwarded to the server via + * the System menu PDU. + */ +void rail_vchannel_send_client_system_menu_order(RAIL_SESSION* session, uint32 window_id, uint16 left, uint16 top) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4 + 2 * 2; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, window_id); + stream_write_uint16(s, left); + stream_write_uint16(s, top); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_SYSMENU, data, + data_length); +} + +/* + * The Language Bar Information PDU is used to set the language bar status. + * It is sent from a client to a server or a server to a client, but only when + * both support the Language Bar docking capability + * (TS_RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED). + * This PDU contains information about the language bar status. + * */ +void rail_vchannel_send_client_langbar_information_order(RAIL_SESSION* session, uint32 langbar_status) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, langbar_status); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_LANGBARINFO, data, data_length); +} + +/* + * The Client Get Application ID PDU is sent from a client to a server. + * This PDU requests information from the server about the Application ID + * that the window SHOULD <15> have on the client. + * */ +void rail_vchannel_send_get_appid_req_order(RAIL_SESSION* session, uint32 window_id) +{ + STREAM st_stream = {0}; + STREAM* s = &st_stream; + uint16 data_length = 4; + void* data = rail_alloc_order_data(data_length); + + stream_init_by_allocated_data(s, data, data_length); + + stream_write_uint32(s, window_id); + + rail_vchannel_send_order_data(session, RDP_RAIL_ORDER_GET_APPID_REQ, data, data_length); +} + +/* + * Look at rail_vchannel_send_handshake_order(...) + */ +void rail_vchannel_process_handshake_order(RAIL_SESSION* session, STREAM* s) +{ + uint32 build_number = 0; + + stream_read_uint32(s, build_number); + rail_core_handle_server_handshake(session, build_number); +} + +/* + * The Server Execute Result PDU is sent from server to client in response to + * a Client Execute PDU request, and contains the result of the server's + * attempt to launch the requested executable. + */ +void rail_vchannel_process_exec_result_order(RAIL_SESSION* session, STREAM* s) +{ + uint16 flags = 0; + uint16 exec_result = 0; + uint32 raw_result = 0; + RAIL_UNICODE_STRING exe_or_file = {0}; + + stream_read_uint16(s, flags); /*Flags (2 bytes)*/ + stream_read_uint16(s, exec_result); /*ExecResult (2 bytes)*/ + stream_read_uint32(s, raw_result); /*RawResult (4 bytes)*/ + stream_seek(s, 2); /*Padding (2 bytes)*/ + read_rail_unicode_string(s, &exe_or_file); /*ExeOrFileLength with ExeOrFile (variable)*/ + + rail_core_handle_exec_result(session, flags, exec_result, raw_result, &exe_or_file); + free_rail_unicode_string(&exe_or_file); +} + +/* + * The Server System Parameters Update PDU is sent from the server to client to + * synchronize system parameters on the client with those on the server. + */ +void rail_vchannel_process_server_sysparam_update_order(RAIL_SESSION* session, STREAM* s) +{ + RAIL_SERVER_SYSPARAM sysparam = {0}; + + stream_read_uint32(s, sysparam.type); + + switch (sysparam.type) + { + case SPI_SETSCREENSAVEACTIVE: + stream_read_uint8(s, sysparam.value.screen_saver_enabled); + break; + + case SPI_SETSCREENSAVESECURE: + stream_read_uint8(s, sysparam.value.screen_saver_lock_enabled); + break; + + default: + assert(!"Undocumented RAIL server sysparam type"); + break; + }; + + rail_core_handle_server_sysparam(session, &sysparam); +} + +/* + * The Server Move/Size Start PDU packet is sent by the server when a window on + * the server is beginning a move or resize. + * The client uses this information to initiate a local move or resize of the + * corresponding local window. + * + * The Server Move/Size End PDU is sent by the server when a window on the + * server is completing a move or resize. + * The client uses this information to end a local move/resize of the + * corresponding local window. + * + */ +void rail_vchannel_process_server_movesize_order(RAIL_SESSION* session, STREAM* s) +{ + uint32 window_id = 0; + uint16 move_size_started = 0; + uint16 move_size_type = 0; + uint16 pos_x = 0; + uint16 pos_y = 0; + + stream_read_uint32(s, window_id); + stream_read_uint16(s, move_size_started); + stream_read_uint16(s, move_size_type); + stream_read_uint16(s, pos_x); + stream_read_uint16(s, pos_y); + + rail_core_handle_server_movesize(session, window_id, move_size_started, move_size_type, pos_x, pos_y); +} + +/* + * The Server Min Max Info PDU is sent from a server to a client when a window + * move or resize on the server is being initiated. + * This PDU contains information about the minimum and maximum extents to + * which the window can be moved or sized. + */ +void rail_vchannel_process_server_minmax_info_order(RAIL_SESSION* session, STREAM* s) +{ + uint32 window_id = 0; + uint16 max_width = 0; + uint16 max_height = 0; + uint16 max_pos_x = 0; + uint16 max_pos_y = 0; + uint16 min_track_width = 0; + uint16 min_track_height = 0; + uint16 max_track_width = 0; + uint16 max_track_height = 0; + + stream_read_uint32(s, window_id); + stream_read_uint16(s, max_width); + stream_read_uint16(s, max_height); + stream_read_uint16(s, max_pos_x); + stream_read_uint16(s, max_pos_y); + stream_read_uint16(s, min_track_width); + stream_read_uint16(s, min_track_height); + stream_read_uint16(s, max_track_width); + stream_read_uint16(s, max_track_height); + + rail_core_handle_server_minmax_info(session, window_id, max_width, + max_height, max_pos_x, max_pos_y, min_track_width, min_track_height, + max_track_width, max_track_height); +} + +/* + *The Language Bar Information PDU is used to set the language bar status. + */ +void rail_vchannel_process_server_langbar_info_order(RAIL_SESSION* session, STREAM* s) +{ + uint32 langbar_status = 0; + + stream_read_uint32(s, langbar_status); + + rail_core_handle_server_langbar_info(session, langbar_status); +} + +/* + * The Server Get Application ID Response PDU is sent from a server to a client. + * This PDU MAY be sent to the client as a response to a Client Get Application + * ID PDU. This PDU specifies the Application ID that the specified window + * SHOULD have on the client. The client MAY ignore this PDU. + */ +static void rail_vchannel_process_server_get_appid_resp_order(RAIL_SESSION* session, STREAM* s) +{ + uint32 window_id = 0; + RAIL_UNICODE_STRING app_id = {0}; + + app_id.length = 256; + app_id.buffer = xmalloc(app_id.length); + + stream_read_uint32(s, window_id); + stream_read(s, app_id.buffer, app_id.length); + + rail_core_handle_server_get_app_resp(session, window_id, &app_id); + free_rail_unicode_string(&app_id); +} + +void rail_vchannel_process_received_vchannel_data(RAIL_SESSION * session, STREAM* s) +{ + size_t length = 0; + uint16 order_type = 0; + uint16 order_length = 0; + + length = ((s->data + s->size) - s->p); + + stream_read_uint16(s, order_type); /* orderType */ + stream_read_uint16(s, order_length); /* orderLength */ + + DEBUG_RAIL("rail_on_channel_data_received: session=0x%p data_size=%d " + "orderType=0x%X orderLength=%d", session, (int) length, order_type, order_length); + + switch (order_type) + { + case RDP_RAIL_ORDER_HANDSHAKE: + rail_vchannel_process_handshake_order(session, s); + break; + + case RDP_RAIL_ORDER_EXEC_RESULT: + rail_vchannel_process_exec_result_order(session, s); + break; + + case RDP_RAIL_ORDER_SYSPARAM: + rail_vchannel_process_server_sysparam_update_order(session, s); + break; + + case RDP_RAIL_ORDER_LOCALMOVESIZE: + rail_vchannel_process_server_movesize_order(session, s); + break; + + case RDP_RAIL_ORDER_MINMAXINFO: + rail_vchannel_process_server_minmax_info_order(session, s); + break; + + case RDP_RAIL_ORDER_LANGBARINFO: + rail_vchannel_process_server_langbar_info_order(session, s); + break; + + case RDP_RAIL_ORDER_GET_APPID_RESP: + rail_vchannel_process_server_get_appid_resp_order(session, s); + break; + + default: + DEBUG_RAIL("rail_on_channel_data_received: " + "Undocumented RAIL server PDU: order_type=0x%X",order_type); + break; + } +} + diff --git a/channels/rail/rail_channel_orders.h b/channels/rail/rail_channel_orders.h new file mode 100644 index 000000000..c1cc4e494 --- /dev/null +++ b/channels/rail/rail_channel_orders.h @@ -0,0 +1,40 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Remote Applications Integrated Locally (RAIL) + + Copyright 2009 Marc-Andre Moreau + Copyright 2011 Roman Barabanov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef __RAIL_CHANNEL_ORDERS_H +#define __RAIL_CHANNEL_ORDERS_H + +#include "rail_core.h" + +void rail_vchannel_send_handshake_order(RAIL_SESSION* session, uint32 build_number); +void rail_vchannel_send_client_information_order(RAIL_SESSION* session, uint32 flags); +void rail_vchannel_send_activate_order(RAIL_SESSION* session, uint32 window_id, uint8 enabled); +void rail_vchannel_send_exec_order(RAIL_SESSION* session, uint16 flags, RAIL_UNICODE_STRING* exe_or_file, + RAIL_UNICODE_STRING* working_directory, RAIL_UNICODE_STRING* arguments); +void rail_vchannel_send_client_sysparam_update_order(RAIL_SESSION* session, RAIL_CLIENT_SYSPARAM* sysparam); +void rail_vchannel_send_syscommand_order(RAIL_SESSION* session, uint32 window_id, uint16 command); +void rail_vchannel_send_notify_event_order(RAIL_SESSION* session, uint32 window_id, uint32 notify_icon_id, uint32 message); +void rail_vchannel_send_client_windowmove_order(RAIL_SESSION* session, uint32 window_id, RAIL_RECT_16* new_position); +void rail_vchannel_send_client_system_menu_order(RAIL_SESSION* session, uint32 window_id, uint16 left, uint16 top); +void rail_vchannel_send_client_langbar_information_order(RAIL_SESSION* session, uint32 langbar_status); +void rail_vchannel_send_get_appid_req_order(RAIL_SESSION* session, uint32 window_id); +void rail_vchannel_process_received_vchannel_data(RAIL_SESSION* session, STREAM* s); + +#endif /* __RAIL_CHANNEL_ORDERS_H */ diff --git a/channels/rail/rail_core.c b/channels/rail/rail_core.c new file mode 100644 index 000000000..9c52aae36 --- /dev/null +++ b/channels/rail/rail_core.c @@ -0,0 +1,471 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Remote Applications Integrated Locally (RAIL) + * + * Copyright 2011 Marc-Andre Moreau + * Copyright 2011 Roman Barabanov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rail_core.h" +#include "rail_channel_orders.h" + +/* +// Initialization stage in UI for RAIL: +// 1) create a sequence of rail_notify_client_sysparam_update() +// calls with all current client system parameters +// +// 2) if Language Bar capability enabled - call updating Language Bar PDU +// +// 3) prepare and call rail_client_execute(exe_or_file, working_dir, args) +// +*/ + + + +/* +Flow of init stage over channel; + + Client notify UI about session start + and go to RAIL_ESTABLISHING state. + + Client wait for Server Handshake PDU + Server send Handshake request + Client check Handshake response. + If NOT OK - exit with specified reason + Client send Handshake response + Server send Server System + Parameters Update (in parallel) + Client send Client Information + Client send Client System Parameters Update + Client send Client Execute + Server send Server Execute Result + Client check Server Execute Result. If NOT OK - exit with specified reason + + Client notify UI about success session establishing and go to + RAIL_ESTABLISHED state. +*/ + +void init_vchannel_event(RAIL_VCHANNEL_EVENT* event, uint32 event_id) +{ + memset(event, 0, sizeof(RAIL_VCHANNEL_EVENT)); + event->event_id = event_id; +} + +void init_rail_string(RAIL_STRING * rail_string, const char * string) +{ + rail_string->buffer = (uint8*)string; + rail_string->length = strlen(string) + 1; +} + +void rail_string2unicode_string(RAIL_SESSION* session, RAIL_STRING* string, RAIL_UNICODE_STRING* unicode_string) +{ + size_t result_length = 0; + char* result_buffer = NULL; + + unicode_string->buffer = NULL; + unicode_string->length = 0; + + if (string->length == 0) return; + + result_buffer = freerdp_uniconv_out(session->uniconv, (char*) string->buffer, &result_length); + + unicode_string->buffer = (uint8*)result_buffer; + unicode_string->length = (uint16)result_length; +} + +void rail_unicode_string2string(RAIL_SESSION* session, RAIL_UNICODE_STRING* unicode_string, RAIL_STRING* string) +{ + char* result_buffer = NULL; + + string->buffer = NULL; + string->length = 0; + + if (unicode_string->length == 0) return; + + result_buffer = freerdp_uniconv_in(session->uniconv, unicode_string->buffer, unicode_string->length); + + string->buffer = (uint8*)result_buffer; + string->length = strlen(result_buffer) + 1; +} + +RAIL_SESSION* rail_core_session_new(RAIL_VCHANNEL_DATA_SENDER* data_sender, RAIL_VCHANNEL_EVENT_SENDER* event_sender) +{ + RAIL_SESSION* self; + + self = (RAIL_SESSION*) xzalloc(sizeof(RAIL_SESSION)); + + if (self != NULL) + { + self->data_sender = data_sender; + self->event_sender = event_sender; + self->uniconv = freerdp_uniconv_new(); + } + + return self; +} + +void rail_core_session_free(RAIL_SESSION* rail_session) +{ + if (rail_session != NULL) + { + freerdp_uniconv_free(rail_session->uniconv); + xfree(rail_session); + } +} + +void rail_core_on_channel_connected(RAIL_SESSION* session) +{ + DEBUG_RAIL("RAIL channel connected."); +} + +void rail_core_on_channel_terminated(RAIL_SESSION* session) +{ + DEBUG_RAIL("RAIL channel terminated."); +} + +void rail_core_handle_server_handshake(RAIL_SESSION* session, uint32 build_number) +{ + uint32 client_build_number = 0x00001db0; + RAIL_VCHANNEL_EVENT event = {0}; + + DEBUG_RAIL("rail_core_handle_server_hadshake: session=0x%p buildNumber=0x%X.", session, build_number); + + // Step 1. Send Handshake PDU (2.2.2.2.1) + // Fixed: MS-RDPERP 1.3.2.1 is not correct! + rail_vchannel_send_handshake_order(session, client_build_number); + + // Step 2. Send Client Information PDU (2.2.2.2.1) + rail_vchannel_send_client_information_order(session, RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE); + + // Step 3. Notify UI about session establishing and about requirements to + // start UI initialization stage. + init_vchannel_event(&event, RAIL_VCHANNEL_EVENT_SESSION_ESTABLISHED); + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_handle_exec_result(RAIL_SESSION* session, uint16 flags, uint16 exec_result, uint32 raw_result, RAIL_UNICODE_STRING* exe_or_file) +{ + RAIL_VCHANNEL_EVENT event = {0}; + RAIL_STRING exe_or_file_; + + DEBUG_RAIL("rail_core_handle_exec_result: session=0x%p flags=0x%X " + "exec_result=0x%X raw_result=0x%X exe_or_file=(length=%d dump>)", + session, flags, exec_result, raw_result, exe_or_file->length); + +#ifdef WITH_DEBUG_RAIL + freerdp_hexdump(exe_or_file->buffer, exe_or_file->length); +#endif + + rail_unicode_string2string(session, exe_or_file, &exe_or_file_); + + init_vchannel_event(&event, RAIL_VCHANNEL_EVENT_EXEC_RESULT_RETURNED); + event.param.exec_result_info.flags = flags; + event.param.exec_result_info.exec_result = exec_result; + event.param.exec_result_info.raw_result = raw_result; + event.param.exec_result_info.exe_or_file = exe_or_file_.buffer; + + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_handle_server_sysparam(RAIL_SESSION* session, RAIL_SERVER_SYSPARAM* sysparam) +{ + RAIL_VCHANNEL_EVENT event = {0}; + + DEBUG_RAIL("rail_core_handle_server_sysparam: session=0x%p " + "type=0x%X scr_enabled=%d scr_lock_enabled=%d", + session, sysparam->type, sysparam->value.screen_saver_enabled, + sysparam->value.screen_saver_lock_enabled); + + init_vchannel_event(&event, RAIL_VCHANNEL_EVENT_SERVER_SYSPARAM_RECEIVED); + event.param.server_param_info.param_type = sysparam->type; + event.param.server_param_info.screen_saver_enabled = + ((sysparam->value.screen_saver_enabled != 0) ? True: False); + + event.param.server_param_info.screen_saver_lock_enabled = + ((sysparam->value.screen_saver_lock_enabled != 0) ? True: False); + + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_handle_server_movesize(RAIL_SESSION* session, uint32 window_id, + uint16 move_size_started, uint16 move_size_type, uint16 pos_x, uint16 pos_y) +{ + RAIL_VCHANNEL_EVENT event = {0}; + + DEBUG_RAIL("rail_core_handle_server_movesize: session=0x%p " + "window_id=0x%X started=%d move_size_type=%d pos_x=%d pos_y=%d", + session, window_id, move_size_started, move_size_type, pos_x, pos_y); + + init_vchannel_event(&event, ((move_size_started != 0) ? + RAIL_VCHANNEL_EVENT_MOVESIZE_STARTED: + RAIL_VCHANNEL_EVENT_MOVESIZE_FINISHED)); + + event.param.movesize_info.window_id = window_id; + event.param.movesize_info.move_size_type = move_size_type; + event.param.movesize_info.pos_x = pos_x; + event.param.movesize_info.pos_y = pos_y; + + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_handle_server_minmax_info(RAIL_SESSION* session, + uint32 window_id, uint16 max_width, uint16 max_height, uint16 max_pos_x, uint16 max_pos_y, + uint16 min_track_width, uint16 min_track_height, uint16 max_track_width, uint16 max_track_height) +{ + RAIL_VCHANNEL_EVENT event = {0}; + + DEBUG_RAIL("rail_core_handle_server_minmax_info: session=0x%p " + "window_id=0x%X max_width=%d max_height=%d max_pos_x=%d max_pos_y=%d " + "min_track_width=%d min_track_height=%d max_track_width=%d max_track_height=%d", + session, window_id, max_width, max_height, max_pos_x, max_pos_y, + min_track_width, min_track_height, max_track_width, max_track_height); + + init_vchannel_event(&event, RAIL_VCHANNEL_EVENT_MINMAX_INFO_UPDATED); + + event.param.minmax_info.window_id = window_id; + + event.param.minmax_info.max_width = max_width; + event.param.minmax_info.max_height = max_height; + event.param.minmax_info.max_pos_x = max_pos_x; + event.param.minmax_info.max_pos_y = max_pos_y; + event.param.minmax_info.min_track_width = min_track_width; + event.param.minmax_info.min_track_height = min_track_height; + event.param.minmax_info.max_track_width = max_track_width; + event.param.minmax_info.max_track_height = max_track_height; + + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_handle_server_langbar_info(RAIL_SESSION* session, uint32 langbar_status) +{ + RAIL_VCHANNEL_EVENT event = {0}; + + DEBUG_RAIL("rail_core_handle_server_langbar_info: session=0x%p " + "langbar_status=0x%X", session, langbar_status); + + init_vchannel_event(&event, RAIL_VCHANNEL_EVENT_LANGBAR_STATUS_UPDATED); + + event.param.langbar_info.status = langbar_status; + + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_handle_server_get_app_resp(RAIL_SESSION* session, uint32 window_id, RAIL_UNICODE_STRING * app_id) +{ + RAIL_VCHANNEL_EVENT event = { 0 }; + RAIL_STRING app_id_; + + DEBUG_RAIL("rail_core_handle_server_get_app_resp: session=0x%p " + "window_id=0x%X app_id=(length=%d dump>)", session, window_id, app_id->length); + +#ifdef WITH_DEBUG_RAIL + freerdp_hexdump(app_id->buffer, app_id->length); +#endif + + rail_unicode_string2string(session, app_id, &app_id_); + + init_vchannel_event(&event, RAIL_VCHANNEL_EVENT_LANGBAR_STATUS_UPDATED); + + event.param.app_response_info.window_id= window_id; + event.param.app_response_info.application_id = app_id_.buffer; + + session->event_sender->send_rail_vchannel_event(session->event_sender->event_sender_object, &event); +} + +void rail_core_send_client_execute(RAIL_SESSION* session, + boolean exec_or_file_is_file_path, const char* rail_exe_or_file, + const char* rail_working_directory, const char* rail_arguments) +{ + RAIL_STRING exe_or_file_; + RAIL_STRING working_directory_; + RAIL_STRING arguments_; + RAIL_UNICODE_STRING exe_or_file; + RAIL_UNICODE_STRING working_directory; + RAIL_UNICODE_STRING arguments; + uint16 flags; + + init_rail_string(&exe_or_file_, rail_exe_or_file); + init_rail_string(&working_directory_, rail_working_directory); + init_rail_string(&arguments_, rail_arguments); + + rail_string2unicode_string(session, &exe_or_file_, &exe_or_file); + rail_string2unicode_string(session, &working_directory_, &working_directory); + rail_string2unicode_string(session, &arguments_, &arguments); + + flags = (RAIL_EXEC_FLAG_EXPAND_WORKINGDIRECTORY | RAIL_EXEC_FLAG_EXPAND_ARGUMENTS); + + if (exec_or_file_is_file_path) + { + flags |= (RAIL_EXEC_FLAG_TRANSLATE_FILES | RAIL_EXEC_FLAG_FILE); + } + + rail_vchannel_send_exec_order(session, flags, &exe_or_file, + &working_directory, &arguments); + + free_rail_unicode_string(&exe_or_file); + free_rail_unicode_string(&working_directory); + free_rail_unicode_string(&arguments); +} + +uint8 boolean2uint8(boolean value) +{ + return ((value == True) ? 1 : 0); +} + +uint8 copy_rail_rect_16(RAIL_RECT_16* src, RAIL_RECT_16* dst) +{ + memcpy(dst, src, sizeof(RAIL_RECT_16)); + return 0; +} + +static void rail_core_handle_ui_update_client_sysparam(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + RAIL_CLIENT_SYSPARAM sys_param; + + memset(&sys_param, 0, sizeof(sys_param)); + + sys_param.type = event->param.sysparam_info.param; + + sys_param.value.full_window_drag_enabled = + boolean2uint8(event->param.sysparam_info.value.full_window_drag_enabled); + + sys_param.value.menu_access_key_always_underlined = + boolean2uint8(event->param.sysparam_info.value.menu_access_key_always_underlined); + + sys_param.value.keyboard_for_user_prefered = + boolean2uint8(event->param.sysparam_info.value.keyboard_for_user_prefered); + + sys_param.value.left_right_mouse_buttons_swapped = + boolean2uint8(event->param.sysparam_info.value.left_right_mouse_buttons_swapped); + + copy_rail_rect_16(&event->param.sysparam_info.value.work_area, + &sys_param.value.work_area); + + copy_rail_rect_16(&event->param.sysparam_info.value.display_resolution, + &sys_param.value.display_resolution); + + copy_rail_rect_16(&event->param.sysparam_info.value.taskbar_size, + &sys_param.value.taskbar_size); + + sys_param.value.high_contrast_system_info.flags = + event->param.sysparam_info.value.high_contrast_system_info.flags; + + + if (sys_param.type == SPI_SETHIGHCONTRAST) + { + RAIL_STRING color_scheme; + + init_rail_string(&color_scheme, + event->param.sysparam_info.value.high_contrast_system_info.color_scheme); + + rail_string2unicode_string(session, &color_scheme, &sys_param.value.high_contrast_system_info.color_scheme); + } + + rail_vchannel_send_client_sysparam_update_order(session, &sys_param); + free_rail_unicode_string(&sys_param.value.high_contrast_system_info.color_scheme); +} + +static void rail_core_handle_ui_execute_remote_app(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_core_send_client_execute(session, + event->param.execute_info.exec_or_file_is_file_path, + event->param.execute_info.exe_or_file, + event->param.execute_info.working_directory, + event->param.execute_info.arguments); +} + +static void rail_core_handle_ui_activate(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_vchannel_send_activate_order( + session, event->param.activate_info.window_id, + ((event->param.activate_info.enabled == True) ? 1 : 0)); +} + +static void rail_core_handle_ui_sys_command(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_vchannel_send_syscommand_order(session, + event->param.syscommand_info.window_id, + event->param.syscommand_info.syscommand); +} + +static void rail_core_handle_ui_notify(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_vchannel_send_notify_event_order(session, + event->param.notify_info.window_id, + event->param.notify_info.notify_icon_id, + event->param.notify_info.message); +} + +static void rail_core_handle_ui_window_move(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_vchannel_send_client_windowmove_order(session, + event->param.window_move_info.window_id, + &event->param.window_move_info.new_position); +} + +static void rail_core_handle_ui_system_menu(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_vchannel_send_client_system_menu_order(session, + event->param.system_menu_info.window_id, + event->param.system_menu_info.left, + event->param.system_menu_info.top); +} + +static void rail_core_handle_ui_get_app_id(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + rail_vchannel_send_get_appid_req_order(session, event->param.get_app_id_info.window_id); +} + +void rail_core_handle_ui_event(RAIL_SESSION* session, RAIL_UI_EVENT* event) +{ + + struct + { + uint32 event_id; + void (*event_handler)(RAIL_SESSION* session, RAIL_UI_EVENT* event); + } handlers_table[] = + { + {RAIL_UI_EVENT_UPDATE_CLIENT_SYSPARAM, rail_core_handle_ui_update_client_sysparam}, + {RAIL_UI_EVENT_EXECUTE_REMOTE_APP, rail_core_handle_ui_execute_remote_app}, + {RAIL_UI_EVENT_ACTIVATE, rail_core_handle_ui_activate}, + {RAIL_UI_EVENT_SYS_COMMAND, rail_core_handle_ui_sys_command}, + {RAIL_UI_EVENT_NOTIFY, rail_core_handle_ui_notify}, + {RAIL_UI_EVENT_WINDOW_MOVE, rail_core_handle_ui_window_move}, + {RAIL_UI_EVENT_SYSTEM_MENU, rail_core_handle_ui_system_menu}, + {RAIL_UI_EVENT_LANGBAR_INFO, rail_core_handle_ui_system_menu}, + {RAIL_UI_EVENT_GET_APP_ID, rail_core_handle_ui_get_app_id} + }; + + int i = 0; + + for (i = 0; i < RAIL_ARRAY_SIZE(handlers_table); i++) + { + if ((event->event_id == handlers_table[i].event_id) && + (handlers_table[i].event_handler != NULL)) + { + (handlers_table[i].event_handler)(session, event); + } + } +} + diff --git a/channels/rail/rail_core.h b/channels/rail/rail_core.h new file mode 100644 index 000000000..027172c19 --- /dev/null +++ b/channels/rail/rail_core.h @@ -0,0 +1,137 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Remote Applications Integrated Locally (RAIL) + * + * Copyright 2011 Marc-Andre Moreau + * Copyright 2011 Roman Barabanov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RAIL_CORE_H +#define __RAIL_CORE_H + +#include +#include +#include +#include + +#include +#include + +#define WITH_DEBUG_RAIL 1 + +#ifdef WITH_DEBUG_RAIL +#define DEBUG_RAIL(fmt, ...) DEBUG_CLASS(RAIL, fmt, ## __VA_ARGS__) +#else +#define DEBUG_RAIL(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + +#define RAIL_ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +typedef struct _RAIL_STRING +{ + uint16 length; + uint8 *buffer; +} RAIL_STRING; + +typedef struct _RAIL_HIGHCONTRAST +{ + uint32 flags; + RAIL_UNICODE_STRING color_scheme; + +} RAIL_HIGHCONTRAST; + +typedef struct _RAIL_CLIENT_SYSPARAM +{ + uint32 type; + + union + { + uint8 full_window_drag_enabled; + uint8 menu_access_key_always_underlined; + uint8 keyboard_for_user_prefered; + uint8 left_right_mouse_buttons_swapped; + RAIL_RECT_16 work_area; + RAIL_RECT_16 display_resolution; + RAIL_RECT_16 taskbar_size; + RAIL_HIGHCONTRAST high_contrast_system_info; + } value; +} RAIL_CLIENT_SYSPARAM; + +struct _RAIL_SERVER_SYSPARAM +{ + uint32 type; + + union + { + uint8 screen_saver_enabled; + uint8 screen_saver_lock_enabled; + } value; +}; +typedef struct _RAIL_SERVER_SYSPARAM RAIL_SERVER_SYSPARAM; + +struct _RAIL_VCHANNEL_DATA_SENDER +{ + void* data_sender_object; + void (*send_rail_vchannel_data)(void* sender_object, void* data, size_t length); +}; +typedef struct _RAIL_VCHANNEL_DATA_SENDER RAIL_VCHANNEL_DATA_SENDER; + +struct _RAIL_VCHANNEL_EVENT_SENDER +{ + void * event_sender_object; + void (*send_rail_vchannel_event)(void* ui_event_sender_object, RAIL_VCHANNEL_EVENT* event); +}; +typedef struct _RAIL_VCHANNEL_EVENT_SENDER RAIL_VCHANNEL_EVENT_SENDER; + +struct _RAIL_SESSION +{ + UNICONV * uniconv; + RAIL_VCHANNEL_DATA_SENDER* data_sender; + RAIL_VCHANNEL_EVENT_SENDER* event_sender; +}; +typedef struct _RAIL_SESSION RAIL_SESSION; + +RAIL_SESSION* rail_core_session_new(RAIL_VCHANNEL_DATA_SENDER *data_sender, RAIL_VCHANNEL_EVENT_SENDER *event_sender); +void rail_core_session_free(RAIL_SESSION * rail_session); + +// RAIL Core Handlers for events from channel plugin interface + +void rail_core_on_channel_connected(RAIL_SESSION* rail_session); +void rail_core_on_channel_terminated(RAIL_SESSION* rail_session); +void rail_core_on_channel_data_received(RAIL_SESSION* rail_session, void* data, size_t length); + +// RAIL Core Handlers for events from UI + +void rail_core_handle_ui_event(RAIL_SESSION* session, RAIL_UI_EVENT* event); + +// RAIL Core Handlers for events from channel orders reader +void rail_core_handle_server_handshake(RAIL_SESSION* session, uint32 build_number); + +void rail_core_handle_exec_result(RAIL_SESSION* session, uint16 flags, + uint16 exec_result, uint32 raw_result, RAIL_UNICODE_STRING* exe_or_file); + +void rail_core_handle_server_sysparam(RAIL_SESSION* session, RAIL_SERVER_SYSPARAM* sysparam); + +void rail_core_handle_server_movesize(RAIL_SESSION* session, uint32 window_id, + uint16 move_size_started, uint16 move_size_type, uint16 pos_x, uint16 pos_y); + +void rail_core_handle_server_minmax_info(RAIL_SESSION* session, uint32 window_id, + uint16 max_width, uint16 max_height, uint16 max_pos_x, uint16 max_pos_y, + uint16 min_track_width, uint16 min_track_height, uint16 max_track_width, uint16 max_track_height); + +void rail_core_handle_server_langbar_info(RAIL_SESSION* session, uint32 langbar_status); +void rail_core_handle_server_get_app_resp(RAIL_SESSION* session, uint32 window_id, RAIL_UNICODE_STRING* app_id); + +#endif /* __RAIL_CORE_H */ diff --git a/channels/rail/rail_main.c b/channels/rail/rail_main.c new file mode 100644 index 000000000..8a6bdf12e --- /dev/null +++ b/channels/rail/rail_main.c @@ -0,0 +1,149 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RAIL Virtual Channel Plugin + * + * Copyright 2011 Marc-Andre Moreau + * Copyright 2011 Roman Barabanov + * Copyright 2011 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rail_core.h" +#include "rail_main.h" + +static void rail_plugin_process_connect(rdpSvcPlugin* plugin) +{ + railPlugin* rail_plugin = (railPlugin*)plugin; + + DEBUG_RAIL("rail_plugin_process_connect() called."); + rail_core_on_channel_connected(rail_plugin->session); +} + +static void rail_plugin_process_terminate(rdpSvcPlugin* plugin) +{ + DEBUG_RAIL("rail_plugin_process_terminate\n"); + xfree(plugin); +} + +static void rail_plugin_send_vchannel_data(void* rail_plugin_object, void* data, size_t length) +{ + STREAM* s = NULL; + railPlugin* plugin = (railPlugin*) rail_plugin_object; + + s = stream_new(length); + stream_write(s, data, length); + + svc_plugin_send((rdpSvcPlugin*) plugin, s); +} + +static void rail_plugin_process_received_vchannel_data(rdpSvcPlugin* plugin, STREAM* data_in) +{ + railPlugin* rail_plugin = (railPlugin*) plugin; + + DEBUG_RAIL("rail_plugin_process_receive: size=%d", stream_get_size(data_in)); + + rail_vchannel_process_received_vchannel_data(rail_plugin->session, data_in); + stream_free(data_in); +} + +static void on_free_rail_vchannel_event(FRDP_EVENT* event) +{ + assert(event->event_type == FRDP_EVENT_TYPE_RAIL_VCHANNEL_2_UI); + + RAIL_VCHANNEL_EVENT* rail_event = (RAIL_VCHANNEL_EVENT*)event->user_data; + + if (rail_event->event_id == RAIL_VCHANNEL_EVENT_APP_RESPONSE_RECEIVED) + xfree((void*)rail_event->param.app_response_info.application_id); + + if (rail_event->event_id == RAIL_VCHANNEL_EVENT_EXEC_RESULT_RETURNED) + xfree((void*)rail_event->param.exec_result_info.exe_or_file); + + xfree(rail_event); +} + +static void rail_plugin_send_vchannel_event(void* rail_plugin_object, RAIL_VCHANNEL_EVENT* event) +{ + railPlugin* plugin = (railPlugin*) rail_plugin_object; + RAIL_VCHANNEL_EVENT* payload = NULL; + FRDP_EVENT* out_event = NULL; + + payload = xnew(RAIL_VCHANNEL_EVENT); + memset(payload, 0, sizeof(RAIL_VCHANNEL_EVENT)); + memcpy(payload, event, sizeof(RAIL_VCHANNEL_EVENT)); + + out_event = freerdp_event_new(FRDP_EVENT_TYPE_RAIL_VCHANNEL_2_UI, on_free_rail_vchannel_event, payload); + + svc_plugin_send_event((rdpSvcPlugin*) plugin, out_event); +} + +static void rail_plugin_process_event(rdpSvcPlugin* plugin, FRDP_EVENT* event) +{ + RAIL_UI_EVENT* rail_ui_event = NULL; + railPlugin* rail_plugin = NULL; + + DEBUG_RAIL("rail_plugin_process_event: event_type=%d\n", event->event_type); + + rail_plugin = (railPlugin*)plugin; + rail_ui_event = (RAIL_UI_EVENT*)event->user_data; + + if (event->event_type == FRDP_EVENT_TYPE_RAIL_UI_2_VCHANNEL) + rail_core_handle_ui_event(rail_plugin->session, rail_ui_event); + + freerdp_event_free(event); +} + +int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) +{ + railPlugin* rail; + + DEBUG_RAIL("RAIL plugin VirtualChannelEntry started."); + + rail = (railPlugin*) xzalloc(sizeof(railPlugin)); + + rail->plugin.channel_def.options = CHANNEL_OPTION_INITIALIZED | + CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP | + CHANNEL_OPTION_SHOW_PROTOCOL; + strcpy(rail->plugin.channel_def.name, "rail"); + + rail->plugin.connect_callback = rail_plugin_process_connect; + rail->plugin.terminate_callback = rail_plugin_process_terminate; + + rail->plugin.receive_callback = rail_plugin_process_received_vchannel_data; + rail->plugin.event_callback = rail_plugin_process_event; + + rail->rail_event_sender.event_sender_object = rail; + rail->rail_event_sender.send_rail_vchannel_event = rail_plugin_send_vchannel_event; + + rail->rail_data_sender.data_sender_object = rail; + rail->rail_data_sender.send_rail_vchannel_data = + rail_plugin_send_vchannel_data; + + rail->session = rail_core_session_new(&rail->rail_data_sender, &rail->rail_event_sender); + + svc_plugin_init((rdpSvcPlugin*) rail, pEntryPoints); + + DEBUG_RAIL("RAIL plugin VirtualChannelEntry finished."); + + return 1; +} + diff --git a/channels/rail/rail_main.h b/channels/rail/rail_main.h new file mode 100644 index 000000000..ce9f55ed8 --- /dev/null +++ b/channels/rail/rail_main.h @@ -0,0 +1,34 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * RAIL Virtual Channel Plugin + * + * Copyright 2011 Marc-Andre Moreau + * Copyright 2011 Roman Barabanov + * Copyright 2011 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RAIL_MAIN_H +#define __RAIL_MAIN_H + +struct rail_plugin +{ + rdpSvcPlugin plugin; + RAIL_VCHANNEL_DATA_SENDER rail_data_sender; + RAIL_VCHANNEL_EVENT_SENDER rail_event_sender; + RAIL_SESSION * session; +}; +typedef struct rail_plugin railPlugin; + +#endif /* __RAIL_MAIN_H */ diff --git a/channels/rdpdr/disk/disk_file.c b/channels/rdpdr/disk/disk_file.c index 486cf57ef..52b7bd839 100644 --- a/channels/rdpdr/disk/disk_file.c +++ b/channels/rdpdr/disk/disk_file.c @@ -407,7 +407,8 @@ boolean disk_file_set_information(DISK_FILE* file, uint32 FsInformationClass, ui case FileAllocationInformation: /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ stream_read_uint64(input, size); - ftruncate(file->fd, size); + if (ftruncate(file->fd, size) != 0) + return False; break; case FileDispositionInformation: diff --git a/include/freerdp/constants.h b/include/freerdp/constants.h index 05abbf565..49a8be494 100644 --- a/include/freerdp/constants.h +++ b/include/freerdp/constants.h @@ -81,7 +81,10 @@ enum FRDP_EVENT_TYPE FRDP_EVENT_TYPE_CB_SYNC = 3, FRDP_EVENT_TYPE_CB_FORMAT_LIST = 4, FRDP_EVENT_TYPE_CB_DATA_REQUEST = 5, - FRDP_EVENT_TYPE_CB_DATA_RESPONSE = 6 + FRDP_EVENT_TYPE_CB_DATA_RESPONSE = 6, + + FRDP_EVENT_TYPE_RAIL_UI_2_VCHANNEL = 100, + FRDP_EVENT_TYPE_RAIL_VCHANNEL_2_UI = 101 }; /** diff --git a/include/freerdp/rail.h b/include/freerdp/rail.h new file mode 100644 index 000000000..10304ea63 --- /dev/null +++ b/include/freerdp/rail.h @@ -0,0 +1,341 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Remote Applications Integrated Locally (RAIL) + * + * Copyright 2011 Marc-Andre Moreau + * Copyright 2011 Roman Barabanov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RAIL_H +#define __RAIL_H + +/* RAIL Constants*/ + +enum RDP_RAIL_PDU_TYPE +{ + RDP_RAIL_ORDER_EXEC = 0x0001, + RDP_RAIL_ORDER_ACTIVATE = 0x0002, + RDP_RAIL_ORDER_SYSPARAM = 0x0003, + RDP_RAIL_ORDER_SYSCOMMAND = 0x0004, + RDP_RAIL_ORDER_HANDSHAKE = 0x0005, + RDP_RAIL_ORDER_NOTIFY_EVENT = 0x0006, + RDP_RAIL_ORDER_WINDOWMOVE = 0x0008, + RDP_RAIL_ORDER_LOCALMOVESIZE = 0x0009, + RDP_RAIL_ORDER_MINMAXINFO = 0x000A, + RDP_RAIL_ORDER_CLIENTSTATUS = 0x000B, + RDP_RAIL_ORDER_SYSMENU = 0x000C, + RDP_RAIL_ORDER_LANGBARINFO = 0x000D, + RDP_RAIL_ORDER_EXEC_RESULT = 0x0080, + RDP_RAIL_ORDER_GET_APPID_REQ = 0x000E, + RDP_RAIL_ORDER_GET_APPID_RESP = 0x000F +}; + +/* RAIL PDU flags */ +#define RAIL_EXEC_FLAG_EXPAND_WORKINGDIRECTORY 0x0001 +#define RAIL_EXEC_FLAG_TRANSLATE_FILES 0x0002 +#define RAIL_EXEC_FLAG_FILE 0x0004 +#define RAIL_EXEC_FLAG_EXPAND_ARGUMENTS 0x0008 + +/* Notification Icon Balloon Tooltip */ +#define NIIF_NONE 0x00000000 +#define NIIF_INFO 0x00000001 +#define NIIF_WARNING 0x00000002 +#define NIIF_ERROR 0x00000003 +#define NIIF_NOSOUND 0x00000010 +#define NIIF_LARGE_ICON 0x00000020 + +/* Client Execute PDU Flags */ +#define RAIL_EXEC_FLAG_EXPAND_WORKING_DIRECTORY 0x0001 +#define RAIL_EXEC_FLAG_TRANSLATE_FILES 0x0002 +#define RAIL_EXEC_FLAG_FILE 0x0004 +#define RAIL_EXEC_FLAG_EXPAND_ARGUMENTS 0x0008 + +/* Server Execute Result PDU */ +#define RAIL_EXEC_S_OK 0x0000 +#define RAIL_EXEC_E_HOOK_NOT_LOADED 0x0001 +#define RAIL_EXEC_E_DECODE_FAILED 0x0002 +#define RAIL_EXEC_E_NOT_IN_ALLOWLIST 0x0003 +#define RAIL_EXEC_E_FILE_NOT_FOUND 0x0005 +#define RAIL_EXEC_E_FAIL 0x0006 +#define RAIL_EXEC_E_SESSION_LOCKED 0x0007 + +/* Client System Parameters Update PDU */ +#define SPI_SETDRAGFULLWINDOWS 0x00000025 +#define SPI_SETKEYBOARDCUES 0x0000100B +#define SPI_SETKEYBOARDPREF 0x00000045 +#define SPI_SETWORKAREA 0x0000002F +#define SPI_SETMOUSEBUTTONSWAP 0x00000021 +#define SPI_SETHIGHCONTRAST 0x00000043 +#define RAIL_SPI_DISPLAYCHANGE 0x0000F001 +#define RAIL_SPI_TASKBARPOS 0x0000F000 + +/* Server System Parameters Update PDU */ +#define SPI_SETSCREENSAVEACTIVE 0x00000011 +#define SPI_SETSCREENSAVESECURE 0x00000077 + +/* Client System Command PDU */ +#define SC_SIZE 0xF000 +#define SC_MOVE 0xF010 +#define SC_MINIMIZE 0xF020 +#define SC_MAXIMIZE 0xF030 +#define SC_CLOSE 0xF060 +#define SC_KEYMENU 0xF100 +#define SC_RESTORE 0xF120 +#define SC_DEFAULT 0xF160 + +/* Client Notify Event PDU */ +#define WM_LBUTTONDOWN 0x00000201 +#define WM_LBUTTONUP 0x00000202 +#define WM_RBUTTONDOWN 0x00000204 +#define WM_RBUTTONUP 0x00000205 +#define WM_CONTEXTMENU 0x0000007B +#define WM_LBUTTONDBLCLK 0x00000203 +#define WM_RBUTTONDBLCLK 0x00000206 + +#define NIN_SELECT 0x00000400 +#define NIN_KEYSELECT 0x00000401 +#define NIN_BALLOONSHOW 0x00000402 +#define NIN_BALLOONHIDE 0x00000403 +#define NIN_BALLOONTIMEOUT 0x00000404 +#define NIN_BALLOONUSERCLICK 0x00000405 + +/* Client Information PDU */ +#define RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE 0x00000001 +#define RAIL_CLIENTSTATUS_AUTORECONNECT 0x00000002 + +/*HIGHCONTRAST flags values */ +#define HCF_AVAILABLE 0x00000002 +#define HCF_CONFIRMHOTKEY 0x00000008 +#define HCF_HIGHCONTRASTON 0x00000001 +#define HCF_HOTKEYACTIVE 0x00000004 +#define HCF_HOTKEYAVAILABLE 0x00000040 +#define HCF_HOTKEYSOUND 0x00000010 +#define HCF_INDICATOR 0x00000020 + +/* Server Move/Size Start PDU */ +#define RAIL_WMSZ_LEFT 0x0001 +#define RAIL_WMSZ_RIGHT 0x0002 +#define RAIL_WMSZ_TOP 0x0003 +#define RAIL_WMSZ_TOPLEFT 0x0004 +#define RAIL_WMSZ_TOPRIGHT 0x0005 +#define RAIL_WMSZ_BOTTOM 0x0006 +#define RAIL_WMSZ_BOTTOMLEFT 0x0007 +#define RAIL_WMSZ_BOTTOMRIGHT 0x0008 +#define RAIL_WMSZ_MOVE 0x0009 +#define RAIL_WMSZ_KEYMOVE 0x000A +#define RAIL_WMSZ_KEYSIZE 0x000B + +/* Language Bar Information PDU */ +#define TF_SFT_SHOWNORMAL 0x00000001 +#define TF_SFT_DOCK 0x00000002 +#define TF_SFT_MINIMIZED 0x00000004 +#define TF_SFT_HIDDEN 0x00000008 +#define TF_SFT_NOTRANSPARENCY 0x00000010 +#define TF_SFT_LOWTRANSPARENCY 0x00000020 +#define TF_SFT_HIGHTRANSPARENCY 0x00000040 +#define TF_SFT_LABELS 0x00000080 +#define TF_SFT_NOLABELS 0x00000100 +#define TF_SFT_EXTRAICONSONMINIMIZED 0x00000200 +#define TF_SFT_NOEXTRAICONSONMINIMIZED 0x00000400 +#define TF_SFT_DESKBAND 0x00000800 + +/* RAIL Common structures */ + +typedef struct _RAIL_RECT_16 +{ + uint16 left; + uint16 top; + uint16 right; + uint16 bottom; +} +RAIL_RECT_16; + +typedef struct _RAIL_UNICODE_STRING +{ + uint16 length; + uint8 *buffer; +} +RAIL_UNICODE_STRING; + +// Events from 'rail' vchannel plugin to UI +enum RAIL_VCHANNEL_EVENT +{ + RAIL_VCHANNEL_EVENT_SESSION_ESTABLISHED = 1, + RAIL_VCHANNEL_EVENT_EXEC_RESULT_RETURNED, + RAIL_VCHANNEL_EVENT_SERVER_SYSPARAM_RECEIVED, + RAIL_VCHANNEL_EVENT_MOVESIZE_STARTED, + RAIL_VCHANNEL_EVENT_MOVESIZE_FINISHED, + RAIL_VCHANNEL_EVENT_MINMAX_INFO_UPDATED, + RAIL_VCHANNEL_EVENT_LANGBAR_STATUS_UPDATED, + RAIL_VCHANNEL_EVENT_APP_RESPONSE_RECEIVED +}; + +typedef struct _RAIL_VCHANNEL_EVENT +{ + uint32 event_id; + + union + { + struct + { + uint16 flags; + uint16 exec_result; + uint32 raw_result; + const char* exe_or_file; + } exec_result_info; + + struct + { + uint32 param_type; + boolean screen_saver_enabled; + boolean screen_saver_lock_enabled; + } server_param_info; + + struct + { + uint32 window_id; + uint16 move_size_type; + uint16 pos_x; + uint16 pos_y; + } movesize_info; + + struct + { + uint32 window_id; + uint16 max_width; + uint16 max_height; + uint16 max_pos_x; + uint16 max_pos_y; + uint16 min_track_width; + uint16 min_track_height; + uint16 max_track_width; + uint16 max_track_height; + } minmax_info; + + struct + { + uint32 status; + } langbar_info; + + struct + { + uint32 window_id; + const char* application_id; + + } app_response_info; + + }param; +} +RAIL_VCHANNEL_EVENT; + +// Events from UI to 'rail' vchannel plugin +enum RAIL_UI_EVENT +{ + RAIL_UI_EVENT_UPDATE_CLIENT_SYSPARAM = 1, + RAIL_UI_EVENT_EXECUTE_REMOTE_APP, + RAIL_UI_EVENT_ACTIVATE, + RAIL_UI_EVENT_SYS_COMMAND, + RAIL_UI_EVENT_NOTIFY, + RAIL_UI_EVENT_WINDOW_MOVE, + RAIL_UI_EVENT_SYSTEM_MENU, + RAIL_UI_EVENT_LANGBAR_INFO, + RAIL_UI_EVENT_GET_APP_ID +}; + +typedef struct _RAIL_UI_EVENT +{ + uint32 event_id; + + union + { + struct + { + uint32 param; + + union + { + boolean full_window_drag_enabled; + boolean menu_access_key_always_underlined; + boolean keyboard_for_user_prefered; + boolean left_right_mouse_buttons_swapped; + RAIL_RECT_16 work_area; + RAIL_RECT_16 display_resolution; + RAIL_RECT_16 taskbar_size; + struct + { + uint32 flags; + const char* color_scheme; + } high_contrast_system_info; + } value; + + } sysparam_info; + + struct + { + boolean exec_or_file_is_file_path; + const char* exe_or_file; + const char* working_directory; + const char* arguments; + } execute_info; + + struct + { + uint32 window_id; + boolean enabled; + } activate_info; + + struct + { + uint32 window_id; + uint32 syscommand; + } syscommand_info; + + struct + { + uint32 window_id; + uint32 notify_icon_id; + uint32 message; + } notify_info; + + struct + { + uint32 window_id; + RAIL_RECT_16 new_position; + } window_move_info; + + struct + { + uint32 window_id; + uint16 left; + uint16 top; + } system_menu_info; + + struct + { + uint32 status; + } langbar_info; + + + struct + { + uint32 window_id; + } get_app_id_info; + } param; +} +RAIL_UI_EVENT; + + +#endif /* __RAIL_H */ + diff --git a/libfreerdp-utils/event.c b/libfreerdp-utils/event.c index 86d994bd2..d0cac34e4 100644 --- a/libfreerdp-utils/event.c +++ b/libfreerdp-utils/event.c @@ -50,6 +50,10 @@ FRDP_EVENT* freerdp_event_new(uint32 event_type, FRDP_EVENT_CALLBACK on_event_fr case FRDP_EVENT_TYPE_CB_DATA_RESPONSE: event = (FRDP_EVENT*)xnew(FRDP_CB_DATA_RESPONSE_EVENT); break; + case FRDP_EVENT_TYPE_RAIL_UI_2_VCHANNEL: + case FRDP_EVENT_TYPE_RAIL_VCHANNEL_2_UI: + event = xnew(FRDP_EVENT); + break; } if (event != NULL) {