server/shadow: Integrate comment from @hardening: use switch to handle different...
[platform/upstream/freerdp.git] / server / shadow / shadow_client.c
index 96aea63..17c36fa 100644 (file)
@@ -26,6 +26,7 @@
 #include <winpr/synch.h>
 #include <winpr/thread.h>
 #include <winpr/sysinfo.h>
+#include <winpr/interlocked.h>
 
 #include <freerdp/log.h>
 
 
 #define TAG CLIENT_TAG("shadow")
 
-void shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
+static void shadow_client_free_queued_message(void *obj)
+{
+       wMessage *message = (wMessage*)obj;
+       if (message->Free)
+       {
+               message->Free(message);
+               message->Free = NULL;
+       }
+}
+
+BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
 {
        rdpSettings* settings;
        rdpShadowServer* server;
+       const wObject cb = { NULL, NULL, NULL, shadow_client_free_queued_message, NULL };
 
        server = (rdpShadowServer*) peer->ContextExtra;
        client->server = server;
@@ -50,31 +62,76 @@ void shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client)
        settings->BitmapCacheV3Enabled = TRUE;
        settings->FrameMarkerCommandEnabled = TRUE;
        settings->SurfaceFrameMarkerEnabled = TRUE;
+       settings->SupportGraphicsPipeline = FALSE;
+
+       settings->DrawAllowSkipAlpha = TRUE;
+       settings->DrawAllowColorSubsampling = TRUE;
+       settings->DrawAllowDynamicColorFidelity = TRUE;
+
+       settings->CompressionLevel = PACKET_COMPR_TYPE_RDP6;
 
        settings->RdpSecurity = TRUE;
        settings->TlsSecurity = TRUE;
        settings->NlaSecurity = FALSE;
 
-       settings->CertificateFile = _strdup(server->CertificateFile);
-       settings->PrivateKeyFile = _strdup(server->PrivateKeyFile);
+       if (!(settings->CertificateFile = _strdup(server->CertificateFile)))
+               goto fail_cert_file;
+       if (!(settings->PrivateKeyFile = _strdup(server->PrivateKeyFile)))
+               goto fail_privkey_file;
+       if (!(settings->RdpKeyFile = _strdup(settings->PrivateKeyFile)))
+               goto fail_rdpkey_file;
+
 
-       settings->RdpKeyFile = _strdup(settings->PrivateKeyFile);
+       if (server->ipcSocket)
+       {
+               settings->LyncRdpMode = TRUE;
+               settings->CompressionEnabled = FALSE;
+       }
 
        client->inLobby = TRUE;
        client->mayView = server->mayView;
        client->mayInteract = server->mayInteract;
 
-       InitializeCriticalSectionAndSpinCount(&(client->lock), 4000);
+       if (!InitializeCriticalSectionAndSpinCount(&(client->lock), 4000))
+               goto fail_client_lock;
 
        region16_init(&(client->invalidRegion));
 
        client->vcm = WTSOpenServerA((LPSTR) peer->context);
+       if (!client->vcm || client->vcm == INVALID_HANDLE_VALUE)
+               goto fail_open_server;
 
-       client->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (!(client->MsgQueue = MessageQueue_New(&cb)))
+               goto fail_message_queue;
 
-       client->encoder = shadow_encoder_new(client);
+       if (!(client->encoder = shadow_encoder_new(client)))
+               goto fail_encoder_new;
 
-       ArrayList_Add(server->clients, (void*) client);
+       if (ArrayList_Add(server->clients, (void*) client) >= 0)
+               return TRUE;
+
+       shadow_encoder_free(client->encoder);
+       client->encoder = NULL;
+fail_encoder_new:
+       MessageQueue_Free(client->MsgQueue);
+       client->MsgQueue = NULL;
+fail_message_queue:
+       WTSCloseServer((HANDLE) client->vcm);
+       client->vcm = NULL;
+fail_open_server:
+       DeleteCriticalSection(&(client->lock));
+fail_client_lock:
+       free(settings->RdpKeyFile);
+       settings->RdpKeyFile = NULL;
+fail_rdpkey_file:
+       free(settings->PrivateKeyFile);
+       settings->PrivateKeyFile = NULL;
+fail_privkey_file:
+       free(settings->CertificateFile);
+       settings->CertificateFile = NULL;
+fail_cert_file:
+
+       return FALSE;
 }
 
 void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
@@ -89,7 +146,9 @@ void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
 
        WTSCloseServer((HANDLE) client->vcm);
 
-       CloseHandle(client->StopEvent);
+    /* Clear queued messages and free resource */
+       MessageQueue_Clear(client->MsgQueue);
+       MessageQueue_Free(client->MsgQueue);
 
        if (client->lobby)
        {
@@ -106,17 +165,21 @@ void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client)
 
 void shadow_client_message_free(wMessage* message)
 {
-       if (message->id == SHADOW_MSG_IN_REFRESH_OUTPUT_ID)
+       switch(message->id)
        {
-               SHADOW_MSG_IN_REFRESH_OUTPUT* wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam;
+               case SHADOW_MSG_IN_REFRESH_OUTPUT_ID:
+                       free(((SHADOW_MSG_IN_REFRESH_OUTPUT*)message->wParam)->rects);
+                       free(message->wParam);
+                       break;
 
-               free(wParam->rects);
-               free(wParam);
-       }
-       else if (message->id == SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID)
-       {
-               SHADOW_MSG_IN_SUPPRESS_OUTPUT* wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam;
-               free(wParam);
+               case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID:
+                       free(message->wParam);
+                       break;
+
+               default:
+                       WLog_ERR(TAG, "Unknown message id: %u", message->id);
+                       free(message->wParam);
+                       break;
        }
 }
 
@@ -127,16 +190,18 @@ BOOL shadow_client_capabilities(freerdp_peer* peer)
 
 BOOL shadow_client_post_connect(freerdp_peer* peer)
 {
+       int authStatus;
        int width, height;
        rdpSettings* settings;
        rdpShadowClient* client;
-       rdpShadowSurface* lobby;
        rdpShadowServer* server;
        RECTANGLE_16 invalidRect;
+       rdpShadowSubsystem* subsystem;
 
        client = (rdpShadowClient*) peer->context;
        settings = peer->settings;
        server = client->server;
+       subsystem = server->subsystem;
 
        if (!server->shareSubRect)
        {
@@ -155,6 +220,9 @@ BOOL shadow_client_post_connect(freerdp_peer* peer)
        if (settings->ColorDepth == 24)
                settings->ColorDepth = 16; /* disable 24bpp */
 
+       if (settings->MultifragMaxRequestSize < 0x3F0000)
+               settings->NSCodec = FALSE; /* NSCodec compressor does not support fragmentation yet */
+
        WLog_ERR(TAG, "Client from %s is activated (%dx%d@%d)",
                        peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
 
@@ -169,29 +237,45 @@ BOOL shadow_client_post_connect(freerdp_peer* peer)
 
        region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &invalidRect);
 
-       lobby = client->lobby = shadow_surface_new(client->server, 0, 0, width, height);
+       shadow_client_init_lobby(client);
 
-       if (!client->lobby)
-               return FALSE;
+       authStatus = -1;
+
+       if (settings->Username && settings->Password)
+               settings->AutoLogonEnabled = TRUE;
 
-       freerdp_image_fill(lobby->data, PIXEL_FORMAT_XRGB32, lobby->scanline,
-                       0, 0, lobby->width, lobby->height, 0x3BB9FF);
+       if (settings->AutoLogonEnabled && server->authentication)
+       {
+               if (subsystem->Authenticate)
+               {
+                       authStatus = subsystem->Authenticate(subsystem,
+                                       settings->Username, settings->Domain, settings->Password);
+               }
+       }
 
-       region16_union_rect(&(lobby->invalidRegion), &(lobby->invalidRegion), &invalidRect);
+       if (server->authentication)
+       {
+               if (authStatus < 0)
+               {
+                       WLog_ERR(TAG, "client authentication failure: %d", authStatus);
+                       return FALSE;
+               }
+       }
 
        return TRUE;
 }
 
-void shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
+BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas)
 {
        wMessage message = { 0 };
        SHADOW_MSG_IN_REFRESH_OUTPUT* wParam;
        wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
 
-       wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT));
+       if (count && !areas)
+               return FALSE;
 
-       if (!wParam)
-               return;
+       if (!(wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT))))
+               return FALSE;
 
        wParam->numRects = (UINT32) count;
 
@@ -200,10 +284,13 @@ void shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_1
                wParam->rects = (RECTANGLE_16*) calloc(wParam->numRects, sizeof(RECTANGLE_16));
 
                if (!wParam->rects)
-                       return;
-       }
+               {
+                       free (wParam);
+                       return FALSE;
+               }
 
-       CopyMemory(wParam->rects, areas, wParam->numRects * sizeof(RECTANGLE_16));
+               CopyMemory(wParam->rects, areas, wParam->numRects * sizeof(RECTANGLE_16));
+       }
 
        message.id = SHADOW_MSG_IN_REFRESH_OUTPUT_ID;
        message.wParam = (void*) wParam;
@@ -211,19 +298,18 @@ void shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_1
        message.context = (void*) client;
        message.Free = shadow_client_message_free;
 
-       MessageQueue_Dispatch(MsgPipe->In, &message);
+       return MessageQueue_Dispatch(MsgPipe->In, &message);
 }
 
-void shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
+BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area)
 {
        wMessage message = { 0 };
        SHADOW_MSG_IN_SUPPRESS_OUTPUT* wParam;
        wMessagePipe* MsgPipe = client->subsystem->MsgPipe;
 
        wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_SUPPRESS_OUTPUT));
-
        if (!wParam)
-               return;
+               return FALSE;
 
        wParam->allow = (UINT32) allow;
 
@@ -236,26 +322,35 @@ void shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGL
        message.context = (void*) client;
        message.Free = shadow_client_message_free;
 
-       MessageQueue_Dispatch(MsgPipe->In, &message);
+       return MessageQueue_Dispatch(MsgPipe->In, &message);
 }
 
 BOOL shadow_client_activate(freerdp_peer* peer)
 {
-       rdpShadowClient* client;
+       rdpSettings* settings = peer->settings;
+       rdpShadowClient* client = (rdpShadowClient*) peer->context;
 
-       client = (rdpShadowClient*) peer->context;
+       if (settings->ClientDir && (strcmp(settings->ClientDir, "librdp") == 0))
+       {
+               /* Hack for Mac/iOS/Android Microsoft RDP clients */
+
+               settings->RemoteFxCodec = FALSE;
+
+               settings->NSCodec = FALSE;
+               settings->NSCodecAllowSubsampling = FALSE;
+
+               settings->SurfaceFrameMarkerEnabled = FALSE;
+       }
 
        client->activated = TRUE;
        client->inLobby = client->mayView ? FALSE : TRUE;
 
        shadow_encoder_reset(client->encoder);
 
-       shadow_client_refresh_rect(client, 0, NULL);
-
-       return TRUE;
+       return shadow_client_refresh_rect(client, 0, NULL);
 }
 
-void shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
+BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
 {
        SURFACE_FRAME* frame;
        wListDictionary* frameList;
@@ -268,6 +363,7 @@ void shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 fra
                ListDictionary_Remove(frameList, (void*) (size_t) frameId);
                free(frame);
        }
+       return TRUE;
 }
 
 int shadow_client_send_surface_frame_marker(rdpShadowClient* client, UINT32 action, UINT32 id)
@@ -333,6 +429,9 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s
        {
                RFX_RECT rect;
                RFX_MESSAGE* messages;
+               RFX_RECT *messageRects = NULL;
+
+               shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX);
 
                s = encoder->bs;
 
@@ -341,9 +440,12 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s
                rect.width = nWidth;
                rect.height = nHeight;
 
-               messages = rfx_encode_messages(encoder->rfx, &rect, 1, pSrcData,
+               if (!(messages = rfx_encode_messages(encoder->rfx, &rect, 1, pSrcData,
                                surface->width, surface->height, nSrcStep, &numMessages,
-                               settings->MultifragMaxRequestSize);
+                               settings->MultifragMaxRequestSize)))
+               {
+                       return 0;
+               }
 
                cmd.codecID = settings->RemoteFxCodecId;
 
@@ -355,11 +457,22 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s
                cmd.bpp = 32;
                cmd.width = surface->width;
                cmd.height = surface->height;
+               cmd.skipCompression = TRUE;
+
+               if (numMessages > 0)
+                       messageRects = messages[0].rects;
 
                for (i = 0; i < numMessages; i++)
                {
                        Stream_SetPosition(s, 0);
-                       rfx_write_message(encoder->rfx, s, &messages[i]);
+                       if (!rfx_write_message(encoder->rfx, s, &messages[i]))
+                       {
+                               while (i < numMessages)
+                               {
+                                       rfx_message_free(encoder->rfx, &messages[i++]);
+                               }
+                               break;
+                       }
                        rfx_message_free(encoder->rfx, &messages[i]);
 
                        cmd.bitmapDataLength = Stream_GetPosition(s);
@@ -374,48 +487,39 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s
                                IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId);
                }
 
+               free(messageRects);
                free(messages);
        }
        else if (settings->NSCodec)
        {
-               NSC_MESSAGE* messages;
+               shadow_encoder_prepare(encoder, FREERDP_CODEC_NSCODEC);
 
                s = encoder->bs;
+               Stream_SetPosition(s, 0);
 
-               messages = nsc_encode_messages(encoder->nsc, pSrcData,
-                               nXSrc, nYSrc, nWidth, nHeight, nSrcStep,
-                               &numMessages, settings->MultifragMaxRequestSize);
+               pSrcData = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
+
+               nsc_compose_message(encoder->nsc, s, pSrcData, nWidth, nHeight, nSrcStep);
 
                cmd.bpp = 32;
                cmd.codecID = settings->NSCodecId;
-
-               for (i = 0; i < numMessages; i++)
-               {
-                       Stream_SetPosition(s, 0);
-
-                       nsc_write_message(encoder->nsc, s, &messages[i]);
-                       nsc_message_free(encoder->nsc, &messages[i]);
-
-                       cmd.destLeft = messages[i].x;
-                       cmd.destTop = messages[i].y;
-                       cmd.destRight = messages[i].x + messages[i].width;
-                       cmd.destBottom = messages[i].y + messages[i].height;
-                       cmd.width = messages[i].width;
-                       cmd.height = messages[i].height;
-
-                       cmd.bitmapDataLength = Stream_GetPosition(s);
-                       cmd.bitmapData = Stream_Buffer(s);
-
-                       first = (i == 0) ? TRUE : FALSE;
-                       last = ((i + 1) == numMessages) ? TRUE : FALSE;
-
-                       if (!encoder->frameAck)
-                               IFCALL(update->SurfaceBits, update->context, &cmd);
-                       else
-                               IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId);
-               }
-
-               free(messages);
+               cmd.destLeft = nXSrc;
+               cmd.destTop = nYSrc;
+               cmd.destRight = cmd.destLeft + nWidth;
+               cmd.destBottom = cmd.destTop + nHeight;
+               cmd.width = nWidth;
+               cmd.height = nHeight;
+
+               cmd.bitmapDataLength = Stream_GetPosition(s);
+               cmd.bitmapData = Stream_Buffer(s);
+
+               first = TRUE;
+               last = TRUE;
+
+               if (!encoder->frameAck)
+                       IFCALL(update->SurfaceBits, update->context, &cmd);
+               else
+                       IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId);
        }
 
        return 1;
@@ -425,18 +529,19 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface*
 {
        BYTE* data;
        BYTE* buffer;
-       int i, j, k;
-       wStream* s;
-       wStream* ts;
-       int e, lines;
+       int yIdx, xIdx, k;
        int rows, cols;
        int nSrcStep;
        BYTE* pSrcData;
+       UINT32 DstSize;
+       UINT32 SrcFormat;
+       BITMAP_DATA* bitmap;
        rdpUpdate* update;
        rdpContext* context;
        rdpSettings* settings;
-       int MaxRegionWidth;
-       int MaxRegionHeight;
+       UINT32 maxUpdateSize;
+       UINT32 totalBitmapSize;
+       UINT32 updateSizeEstimate;
        BITMAP_DATA* bitmapData;
        BITMAP_UPDATE bitmapUpdate;
        rdpShadowServer* server;
@@ -449,11 +554,16 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface*
        server = client->server;
        encoder = client->encoder;
 
+       maxUpdateSize = settings->MultifragMaxRequestSize;
+
+       if (settings->ColorDepth < 32)
+               shadow_encoder_prepare(encoder, FREERDP_CODEC_INTERLEAVED);
+       else
+               shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR);
+
        pSrcData = surface->data;
        nSrcStep = surface->scanline;
-
-       MaxRegionWidth = 64 * 4;
-       MaxRegionHeight = 64 * 1;
+       SrcFormat = PIXEL_FORMAT_RGB32;
 
        if ((nXSrc % 4) != 0)
        {
@@ -467,39 +577,12 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface*
                nYSrc -= (nYSrc % 4);
        }
 
-       if ((nWidth * nHeight) > (MaxRegionWidth * MaxRegionHeight))
-       {
-               int nXSrcSub;
-               int nYSrcSub;
-               int nWidthSub;
-               int nHeightSub;
-               rows = (nWidth + (MaxRegionWidth - (nWidth % MaxRegionWidth))) / MaxRegionWidth;
-               cols = (nHeight + (MaxRegionHeight - (nHeight % MaxRegionHeight))) / MaxRegionHeight;
-
-               for (i = 0; i < rows; i++)
-               {
-                       for (j = 0; j < cols; j++)
-                       {
-                               nXSrcSub = nXSrc + (i * MaxRegionWidth);
-                               nYSrcSub = nYSrc + (j * MaxRegionHeight);
-
-                               nWidthSub = (i < (rows - 1)) ? MaxRegionWidth : nWidth - (i * MaxRegionWidth);
-                               nHeightSub = (j < (cols - 1)) ? MaxRegionHeight : nHeight - (j * MaxRegionHeight);
-
-                               if ((nWidthSub * nHeightSub) > 0)
-                               {
-                                       shadow_client_send_bitmap_update(client, surface, nXSrcSub, nYSrcSub, nWidthSub, nHeightSub);
-                               }
-                       }
-               }
-
-               return 1;
-       }
-
-       rows = (nWidth + (64 - (nWidth % 64))) / 64;
-       cols = (nHeight + (64 - (nHeight % 64))) / 64;
+       rows = (nHeight / 64) + ((nHeight % 64) ? 1 : 0);
+       cols = (nWidth / 64) + ((nWidth % 64) ? 1 : 0);
 
        k = 0;
+       totalBitmapSize = 0;
+
        bitmapUpdate.count = bitmapUpdate.number = rows * cols;
        bitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * bitmapUpdate.number);
        bitmapUpdate.rectangles = bitmapData;
@@ -519,108 +602,119 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface*
                nHeight += (nHeight % 4);
        }
 
-       for (i = 0; i < rows; i++)
+       for (yIdx = 0; yIdx < rows; yIdx++)
        {
-               for (j = 0; j < cols; j++)
+               for (xIdx = 0; xIdx < cols; xIdx++)
                {
-                       nWidth = (i < (rows - 1)) ? 64 : nWidth - (i * 64);
-                       nHeight = (j < (cols - 1)) ? 64 : nHeight - (j * 64);
-
-                       bitmapData[k].bitsPerPixel = 16;
-                       bitmapData[k].width = nWidth;
-                       bitmapData[k].height = nHeight;
-                       bitmapData[k].destLeft = nXSrc + (i * 64);
-                       bitmapData[k].destTop = nYSrc + (j * 64);
-                       bitmapData[k].destRight = bitmapData[k].destLeft + nWidth - 1;
-                       bitmapData[k].destBottom = bitmapData[k].destTop + nHeight - 1;
-                       bitmapData[k].compressed = TRUE;
-
-                       if (((nWidth * nHeight) > 0) && (nWidth >= 4) && (nHeight >= 4))
-                       {
-                               UINT32 srcFormat = PIXEL_FORMAT_RGB32;
+                       bitmap = &bitmapData[k];
 
-                               e = nWidth % 4;
+                       bitmap->width = 64;
+                       bitmap->height = 64;
+                       bitmap->destLeft = nXSrc + (xIdx * 64);
+                       bitmap->destTop = nYSrc + (yIdx * 64);
 
-                               if (e != 0)
-                                       e = 4 - e;
+                       if ((bitmap->destLeft + bitmap->width) > (nXSrc + nWidth))
+                               bitmap->width = (nXSrc + nWidth) - bitmap->destLeft;
 
-                               s = encoder->bs;
-                               ts = encoder->bts;
+                       if ((bitmap->destTop + bitmap->height) > (nYSrc + nHeight))
+                               bitmap->height = (nYSrc + nHeight) - bitmap->destTop;
 
-                               Stream_SetPosition(s, 0);
-                               Stream_SetPosition(ts, 0);
+                       bitmap->destRight = bitmap->destLeft + bitmap->width - 1;
+                       bitmap->destBottom = bitmap->destTop + bitmap->height - 1;
+                       bitmap->compressed = TRUE;
 
-                               data = surface->data;
-                               data = &data[(bitmapData[k].destTop * nSrcStep) +
-                                            (bitmapData[k].destLeft * 4)];
+                       if ((bitmap->width < 4) || (bitmap->height < 4))
+                               continue;
 
-                               srcFormat = PIXEL_FORMAT_RGB32;
+                       if (settings->ColorDepth < 32)
+                       {
+                               int bitsPerPixel = settings->ColorDepth;
+                               int bytesPerPixel = (bitsPerPixel + 7) / 8;
 
-                               if (settings->ColorDepth > 24)
-                               {
-                                       int dstSize;
+                               DstSize = 64 * 64 * 4;
+                               buffer = encoder->grid[k];
 
-                                       buffer = encoder->grid[k];
+                               interleaved_compress(encoder->interleaved, buffer, &DstSize, bitmap->width, bitmap->height,
+                                               pSrcData, SrcFormat, nSrcStep, bitmap->destLeft, bitmap->destTop, NULL, bitsPerPixel);
 
-                                       buffer = freerdp_bitmap_compress_planar(encoder->planar,
-                                                       data, srcFormat, nWidth, nHeight, nSrcStep, buffer, &dstSize);
+                               bitmap->bitmapDataStream = buffer;
+                               bitmap->bitmapLength = DstSize;
+                               bitmap->bitsPerPixel = bitsPerPixel;
+                               bitmap->cbScanWidth = bitmap->width * bytesPerPixel;
+                               bitmap->cbUncompressedSize = bitmap->width * bitmap->height * bytesPerPixel;
+                       }
+                       else
+                       {
+                               int dstSize;
 
-                                       bitmapData[k].bitmapDataStream = buffer;
-                                       bitmapData[k].bitmapLength = dstSize;
+                               buffer = encoder->grid[k];
+                               data = &pSrcData[(bitmap->destTop * nSrcStep) + (bitmap->destLeft * 4)];
 
-                                       bitmapData[k].bitsPerPixel = 32;
-                                       bitmapData[k].cbScanWidth = nWidth * 4;
-                                       bitmapData[k].cbUncompressedSize = nWidth * nHeight * 4;
-                               }
-                               else
-                               {
-                                       int bytesPerPixel = 2;
-                                       UINT32 dstFormat = PIXEL_FORMAT_RGB16;
+                               buffer = freerdp_bitmap_compress_planar(encoder->planar, data, SrcFormat,
+                                               bitmap->width, bitmap->height, nSrcStep, buffer, &dstSize);
 
-                                       if (settings->ColorDepth == 15)
-                                       {
-                                               bytesPerPixel = 2;
-                                               dstFormat = PIXEL_FORMAT_RGB15;
-                                       }
-                                       else if (settings->ColorDepth == 24)
-                                       {
-                                               bytesPerPixel = 3;
-                                               dstFormat = PIXEL_FORMAT_XRGB32;
-                                       }
+                               bitmap->bitmapDataStream = buffer;
+                               bitmap->bitmapLength = dstSize;
+                               bitmap->bitsPerPixel = 32;
+                               bitmap->cbScanWidth = bitmap->width * 4;
+                               bitmap->cbUncompressedSize = bitmap->width * bitmap->height * 4;
+                       }
 
-                                       buffer = encoder->grid[k];
+                       bitmap->cbCompFirstRowSize = 0;
+                       bitmap->cbCompMainBodySize = bitmap->bitmapLength;
 
-                                       freerdp_image_copy(buffer, dstFormat, -1, 0, 0, nWidth, nHeight,
-                                                       data, srcFormat, nSrcStep, 0, 0, NULL);
+                       totalBitmapSize += bitmap->bitmapLength;
+                       k++;
+               }
+       }
 
-                                       lines = freerdp_bitmap_compress((char*) buffer, nWidth, nHeight, s,
-                                                       settings->ColorDepth, 64 * 64 * 4, nHeight - 1, ts, e);
+       bitmapUpdate.count = bitmapUpdate.number = k;
 
-                                       Stream_SealLength(s);
+       updateSizeEstimate = totalBitmapSize + (k * bitmapUpdate.count) + 16;
 
-                                       bitmapData[k].bitmapDataStream = Stream_Buffer(s);
-                                       bitmapData[k].bitmapLength = Stream_Length(s);
+       if (updateSizeEstimate > maxUpdateSize)
+       {
+               UINT32 i, j;
+               UINT32 updateSize;
+               UINT32 newUpdateSize;
+               BITMAP_DATA* fragBitmapData;
 
-                                       buffer = encoder->grid[k];
-                                       CopyMemory(buffer, bitmapData[k].bitmapDataStream, bitmapData[k].bitmapLength);
-                                       bitmapData[k].bitmapDataStream = buffer;
+               fragBitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * k);
+               bitmapUpdate.rectangles = fragBitmapData;
 
-                                       bitmapData[k].bitsPerPixel = settings->ColorDepth;
-                                       bitmapData[k].cbScanWidth = nWidth * bytesPerPixel;
-                                       bitmapData[k].cbUncompressedSize = nWidth * nHeight * bytesPerPixel;
-                               }
+               i = j = 0;
+               updateSize = 1024;
 
-                               bitmapData[k].cbCompFirstRowSize = 0;
-                               bitmapData[k].cbCompMainBodySize = bitmapData[k].bitmapLength;
+               while (i < k)
+               {
+                       newUpdateSize = updateSize + (bitmapData[i].bitmapLength + 16);
 
-                               k++;
+                       if ((newUpdateSize < maxUpdateSize) && ((i + 1) < k))
+                       {
+                               CopyMemory(&fragBitmapData[j++], &bitmapData[i++], sizeof(BITMAP_DATA));
+                               updateSize = newUpdateSize;
+                       }
+                       else
+                       {
+                               if ((i + 1) >= k)
+                               {
+                                       CopyMemory(&fragBitmapData[j++], &bitmapData[i++], sizeof(BITMAP_DATA));
+                                       updateSize = newUpdateSize;
+                               }
+                               
+                               bitmapUpdate.count = bitmapUpdate.number = j;
+                               IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
+                               updateSize = 1024;
+                               j = 0;
                        }
                }
-       }
 
-       bitmapUpdate.count = bitmapUpdate.number = k;
-
-       IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
+               free(fragBitmapData);
+       }
+       else
+       {
+               IFCALL(update->BitmapUpdate, context, &bitmapUpdate);
+       }
 
        free(bitmapData);
 
@@ -686,17 +780,10 @@ int shadow_client_send_surface_update(rdpShadowClient* client)
 
        if (settings->RemoteFxCodec || settings->NSCodec)
        {
-               if (settings->RemoteFxCodec)
-                       shadow_encoder_prepare(encoder, SHADOW_CODEC_REMOTEFX);
-               else if (settings->NSCodec)
-                       shadow_encoder_prepare(encoder, SHADOW_CODEC_NSCODEC);
-
                status = shadow_client_send_surface_bits(client, surface, nXSrc, nYSrc, nWidth, nHeight);
        }
        else
        {
-               shadow_encoder_prepare(encoder, SHADOW_CODEC_BITMAP);
-
                status = shadow_client_send_bitmap_update(client, surface, nXSrc, nYSrc, nWidth, nHeight);
        }
 
@@ -725,14 +812,188 @@ int shadow_client_surface_update(rdpShadowClient* client, REGION16* region)
        return 1;
 }
 
+int shadow_client_convert_alpha_pointer_data(BYTE* pixels, BOOL premultiplied,
+               UINT32 width, UINT32 height, POINTER_COLOR_UPDATE* pointerColor)
+{
+       UINT32 x, y;
+       BYTE* pSrc8;
+       BYTE* pDst8;
+       int xorStep;
+       int andStep;
+       UINT32 andBit;
+       BYTE* andBits;
+       UINT32 andPixel;
+       BYTE A, R, G, B;
+
+       xorStep = (width * 3);
+       xorStep += (xorStep % 2);
+
+       andStep = ((width + 7) / 8);
+       andStep += (andStep % 2);
+
+       pointerColor->lengthXorMask = height * xorStep;
+       pointerColor->xorMaskData = (BYTE*) calloc(1, pointerColor->lengthXorMask);
+
+       if (!pointerColor->xorMaskData)
+               return -1;
+
+       pointerColor->lengthAndMask = height * andStep;
+       pointerColor->andMaskData = (BYTE*) calloc(1, pointerColor->lengthAndMask);
+
+       if (!pointerColor->andMaskData)
+               return -1;
+
+       for (y = 0; y < height; y++)
+       {
+               pSrc8 = &pixels[(width * 4) * (height - 1 - y)];
+               pDst8 = &(pointerColor->xorMaskData[y * xorStep]);
+
+               andBit = 0x80;
+               andBits = &(pointerColor->andMaskData[andStep * y]);
+
+               for (x = 0; x < width; x++)
+               {
+                       B = *pSrc8++;
+                       G = *pSrc8++;
+                       R = *pSrc8++;
+                       A = *pSrc8++;
+
+                       andPixel = 0;
+
+                       if (A < 64)
+                               A = 0; /* pixel cannot be partially transparent */
+
+                       if (!A)
+                       {
+                               /* transparent pixel: XOR = black, AND = 1 */
+                               andPixel = 1;
+                               B = G = R = 0;
+                       }
+                       else
+                       {
+                               if (premultiplied)
+                               {
+                                       B = (B * 0xFF ) / A;
+                                       G = (G * 0xFF ) / A;
+                                       R = (R * 0xFF ) / A;
+                               }
+                       }
+
+                       *pDst8++ = B;
+                       *pDst8++ = G;
+                       *pDst8++ = R;
+
+                       if (andPixel) *andBits |= andBit;
+                       if (!(andBit >>= 1)) { andBits++; andBit = 0x80; }
+               }
+       }
+
+       return 1;
+}
+
+int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message)
+{
+       rdpContext* context = (rdpContext*) client;
+       rdpUpdate* update = context->update;
+
+       /* FIXME: the pointer updates appear to be broken when used with bulk compression and mstsc */
+
+       switch(message->id)
+       {
+               case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+               {
+                       POINTER_POSITION_UPDATE pointerPosition;
+                       SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg = (SHADOW_MSG_OUT_POINTER_POSITION_UPDATE*) message->wParam;
+
+                       pointerPosition.xPos = msg->xPos;
+                       pointerPosition.yPos = msg->yPos;
+
+                       if (client->activated)
+                       {
+                               if ((msg->xPos != client->pointerX) || (msg->yPos != client->pointerY))
+                               {
+                                       IFCALL(update->pointer->PointerPosition, context, &pointerPosition);
+
+                                       client->pointerX = msg->xPos;
+                                       client->pointerY = msg->yPos;
+                               }
+                       }
+                       break;
+               }
+               case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+               {
+                       POINTER_NEW_UPDATE pointerNew;
+                       POINTER_COLOR_UPDATE* pointerColor;
+                       POINTER_CACHED_UPDATE pointerCached;
+                       SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*) message->wParam;
+
+                       ZeroMemory(&pointerNew, sizeof(POINTER_NEW_UPDATE));
+
+                       pointerNew.xorBpp = 24;
+                       pointerColor = &(pointerNew.colorPtrAttr);
+
+                       pointerColor->cacheIndex = 0;
+                       pointerColor->xPos = msg->xHot;
+                       pointerColor->yPos = msg->yHot;
+                       pointerColor->width = msg->width;
+                       pointerColor->height = msg->height;
+
+                       pointerCached.cacheIndex = pointerColor->cacheIndex;
+
+                       if (client->activated)
+                       {
+                               shadow_client_convert_alpha_pointer_data(msg->pixels, msg->premultiplied,
+                                               msg->width, msg->height, pointerColor);
+
+                               IFCALL(update->pointer->PointerNew, context, &pointerNew);
+                               IFCALL(update->pointer->PointerCached, context, &pointerCached);
+
+                               free(pointerColor->xorMaskData);
+                               free(pointerColor->andMaskData);
+                       }
+                       break;
+               }
+               case SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES_ID:
+               {
+                       SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES* msg = (SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES*) message->wParam;
+                       if (client->activated && client->rdpsnd && client->rdpsnd->Activated)
+                       {
+                               client->rdpsnd->src_format = msg->audio_format;
+                               IFCALL(client->rdpsnd->SendSamples, client->rdpsnd, msg->buf, msg->nFrames, msg->wTimestamp);
+                       }
+                       break;
+               }
+               case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID:
+               {
+                       SHADOW_MSG_OUT_AUDIO_OUT_VOLUME* msg = (SHADOW_MSG_OUT_AUDIO_OUT_VOLUME*) message->wParam;
+                       if (client->activated && client->rdpsnd && client->rdpsnd->Activated)
+                       {
+                               IFCALL(client->rdpsnd->SetVolume, client->rdpsnd, msg->left, msg->right);
+                       }
+                       break;
+               }
+               default:
+                       WLog_ERR(TAG, "Unknown message id: %u", message->id);
+                       break;
+       }
+
+       shadow_client_free_queued_message(message);
+
+       return 1;
+}
+
 void* shadow_client_thread(rdpShadowClient* client)
 {
        DWORD status;
        DWORD nCount;
+       wMessage message;
+       wMessage pointerPositionMsg;
+       wMessage pointerAlphaMsg;
+       wMessage audioVolumeMsg;
        HANDLE events[32];
-       HANDLE StopEvent;
        HANDLE ClientEvent;
        HANDLE ChannelEvent;
+       void* UpdateSubscriber;
        HANDLE UpdateEvent;
        freerdp_peer* peer;
        rdpContext* context;
@@ -741,6 +1002,7 @@ void* shadow_client_thread(rdpShadowClient* client)
        rdpShadowScreen* screen;
        rdpShadowEncoder* encoder;
        rdpShadowSubsystem* subsystem;
+       wMessageQueue* MsgQueue = client->MsgQueue;
 
        server = client->server;
        screen = server->screen;
@@ -759,35 +1021,31 @@ void* shadow_client_thread(rdpShadowClient* client)
 
        peer->Initialize(peer);
 
-       peer->update->RefreshRect = (pRefreshRect) shadow_client_refresh_rect;
-       peer->update->SuppressOutput = (pSuppressOutput) shadow_client_suppress_output;
-       peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge) shadow_client_surface_frame_acknowledge;
+       peer->update->RefreshRect = (pRefreshRect)shadow_client_refresh_rect;
+       peer->update->SuppressOutput = (pSuppressOutput)shadow_client_suppress_output;
+       peer->update->SurfaceFrameAcknowledge = (pSurfaceFrameAcknowledge)shadow_client_surface_frame_acknowledge;
 
-       StopEvent = client->StopEvent;
-       UpdateEvent = subsystem->updateEvent;
+       if ((!client->vcm) || (!subsystem->updateEvent))
+               goto out;
+
+       UpdateSubscriber = shadow_multiclient_get_subscriber(subsystem->updateEvent);
+       if (!UpdateSubscriber)
+               goto out;
+
+       UpdateEvent = shadow_multiclient_getevent(UpdateSubscriber);
        ClientEvent = peer->GetEventHandle(peer);
        ChannelEvent = WTSVirtualChannelManagerGetEventHandle(client->vcm);
 
        while (1)
        {
                nCount = 0;
-               events[nCount++] = StopEvent;
                events[nCount++] = UpdateEvent;
                events[nCount++] = ClientEvent;
                events[nCount++] = ChannelEvent;
+               events[nCount++] = MessageQueue_Event(MsgQueue);
 
                status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
 
-               if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
-               {
-                       if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
-                       {
-                               EnterSynchronizationBarrier(&(subsystem->barrier), 0);
-                       }
-
-                       break;
-               }
-
                if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
                {
                        if (client->activated)
@@ -806,9 +1064,11 @@ void* shadow_client_thread(rdpShadowClient* client)
                                shadow_client_send_surface_update(client);
                        }
 
-                       EnterSynchronizationBarrier(&(subsystem->barrier), 0);
-
-                       while (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0);
+                       /* 
+                        * The return value of shadow_multiclient_consume is whether or not the subscriber really consumes the event.
+                        * It's not cared currently.
+                        */
+                       (void)shadow_multiclient_consume(UpdateSubscriber);
                }
 
                if (WaitForSingleObject(ClientEvent, 0) == WAIT_OBJECT_0)
@@ -822,25 +1082,101 @@ void* shadow_client_thread(rdpShadowClient* client)
 
                if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0)
                {
-                       if (WTSVirtualChannelManagerCheckFileDescriptor(client->vcm) != TRUE)
+                       if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
                        {
                                WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure");
                                break;
                        }
                }
+
+               if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0)
+               {
+                       /* Drain messages. Pointer update could be accumulated. */
+                       pointerPositionMsg.id = 0;
+                       pointerPositionMsg.Free= NULL;
+                       pointerAlphaMsg.id = 0;
+                       pointerAlphaMsg.Free = NULL;
+                       audioVolumeMsg.id = 0;
+                       audioVolumeMsg.Free = NULL;
+                       while (MessageQueue_Peek(MsgQueue, &message, TRUE))
+                       {
+                               if (message.id == WMQ_QUIT)
+                               {
+                                       break;
+                               }
+
+                               switch(message.id)
+                               {
+                                       case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+                                               /* Abandon previous message */
+                                               shadow_client_free_queued_message(&pointerPositionMsg);
+                                               CopyMemory(&pointerPositionMsg, &message, sizeof(wMessage));
+                                               break;
+
+                                       case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+                                               /* Abandon previous message */
+                                               shadow_client_free_queued_message(&pointerAlphaMsg);
+                                               CopyMemory(&pointerAlphaMsg, &message, sizeof(wMessage));
+                                               break;
+
+                                       case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID:
+                                               /* Abandon previous message */
+                                               shadow_client_free_queued_message(&audioVolumeMsg);
+                                               CopyMemory(&audioVolumeMsg, &message, sizeof(wMessage));
+                                               break;
+
+                                       default:
+                                               shadow_client_subsystem_process_message(client, &message);
+                                               break;
+                               }
+                       }
+
+                       if (message.id == WMQ_QUIT)
+                       {
+                               /* Release stored message */
+                               shadow_client_free_queued_message(&pointerPositionMsg);
+                               shadow_client_free_queued_message(&pointerAlphaMsg);
+                               shadow_client_free_queued_message(&audioVolumeMsg);
+                               break;
+                       }
+                       else
+                       {
+                               /* Process accumulated messages if needed */
+                               if (pointerPositionMsg.id)
+                               {
+                                       shadow_client_subsystem_process_message(client, &pointerPositionMsg);
+                               }
+                               if (pointerAlphaMsg.id)
+                               {
+                                       shadow_client_subsystem_process_message(client, &pointerAlphaMsg);
+                               }
+                               if (audioVolumeMsg.id)
+                               {
+                                       shadow_client_subsystem_process_message(client, &audioVolumeMsg);
+                               }
+                       }
+               }
+       }
+
+       /* Free channels early because we establish channels in post connect */
+       shadow_client_channels_free(client);
+
+       if (UpdateSubscriber)
+       {
+               shadow_multiclient_release_subscriber(UpdateSubscriber);
+               UpdateSubscriber = NULL;
        }
 
+out:
        peer->Disconnect(peer);
        
        freerdp_peer_context_free(peer);
        freerdp_peer_free(peer);
-
        ExitThread(0);
-
        return NULL;
 }
 
-void shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
+BOOL shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
 {
        rdpShadowClient* client;
        rdpShadowServer* server;
@@ -851,10 +1187,117 @@ void shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
        peer->ContextSize = sizeof(rdpShadowClient);
        peer->ContextNew = (psPeerContextNew) shadow_client_context_new;
        peer->ContextFree = (psPeerContextFree) shadow_client_context_free;
-       freerdp_peer_context_new(peer);
+
+       if (!freerdp_peer_context_new(peer))
+               return FALSE;
 
        client = (rdpShadowClient*) peer->context;
 
-       client->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
-                       shadow_client_thread, client, 0, NULL);
+       if (!(client->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
+                       shadow_client_thread, client, 0, NULL)))
+       {
+               freerdp_peer_context_free(peer);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void shadow_msg_out_addref(wMessage* message)
+{
+       SHADOW_MSG_OUT* msg = (SHADOW_MSG_OUT *)message->wParam;
+       InterlockedIncrement(&(msg->refCount));
+}
+
+static void shadow_msg_out_release(wMessage* message)
+{
+       SHADOW_MSG_OUT* msg = (SHADOW_MSG_OUT *)message->wParam;
+       if (InterlockedDecrement(&(msg->refCount)) <= 0)
+       {
+               if (msg->Free)
+                       msg->Free(message->id, msg);
+       }
+}
+
+static BOOL shadow_client_dispatch_msg(rdpShadowClient* client, wMessage* message)
+{
+       /* Add reference when it is posted */
+       shadow_msg_out_addref(message);
+       if (MessageQueue_Dispatch(client->MsgQueue, message))
+       {
+               return TRUE;
+       }
+       else
+       {
+               /* Release the reference since post failed */
+               shadow_msg_out_release(message);
+               return FALSE;
+       }
+}
+
+BOOL shadow_client_post_msg(rdpShadowClient* client, void* context, UINT32 type, SHADOW_MSG_OUT* msg, void* lParam)
+{
+       wMessage message = {0};
+
+       message.context = context;
+       message.id = type;
+       message.wParam = (void *)msg;
+       message.lParam = lParam;
+       message.Free = shadow_msg_out_release;
+
+       return shadow_client_dispatch_msg(client, &message);
+}
+
+int shadow_client_boardcast_msg(rdpShadowServer* server, void* context, UINT32 type, SHADOW_MSG_OUT* msg, void* lParam)
+{
+       wMessage message = {0};
+       rdpShadowClient* client = NULL;
+       int count = 0;
+       int index = 0;
+
+       message.context = context;
+       message.id = type;
+       message.wParam = (void *)msg;
+       message.lParam = lParam;
+       message.Free = shadow_msg_out_release;
+
+       /* First add reference as we reference it in this function.
+     * Therefore it would not be free'ed during post. */
+       shadow_msg_out_addref(&message);
+
+       ArrayList_Lock(server->clients);
+       for (index = 0; index < ArrayList_Count(server->clients); index++)
+       {
+               client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
+               if (shadow_client_dispatch_msg(client, &message))
+               {
+                       count++;
+               }
+       }
+       ArrayList_Unlock(server->clients);
+
+    /* Release the reference for this function */
+       shadow_msg_out_release(&message);
+
+       return count;
+}
+
+int shadow_client_boardcast_quit(rdpShadowServer* server, int nExitCode)
+{
+       wMessageQueue* queue = NULL;
+       int count = 0;
+       int index = 0;
+
+       ArrayList_Lock(server->clients);
+       for (index = 0; index < ArrayList_Count(server->clients); index++)
+       {
+               queue = ((rdpShadowClient*)ArrayList_GetItem(server->clients, index))->MsgQueue;
+               if (MessageQueue_PostQuit(queue, nExitCode))
+               {
+                       count++;
+               }
+       }
+       ArrayList_Unlock(server->clients);
+
+       return count;
 }