diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 8c1f25675..9c681f2d3 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -1,2 +1,3 @@ option(WITH_DEBUG_TRANSPORT "Print transport debug message." OFF) option(WITH_DEBUG_CHANMAN "Print channel manager debug message." OFF) +option(WITH_DEBUG_SVC "Print static virtual channel debug message." OFF) diff --git a/config.h.in b/config.h.in index 77cd75c75..1d70425fa 100644 --- a/config.h.in +++ b/config.h.in @@ -11,3 +11,4 @@ /* Options */ #cmakedefine WITH_DEBUG_TRANSPORT #cmakedefine WITH_DEBUG_CHANMAN +#cmakedefine WITH_DEBUG_SVC diff --git a/include/freerdp/utils/svc_plugin.h b/include/freerdp/utils/svc_plugin.h new file mode 100644 index 000000000..041204743 --- /dev/null +++ b/include/freerdp/utils/svc_plugin.h @@ -0,0 +1,45 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Static Virtual Channel Interface + * + * 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 __SVC_PLUGIN_UTILS_H +#define __SVC_PLUGIN_UTILS_H + +/* static channel plugin base implementation */ + +#include + +typedef struct rdp_svc_plugin_private rdpSvcPluginPrivate; +typedef struct rdp_svc_plugin rdpSvcPlugin; +struct rdp_svc_plugin +{ + CHANNEL_ENTRY_POINTS channel_entry_points; + CHANNEL_DEF channel_def; + + void (*connect_callback)(rdpSvcPlugin* plugin); + void (*receive_callback)(rdpSvcPlugin* plugin, uint8* data, int size); + void (*terminate_callback)(rdpSvcPlugin* plugin); + + rdpSvcPluginPrivate* priv; +}; + +void svc_plugin_init(rdpSvcPlugin* plugin); +int svc_plugin_send(rdpSvcPlugin* plugin, uint8* data, int size); + +#endif /* __SVC_PLUGIN_UTILS_H */ diff --git a/libfreerdp-utils/CMakeLists.txt b/libfreerdp-utils/CMakeLists.txt index 4ef3760c0..fdec57ed3 100644 --- a/libfreerdp-utils/CMakeLists.txt +++ b/libfreerdp-utils/CMakeLists.txt @@ -25,6 +25,7 @@ set(FREERDP_UTILS_SRCS mutex.c semaphore.c stream.c + svc_plugin.c unicode.c wait_obj.c) diff --git a/libfreerdp-utils/svc_plugin.c b/libfreerdp-utils/svc_plugin.c new file mode 100644 index 000000000..0d36be2ae --- /dev/null +++ b/libfreerdp-utils/svc_plugin.c @@ -0,0 +1,233 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Static Virtual Channel Interface + * + * 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 "config.h" +#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 +{ + rdpSvcPlugin* plugin; + rdpSvcPluginList* next; +}; + +static rdpSvcPluginList* g_svc_plugin_list = NULL; + +/* For locking the global resources */ +static freerdp_mutex g_mutex = NULL; + +struct rdp_svc_plugin_private +{ + void* init_handle; + uint32 open_handle; +}; + +static rdpSvcPlugin* svc_plugin_find_by_init_handle(void* init_handle) +{ + rdpSvcPluginList * list; + rdpSvcPlugin * plugin; + + freerdp_mutex_lock(g_mutex); + for (list = g_svc_plugin_list; list; list = list->next) + { + plugin = list->plugin; + if (plugin->priv->init_handle == init_handle) + { + freerdp_mutex_unlock(g_mutex); + return plugin; + } + } + freerdp_mutex_unlock(g_mutex); + return NULL; +} + +static rdpSvcPlugin* svc_plugin_find_by_open_handle(uint32 open_handle) +{ + rdpSvcPluginList * list; + rdpSvcPlugin * plugin; + + freerdp_mutex_lock(g_mutex); + for (list = g_svc_plugin_list; list; list = list->next) + { + plugin = list->plugin; + if (plugin->priv->open_handle == open_handle) + { + freerdp_mutex_unlock(g_mutex); + return plugin; + } + } + freerdp_mutex_unlock(g_mutex); + return NULL; +} + +static void svc_plugin_remove(rdpSvcPlugin* plugin) +{ + rdpSvcPluginList* list; + rdpSvcPluginList* prev; + + /* Remove from global list */ + freerdp_mutex_lock(g_mutex); + for (prev = NULL, list = g_svc_plugin_list; list; prev = list, list = list->next) + { + if (list->plugin == plugin) + break; + } + if (list) + { + if (prev) + prev->next = list->next; + else + g_svc_plugin_list = list->next; + xfree(list); + } + freerdp_mutex_unlock(g_mutex); +} + +static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, uint32 dataLength, + uint32 totalLength, uint32 dataFlags) +{ + plugin->receive_callback(plugin, (uint8*)pData, dataLength); +} + +static void svc_plugin_open_event(uint32 openHandle, uint32 event, void* pData, uint32 dataLength, + uint32 totalLength, uint32 dataFlags) +{ + rdpSvcPlugin* plugin; + + DEBUG_SVC("event %d dataLength %d", event, dataLength); + + plugin = (rdpSvcPlugin*)svc_plugin_find_by_open_handle(openHandle); + if (plugin == NULL) + { + printf("svc_plugin_open_event: error no match\n"); + return; + } + switch (event) + { + case CHANNEL_EVENT_DATA_RECEIVED: + svc_plugin_process_received(plugin, pData, dataLength, totalLength, dataFlags); + break; + case CHANNEL_EVENT_WRITE_COMPLETE: + xfree(pData); + break; + } +} + +static void svc_plugin_process_connected(rdpSvcPlugin* plugin, void* pData, uint32 dataLength) +{ + uint32 error; + + error = plugin->channel_entry_points.pVirtualChannelOpen(plugin->priv->init_handle, &plugin->priv->open_handle, + plugin->channel_def.name, svc_plugin_open_event); + if (error != CHANNEL_RC_OK) + { + printf("svc_plugin_process_connected: open failed\n"); + return; + } + plugin->connect_callback(plugin); +} + +static void svc_plugin_process_terminated(rdpSvcPlugin* plugin) +{ + plugin->channel_entry_points.pVirtualChannelClose(plugin->priv->open_handle); + svc_plugin_remove(plugin); + xfree(plugin->priv); + plugin->priv = NULL; + plugin->terminate_callback(plugin); +} + +static void svc_plugin_init_event(void* pInitHandle, uint32 event, void* pData, uint32 dataLength) +{ + rdpSvcPlugin* plugin; + + DEBUG_SVC("event %d", event); + + plugin = (rdpSvcPlugin*)svc_plugin_find_by_init_handle(pInitHandle); + if (plugin == NULL) + { + printf("svc_plugin_init_event: error no match\n"); + return; + } + switch (event) + { + case CHANNEL_EVENT_CONNECTED: + svc_plugin_process_connected(plugin, pData, dataLength); + break; + case CHANNEL_EVENT_DISCONNECTED: + break; + case CHANNEL_EVENT_TERMINATED: + svc_plugin_process_terminated(plugin); + break; + } +} + +void svc_plugin_init(rdpSvcPlugin* plugin) +{ + rdpSvcPluginList* list; + + /** + * The channel manager will guarantee only one thread can call + * VirtualChannelInit at a time. So this should be safe. + */ + if (g_mutex == NULL) + g_mutex = freerdp_mutex_new(); + + plugin->priv = (rdpSvcPluginPrivate*)xmalloc(sizeof(rdpSvcPluginPrivate)); + memset(plugin->priv, 0, sizeof(rdpSvcPluginPrivate)); + + /* Add it to the global list */ + list = (rdpSvcPluginList*)xmalloc(sizeof(rdpSvcPluginList)); + list->plugin = plugin; + + freerdp_mutex_lock(g_mutex); + list->next = g_svc_plugin_list; + g_svc_plugin_list = list; + freerdp_mutex_unlock(g_mutex); + + plugin->channel_entry_points.pVirtualChannelInit(&plugin->priv->init_handle, + &plugin->channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, svc_plugin_init_event); +} + +int svc_plugin_send(rdpSvcPlugin* plugin, uint8* data, int size) +{ + uint32 error = 0; + + DEBUG_SVC("size %d", size); + + error = plugin->channel_entry_points.pVirtualChannelWrite(plugin->priv->open_handle, + data, size, data); + if (error != CHANNEL_RC_OK) + printf("svc_plugin_send: VirtualChannelWrite failed %d", error); + + return error; +}