/*
 * rfbserver.c - deal with server-side of the RFB protocol.
 */

/*
 *  Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
 *  Copyright (C) 2002 RealVNC Ltd.
 *  OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
 *  Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
 *  All Rights Reserved.
 *
 *  This is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */

#define _CRT_SECURE_NO_WARNINGS
#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#endif
#include <string.h>
#include <rfb/rfb.h>
#include <rfb/rfbregion.h>
#include "private.h"
#include "defines.h"

#include <shlwapi.h>
#include <Shlobj.h>

#ifdef LIBVNCSERVER_HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef WIN32
#define x_write(sock,buf,len) send(sock,buf,len,0)
#define S_ISDIR(m)	(((m)&_S_IFMT) == _S_IFDIR)
#else
#ifdef LIBVNCSERVER_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <pwd.h>
#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#endif
#endif

#ifdef DEBUGPROTO
#undef DEBUGPROTO
#define DEBUGPROTO(x) x
#else
#define DEBUGPROTO(x)
#endif
#include <stdarg.h>
#include <scale.h>
/* stst() */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* readdir() */
/* errno */
#include <errno.h>
/* strftime() */
#include <time.h>
#define CFF_RECURSE 0x1

static void rfbProcessClientProtocolVersion(rfbClientPtr cl);
static void rfbProcessClientNormalMessage(rfbClientPtr cl);
static void rfbProcessClientInitMessage(rfbClientPtr cl);

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
void rfbIncrClientRef(rfbClientPtr cl)
{
    LOCK(cl->refCountMutex);
    cl->refCount++;
    UNLOCK(cl->refCountMutex);
}

void rfbDecrClientRef(rfbClientPtr cl)
{
    LOCK(cl->refCountMutex);
    cl->refCount--;
    if(cl->refCount<=0) /* just to be sure also < 0 */
        TSIGNAL(cl->deleteCond);
    UNLOCK(cl->refCountMutex);
}
#else
void rfbIncrClientRef(rfbClientPtr cl) {}
void rfbDecrClientRef(rfbClientPtr cl) {}
#endif

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
static MUTEX(rfbClientListMutex);
#endif

struct rfbClientIterator
{
    rfbClientPtr next;
    rfbScreenInfoPtr screen;
    rfbBool closedToo;
};

void
rfbClientListInit(rfbScreenInfoPtr rfbScreen)
{
    /**if(sizeof(rfbBool)!=1) {
        /* a sanity check /
        fprintf(stderr,"rfbBool's size is not 1 (%d)!\n",(int)sizeof(rfbBool));
    /* we cannot continue, because rfbBool is supposed to be char everywhere /
    exit(1);
    }*/
    rfbScreen->clientHead = NULL;
    INIT_MUTEX(rfbClientListMutex);
}

rfbClientIteratorPtr
rfbGetClientIterator(rfbScreenInfoPtr rfbScreen)
{
    rfbClientIteratorPtr i =
        (rfbClientIteratorPtr)MemAlloc(sizeof(struct rfbClientIterator));
    if (i) {
        i->next = NULL;
        i->screen = rfbScreen;
        i->closedToo = FALSE;
    }
    return i;
}

rfbClientIteratorPtr
rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen)
{
    rfbClientIteratorPtr i =
        (rfbClientIteratorPtr)MemAlloc(sizeof(struct rfbClientIterator));
    if (i) {
        i->next = NULL;
        i->screen = rfbScreen;
        i->closedToo = TRUE;
    }
    return i;
}

rfbClientPtr
rfbClientIteratorHead(rfbClientIteratorPtr i)
{
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
    if(i->next != 0)
    {
        rfbDecrClientRef(i->next);
        rfbIncrClientRef(i->screen->clientHead);
    }
#endif
    LOCK(rfbClientListMutex);
    i->next = i->screen->clientHead;
    UNLOCK(rfbClientListMutex);
    return i->next;
}

rfbClientPtr
rfbClientIteratorNext(rfbClientIteratorPtr i)
{
    if (!i)
        return 0;
    if(i->next == 0)
    {
        LOCK(rfbClientListMutex);
        i->next = i->screen->clientHead;
        UNLOCK(rfbClientListMutex);
    }
    else
    {
        IF_PTHREADS(rfbClientPtr cl = i->next);
        i->next = i->next->next;
        IF_PTHREADS(rfbDecrClientRef(cl));
    }

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
    if(!i->closedToo)
        while(i->next && i->next->sock<0)
            i->next = i->next->next;
    if(i->next)
        rfbIncrClientRef(i->next);
#endif

    return i->next;
}

void
rfbReleaseClientIterator(rfbClientIteratorPtr iterator)
{
    IF_PTHREADS(if(iterator->next) rfbDecrClientRef(iterator->next));
    MemFree(iterator);
}


/*
 * rfbNewClientConnection is called from sockets.c when a new connection
 * comes in.
 */

void
rfbNewClientConnection(rfbScreenInfoPtr rfbScreen,
                       int sock)
{
    rfbClientPtr cl;

    cl = rfbNewClient(rfbScreen,sock);
#ifdef CORBA
    if(cl!=NULL)
        newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE),1,1,1);
#endif
}


/*
 * rfbReverseConnection is called to make an outward
 * connection to a "listening" RFB client.
 */

rfbClientPtr
rfbReverseConnection(rfbScreenInfoPtr rfbScreen,
                     char *host,
                     int port)
{
    int sock;
    rfbClientPtr cl;

    if ((sock = rfbConnect(rfbScreen, host, port)) < 0)
        return (rfbClientPtr)NULL;

    cl = rfbNewClient(rfbScreen, sock);

    if (cl)
    {
        cl->reverseConnection = TRUE;
    }

    return cl;
}


void
rfbSetProtocolVersion(rfbScreenInfoPtr rfbScreen, int major_, int minor_)
{
    /* Permit the server to set the version to report */
    /* TODO: sanity checking */
    if ((major_==3) && (minor_ > 2 && minor_ < 9))
    {
        rfbScreen->protocolMajorVersion = major_;
        rfbScreen->protocolMinorVersion = minor_;
    }
}

/*
 * rfbNewClient is called when a new connection has been made by whatever
 * means.
 */

static rfbClientPtr
rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
                     int sock,
                     rfbBool isUDP)
{
    rfbProtocolVersionMsg pv;
    rfbClientIteratorPtr iterator;
    rfbClientPtr cl,cl_;
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(struct sockaddr_in);
    rfbProtocolExtension* extension;

    cl = (rfbClientPtr)MemAlloc(sizeof(rfbClientRec));

    cl->screen = rfbScreen;
    cl->sock = sock;
    cl->viewOnly = FALSE;
    cl->allowClipboard = TRUE;
    /* setup pseudo scaling */
    cl->scaledScreen = rfbScreen;
    cl->scaledScreen->scaledScreenRefCount++;
    InitializeCriticalSection(&cl->csRgn);

    cl->clientData = NULL;
    cl->clientGoneHook = rfbDoNothingWithClient;

    if(!isUDP)
    {
        int one=1;

        getpeername(sock, (struct sockaddr *)&addr, &addrlen);
        cl->host = MemAlloc(16);
        mincpy(cl->host, inet_ntoa(addr.sin_addr), strlen(inet_ntoa(addr.sin_addr)));

        iterator = rfbGetClientIterator(rfbScreen);
        while ((cl_ = rfbClientIteratorNext(iterator)) != NULL)
        {
        }
        rfbReleaseClientIterator(iterator);

        if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
                       (char *)&one, sizeof(one)) < 0)
        {
            closesocket(sock);
            return NULL;
        }

        FD_SET(sock,&(rfbScreen->allFds));
        rfbScreen->maxFd = max(sock,rfbScreen->maxFd);

        INIT_MUTEX(cl->outputMutex);
        INIT_MUTEX(cl->refCountMutex);
        INIT_MUTEX(cl->sendMutex);
        INIT_COND(cl->deleteCond);

        cl->state = RFB_PROTOCOL_VERSION;

        cl->reverseConnection = FALSE;
        cl->readyForSetColourMapEntries = FALSE;
        cl->useCopyRect = FALSE;
        cl->preferredEncoding = -1;
        cl->correMaxWidth = 48;
        cl->correMaxHeight = 48;
#ifdef LIBVNCSERVER_HAVE_LIBZ
        cl->zrleData = NULL;
#endif

        cl->copyRegion = sraRgnCreate();
        cl->copyDX = 0;
        cl->copyDY = 0;

        EnterCriticalSection(&cl->csRgn);
            cl->modifiedRegion =
                sraRgnCreateRect(0,0,rfbScreen->width,rfbScreen->height);
        LeaveCriticalSection(&cl->csRgn);

        INIT_MUTEX(cl->updateMutex);
        INIT_COND(cl->updateCond);

        cl->requestedRegion = sraRgnCreate();

        cl->format = cl->screen->serverFormat;
        cl->translateFn = rfbTranslateNone;
        cl->translateLookupTable = NULL;

        LOCK(rfbClientListMutex);

        IF_PTHREADS(cl->refCount = 0);
        cl->next = rfbScreen->clientHead;
        cl->prev = NULL;
        if (rfbScreen->clientHead)
            rfbScreen->clientHead->prev = cl;

        rfbScreen->clientHead = cl;
        UNLOCK(rfbClientListMutex);

#ifdef LIBVNCSERVER_HAVE_LIBZ
        cl->tightQualityLevel = -1;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
        cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION;
        {
            int i;
            for (i = 0; i < 4; i++)
                cl->zsActive[i] = FALSE;
        }
#endif
#endif

        cl->fileTransfer.fd_file = INVALID_HANDLE_VALUE;

        cl->enableCursorShapeUpdates = FALSE;
        cl->enableCursorPosUpdates = FALSE;
        cl->useRichCursorEncoding = FALSE;
        cl->enableLastRectEncoding = FALSE;
        cl->enableKeyboardLedState = FALSE;
        cl->enableSupportedMessages = FALSE;
        cl->enableSupportedEncodings = FALSE;
        cl->enableServerIdentity = FALSE;
        cl->lastKeyboardLedState = -1;
        cl->cursorX = rfbScreen->cursorX;
        cl->cursorY = rfbScreen->cursorY;
        cl->useNewFBSize = FALSE;

#ifdef LIBVNCSERVER_HAVE_LIBZ
        cl->compStreamInited = FALSE;
        cl->compStream.total_in = 0;
        cl->compStream.total_out = 0;
        cl->compStream.zalloc = Z_NULL;
        cl->compStream.zfree = Z_NULL;
        cl->compStream.opaque = Z_NULL;

        cl->zlibCompressLevel = 5;
#endif

        cl->progressiveSliceY = 0;

        cl->extensions = NULL;

        cl->lastPtrX = -1;

        //sprintf(pv,rfbProtocolVersionFormat,rfbScreen->protocolMajorVersion,
        //        rfbScreen->protocolMinorVersion);
        //
        // File transfer in UltraVNC works only when version = 3.4 or 3.6 or 3.14 or 3.16
        // Minor = 4 means that server supports FileTransfer and requires ms logon
        // Minor = 6 means that server support FileTransfer and requires normal VNC logon
        strcpy(pv, "RFB 003.006\n");

        if (rfbWriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0)
        {
            rfbCloseClient(cl);
            rfbClientConnectionGone(cl);
            return NULL;
        }
    }

    for(extension = rfbGetExtensionIterator(); extension;
            extension=extension->next)
    {
        void* data = NULL;
        /* if the extension does not have a newClient method, it wants
         * to be initialized later. */
        if(extension->newClient && extension->newClient(cl, &data))
            rfbEnableExtension(cl, extension, data);
    }
    rfbReleaseExtensionIterator();

    switch (cl->screen->newClientHook(cl))
    {
    case RFB_CLIENT_ON_HOLD:
        cl->onHold = TRUE;
        break;
    case RFB_CLIENT_ACCEPT:
        cl->onHold = FALSE;
        break;
    case RFB_CLIENT_REFUSE:
        rfbCloseClient(cl);
        rfbClientConnectionGone(cl);
        cl = NULL;
        break;
    }
    return cl;
}

rfbClientPtr
rfbNewClient(rfbScreenInfoPtr rfbScreen,
             int sock)
{
    return(rfbNewTCPOrUDPClient(rfbScreen,sock,FALSE));
}

rfbClientPtr
rfbNewUDPClient(rfbScreenInfoPtr rfbScreen)
{
    return((rfbScreen->udpClient=
                rfbNewTCPOrUDPClient(rfbScreen,rfbScreen->udpSock,TRUE)));
}

/*
 * rfbClientConnectionGone is called from sockets.c just after a connection
 * has gone away.
 */

void
rfbClientConnectionGone(rfbClientPtr cl)
{
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
    int i;
#endif

    LOCK(rfbClientListMutex);

    if (cl->prev)
        cl->prev->next = cl->next;
    else
        cl->screen->clientHead = cl->next;
    if (cl->next)
        cl->next->prev = cl->prev;

    UNLOCK(rfbClientListMutex);

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
    if(cl->screen->backgroundLoop != FALSE) {
      int i;
      do {
       LOCK(cl->refCountMutex);
       i=cl->refCount;
       if(i>0)
         WAIT(cl->deleteCond,cl->refCountMutex);
       UNLOCK(cl->refCountMutex);
      } while(i>0);
    }
#endif

    if(cl->sock != -1)
        closesocket(cl->sock);

    if (cl->scaledScreen!=NULL)
        cl->scaledScreen->scaledScreenRefCount--;

#ifdef LIBVNCSERVER_HAVE_LIBZ
    rfbFreeZrleData(cl);
#endif

    rfbFreeUltraData(cl);

    /* free buffers holding pixel data before and after encoding */
    MemFree(cl->beforeEncBuf);
    MemFree(cl->afterEncBuf);

    if(cl->sock>=0)
        FD_CLR(cl->sock,&(cl->screen->allFds));

    cl->clientGoneHook(cl);

    MemFree(cl->host);

#ifdef LIBVNCSERVER_HAVE_LIBZ
    /* Release the compression state structures if any. */
    if ( cl->compStreamInited )
    {
        deflateEnd( &(cl->compStream) );
    }

#ifdef LIBVNCSERVER_HAVE_LIBJPEG
    for (i = 0; i < 4; i++)
    {
        if (cl->zsActive[i])
            deflateEnd(&cl->zsStruct[i]);
    }
#endif
#endif

    if (cl->screen->pointerClient == cl)
        cl->screen->pointerClient = NULL;

    EnterCriticalSection(&cl->csRgn);
        sraRgnDestroy(cl->modifiedRegion);
        sraRgnDestroy(cl->requestedRegion);
        sraRgnDestroy(cl->copyRegion);
    LeaveCriticalSection(&cl->csRgn);
    DeleteCriticalSection(&cl->csRgn);

    if (cl->translateLookupTable) MemFree(cl->translateLookupTable);

    TINI_COND(cl->updateCond);
    TINI_MUTEX(cl->updateMutex);

    /* make sure outputMutex is unlocked before destroying */
    LOCK(cl->outputMutex);
    UNLOCK(cl->outputMutex);
    TINI_MUTEX(cl->outputMutex);

#ifdef CORBA
    destroyConnection(cl);
#endif

    LOCK(cl->sendMutex);
    UNLOCK(cl->sendMutex);
    TINI_MUTEX(cl->sendMutex);

    MemFree(cl);
}


/*
 * rfbProcessClientMessage is called when there is data to read from a client.
 */

void
rfbProcessClientMessage(rfbClientPtr cl)
{
    switch (cl->state)
    {
    case RFB_PROTOCOL_VERSION:
        rfbProcessClientProtocolVersion(cl);
        return;
    case RFB_SECURITY_TYPE:
        rfbProcessClientSecurityType(cl);
        return;
    case RFB_AUTHENTICATION:
        rfbAuthProcessClientMessage(cl);
        return;
    case RFB_INITIALISATION:
        rfbProcessClientInitMessage(cl);
        return;
    default:
        rfbProcessClientNormalMessage(cl);
        return;
    }
}


/*
 * rfbProcessClientProtocolVersion is called when the client sends its
 * protocol version.
 */

static void
rfbProcessClientProtocolVersion(rfbClientPtr cl)
{
    rfbProtocolVersionMsg pv;
    int n, major_, minor_;

    if ((n = rfbReadExact(cl, pv, sz_rfbProtocolVersionMsg)) <= 0)
    {
        rfbCloseClient(cl);
        return;
    }

    pv[sz_rfbProtocolVersionMsg] = 0;
    if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2)
    {
        rfbCloseClient(cl);
        return;
    }

    if (major_ != rfbProtocolMajorVersion)
    {
        rfbCloseClient(cl);
        return;
    }

    /* Check for the minor version use either of the two standard version of RFB */
    /*
     * UltraVNC Viewer detects FileTransfer compatible servers via rfb versions
     * 3.4, 3.6, 3.14, 3.16
     * It's a bad method, but it is what they use to enable features...
     * maintaining RFB version compatibility across multiple servers is a pain
     * Should use something like ServerIdentity encoding
     */
    cl->protocolMajorVersion = major_;
    cl->protocolMinorVersion = minor_;

    rfbAuthNewClient(cl);
}


void
rfbClientSendString(rfbClientPtr cl, char *reason)
{
    char *buf;
    int len = strlen(reason);

    buf = (char *)MemAlloc(4 + len);
    ((uint32_t *)buf)[0] = Swap32IfLE(len);
    memcpy(buf + 4, reason, len);

    rfbWriteExact(cl, buf, 4 + len);
    MemFree(buf);

    rfbCloseClient(cl);
}

/*
 * rfbClientConnFailed is called when a client connection has failed either
 * because it talks the wrong protocol or it has failed authentication.
 */

void
rfbClientConnFailed(rfbClientPtr cl,
                    char *reason)
{
    char *buf;
    int len = strlen(reason);

    buf = (char *)MemAlloc(8 + len);
    ((uint32_t *)buf)[0] = Swap32IfLE(rfbConnFailed);
    ((uint32_t *)buf)[1] = Swap32IfLE(len);
    memcpy(buf + 8, reason, len);

    rfbWriteExact(cl, buf, 8 + len);
    MemFree(buf);

    rfbCloseClient(cl);
}


/*
 * rfbProcessClientInitMessage is called when the client sends its
 * initialisation message.
 */

static void
rfbProcessClientInitMessage(rfbClientPtr cl)
{
    rfbClientInitMsg ci;
    union
    {
        char buf[256];
        rfbServerInitMsg si;
    } u;
    int len, n;
    rfbClientIteratorPtr iterator;
    rfbClientPtr otherCl;
    rfbExtensionData* extension;

    if ((n = rfbReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0)
    {
        rfbCloseClient(cl);
        return;
    }

    memset(u.buf,0,sizeof(u.buf));

    u.si.framebufferWidth = Swap16IfLE(cl->screen->width);
    u.si.framebufferHeight = Swap16IfLE(cl->screen->height);
    u.si.format = cl->screen->serverFormat;
    u.si.format.redMax = Swap16IfLE(u.si.format.redMax);
    u.si.format.greenMax = Swap16IfLE(u.si.format.greenMax);
    u.si.format.blueMax = Swap16IfLE(u.si.format.blueMax);

    strncpy(u.buf + sz_rfbServerInitMsg, cl->screen->desktopName, 127);
    len = strlen(u.buf + sz_rfbServerInitMsg);
    u.si.nameLength = Swap32IfLE(len);

    if (rfbWriteExact(cl, u.buf, sz_rfbServerInitMsg + len) < 0)
    {
        rfbCloseClient(cl);
        return;
    }

    for(extension = cl->extensions; extension;)
    {
        rfbExtensionData* next = extension->next;
        if(extension->extension->init &&
                !extension->extension->init(cl, extension->data))
            /* extension requested that it be removed */
            rfbDisableExtension(cl, extension->extension);
        extension = next;
    }

    cl->state = RFB_NORMAL;

    if (!cl->reverseConnection &&
            (cl->screen->neverShared || (!cl->screen->alwaysShared && !ci.shared)))
    {

        if (cl->screen->dontDisconnect)
        {
            iterator = rfbGetClientIterator(cl->screen);
            while ((otherCl = rfbClientIteratorNext(iterator)) != NULL)
            {
                if ((otherCl != cl) && (otherCl->state == RFB_NORMAL))
                {
                    rfbCloseClient(cl);
                    rfbReleaseClientIterator(iterator);
                    return;
                }
            }
            rfbReleaseClientIterator(iterator);
        }
        else
        {
            iterator = rfbGetClientIterator(cl->screen);
            while ((otherCl = rfbClientIteratorNext(iterator)) != NULL)
            {
                if ((otherCl != cl) && (otherCl->state == RFB_NORMAL))
                {
                    rfbCloseClient(otherCl);
                }
            }
            rfbReleaseClientIterator(iterator);
        }
    }
}

/* The values come in based on the scaled screen, we need to convert them to
 * values based on the man screen's coordinate system
 */
static rfbBool rectSwapIfLEAndClip(uint16_t* x,uint16_t* y,uint16_t* w,uint16_t* h,
                                   rfbClientPtr cl)
{
    int x1=Swap16IfLE(*x);
    int y1=Swap16IfLE(*y);
    int w1=Swap16IfLE(*w);
    int h1=Swap16IfLE(*h);

    rfbScaledCorrection(cl->scaledScreen, cl->screen, &x1, &y1, &w1, &h1);
    *x = x1;
    *y = y1;
    *w = w1;
    *h = h1;

    if(*w>cl->screen->width-*x)
        *w=cl->screen->width-*x;
    /* possible underflow */
    if(*w>cl->screen->width-*x)
        return FALSE;
    if(*h>cl->screen->height-*y)
        *h=cl->screen->height-*y;
    if(*h>cl->screen->height-*y)
        return FALSE;

    return TRUE;
}

/*
 * Send keyboard state (PointerPos pseudo-encoding).
 */

rfbBool
rfbSendKeyboardLedState(rfbClientPtr cl)
{
    rfbFramebufferUpdateRectHeader rect;

    if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.encoding = Swap32IfLE(rfbEncodingKeyboardLedState);
    rect.r.x = Swap16IfLE(cl->lastKeyboardLedState);
    rect.r.y = 0;
    rect.r.w = 0;
    rect.r.h = 0;

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
           sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    if (!rfbSendUpdateBuf(cl))
        return FALSE;

    return TRUE;
}


#define rfbSetBit(buffer, position)  (buffer[(position & 255) / 8] |= (1 << (position % 8)))

/*
 * Send rfbEncodingSupportedMessages.
 */

rfbBool
rfbSendSupportedMessages(rfbClientPtr cl)
{
    rfbFramebufferUpdateRectHeader rect;
    rfbSupportedMessages msgs;

    if (cl->ublen + sz_rfbFramebufferUpdateRectHeader
            + sz_rfbSupportedMessages > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.encoding = Swap32IfLE(rfbEncodingSupportedMessages);
    rect.r.x = 0;
    rect.r.y = 0;
    rect.r.w = Swap16IfLE(sz_rfbSupportedMessages);
    rect.r.h = 0;

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
           sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    memset((char *)&msgs, 0, sz_rfbSupportedMessages);
    rfbSetBit(msgs.client2server, rfbSetPixelFormat);
    rfbSetBit(msgs.client2server, rfbFixColourMapEntries);
    rfbSetBit(msgs.client2server, rfbSetEncodings);
    rfbSetBit(msgs.client2server, rfbFramebufferUpdateRequest);
    rfbSetBit(msgs.client2server, rfbKeyEvent);
    rfbSetBit(msgs.client2server, rfbPointerEvent);
    rfbSetBit(msgs.client2server, rfbClientCutText);
    rfbSetBit(msgs.client2server, rfbFileTransfer);
    rfbSetBit(msgs.client2server, rfbSetScale);
    /*rfbSetBit(msgs.client2server, rfbSetServerInput);  */
    /*rfbSetBit(msgs.client2server, rfbSetSW);           */
    /*rfbSetBit(msgs.client2server, rfbTextChat);        */
    rfbSetBit(msgs.client2server, rfbPalmVNCSetScaleFactor);
    rfbSetBit(msgs.client2server, rfbXvp);

    rfbSetBit(msgs.server2client, rfbFramebufferUpdate);
    rfbSetBit(msgs.server2client, rfbSetColourMapEntries);
    rfbSetBit(msgs.server2client, rfbBell);
    rfbSetBit(msgs.server2client, rfbServerCutText);
    rfbSetBit(msgs.server2client, rfbResizeFrameBuffer);
    rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer);
    rfbSetBit(msgs.server2client, rfbXvp);

    memcpy(&cl->updateBuf[cl->ublen], (char *)&msgs, sz_rfbSupportedMessages);
    cl->ublen += sz_rfbSupportedMessages;

    if (!rfbSendUpdateBuf(cl))
        return FALSE;

    return TRUE;
}



/*
 * Send rfbEncodingSupportedEncodings.
 */

rfbBool
rfbSendSupportedEncodings(rfbClientPtr cl)
{
    rfbFramebufferUpdateRectHeader rect;
    static uint32_t supported[] =
    {
        rfbEncodingRaw,
        rfbEncodingCopyRect,
        rfbEncodingRRE,
        rfbEncodingCoRRE,
        rfbEncodingHextile,
#ifdef LIBVNCSERVER_HAVE_LIBZ
        rfbEncodingZlib,
        rfbEncodingZRLE,
        rfbEncodingZYWRLE,
#endif
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
        rfbEncodingTight,
#endif
        rfbEncodingUltra,
        rfbEncodingUltraZip,
        rfbEncodingXCursor,
        rfbEncodingRichCursor,
        rfbEncodingPointerPos,
        rfbEncodingLastRect,
        rfbEncodingNewFBSize,
        rfbEncodingKeyboardLedState,
        rfbEncodingSupportedMessages,
        rfbEncodingSupportedEncodings,
        rfbEncodingServerIdentity,
    };
    uint32_t nEncodings = sizeof(supported) / sizeof(supported[0]), i;

    /* think rfbSetEncodingsMsg */

    if (cl->ublen + sz_rfbFramebufferUpdateRectHeader
            + (nEncodings * sizeof(uint32_t)) > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.encoding = Swap32IfLE(rfbEncodingSupportedEncodings);
    rect.r.x = 0;
    rect.r.y = 0;
    rect.r.w = Swap16IfLE(nEncodings * sizeof(uint32_t));
    rect.r.h = Swap16IfLE(nEncodings);

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
           sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    for (i = 0; i < nEncodings; i++)
    {
        uint32_t encoding = Swap32IfLE(supported[i]);
        memcpy(&cl->updateBuf[cl->ublen], (char *)&encoding, sizeof(encoding));
        cl->ublen += sizeof(encoding);
    }

    if (!rfbSendUpdateBuf(cl))
        return FALSE;

    return TRUE;
}


void
rfbSetServerVersionIdentity(rfbScreenInfoPtr screen, char *fmt, ...)
{
    char buffer[256];
    va_list ap;

    va_start(ap, fmt);
    wvnsprintf(buffer, sizeof(buffer)-1, fmt, ap);
    va_end(ap);

    if (screen->versionString!=NULL) MemFree(screen->versionString);
    screen->versionString = MemAlloc(strlen(buffer));
    mincpy(screen->versionString, buffer, strlen(buffer));
}

/*
 * Send rfbEncodingServerIdentity.
 */

rfbBool
rfbSendServerIdentity(rfbClientPtr cl)
{
    rfbFramebufferUpdateRectHeader rect;
    char buffer[512];

    /* tack on our library version */
    wsprintf(buffer, "%s (%s)",
             (cl->screen->versionString==NULL ? "unknown" : cl->screen->versionString),
             LIBVNCSERVER_PACKAGE_STRING);

    if (cl->ublen + sz_rfbFramebufferUpdateRectHeader
            + (strlen(buffer)+1) > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.encoding = Swap32IfLE(rfbEncodingServerIdentity);
    rect.r.x = 0;
    rect.r.y = 0;
    rect.r.w = Swap16IfLE(strlen(buffer)+1);
    rect.r.h = 0;

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
           sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    memcpy(&cl->updateBuf[cl->ublen], buffer, strlen(buffer)+1);
    cl->ublen += strlen(buffer)+1;

    if (!rfbSendUpdateBuf(cl))
        return FALSE;

    return TRUE;
}

/*
 * Send an xvp server message
 */

rfbBool
rfbSendXvp(rfbClientPtr cl, uint8_t version, uint8_t code)
{
    rfbXvpMsg xvp;

    xvp.type = rfbXvp;
    xvp.pad = 0;
    xvp.version = version;
    xvp.code = code;

    LOCK(cl->sendMutex);
    if (rfbWriteExact(cl, (char *)&xvp, sz_rfbXvpMsg) < 0)
    {
        rfbCloseClient(cl);
    }
    UNLOCK(cl->sendMutex);

    return TRUE;
}


rfbBool rfbSendTextChatMessage(rfbClientPtr cl, uint32_t length, char *buffer)
{
    rfbTextChatMsg tc;
    int bytesToSend=0;

    memset((char *)&tc, 0, sizeof(tc));
    tc.type = rfbTextChat;
    tc.length = Swap32IfLE(length);

    switch(length)
    {
    case rfbTextChatOpen:
    case rfbTextChatClose:
    case rfbTextChatFinished:
        bytesToSend=0;
        break;
    default:
        bytesToSend=length;
        if (bytesToSend>rfbTextMaxSize)
            bytesToSend=rfbTextMaxSize;
    }

    if (cl->ublen + sz_rfbTextChatMsg + bytesToSend > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    memcpy(&cl->updateBuf[cl->ublen], (char *)&tc, sz_rfbTextChatMsg);
    cl->ublen += sz_rfbTextChatMsg;
    if (bytesToSend>0)
    {
        memcpy(&cl->updateBuf[cl->ublen], buffer, bytesToSend);
        cl->ublen += bytesToSend;
    }

    if (!rfbSendUpdateBuf(cl))
        return FALSE;

    return TRUE;
}

#define FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN(msg, cl, ret) \
	if ((cl->screen->getFileTransferPermission != NULL \
	    && cl->screen->getFileTransferPermission(cl) != TRUE)) { \
		rfbCloseClient(cl); \
		return ret; \
	}

int DB = 1;

rfbBool rfbSendFileTransferMessage(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length, char *buffer)
{
    rfbFileTransferMsg ft;
    ft.type = rfbFileTransfer;
    ft.contentType = contentType;
    ft.contentParam = contentParam;
    ft.pad          = 0; /* UltraVNC did not Swap16LE(ft.contentParam) (Looks like it might be BigEndian) */
    ft.size         = Swap32IfLE(size);
    ft.length       = Swap32IfLE(length);

    FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);
    LOCK(cl->sendMutex);
    if (rfbWriteExact(cl, (char *)&ft, sz_rfbFileTransferMsg) < 0)
    {
        rfbCloseClient(cl);
        UNLOCK(cl->sendMutex);
        return FALSE;
    }

    if (length>0)
    {
        if (rfbWriteExact(cl, buffer, length) < 0)
        {
            rfbCloseClient(cl);
            UNLOCK(cl->sendMutex);
            return FALSE;
        }
    }
    UNLOCK(cl->sendMutex);

    return TRUE;
}

/*
 * UltraVNC uses Windows Structures
 */
#define MAX_PATH 260
// for bellow stuff we use real windows stuff
/*
typedef struct {
    uint32_t dwLowDateTime;
    uint32_t dwHighDateTime;
} RFB_FILETIME;

typedef struct {
    uint32_t dwFileAttributes;
    RFB_FILETIME ftCreationTime;
    RFB_FILETIME ftLastAccessTime;
    RFB_FILETIME ftLastWriteTime;
    uint32_t nFileSizeHigh;
    uint32_t nFileSizeLow;
    uint32_t dwReserved0;
    uint32_t dwReserved1;
    uint8_t  cFileName[ MAX_PATH ];
    uint8_t  cAlternateFileName[ 14 ];
} RFB_FIND_DATA;

#define RFB_FILE_ATTRIBUTE_READONLY   0x1
#define RFB_FILE_ATTRIBUTE_HIDDEN     0x2
#define RFB_FILE_ATTRIBUTE_SYSTEM     0x4
#define RFB_FILE_ATTRIBUTE_DIRECTORY  0x10
#define RFB_FILE_ATTRIBUTE_ARCHIVE    0x20
#define RFB_FILE_ATTRIBUTE_NORMAL     0x80
#define RFB_FILE_ATTRIBUTE_TEMPORARY  0x100
#define RFB_FILE_ATTRIBUTE_COMPRESSED 0x800
*/

#define rfbFilenameTranslate2UNIX(not_used, src, dest) strcpy(dest, src)
/*rfbBool rfbFilenameTranslate2UNIX(rfbClientPtr cl, char *path, char *unixPath)
{
    int x;
    char *home=NULL;

    FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);
*/
/* C: */
/*    if (path[1]==':')
      strcpy(unixPath, &path[2]);

    for (x=0;x<(int)strlen(unixPath);x++)
      if (unixPath[x]=='\\') unixPath[x]='/';
    return TRUE;
}*/

rfbBool rfbFilenameTranslate2DOS(rfbClientPtr cl, char *unixPath, char *path)
{
    int x;

    FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

    //sprintf(path,"C:%s", unixPath);
    strcpy(path, "C:");
    strcat(path, unixPath);
    for (x=2; x<(int)strlen(path); x++)
        if (path[x]=='/') path[x]='\\';
    return TRUE;
}

rfbBool rfbSendDirContent(rfbClientPtr cl, int length, char *buffer)
{
    char path[MAX_PATH];
    WCHAR pathW[MAX_PATH];
    WIN32_FIND_DATAA win32filenameA = {0};
    WIN32_FIND_DATAW win32filenameW = {0};
    int nOptLen = 0, retval=0, nFolder = -1,dwLen;
    HANDLE hFindHandle;

    FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

    // Client thinks we are Winblows... we are...
    rfbFilenameTranslate2UNIX(cl, buffer, path);

    if (!memcmp(path, "My Documents", 12))
        nFolder = CSIDL_PERSONAL;
    if (!memcmp(path, "Desktop", 7))
        nFolder = CSIDL_DESKTOP;
    if (!memcmp(path, "Network Favorites", 17))
        nFolder = CSIDL_NETHOOD;

    if (nFolder != -1)
    {
        char tmp[MAX_PATH],*p;
        SHGetSpecialFolderPath(NULL, tmp, nFolder, FALSE);
        p=strchr(path,'\\');
        if (p && *(p+1))
        {
            strcat(tmp,p);
        }
        strcpy(path,tmp);
    }

    if(path[strlen(path) - 1] == '\\')
        strcat(path, "*");
    else
        strcat(path, "\\*");

    dwLen=strlen(path)+1;
    MultiByteToWideChar(1251,0,path,dwLen,pathW,dwLen);

    hFindHandle = FindFirstFileW(pathW, &win32filenameW);
    if(hFindHandle == INVALID_HANDLE_VALUE)
        return rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, 0, NULL);

    // send back the path name (necessary for links)
    if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, length, buffer)==FALSE) return FALSE;
    do
    {
        // normally everything goes in big endian, but this struct should be little?

        // Do not show hidden files (but show how to move up the tree)
        if (wcscmp(win32filenameW.cFileName, L"."))
        {
            memcpy(&win32filenameA,&win32filenameW,((int)&win32filenameA.cFileName)-(int)&win32filenameA);

            dwLen=lstrlenW(win32filenameW.cFileName)+1;
            WideCharToMultiByte(1251,WC_COMPOSITECHECK,win32filenameW.cFileName,dwLen,win32filenameA.cFileName,dwLen,NULL,NULL);
            dwLen=lstrlenW(win32filenameW.cAlternateFileName);
            if (dwLen)
                WideCharToMultiByte(1251,WC_COMPOSITECHECK,win32filenameW.cAlternateFileName,dwLen,win32filenameA.cAlternateFileName,dwLen,NULL,NULL);

            nOptLen = sizeof(WIN32_FIND_DATAA) - MAX_PATH - 14 + strlen((char *)win32filenameA.cFileName);

            if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, nOptLen, (char *)&win32filenameA)==FALSE) return FALSE;
        }

    }
    while(FindNextFileW(hFindHandle, &win32filenameW));
    FindClose(hFindHandle);
    // End of the transfer
    return rfbSendFileTransferMessage(cl, rfbDirPacket, 0, 0, 0, NULL);
}


char *rfbProcessFileTransferReadBuffer(rfbClientPtr cl, uint32_t length)
{
    char *buffer=NULL;
    int   n=0;

    FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, NULL);
    /*
    rfbLog("rfbProcessFileTransferReadBuffer(%dlen)\n", length);
    */
    if (length>0)
    {
        buffer=MemAlloc(length+1);
        if (buffer!=NULL)
        {
            if ((n = rfbReadExact(cl, (char *)buffer, length)) <= 0)
            {
                rfbCloseClient(cl);
                /* NOTE: don't forget to free(buffer) if you return early! */
                if (buffer!=NULL) MemFree(buffer);
                return NULL;
            }
            /* Null Terminate */
            buffer[length]=0;
        }
    }
    return buffer;
}


rfbBool rfbSendFileTransferChunk(rfbClientPtr cl)
{
    /* Allocate buffer for compression */
    unsigned char readBuf[sz_rfbBlockSize];
    int bytesRead=0;
    int retval=0;
    fd_set wfds;
    struct timeval tv;
    int n;
    int i;
#ifdef LIBVNCSERVER_HAVE_LIBZ
    unsigned char compBuf[sz_rfbBlockSize + 1024];
    unsigned long nMaxCompSize = sizeof(compBuf);
    int nRetC = 0;
#endif

    /*
     * Don't close the client if we get into this one because
     * it is called from many places to service file transfers.
     * Note that permitFileTransfer is checked first.
     */
    if (cl->screen->getFileTransferPermission != NULL
        && cl->screen->getFileTransferPermission(cl) != TRUE)
    {
        return TRUE;
    }

    // If not sending, or no file _open...   Return as if we sent something!
    //if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1))
    if ((cl->fileTransfer.fd_file!=INVALID_HANDLE_VALUE) && (cl->fileTransfer.sending==1))
    {
        FD_ZERO(&wfds);
        FD_SET(cl->sock, &wfds);

        // return immediately
        tv.tv_sec = 0;
        tv.tv_usec = 0;
        n = select(cl->sock + 1, NULL, &wfds, NULL, &tv);

        // We have space on the transmit queue
        if (n > 0)
        {
            for(i = 0; i < 32; i++)	// 32 * 8 = 256kb
            {
                if(!ReadFile(cl->fileTransfer.fd_file, readBuf, sz_rfbBlockSize, (LPDWORD)&bytesRead, NULL))
                    bytesRead = -1;	// indicate error
                switch (bytesRead)
                {
                case 0:
                {
                    retval = rfbSendFileTransferMessage(cl, rfbEndOfFile, 0, 0, 0, NULL);

                    SysCloseHandle(cl->fileTransfer.fd_file);
                    cl->fileTransfer.fd_file = INVALID_HANDLE_VALUE;
                    cl->fileTransfer.sending   = 0;
                    cl->fileTransfer.receiving = 0;
                    if (cl->fileTransfer.bDir)
                        DeleteFileW(cl->fileTransfer.szCabFile);
                    return retval;
                }
                case -1:
                {
                    // TODO : send an error msg to the client...
                    retval = rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, 0, 0, NULL);

                    SysCloseHandle(cl->fileTransfer.fd_file);
                    cl->fileTransfer.fd_file = INVALID_HANDLE_VALUE;
                    cl->fileTransfer.sending   = 0;
                    cl->fileTransfer.receiving = 0;
                    if (cl->fileTransfer.bDir)
                        DeleteFileW(cl->fileTransfer.szCabFile);
                    return retval;
                }
                default:
                {
                    if (!cl->fileTransfer.compressionEnabled)
                        retval = rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, readBuf);
                    else
                    {
#ifdef LIBVNCSERVER_HAVE_LIBZ
                        nRetC = compress(compBuf, &nMaxCompSize, readBuf, bytesRead);

                        if ((nRetC==0) && (nMaxCompSize<bytesRead))
                            return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 1, nMaxCompSize, (char *)compBuf);
                        else
                            return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf);
#else
                        /* We do not support compression of the data stream */
                        return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf);
#endif
                    }
                }
                }
            }
        }
    }
    return TRUE;
}

WCHAR *GetFileName(WCHAR *lpPath)
{
    WCHAR *p=lpPath+lstrlenW(lpPath);
    for (; *(p-1) != L'\\'; p--) ;
    return p;
}

rfbBool rfbProcessFileTransfer(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length)
{
    char *buffer=NULL, *p=NULL;
    int retval=0;
    char filename1[MAX_PATH];
    char filename2[MAX_PATH];
    char szFileTime[MAX_PATH];
    WCHAR filename1W[MAX_PATH];
    WCHAR filename2W[MAX_PATH];
    //struct stat statbuf;
    BY_HANDLE_FILE_INFORMATION fstats;
    DWORD dwFileAttributes;
    SYSTEMTIME st;
    uint32_t sizeHtmp=0;
    int n=0;
    char timespec[64];
#ifdef LIBVNCSERVER_HAVE_LIBZ
    unsigned char compBuff[sz_rfbBlockSize];
    unsigned long nRawBytes = sz_rfbBlockSize;
    int nRet = 0;
#endif
    TCHAR szDrivesList[256]; // Format when filled : "C:\<NULL>D:\<NULL>....Z:\<NULL><NULL>
    DWORD dwLen, nFolder = -1;
    int nIndex = 0;
    int nType = 0;
    TCHAR szDrive[4];

    FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

    switch (contentType)
    {
    case rfbDirContentRequest:
        switch (contentParam)
        {
        case rfbRDrivesList: // Client requests the List of Local Drives

            //rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDrivesList:\n");

            // Format when filled : "C:\<NULL>D:\<NULL>....Z:\<NULL><NULL>
            //
            // We replace the "\" char following the drive letter and ":"
            // with a char corresponding to the type of drive
            // We obtain something like "C:l<NULL>D:c<NULL>....Z:n\<NULL><NULL>"
            //  Isn't it ugly ?
            // DRIVE_FIXED = 'l'     (local?)
            // DRIVE_REMOVABLE = 'f' (floppy?)
            // DRIVE_CDROM = 'c'
            // DRIVE_REMOTE = 'n'
            //

            // in unix, there are no 'drives'  (We could list mount points though)
            // We fake the root as a "C:" for the Winblows users

            dwLen = GetLogicalDriveStrings(256, szDrivesList);

            while (nIndex < dwLen - 3)
            {
                strcpy(szDrive, szDrivesList + nIndex);
                // We replace the "\" char following the drive letter and ":"
                // with a char corresponding to the type of drive
                // We obtain something like "C:l<NULL>D:c<NULL>....Z:n\<NULL><NULL>"
                // Isn't it ugly ?
                nType = GetDriveType(szDrive);
                switch (nType)
                {
                case DRIVE_FIXED:
                    szDrivesList[nIndex + 2] = 'l';
                    break;
                case DRIVE_REMOVABLE:
                    szDrivesList[nIndex + 2] = 'f';
                    break;
                case DRIVE_CDROM:
                    szDrivesList[nIndex + 2] = 'c';
                    break;
                case DRIVE_REMOTE:
                    szDrivesList[nIndex + 2] = 'n';
                    break;
                }
                nIndex += 4;
            }

            retval = rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADrivesList, 0, dwLen, szDrivesList);
            if (buffer!=NULL) MemFree(buffer);
            return retval;
            break;
        case rfbRDirContent: // Client requests the content of a directory

            //rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent\n");

            SetErrorMode(SEM_FAILCRITICALERRORS);

            if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
            retval = rfbSendDirContent(cl, length, buffer);
            if (buffer!=NULL) MemFree(buffer);
            return retval;
        }
        break;
    case rfbFileTransferRequest:

        //rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest:\n");

        // add some space to the end of the buffer as we will be adding a timespec to it
        if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
        // The client requests a File
        rfbFilenameTranslate2UNIX(cl, buffer, filename1);

        if (!memcmp(filename1, "My Documents", 12))
            nFolder = CSIDL_PERSONAL;
        if (!memcmp(filename1, "Desktop", 7))
            nFolder = CSIDL_DESKTOP;
        if (!memcmp(filename1, "Network Favorites", 17))
            nFolder = CSIDL_NETHOOD;

        if (nFolder != -1)
        {
            char tmp[MAX_PATH],*p;
            SHGetSpecialFolderPath(NULL, tmp, nFolder, FALSE);
            p=strchr(filename1,'\\');
            if (p && *(p+1))
                strcat(tmp,p);
            strcpy(filename1,tmp);
        }

        dwLen=strlen(filename1)+1;
        MultiByteToWideChar(1251,0,filename1,dwLen,filename1W,dwLen);
        if (GetFileAttributesW(filename1W) == INVALID_FILE_ATTRIBUTES)
        {
            WCHAR *p=GetFileName(filename1W);

            if (*p == '[')
            {
                DWORD dwAttribs=0;
                int dwLen=p-filename1W;

                memcpy(filename2W,filename1W,dwLen*sizeof(WCHAR));
                filename2W[dwLen]=0;
                lstrcatW(filename2W,p+2);
                filename2W[lstrlenW(filename2W)-2]=0;

                if (((dwAttribs=GetFileAttributesW(filename2W)) != INVALID_FILE_ATTRIBUTES) && (dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
                {
                    CHAR *p=0;
                    WCHAR szmask[4]=L"*.*";
                    WCHAR *lpmask[2]= {szmask,0};
                    WCHAR tmppath[MAX_PATH];

                    GetTempPathW(MAX_PATH-1,tmppath);
                    GetTempFileNameW(tmppath,L"_",0,filename1W);
                    DeleteFileW(filename1W);

                    ArchCreateFromFolderW(filename1W,filename2W,lpmask,1,CFF_RECURSE,0,0,5);
                    cl->fileTransfer.bDir=TRUE;
                    lstrcpyW(cl->fileTransfer.szCabFile,filename1W);

                    p=filename1+dwLen;
                    memcpy(filename2,filename1,dwLen);
                    filename2[dwLen]=0;
                    lstrcat(filename2,p+2);
                    filename2[lstrlen(filename2)-2]=0;
                    buffer=MemRealloc(buffer,strlen(filename2)+5);
                    lstrcpy(buffer,filename2);
                    lstrcat(buffer,".zip");
                }
            }
        }

        cl->fileTransfer.fd_file = CreateFileW(filename1W, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

        if (cl->fileTransfer.fd_file!=INVALID_HANDLE_VALUE)
        {
            if (GetFileInformationByHandle(cl->fileTransfer.fd_file, &fstats))
            {
                // Add the File Time Stamp to the filename
                FileTimeToSystemTime(&fstats.ftCreationTime, &st);
                wsprintfA(timespec, "%02d/%02d/%04d %02d:%02d",
                          st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute);
                buffer=MemRealloc(buffer, lstrlen(buffer) + strlen(timespec) + 2); // comma, and Null term
                if (buffer==NULL)
                {
                    return FALSE;
                }
                strcat(buffer,",");
                strcat(buffer, timespec);
                length = lstrlen(buffer);
            }
            else
            {
                SysCloseHandle(cl->fileTransfer.fd_file);
                cl->fileTransfer.fd_file = INVALID_HANDLE_VALUE;
            }
        }
        //    if (cl->fileTransfer.fd!=-1) {
        //        if (fstat(cl->fileTransfer.fd, &statbuf)!=0) {
        //			fclose((FILE*)cl->fileTransfer.fd);
        //            cl->fileTransfer.fd=-1;
        //        }
        //        else
        //        {
        //          // Add the File Time Stamp to the filename
        //          strftime(timespec, sizeof(timespec), "%m/%d/%Y %H:%M",gmtime(&statbuf.st_ctime));
        //          buffer=realloc(buffer, length + strlen(timespec) + 2); // comma, and Null term
        //          if (buffer==NULL) {
        //              return FALSE;
        //          }
        //          strcat(buffer,",");
        //          strcat(buffer, timespec);
        //          length = strlen(buffer);
        //        }
        //    }

        // The viewer supports compression if size==1
        cl->fileTransfer.compressionEnabled = (size==1);


        //rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\")%s\n", buffer, filename1, (size==1?" <Compression Enabled>":""));


        // File Size in bytes, 0xFFFFFFFF (-1) means error
        retval = rfbSendFileTransferMessage(cl, rfbFileHeader, 0, (cl->fileTransfer.fd_file==INVALID_HANDLE_VALUE ? -1 : fstats.nFileSizeLow), length, buffer);
        //retval = rfbSendFileTransferMessage(cl, rfbFileHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : statbuf.st_size), length, buffer);

        //if (cl->fileTransfer.fd==-1)
        if (cl->fileTransfer.fd_file==INVALID_HANDLE_VALUE)
        {
            if (buffer!=NULL) MemFree(buffer);
            return retval;
        }
        // setup filetransfer stuff
        //cl->fileTransfer.fileSize = statbuf.st_size;
        //cl->fileTransfer.numPackets = statbuf.st_size / sz_rfbBlockSize;
        cl->fileTransfer.fileSize = fstats.nFileSizeLow;
        cl->fileTransfer.numPackets = fstats.nFileSizeLow / sz_rfbBlockSize;
        cl->fileTransfer.receiving = 0;
        cl->fileTransfer.sending = 0; /* set when we receive a rfbFileHeader: */

        // TODO: finish 64-bit file size support
        sizeHtmp = 0;
        LOCK(cl->sendMutex);
        //sizeHtmp = fstats.nFileSizeHigh; ?
        if (rfbWriteExact(cl, (char *)&sizeHtmp, 4) < 0)
        {
            if (cl->fileTransfer.bDir)
                DeleteFile(filename1);
            // shouldnt we close file here?
            rfbCloseClient(cl);
            UNLOCK(cl->sendMutex);
            if (buffer!=NULL) MemFree(buffer);
            return FALSE;
        }
        UNLOCK(cl->sendMutex);
        break;

    case rfbFileHeader:
        // Destination file (viewer side) is ready for reception (size > 0) or not (size = -1)
        if (size==-1)
        {
            //closesocket(cl->fileTransfer.fd);
            //cl->fileTransfer.fd=-1;
            // why close socket when it's file?
            SysCloseHandle(cl->fileTransfer.fd_file);
            cl->fileTransfer.fd_file = INVALID_HANDLE_VALUE;
            return TRUE;
        }


        //rfbLog("rfbProcessFileTransfer() rfbFileHeader (%d bytes of a file)\n", size);


        // Starts the transfer!
        cl->fileTransfer.sending=1;
        return rfbSendFileTransferChunk(cl);
        break;


        // sending from the viewer to the server


    case rfbFileTransferOffer:
        // client is sending a file to us
        // buffer contains full path name (plus FileTime)
        // size contains size of the file
        //
        //rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer:\n";

        if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;

        // Parse the FileTime
        //p = strrchr(buffer, ',');
        // i think above is same as p = strchr(...), but that still needs crt,
        // and doesnt look like msvc has intrinsic for scasb, so...
        p = buffer;
        while(*p && *p != ',') p++;
        if(*p == 0) p = NULL;

        if (p!=NULL)
        {
            *p = '\0';
            strcpy(szFileTime, p+1);
        }
        else
            szFileTime[0]=0;



        // Need to _read in sizeHtmp
        if ((n = rfbReadExact(cl, (char *)&sizeHtmp, 4)) <= 0)
        {
            rfbCloseClient(cl);
            // NOTE: don't forget to free(buffer) if you return early!
            if (buffer!=NULL) MemFree(buffer);
            return FALSE;
        }
        sizeHtmp = Swap32IfLE(sizeHtmp);

        rfbFilenameTranslate2UNIX(cl, buffer, filename1);

        // If the file exists... We can send a rfbFileChecksums back to the client before we send an rfbFileAcceptHeader
        // TODO: Delta Transfer

        //cl->fileTransfer.fd=_open(filename1, O_CREAT|O_WRONLY|O_TRUNC, 0744);
        cl->fileTransfer.fd_file = CreateFileA(filename1, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

        // File Size in bytes, 0xFFFFFFFF (-1) means error
        retval = rfbSendFileTransferMessage(cl, rfbFileAcceptHeader, 0, (cl->fileTransfer.fd_file==INVALID_HANDLE_VALUE ? -1 : 0), length, buffer);
        //retval = rfbSendFileTransferMessage(cl, rfbFileAcceptHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : 0), length, buffer);
        //if (cl->fileTransfer.fd==-1) {
        if (cl->fileTransfer.fd_file==INVALID_HANDLE_VALUE)
        {
            MemFree(buffer);
            return retval;
        }

        // setup filetransfer stuff
        cl->fileTransfer.fileSize = size;
        cl->fileTransfer.numPackets = size / sz_rfbBlockSize;
        cl->fileTransfer.receiving = 1;
        cl->fileTransfer.sending = 0;
        break;

    case rfbFilePacket:

        //rfbLog("rfbProcessFileTransfer() rfbFilePacket:\n");

        if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
        if (cl->fileTransfer.fd_file!=INVALID_HANDLE_VALUE)
        {
            //if (cl->fileTransfer.fd!=-1) {
            // buffer contains the contents of the file
            if (size==0)
                // why x_write is defined as send?
                //retval=x_write(cl->fileTransfer.fd, buffer, length);
                WriteFile(cl->fileTransfer.fd_file, buffer, length, (LPDWORD)&retval, NULL);
            else
            {
#ifdef LIBVNCSERVER_HAVE_LIBZ
                // compressed packet
                nRet = uncompress(compBuff,&nRawBytes,(const unsigned char*)buffer, length);
                //retval=x_write(cl->fileTransfer.fd, compBuff, nRawBytes);
                WriteFile(cl->fileTransfer.fd_file, compBuff, nRawBytes, (LPDWORD)&retval, NULL);
#else
                // Write the file out as received...
                //retval=x_write(cl->fileTransfer.fd, buffer, length);
                WriteFile(cl->fileTransfer.fd_file, buffer, length, (LPDWORD)&retval, NULL);
#endif
            }
            if (retval==-1)
            {
                //closesocket(cl->fileTransfer.fd);
                //cl->fileTransfer.fd=-1;
                // wth why sockets?
                SysCloseHandle(cl->fileTransfer.fd_file);
                cl->fileTransfer.fd_file = INVALID_HANDLE_VALUE;
                cl->fileTransfer.sending   = 0;
                cl->fileTransfer.receiving = 0;
            }
        }
        break;

    case rfbEndOfFile:
        //if (cl->fileTransfer.fd!=-1)
        //    closesocket(cl->fileTransfer.fd);
        //cl->fileTransfer.fd=-1;
        if (cl->fileTransfer.fd_file!=INVALID_HANDLE_VALUE)
            SysCloseHandle(cl->fileTransfer.fd_file);
        cl->fileTransfer.fd_file=INVALID_HANDLE_VALUE;
        cl->fileTransfer.sending   = 0;
        cl->fileTransfer.receiving = 0;
        break;

    case rfbAbortFileTransfer:
        if (cl->fileTransfer.fd_file!=INVALID_HANDLE_VALUE)
        {
            SysCloseHandle(cl->fileTransfer.fd_file);
            cl->fileTransfer.fd_file=INVALID_HANDLE_VALUE;
            cl->fileTransfer.sending   = 0;
            cl->fileTransfer.receiving = 0;
        }
        else
        {
            // We use this message for FileTransfer rights (<=RC18 versions)
            // The client asks for FileTransfer permission
            //
            if (contentParam == 0)
            {
                // Old method for FileTransfer handshake perimssion (<=RC18) (Deny it)
                return rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, -1, 0, "");
            }
            // New method is allowed
            if (cl->screen->getFileTransferPermission!=NULL)
            {
                if (cl->screen->getFileTransferPermission(cl)==TRUE)
                {
                    return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); // Permit
                }
                else
                {
                    return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); // Deny
                }
            }
            else
            {
                /**if (cl->permitFileTransfer)
                {
                    return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); // Permit
                }
                else*/
                {
                    return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); // DEFAULT: DENY (for security)
                }

            }
        }
        break;


    case rfbCommand:

        //rfbLog("rfbProcessFileTransfer() rfbCommand:\n");

        if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
        switch (contentParam)
        {
        case rfbCDirCreate:  // Client requests the creation of a directory
            rfbFilenameTranslate2UNIX(cl, buffer, filename1);
            retval = CreateDirectoryA(filename1, NULL);
            retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbADirCreate, retval, length, buffer);
            if (buffer!=NULL) MemFree(buffer);
            return retval;
        case rfbCFileDelete: // Client requests the deletion of a file
            rfbFilenameTranslate2UNIX(cl, buffer, filename1);
            dwFileAttributes = GetFileAttributesA(filename1);
            if(dwFileAttributes == INVALID_FILE_ATTRIBUTES)
                retval = -1;
            else if(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                retval = RemoveDirectory(filename1) ? 0 : -1;	// will fail if not empty, but same for _rmdir
            else	// file?
                retval = DeleteFileA(filename1) ? 0 : -1;

            /*if (stat(filename1,&statbuf)==0)
            {
                if (S_ISDIR(statbuf.st_mode))
                    retval = _rmdir(filename1);
                else
                    retval = _unlink(filename1);
            }
            else retval=-1; */
            retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileDelete, retval, length, buffer);
            if (buffer!=NULL) MemFree(buffer);
            return retval;
        case rfbCFileRename: // Client requests the Renaming of a file/directory
            //p = strrchr(buffer, '*');
            p = buffer;
            while(*p && *p != '*') p++;
            if(*p == 0) p = NULL;

            if (p != NULL)
            {
                // Split into 2 filenames ('*' is a seperator)
                *p = '\0';
                rfbFilenameTranslate2UNIX(cl, buffer, filename1);
                rfbFilenameTranslate2UNIX(cl, p+1,    filename2);
                //retval = rename(filename1,filename2);
                retval = MoveFile(filename1, filename2) ? 0 : -1;
                // Restore the buffer so the reply is good
                *p = '*';
                retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileRename, retval, length, buffer);
                if (buffer!=NULL) MemFree(buffer);
                return retval;
            }
            break;
        }

        break;
    }

    // NOTE: don't forget to free(buffer) if you return early!
    if (buffer!=NULL) MemFree(buffer);
    return TRUE;
}

/*
 * rfbProcessClientNormalMessage is called when the client has sent a normal
 * protocol message.
 */

static void
rfbProcessClientNormalMessage(rfbClientPtr cl)
{
    int n=0;
    rfbClientToServerMsg msg;
    char *str;
    int i;
    uint32_t enc=0;
    uint32_t lastPreferredEncoding = -1;

    if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0)
    {
        rfbCloseClient(cl);
        return;
    }

    switch (msg.type)
    {

    case rfbSetPixelFormat:

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbSetPixelFormatMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel;
        cl->format.depth = msg.spf.format.depth;
        cl->format.bigEndian = (msg.spf.format.bigEndian ? TRUE : FALSE);
        cl->format.trueColour = (msg.spf.format.trueColour ? TRUE : FALSE);
        cl->format.redMax = Swap16IfLE(msg.spf.format.redMax);
        cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax);
        cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax);
        cl->format.redShift = msg.spf.format.redShift;
        cl->format.greenShift = msg.spf.format.greenShift;
        cl->format.blueShift = msg.spf.format.blueShift;

        cl->readyForSetColourMapEntries = TRUE;
        cl->screen->setTranslateFunction(cl);
        return;

    case rfbFixColourMapEntries:
        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbFixColourMapEntriesMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }
        rfbCloseClient(cl);
        return;


        /* NOTE: Some clients send us a set of encodings (ie: PointerPos) designed to enable/disable features...
         * We may want to look into this...
         * Example:
         *     case rfbEncodingXCursor:
         *         cl->enableCursorShapeUpdates = TRUE;
         *
         * Currently: cl->enableCursorShapeUpdates can *never* be turned off...
         */
    case rfbSetEncodings:
    {

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbSetEncodingsMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings);

        /*
         * UltraVNC Client has the ability to adapt to changing network environments
         * So, let's give it a change to tell us what it wants now!
         */
        if (cl->preferredEncoding!=-1)
            lastPreferredEncoding = cl->preferredEncoding;

        /* Reset all flags to defaults (allows us to switch between PointerPos and Server Drawn Cursors) */
        cl->preferredEncoding=-1;
        cl->useCopyRect              = FALSE;
        cl->useNewFBSize             = FALSE;
        cl->cursorWasChanged         = FALSE;
        cl->useRichCursorEncoding    = FALSE;
        cl->enableCursorPosUpdates   = FALSE;
        cl->enableCursorShapeUpdates = FALSE;
        cl->enableLastRectEncoding   = FALSE;
        cl->enableKeyboardLedState   = FALSE;
        cl->enableSupportedMessages  = FALSE;
        cl->enableSupportedEncodings = FALSE;
        cl->enableServerIdentity     = FALSE;


        for (i = 0; i < msg.se.nEncodings; i++)
        {
            if ((n = rfbReadExact(cl, (char *)&enc, 4)) <= 0)
            {
                rfbCloseClient(cl);
                return;
            }
            enc = Swap32IfLE(enc);

            switch (enc)
            {

            case rfbEncodingCopyRect:
                cl->useCopyRect = TRUE;
                break;
            case rfbEncodingRaw:
            case rfbEncodingRRE:
            case rfbEncodingCoRRE:
            case rfbEncodingHextile:
            case rfbEncodingUltra:
#ifdef LIBVNCSERVER_HAVE_LIBZ
            case rfbEncodingZlib:
            case rfbEncodingZRLE:
            case rfbEncodingZYWRLE:
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
            case rfbEncodingTight:
#endif
#endif
                /* The first supported encoding is the 'preferred' encoding */
                if (cl->preferredEncoding == -1)
                    cl->preferredEncoding = enc;


                break;
            case rfbEncodingXCursor:
                if(!cl->screen->dontConvertRichCursorToXCursor)
                {
                    /* if cursor was drawn, hide the cursor */
                    if(!cl->enableCursorShapeUpdates)
                        rfbRedrawAfterHideCursor(cl,NULL);

                    cl->enableCursorShapeUpdates = TRUE;
                    cl->cursorWasChanged = TRUE;
                }
                break;
            case rfbEncodingRichCursor:
                /* if cursor was drawn, hide the cursor */
                if(!cl->enableCursorShapeUpdates)
                    rfbRedrawAfterHideCursor(cl,NULL);

                cl->enableCursorShapeUpdates = TRUE;
                cl->useRichCursorEncoding = TRUE;
                cl->cursorWasChanged = TRUE;
                break;
            case rfbEncodingPointerPos:
                if (!cl->enableCursorPosUpdates)
                {
                    cl->enableCursorPosUpdates = TRUE;
                    cl->cursorWasMoved = TRUE;
                }
                break;
            case rfbEncodingLastRect:
                if (!cl->enableLastRectEncoding)
                {
                    cl->enableLastRectEncoding = TRUE;
                }
                break;
            case rfbEncodingNewFBSize:
                if (!cl->useNewFBSize)
                {
                    cl->useNewFBSize = TRUE;
                }
                break;
            case rfbEncodingKeyboardLedState:
                if (!cl->enableKeyboardLedState)
                {
                    cl->enableKeyboardLedState = TRUE;
                }
                break;
            case rfbEncodingSupportedMessages:
                if (!cl->enableSupportedMessages)
                {
                    cl->enableSupportedMessages = TRUE;
                }
                break;
            case rfbEncodingSupportedEncodings:
                if (!cl->enableSupportedEncodings)
                {
                    cl->enableSupportedEncodings = TRUE;
                }
                break;
            case rfbEncodingServerIdentity:
                if (!cl->enableServerIdentity)
                {
                    cl->enableServerIdentity = TRUE;
                }
                break;
            case rfbEncodingXvp:
                if (!rfbSendXvp(cl, 1, rfbXvp_Init))
                {
                    rfbCloseClient(cl);
                    return;
                }
                break;
            default:
#ifdef LIBVNCSERVER_HAVE_LIBZ
                if ( enc >= (uint32_t)rfbEncodingCompressLevel0 &&
                        enc <= (uint32_t)rfbEncodingCompressLevel9 )
                {
                    cl->zlibCompressLevel = enc & 0x0F;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
                    cl->tightCompressLevel = enc & 0x0F;
#endif
                }
                else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 &&
                          enc <= (uint32_t)rfbEncodingQualityLevel9 )
                {
                    cl->tightQualityLevel = enc & 0x0F;
                }
                else
#endif
                {
                    rfbExtensionData* e;
                    for(e = cl->extensions; e;)
                    {
                        rfbExtensionData* next = e->next;
                        if(e->extension->enablePseudoEncoding &&
                                e->extension->enablePseudoEncoding(cl,
                                        &e->data, (int)enc))
                            /* ext handles this encoding */
                            break;
                        e = next;
                    }
                    if(e == NULL)
                    {
                        rfbBool handled = FALSE;
                        /* if the pseudo encoding is not handled by the
                           enabled extensions, search through all
                           extensions. */
                        rfbProtocolExtension* e;

                        for(e = rfbGetExtensionIterator(); e;)
                        {
                            int* encs = e->pseudoEncodings;
                            while(encs && *encs!=0)
                            {
                                if(*encs==(int)enc)
                                {
                                    void* data = NULL;
                                    if(e->enablePseudoEncoding(cl, &data, (int)enc))
                                    {
                                        rfbEnableExtension(cl, e, data);
                                        handled = TRUE;
                                        e = NULL;
                                        break;
                                    }
                                }
                                encs++;
                            }

                            if(e)
                                e = e->next;
                        }
                        rfbReleaseExtensionIterator();
                    }
                }
            }
        }



        if (cl->preferredEncoding == -1)
        {
            if (lastPreferredEncoding==-1)
            {
                cl->preferredEncoding = rfbEncodingRaw;
            }
            else
            {
                cl->preferredEncoding = lastPreferredEncoding;
            }
        }

        if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates)
        {
            cl->enableCursorPosUpdates = FALSE;
        }

        return;
    }


    case rfbFramebufferUpdateRequest:
    {
        sraRegionPtr tmpRegion;

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbFramebufferUpdateRequestMsg-1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        /* The values come in based on the scaled screen, we need to convert them to
         * values based on the main screen's coordinate system
         */
        if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h,cl))
        {
            return;
        }

        EnterCriticalSection(&cl->csRgn);
            tmpRegion =
                sraRgnCreateRect(msg.fur.x,
                                 msg.fur.y,
                                 msg.fur.x+msg.fur.w,
                                 msg.fur.y+msg.fur.h);

            LOCK(cl->updateMutex);
            sraRgnOr(cl->requestedRegion,tmpRegion);

            if (!cl->readyForSetColourMapEntries)
            {
                /* client hasn't sent a SetPixelFormat so is using server's */
                cl->readyForSetColourMapEntries = TRUE;
                if (!cl->format.trueColour)
                {
                    if (!rfbSetClientColourMap(cl, 0, 0))
                    {
                        sraRgnDestroy(tmpRegion);
                        TSIGNAL(cl->updateCond);
                        UNLOCK(cl->updateMutex);
                        return;
                    }
                }
            }

            if (!msg.fur.incremental)
            {
                sraRgnOr(cl->modifiedRegion,tmpRegion);
                sraRgnSubtract(cl->copyRegion,tmpRegion);
            }
            TSIGNAL(cl->updateCond);
            UNLOCK(cl->updateMutex);

            sraRgnDestroy(tmpRegion);
        LeaveCriticalSection(&cl->csRgn);
        return;
    }

    case rfbKeyEvent:

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbKeyEventMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        if(!cl->viewOnly)
        {
            cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl);
        }

        return;


    case rfbPointerEvent:

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbPointerEventMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        if (cl->screen->pointerClient && cl->screen->pointerClient != cl)
            return;

        if (msg.pe.buttonMask == 0)
            cl->screen->pointerClient = NULL;
        else
            cl->screen->pointerClient = cl;

        if(!cl->viewOnly)
        {
            if (msg.pe.buttonMask != cl->lastPtrButtons ||
                    cl->screen->deferPtrUpdateTime == 0)
            {
                cl->screen->ptrAddEvent(msg.pe.buttonMask,
                                        ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x)),
                                        ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y)),
                                        cl);
                cl->lastPtrButtons = msg.pe.buttonMask;
            }
            else
            {
                cl->lastPtrX = ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x));
                cl->lastPtrY = ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y));
                cl->lastPtrButtons = msg.pe.buttonMask;
            }
        }
        return;


    case rfbFileTransfer:
        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbFileTransferMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }
        msg.ft.size         = Swap32IfLE(msg.ft.size);
        msg.ft.length       = Swap32IfLE(msg.ft.length);
        /* record statistics in rfbProcessFileTransfer as length is filled with garbage when it is not valid */
        rfbProcessFileTransfer(cl, msg.ft.contentType, msg.ft.contentParam, msg.ft.size, msg.ft.length);
        return;

    case rfbSetSW:
        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbSetSWMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }
        msg.sw.x = Swap16IfLE(msg.sw.x);
        msg.sw.y = Swap16IfLE(msg.sw.y);
        /* msg.sw.status is not initialized in the ultraVNC viewer and contains random numbers (why???) */

        if (cl->screen->setSingleWindow!=NULL)
            cl->screen->setSingleWindow(cl, msg.sw.x, msg.sw.y);
        return;

    case rfbSetServerInput:
        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbSetServerInputMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        /* msg.sim.pad is not initialized in the ultraVNC viewer and contains random numbers (why???) */
        /* msg.sim.pad = Swap16IfLE(msg.sim.pad); */

        if (cl->screen->setServerInput!=NULL)
            cl->screen->setServerInput(cl, msg.sim.status);
        return;

    case rfbTextChat:
        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbTextChatMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        msg.tc.pad2   = Swap16IfLE(msg.tc.pad2);
        msg.tc.length = Swap32IfLE(msg.tc.length);

        switch (msg.tc.length)
        {
        case rfbTextChatOpen:
        case rfbTextChatClose:
        case rfbTextChatFinished:
            /* commands do not have text following */
            /* Why couldn't they have used the pad byte??? */
            str=NULL;
            break;
        default:
            if ((msg.tc.length>0) && (msg.tc.length<rfbTextMaxSize))
            {
                str = (char *)MemAlloc(msg.tc.length);
                if (str==NULL)
                {
                    rfbCloseClient(cl);
                    return;
                }
                if ((n = rfbReadExact(cl, str, msg.tc.length)) <= 0)
                {
                    MemFree(str);
                    rfbCloseClient(cl);
                    return;
                }
            }
            else
            {
                rfbCloseClient(cl);
                return;
            }
        }

        /* Note: length can be commands: rfbTextChatOpen, rfbTextChatClose, and rfbTextChatFinished
         * at which point, the str is NULL (as it is not sent)
         */
        if (cl->screen->setTextChat!=NULL)
            cl->screen->setTextChat(cl, msg.tc.length, str);

        MemFree(str);
        return;


    case rfbClientCutText:

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbClientCutTextMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }

        msg.cct.length = Swap32IfLE(msg.cct.length);

        str = (char *)MemAlloc(msg.cct.length);

        if ((n = rfbReadExact(cl, str, msg.cct.length)) <= 0)
        {
            MemFree(str);
            rfbCloseClient(cl);
            return;
        }
        if((!cl->viewOnly) && (cl->allowClipboard))
        {
            cl->screen->setXCutText(str, msg.cct.length, cl);
        }
        MemFree(str);

        return;

    case rfbPalmVNCSetScaleFactor:
        cl->PalmVNC = TRUE;
        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbSetScaleMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }
        rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale);

        rfbSendNewScaleSize(cl);
        return;

    case rfbSetScale:

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbSetScaleMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }
        rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale);

        rfbSendNewScaleSize(cl);
        return;

    case rfbXvp:

        if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
                              sz_rfbXvpMsg - 1)) <= 0)
        {
            rfbCloseClient(cl);
            return;
        }
        /* only version when is defined, so echo back a fail */
        if(msg.xvp.version != 1)
        {
            rfbSendXvp(cl, msg.xvp.version, rfbXvp_Fail);
        }
        else
        {
            /* if the hook exists and fails, send a fail msg */
            if(cl->screen->xvpHook && !cl->screen->xvpHook(cl, msg.xvp.version, msg.xvp.code))
                rfbSendXvp(cl, 1, rfbXvp_Fail);
        }
        return;

    default:
    {
        rfbExtensionData *e,*next;

        for(e=cl->extensions; e;)
        {
            next = e->next;
            if(e->extension->handleMessage &&
                    e->extension->handleMessage(cl, e->data, &msg))
            {
                return;
            }
            e = next;
        }

        rfbCloseClient(cl);
        return;
    }
    }
}



/*
 * rfbSendFramebufferUpdate - send the currently pending framebuffer update to
 * the RFB client.
 * givenUpdateRegion is not changed.
 */

rfbBool
rfbSendFramebufferUpdate(rfbClientPtr cl,
                         sraRegionPtr givenUpdateRegion)
{
    sraRectangleIterator* i=NULL;
    sraRect rect;
    int nUpdateRegionRects;
    rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf;
    sraRegionPtr updateRegion,updateCopyRegion,tmpRegion;
    int dx, dy;
    rfbBool sendCursorShape = FALSE;
    rfbBool sendCursorPos = FALSE;
    rfbBool sendKeyboardLedState = FALSE;
    rfbBool sendSupportedMessages = FALSE;
    rfbBool sendSupportedEncodings = FALSE;
    rfbBool sendServerIdentity = FALSE;
    rfbBool result = TRUE;


    if(cl->screen->displayHook)
        cl->screen->displayHook(cl);

    /*
     * If framebuffer size was changed and the client supports NewFBSize
     * encoding, just send NewFBSize marker and return.
     */

    if (cl->useNewFBSize && cl->newFBSizePending)
    {
        LOCK(cl->updateMutex);
        cl->newFBSizePending = FALSE;
        UNLOCK(cl->updateMutex);
        fu->type = rfbFramebufferUpdate;
        fu->nRects = Swap16IfLE(1);
        cl->ublen = sz_rfbFramebufferUpdateMsg;
        if (!rfbSendNewFBSize(cl, cl->scaledScreen->width, cl->scaledScreen->height))
        {
            if(cl->screen->displayFinishedHook)
                cl->screen->displayFinishedHook(cl, FALSE);
            return FALSE;
        }
        result = rfbSendUpdateBuf(cl);
        if(cl->screen->displayFinishedHook)
            cl->screen->displayFinishedHook(cl, result);
        return result;
    }

    /*
     * If this client understands cursor shape updates, cursor should be
     * removed from the framebuffer. Otherwise, make sure it's put up.
     */

    if (cl->enableCursorShapeUpdates)
    {
        if (cl->cursorWasChanged && cl->readyForSetColourMapEntries)
            sendCursorShape = TRUE;
    }

    /*
     * Do we plan to send cursor position update?
     */

    if (cl->enableCursorPosUpdates && cl->cursorWasMoved)
        sendCursorPos = TRUE;

    /*
     * Do we plan to send a keyboard state update?
     */
    if ((cl->enableKeyboardLedState) &&
            (cl->screen->getKeyboardLedStateHook!=NULL))
    {
        int x;
        x=cl->screen->getKeyboardLedStateHook(cl->screen);
        if (x!=cl->lastKeyboardLedState)
        {
            sendKeyboardLedState = TRUE;
            cl->lastKeyboardLedState=x;
        }
    }

    /*
     * Do we plan to send a rfbEncodingSupportedMessages?
     */
    if (cl->enableSupportedMessages)
    {
        sendSupportedMessages = TRUE;
        /* We only send this message ONCE <per setEncodings message received>
         * (We disable it here)
         */
        cl->enableSupportedMessages = FALSE;
    }
    /*
     * Do we plan to send a rfbEncodingSupportedEncodings?
     */
    if (cl->enableSupportedEncodings)
    {
        sendSupportedEncodings = TRUE;
        /* We only send this message ONCE <per setEncodings message received>
         * (We disable it here)
         */
        cl->enableSupportedEncodings = FALSE;
    }
    /*
     * Do we plan to send a rfbEncodingServerIdentity?
     */
    if (cl->enableServerIdentity)
    {
        sendServerIdentity = TRUE;
        /* We only send this message ONCE <per setEncodings message received>
         * (We disable it here)
         */
        cl->enableServerIdentity = FALSE;
    }

    EnterCriticalSection(&cl->csRgn);
        LOCK(cl->updateMutex);

        /*
         * The modifiedRegion may overlap the destination copyRegion.  We remove
         * any overlapping bits from the copyRegion (since they'd only be
         * overwritten anyway).
         */

        sraRgnSubtract(cl->copyRegion,cl->modifiedRegion);

        /*
         * The client is interested in the region requestedRegion.  The region
         * which should be updated now is the intersection of requestedRegion
         * and the union of modifiedRegion and copyRegion.  If it's empty then
         * no update is needed.
         */

        updateRegion = sraRgnCreateRgn(givenUpdateRegion);
        if(cl->screen->progressiveSliceHeight>0)
        {
            int height=cl->screen->progressiveSliceHeight,
                y=cl->progressiveSliceY;
            sraRegionPtr bbox=sraRgnBBox(updateRegion);
            sraRect rect;
            if(sraRgnPopRect(bbox,&rect,0))
            {
                sraRegionPtr slice;
                if(y<rect.y1 || y>=rect.y2)
                    y=rect.y1;
                slice=sraRgnCreateRect(0,y,cl->screen->width,y+height);
                sraRgnAnd(updateRegion,slice);
                sraRgnDestroy(slice);
            }
            sraRgnDestroy(bbox);
            y+=height;
            if(y>=cl->screen->height)
                y=0;
            cl->progressiveSliceY=y;
        }

        sraRgnOr(updateRegion,cl->copyRegion);
        if(!sraRgnAnd(updateRegion,cl->requestedRegion) &&
                sraRgnEmpty(updateRegion) &&
                (cl->enableCursorShapeUpdates ||
                 (cl->cursorX == cl->screen->cursorX && cl->cursorY == cl->screen->cursorY)) &&
                !sendCursorShape && !sendCursorPos && !sendKeyboardLedState &&
                !sendSupportedMessages && !sendSupportedEncodings && !sendServerIdentity)
        {
            sraRgnDestroy(updateRegion);
            UNLOCK(cl->updateMutex);
            LeaveCriticalSection(&cl->csRgn);
            if(cl->screen->displayFinishedHook)
                cl->screen->displayFinishedHook(cl, TRUE);
            return TRUE;
        }

        /*
         * We assume that the client doesn't have any pixel data outside the
         * requestedRegion.  In other words, both the source and destination of a
         * copy must lie within requestedRegion.  So the region we can send as a
         * copy is the intersection of the copyRegion with both the requestedRegion
         * and the requestedRegion translated by the amount of the copy.  We set
         * updateCopyRegion to this.
         */

        updateCopyRegion = sraRgnCreateRgn(cl->copyRegion);
        sraRgnAnd(updateCopyRegion,cl->requestedRegion);
        tmpRegion = sraRgnCreateRgn(cl->requestedRegion);
        sraRgnOffset(tmpRegion,cl->copyDX,cl->copyDY);
        sraRgnAnd(updateCopyRegion,tmpRegion);
        sraRgnDestroy(tmpRegion);
        dx = cl->copyDX;
        dy = cl->copyDY;

        /*
         * Next we remove updateCopyRegion from updateRegion so that updateRegion
         * is the part of this update which is sent as ordinary pixel data (i.e not
         * a copy).
         */

        sraRgnSubtract(updateRegion,updateCopyRegion);

        /*
         * Finally we leave modifiedRegion to be the remainder (if any) of parts of
         * the screen which are modified but outside the requestedRegion.  We also
         * empty both the requestedRegion and the copyRegion - note that we never
         * carry over a copyRegion for a future update.
         */

        sraRgnOr(cl->modifiedRegion,cl->copyRegion);
        sraRgnSubtract(cl->modifiedRegion,updateRegion);
        sraRgnSubtract(cl->modifiedRegion,updateCopyRegion);

        sraRgnMakeEmpty(cl->requestedRegion);
        sraRgnMakeEmpty(cl->copyRegion);
        cl->copyDX = 0;
        cl->copyDY = 0;

        UNLOCK(cl->updateMutex);
    LeaveCriticalSection(&cl->csRgn);

    if (!cl->enableCursorShapeUpdates)
    {
        if(cl->cursorX != cl->screen->cursorX || cl->cursorY != cl->screen->cursorY)
        {
            rfbRedrawAfterHideCursor(cl,updateRegion);
            LOCK(cl->screen->cursorMutex);
            cl->cursorX = cl->screen->cursorX;
            cl->cursorY = cl->screen->cursorY;
            UNLOCK(cl->screen->cursorMutex);
            rfbRedrawAfterHideCursor(cl,updateRegion);
        }
        rfbShowCursor(cl);
    }

    /*
     * Now send the update.
     */

    if (cl->preferredEncoding == rfbEncodingCoRRE)
    {
        nUpdateRegionRects = 0;

        for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);)
        {
            int x = rect.x1;
            int y = rect.y1;
            int w = rect.x2 - x;
            int h = rect.y2 - y;
            int rectsPerRow, rows;
            /* We need to count the number of rects in the scaled screen */
            if (cl->screen!=cl->scaledScreen)
                rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h);
            rectsPerRow = (w-1)/cl->correMaxWidth+1;
            rows = (h-1)/cl->correMaxHeight+1;
            nUpdateRegionRects += rectsPerRow*rows;
        }
        sraRgnReleaseIterator(i);
        i=NULL;
    }
    else if (cl->preferredEncoding == rfbEncodingUltra)
    {
        nUpdateRegionRects = 0;

        for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);)
        {
            int x = rect.x1;
            int y = rect.y1;
            int w = rect.x2 - x;
            int h = rect.y2 - y;
            /* We need to count the number of rects in the scaled screen */
            if (cl->screen!=cl->scaledScreen)
                rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h);
            nUpdateRegionRects += (((h-1) / (ULTRA_MAX_SIZE( w ) / w)) + 1);
        }
        sraRgnReleaseIterator(i);
        i=NULL;
#ifdef LIBVNCSERVER_HAVE_LIBZ
    }
    else if (cl->preferredEncoding == rfbEncodingZlib)
    {
        nUpdateRegionRects = 0;

        for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);)
        {
            int x = rect.x1;
            int y = rect.y1;
            int w = rect.x2 - x;
            int h = rect.y2 - y;
            /* We need to count the number of rects in the scaled screen */
            if (cl->screen!=cl->scaledScreen)
                rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h);
            nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1);
        }
        sraRgnReleaseIterator(i);
        i=NULL;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
    }
    else if (cl->preferredEncoding == rfbEncodingTight)
    {
        nUpdateRegionRects = 0;

        for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);)
        {
            int x = rect.x1;
            int y = rect.y1;
            int w = rect.x2 - x;
            int h = rect.y2 - y;
            int n;
            /* We need to count the number of rects in the scaled screen */
            if (cl->screen!=cl->scaledScreen)
                rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h);
            n = rfbNumCodedRectsTight(cl, x, y, w, h);
            if (n == 0)
            {
                nUpdateRegionRects = 0xFFFF;
                break;
            }
            nUpdateRegionRects += n;
        }
        sraRgnReleaseIterator(i);
        i=NULL;
#endif
#endif
    }
    else
    {
        nUpdateRegionRects = sraRgnCountRects(updateRegion);
    }

    fu->type = rfbFramebufferUpdate;
    if (nUpdateRegionRects != 0xFFFF)
    {
        if(cl->screen->maxRectsPerUpdate>0
                /* CoRRE splits the screen into smaller squares */
                && cl->preferredEncoding != rfbEncodingCoRRE
                /* Ultra encoding splits rectangles up into smaller chunks */
                && cl->preferredEncoding != rfbEncodingUltra
#ifdef LIBVNCSERVER_HAVE_LIBZ
                /* Zlib encoding splits rectangles up into smaller chunks */
                && cl->preferredEncoding != rfbEncodingZlib
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
                /* Tight encoding counts the rectangles differently */
                && cl->preferredEncoding != rfbEncodingTight
#endif
#endif
                && nUpdateRegionRects>cl->screen->maxRectsPerUpdate)
        {
            EnterCriticalSection(&cl->csRgn);
            {
                sraRegion* newUpdateRegion = sraRgnBBox(updateRegion);
                sraRgnDestroy(updateRegion);
                updateRegion = newUpdateRegion;
                nUpdateRegionRects = sraRgnCountRects(updateRegion);
            }
            LeaveCriticalSection(&cl->csRgn);
        }
        fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) +
                                           nUpdateRegionRects +
                                           !!sendCursorShape + !!sendCursorPos + !!sendKeyboardLedState +
                                           !!sendSupportedMessages + !!sendSupportedEncodings + !!sendServerIdentity));
    }
    else
    {
        fu->nRects = 0xFFFF;
    }
    cl->ublen = sz_rfbFramebufferUpdateMsg;

    if (sendCursorShape)
    {
        cl->cursorWasChanged = FALSE;
        if (!rfbSendCursorShape(cl))
            goto updateFailed;
    }

    if (sendCursorPos)
    {
        cl->cursorWasMoved = FALSE;
        if (!rfbSendCursorPos(cl))
            goto updateFailed;
    }

    if (sendKeyboardLedState)
    {
        if (!rfbSendKeyboardLedState(cl))
            goto updateFailed;
    }

    if (sendSupportedMessages)
    {
        if (!rfbSendSupportedMessages(cl))
            goto updateFailed;
    }
    if (sendSupportedEncodings)
    {
        if (!rfbSendSupportedEncodings(cl))
            goto updateFailed;
    }
    if (sendServerIdentity)
    {
        if (!rfbSendServerIdentity(cl))
            goto updateFailed;
    }

    if (!sraRgnEmpty(updateCopyRegion))
    {
        if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy))
            goto updateFailed;
    }

    for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);)
    {
        int x = rect.x1;
        int y = rect.y1;
        int w = rect.x2 - x;
        int h = rect.y2 - y;

        /* We need to count the number of rects in the scaled screen */
        if (cl->screen!=cl->scaledScreen)
            rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h);

        switch (cl->preferredEncoding)
        {
        case -1:
        case rfbEncodingRaw:
            if (!rfbSendRectEncodingRaw(cl, x, y, w, h))
                goto updateFailed;
            break;
        case rfbEncodingRRE:
            if (!rfbSendRectEncodingRRE(cl, x, y, w, h))
                goto updateFailed;
            break;
        case rfbEncodingCoRRE:
            if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h))
                goto updateFailed;
            break;
        case rfbEncodingHextile:
            if (!rfbSendRectEncodingHextile(cl, x, y, w, h))
                goto updateFailed;
            break;
        case rfbEncodingUltra:
            if (!rfbSendRectEncodingUltra(cl, x, y, w, h))
                goto updateFailed;
            break;
#ifdef LIBVNCSERVER_HAVE_LIBZ
        case rfbEncodingZlib:
            if (!rfbSendRectEncodingZlib(cl, x, y, w, h))
                goto updateFailed;
            break;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
        case rfbEncodingTight:
            if (!rfbSendRectEncodingTight(cl, x, y, w, h))
                goto updateFailed;
            break;
#endif
#endif
#ifdef LIBVNCSERVER_HAVE_LIBZ
        case rfbEncodingZRLE:
        case rfbEncodingZYWRLE:
            if (!rfbSendRectEncodingZRLE(cl, x, y, w, h))
                goto updateFailed;
            break;
#endif
        }
    }
    if (i)
    {
        sraRgnReleaseIterator(i);
        i = NULL;
    }

    if ( nUpdateRegionRects == 0xFFFF &&
            !rfbSendLastRectMarker(cl) )
        goto updateFailed;

    if (!rfbSendUpdateBuf(cl))
    {
updateFailed:
        result = FALSE;
    }

    if (!cl->enableCursorShapeUpdates)
    {
        rfbHideCursor(cl);
    }

    if(i)
        sraRgnReleaseIterator(i);
    sraRgnDestroy(updateRegion);
    sraRgnDestroy(updateCopyRegion);

    if(cl->screen->displayFinishedHook)
        cl->screen->displayFinishedHook(cl, result);
    return result;
}


/*
 * Send the copy region as a string of CopyRect encoded rectangles.
 * The only slightly tricky thing is that we should send the messages in
 * the correct order so that an earlier CopyRect will not corrupt the source
 * of a later one.
 */

rfbBool
rfbSendCopyRegion(rfbClientPtr cl,
                  sraRegionPtr reg,
                  int dx,
                  int dy)
{
    int x, y, w, h;
    rfbFramebufferUpdateRectHeader rect;
    rfbCopyRect cr;
    sraRectangleIterator* i;
    sraRect rect1;

    /* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */
    i = sraRgnGetReverseIterator(reg,dx>0,dy>0);

    /* correct for the scale of the screen */
    dx = ScaleX(cl->screen, cl->scaledScreen, dx);
    dy = ScaleX(cl->screen, cl->scaledScreen, dy);

    while(sraRgnIteratorNext(i,&rect1))
    {
        x = rect1.x1;
        y = rect1.y1;
        w = rect1.x2 - x;
        h = rect1.y2 - y;

        /* correct for scaling (if necessary) */
        rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h);

        rect.r.x = Swap16IfLE(x);
        rect.r.y = Swap16IfLE(y);
        rect.r.w = Swap16IfLE(w);
        rect.r.h = Swap16IfLE(h);
        rect.encoding = Swap32IfLE(rfbEncodingCopyRect);

        memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
               sz_rfbFramebufferUpdateRectHeader);
        cl->ublen += sz_rfbFramebufferUpdateRectHeader;

        cr.srcX = Swap16IfLE(x - dx);
        cr.srcY = Swap16IfLE(y - dy);

        memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect);
        cl->ublen += sz_rfbCopyRect;

    }
    sraRgnReleaseIterator(i);

    return TRUE;
}

/*
 * Send a given rectangle in raw encoding (rfbEncodingRaw).
 */

rfbBool
rfbSendRectEncodingRaw(rfbClientPtr cl,
                       int x,
                       int y,
                       int w,
                       int h)
{
    rfbFramebufferUpdateRectHeader rect;
    int nlines;
    int bytesPerLine = w * (cl->format.bitsPerPixel / 8);
    char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
                   + (x * (cl->scaledScreen->bitsPerPixel / 8)));

    /* Flush the buffer to guarantee correct alignment for translateFn(). */
    if (cl->ublen > 0)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.r.x = Swap16IfLE(x);
    rect.r.y = Swap16IfLE(y);
    rect.r.w = Swap16IfLE(w);
    rect.r.h = Swap16IfLE(h);
    rect.encoding = Swap32IfLE(rfbEncodingRaw);

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine;

    while (TRUE)
    {
        if (nlines > h)
            nlines = h;

        (*cl->translateFn)(cl->translateLookupTable,
                           &(cl->screen->serverFormat),
                           &cl->format, fbptr, &cl->updateBuf[cl->ublen],
                           cl->scaledScreen->paddedWidthInBytes, w, nlines);

        cl->ublen += nlines * bytesPerLine;
        h -= nlines;

        if (h == 0)     /* rect fitted in buffer, do next one */
            return TRUE;

        /* buffer full - flush partial rect and do another nlines */

        if (!rfbSendUpdateBuf(cl))
            return FALSE;

        fbptr += (cl->scaledScreen->paddedWidthInBytes * nlines);

        nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine;
        if (nlines == 0)
        {
            rfbCloseClient(cl);
            return FALSE;
        }
    }
}



/*
 * Send an empty rectangle with encoding field set to value of
 * rfbEncodingLastRect to notify client that this is the last
 * rectangle in framebuffer update ("LastRect" extension of RFB
 * protocol).
 */

rfbBool
rfbSendLastRectMarker(rfbClientPtr cl)
{
    rfbFramebufferUpdateRectHeader rect;

    if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.encoding = Swap32IfLE(rfbEncodingLastRect);
    rect.r.x = 0;
    rect.r.y = 0;
    rect.r.w = 0;
    rect.r.h = 0;

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    return TRUE;
}


/*
 * Send NewFBSize pseudo-rectangle. This tells the client to change
 * its framebuffer size.
 */

rfbBool
rfbSendNewFBSize(rfbClientPtr cl,
                 int w,
                 int h)
{
    rfbFramebufferUpdateRectHeader rect;

    if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE)
    {
        if (!rfbSendUpdateBuf(cl))
            return FALSE;
    }

    rect.encoding = Swap32IfLE(rfbEncodingNewFBSize);
    rect.r.x = 0;
    rect.r.y = 0;
    rect.r.w = Swap16IfLE(w);
    rect.r.h = Swap16IfLE(h);

    memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
           sz_rfbFramebufferUpdateRectHeader);
    cl->ublen += sz_rfbFramebufferUpdateRectHeader;

    return TRUE;
}


/*
 * Send the contents of cl->updateBuf.  Returns 1 if successful, -1 if
 * not (errno should be set).
 */

rfbBool
rfbSendUpdateBuf(rfbClientPtr cl)
{
    if(cl->sock<0)
        return FALSE;

    if (rfbWriteExact(cl, cl->updateBuf, cl->ublen) < 0)
    {
        rfbCloseClient(cl);
        return FALSE;
    }

    cl->ublen = 0;
    return TRUE;
}

/*
 * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the
 * client, using values from the currently installed colormap.
 */

rfbBool
rfbSendSetColourMapEntries(rfbClientPtr cl,
                           int firstColour,
                           int nColours)
{
    char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2];
    char *wbuf = buf;
    rfbSetColourMapEntriesMsg *scme;
    uint16_t *rgb;
    rfbColourMap* cm = &cl->screen->colourMap;
    int i, len;

    if (nColours > 256)
    {
        /* some rare hardware has, e.g., 4096 colors cells: PseudoColor:12 */
        wbuf = (char *) MemAlloc(sz_rfbSetColourMapEntriesMsg + nColours * 3 * 2);
    }

    scme = (rfbSetColourMapEntriesMsg *)wbuf;
    rgb = (uint16_t *)(&wbuf[sz_rfbSetColourMapEntriesMsg]);

    scme->type = rfbSetColourMapEntries;

    scme->firstColour = Swap16IfLE(firstColour);
    scme->nColours = Swap16IfLE(nColours);

    len = sz_rfbSetColourMapEntriesMsg;

    for (i = 0; i < nColours; i++)
    {
        if(i<(int)cm->count)
        {
            if(cm->is16)
            {
                rgb[i*3] = Swap16IfLE(cm->data.shorts[i*3]);
                rgb[i*3+1] = Swap16IfLE(cm->data.shorts[i*3+1]);
                rgb[i*3+2] = Swap16IfLE(cm->data.shorts[i*3+2]);
            }
            else
            {
                rgb[i*3] = Swap16IfLE((unsigned short)cm->data.bytes[i*3]);
                rgb[i*3+1] = Swap16IfLE((unsigned short)cm->data.bytes[i*3+1]);
                rgb[i*3+2] = Swap16IfLE((unsigned short)cm->data.bytes[i*3+2]);
            }
        }
    }

    len += nColours * 3 * 2;

    LOCK(cl->sendMutex);
    if (rfbWriteExact(cl, wbuf, len) < 0)
    {
        rfbCloseClient(cl);
        if (wbuf != buf) MemFree(wbuf);
        UNLOCK(cl->sendMutex);
        return FALSE;
    }
    UNLOCK(cl->sendMutex);

    if (wbuf != buf) MemFree(wbuf);
    return TRUE;
}

/*
 * rfbSendBell sends a Bell message to all the clients.
 */

void
rfbSendBell(rfbScreenInfoPtr rfbScreen)
{
    rfbClientIteratorPtr i;
    rfbClientPtr cl;
    rfbBellMsg b;

    i = rfbGetClientIterator(rfbScreen);
    while((cl=rfbClientIteratorNext(i)))
    {
        b.type = rfbBell;
        LOCK(cl->sendMutex);
        if (rfbWriteExact(cl, (char *)&b, sz_rfbBellMsg) < 0)
        {
            rfbCloseClient(cl);
        }
        UNLOCK(cl->sendMutex);
    }
    rfbReleaseClientIterator(i);
}


/*
 * rfbSendServerCutText sends a ServerCutText message to all the clients.
 */

void
rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len)
{
    rfbClientPtr cl;
    rfbServerCutTextMsg sct;
    rfbClientIteratorPtr iterator;

    iterator = rfbGetClientIterator(rfbScreen);
    while ((cl = rfbClientIteratorNext(iterator)) != NULL)
    {
        sct.type = rfbServerCutText;
        sct.length = Swap32IfLE(len);
        LOCK(cl->sendMutex);
        if (rfbWriteExact(cl, (char *)&sct,
                          sz_rfbServerCutTextMsg) < 0)
        {
            rfbCloseClient(cl);
            UNLOCK(cl->sendMutex);
            continue;
        }
        if (rfbWriteExact(cl, str, len) < 0)
        {
            rfbCloseClient(cl);
        }
        UNLOCK(cl->sendMutex);
    }
    rfbReleaseClientIterator(iterator);
}

/*****************************************************************************
 *
 * UDP can be used for keyboard and pointer events when the underlying
 * network is highly reliable.  This is really here to support ORL's
 * videotile, whose TCP implementation doesn't like sending lots of small
 * packets (such as 100s of pen readings per second!).
 */

static unsigned char ptrAcceleration = 50;

void
rfbNewUDPConnection(rfbScreenInfoPtr rfbScreen,
                    int sock)
{
    x_write(sock, &ptrAcceleration, 1);
}

/*
 * Because UDP is a message based service, we can't read the first byte and
 * then the rest of the packet separately like we do with TCP.  We will always
 * get a whole packet delivered in one go, so we ask read() for the maximum
 * number of bytes we can possibly get.
 */

void
rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen)
{
    int n;
    rfbClientPtr cl=rfbScreen->udpClient;
    rfbClientToServerMsg msg;

    if((!cl) || cl->onHold)
        return;
    //if ((n = _read(rfbScreen->udpSock, (char *)&msg, sizeof(msg))) <= 0) {
    if ((n = recv(rfbScreen->udpSock, (char *)&msg, sizeof(msg), 0)) <= 0)
    {
        rfbDisconnectUDPSock(rfbScreen);
        return;
    }

    switch (msg.type)
    {

    case rfbKeyEvent:
        if (n != sz_rfbKeyEventMsg)
        {
            rfbDisconnectUDPSock(rfbScreen);
            return;
        }
        cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl);
        break;

    case rfbPointerEvent:
        if (n != sz_rfbPointerEventMsg)
        {
            rfbDisconnectUDPSock(rfbScreen);
            return;
        }
        cl->screen->ptrAddEvent(msg.pe.buttonMask,
                                Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl);
        break;

    default:
        rfbDisconnectUDPSock(rfbScreen);
    }
}


