text: //  Copyright (C) 2001-2006 Constantin Kaplinsky. All Rights Reserved.
  //  Copyright (C) 2002 Vladimir Vologzhanin. All Rights Reserved.
  //  Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
  //  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
  //
  //  This file is part of the VNC system.
  //
  //  The VNC system 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 program 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 program; if not, write to the Free Software
  //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  //  USA.
  //
  // TightVNC distribution homepage on the Web: http://www.tightvnc.com/
  //
  // If the source code for the VNC system is not available from the place 
  // whence you received this file, check http://www.uk.research.att.com/vnc or contact
  // the authors on vnc@uk.research.att.com for information on obtaining it.
  
  
  // vncClient.cpp
  
  // The per-client object.  This object takes care of all per-client stuff,
  // such as socket input and buffering of updates.
  
  // vncClient class handles the following functions:
  // - Recieves requests from the connected client and
  //   handles them
  // - Handles incoming updates properly, using a vncBuffer
  //   object to keep track of screen changes
  // It uses a vncBuffer and is passed the vncDesktop and
  // vncServer to communicate with.
  
  // Includes
  #include "stdhdrs.h"
  #include <omnithread.h>
  #include "resource.h"
  
  // Custom
  #include "vncClient.h"
  #include "VSocket.h"
  #include "vncDesktop.h"
  #include "vncRegion.h"
  #include "vncBuffer.h"
  #include "vncService.h"
  #include "vncPasswd.h"
  #include "vncAcceptDialog.h"
  #include "vncKeymap.h"
  #include "Windows.h"
  extern "C" {
  #include "d3des.h"
  }
  
  #include "FileTransferItemInfo.h"
  #include "vncMenu.h"
  
  //
  // Normally, using macros is no good, but this macro saves us from
  // writing constants twice -- it constructs signature names from codes.
  // Note that "code_sym" argument should be a single symbol, not an expression.
  //
  
  #define SetCapInfo(cap_ptr, code_sym, vendor)			\
  {														\
  	rfbCapabilityInfo *pcap = (cap_ptr);				\
  	pcap->code = Swap32IfLE(code_sym);					\
  	memcpy(pcap->vendorSignature, (vendor),				\
  	sz_rfbCapabilityInfoVendor);						\
  	memcpy(pcap->nameSignature, sig_##code_sym,			\
  	sz_rfbCapabilityInfoName);							\
  }
  
  // vncClient thread class
  
  class vncClientThread : public omni_thread
  {
  public:
  	char * ConvertPath(char *path);
  
  	// Init
  	virtual BOOL Init(vncClient *client,
  					  vncServer *server,
  					  VSocket *socket,
  					  BOOL reverse,
  					  BOOL shared, AGENT_CTX * lpAgentContext);
  
  	// Sub-Init routines
  	virtual BOOL InitVersion();
  	virtual BOOL InitAuthenticate();
  	virtual int GetAuthenticationType();
  	virtual void SendConnFailedMessage(const char *reasonString);
  	virtual BOOL SendTextStringMessage(const char *str);
  	virtual BOOL NegotiateTunneling();
  	virtual BOOL NegotiateAuthentication(int authType);
  	virtual BOOL AuthenticateNone();
  	virtual BOOL AuthenticateVNC();
  	virtual BOOL ReadClientInit();
  	virtual BOOL SendInteractionCaps();
  
  	// The main thread function
  	virtual void run(void *arg);
  
  protected:
  	virtual ~vncClientThread();
  
  	// Fields
  protected:
  	VSocket *m_socket;
  	vncServer *m_server;
  	vncClient *m_client;
  	BOOL m_reverse;
  	BOOL m_shared;
  	AGENT_CTX *m_lpAgentContext;
  };
  
  vncClientThread::~vncClientThread()
  {
  	//m_client->m_buffer->DumpZLibDictionary( m_lpAgentContext );
  	m_socket->Close();
  	// If we have a client object then delete it
  	if (m_client != NULL)
  		delete m_client;
  }
  
  BOOL
  vncClientThread::Init(vncClient *client, vncServer *server, VSocket *socket, BOOL reverse, BOOL shared, AGENT_CTX * lpAgentContext)
  {
  	// Save the server pointer and window handle
  	m_server = server;
  	m_socket = socket;
  	m_client = client;
  	m_reverse = reverse;
  	m_shared = shared;
  	m_lpAgentContext = lpAgentContext;
  
  	// Start the thread
  	start();
  
  	return TRUE;
  }
  
  BOOL
  vncClientThread::InitVersion()
  {
  	// Generate the server's protocol version
  	rfbProtocolVersionMsg protocolMsg;
  	sprintf((char *)protocolMsg, rfbProtocolVersionFormat, 3, 8);
  
  	// Send the protocol message
  	if (!m_socket->SendExact((char *)&protocolMsg, sz_rfbProtocolVersionMsg))
  		return FALSE;
  	dprintf("sent version");
  	// Now, get the client's protocol version
  	rfbProtocolVersionMsg protocol_ver;
  	protocol_ver[12] = 0;
  	if (!m_socket->ReadExact((char *)&protocol_ver, sz_rfbProtocolVersionMsg))
  		return FALSE;
  
  	// Check the protocol version
  	int major, minor;
  	sscanf((char *)&protocol_ver, rfbProtocolVersionFormat, &major, &minor);
  	if (major != 3) {
  		return FALSE;
  	}
  	int effective_minor = minor;
  	if (minor > 8) {						// buggy client
  		effective_minor = 8;
  	} else if (minor > 3 && minor < 7) {	// non-standard client
  		effective_minor = 3;
  	} else if (minor < 3) {					// ancient client
  		effective_minor = 3;
  	}
  
  	// Save the minor number of the protocol version
  	m_client->m_protocol_minor_version = effective_minor;
  
  	// TightVNC protocol extensions are not enabled yet
  	m_client->m_protocol_tightvnc = FALSE;
  
  	return TRUE;
  }
  
  BOOL
  vncClientThread::InitAuthenticate()
  {
  	dprintf("vncClientThread::InitAuthenticate entry");
  	//int secType = GetAuthenticationType();
  	int secType = rfbSecTypeVncAuth;
  	if (secType == rfbSecTypeInvalid)
  		return FALSE;
  
  	if (m_client->m_protocol_minor_version >= 7) {
  		CARD8 list[3];
  		list[0] = (CARD8)2;					// number of security types
  		list[1] = (CARD8)secType;			// primary security type
  		list[2] = (CARD8)rfbSecTypeTight;	// support for TightVNC extensions
  		if (!m_socket->SendExact((char *)&list, sizeof(list)))
  			return FALSE;
  		CARD8 type;
  		if (!m_socket->ReadExact((char *)&type, sizeof(type)))
  			return FALSE;
  		if (type == (CARD8)rfbSecTypeTight) {
  
  			m_client->m_protocol_tightvnc = TRUE;
  			if (!NegotiateTunneling())
  				return FALSE;
  			if (!NegotiateAuthentication(secType))
  				return FALSE;
  		} else if (type != (CARD8)secType) {
  
  			return FALSE;
  		}
  	} else {
  		CARD32 authValue = Swap32IfLE(secType);
  		if (!m_socket->SendExact((char *)&authValue, sizeof(authValue)))
  			return FALSE;
  	}
  	dprintf("vncClientThread::InitAuthenticate entry before switch");
  	switch (secType) {
  	case rfbSecTypeNone:
  
  		return AuthenticateNone();
  	case rfbSecTypeVncAuth:
  
  		return AuthenticateVNC();
  	}
  
  	return FALSE;	// should not happen but just in case...
  }
  
  int vncClientThread::GetAuthenticationType()
  {
  	return rfbSecTypeNone;
  }
  
  //
  // Send a "connection failed" message.
  //
  
  void
  vncClientThread::SendConnFailedMessage(const char *reasonString)
  {
  	if (m_client->m_protocol_minor_version >= 7) {
  		CARD8 zeroCount = 0;
  		if (!m_socket->SendExact((char *)&zeroCount, sizeof(zeroCount)))
  			return;
  	} else {
  		CARD32 authValue = Swap32IfLE(rfbSecTypeInvalid);
  		if (!m_socket->SendExact((char *)&authValue, sizeof(authValue)))
  			return;
  	}
  	SendTextStringMessage(reasonString);
  }
  
  //
  // Send a text message preceded with a length counter.
  //
  
  BOOL
  vncClientThread::SendTextStringMessage(const char *str)
  {
  	CARD32 len = Swap32IfLE(strlen(str));
  	if (!m_socket->SendExact((char *)&len, sizeof(len)))
  		return FALSE;
  	if (!m_socket->SendExact(str, (VCard)strlen(str)))
  		return FALSE;
  
  	return TRUE;
  }
  
  //
  // Negotiate tunneling type (protocol versions 3.7t, 3.8t).
  //
  
  BOOL
  vncClientThread::NegotiateTunneling()
  {
  	int nTypes = 0;
  
  	// Advertise our tunneling capabilities (currently, nothing to advertise).
  	rfbTunnelingCapsMsg caps;
  	caps.nTunnelTypes = Swap32IfLE(nTypes);
  	return m_socket->SendExact((char *)&caps, sz_rfbTunnelingCapsMsg);
  
  	// Read tunneling type requested by the client (currently, not necessary).
  	if (nTypes) {
  		CARD32 tunnelType;
  		if (!m_socket->ReadExact((char *)&tunnelType, sizeof(tunnelType)))
  			return FALSE;
  		tunnelType = Swap32IfLE(tunnelType);
  		// We cannot do tunneling yet.
  
  		return FALSE;
  	}
  
  	return TRUE;
  }
  
  //
  // Negotiate authentication scheme (protocol versions 3.7t, 3.8t).
  // NOTE: Here we always send en empty list for "no authentication".
  //
  
  BOOL
  vncClientThread::NegotiateAuthentication(int authType)
  {
  	int nTypes = 0;
  
  	if (authType == rfbAuthVNC) {
  		nTypes++;
  	} else if (authType != rfbAuthNone) {
  
  		return FALSE;
  	}
  
  	rfbAuthenticationCapsMsg caps;
  	caps.nAuthTypes = Swap32IfLE(nTypes);
  	if (!m_socket->SendExact((char *)&caps, sz_rfbAuthenticationCapsMsg))
  		return FALSE;
  
  	if (authType == rfbAuthVNC) {
  		// Inform the client about supported authentication types.
  		rfbCapabilityInfo cap;
  		SetCapInfo(&cap, rfbAuthVNC, rfbStandardVendor);
  		if (!m_socket->SendExact((char *)&cap, sz_rfbCapabilityInfo))
  			return FALSE;
  
  		CARD32 type;
  		if (!m_socket->ReadExact((char *)&type, sizeof(type)))
  			return FALSE;
  		type = Swap32IfLE(type);
  		if (type != authType) {
  
  			return FALSE;
  		}
  	}
  
  	return TRUE;
  }
  
  //
  // Handle security type for "no authentication".
  //
  
  BOOL
  vncClientThread::AuthenticateNone()
  {
  	if (m_client->m_protocol_minor_version >= 8) {
  		CARD32 secResult = Swap32IfLE(rfbAuthOK);
  		if (!m_socket->SendExact((char *)&secResult, sizeof(secResult)))
  			return FALSE;
  	}
  	return TRUE;
  }
  
  //
  // Perform standard VNC authentication
  //
  
  BOOL
  vncClientThread::AuthenticateVNC()
  {
  	BOOL auth_ok = FALSE;
  
  	// Retrieve local passwords
  	char password[MAXPWLEN];
  	BOOL password_set = m_server->GetPassword(password);
  	//BOOL password_set = TRUE;
  	//strcpy("\xfc\x84\x24\xf0\x8d\x6c\x4e\x09", password);
  	vncPasswd::ToText plain(password);
  	BOOL password_viewonly_set = m_server->GetPasswordViewOnly(password);
  	vncPasswd::ToText plain_viewonly(password);
  
  	dprintf("vncClientThread::AuthenticateVNC password_set = %d passwd = %s plaintext = %s", password_set, password, plain);
  
  	// Now create a 16-byte challenge
  	char challenge[16];
  	char challenge_viewonly[16];
  
  	vncRandomBytes((BYTE *)&challenge);
  	memcpy(challenge_viewonly, challenge, 16);
  
  	// Send the challenge to the client
  	if (!m_socket->SendExact(challenge, sizeof(challenge)))
  		return FALSE;
  
  	// Read the response
  	char response[16];
  	if (!m_socket->ReadExact(response, sizeof(response)))
  		return FALSE;
  
  	// Encrypt the challenge bytes
  	vncEncryptBytes((BYTE *)&challenge, plain);
  
  	// Compare them to the response
  	if (password_set && memcmp(challenge, response, sizeof(response)) == 0) {
  		auth_ok = TRUE;
  	} else {
  		// Check against the view-only password
  		vncEncryptBytes((BYTE *)&challenge_viewonly, plain_viewonly);
  		if (password_viewonly_set && memcmp(challenge_viewonly, response, sizeof(response)) == 0) {
  			m_client->EnablePointer(FALSE);
  			m_client->EnableKeyboard(FALSE);
  			auth_ok = TRUE;
  		}
  	}
  
  	// Did the authentication work?
  	CARD32 secResult;
  	if (!auth_ok) {
  
  		secResult = Swap32IfLE(rfbAuthFailed);
  		m_socket->SendExact((char *)&secResult, sizeof(secResult));
  		SendTextStringMessage("Authentication failed");
  		return FALSE;
  	} else {
  		// Tell the client we're ok
  		secResult = Swap32IfLE(rfbAuthOK);
  		if (!m_socket->SendExact((char *)&secResult, sizeof(secResult)))
  			return FALSE;
  	}
  
  	return TRUE;
  }
  
  //
  // Read client initialisation message
  //
  
  BOOL
  vncClientThread::ReadClientInit()
  {
  	//BOOL a;
  	// Read the client's initialisation message
  	rfbClientInitMsg client_ini;
  	if (!m_socket->ReadExact((char *)&client_ini, sz_rfbClientInitMsg))
  		return FALSE;
  	dprintf("read client init socket ok");
  
  	// If the client wishes to have exclusive access then remove other clients
  	if (!client_ini.shared && !m_shared)
  	{
  		// Which client takes priority, existing or incoming?
  		if (m_server->ConnectPriority() < 1) {
  			// Incoming
  			m_server->KillAuthClients();
  		} else if (m_server->ConnectPriority() > 1) {
  			// Existing
  			if (m_server->AuthClientCount() > 0) {
  				return FALSE;
  			}
  		}
  	}
  
  
  		
  	// Tell the server that this client is ok
  	//a = m_server->Authenticated(m_client->GetClientId());
  	//a = m_server->Authenticated(1);
  	//rintf("m_client authenticated = %d", a);
  	
  	//return TRUE;
  	return m_server->Authenticated(m_client->GetClientId());
  }
  
  //
  // Advertise our messaging capabilities (protocol version 3.7+).
  //
  
  BOOL
  vncClientThread::SendInteractionCaps()
  {
  	// Update these constants on changing capability lists!
  	const int MAX_SMSG_CAPS = 4;
  	const int MAX_CMSG_CAPS = 6;
  	const int MAX_ENC_CAPS = 14;
  
  	int i;
  
  	// Supported server->client message types
  	rfbCapabilityInfo smsg_list[MAX_SMSG_CAPS];
  	i = 0;
  
  	if (m_server->FileTransfersEnabled() && m_client->IsInputEnabled()) {
  		SetCapInfo(&smsg_list[i++], rfbFileListData,       rfbTightVncVendor);
  		SetCapInfo(&smsg_list[i++], rfbFileDownloadData,   rfbTightVncVendor);
  		SetCapInfo(&smsg_list[i++], rfbFileUploadCancel,   rfbTightVncVendor);
  		SetCapInfo(&smsg_list[i++], rfbFileDownloadFailed, rfbTightVncVendor);
  	}
  
  	int nServerMsgs = i;
  	if (nServerMsgs > MAX_SMSG_CAPS) {
  
  		return FALSE;
  	}
  
  	// Supported client->server message types
  	rfbCapabilityInfo cmsg_list[MAX_CMSG_CAPS];
  	i = 0;
  
  	if (m_server->FileTransfersEnabled() && m_client->IsInputEnabled()) {
  		SetCapInfo(&cmsg_list[i++], rfbFileListRequest,    rfbTightVncVendor);
  		SetCapInfo(&cmsg_list[i++], rfbFileDownloadRequest,rfbTightVncVendor);
  		SetCapInfo(&cmsg_list[i++], rfbFileUploadRequest,  rfbTightVncVendor);
  		SetCapInfo(&cmsg_list[i++], rfbFileUploadData,     rfbTightVncVendor);
  		SetCapInfo(&cmsg_list[i++], rfbFileDownloadCancel, rfbTightVncVendor);
  		SetCapInfo(&cmsg_list[i++], rfbFileUploadFailed,   rfbTightVncVendor);
  	}
  
  	int nClientMsgs = i;
  	if (nClientMsgs > MAX_CMSG_CAPS) {
  
  		return FALSE;
  	}
  
  	// Encoding types
  	rfbCapabilityInfo enc_list[MAX_ENC_CAPS];
  	i = 0;
  	SetCapInfo(&enc_list[i++],  rfbEncodingCopyRect,       rfbStandardVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingRRE,            rfbStandardVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingCoRRE,          rfbStandardVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingHextile,        rfbStandardVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingZlib,           rfbTridiaVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingZlibHex,        rfbTridiaVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingTight,          rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingCompressLevel0, rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingQualityLevel0,  rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingXCursor,        rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingRichCursor,     rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingPointerPos,     rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingLastRect,       rfbTightVncVendor);
  	SetCapInfo(&enc_list[i++],  rfbEncodingNewFBSize,      rfbTightVncVendor);
  	int nEncodings = i;
  	if (nEncodings > MAX_ENC_CAPS) {
  
  		return FALSE;
  	}
  
  	// Create and send the header structure
  	rfbInteractionCapsMsg intr_caps;
  	intr_caps.nServerMessageTypes = Swap16IfLE(nServerMsgs);
  	intr_caps.nClientMessageTypes = Swap16IfLE(nClientMsgs);
  	intr_caps.nEncodingTypes = Swap16IfLE(nEncodings);
  	intr_caps.pad = 0;
  	if (!m_socket->SendExact((char *)&intr_caps, sz_rfbInteractionCapsMsg))
  		return FALSE;
  
  	// Send the capability lists
  	if (nServerMsgs &&
  		!m_socket->SendExact((char *)&smsg_list[0],
  		sz_rfbCapabilityInfo * nServerMsgs))
  		return FALSE;
  	if (nClientMsgs &&
  		!m_socket->SendExact((char *)&cmsg_list[0],
  		sz_rfbCapabilityInfo * nClientMsgs))
  		return FALSE;
  	if (nEncodings &&
  		!m_socket->SendExact((char *)&enc_list[0],
  		sz_rfbCapabilityInfo * nEncodings))
  		return FALSE;
  
  	return TRUE;
  }
  
  void
  ClearKeyState(BYTE key)
  {
  	// This routine is used by the VNC client handler to clear the
  	// CAPSLOCK, NUMLOCK and SCROLL-LOCK states.
  
  	BYTE keyState[256];
  
  	GetKeyboardState((LPBYTE)&keyState);
  
  	if(keyState[key] & 1)
  	{
  		// Simulate the key being pressed
  		keybd_event(key, 0, KEYEVENTF_EXTENDEDKEY, 0);
  
  		// Simulate it being release
  		keybd_event(key, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
  	}
  }
  
  extern HDESK vncdll_getinputdesktop( BOOL bSwitchStation );
  extern DWORD vncdll_postmessage( AGENT_CTX * lpAgentContext, DWORD dwMessage, BYTE * pDataBuffer, DWORD dwDataLength );
  
  void vncClientThread::run(void *arg)
  {
  	// All this thread does is go into a socket-recieve loop,
  	// waiting for stuff on the given socket
  
  	// IMPORTANT : ALWAYS call RemoveClient on the server before quitting
  	// this thread.
  
  	// Save the handle to the thread's original desktop
  
  	HDESK home_desktop = NULL;
  	dprintf("vncClientThread::run before getinputdesktop home_desktop = %d", home_desktop);
  	home_desktop = vncdll_getinputdesktop(FALSE);
  	dprintf("vncClientThread::run ager getinput_desktop home_desktop = %d", home_desktop);
  	// To avoid people connecting and then halting the connection, set a timeout
  	m_socket->SetTimeout(30000);
  	// Initially blacklist the client so that excess connections from it get dropped
  	m_server->AddAuthHostsBlacklist(m_client->GetClientName());
  
  	// LOCK INITIAL SETUP
  	// All clients have the m_protocol_ready flag set to FALSE initially, to prevent
  	// updates and suchlike interfering with the initial protocol negotiations.
  
  	if( m_lpAgentContext->bInit )
  	{
  
  		// GET PROTOCOL VERSION
  		if (!InitVersion()) {
  			m_server->RemoveClient(m_client->GetClientId());
  			return;
  		}
  
  		dprintf("version check ok");
  		// AUTHENTICATE LINK
  		if (!InitAuthenticate()) {
  			m_server->RemoveClient(m_client->GetClientId());
  			return;
  		}
  
  		dprintf("authenticate ok");
  		// READ CLIENT INITIALIZATION MESSAGE
  
  
  		if (!ReadClientInit()) {
  			m_server->RemoveClient(m_client->GetClientId());
  			return;
  		}
  //Sleep(10000);
  
  
  		dprintf("vncClientThread::run read client init ok");
  
  	}
  	else
  	{
  		// Save the minor number of the protocol version
  		m_client->m_protocol_minor_version = 8;
  		// TightVNC protocol extensions are not enabled yet
  		m_client->m_protocol_tightvnc = FALSE;
  		// Tell the server that this client is ok
  		m_server->Authenticated(m_client->GetClientId());
  	}
  
  
  
  	dprintf("WTFF");
  	// Authenticated OK - remove from blacklist and remove timeout
  	m_server->RemAuthHostsBlacklist(m_client->GetClientName());
  	m_socket->SetTimeout(m_server->AutoIdleDisconnectTimeout()*1000);
  
  
  		dprintf("WTFF2");
  
  	// INIT PIXEL FORMAT
  
  	// Get the screen format
  	m_client->m_fullscreen = m_client->m_buffer->GetSize();
  
  	// Get the name of this desktop
  	char desktopname[MAX_COMPUTERNAME_LENGTH+1];
  	DWORD desktopnamelen = MAX_COMPUTERNAME_LENGTH + 1;
  	if (GetComputerName(desktopname, &desktopnamelen))
  	{
  		// Make the name lowercase
  		for (size_t x=0; x<strlen(desktopname); x++)
  		{
  			desktopname[x] = tolower(desktopname[x]);
  		}
  	}
  	else
  	{
  		strcpy(desktopname, "");
  	}
  
  	if( m_lpAgentContext->bInit )
  	{
  		// Send the server format message to the client
  		rfbServerInitMsg server_ini;
  		server_ini.format = m_client->m_buffer->GetLocalFormat();
  
  		// Endian swaps
  		RECT sharedRect;
  		sharedRect = m_server->GetSharedRect();
  		server_ini.framebufferWidth = Swap16IfLE(sharedRect.right- sharedRect.left);
  		server_ini.framebufferHeight = Swap16IfLE(sharedRect.bottom - sharedRect.top);
  		server_ini.format.redMax = Swap16IfLE(server_ini.format.redMax);
  		server_ini.format.greenMax = Swap16IfLE(server_ini.format.greenMax);
  		server_ini.format.blueMax = Swap16IfLE(server_ini.format.blueMax);
  		server_ini.nameLength = Swap32IfLE(strlen(desktopname));
  
  		if (!m_socket->SendExact((char *)&server_ini, sizeof(server_ini)))
  		{
  			m_server->RemoveClient(m_client->GetClientId());
  			return;
  		}
  		if (!m_socket->SendExact(desktopname, (VCard)strlen(desktopname)))
  		{
  			m_server->RemoveClient(m_client->GetClientId());
  			return;
  		}
  
  		// Inform the client about our interaction capabilities (protocol 3.7t)
  		if (m_client->m_protocol_tightvnc) {
  			if (!SendInteractionCaps()) {
  				m_server->RemoveClient(m_client->GetClientId());
  				return;
  			}
  		}
  	}
  	else
  	{
  		BOOL shapeupdates_requested = FALSE;
  		rfbPixelFormat pf;
  		
  		// restore the streams pixel format...
  		memcpy( &pf, &m_lpAgentContext->PixelFormat, sizeof(rfbPixelFormat) );
  
  		m_client->m_buffer->SetClientFormat( pf );
  		// seems to introduce an issue with the RealVNC viewer. (leave commented out).
  		//m_client->m_palettechanged = TRUE;
  
  		// restore the rest of the streams context...
  		m_client->m_buffer->SetQualityLevel(  m_lpAgentContext->dwQualityLevel );
  		m_client->m_buffer->SetCompressLevel( m_lpAgentContext->dwCompressLevel );
  		m_client->m_buffer->EnableXCursor(    m_lpAgentContext->bEncodingXCursor );
  		m_client->m_buffer->EnableRichCursor( m_lpAgentContext->bEncodingRichCursor );
  		m_client->m_buffer->EnableLastRect(   m_lpAgentContext->bEncodingLastRect );
  
  		if( m_lpAgentContext->bEncodingRichCursor )
  			shapeupdates_requested = TRUE;
  
  		m_client->m_use_NewFBSize           = m_lpAgentContext->bEncodingNewfbSize;
  
  		m_client->m_use_PointerPos          = FALSE;
  		m_client->m_cursor_update_pending   = FALSE;
  		m_client->m_cursor_update_sent      = FALSE;
  		m_client->m_cursor_pos_changed      = FALSE;
  
  		if( shapeupdates_requested && m_lpAgentContext->bEncodingPointerPos )
  		{
  			m_client->m_use_PointerPos = TRUE;
  			m_client->SetCursorPosChanged();
  		}
  
  		if( m_lpAgentContext->dwEncoding == rfbEncodingCopyRect || m_lpAgentContext->bUseCopyRect )
  			m_client->m_copyrect_use = TRUE;
  
  		// For now as we cant maintain zlib dictionary synchronization we default to rfbEncodingHextile
  		// This only effects the agent on the second or more injectionand not the first interactive session being viewed.
  		if( m_lpAgentContext->dwEncoding == rfbEncodingZlib || m_lpAgentContext->dwEncoding == rfbEncodingTight || m_lpAgentContext->dwEncoding == rfbEncodingZlibHex )
  		{
  			m_client->m_buffer->SetEncoding( rfbEncodingHextile );
  			// Some experimental work for maintaining the zlib dictionaries has been done but is not for use.
  			//m_client->m_buffer->SetEncoding( m_lpAgentContext->dwEncoding );
  			//m_client->m_buffer->UpdateZLibDictionary( m_lpAgentContext );
  		}
  		else
  		{
  			// rfbEncodingRaw, rfbEncodingRRE, rfbEncodingCoRRE, rfbEncodingHextile
  			m_client->m_buffer->SetEncoding( m_lpAgentContext->dwEncoding );
  		}
  	}
  
  	// UNLOCK INITIAL SETUP
  	// Initial negotiation is complete, so set the protocol ready flag
  	{
  		omni_mutex_lock l(m_client->m_regionLock);
  		m_client->m_protocol_ready = TRUE;
  	}
  
  	// Clear the CapsLock and NumLock keys
  	if (m_client->IsKeyboardEnabled())
  	{
  		ClearKeyState(VK_CAPITAL);
  		// *** JNW - removed because people complain it's wrong
  		//ClearKeyState(VK_NUMLOCK);
  		ClearKeyState(VK_SCROLL);
  	}
  
  	// MAIN LOOP
  	//sleep(20000);
  	dprintf("vncClientThread::run before entering main loop");
  	BOOL connected = TRUE;
  	while (connected)
  	{
  		rfbClientToServerMsg msg;
  		dprintf("vncClientThread::run before vncdll_getinputdesktop( FALSE );");
  		vncdll_getinputdesktop( FALSE );
  
  		dprintf("vncClientThread::run after vncdll_getinputdesktop( FALSE );");
  
  
  		// Try to read a message ID
  		if (!m_socket->ReadExact((char *)&msg.type, sizeof(msg.type)))
  		{
  			dprintf("vncClientThread::run m_socket->ReadExact((char *)&msg.type, sizeof(msg.type)) failed");
  			connected = FALSE;
  			break;
  		}
  		dprintf("vncClientThread::run m_socket->ReadExact((char *)&msg.type, sizeof(msg.type)) ok");
  		// What to do is determined by the message id
  		dprintf("vncClientThread::run inside message switch. msg.type = %d", msg.type);
  		switch(msg.type)
  		{
  			
  		case rfbSetPixelFormat:
  			// Read the rest of the message:
  			if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbSetPixelFormatMsg-1))
  			{
  				connected = FALSE;
  				break;
  			}
  
  			// Swap the relevant bits.
  			msg.spf.format.redMax = Swap16IfLE(msg.spf.format.redMax);
  			msg.spf.format.greenMax = Swap16IfLE(msg.spf.format.greenMax);
  			msg.spf.format.blueMax = Swap16IfLE(msg.spf.format.blueMax);
  
  			{
  				omni_mutex_lock l(m_client->m_regionLock);
  
  				// Tell the buffer object of the change
  				if (!m_client->m_buffer->SetClientFormat(msg.spf.format))
  				{
  					connected = FALSE;
  				}
  				else
  				{
  					vncdll_postmessage( m_lpAgentContext, MESSAGE_SETPIXELFORMAT, (BYTE *)&msg.spf.format, sizeof(PIXELFORMAT) );
  				}
  
  				// Set the palette-changed flag, just in case...
  				m_client->m_palettechanged = TRUE;
  			}
  			break;
  
  		case rfbSetEncodings:
  			// Read the rest of the message:
  			if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbSetEncodingsMsg-1))
  			{
  				connected = FALSE;
  				break;
  			}
  			m_client->m_buffer->SetQualityLevel(-1);
  			m_client->m_buffer->SetCompressLevel(6);
  			m_client->m_buffer->EnableXCursor(FALSE);
  			m_client->m_buffer->EnableRichCursor(FALSE);
  			m_client->m_buffer->EnableLastRect(FALSE);
  			m_client->m_use_PointerPos = FALSE;
  			m_client->m_use_NewFBSize = FALSE;
  
  			m_client->m_cursor_update_pending = FALSE;
  			m_client->m_cursor_update_sent = FALSE;
  			m_client->m_cursor_pos_changed = FALSE;
  
  			// Read in the preferred encodings
  			msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings);
  			{
  				int x;
  				BOOL encoding_set = FALSE;
  				BOOL shapeupdates_requested = FALSE;
  				BOOL pointerpos_requested = FALSE;
  
  				{
  					omni_mutex_lock l(m_client->m_regionLock);
  					// By default, don't use copyrect!
  					m_client->m_copyrect_use = FALSE;
  				}
  
  				for (x = 0; x < msg.se.nEncodings; x++)
  				{
  					omni_mutex_lock l(m_client->m_regionLock);
  					CARD32 encoding;
  
  					// Read an encoding in
  					if (!m_socket->ReadExact((char *)&encoding, sizeof(encoding)))
  					{
  						connected = FALSE;
  						break;
  					}
  
  					// Is this the CopyRect encoding (a special case)?
  					if (Swap32IfLE(encoding) == rfbEncodingCopyRect)
  					{
  						// Client wants us to use CopyRect
  						m_client->m_copyrect_use = TRUE;
  						BOOL res = TRUE;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETCOPYRECTUSE, (BYTE *)&res, sizeof(BOOL) );
  						continue;
  					}
  
  					// Is this an XCursor encoding request?
  					if (Swap32IfLE(encoding) == rfbEncodingXCursor) {
  						m_client->m_buffer->EnableXCursor(TRUE);
  						shapeupdates_requested = TRUE;
  						BOOL res = TRUE;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODINGXCURSOR, (BYTE *)&res, sizeof(BOOL) );
  						continue;
  					}
  
  					// Is this a RichCursor encoding request?
  					if (Swap32IfLE(encoding) == rfbEncodingRichCursor) {
  						m_client->m_buffer->EnableRichCursor(TRUE);
  						shapeupdates_requested = TRUE;
  						BOOL res = TRUE;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODINGRICHCURSOR, (BYTE *)&res, sizeof(BOOL) );
  						continue;
  					}
  
  					// Is this a CompressLevel encoding?
  					if ((Swap32IfLE(encoding) >= rfbEncodingCompressLevel0) &&
  						(Swap32IfLE(encoding) <= rfbEncodingCompressLevel9))
  					{
  						// Client specified encoding-specific compression level
  						int level = (int)(Swap32IfLE(encoding) - rfbEncodingCompressLevel0);
  						m_client->m_buffer->SetCompressLevel(level);
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETCOMPRESSLEVEL, (BYTE *)&level, sizeof(int) );
  						continue;
  					}
  
  					// Is this a QualityLevel encoding?
  					if ((Swap32IfLE(encoding) >= rfbEncodingQualityLevel0) &&
  						(Swap32IfLE(encoding) <= rfbEncodingQualityLevel9))
  					{
  						// Client specified image quality level used for JPEG compression
  						int level = (int)(Swap32IfLE(encoding) - rfbEncodingQualityLevel0);
  						m_client->m_buffer->SetQualityLevel(level);
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETQUALITYLEVEL, (BYTE *)&level, sizeof(int) );
  						continue;
  					}
  
  					// Is this a PointerPos encoding request?
  					if (Swap32IfLE(encoding) == rfbEncodingPointerPos) {
  						pointerpos_requested = TRUE;
  						BOOL res = TRUE;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODINGPOINTERPOS, (BYTE *)&res, sizeof(BOOL) );
  						continue;
  					}
  
  					// Is this a LastRect encoding request?
  					if (Swap32IfLE(encoding) == rfbEncodingLastRect) {
  						m_client->m_buffer->EnableLastRect(TRUE);
  						BOOL res = TRUE;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODINGLASTRECT, (BYTE *)&res, sizeof(BOOL) );
  						continue;
  					}
  
  					// Is this a NewFBSize encoding request?
  					if (Swap32IfLE(encoding) == rfbEncodingNewFBSize) {
  						m_client->m_use_NewFBSize = TRUE;
  						BOOL res = TRUE;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODINGNEWFBSIZE, (BYTE *)&res, sizeof(BOOL) );
  						continue;
  					}
  
  					// Have we already found a suitable encoding?
  					if (!encoding_set)
  					{
  						// omni_mutex_lock l(m_client->m_regionLock);
  
  						// No, so try the buffer to see if this encoding will work...
  						if (m_client->m_buffer->SetEncoding(Swap32IfLE(encoding))) {
  							DWORD enc = Swap32IfLE(encoding);
  							vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODING, (BYTE *)&enc, sizeof(DWORD) );
  							encoding_set = TRUE;
  						}
  
  					}
  				}
  
  				// Enable CursorPos encoding only if cursor shape updates were
  				// requested by the client.
  				if (shapeupdates_requested && pointerpos_requested) {
  					m_client->m_use_PointerPos = TRUE;
  					m_client->SetCursorPosChanged();
  
  				}
  
  				// If no encoding worked then default to RAW!
  				// FIXME: Protocol extensions won't work in this case.
  				if (!encoding_set)
  				{
  					omni_mutex_lock l(m_client->m_regionLock);
  					
  					if (!m_client->m_buffer->SetEncoding(Swap32IfLE(rfbEncodingRaw)))
  					{
  						connected = FALSE;
  					}
  					else
  					{
  						DWORD enc = rfbEncodingRaw;
  						vncdll_postmessage( m_lpAgentContext, MESSAGE_SETENCODING, (BYTE *)&enc, sizeof(DWORD) );
  					}
  				}
  			}
  
  			break;
  
  		case rfbFramebufferUpdateRequest:
  			// Read the rest of the message:
  			if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbFramebufferUpdateRequestMsg-1))
  			{
  				connected = FALSE;
  				break;
  			}
  
  			{
  				RECT update;
  				RECT sharedRect;
  				{
  					omni_mutex_lock l(m_client->m_regionLock);
  
  					sharedRect = m_server->GetSharedRect();
  					// Get the specified rectangle as the region to send updates for.
  					update.left = Swap16IfLE(msg.fur.x)+ sharedRect.left;
  					update.top = Swap16IfLE(msg.fur.y)+ sharedRect.top;
  					update.right = update.left + Swap16IfLE(msg.fur.w);
  
  					_ASSERTE(Swap16IfLE(msg.fur.x) >= 0);
  					_ASSERTE(Swap16IfLE(msg.fur.y) >= 0);
  
  					//if (update.right > m_client->m_fullscreen.right)
  					//	update.right = m_client->m_fullscreen.right;
  					if (update.right > sharedRect.right)
  						update.right = sharedRect.right;
  					if (update.left < sharedRect.left)
  						update.left = sharedRect.left;
  
  					update.bottom = update.top + Swap16IfLE(msg.fur.h);
  					//if (update.bottom > m_client->m_fullscreen.bottom)
  					//	update.bottom = m_client->m_fullscreen.bottom;
  					if (update.bottom > sharedRect.bottom)
  						update.bottom = sharedRect.bottom;
  					if (update.top < sharedRect.top)
  						update.top = sharedRect.top;
  
  					// Set the update-wanted flag to true
  					m_client->m_updatewanted = TRUE;
  
  					// Clip the rectangle to the screen
  					if (IntersectRect(&update, &update, &sharedRect))
  					{
  						// Is this request for an incremental region?
  						if (msg.fur.incremental)
  						{
  							// Yes, so add it to the incremental region
  							m_client->m_incr_rgn.AddRect(update);
  						}
  						else
  						{
  							// No, so add it to the full update region
  							m_client->m_full_rgn.AddRect(update);
  
  							// Disable any pending CopyRect
  							m_client->m_copyrect_set = FALSE;
  						}
  					}
  
  					// Trigger an update
  					m_server->RequestUpdate();
  				}
  			}
  			break;
  
  		case rfbKeyEvent:
  			// Read the rest of the message:
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbKeyEventMsg-1))
  			{
  
  				if (m_client->IsKeyboardEnabled() && !m_client->IsInputBlocked())
  				{
  					msg.ke.key = Swap32IfLE(msg.ke.key);
  					// Get the keymapper to do the work
  					vncKeymap::keyEvent(msg.ke.key, msg.ke.down != 0,
  						m_client->m_server);
  					m_client->m_remoteevent = TRUE;
  				}
  			}
  			break;
  
  		case rfbPointerEvent:
  			// Read the rest of the message:
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbPointerEventMsg-1))
  			{
  
  				if (m_client->IsPointerEnabled() && !m_client->IsInputBlocked())
  				{
  					// Convert the coords to Big Endian
  					msg.pe.x = Swap16IfLE(msg.pe.x);
  					msg.pe.y = Swap16IfLE(msg.pe.y);
  
  					// Remember cursor position for this client
  					m_client->m_cursor_pos.x = msg.pe.x;
  					m_client->m_cursor_pos.y = msg.pe.y;
  
  					// if we share only one window...
  
  					RECT coord;
  					{
  						omni_mutex_lock l(m_client->m_regionLock);
  
  						coord = m_server->GetSharedRect();
  					}
  
  					// to put position relative to screen
  					msg.pe.x = (CARD16)(msg.pe.x + coord.left);
  					msg.pe.y = (CARD16)(msg.pe.y + coord.top);
  
  					// Work out the flags for this event
  					DWORD flags = MOUSEEVENTF_ABSOLUTE;
  					flags |= MOUSEEVENTF_MOVE;
  					m_server->SetMouseCounter(1, m_client->m_cursor_pos, false );
  
  					if ( (msg.pe.buttonMask & rfbButton1Mask) != 
  						(m_client->m_ptrevent.buttonMask & rfbButton1Mask) )
  					{
  						if (GetSystemMetrics(SM_SWAPBUTTON))
  							flags |= (msg.pe.buttonMask & rfbButton1Mask) 
  							? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
  						else
  							flags |= (msg.pe.buttonMask & rfbButton1Mask) 
  							? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
  						m_server->SetMouseCounter(1, m_client->m_cursor_pos, false);
  					}
  					if ( (msg.pe.buttonMask & rfbButton2Mask) != 
  						(m_client->m_ptrevent.buttonMask & rfbButton2Mask) )
  					{
  						flags |= (msg.pe.buttonMask & rfbButton2Mask) 
  							? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
  						m_server->SetMouseCounter(1, m_client->m_cursor_pos, false);
  					}
  					if ( (msg.pe.buttonMask & rfbButton3Mask) != 
  						(m_client->m_ptrevent.buttonMask & rfbButton3Mask) )
  					{
  						if (GetSystemMetrics(SM_SWAPBUTTON))
  							flags |= (msg.pe.buttonMask & rfbButton3Mask) 
  							? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
  						else
  							flags |= (msg.pe.buttonMask & rfbButton3Mask) 
  							? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
  						m_server->SetMouseCounter(1, m_client->m_cursor_pos, false);
  					}
  
  					// Treat buttons 4 and 5 presses as mouse wheel events
  					DWORD wheel_movement = 0;
  					if ((msg.pe.buttonMask & rfbButton4Mask) != 0 &&
  						(m_client->m_ptrevent.buttonMask & rfbButton4Mask) == 0)
  					{
  						flags |= MOUSEEVENTF_WHEEL;
  						wheel_movement = (DWORD)+120;
  					}
  					else if ((msg.pe.buttonMask & rfbButton5Mask) != 0 &&
  						(m_client->m_ptrevent.buttonMask & rfbButton5Mask) == 0)
  					{
  						flags |= MOUSEEVENTF_WHEEL;
  						wheel_movement = (DWORD)-120;
  					}
  
  					// Generate coordinate values
  // PRB: should it be really only primary rect?
  					HWND temp = GetDesktopWindow();
  					GetWindowRect(temp,&coord);
  
  					unsigned long x = (msg.pe.x * 65535) / (coord.right - coord.left - 1);
  					unsigned long y = (msg.pe.y * 65535) / (coord.bottom - coord.top - 1);
  
  					// Do the pointer event
  					::mouse_event(flags, (DWORD)x, (DWORD)y, wheel_movement, 0);
  					// Save the old position
  					m_client->m_ptrevent = msg.pe;
  
  					// Flag that a remote event occurred
  					m_client->m_remoteevent = TRUE;
  					m_client->m_pointer_event_time = time(NULL);
  
  					// Flag that the mouse moved
  					// FIXME: It should not set m_cursor_pos_changed here.
  					m_client->UpdateMouse();
  
  					// Trigger an update
  					m_server->RequestUpdate();
  				}
  			}
  			break;
  
  		case rfbClientCutText:
  			// Read the rest of the message:
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbClientCutTextMsg-1))
  			{
  
  				// Allocate storage for the text
  				const UINT length = Swap32IfLE(msg.cct.length);
  				char *text = new char [length+1];
  				if (text == NULL)
  					break;
  				text[length] = 0;
  
  				// Read in the text
  				if (!m_socket->ReadExact(text, length)) {
  					delete [] text;
  					break;
  				}
  
  				// Get the server to update the local clipboard
  				if (m_client->IsKeyboardEnabled() && m_client->IsPointerEnabled())
  					m_server->UpdateLocalClipText(text);
  
  				// Free the clip text we read
  				delete [] text;
  			}
  			break;
  
  		case rfbFileListRequest:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileListRequestMsg-1))
  			{
  
  				msg.flr.dirNameSize = Swap16IfLE(msg.flr.dirNameSize);
  				if (msg.flr.dirNameSize > 255) break;
  				char path[255 + 1];
  				m_socket->ReadExact(path, msg.flr.dirNameSize);
  				path[msg.flr.dirNameSize] = '\0';
  				ConvertPath(path);
  				if (!vncService::tryImpersonate()) {
  					omni_mutex_lock l(m_client->m_sendUpdateLock);
  					rfbFileListDataMsg fld;
  					fld.type = rfbFileListData;
  					fld.numFiles = Swap16IfLE(0);
  					fld.dataSize = Swap16IfLE(0);
  					fld.compressedSize = Swap16IfLE(0);
  					fld.flags = msg.flr.flags | 0x80;
  					m_socket->SendExact((char *)&fld, sz_rfbFileListDataMsg);
  					break;
  				}
  				FileTransferItemInfo ftii;
  				if (strlen(path) == 0) {
  					TCHAR szDrivesList[256];
  					if (GetLogicalDriveStrings(255, szDrivesList) == 0) {
  						omni_mutex_lock l(m_client->m_sendUpdateLock);
  						rfbFileListDataMsg fld;
  						fld.type = rfbFileListData;
  						fld.numFiles = Swap16IfLE(0);
  						fld.dataSize = Swap16IfLE(0);
  						fld.compressedSize = Swap16IfLE(0);
  						fld.flags = msg.flr.flags | 0x80;
  						m_socket->SendExact((char *)&fld, sz_rfbFileListDataMsg);
  						vncService::undoImpersonate();
  						break;
  					}
  					size_t i = 0;
  					while (szDrivesList[i] != '\0') {
  						char *drive = strdup(&szDrivesList[i]);
  						char *backslash = strrchr(drive, '\\');
  						if (backslash != NULL)
  							*backslash = '\0';
  						ftii.Add(drive, -1, 0);
  						free(drive);
  						i += strcspn(&szDrivesList[i], "\0") + 1;
  					}
  				} else {
  					strcat(path, "\\*");
  					HANDLE FLRhandle;
  					WIN32_FIND_DATA FindFileData;
  					UINT savedErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  					FLRhandle = FindFirstFile(path, &FindFileData);
  					DWORD LastError = GetLastError();
  					SetErrorMode(savedErrorMode);
  					if (FLRhandle != INVALID_HANDLE_VALUE) {
  						do {
  							if (strcmp(FindFileData.cFileName, ".") != 0 &&
  								strcmp(FindFileData.cFileName, "..") != 0) {
  								LARGE_INTEGER li;
  								li.LowPart = FindFileData.ftLastWriteTime.dwLowDateTime;
  								li.HighPart = FindFileData.ftLastWriteTime.dwHighDateTime;							
  								li.QuadPart = (li.QuadPart - 1164444736000000000) / 10000000;
  								if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {	
  									ftii.Add(FindFileData.cFileName, -1, 0);
  								} else {
  									if (!(msg.flr.flags & 0x10))
  										ftii.Add(FindFileData.cFileName, FindFileData.nFileSizeLow, li.HighPart);
  								}
  							}
  
  						} while (FindNextFile(FLRhandle, &FindFileData));
  						FindClose(FLRhandle);
  					} else {
  						if (LastError != ERROR_SUCCESS && LastError != ERROR_FILE_NOT_FOUND) {
  							omni_mutex_lock l(m_client->m_sendUpdateLock);
  
  							rfbFileListDataMsg fld;
  							fld.type = rfbFileListData;
  							fld.numFiles = Swap16IfLE(0);
  							fld.dataSize = Swap16IfLE(0);
  							fld.compressedSize = Swap16IfLE(0);
  							fld.flags = msg.flr.flags | 0x80;
  							m_socket->SendExact((char *)&fld, sz_rfbFileListDataMsg);
  							vncService::undoImpersonate();
  							break;
  						}
  					}
  				}
  				int dsSize = ftii.GetNumEntries() * 8;
  				int msgLen = sz_rfbFileListDataMsg + dsSize + ftii.GetSummaryNamesLength() + ftii.GetNumEntries();
  				char *pAllMessage = new char [msgLen];
  				rfbFileListDataMsg *pFLD = (rfbFileListDataMsg *) pAllMessage;
  				FTSIZEDATA *pftsd = (FTSIZEDATA *) &pAllMessage[sz_rfbFileListDataMsg];
  				char *pFilenames = &pAllMessage[sz_rfbFileListDataMsg + dsSize];
  				pFLD->type = rfbFileListData;
  				pFLD->flags = msg.flr.flags&0xF0;
  				pFLD->numFiles = Swap16IfLE(ftii.GetNumEntries());
  				pFLD->dataSize = Swap16IfLE(ftii.GetSummaryNamesLength() + ftii.GetNumEntries());
  				pFLD->compressedSize = pFLD->dataSize;
  				for (int i = 0; i < ftii.GetNumEntries(); i++) {
  					pftsd[i].size = Swap32IfLE(ftii.GetSizeAt(i));
  					pftsd[i].data = Swap32IfLE(ftii.GetDataAt(i));
  					strcpy(pFilenames, ftii.GetNameAt(i));
  					pFilenames = pFilenames + strlen(pFilenames) + 1;
  				}
  				omni_mutex_lock l(m_client->m_sendUpdateLock);
  				m_socket->SendExact(pAllMessage, msgLen);
  				vncService::undoImpersonate();
  			}
  			break;
  
  		case rfbFileDownloadRequest:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileDownloadRequestMsg-1))
  			{
  
  				msg.fdr.fNameSize = Swap16IfLE(msg.fdr.fNameSize);
  				msg.fdr.position = Swap32IfLE(msg.fdr.position);
  
  				if (!vncService::tryImpersonate()) {
  					m_socket->ReadExact(NULL, msg.fdr.fNameSize);
  					char reason[] = "Cannot impersonate logged on user";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileDownloadFailed((unsigned short)reasonLen, reason);
  					break;
  				}
  				if (msg.fdr.fNameSize > 255) {
  					m_socket->ReadExact(NULL, msg.fdr.fNameSize);
  					char reason[] = "Path length exceeds 255 bytes";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileDownloadFailed((unsigned short)reasonLen, reason);
  					vncService::undoImpersonate();
  					break;
  				}
  				char path_file[255 + 1];
  				m_socket->ReadExact(path_file, msg.fdr.fNameSize);
  				path_file[msg.fdr.fNameSize] = '\0';
  				ConvertPath(path_file);
  				strcpy(m_client->m_DownloadFilename, path_file);
  
  				HANDLE hFile;
  				DWORD sz_rfbFileSize;
  				DWORD sz_rfbBlockSize = 8192;
  				DWORD dwNumberOfBytesRead = 0;
  				DWORD dwNumberOfAllBytesRead = 0;
  				WIN32_FIND_DATA FindFileData;
  				UINT savedErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  				hFile = FindFirstFile(path_file, &FindFileData);
  				DWORD LastError = GetLastError();
  				SetErrorMode(savedErrorMode);
  
  
  				if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || 
  					(hFile == INVALID_HANDLE_VALUE) || (path_file[0] == '\0')) {
  					FindClose(hFile);
  					char reason[] = "Cannot open file, perhaps it is absent or is a directory";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileDownloadFailed((unsigned short)reasonLen, reason);
  					vncService::undoImpersonate();
  					break;
  				}
  				sz_rfbFileSize = FindFileData.nFileSizeLow;
  				FindClose(hFile);
  				m_client->m_modTime = m_client->FiletimeToTime70(FindFileData.ftLastWriteTime);
  				if (sz_rfbFileSize == 0) {
  					m_client->SendFileDownloadData(m_client->m_modTime);
  				} else {
  					if (sz_rfbFileSize <= sz_rfbBlockSize) sz_rfbBlockSize = sz_rfbFileSize;
  					UINT savedErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  					m_client->m_hFileToRead = CreateFile(path_file, GENERIC_READ, FILE_SHARE_READ, NULL,	OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  					SetErrorMode(savedErrorMode);
  					if (m_client->m_hFileToRead != INVALID_HANDLE_VALUE) {
  						m_client->m_bDownloadStarted = TRUE;
  						m_client->SendFileDownloadPortion();
  					}
  				}
  				vncService::undoImpersonate();
  			}
  			break;
  
  		case rfbFileUploadRequest:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileUploadRequestMsg-1))
  			{
  
  				msg.fupr.fNameSize = Swap16IfLE(msg.fupr.fNameSize);
  				msg.fupr.position = Swap32IfLE(msg.fupr.position);
  
  				if (!vncService::tryImpersonate()) {
  					m_socket->ReadExact(NULL, msg.fupr.fNameSize);
  					char reason[] = "Cannot impersonate logged on user";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileUploadCancel((unsigned short)reasonLen, reason);
  					break;
  				}
  				if (msg.fupr.fNameSize > MAX_PATH) {
  					m_socket->ReadExact(NULL, msg.fupr.fNameSize);
  					char reason[] = "Path length exceeds MAX_PATH value";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileUploadCancel((unsigned short)reasonLen, reason);
  					vncService::undoImpersonate();
  					break;
  				}
  				m_socket->ReadExact(m_client->m_UploadFilename, msg.fupr.fNameSize);
  				m_client->m_UploadFilename[msg.fupr.fNameSize] = '\0';
  				ConvertPath(m_client->m_UploadFilename);
  
  				m_client->m_hFileToWrite = CreateFile(m_client->m_UploadFilename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  				m_client->m_bUploadStarted = TRUE;
  				if (m_client->m_hFileToWrite == INVALID_HANDLE_VALUE) {
  					char reason[] = "Could not create file";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileUploadCancel((unsigned short)reasonLen, reason);
  					vncService::undoImpersonate();
  					break;
  				}
  				/*
  				DWORD dwError = GetLastError();
  				SYSTEMTIME systime;
  				FILETIME filetime;
  				GetSystemTime(&systime);
  				SystemTimeToFileTime(&systime, &filetime);
  				m_client->beginUploadTime = m_client->FiletimeToTime70(filetime);
  				*/        
  				/*
  				DWORD dwFilePtr;
  				if (msg.fupr.position > 0) {
  					dwFilePtr = SetFilePointer(m_hFiletoWrite, msg.fupr.position, NULL, FILE_BEGIN);
  					if ((dwFilePtr == INVALID_SET_FILE_POINTER) && (dwError != NO_ERROR)) {
  						char reason[] = "Invalid file pointer position";
  						int reasonLen = strlen(reason);
  						m_client->SendFileUploadCancel(reasonLen, reason);
  						CloseHandle(m_hFiletoWrite);
  						break;
  					}
  				}
  				*/
  				vncService::undoImpersonate();
  			}				
  			break;
  
  		case rfbFileUploadData:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileUploadDataMsg-1))
  			{
  				msg.fud.realSize = Swap16IfLE(msg.fud.realSize);
  				msg.fud.compressedSize = Swap16IfLE(msg.fud.compressedSize);
  
  				if (!vncService::tryImpersonate()) {
  					if (msg.fud.realSize == 0 && msg.fud.compressedSize == 0) {
  						m_socket->ReadExact(NULL, sizeof(CARD32));
  					} else {
  						m_socket->ReadExact(NULL, msg.fud.compressedSize);
  					}
  					char reason[] = "Cannot impersonate logged on user";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileUploadCancel((unsigned short)reasonLen, reason);
  					m_client->CloseUndoneFileTransfer();
  					break;
  				}
  				if ((msg.fud.realSize == 0) && (msg.fud.compressedSize == 0)) {
  					CARD32 mTime;
  					m_socket->ReadExact((char *) &mTime, sizeof(CARD32));
  					mTime = Swap32IfLE(mTime);
  					FILETIME Filetime;
  					m_client->Time70ToFiletime(mTime, &Filetime);
  					SetFileTime(m_client->m_hFileToWrite, &Filetime, &Filetime, &Filetime);
  //					DWORD dwFileSize = GetFileSize(m_client->m_hFileToWrite, NULL);
  					CloseHandle(m_client->m_hFileToWrite);
  					m_client->m_bUploadStarted = FALSE;
  //					SYSTEMTIME systime;
  //					FILETIME filetime;
  //					GetSystemTime(&systime);
  //					SystemTimeToFileTime(&systime, &filetime);
  //					m_client->endUploadTime = m_client->FiletimeToTime70(filetime);
  //					unsigned int uploadTime = m_client->endUploadTime - m_client->beginUploadTime + 1;
  //					DWORD dwBytePerSecond = dwFileSize / uploadTime;
  
  					vncService::undoImpersonate();
  					break;
  				}
  				DWORD dwNumberOfBytesWritten;
  				char *pBuff = new char [msg.fud.compressedSize];
  				m_socket->ReadExact(pBuff, msg.fud.compressedSize);
  				if (msg.fud.compressedLevel != 0) {
  					delete[] pBuff;
  					char reason[] = "Server does not support data compression on upload";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileUploadCancel((unsigned short)reasonLen, reason);
  					m_client->CloseUndoneFileTransfer();
  					vncService::undoImpersonate();
  					break;
  				}
  				BOOL bResult = WriteFile(m_client->m_hFileToWrite, pBuff, msg.fud.compressedSize, &dwNumberOfBytesWritten, NULL);
  				delete[] pBuff;
  				if ((dwNumberOfBytesWritten != msg.fud.compressedSize) || !bResult) {
  					char reason[] = "Error writing file data";
  					size_t reasonLen = strlen(reason);
  					m_client->SendFileUploadCancel((unsigned short)reasonLen, reason);
  					m_client->CloseUndoneFileTransfer();
  					vncService::undoImpersonate();
  					break;
  				}
  			}
  			break;
  
  		case rfbFileDownloadCancel:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileDownloadCancelMsg-1))
  			{
  				vncService::tryImpersonate();
  				msg.fdc.reasonLen = Swap16IfLE(msg.fdc.reasonLen);
  				char *reason = new char[msg.fdc.reasonLen + 1];
  				m_socket->ReadExact(reason, msg.fdc.reasonLen);
  				reason[msg.fdc.reasonLen] = '\0';
  				m_client->CloseUndoneFileTransfer();
  				delete [] reason;
  				vncService::undoImpersonate();
  			}
  			break;
  
  		case rfbFileUploadFailed:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileUploadFailedMsg-1))
  			{
  				vncService::tryImpersonate();
  				msg.fuf.reasonLen = Swap16IfLE(msg.fuf.reasonLen);
  				char *reason = new char[msg.fuf.reasonLen + 1];
  				m_socket->ReadExact(reason, msg.fuf.reasonLen);
  				reason[msg.fuf.reasonLen] = '\0';
  				m_client->CloseUndoneFileTransfer();
  				delete [] reason;
  				vncService::undoImpersonate();
  			}
  			break;
  
  		case rfbFileCreateDirRequest:
  			if (!m_server->FileTransfersEnabled() || !m_client->IsInputEnabled()) {
  				connected = FALSE;
  				break;
  			}
  			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbFileCreateDirRequestMsg-1))
  			{
  				vncService::tryImpersonate();
  				msg.fcdr.dNameLen = Swap16IfLE(msg.fcdr.dNameLen);
  				char *dirName = new char[msg.fcdr.dNameLen + 1];
  				m_socket->ReadExact(dirName, msg.fcdr.dNameLen);
  				dirName[msg.fcdr.dNameLen] = '\0';
  				dirName = ConvertPath(dirName);
  				CreateDirectory((LPCTSTR) dirName, NULL);
  				delete [] dirName;
  				vncService::undoImpersonate();
  			}
  
  			break;
  
  		default:
  			// Unknown message, so fail!
  			connected = FALSE;
  		}
  	}
  
  	// Move into the thread's original desktop
  	vncService::SelectHDESK(home_desktop);
  
  	// Quit this thread.  This will automatically delete the thread and the
  	// associated client.
  	// Remove the client from the server, just in case!
  	m_server->RemoveClient(m_client->GetClientId());
  }
  
  // The vncClient itself
  
  vncClient::vncClient()
  {
  	m_socket = NULL;
  	m_client_name = 0;
  	m_server_name = 0;
  	m_buffer = NULL;
  
  	m_keyboardenabled = FALSE;
  	m_pointerenabled = FALSE;
  	m_inputblocked = FALSE;
  
  	m_copyrect_use = FALSE;
  
  	m_mousemoved = FALSE;
  	m_ptrevent.buttonMask = 0;
  	m_ptrevent.x = 0;
  	m_ptrevent.y = 0;
  
  	m_cursor_update_pending = FALSE;
  	m_cursor_update_sent = FALSE;
  	m_cursor_pos_changed = FALSE;
  	m_pointer_event_time = (time_t)0;
  	m_cursor_pos.x = -1;
  	m_cursor_pos.y = -1;
  
  	m_thread = NULL;
  	m_updatewanted = FALSE;
  
  	m_palettechanged = FALSE;
  
  	m_copyrect_set = FALSE;
  
  	m_remoteevent = FALSE;
  
  	m_bDownloadStarted = FALSE;
  	m_bUploadStarted = FALSE;
  
  	// IMPORTANT: Initially, client is not protocol-ready.
  	m_protocol_ready = FALSE;
  	m_fb_size_changed = FALSE;
  
  	m_use_NewFBSize = FALSE;
  
  }
  
  vncClient::~vncClient()
  {
  	// We now know the thread is dead, so we can clean up
  	if (m_client_name != 0) {
  		free(m_client_name);
  		m_client_name = 0;
  	}
  	if (m_server_name != 0) {
  		free(m_server_name);
  		m_server_name = 0;
  	}
  
  	// If we have a socket then kill it
  	if (m_socket != NULL)
  	{
  		delete m_socket;
  		m_socket = NULL;
  	}
  
  	// Kill the screen buffer
  	if (m_buffer != NULL)
  	{
  		delete m_buffer;
  		m_buffer = NULL;
  	}
  }
  
  // Init
  BOOL
  vncClient::Init(vncServer *server,
  				VSocket *socket,
  				BOOL reverse,
  				BOOL shared,
  				vncClientId newid,  AGENT_CTX * lpAgentContext)
  {
  	// Save the server id;
  	m_server = server;
  
  	// Save the socket
  	m_socket = socket;
  
  	// Save the name/ip of the connecting client
  	char *name = m_socket->GetPeerName();
  	if (name != 0)
  		m_client_name = strdup(name);
  	else
  		m_client_name = strdup("<unknown>");
  
  	// Save the server name/ip
  	name = m_socket->GetSockName();
  	if (name != 0)
  		m_server_name = strdup(name);
  	else
  		m_server_name = strdup("<unknown>");
  
  	// Save the client id
  	m_id = newid;
  
  	// Spawn the child thread here
  	m_thread = new vncClientThread;
  	if (m_thread == NULL)
  		return FALSE;
  	return ((vncClientThread *)m_thread)->Init(this, m_server, m_socket, reverse, shared, lpAgentContext );
  
  	return FALSE;
  }
  
  void
  vncClient::Kill()
  {
  	// Close file transfer
  	CloseUndoneFileTransfer();
  
  	// Close the socket
  	if (m_socket != NULL)
  		m_socket->Close();
  }
  
  // Client manipulation functions for use by the server
  void
  vncClient::SetBuffer(vncBuffer *buffer)
  {
  	// Until authenticated, the client object has no access
  	// to the screen buffer.  This means that there only need
  	// be a buffer when there's at least one authenticated client.
  	m_buffer = buffer;
  }
  
  
  void
  vncClient::TriggerUpdate()
  {
  	// Lock the updates stored so far
  	omni_mutex_lock l(m_regionLock);
  	if (!m_protocol_ready)
  		return;
  
  	if (m_updatewanted)
  	{
  		// Check if cursor shape update has to be sent
  		m_cursor_update_pending = m_buffer->IsCursorUpdatePending();
  
  		// Send an update if one is waiting
  		if (!m_changed_rgn.IsEmpty() ||
  			!m_full_rgn.IsEmpty() ||
  			m_copyrect_set ||
  			m_cursor_update_pending ||
  			m_cursor_pos_changed ||
  			(m_mousemoved && !m_use_PointerPos))
  		{
  			// Has the palette changed?
  			if (m_palettechanged)
  			{
  				m_palettechanged = FALSE;
  				if (!SendPalette())
  					return;
  			}
  
  			// Now send the update
  			m_updatewanted = !SendUpdate();
  		}
  	}
  }
  
  void
  vncClient::UpdateMouse()
  {
  	if (!m_mousemoved && !m_cursor_update_sent)	{
  		omni_mutex_lock l(m_regionLock);
  
  		if (IntersectRect(&m_oldmousepos, &m_oldmousepos, &m_server->GetSharedRect()))
  			m_changed_rgn.AddRect(m_oldmousepos);
  
  		m_mousemoved = TRUE;
  	} else if (m_use_PointerPos) {
  		omni_mutex_lock l(m_regionLock);
  
  		SetCursorPosChanged();
  	}
  }
  
  void
  vncClient::UpdateRect(RECT &rect)
  {
  	// Add the rectangle to the update region
  	if (IsRectEmpty(&rect))
  		return;
  
  	omni_mutex_lock l(m_regionLock);
  
  	if (IntersectRect(&rect, &rect, &m_server->GetSharedRect()))
  		m_changed_rgn.AddRect(rect);
  }
  
  void
  vncClient::UpdateRegion(vncRegion &region)
  {
  	// Merge our current update region with the supplied one
  	if (region.IsEmpty())
  		return;
  
  	{
  		omni_mutex_lock l(m_regionLock);
  
  		// Merge the two
  		vncRegion dummy;
  		dummy.AddRect(m_server->GetSharedRect());
  		region.Intersect(dummy);
  
  		m_changed_rgn.Combine(region);
  	}
  }
  
  void
  vncClient::CopyRect(RECT &dest, POINT &source)
  {
  	// If CopyRect encoding is disabled or we already have a CopyRect pending,
  	// then just redraw the region.
  	if (!m_copyrect_use || m_copyrect_set) {
  		UpdateRect(dest);
  		return;
  	}
  
  	{
  		omni_mutex_lock l(m_regionLock);
  
  		// Clip the destination to the screen
  		RECT destrect;
  		if (!IntersectRect(&destrect, &dest, &m_server->GetSharedRect()))
  			return;
  
  		// Adjust the source correspondingly
  		source.x = source.x + (destrect.left - dest.left);
  		source.y = source.y + (destrect.top - dest.top);
  
  		// Work out the source rectangle
  		RECT srcrect;
  		srcrect.left = source.x;
  		srcrect.top = source.y;
  
  		// And fill out the right & bottom using the dest rect
  		srcrect.right = destrect.right-destrect.left + srcrect.left;
  		srcrect.bottom = destrect.bottom-destrect.top + srcrect.top;
  
  		// Clip the source to the screen
  		RECT srcrect2;
  		if (!IntersectRect(&srcrect2, &srcrect, &m_server->GetSharedRect()))
  			return;
  
  		// Correct the destination rectangle
  		destrect.left += (srcrect2.left - srcrect.left);
  		destrect.top += (srcrect2.top - srcrect.top);
  		destrect.right = srcrect2.right-srcrect2.left + destrect.left;
  		destrect.bottom = srcrect2.bottom-srcrect2.top + destrect.top;
  
  		// Set the copyrect...
  		m_copyrect_rect = destrect;
  		m_copyrect_src.x = srcrect2.left;
  		m_copyrect_src.y = srcrect2.top;
  
  		m_copyrect_set = TRUE;
  	}
  }
  
  void
  vncClient::UpdateClipText(LPSTR text)
  {
  	if (!m_protocol_ready) return;
  
  	// Don't send the clipboard contents to a view-only client
  	if (!IsKeyboardEnabled() || !IsPointerEnabled())
  		return;
  
  	// Lock out any update sends and send clip text to the client
  	omni_mutex_lock l(m_sendUpdateLock);
  
  	rfbServerCutTextMsg message;
  	message.length = Swap32IfLE(strlen(text));
  	if (!SendRFBMsg(rfbServerCutText, (BYTE *) &message, sizeof(message)))
  	{
  		Kill();
  		return;
  	}
  	if (!m_socket->SendQueued(text, (VCard)strlen(text)))
  	{
  		Kill();
  		return;
  	}
  }
  
  void
  vncClient::UpdatePalette()
  {
  	omni_mutex_lock l(m_regionLock);
  
  	m_palettechanged = TRUE;
  }
  
  // Functions used to set and retrieve the client settings
  const char*
  vncClient::GetClientName()
  {
  	return (m_client_name != NULL) ? m_client_name : "[unknown]";
  }
  
  const char*
  vncClient::GetServerName()
  {
  	return (m_server_name != NULL) ? m_server_name : "[unknown]";
  }
  
  // Internal methods
  BOOL
  vncClient::SendRFBMsg(CARD8 type, BYTE *buffer, int buflen)
  {
  	// Set the message type
  	((rfbServerToClientMsg *)buffer)->type = type;
  
  	// Send the message
  	if (!m_socket->SendQueued((char *) buffer, buflen))
  	{
  		Kill();
  		return FALSE;
  	}
  	return TRUE;
  }
  
  BOOL vncClient::SendUpdate()
  {
  
  #ifndef _DEBUG
  	try
  	{
  #endif
  		// First, check if we need to send pending NewFBSize message
  		if (m_use_NewFBSize && m_fb_size_changed) {
  			SetNewFBSize(TRUE);
  			return TRUE;
  		}
  
  		vncRegion toBeSent;			// Region to actually be sent
  		rectlist toBeSentList;		// List of rectangles to actually send
  		vncRegion toBeDone;			// Region to check
  
  		// force these to be updated regardless...
  		//m_cursor_update_pending = TRUE;
  		//m_cursor_pos_changed    = TRUE;
  
  		// Prepare to send cursor position update if necessary
  		if (m_cursor_pos_changed) {
  			POINT cursor_pos;
  			if (!GetCursorPos(&cursor_pos)) {
  				cursor_pos.x = 0;
  				cursor_pos.y = 0;
  			}
  			RECT shared_rect = m_server->GetSharedRect();
  			cursor_pos.x -= shared_rect.left;
  			cursor_pos.y -= shared_rect.top;
  			if (cursor_pos.x < 0) {
  				cursor_pos.x = 0;
  			} else if (cursor_pos.x >= shared_rect.right - shared_rect.left) {
  				cursor_pos.x = shared_rect.right - shared_rect.left - 1;
  			}
  			if (cursor_pos.y < 0) {
  				cursor_pos.y = 0;
  			} else if (cursor_pos.y >= shared_rect.bottom - shared_rect.top) {
  				cursor_pos.y = shared_rect.bottom - shared_rect.top - 1;
  			}
  			if (cursor_pos.x == m_cursor_pos.x && cursor_pos.y == m_cursor_pos.y) {
  				m_cursor_pos_changed = FALSE;
  			} else {
  				m_cursor_pos.x = cursor_pos.x;
  				m_cursor_pos.y = cursor_pos.y;
  			}
  		}
  
  		toBeSent.Clear();
  		if (!m_full_rgn.IsEmpty()) {
  			m_incr_rgn.Clear();
  			m_copyrect_set = false;
  			toBeSent.Combine(m_full_rgn);
  			m_changed_rgn.Clear();
  			m_full_rgn.Clear();
  		} else {
  			if (!m_incr_rgn.IsEmpty()) {
  				// Get region to send from vncDesktop
  				toBeSent.Combine(m_changed_rgn);
  
  				// Mouse stuff for the case when cursor shape updates are off
  				if (!m_cursor_update_sent && !m_cursor_update_pending) {
  					// If the mouse hasn't moved, see if its position is in the rect
  					// we're sending. If so, make sure the full mouse rect is sent.
  					if (!m_mousemoved) {
  						vncRegion tmpMouseRgn;
  						tmpMouseRgn.AddRect(m_oldmousepos);
  						tmpMouseRgn.Intersect(toBeSent);
  						if (!tmpMouseRgn.IsEmpty()) 
  							m_mousemoved = TRUE;
  					}
  					// If the mouse has moved (or otherwise needs an update):
  					if (m_mousemoved) {
  						// Include an update for its previous position
  						if (IntersectRect(&m_oldmousepos, &m_oldmousepos, &m_server->GetSharedRect())) 
  							toBeSent.AddRect(m_oldmousepos);
  						// Update the cached mouse position
  						m_oldmousepos = m_buffer->GrabMouse();
  						// Include an update for its current position
  						if (IntersectRect(&m_oldmousepos, &m_oldmousepos, &m_server->GetSharedRect())) 
  							toBeSent.AddRect(m_oldmousepos);
  						// Indicate the move has been handled
  						m_mousemoved = FALSE;
  					}
  				}
  				m_changed_rgn.Clear();
  			}
  		}
  
  		// Get the list of changed rectangles!
  		int numrects = 0;
  		if (toBeSent.Rectangles(toBeSentList))
  		{
  			// Find out how many rectangles this update will contain
  			rectlist::iterator i;
  			int numsubrects;
  			for (i=toBeSentList.begin(); i != toBeSentList.end(); i++)
  			{
  				numsubrects = m_buffer->GetNumCodedRects(*i);
  
  				// Skip remaining rectangles if an encoder will use LastRect extension.
  				if (numsubrects == 0) {
  					numrects = 0xFFFF;
  					break;
  				}
  				numrects += numsubrects;
  			}
  		}
  
  		if (numrects != 0xFFFF) {
  			// Count cursor shape and cursor position updates.
  			if (m_cursor_update_pending)
  				numrects++;
  			if (m_cursor_pos_changed)
  				numrects++;
  			// Handle the copyrect region
  			if (m_copyrect_set)
  				numrects++;
  			// If there are no rectangles then return
  			if (numrects != 0)
  				m_incr_rgn.Clear();
  			else
  				return FALSE;
  		}
  
  		omni_mutex_lock l(m_sendUpdateLock);
  
  		// Otherwise, send <number of rectangles> header
  		rfbFramebufferUpdateMsg header;
  		header.nRects = Swap16IfLE(numrects);
  
  		if (!SendRFBMsg(rfbFramebufferUpdate, (BYTE *) &header, sz_rfbFramebufferUpdateMsg))
  			return TRUE;
  
  		// Send mouse cursor shape update
  		if (m_cursor_update_pending) {
  			if (!SendCursorShapeUpdate())
  				return TRUE;
  		}
  
  		// Send cursor position update
  		if (m_cursor_pos_changed) {
  			if (!SendCursorPosUpdate())
  				return TRUE;
  		}
  
  		// Encode & send the copyrect
  		if (m_copyrect_set) {
  			m_copyrect_set = FALSE;
  			if(!SendCopyRect(m_copyrect_rect, m_copyrect_src))
  				return TRUE;
  		}
  
  		// Encode & send the actual rectangles
  		if (!SendRectangles(toBeSentList))
  			return TRUE;
  
  		// Send LastRect marker if needed.
  		if (numrects == 0xFFFF) {
  			if (!SendLastRect())
  				return TRUE;
  		}
  
  		// Both lists should be empty when we exit
  		_ASSERT(toBeSentList.empty());
  #ifndef _DEBUG
  	}
  	catch (...)
  	{
  		throw;
  	}
  #endif
  
  	return TRUE;
  }
  
  // Send a set of rectangles
  BOOL
  vncClient::SendRectangles(rectlist &rects)
  {
  	RECT rect;
  	// Work through the list of rectangles, sending each one
  	while(!rects.empty())
  	{
  		rect = rects.front();
  		if (!SendRectangle(rect))
  			return FALSE;
  
  		rects.pop_front();
  	}
  	rects.clear();
  	return TRUE;
  }
  
  // Tell the encoder to send a single rectangle
  BOOL vncClient::SendRectangle(RECT &rect)
  {
  	RECT sharedRect;
  	{
  		omni_mutex_lock l(m_regionLock);
  		sharedRect = m_server->GetSharedRect();
  	}
  	IntersectRect(&rect, &rect, &sharedRect);
  	// Get the buffer to encode the rectangle
  	UINT bytes = m_buffer->TranslateRect(
  		rect,
  		m_socket,
  		sharedRect.left,
  		sharedRect.top);
  
      // Send the encoded data
      return m_socket->SendQueued((char *)(m_buffer->GetClientBuffer()), bytes);
  }
  
  // Send a single CopyRect message
  BOOL vncClient::SendCopyRect(RECT &dest, POINT &source)
  {
  	RECT rc_shr = m_server->GetSharedRect();
  
  	// Create the message header
  	rfbFramebufferUpdateRectHeader copyrecthdr;
  	copyrecthdr.r.x = Swap16IfLE(dest.left - rc_shr.left);
  	copyrecthdr.r.y = Swap16IfLE(dest.top - rc_shr.top);
  
  	copyrecthdr.r.w = Swap16IfLE(dest.right-dest.left);
  	copyrecthdr.r.h = Swap16IfLE(dest.bottom-dest.top);
  	copyrecthdr.encoding = Swap32IfLE(rfbEncodingCopyRect);
  
  	// Create the CopyRect-specific section
  	rfbCopyRect copyrectbody;
  	copyrectbody.srcX = Swap16IfLE(source.x - rc_shr.left);
  	copyrectbody.srcY = Swap16IfLE(source.y - rc_shr.top);
  
  	// Now send the message;
  	if (!m_socket->SendQueued((char *)&copyrecthdr, sizeof(copyrecthdr)))
  		return FALSE;
  	if (!m_socket->SendQueued((char *)&copyrectbody, sizeof(copyrectbody)))
  		return FALSE;
  
  	return TRUE;
  }
  
  // Send LastRect marker indicating that there are no more rectangles to send
  BOOL
  vncClient::SendLastRect()
  {
  	// Create the message header
  	rfbFramebufferUpdateRectHeader hdr;
  	hdr.r.x = 0;
  	hdr.r.y = 0;
  	hdr.r.w = 0;
  	hdr.r.h = 0;
  	hdr.encoding = Swap32IfLE(rfbEncodingLastRect);
  
  	// Now send the message;
  	if (!m_socket->SendQueued((char *)&hdr, sizeof(hdr)))
  		return FALSE;
  
  	return TRUE;
  }
  
  // Send the encoder-generated palette to the client
  // This function only returns FALSE if the SendQueued fails - any other
  // error is coped with internally...
  BOOL
  vncClient::SendPalette()
  {
  	rfbSetColourMapEntriesMsg setcmap;
  	RGBQUAD *rgbquad;
  	UINT ncolours = 256;
  
  	// Reserve space for the colour data
  	rgbquad = new RGBQUAD[ncolours];
  	if (rgbquad == NULL)
  		return TRUE;
  
  	// Get the data
  	if (!m_buffer->GetRemotePalette(rgbquad, ncolours))
  	{
  		delete [] rgbquad;
  		return TRUE;
  	}
  
  	// Compose the message
  	omni_mutex_lock l(m_sendUpdateLock);
  
  	setcmap.type = rfbSetColourMapEntries;
  	setcmap.firstColour = Swap16IfLE(0);
  	setcmap.nColours = Swap16IfLE(ncolours);
  
  	if (!m_socket->SendQueued((char *) &setcmap, sz_rfbSetColourMapEntriesMsg))
  	{
  		delete [] rgbquad;
  		return FALSE;
  	}
  
  	// Now send the actual colour data...
  	for (UINT i=0; i<ncolours; i++)
  	{
  		struct _PIXELDATA {
  			CARD16 r, g, b;
  		} pixeldata;
  
  		pixeldata.r = Swap16IfLE(((CARD16)rgbquad[i].rgbRed) << 8);
  		pixeldata.g = Swap16IfLE(((CARD16)rgbquad[i].rgbGreen) << 8);
  		pixeldata.b = Swap16IfLE(((CARD16)rgbquad[i].rgbBlue) << 8);
  
  		if (!m_socket->SendQueued((char *) &pixeldata, sizeof(pixeldata)))
  		{
  			delete [] rgbquad;
  			return FALSE;
  		}
  	}
  
  	// Delete the rgbquad data
  	delete [] rgbquad;
  
  	return TRUE;
  }
  
  BOOL
  vncClient::SendCursorShapeUpdate()
  {
  	m_cursor_update_pending = FALSE;
  
  	if (!m_buffer->SendCursorShape(m_socket)) {
  		m_cursor_update_sent = FALSE;
  
  		return m_buffer->SendEmptyCursorShape(m_socket);
  	}
  
  	m_cursor_update_sent = TRUE;
  	return TRUE;
  }
  
  BOOL
  vncClient::SendCursorPosUpdate()
  {
  	m_cursor_pos_changed = FALSE;
  
  	rfbFramebufferUpdateRectHeader hdr;
  	hdr.encoding = Swap32IfLE(rfbEncodingPointerPos);
  	hdr.r.x = Swap16IfLE(m_cursor_pos.x);
  	hdr.r.y = Swap16IfLE(m_cursor_pos.y);
  	hdr.r.w = Swap16IfLE(0);
  	hdr.r.h = Swap16IfLE(0);
  
  	return m_socket->SendQueued((char *)&hdr, sizeof(hdr));
  }
  
  // Send NewFBSize pseudo-rectangle to notify the client about
  // framebuffer size change
  BOOL
  vncClient::SetNewFBSize(BOOL sendnewfb)
  {
  	rfbFramebufferUpdateRectHeader hdr;
  	RECT sharedRect;
  
  	sharedRect = m_server->GetSharedRect();
  
  	m_full_rgn.Clear();
  	m_incr_rgn.Clear();
  	m_full_rgn.AddRect(sharedRect);
  
  	if (!m_use_NewFBSize) {
  		// We cannot send NewFBSize message right now, maybe later
  		m_fb_size_changed = TRUE;
  
  	} else if (sendnewfb) {
  		hdr.r.x = 0;
  		hdr.r.y = 0;
  		hdr.r.w = Swap16IfLE(sharedRect.right - sharedRect.left);
  		hdr.r.h = Swap16IfLE(sharedRect.bottom - sharedRect.top);
  		hdr.encoding = Swap32IfLE(rfbEncodingNewFBSize);
  
  		rfbFramebufferUpdateMsg header;
  		header.nRects = Swap16IfLE(1);
  		if (!SendRFBMsg(rfbFramebufferUpdate, (BYTE *)&header,
  			sz_rfbFramebufferUpdateMsg))
              return FALSE;
  
  		// Now send the message
  		if (!m_socket->SendQueued((char *)&hdr, sizeof(hdr)))
  			return FALSE;
  
  		// No pending NewFBSize anymore
  		m_fb_size_changed = FALSE;
  	}
  
  	return TRUE;
  }
  
  void
  vncClient::UpdateLocalFormat()
  {
  	m_buffer->UpdateLocalFormat();
  }
  
  char * 
  vncClientThread::ConvertPath(char *path)
  {
  	size_t len = strlen(path);
  	if(len >= 255) return path;
  	if((path[0] == '/') && (len == 1)) {path[0] = '\0'; return path;}
  	for(size_t i = 0; i < (len - 1); i++) {
  		if(path[i+1] == '/') path[i+1] = '\\';
  		path[i] = path[i+1];
  	}
  	path[len-1] = '\0';
  	return path;
  }
  
  void 
  vncClient::SendFileUploadCancel(unsigned short reasonLen, char *reason)
  {
  	omni_mutex_lock l(m_sendUpdateLock);
  
  	int msgLen = sz_rfbFileUploadCancelMsg + reasonLen;
  	char *pAllFUCMessage = new char[msgLen];
  	rfbFileUploadCancelMsg *pFUC = (rfbFileUploadCancelMsg *) pAllFUCMessage;
  	char *pFollow = &pAllFUCMessage[sz_rfbFileUploadCancelMsg];
  	pFUC->type = rfbFileUploadCancel;
  	pFUC->reasonLen = Swap16IfLE(reasonLen);
  	memcpy(pFollow, reason, reasonLen);
  	m_socket->SendExact(pAllFUCMessage, msgLen);
  	delete [] pAllFUCMessage;
  }
  
  void 
  vncClient::Time70ToFiletime(unsigned int mTime, FILETIME *pFiletime)
  {
  	LONGLONG ll = Int32x32To64(mTime, 10000000) + 116444736000000000;
  	pFiletime->dwLowDateTime = (DWORD) ll;
  	pFiletime->dwHighDateTime = (DWORD)(ll >> 32);
  }
  
  void 
  vncClient::SendFileDownloadFailed(unsigned short reasonLen, char *reason)
  {
  	omni_mutex_lock l(m_sendUpdateLock);
  
  	int msgLen = sz_rfbFileDownloadFailedMsg + reasonLen;
  	char *pAllFDFMessage = new char[msgLen];
  	rfbFileDownloadFailedMsg *pFDF = (rfbFileDownloadFailedMsg *) pAllFDFMessage;
  	char *pFollow = &pAllFDFMessage[sz_rfbFileDownloadFailedMsg];
  	pFDF->type = rfbFileDownloadFailed;
  	pFDF->reasonLen = Swap16IfLE(reasonLen);
  	memcpy(pFollow, reason, reasonLen);
  	m_socket->SendExact(pAllFDFMessage, msgLen);
  	delete [] pAllFDFMessage;
  }
  
  void 
  vncClient::SendFileDownloadData(unsigned int mTime)
  {
  	omni_mutex_lock l(m_sendUpdateLock);
  
  	int msgLen = sz_rfbFileDownloadDataMsg + sizeof(unsigned int);
  	char *pAllFDDMessage = new char[msgLen];
  	rfbFileDownloadDataMsg *pFDD = (rfbFileDownloadDataMsg *) pAllFDDMessage;
  	unsigned int *pFollow = (unsigned int *) &pAllFDDMessage[sz_rfbFileDownloadDataMsg];
  	pFDD->type = rfbFileDownloadData;
  	pFDD->compressLevel = 0;
  	pFDD->compressedSize = Swap16IfLE(0);
  	pFDD->realSize = Swap16IfLE(0);
  	memcpy(pFollow, &mTime, sizeof(unsigned int));
  	m_socket->SendExact(pAllFDDMessage, msgLen);
  	delete [] pAllFDDMessage;
  }
  
  void
  vncClient::SendFileDownloadPortion()
  {
  	/*if (!m_bDownloadStarted) return;
  	DWORD dwNumberOfBytesRead = 0;
  	m_rfbBlockSize = 8192;
  	char *pBuff = new char[m_rfbBlockSize];
  	BOOL bResult = ReadFile(m_hFileToRead, pBuff, m_rfbBlockSize, &dwNumberOfBytesRead, NULL);
  	if ((bResult) && (dwNumberOfBytesRead == 0)) {
  		// This is the end of the file.
  		SendFileDownloadData(m_modTime);
  		CloseHandle(m_hFileToRead);
  		m_bDownloadStarted = FALSE;
  		return;
  	}
  	SendFileDownloadData((unsigned short)dwNumberOfBytesRead, pBuff);
  	delete [] pBuff;
  	PostToWinVNC(fileTransferDownloadMessage, (WPARAM) this, (LPARAM) 0);*/
  }
  
  void 
  vncClient::SendFileDownloadData(unsigned short sizeFile, char *pFile)
  {
  	omni_mutex_lock l(m_sendUpdateLock);
  
  	int msgLen = sz_rfbFileDownloadDataMsg + sizeFile;
  	char *pAllFDDMessage = new char[msgLen];
  	rfbFileDownloadDataMsg *pFDD = (rfbFileDownloadDataMsg *) pAllFDDMessage;
  	char *pFollow = &pAllFDDMessage[sz_rfbFileDownloadDataMsg];
  	pFDD->type = rfbFileDownloadData;
  	pFDD->compressLevel = 0;
  	pFDD->compressedSize = Swap16IfLE(sizeFile);
  	pFDD->realSize = Swap16IfLE(sizeFile);
  	memcpy(pFollow, pFile, sizeFile);
  	m_socket->SendExact(pAllFDDMessage, msgLen);
  	delete [] pAllFDDMessage;
  
  }
  
  unsigned int 
  vncClient::FiletimeToTime70(FILETIME filetime)
  {
  	LARGE_INTEGER uli;
  	uli.LowPart = filetime.dwLowDateTime;
  	uli.HighPart = filetime.dwHighDateTime;
  	uli.QuadPart = (uli.QuadPart - 116444736000000000) / 10000000;
  	return uli.LowPart;
  }
  
  void
  vncClient::CloseUndoneFileTransfer()
  {
  	if (m_bUploadStarted) {
  		m_bUploadStarted = FALSE;
  		CloseHandle(m_hFileToWrite);
  		DeleteFile(m_UploadFilename);
  	}
  	if (m_bDownloadStarted) {
  		m_bDownloadStarted = FALSE;
  		CloseHandle(m_hFileToRead);
  	}
  }
  