diff --git a/channels/CMakeLists.txt b/channels/CMakeLists.txt index 47496e308..cac9ecb25 100644 --- a/channels/CMakeLists.txt +++ b/channels/CMakeLists.txt @@ -17,5 +17,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +add_subdirectory(cliprdr) add_subdirectory(rdpdbg) diff --git a/channels/cliprdr/CMakeLists.txt b/channels/cliprdr/CMakeLists.txt new file mode 100644 index 000000000..d74f823f2 --- /dev/null +++ b/channels/cliprdr/CMakeLists.txt @@ -0,0 +1,33 @@ +# 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(CLIPRDR_SRCS + cliprdr_constants.h + cliprdr_format.c + cliprdr_format.h + cliprdr_main.c + cliprdr_main.h +) + +add_library(cliprdr SHARED ${CLIPRDR_SRCS}) +set_target_properties(cliprdr PROPERTIES PREFIX "") + +target_link_libraries(cliprdr freerdp-utils) + +install(TARGETS cliprdr DESTINATION ${FREERDP_PLUGIN_PATH}) diff --git a/channels/cliprdr/cliprdr_constants.h b/channels/cliprdr/cliprdr_constants.h new file mode 100644 index 000000000..d18864d00 --- /dev/null +++ b/channels/cliprdr/cliprdr_constants.h @@ -0,0 +1,58 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Clipboard Virtual Channel + * + * Copyright 2009-2011 Jay Sorg + * Copyright 2010-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 __CLIPRDR_CONSTANTS +#define __CLIPRDR_CONSTANTS + +/* CLIPRDR_HEADER.msgType */ +#define CB_MONITOR_READY 1 +#define CB_FORMAT_LIST 2 +#define CB_FORMAT_LIST_RESPONSE 3 +#define CB_FORMAT_DATA_REQUEST 4 +#define CB_FORMAT_DATA_RESPONSE 5 +#define CB_TEMP_DIRECTORY 6 +#define CB_CLIP_CAPS 7 +#define CB_FILECONTENTS_REQUEST 8 +#define CB_FILECONTENTS_RESPONSE 9 +#define CB_LOCK_CLIPDATA 10 +#define CB_UNLOCK_CLIPDATA 11 + +/* CLIPRDR_HEADER.msgFlags */ +#define CB_RESPONSE_OK 1 +#define CB_RESPONSE_FAIL 2 +#define CB_ASCII_NAMES 4 + +/* CLIPRDR_CAPS_SET.capabilitySetType */ +#define CB_CAPSTYPE_GENERAL 1 + +/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */ +#define CB_CAPSTYPE_GENERAL_LEN 12 + +/* CLIPRDR_GENERAL_CAPABILITY.version */ +#define CB_CAPS_VERSION_1 1 +#define CB_CAPS_VERSION_2 2 + +/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */ +#define CB_USE_LONG_FORMAT_NAMES 2 +#define CB_STREAM_FILECLIP_ENABLED 4 +#define CB_FILECLIP_NO_FILE_PATHS 8 +#define CB_CAN_LOCK_CLIPDATA 16 + +#endif /* __CLIPRDR_CONSTANTS */ diff --git a/channels/cliprdr/cliprdr_format.c b/channels/cliprdr/cliprdr_format.c new file mode 100644 index 000000000..6cce75823 --- /dev/null +++ b/channels/cliprdr/cliprdr_format.c @@ -0,0 +1,172 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Clipboard Virtual Channel + * + * Copyright 2009-2011 Jay Sorg + * Copyright 2010-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 "cliprdr_constants.h" +#include "cliprdr_main.h" +#include "cliprdr_format.h" + +#define CFSTR_HTML "H\0T\0M\0L\0 \0F\0o\0r\0m\0a\0t\0\0" +#define CFSTR_PNG "P\0N\0G\0\0" +#define CFSTR_JPEG "J\0F\0I\0F\0\0" +#define CFSTR_GIF "G\0I\0F\0\0" + +void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, FRDP_CB_FORMAT_LIST_EVENT* cb_event) +{ + STREAM* data_out; + int i; + + data_out = cliprdr_packet_new(CB_FORMAT_LIST, 0, 36 * cb_event->num_formats); + + for (i = 0; i < cb_event->num_formats; i++) + { + stream_write_uint32(data_out, cb_event->formats[i]); + switch (cb_event->formats[i]) + { + case CB_FORMAT_HTML: + memcpy(stream_get_tail(data_out), CFSTR_HTML, sizeof(CFSTR_HTML)); + break; + case CB_FORMAT_PNG: + memcpy(stream_get_tail(data_out), CFSTR_PNG, sizeof(CFSTR_PNG)); + break; + case CB_FORMAT_JPEG: + memcpy(stream_get_tail(data_out), CFSTR_JPEG, sizeof(CFSTR_JPEG)); + break; + case CB_FORMAT_GIF: + memcpy(stream_get_tail(data_out), CFSTR_GIF, sizeof(CFSTR_GIF)); + break; + } + stream_seek(data_out, 32); + } + + cliprdr_packet_send(cliprdr, data_out); +} + +static void cliprdr_send_format_list_response(cliprdrPlugin* cliprdr) +{ + STREAM* data_out; + + data_out = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, CB_RESPONSE_OK, 0); + cliprdr_packet_send(cliprdr, data_out); +} + +void cliprdr_process_format_list(cliprdrPlugin* cliprdr, STREAM* data_in, uint32 dataLen) +{ + FRDP_CB_FORMAT_LIST_EVENT* cb_event; + uint32 format; + int num_formats; + int supported; + int i; + + cb_event = (FRDP_CB_FORMAT_LIST_EVENT*)freerdp_event_new(FRDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL); + num_formats = dataLen / 36; + cb_event->formats = (uint32*)xmalloc(sizeof(uint32) * num_formats); + cb_event->num_formats = 0; + if (num_formats * 36 != dataLen) + DEBUG_WARN("dataLen %d not devided by 36!"); + for (i = 0; i < num_formats; i++) + { + stream_read_uint32(data_in, format); + supported = 1; + switch (format) + { + case CB_FORMAT_TEXT: + case CB_FORMAT_DIB: + case CB_FORMAT_UNICODETEXT: + break; + + default: + if (memcmp(stream_get_tail(data_in), CFSTR_HTML, sizeof(CFSTR_HTML)) == 0) + { + format = CB_FORMAT_HTML; + break; + } + if (memcmp(stream_get_tail(data_in), CFSTR_PNG, sizeof(CFSTR_PNG)) == 0) + { + format = CB_FORMAT_PNG; + break; + } + if (memcmp(stream_get_tail(data_in), CFSTR_JPEG, sizeof(CFSTR_JPEG)) == 0) + { + format = CB_FORMAT_JPEG; + break; + } + if (memcmp(stream_get_tail(data_in), CFSTR_GIF, sizeof(CFSTR_GIF)) == 0) + { + format = CB_FORMAT_GIF; + break; + } + supported = 0; + break; + } + stream_seek(data_in, 32); + + if (supported) + cb_event->formats[cb_event->num_formats++] = format; + } + + svc_plugin_send_event((rdpSvcPlugin*)cliprdr, (FRDP_EVENT*)cb_event); + cliprdr_send_format_list_response(cliprdr); +} + +void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, STREAM* data_in) +{ + FRDP_CB_DATA_REQUEST_EVENT* cb_event; + + cb_event = (FRDP_CB_DATA_REQUEST_EVENT*)freerdp_event_new(FRDP_EVENT_TYPE_CB_DATA_REQUEST, NULL, NULL); + stream_read_uint32(data_in, cb_event->format); + svc_plugin_send_event((rdpSvcPlugin*)cliprdr, (FRDP_EVENT*)cb_event); +} + +void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, FRDP_CB_DATA_RESPONSE_EVENT* cb_event) +{ + STREAM* data_out; + + data_out = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, cb_event->size); + stream_write(data_out, cb_event->data, cb_event->size); + cliprdr_packet_send(cliprdr, data_out); +} + +void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, FRDP_CB_DATA_REQUEST_EVENT* cb_event) +{ + STREAM* data_out; + + data_out = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4); + stream_write_uint32(data_out, cb_event->format); + cliprdr_packet_send(cliprdr, data_out); +} + +void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, STREAM* data_in, uint32 dataLen) +{ + FRDP_CB_DATA_RESPONSE_EVENT* cb_event; + + cb_event = (FRDP_CB_DATA_RESPONSE_EVENT*)freerdp_event_new(FRDP_EVENT_TYPE_CB_DATA_RESPONSE, NULL, NULL); + cb_event->size = dataLen; + cb_event->data = (uint8*)xmalloc(dataLen); + memcpy(cb_event->data, stream_get_tail(data_in), dataLen); + svc_plugin_send_event((rdpSvcPlugin*)cliprdr, (FRDP_EVENT*)cb_event); +} diff --git a/channels/cliprdr/cliprdr_format.h b/channels/cliprdr/cliprdr_format.h new file mode 100644 index 000000000..59ccbc142 --- /dev/null +++ b/channels/cliprdr/cliprdr_format.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Clipboard Virtual Channel + * + * Copyright 2009-2011 Jay Sorg + * Copyright 2010-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 __CLIPRDR_FORMAT_H +#define __CLIPRDR_FORMAT_H + +void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, FRDP_CB_FORMAT_LIST_EVENT* cb_event); +void cliprdr_process_format_list(cliprdrPlugin* cliprdr, STREAM* data_in, uint32 dataLen); + +void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, STREAM* data_in); +void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, FRDP_CB_DATA_RESPONSE_EVENT* cb_event); + +void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, FRDP_CB_DATA_REQUEST_EVENT* cb_event); +void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, STREAM* data_in, uint32 dataLen); + +#endif /* __CLIPRDR_FORMAT_H */ diff --git a/channels/cliprdr/cliprdr_main.c b/channels/cliprdr/cliprdr_main.c new file mode 100644 index 000000000..090b5302e --- /dev/null +++ b/channels/cliprdr/cliprdr_main.c @@ -0,0 +1,193 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Clipboard Virtual Channel + * + * Copyright 2009-2011 Jay Sorg + * Copyright 2010-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 "cliprdr_constants.h" +#include "cliprdr_main.h" +#include "cliprdr_format.h" + +STREAM* cliprdr_packet_new(uint16 msgType, uint16 msgFlags, uint32 dataLen) +{ + STREAM* data_out; + + data_out = stream_new(dataLen + 8); + stream_write_uint16(data_out, msgType); + stream_write_uint16(data_out, msgFlags); + /* Write actual length after the entire packet has been constructed. */ + stream_seek(data_out, 4); + + return data_out; +} + +void cliprdr_packet_send(cliprdrPlugin* cliprdr, STREAM* data_out) +{ + int pos; + uint32 dataLen; + + pos = stream_get_pos(data_out); + dataLen = pos - 8; + stream_set_pos(data_out, 4); + stream_write_uint32(data_out, dataLen); + stream_set_pos(data_out, pos); + + svc_plugin_send((rdpSvcPlugin*)cliprdr, data_out); +} + +static void cliprdr_process_connect(rdpSvcPlugin* plugin) +{ + DEBUG_SVC("connecting"); +} + +static void cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, STREAM* data_in) +{ + uint16 cCapabilitiesSets; + + stream_read_uint16(data_in, cCapabilitiesSets); + DEBUG_SVC("cCapabilitiesSets %d", cCapabilitiesSets); +} + +static void cliprdr_send_clip_caps(cliprdrPlugin* cliprdr) +{ + STREAM* data_out; + + data_out = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN); + + stream_write_uint16(data_out, 1); /* cCapabilitiesSets */ + stream_write_uint16(data_out, 0); /* pad1 */ + stream_write_uint16(data_out, CB_CAPSTYPE_GENERAL); /* capabilitySetType */ + stream_write_uint16(data_out, CB_CAPSTYPE_GENERAL_LEN); /* lengthCapability */ + stream_write_uint32(data_out, CB_CAPS_VERSION_2); /* version */ + stream_write_uint32(data_out, 0); /* generalFlags */ + + cliprdr_packet_send(cliprdr, data_out); +} + +static void cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr) +{ + FRDP_EVENT* event; + + cliprdr_send_clip_caps(cliprdr); + + event = freerdp_event_new(FRDP_EVENT_TYPE_CB_SYNC, NULL, NULL); + svc_plugin_send_event((rdpSvcPlugin*)cliprdr, event); +} + +static void cliprdr_process_receive(rdpSvcPlugin* plugin, STREAM* data_in) +{ + cliprdrPlugin* cliprdr = (cliprdrPlugin*)plugin; + uint16 msgType; + uint16 msgFlags; + uint32 dataLen; + + stream_read_uint16(data_in, msgType); + stream_read_uint16(data_in, msgFlags); + stream_read_uint32(data_in, dataLen); + + DEBUG_SVC("msgType %d msgFlags %d dataLen %d", msgType, msgFlags, dataLen); + + switch (msgType) + { + case CB_CLIP_CAPS: + cliprdr_process_clip_caps(cliprdr, data_in); + break; + + case CB_MONITOR_READY: + cliprdr_process_monitor_ready(cliprdr); + break; + + case CB_FORMAT_LIST: + cliprdr_process_format_list(cliprdr, data_in, dataLen); + break; + + case CB_FORMAT_LIST_RESPONSE: + break; + + case CB_FORMAT_DATA_REQUEST: + cliprdr_process_format_data_request(cliprdr, data_in); + break; + + case CB_FORMAT_DATA_RESPONSE: + cliprdr_process_format_data_response(cliprdr, data_in, dataLen); + break; + + default: + DEBUG_WARN("unknown msgType %d", msgType); + break; + } + + stream_free(data_in); +} + +static void cliprdr_process_event(rdpSvcPlugin* plugin, FRDP_EVENT* event) +{ + switch (event->event_type) + { + case FRDP_EVENT_TYPE_CB_FORMAT_LIST: + cliprdr_process_format_list_event((cliprdrPlugin*)plugin, (FRDP_CB_FORMAT_LIST_EVENT*)event); + break; + + case FRDP_EVENT_TYPE_CB_DATA_REQUEST: + cliprdr_process_format_data_request_event((cliprdrPlugin*)plugin, (FRDP_CB_DATA_REQUEST_EVENT*)event); + break; + + case FRDP_EVENT_TYPE_CB_DATA_RESPONSE: + cliprdr_process_format_data_response_event((cliprdrPlugin*)plugin, (FRDP_CB_DATA_RESPONSE_EVENT*)event); + break; + + default: + DEBUG_WARN("unknown event type %d", event->event_type); + break; + } + freerdp_event_free(event); +} + +static void cliprdr_process_terminate(rdpSvcPlugin* plugin) +{ + xfree(plugin); +} + +int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints) +{ + cliprdrPlugin* cliprdr; + + cliprdr = (cliprdrPlugin*)xmalloc(sizeof(cliprdrPlugin)); + memset(cliprdr, 0, sizeof(cliprdrPlugin)); + + cliprdr->plugin.channel_def.options = CHANNEL_OPTION_INITIALIZED | + CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP | + CHANNEL_OPTION_SHOW_PROTOCOL; + strcpy(cliprdr->plugin.channel_def.name, "cliprdr"); + + cliprdr->plugin.connect_callback = cliprdr_process_connect; + cliprdr->plugin.receive_callback = cliprdr_process_receive; + cliprdr->plugin.event_callback = cliprdr_process_event; + cliprdr->plugin.terminate_callback = cliprdr_process_terminate; + + svc_plugin_init((rdpSvcPlugin*)cliprdr, pEntryPoints); + + return 1; +} diff --git a/channels/cliprdr/cliprdr_main.h b/channels/cliprdr/cliprdr_main.h new file mode 100644 index 000000000..1859dc122 --- /dev/null +++ b/channels/cliprdr/cliprdr_main.h @@ -0,0 +1,35 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Clipboard Virtual Channel + * + * Copyright 2009-2011 Jay Sorg + * Copyright 2010-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 __CLIPRDR_MAIN_H +#define __CLIPRDR_MAIN_H + +#include + +typedef struct cliprdr_plugin cliprdrPlugin; +struct cliprdr_plugin +{ + rdpSvcPlugin plugin; +}; + +STREAM* cliprdr_packet_new(uint16 msgType, uint16 msgFlags, uint32 dataLen); +void cliprdr_packet_send(cliprdrPlugin* cliprdr, STREAM* data_out); + +#endif /* __CLIPRDR_MAIN_H */ diff --git a/channels/rdpdbg/rdpdbg_main.c b/channels/rdpdbg/rdpdbg_main.c index e4eda5a43..e1067417b 100644 --- a/channels/rdpdbg/rdpdbg_main.c +++ b/channels/rdpdbg/rdpdbg_main.c @@ -34,14 +34,14 @@ struct rdpdbg_plugin static void rdpdbg_process_connect(rdpSvcPlugin* plugin) { - printf("rdpdbg_process_connect\n"); + DEBUG_WARN("connecting"); } static void rdpdbg_process_receive(rdpSvcPlugin* plugin, STREAM* data_in) { STREAM* data_out; - printf("rdpdbg_process_receive: size %d\n", stream_get_size(data_in)); + DEBUG_WARN("size %d", stream_get_size(data_in)); stream_free(data_in); data_out = stream_new(8); @@ -51,7 +51,7 @@ static void rdpdbg_process_receive(rdpSvcPlugin* plugin, STREAM* data_in) static void rdpdbg_process_event(rdpSvcPlugin* plugin, FRDP_EVENT* event) { - printf("rdpdbg_process_event: event_type %d\n", event->event_type); + DEBUG_WARN("event_type %d", event->event_type); freerdp_event_free(event); event = freerdp_event_new(FRDP_EVENT_TYPE_DEBUG, NULL, NULL); @@ -60,7 +60,7 @@ static void rdpdbg_process_event(rdpSvcPlugin* plugin, FRDP_EVENT* event) static void rdpdbg_process_terminate(rdpSvcPlugin* plugin) { - printf("rdpdbg_process_terminate\n"); + DEBUG_WARN("terminating"); xfree(plugin); } diff --git a/config.h.in b/config.h.in index 1d70425fa..7baeabd19 100644 --- a/config.h.in +++ b/config.h.in @@ -1,3 +1,6 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + /* Include files */ #cmakedefine HAVE_SYS_PARAM_H #cmakedefine HAVE_SYS_SOCKET_H @@ -12,3 +15,5 @@ #cmakedefine WITH_DEBUG_TRANSPORT #cmakedefine WITH_DEBUG_CHANMAN #cmakedefine WITH_DEBUG_SVC + +#endif diff --git a/cunit/CMakeLists.txt b/cunit/CMakeLists.txt index 4ff394cfc..4b0f84f94 100644 --- a/cunit/CMakeLists.txt +++ b/cunit/CMakeLists.txt @@ -47,6 +47,8 @@ add_executable(test_freerdp test_transport.h test_chanman.c test_chanman.h + test_cliprdr.c + test_cliprdr.h test_freerdp.c test_freerdp.h) diff --git a/cunit/test_chanman.c b/cunit/test_chanman.c index 3781f47dc..7ad61c034 100644 --- a/cunit/test_chanman.c +++ b/cunit/test_chanman.c @@ -71,16 +71,20 @@ void test_chanman(void) freerdp_chanman_post_connect(chan_man, &inst); freerdp_chanman_data(&inst, 0, "testdata", 8, CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, 8); - - freerdp_chanman_check_fds(chan_man, &inst); + freerdp_chanman_data(&inst, 0, "testdata1", 9, CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, 9); + freerdp_chanman_data(&inst, 0, "testdata11", 10, CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, 10); + freerdp_chanman_data(&inst, 0, "testdata111", 11, CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, 11); event = freerdp_event_new(FRDP_EVENT_TYPE_DEBUG, NULL, NULL); freerdp_chanman_send_event(chan_man, "rdpdbg", event); - event = freerdp_chanman_pop_event(chan_man); + while ((event = freerdp_chanman_pop_event(chan_man)) == NULL) + { + freerdp_chanman_check_fds(chan_man, &inst); + } printf("responded event_type %d\n", event->event_type); freerdp_event_free(event); - freerdp_chanman_close(chan_man, NULL); + freerdp_chanman_close(chan_man, &inst); freerdp_chanman_free(chan_man); } diff --git a/cunit/test_cliprdr.c b/cunit/test_cliprdr.c new file mode 100644 index 000000000..52791d88b --- /dev/null +++ b/cunit/test_cliprdr.c @@ -0,0 +1,246 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Clipboard Virtual Channel Unit Tests + * + * 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 + +#include "test_cliprdr.h" + +int init_cliprdr_suite(void) +{ + freerdp_chanman_global_init(); + return 0; +} + +int clean_cliprdr_suite(void) +{ + freerdp_chanman_global_uninit(); + return 0; +} + +int add_cliprdr_suite(void) +{ + add_test_suite(cliprdr); + + add_test_function(cliprdr); + + return 0; +} + +static const uint8 test_clip_caps_data[] = +{ + "\x07\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x01\x00\x0C\x00" + "\x02\x00\x00\x00\x0E\x00\x00\x00" +}; + +static const uint8 test_monitor_ready_data[] = +{ + "\x01\x00\x00\x00\x00\x00\x00\x00" +}; + +static const uint8 test_format_list_data[] = +{ + "\x02\x00\x00\x00\x48\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\xd0\x00\x00" + "\x48\x00\x54\x00\x4D\x00\x4C\x00\x20\x00\x46\x00\x6F\x00\x72\x00" + "\x6D\x00\x61\x00\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +static const uint8 test_format_list_response_data[] = +{ + "\x03\x00\x01\x00\x00\x00\x00\x00" +}; + +static const uint8 test_data_request_data[] = +{ + "\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00" +}; + +static const uint8 test_data_response_data[] = +{ + "\x05\x00\x01\x00\x18\x00\x00\x00\x68\x00\x65\x00\x6C\x00\x6C\x00" + "\x6F\x00\x20\x00\x77\x00\x6F\x00\x72\x00\x6c\x00\x64\x00\x00\x00" +}; + +static int test_rdp_channel_data(rdpInst* inst, int chan_id, char* data, int data_size) +{ + printf("chan_id %d data_size %d\n", chan_id, data_size); + freerdp_hexdump(data, data_size); +} + +static int event_processed; + +static void event_process_callback(FRDP_EVENT* event) +{ + printf("Event %d processed.\n", event->event_type); + event_processed = 1; +} + +void test_cliprdr(void) +{ + rdpChanMan* chan_man; + rdpSettings settings = { 0 }; + rdpInst inst = { 0 }; + FRDP_EVENT* event; + FRDP_CB_FORMAT_LIST_EVENT* format_list_event; + FRDP_CB_DATA_REQUEST_EVENT* data_request_event; + FRDP_CB_DATA_RESPONSE_EVENT* data_response_event; + int i; + + settings.hostname = "testhost"; + inst.settings = &settings; + inst.rdp_channel_data = test_rdp_channel_data; + + chan_man = freerdp_chanman_new(); + + freerdp_chanman_load_plugin(chan_man, &settings, "../channels/cliprdr/cliprdr.so", NULL); + freerdp_chanman_pre_connect(chan_man, &inst); + freerdp_chanman_post_connect(chan_man, &inst); + + /* server sends cliprdr capabilities and monitor ready PDU */ + freerdp_chanman_data(&inst, 0, (char*)test_clip_caps_data, sizeof(test_clip_caps_data) - 1, + CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, sizeof(test_clip_caps_data) - 1); + + freerdp_chanman_data(&inst, 0, (char*)test_monitor_ready_data, sizeof(test_monitor_ready_data) - 1, + CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, sizeof(test_monitor_ready_data) - 1); + + /* cliprdr sends clipboard_sync event to UI */ + while ((event = freerdp_chanman_pop_event(chan_man)) == NULL) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + printf("Got event %d\n", event->event_type); + CU_ASSERT(event->event_type == FRDP_EVENT_TYPE_CB_SYNC); + freerdp_event_free(event); + + /* UI sends format_list event to cliprdr */ + event = freerdp_event_new(FRDP_EVENT_TYPE_CB_FORMAT_LIST, event_process_callback, NULL); + format_list_event = (FRDP_CB_FORMAT_LIST_EVENT*)event; + format_list_event->num_formats = 2; + format_list_event->formats = (uint32*)xmalloc(sizeof(uint32) * 2); + format_list_event->formats[0] = CB_FORMAT_TEXT; + format_list_event->formats[1] = CB_FORMAT_HTML; + event_processed = 0; + freerdp_chanman_send_event(chan_man, "cliprdr", event); + + /* cliprdr sends format list PDU to server */ + while (!event_processed) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + + /* server sends format list response PDU to cliprdr */ + freerdp_chanman_data(&inst, 0, (char*)test_format_list_response_data, sizeof(test_format_list_response_data) - 1, + CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, sizeof(test_format_list_response_data) - 1); + + /* server sends format list PDU to cliprdr */ + freerdp_chanman_data(&inst, 0, (char*)test_format_list_data, sizeof(test_format_list_data) - 1, + CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, sizeof(test_format_list_data) - 1); + + /* cliprdr sends format_list event to UI */ + while ((event = freerdp_chanman_pop_event(chan_man)) == NULL) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + printf("Got event %d\n", event->event_type); + CU_ASSERT(event->event_type == FRDP_EVENT_TYPE_CB_FORMAT_LIST); + if (event->event_type == FRDP_EVENT_TYPE_CB_FORMAT_LIST) + { + format_list_event = (FRDP_CB_FORMAT_LIST_EVENT*)event; + for (i = 0; i < format_list_event->num_formats; i++) + printf("Format: 0x%X\n", format_list_event->formats[i]); + } + freerdp_event_free(event); + + /* server sends data request PDU to cliprdr */ + freerdp_chanman_data(&inst, 0, (char*)test_data_request_data, sizeof(test_data_request_data) - 1, + CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, sizeof(test_data_request_data) - 1); + + /* cliprdr sends data request event to UI */ + while ((event = freerdp_chanman_pop_event(chan_man)) == NULL) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + printf("Got event %d\n", event->event_type); + CU_ASSERT(event->event_type == FRDP_EVENT_TYPE_CB_DATA_REQUEST); + if (event->event_type == FRDP_EVENT_TYPE_CB_DATA_REQUEST) + { + data_request_event = (FRDP_CB_DATA_REQUEST_EVENT*)event; + printf("Requested format: 0x%X\n", data_request_event->format); + } + freerdp_event_free(event); + + /* UI sends data response event to cliprdr */ + event = freerdp_event_new(FRDP_EVENT_TYPE_CB_DATA_RESPONSE, event_process_callback, NULL); + data_response_event = (FRDP_CB_DATA_RESPONSE_EVENT*)event; + data_response_event->data = (uint8*)xmalloc(6); + strcpy(data_response_event->data, "hello"); + data_response_event->size = 6; + event_processed = 0; + freerdp_chanman_send_event(chan_man, "cliprdr", event); + + /* cliprdr sends data response PDU to server */ + while (!event_processed) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + + /* UI sends data request event to cliprdr */ + event = freerdp_event_new(FRDP_EVENT_TYPE_CB_DATA_REQUEST, event_process_callback, NULL); + data_request_event = (FRDP_CB_DATA_REQUEST_EVENT*)event; + data_request_event->format = CB_FORMAT_UNICODETEXT; + event_processed = 0; + freerdp_chanman_send_event(chan_man, "cliprdr", event); + + /* cliprdr sends data request PDU to server */ + while (!event_processed) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + + /* server sends data response PDU to cliprdr */ + freerdp_chanman_data(&inst, 0, (char*)test_data_response_data, sizeof(test_data_response_data) - 1, + CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, sizeof(test_data_response_data) - 1); + + /* cliprdr sends data response event to UI */ + while ((event = freerdp_chanman_pop_event(chan_man)) == NULL) + { + freerdp_chanman_check_fds(chan_man, &inst); + } + printf("Got event %d\n", event->event_type); + CU_ASSERT(event->event_type == FRDP_EVENT_TYPE_CB_DATA_RESPONSE); + if (event->event_type == FRDP_EVENT_TYPE_CB_DATA_RESPONSE) + { + data_response_event = (FRDP_CB_DATA_RESPONSE_EVENT*)event; + printf("Data response size: %d\n", data_response_event->size); + freerdp_hexdump(data_response_event->data, data_response_event->size); + } + freerdp_event_free(event); + + freerdp_chanman_close(chan_man, &inst); + freerdp_chanman_free(chan_man); +} diff --git a/cunit/test_cliprdr.h b/cunit/test_cliprdr.h new file mode 100644 index 000000000..f1a5509ca --- /dev/null +++ b/cunit/test_cliprdr.h @@ -0,0 +1,26 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Clipboard Virtual Channel Unit Tests + * + * 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 "test_freerdp.h" + +int init_cliprdr_suite(void); +int clean_cliprdr_suite(void); +int add_cliprdr_suite(void); + +void test_cliprdr(void); diff --git a/cunit/test_freerdp.c b/cunit/test_freerdp.c index 6da5a8d7a..959f0e636 100644 --- a/cunit/test_freerdp.c +++ b/cunit/test_freerdp.c @@ -31,6 +31,7 @@ #include "test_utils.h" #include "test_transport.h" #include "test_chanman.h" +#include "test_cliprdr.h" #include "test_freerdp.h" void dump_data(unsigned char * p, int len, int width, char* name) @@ -123,6 +124,7 @@ int main(int argc, char* argv[]) add_utils_suite(); add_transport_suite(); add_chanman_suite(); + add_cliprdr_suite(); } else { @@ -160,6 +162,10 @@ int main(int argc, char* argv[]) { add_chanman_suite(); } + else if (strcmp("cliprdr", argv[*pindex]) == 0) + { + add_cliprdr_suite(); + } else if (strcmp("per", argv[*pindex]) == 0) { add_per_suite(); diff --git a/include/freerdp/constants.h b/include/freerdp/constants.h index e9c8ad6a9..c970dc0a7 100644 --- a/include/freerdp/constants.h +++ b/include/freerdp/constants.h @@ -99,7 +99,26 @@ enum FRDP_EVENT_TYPE { FRDP_EVENT_TYPE_DEBUG = 0, FRDP_EVENT_TYPE_VIDEO_FRAME = 1, - FRDP_EVENT_TYPE_REDRAW = 2 + FRDP_EVENT_TYPE_REDRAW = 2, + 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 +}; + +/** + * Clipboard Formats + */ +enum FRDP_CB_FORMAT +{ + CB_FORMAT_RAW = 0, + CB_FORMAT_TEXT = 1, + CB_FORMAT_DIB = 8, + CB_FORMAT_UNICODETEXT = 13, + CB_FORMAT_HTML = 0xD010, + CB_FORMAT_PNG = 0xD011, + CB_FORMAT_JPEG = 0xD012, + CB_FORMAT_GIF = 0xD013 }; #endif diff --git a/include/freerdp/types.h b/include/freerdp/types.h index 6f5a89b9f..7c949a457 100644 --- a/include/freerdp/types.h +++ b/include/freerdp/types.h @@ -164,6 +164,31 @@ struct _FRDP_REDRAW_EVENT }; typedef struct _FRDP_REDRAW_EVENT FRDP_REDRAW_EVENT; +typedef FRDP_EVENT FRDP_CB_SYNC_EVENT; + +struct _FRDP_CB_FORMAT_LIST_EVENT +{ + FRDP_EVENT event; + uint32* formats; + uint16 num_formats; +}; +typedef struct _FRDP_CB_FORMAT_LIST_EVENT FRDP_CB_FORMAT_LIST_EVENT; + +struct _FRDP_CB_DATA_REQUEST_EVENT +{ + FRDP_EVENT event; + uint32 format; +}; +typedef struct _FRDP_CB_DATA_REQUEST_EVENT FRDP_CB_DATA_REQUEST_EVENT; + +struct _FRDP_CB_DATA_RESPONSE_EVENT +{ + FRDP_EVENT event; + uint8* data; + uint32 size; +}; +typedef struct _FRDP_CB_DATA_RESPONSE_EVENT FRDP_CB_DATA_RESPONSE_EVENT; + typedef struct rdp_inst rdpInst; #endif /* __RDP_TYPES_H */ diff --git a/include/freerdp/utils/debug.h b/include/freerdp/utils/debug.h index 99bc8e796..c59efafe7 100644 --- a/include/freerdp/utils/debug.h +++ b/include/freerdp/utils/debug.h @@ -20,9 +20,7 @@ #ifndef __UTILS_DEBUG_H #define __UTILS_DEBUG_H -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #ifdef WITH_DEBUG_ASSERT #include @@ -36,6 +34,7 @@ #define DEBUG_NULL(fmt, ...) do { } while (0) #define DEBUG_PRINT(_dbg_str, fmt, ...) printf(_dbg_str fmt "\n" , __FUNCTION__, __LINE__, ## __VA_ARGS__) #define DEBUG_CLASS(_dbg_class, fmt, ...) DEBUG_PRINT("DBG_" #_dbg_class " %s (%d): ", fmt, ## __VA_ARGS__) +#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("Warning %s (%d): ", fmt, ## __VA_ARGS__) #ifdef WITH_DEBUG #define DEBUG(fmt, ...) DEBUG_PRINT("DBG %s (%d): ", fmt, ## __VA_ARGS__) diff --git a/include/freerdp/utils/list.h b/include/freerdp/utils/list.h index 37f3b69aa..94bb420f3 100644 --- a/include/freerdp/utils/list.h +++ b/include/freerdp/utils/list.h @@ -20,6 +20,8 @@ #ifndef __LIST_UTILS_H #define __LIST_UTILS_H +#include + #define DEFINE_LIST_TYPE(_list_type, _item_type) \ \ struct _item_type##_full \ @@ -32,7 +34,7 @@ struct _item_type##_full \ static struct _item_type* _item_type##_new(void) \ { \ struct _item_type* item; \ - item = (struct _item_type*)malloc(sizeof(struct _item_type##_full));\ + item = (struct _item_type*)xmalloc(sizeof(struct _item_type##_full));\ memset(item, 0, sizeof(struct _item_type##_full)); \ return item; \ } \ @@ -58,7 +60,7 @@ struct _list_type \ static struct _list_type* _list_type##_new(void) \ { \ struct _list_type* list; \ - list = (struct _list_type*)malloc(sizeof(struct _list_type)); \ + list = (struct _list_type*)xmalloc(sizeof(struct _list_type)); \ memset(list, 0, sizeof(struct _list_type)); \ return list; \ } \ @@ -101,9 +103,9 @@ void _list_type##_free(struct _list_type* list) \ { \ item = _list_type##_dequeue(list); \ _item_type##_free(item); \ - free(item); \ + xfree(item); \ } \ - free(list); \ + xfree(list); \ } #endif diff --git a/include/freerdp/utils/svc_plugin.h b/include/freerdp/utils/svc_plugin.h index 4081bf0d9..cccb82ba2 100644 --- a/include/freerdp/utils/svc_plugin.h +++ b/include/freerdp/utils/svc_plugin.h @@ -26,6 +26,7 @@ #include #include #include +#include typedef struct rdp_svc_plugin_private rdpSvcPluginPrivate; typedef struct rdp_svc_plugin rdpSvcPlugin; @@ -46,4 +47,10 @@ void svc_plugin_init(rdpSvcPlugin* plugin, CHANNEL_ENTRY_POINTS* pEntryPoints); int svc_plugin_send(rdpSvcPlugin* plugin, STREAM* data_out); int svc_plugin_send_event(rdpSvcPlugin* plugin, FRDP_EVENT* event); +#ifdef WITH_DEBUG_SVC +#define DEBUG_SVC(fmt, ...) DEBUG_CLASS(SVC, fmt, ## __VA_ARGS__) +#else +#define DEBUG_SVC(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) +#endif + #endif /* __SVC_PLUGIN_UTILS_H */ diff --git a/include/freerdp/utils/thread.h b/include/freerdp/utils/thread.h new file mode 100644 index 000000000..82f6192c2 --- /dev/null +++ b/include/freerdp/utils/thread.h @@ -0,0 +1,41 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Thread Utils + * + * 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 __THREAD_UTILS_H +#define __THREAD_UTILS_H + +#ifdef _WIN32 + +#define freerdp_thread_create(_proc, _arg) do { \ + DWORD thread; \ + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_proc, _arg, 0, &thread); \ + while (0) + +#else + +#include +#define freerdp_thread_create(_proc, _arg) do { \ + pthread_t thread; \ + pthread_create(&thread, 0, _proc, _arg); \ + pthread_detach(thread); \ + } while (0) + +#endif + +#endif /* __THREAD_UTILS_H */ diff --git a/libfreerdp-utils/event.c b/libfreerdp-utils/event.c index 66f858c23..6e3500178 100644 --- a/libfreerdp-utils/event.c +++ b/libfreerdp-utils/event.c @@ -40,6 +40,22 @@ FRDP_EVENT* freerdp_event_new(uint32 event_type, FRDP_EVENT_CALLBACK on_event_fr event = (FRDP_EVENT*)xmalloc(sizeof(FRDP_REDRAW_EVENT)); memset(event, 0, sizeof(FRDP_REDRAW_EVENT)); break; + case FRDP_EVENT_TYPE_CB_SYNC: + event = (FRDP_EVENT*)xmalloc(sizeof(FRDP_CB_SYNC_EVENT)); + memset(event, 0, sizeof(FRDP_CB_SYNC_EVENT)); + break; + case FRDP_EVENT_TYPE_CB_FORMAT_LIST: + event = (FRDP_EVENT*)xmalloc(sizeof(FRDP_CB_FORMAT_LIST_EVENT)); + memset(event, 0, sizeof(FRDP_CB_FORMAT_LIST_EVENT)); + break; + case FRDP_EVENT_TYPE_CB_DATA_REQUEST: + event = (FRDP_EVENT*)xmalloc(sizeof(FRDP_CB_DATA_REQUEST_EVENT)); + memset(event, 0, sizeof(FRDP_CB_DATA_REQUEST_EVENT)); + break; + case FRDP_EVENT_TYPE_CB_DATA_RESPONSE: + event = (FRDP_EVENT*)xmalloc(sizeof(FRDP_CB_DATA_RESPONSE_EVENT)); + memset(event, 0, sizeof(FRDP_CB_DATA_RESPONSE_EVENT)); + break; } if (event != NULL) { @@ -68,6 +84,20 @@ void freerdp_event_free(FRDP_EVENT* event) xfree(vevent->visible_rects); } break; + case FRDP_EVENT_TYPE_CB_FORMAT_LIST: + { + FRDP_CB_FORMAT_LIST_EVENT* cb_event = (FRDP_CB_FORMAT_LIST_EVENT*)event; + + xfree(cb_event->formats); + } + break; + case FRDP_EVENT_TYPE_CB_DATA_RESPONSE: + { + FRDP_CB_DATA_RESPONSE_EVENT* cb_event = (FRDP_CB_DATA_RESPONSE_EVENT*)event; + + xfree(cb_event->data); + } + break; } xfree(event); } diff --git a/libfreerdp-utils/svc_plugin.c b/libfreerdp-utils/svc_plugin.c index 210b7f1b5..ba5d6fae3 100644 --- a/libfreerdp-utils/svc_plugin.c +++ b/libfreerdp-utils/svc_plugin.c @@ -22,19 +22,18 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include -#ifdef WITH_DEBUG_SVC -#define DEBUG_SVC(fmt, ...) DEBUG_CLASS(SVC, fmt, ## __VA_ARGS__) -#else -#define DEBUG_SVC(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__) -#endif - /* The list of all plugin instances. */ typedef struct rdp_svc_plugin_list rdpSvcPluginList; struct rdp_svc_plugin_list @@ -48,11 +47,42 @@ static rdpSvcPluginList* g_svc_plugin_list = NULL; /* For locking the global resources */ static freerdp_mutex g_mutex = NULL; +/* Queue for receiving packets */ +struct svc_data_in_item +{ + STREAM* data_in; + FRDP_EVENT* event_in; +}; + +DEFINE_LIST_TYPE(svc_data_in_list, svc_data_in_item); + +void svc_data_in_item_free(struct svc_data_in_item* item) +{ + if (item->data_in) + { + stream_free(item->data_in); + item->data_in = NULL; + } + if (item->event_in) + { + freerdp_event_free(item->event_in); + item->event_in = NULL; + } +} + struct rdp_svc_plugin_private { void* init_handle; uint32 open_handle; STREAM* data_in; + + struct svc_data_in_list* data_in_list; + freerdp_mutex* data_in_mutex; + + struct wait_obj* signals[5]; + int num_signals; + + int thread_status; }; static rdpSvcPlugin* svc_plugin_find_by_init_handle(void* init_handle) @@ -120,6 +150,7 @@ static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, uint3 uint32 totalLength, uint32 dataFlags) { STREAM* data_in; + struct svc_data_in_item* item; if (dataFlags & CHANNEL_FLAG_FIRST) { @@ -138,13 +169,35 @@ static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, uint3 { printf("svc_plugin_process_received: read error\n"); } - /* the stream ownership is passed to the callback who is responsible for freeing it. */ + plugin->priv->data_in = NULL; stream_set_pos(data_in, 0); - plugin->receive_callback(plugin, data_in); + + item = svc_data_in_item_new(); + item->data_in = data_in; + + freerdp_mutex_lock(plugin->priv->data_in_mutex); + svc_data_in_list_enqueue(plugin->priv->data_in_list, item); + freerdp_mutex_unlock(plugin->priv->data_in_mutex); + + wait_obj_set(plugin->priv->signals[1]); } } +static void svc_plugin_process_event(rdpSvcPlugin* plugin, FRDP_EVENT* event_in) +{ + struct svc_data_in_item* item; + + item = svc_data_in_item_new(); + item->event_in = event_in; + + freerdp_mutex_lock(plugin->priv->data_in_mutex); + svc_data_in_list_enqueue(plugin->priv->data_in_list, item); + freerdp_mutex_unlock(plugin->priv->data_in_mutex); + + wait_obj_set(plugin->priv->signals[1]); +} + static void svc_plugin_open_event(uint32 openHandle, uint32 event, void* pData, uint32 dataLength, uint32 totalLength, uint32 dataFlags) { @@ -168,11 +221,71 @@ static void svc_plugin_open_event(uint32 openHandle, uint32 event, void* pData, stream_free((STREAM*)pData); break; case CHANNEL_EVENT_USER: - plugin->event_callback(plugin, (FRDP_EVENT*)pData); + svc_plugin_process_event(plugin, (FRDP_EVENT*)pData); break; } } +static void svc_plugin_process_data_in(rdpSvcPlugin* plugin) +{ + struct svc_data_in_item* item; + + while (1) + { + /* terminate signal */ + if (wait_obj_is_set(plugin->priv->signals[0])) + break; + + freerdp_mutex_lock(plugin->priv->data_in_mutex); + item = svc_data_in_list_dequeue(plugin->priv->data_in_list); + freerdp_mutex_unlock(plugin->priv->data_in_mutex); + + if (item != NULL) + { + /* the ownership of the data is passed to the callback */ + if (item->data_in) + plugin->receive_callback(plugin, item->data_in); + if (item->event_in) + plugin->event_callback(plugin, item->event_in); + xfree(item); + } + else + break; + } +} + +static void* svc_plugin_thread_func(void* arg) +{ + rdpSvcPlugin* plugin = (rdpSvcPlugin*)arg; + + DEBUG_SVC("in"); + + plugin->connect_callback(plugin); + + while (1) + { + wait_obj_select(plugin->priv->signals, plugin->priv->num_signals, -1); + + /* terminate signal */ + if (wait_obj_is_set(plugin->priv->signals[0])) + break; + + /* data_in signal */ + if (wait_obj_is_set(plugin->priv->signals[1])) + { + wait_obj_clear(plugin->priv->signals[1]); + /* process data in */ + svc_plugin_process_data_in(plugin); + } + } + + plugin->priv->thread_status = -1; + + DEBUG_SVC("out"); + + return 0; +} + static void svc_plugin_process_connected(rdpSvcPlugin* plugin, void* pData, uint32 dataLength) { uint32 error; @@ -184,15 +297,46 @@ static void svc_plugin_process_connected(rdpSvcPlugin* plugin, void* pData, uint printf("svc_plugin_process_connected: open failed\n"); return; } - plugin->connect_callback(plugin); + + plugin->priv->data_in_list = svc_data_in_list_new(); + plugin->priv->data_in_mutex = freerdp_mutex_new(); + + /* terminate signal */ + plugin->priv->signals[plugin->priv->num_signals++] = wait_obj_new(); + /* data_in signal */ + plugin->priv->signals[plugin->priv->num_signals++] = wait_obj_new(); + + plugin->priv->thread_status = 1; + + freerdp_thread_create(svc_plugin_thread_func, plugin); } static void svc_plugin_process_terminated(rdpSvcPlugin* plugin) { + struct timespec ts; + int i; + + wait_obj_set(plugin->priv->signals[0]); + i = 0; + ts.tv_sec = 0; + ts.tv_nsec = 10000000; + while (plugin->priv->thread_status > 0 && i < 1000) + { + i++; + nanosleep(&ts, NULL); + } + plugin->channel_entry_points.pVirtualChannelClose(plugin->priv->open_handle); svc_plugin_remove(plugin); + for (i = 0; i < plugin->priv->num_signals; i++) + wait_obj_free(plugin->priv->signals[i]); + plugin->priv->num_signals = 0; + + freerdp_mutex_free(plugin->priv->data_in_mutex); + svc_data_in_list_free(plugin->priv->data_in_list); + if (plugin->priv->data_in != NULL) { stream_free(plugin->priv->data_in); @@ -267,7 +411,10 @@ int svc_plugin_send(rdpSvcPlugin* plugin, STREAM* data_out) error = plugin->channel_entry_points.pVirtualChannelWrite(plugin->priv->open_handle, stream_get_data(data_out), stream_get_length(data_out), data_out); if (error != CHANNEL_RC_OK) + { + stream_free(data_out); printf("svc_plugin_send: VirtualChannelWrite failed %d\n", error); + } return error; }