freerdp: merging with master
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Tue, 2 Oct 2012 20:32:41 +0000 (16:32 -0400)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Tue, 2 Oct 2012 20:32:41 +0000 (16:32 -0400)
1  2 
channels/drdynvc/CMakeLists.txt
channels/drdynvc/client/drdynvc_main.c
channels/drdynvc/client/drdynvc_types.h
channels/drdynvc/client/dvcman.c
libfreerdp/utils/CMakeLists.txt
libfreerdp/utils/msusb.c
libfreerdp/utils/svc_plugin.c

  # 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()
++
index 0000000,1807df1..801cc9c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,351 +1,367 @@@
 -              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)
index 0000000,5c9ce59..e19d0d1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,37 +1,38 @@@
+ /**
+  * 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
index 0000000,a8aed81..b23a50e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,446 +1,463 @@@
 -      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;
+ }
index 0000000,41b2c63..70ed640
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,85 +1,86 @@@
+ # 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")
index 0c918b8,0000000..0c918b8
mode 100644,000000..100644
--- /dev/null
index 0000000,0506c2b..7b95e43
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,438 +1,442 @@@
 -      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;
+ }
++