# See the License for the specific language governing permissions and
# limitations under the License.
+set(DRDYNVC_SRCS
+ drdynvc_main.c
+ drdynvc_main.h
+ drdynvc_types.h
+ dvcman.c
+ dvcman.h
+)
+
+add_library(drdynvc ${DRDYNVC_SRCS})
+set_target_properties(drdynvc PROPERTIES PREFIX "")
+
+target_link_libraries(drdynvc freerdp-utils)
+
+install(TARGETS drdynvc DESTINATION ${FREERDP_PLUGIN_PATH})
+
+add_subdirectory(tsmf)
+add_subdirectory(audin)
+add_subdirectory(urbdrc)
+
+ if(WITH_CLIENT_CHANNELS)
+ add_subdirectory(client)
+ endif()
+
++
--- /dev/null
- cb = 3;
+ /**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel
+ *
+ * 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.
+ */
+
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ #include <freerdp/constants.h>
+ #include <freerdp/utils/memory.h>
+ #include <freerdp/utils/stream.h>
+ #include <freerdp/utils/svc_plugin.h>
+ #include <freerdp/utils/wait_obj.h>
+
+ #include "dvcman.h"
+ #include "drdynvc_types.h"
+ #include "drdynvc_main.h"
+
+ #define CREATE_REQUEST_PDU 0x01
+ #define DATA_FIRST_PDU 0x02
+ #define DATA_PDU 0x03
+ #define CLOSE_REQUEST_PDU 0x04
+ #define CAPABILITY_REQUEST_PDU 0x05
+
+ struct drdynvc_plugin
+ {
+ rdpSvcPlugin plugin;
+
+ int version;
+ int PriorityCharge0;
+ int PriorityCharge1;
+ int PriorityCharge2;
+ int PriorityCharge3;
++ int channel_error;
+
+ IWTSVirtualChannelManager* channel_mgr;
+ };
+
+ static int drdynvc_write_variable_uint(STREAM* stream, uint32 val)
+ {
+ int cb;
+
+ if (val <= 0xFF)
+ {
+ cb = 0;
+ stream_write_uint8(stream, val);
+ }
+ else if (val <= 0xFFFF)
+ {
+ cb = 1;
+ stream_write_uint16(stream, val);
+ }
+ else
+ {
- if (data_size <= CHANNEL_CHUNK_LENGTH - pos)
++ cb = 2;
+ stream_write_uint32(stream, val);
+ }
+ return cb;
+ }
+
+ int drdynvc_write_data(drdynvcPlugin* drdynvc, uint32 ChannelId, uint8* data, uint32 data_size)
+ {
+ STREAM* data_out;
+ uint32 pos = 0;
+ uint32 cbChId;
+ uint32 cbLen;
+ uint32 chunk_len;
+ int error;
+
+ DEBUG_DVC("ChannelId=%d size=%d", ChannelId, data_size);
+
++ if (drdynvc->channel_error != CHANNEL_RC_OK)
++ return 1;
++
+ data_out = stream_new(CHANNEL_CHUNK_LENGTH);
+ stream_set_pos(data_out, 1);
+ cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
+
++ if(data_size == 0)
++ {
++ pos = stream_get_pos(data_out);
++ stream_set_pos(data_out, 0);
++ stream_write_uint8(data_out, 0x40 | cbChId);
++ stream_set_pos(data_out, pos);
++ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
++ }
++ else if (data_size <= CHANNEL_CHUNK_LENGTH - pos)
+ {
+ pos = stream_get_pos(data_out);
+ stream_set_pos(data_out, 0);
+ stream_write_uint8(data_out, 0x30 | cbChId);
+ stream_set_pos(data_out, pos);
+ stream_write(data_out, data, data_size);
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ }
+ else
+ {
+ /* Fragment the data */
+ cbLen = drdynvc_write_variable_uint(data_out, data_size);
+ pos = stream_get_pos(data_out);
+ stream_set_pos(data_out, 0);
+ stream_write_uint8(data_out, 0x20 | cbChId | (cbLen << 2));
+ stream_set_pos(data_out, pos);
+ chunk_len = CHANNEL_CHUNK_LENGTH - pos;
+ stream_write(data_out, data, chunk_len);
+ data += chunk_len;
+ data_size -= chunk_len;
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+
+ while (error == CHANNEL_RC_OK && data_size > 0)
+ {
+ data_out = stream_new(CHANNEL_CHUNK_LENGTH);
+ stream_set_pos(data_out, 1);
+ cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
+
+ pos = stream_get_pos(data_out);
+ stream_set_pos(data_out, 0);
+ stream_write_uint8(data_out, 0x30 | cbChId);
+ stream_set_pos(data_out, pos);
+
+ chunk_len = data_size;
+ if (chunk_len > CHANNEL_CHUNK_LENGTH - pos)
+ chunk_len = CHANNEL_CHUNK_LENGTH - pos;
+ stream_write(data_out, data, chunk_len);
+ data += chunk_len;
+ data_size -= chunk_len;
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ }
+ }
+ if (error != CHANNEL_RC_OK)
+ {
++ drdynvc->channel_error = error;
+ DEBUG_WARN("VirtualChannelWrite failed %d", error);
+ return 1;
+ }
+ return 0;
+ }
+
+ int drdynvc_push_event(drdynvcPlugin* drdynvc, RDP_EVENT* event)
+ {
+ int error;
+
+ error = svc_plugin_send_event((rdpSvcPlugin*)drdynvc, event);
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("pVirtualChannelEventPush failed %d", error);
+ return 1;
+ }
+ return 0;
+ }
+
+ static int drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+ {
+ STREAM* data_out;
+ int error;
+
+ DEBUG_DVC("Sp=%d cbChId=%d", Sp, cbChId);
+ stream_seek(s, 1); /* pad */
+ stream_read_uint16(s, drdynvc->version);
+ if (drdynvc->version == 2)
+ {
+ stream_read_uint16(s, drdynvc->PriorityCharge0);
+ stream_read_uint16(s, drdynvc->PriorityCharge1);
+ stream_read_uint16(s, drdynvc->PriorityCharge2);
+ stream_read_uint16(s, drdynvc->PriorityCharge3);
+ }
+ data_out = stream_new(4);
+ stream_write_uint16(data_out, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
+ stream_write_uint16(data_out, drdynvc->version);
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("VirtualChannelWrite failed %d", error);
+ return 1;
+ }
++ drdynvc->channel_error = error;
++
+ return 0;
+ }
+
+ static uint32 drdynvc_read_variable_uint(STREAM* stream, int cbLen)
+ {
+ uint32 val;
+
+ switch (cbLen)
+ {
+ case 0:
+ stream_read_uint8(stream, val);
+ break;
+ case 1:
+ stream_read_uint16(stream, val);
+ break;
+ default:
+ stream_read_uint32(stream, val);
+ break;
+ }
+ return val;
+ }
+
+ static int drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+ {
+ STREAM* data_out;
+ int pos;
+ int error;
+ uint32 ChannelId;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ pos = stream_get_pos(s);
+ DEBUG_DVC("ChannelId=%d ChannelName=%s", ChannelId, stream_get_tail(s));
+
+ error = dvcman_create_channel(drdynvc->channel_mgr, ChannelId, (char*)stream_get_tail(s));
+
+ data_out = stream_new(pos + 4);
+ stream_write_uint8(data_out, 0x10 | cbChId);
+ stream_set_pos(s, 1);
+ stream_copy(data_out, s, pos - 1);
+
+ if (error == 0)
+ {
+ DEBUG_DVC("channel created");
+ stream_write_uint32(data_out, 0);
+ }
+ else
+ {
+ DEBUG_DVC("no listener");
+ stream_write_uint32(data_out, (uint32)(-1));
+ }
+
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("VirtualChannelWrite failed %d", error);
+ return 1;
+ }
+ return 0;
+ }
+
+ static int drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+ {
+ uint32 ChannelId;
+ uint32 Length;
+ int error;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ Length = drdynvc_read_variable_uint(s, Sp);
+ DEBUG_DVC("ChannelId=%d Length=%d", ChannelId, Length);
+
+ error = dvcman_receive_channel_data_first(drdynvc->channel_mgr, ChannelId, Length);
+ if (error)
+ return error;
+
+ return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
+ stream_get_tail(s), stream_get_left(s));
+ }
+
+ static int drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+ {
+ uint32 ChannelId;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ DEBUG_DVC("ChannelId=%d", ChannelId);
+
+ return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
+ stream_get_tail(s), stream_get_left(s));
+ }
+
+ static int drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+ {
+ uint32 ChannelId;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ DEBUG_DVC("ChannelId=%d", ChannelId);
+ dvcman_close_channel(drdynvc->channel_mgr, ChannelId);
+
+ return 0;
+ }
+
+ static void drdynvc_process_receive(rdpSvcPlugin* plugin, STREAM* s)
+ {
+ drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
+ int value;
+ int Cmd;
+ int Sp;
+ int cbChId;
+
+ stream_read_uint8(s, value);
+ Cmd = (value & 0xf0) >> 4;
+ Sp = (value & 0x0c) >> 2;
+ cbChId = (value & 0x03) >> 0;
+
+ DEBUG_DVC("Cmd=0x%x", Cmd);
+
+ switch (Cmd)
+ {
+ case CAPABILITY_REQUEST_PDU:
+ drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
+ break;
+ case CREATE_REQUEST_PDU:
+ drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
+ break;
+ case DATA_FIRST_PDU:
+ drdynvc_process_data_first(drdynvc, Sp, cbChId, s);
+ break;
+ case DATA_PDU:
+ drdynvc_process_data(drdynvc, Sp, cbChId, s);
+ break;
+ case CLOSE_REQUEST_PDU:
+ drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
+ break;
+ default:
+ DEBUG_WARN("unknown drdynvc cmd 0x%x", Cmd);
+ break;
+ }
+
+ stream_free(s);
+ }
+
+ static void drdynvc_process_connect(rdpSvcPlugin* plugin)
+ {
+ drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
+
+ DEBUG_DVC("connecting");
+
+ drdynvc->channel_mgr = dvcman_new(drdynvc);
++ drdynvc->channel_error = 0;
+ dvcman_load_plugin(drdynvc->channel_mgr, svc_plugin_get_data(plugin));
+ dvcman_init(drdynvc->channel_mgr);
+ }
+
+ static void drdynvc_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
+ {
+ freerdp_event_free(event);
+ }
+
+ static void drdynvc_process_terminate(rdpSvcPlugin* plugin)
+ {
+ drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
+
+ DEBUG_DVC("terminating");
+
+ if (drdynvc->channel_mgr != NULL)
+ dvcman_free(drdynvc->channel_mgr);
+ xfree(drdynvc);
+ }
+
+ DEFINE_SVC_PLUGIN(drdynvc, "drdynvc",
+ CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
+ CHANNEL_OPTION_COMPRESS_RDP)
--- /dev/null
+ /**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel
+ *
+ * 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 __DRDYNVC_TYPES_H
+ #define __DRDYNVC_TYPES_H
+
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
+ #include <freerdp/dvc.h>
+ #include <freerdp/types.h>
+ #include <freerdp/utils/debug.h>
++#include <pthread.h>
+
+ #ifdef WITH_DEBUG_DVC
+ #define DEBUG_DVC(fmt, ...) DEBUG_CLASS(DVC, fmt, ## __VA_ARGS__)
+ #else
+ #define DEBUG_DVC(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
+ #endif
+
+ #endif
--- /dev/null
- return drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize);
+ /**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel Manager
+ *
+ * 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.
+ */
+
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ #include <freerdp/utils/memory.h>
+ #include <freerdp/utils/stream.h>
+ #include <freerdp/utils/list.h>
+ #include <freerdp/utils/load_plugin.h>
+
+ #include "drdynvc_types.h"
+ #include "dvcman.h"
+
+ #define MAX_PLUGINS 10
+
+ typedef struct _DVCMAN DVCMAN;
+ struct _DVCMAN
+ {
+ IWTSVirtualChannelManager iface;
+
+ drdynvcPlugin* drdynvc;
+
+ const char* plugin_names[MAX_PLUGINS];
+ IWTSPlugin* plugins[MAX_PLUGINS];
+ int num_plugins;
+
+ IWTSListener* listeners[MAX_PLUGINS];
+ int num_listeners;
+
+ LIST* channels;
+ };
+
+ typedef struct _DVCMAN_LISTENER DVCMAN_LISTENER;
+ struct _DVCMAN_LISTENER
+ {
+ IWTSListener iface;
+
+ DVCMAN* dvcman;
+ char* channel_name;
+ uint32 flags;
+ IWTSListenerCallback* listener_callback;
+ };
+
+ typedef struct _DVCMAN_ENTRY_POINTS DVCMAN_ENTRY_POINTS;
+ struct _DVCMAN_ENTRY_POINTS
+ {
+ IDRDYNVC_ENTRY_POINTS iface;
+
+ DVCMAN* dvcman;
+ RDP_PLUGIN_DATA* plugin_data;
+ };
+
+ typedef struct _DVCMAN_CHANNEL DVCMAN_CHANNEL;
+ struct _DVCMAN_CHANNEL
+ {
+ IWTSVirtualChannel iface;
+
+ DVCMAN* dvcman;
+ DVCMAN_CHANNEL* next;
+ uint32 channel_id;
+ IWTSVirtualChannelCallback* channel_callback;
+
+ STREAM* dvc_data;
++
++ pthread_mutex_t dvc_chan_mutex;
+ };
+
+ static int dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
+ {
+ *ppPropertyBag = NULL;
+ return 1;
+ }
+
+ static int dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
+ const char* pszChannelName, uint32 ulFlags,
+ IWTSListenerCallback* pListenerCallback, IWTSListener** ppListener)
+ {
+ DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
+ DVCMAN_LISTENER* listener;
+
+ if (dvcman->num_listeners < MAX_PLUGINS)
+ {
+ DEBUG_DVC("%d.%s.", dvcman->num_listeners, pszChannelName);
+ listener = xnew(DVCMAN_LISTENER);
+ listener->iface.GetConfiguration = dvcman_get_configuration;
+ listener->dvcman = dvcman;
+ listener->channel_name = xstrdup(pszChannelName);
+ listener->flags = ulFlags;
+ listener->listener_callback = pListenerCallback;
+
+ if (ppListener)
+ *ppListener = (IWTSListener*)listener;
+ dvcman->listeners[dvcman->num_listeners++] = (IWTSListener*)listener;
+ return 0;
+ }
+ else
+ {
+ DEBUG_WARN("Maximum DVC listener number reached.");
+ return 1;
+ }
+ }
+
+ static int dvcman_push_event(IWTSVirtualChannelManager* pChannelMgr, RDP_EVENT* pEvent)
+ {
+ int error;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ error = drdynvc_push_event(dvcman->drdynvc, pEvent);
+
+ if (error == 0)
+ {
+ DEBUG_DVC("event_type %d pushed.", pEvent->event_type);
+ }
+ else
+ {
+ DEBUG_WARN("event_type %d push failed.", pEvent->event_type);
+ }
+
+ return error;
+ }
+
+ static int dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name, IWTSPlugin* pPlugin)
+ {
+ DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->dvcman;
+
+ if (dvcman->num_plugins < MAX_PLUGINS)
+ {
+ DEBUG_DVC("num_plugins %d", dvcman->num_plugins);
+ dvcman->plugin_names[dvcman->num_plugins] = name;
+ dvcman->plugins[dvcman->num_plugins++] = pPlugin;
+ return 0;
+ }
+ else
+ {
+ DEBUG_WARN("Maximum DVC plugin number reached.");
+ return 1;
+ }
+ }
+
+ IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
+ {
+ int i;
+ DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->dvcman;
+
+ for (i = 0; i < dvcman->num_plugins; i++)
+ {
+ if (dvcman->plugin_names[i] == name ||
+ strcmp(dvcman->plugin_names[i], name) == 0)
+ {
+ return dvcman->plugins[i];
+ }
+ }
+
+ return NULL;
+ }
+
+ RDP_PLUGIN_DATA* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
+ {
+ return ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->plugin_data;
+ }
+
++uint32 dvcman_get_channel_id(IWTSVirtualChannel * channel)
++{
++ return ((DVCMAN_CHANNEL*)channel)->channel_id;
++}
++
++IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId)
++{
++ LIST_ITEM* curr;
++ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
++
++ for (curr = dvcman->channels->head; curr; curr = curr->next)
++ {
++ if (((DVCMAN_CHANNEL*) curr->data)->channel_id == ChannelId)
++ {
++ return (IWTSVirtualChannel*)curr->data;
++ }
++ }
++
++ return NULL;
++}
++
++
+ IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
+ {
+ DVCMAN* dvcman;
+
+ dvcman = xnew(DVCMAN);
+ dvcman->iface.CreateListener = dvcman_create_listener;
+ dvcman->iface.PushEvent = dvcman_push_event;
++ dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
++ dvcman->iface.GetChannelId = dvcman_get_channel_id;
+ dvcman->drdynvc = plugin;
+ dvcman->channels = list_new();
+
+ return (IWTSVirtualChannelManager*) dvcman;
+ }
+
+ int dvcman_load_plugin(IWTSVirtualChannelManager* pChannelMgr, RDP_PLUGIN_DATA* data)
+ {
+ DVCMAN_ENTRY_POINTS entryPoints;
+ PDVC_PLUGIN_ENTRY pDVCPluginEntry = NULL;
+
+ while (data && data->size > 0)
+ {
+ pDVCPluginEntry = (PDVC_PLUGIN_ENTRY) freerdp_load_plugin((char*) data->data[0], "DVCPluginEntry");
+
+ if (pDVCPluginEntry != NULL)
+ {
+ entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
+ entryPoints.iface.GetPlugin = dvcman_get_plugin;
+ entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
+ entryPoints.dvcman = (DVCMAN*) pChannelMgr;
+ entryPoints.plugin_data = data;
+ pDVCPluginEntry((IDRDYNVC_ENTRY_POINTS*) &entryPoints);
+ }
+
+ data = (RDP_PLUGIN_DATA*)(((uint8*) data) + data->size);
+ }
+
+ return 0;
+ }
+
+ static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
+ {
+ if (channel->channel_callback)
+ channel->channel_callback->OnClose(channel->channel_callback);
+
+ xfree(channel);
+ }
+
+ void dvcman_free(IWTSVirtualChannelManager* pChannelMgr)
+ {
+ int i;
+ IWTSPlugin* pPlugin;
+ DVCMAN_LISTENER* listener;
+ DVCMAN_CHANNEL* channel;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ while ((channel = (DVCMAN_CHANNEL*) list_dequeue(dvcman->channels)) != NULL)
+ dvcman_channel_free(channel);
+
+ list_free(dvcman->channels);
+
+ for (i = 0; i < dvcman->num_listeners; i++)
+ {
+ listener = (DVCMAN_LISTENER*) dvcman->listeners[i];
+ xfree(listener->channel_name);
+ xfree(listener);
+ }
+
+ for (i = 0; i < dvcman->num_plugins; i++)
+ {
+ pPlugin = dvcman->plugins[i];
+
+ if (pPlugin->Terminated)
+ pPlugin->Terminated(pPlugin);
+ }
+
+ xfree(dvcman);
+ }
+
+ int dvcman_init(IWTSVirtualChannelManager* pChannelMgr)
+ {
+ int i;
+ IWTSPlugin* pPlugin;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ for (i = 0; i < dvcman->num_plugins; i++)
+ {
+ pPlugin = dvcman->plugins[i];
+
+ if (pPlugin->Initialize)
+ pPlugin->Initialize(pPlugin, pChannelMgr);
+ }
+
+ return 0;
+ }
+
+ static int dvcman_write_channel(IWTSVirtualChannel* pChannel, uint32 cbSize, uint8* pBuffer, void* pReserved)
+ {
+ DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*) pChannel;
++ int error;
++
++ pthread_mutex_lock(&channel->dvc_chan_mutex);
++ error = drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize);
++ pthread_mutex_unlock(&channel->dvc_chan_mutex);
+
-static DVCMAN_CHANNEL* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId)
-{
- LIST_ITEM* curr;
- DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
-
- for (curr = dvcman->channels->head; curr; curr = curr->next)
- {
- if (((DVCMAN_CHANNEL*) curr->data)->channel_id == ChannelId)
- {
- return (DVCMAN_CHANNEL*)curr->data;
- }
- }
-
- return NULL;
-}
++ return error;
+ }
+
+ static int dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
+ {
+ DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*) pChannel;
+ DVCMAN* dvcman = channel->dvcman;
+
+ DEBUG_DVC("id=%d", channel->channel_id);
+
+ if (list_remove(dvcman->channels, channel) == NULL)
+ DEBUG_WARN("channel not found");
+
+ dvcman_channel_free(channel);
+
+ return 1;
+ }
+
+ int dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, const char* ChannelName)
+ {
+ int i;
+ int bAccept;
+ DVCMAN_LISTENER* listener;
+ DVCMAN_CHANNEL* channel;
+ IWTSVirtualChannelCallback* pCallback;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ for (i = 0; i < dvcman->num_listeners; i++)
+ {
+ listener = (DVCMAN_LISTENER*)dvcman->listeners[i];
+
+ if (strcmp(listener->channel_name, ChannelName) == 0)
+ {
+ channel = xnew(DVCMAN_CHANNEL);
+ channel->iface.Write = dvcman_write_channel;
+ channel->iface.Close = dvcman_close_channel_iface;
+ channel->dvcman = dvcman;
+ channel->channel_id = ChannelId;
++ pthread_mutex_init(&channel->dvc_chan_mutex, NULL);
+
+ bAccept = 1;
+ pCallback = NULL;
+
+ if (listener->listener_callback->OnNewChannelConnection(listener->listener_callback,
+ (IWTSVirtualChannel*) channel, NULL, &bAccept, &pCallback) == 0 && bAccept == 1)
+ {
+ DEBUG_DVC("listener %s created new channel %d",
+ listener->channel_name, channel->channel_id);
+ channel->channel_callback = pCallback;
+ list_add(dvcman->channels, channel);
+
+ return 0;
+ }
+ else
+ {
+ DEBUG_WARN("channel rejected by plugin");
+ dvcman_channel_free(channel);
+ return 1;
+ }
+ }
+ }
+
+ return 1;
+ }
+
- channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ int dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId)
+ {
+ DVCMAN_CHANNEL* channel;
+ IWTSVirtualChannel* ichannel;
+
- channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
++ channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ if (channel == NULL)
+ {
+ DEBUG_WARN("ChannelId %d not found!", ChannelId);
+ return 1;
+ }
+
+ if (channel->dvc_data)
+ {
+ stream_free(channel->dvc_data);
+ channel->dvc_data = NULL;
+ }
+
+ DEBUG_DVC("dvcman_close_channel: channel %d closed", ChannelId);
+ ichannel = (IWTSVirtualChannel*)channel;
+ ichannel->Close(ichannel);
+
+ return 0;
+ }
+
+ int dvcman_receive_channel_data_first(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint32 length)
+ {
+ DVCMAN_CHANNEL* channel;
+
- channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
++ channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ if (channel == NULL)
+ {
+ DEBUG_WARN("ChannelId %d not found!", ChannelId);
+ return 1;
+ }
+
+ if (channel->dvc_data)
+ stream_free(channel->dvc_data);
+
+ channel->dvc_data = stream_new(length);
+
+ return 0;
+ }
+
+ int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint8* data, uint32 data_size)
+ {
+ int error = 0;
+ DVCMAN_CHANNEL* channel;
+
++ channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ if (channel == NULL)
+ {
+ DEBUG_WARN("ChannelId %d not found!", ChannelId);
+ return 1;
+ }
+
+ if (channel->dvc_data)
+ {
+ /* Fragmented data */
+ if (stream_get_length(channel->dvc_data) + data_size > (uint32) stream_get_size(channel->dvc_data))
+ {
+ DEBUG_WARN("data exceeding declared length!");
+ stream_free(channel->dvc_data);
+ channel->dvc_data = NULL;
+ return 1;
+ }
+
+ stream_write(channel->dvc_data, data, data_size);
+
+ if (stream_get_length(channel->dvc_data) >= stream_get_size(channel->dvc_data))
+ {
+ error = channel->channel_callback->OnDataReceived(channel->channel_callback,
+ stream_get_size(channel->dvc_data), stream_get_data(channel->dvc_data));
+ stream_free(channel->dvc_data);
+ channel->dvc_data = NULL;
+ }
+ }
+ else
+ {
+ error = channel->channel_callback->OnDataReceived(channel->channel_callback, data_size, data);
+ }
+
+ return error;
+ }
--- /dev/null
+ # FreeRDP: A Remote Desktop Protocol Client
+ # libfreerdp-utils cmake build script
+ #
+ # Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+
+ set(MODULE_NAME "freerdp-utils")
+ set(MODULE_PREFIX "FREERDP_UTILS")
+
+ set(CMAKE_THREAD_PREFER_PTHREAD)
+ find_required_package(Threads)
+
+ set(${MODULE_PREFIX}_SRCS
+ args.c
+ dsp.c
+ event.c
+ bitmap.c
+ hexdump.c
+ list.c
+ file.c
++ msusb.c
+ load_plugin.c
+ memory.c
+ passphrase.c
+ pcap.c
+ profiler.c
+ rail.c
+ signal.c
+ sleep.c
+ stopwatch.c
+ stream.c
+ string.c
+ svc_plugin.c
+ tcp.c
+ thread.c
+ time.c
+ uds.c
+ unicode.c
+ wait_obj.c)
+
+ if(WITH_MONOLITHIC_BUILD)
+ add_library(${MODULE_NAME} OBJECT ${${MODULE_PREFIX}_SRCS})
+ else()
+ add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+ endif()
+
+ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION_FULL} SOVERSION ${FREERDP_VERSION} PREFIX "lib")
+
+ set(${MODULE_PREFIX}_LIBS
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${CMAKE_DL_LIBS})
+
+ if(WIN32)
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ws2_32)
+ endif()
+
+ if(${CMAKE_SYSTEM_NAME} MATCHES SunOS)
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} rt)
+ endif()
+
+ if(WITH_MONOLITHIC_BUILD)
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
+ set(FREERDP_LIBS ${FREERDP_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
+ else()
+ if(NOT WIN32)
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr-crt)
+ endif()
+
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr-synch)
+
+ target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
+ install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ endif()
+
+ set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP")
--- /dev/null
- error = plugin->channel_entry_points.pVirtualChannelWrite(plugin->priv->open_handle,
- stream_get_data(data_out), stream_get_length(data_out), data_out);
+ /**
+ * 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.
+ */
+
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+
+ #include <freerdp/constants.h>
+ #include <freerdp/utils/memory.h>
+ #include <freerdp/utils/debug.h>
+ #include <freerdp/utils/stream.h>
+ #include <freerdp/utils/list.h>
+ #include <freerdp/utils/thread.h>
+ #include <freerdp/utils/event.h>
+ #include <freerdp/utils/svc_plugin.h>
+
+ /* 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 HANDLE g_mutex = NULL;
+
+ /* Queue for receiving packets */
+ struct _svc_data_in_item
+ {
+ STREAM* data_in;
+ RDP_EVENT* event_in;
+ };
+ typedef struct _svc_data_in_item svc_data_in_item;
+
+ static void svc_data_in_item_free(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;
+ }
+ xfree(item);
+ }
+
+ struct rdp_svc_plugin_private
+ {
+ void* init_handle;
+ uint32 open_handle;
+ STREAM* data_in;
+
+ LIST* data_in_list;
+ freerdp_thread* thread;
+ };
+
+ static rdpSvcPlugin* svc_plugin_find_by_init_handle(void* init_handle)
+ {
+ rdpSvcPluginList* list;
+ rdpSvcPlugin* plugin;
+
+ WaitForSingleObject(g_mutex, INFINITE);
+
+ for (list = g_svc_plugin_list; list; list = list->next)
+ {
+ plugin = list->plugin;
+
+ if (plugin->priv->init_handle == init_handle)
+ {
+ ReleaseMutex(g_mutex);
+ return plugin;
+ }
+ }
+
+ ReleaseMutex(g_mutex);
+
+ return NULL;
+ }
+
+ static rdpSvcPlugin* svc_plugin_find_by_open_handle(uint32 open_handle)
+ {
+ rdpSvcPluginList * list;
+ rdpSvcPlugin * plugin;
+
+ WaitForSingleObject(g_mutex, INFINITE);
+
+ for (list = g_svc_plugin_list; list; list = list->next)
+ {
+ plugin = list->plugin;
+
+ if (plugin->priv->open_handle == open_handle)
+ {
+ ReleaseMutex(g_mutex);
+ return plugin;
+ }
+ }
+ ReleaseMutex(g_mutex);
+
+ return NULL;
+ }
+
+ static void svc_plugin_remove(rdpSvcPlugin* plugin)
+ {
+ rdpSvcPluginList* list;
+ rdpSvcPluginList* prev;
+
+ /* Remove from global list */
+ WaitForSingleObject(g_mutex, INFINITE);
+ 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);
+ }
+ ReleaseMutex(g_mutex);
+ }
+
+ static void svc_plugin_process_received(rdpSvcPlugin* plugin, void* pData, uint32 dataLength,
+ uint32 totalLength, uint32 dataFlags)
+ {
+ STREAM* data_in;
+ svc_data_in_item* item;
+
+ if ( (dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
+ {
+ /* According to MS-RDPBCGR 2.2.6.1, "All virtual channel traffic MUST be suspended.
+ This flag is only valid in server-to-client virtual channel traffic. It MUST be
+ ignored in client-to-server data." Thus it would be best practice to cease data
+ transmission. However, simply returing here avoids a crash. */
+ return;
+ }
+
+ if (dataFlags & CHANNEL_FLAG_FIRST)
+ {
+ if (plugin->priv->data_in != NULL)
+ stream_free(plugin->priv->data_in);
+ plugin->priv->data_in = stream_new(totalLength);
+ }
+
+ data_in = plugin->priv->data_in;
+ stream_check_size(data_in, (int) dataLength);
+ stream_write(data_in, pData, dataLength);
+
+ if (dataFlags & CHANNEL_FLAG_LAST)
+ {
+ if (stream_get_size(data_in) != stream_get_length(data_in))
+ {
+ printf("svc_plugin_process_received: read error\n");
+ }
+
+ plugin->priv->data_in = NULL;
+ stream_set_pos(data_in, 0);
+
+ item = xnew(svc_data_in_item);
+ item->data_in = data_in;
+
+ freerdp_thread_lock(plugin->priv->thread);
+ list_enqueue(plugin->priv->data_in_list, item);
+ freerdp_thread_unlock(plugin->priv->thread);
+
+ freerdp_thread_signal(plugin->priv->thread);
+ }
+ }
+
+ static void svc_plugin_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event_in)
+ {
+ svc_data_in_item* item;
+
+ item = xnew(svc_data_in_item);
+ item->event_in = event_in;
+
+ freerdp_thread_lock(plugin->priv->thread);
+ list_enqueue(plugin->priv->data_in_list, item);
+ freerdp_thread_unlock(plugin->priv->thread);
+
+ freerdp_thread_signal(plugin->priv->thread);
+ }
+
+ static void svc_plugin_open_event(uint32 openHandle, uint32 event, void* pData, uint32 dataLength,
+ uint32 totalLength, uint32 dataFlags)
+ {
+ rdpSvcPlugin* plugin;
+
+ DEBUG_SVC("openHandle %d event %d dataLength %d totalLength %d dataFlags %d",
+ openHandle, event, dataLength, totalLength, dataFlags);
+
+ 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:
+ stream_free((STREAM*) pData);
+ break;
+
+ case CHANNEL_EVENT_USER:
+ svc_plugin_process_event(plugin, (RDP_EVENT*) pData);
+ break;
+ }
+ }
+
+ static void svc_plugin_process_data_in(rdpSvcPlugin* plugin)
+ {
+ svc_data_in_item* item;
+
+ while (1)
+ {
+ /* terminate signal */
+ if (freerdp_thread_is_stopped(plugin->priv->thread))
+ break;
+
+ freerdp_thread_lock(plugin->priv->thread);
+ item = list_dequeue(plugin->priv->data_in_list);
+ freerdp_thread_unlock(plugin->priv->thread);
+
+ if (item != NULL)
+ {
+ /* the ownership of the data is passed to the callback */
+ if (item->data_in)
+ IFCALL(plugin->receive_callback, plugin, item->data_in);
+ if (item->event_in)
+ IFCALL(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");
+
+ IFCALL(plugin->connect_callback, plugin);
+
+ while (1)
+ {
+ if (plugin->interval_ms > 0)
+ freerdp_thread_wait_timeout(plugin->priv->thread, plugin->interval_ms);
+ else
+ freerdp_thread_wait(plugin->priv->thread);
+
+ if (freerdp_thread_is_stopped(plugin->priv->thread))
+ break;
+
+ freerdp_thread_reset(plugin->priv->thread);
+ svc_plugin_process_data_in(plugin);
+
+ if (plugin->interval_ms > 0)
+ IFCALL(plugin->interval_callback, plugin);
+ }
+
+ freerdp_thread_quit(plugin->priv->thread);
+
+ DEBUG_SVC("out");
+
+ return 0;
+ }
+
+ 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->priv->data_in_list = list_new();
+ plugin->priv->thread = freerdp_thread_new();
+
+ freerdp_thread_start(plugin->priv->thread, svc_plugin_thread_func, plugin);
+ }
+
+ static void svc_plugin_process_terminated(rdpSvcPlugin* plugin)
+ {
+ svc_data_in_item* item;
+
+ freerdp_thread_stop(plugin->priv->thread);
+ freerdp_thread_free(plugin->priv->thread);
+
+ plugin->channel_entry_points.pVirtualChannelClose(plugin->priv->open_handle);
+ xfree(plugin->channel_entry_points.pExtendedData);
+
+ svc_plugin_remove(plugin);
+
+ while ((item = list_dequeue(plugin->priv->data_in_list)) != NULL)
+ svc_data_in_item_free(item);
+ list_free(plugin->priv->data_in_list);
+
+ if (plugin->priv->data_in != NULL)
+ {
+ stream_free(plugin->priv->data_in);
+ plugin->priv->data_in = NULL;
+ }
+ xfree(plugin->priv);
+ plugin->priv = NULL;
+
+ IFCALL(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)
+ {
+ 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, CHANNEL_ENTRY_POINTS* pEntryPoints)
+ {
+ 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 = CreateMutex(NULL, FALSE, NULL);
+
+ memcpy(&plugin->channel_entry_points, pEntryPoints, pEntryPoints->cbSize);
+
+ plugin->priv = xnew(rdpSvcPluginPrivate);
+
+ /* Add it to the global list */
+ list = xnew(rdpSvcPluginList);
+ list->plugin = plugin;
+
+ WaitForSingleObject(g_mutex, INFINITE);
+ list->next = g_svc_plugin_list;
+ g_svc_plugin_list = list;
+ ReleaseMutex(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, STREAM* data_out)
+ {
+ uint32 error = 0;
+
+ DEBUG_SVC("length %d", (int) stream_get_length(data_out));
+
++ if (!plugin || !plugin->priv)
++ error = CHANNEL_RC_BAD_INIT_HANDLE;
++ else
++ 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;
+ }
+
+ int svc_plugin_send_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
+ {
+ uint32 error = 0;
+
+ DEBUG_SVC("event_type %d", event->event_type);
+
+ error = plugin->channel_entry_points.pVirtualChannelEventPush(plugin->priv->open_handle, event);
+
+ if (error != CHANNEL_RC_OK)
+ printf("svc_plugin_send_event: VirtualChannelEventPush failed %d\n", error);
+
+ return error;
+ }
++