// 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 #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; // 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() { int secType = GetAuthenticationType(); 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; } 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); vncPasswd::ToText plain(password); BOOL password_viewonly_set = m_server->GetPasswordViewOnly(password); vncPasswd::ToText plain_viewonly(password); // 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() { // Read the client's initialisation message rfbClientInitMsg client_ini; if (!m_socket->ReadExact((char *)&client_ini, sz_rfbClientInitMsg)) return FALSE; // 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 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 = vncdll_getinputdesktop(FALSE); // 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; } // AUTHENTICATE LINK if (!InitAuthenticate()) { m_server->RemoveClient(m_client->GetClientId()); return; } // READ CLIENT INITIALIZATION MESSAGE if (!ReadClientInit()) { m_server->RemoveClient(m_client->GetClientId()); return; } } 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()); } // Authenticated OK - remove from blacklist and remove timeout m_server->RemAuthHostsBlacklist(m_client->GetClientName()); m_socket->SetTimeout(m_server->AutoIdleDisconnectTimeout()*1000); // 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; xbInit ) { // 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 BOOL connected = TRUE; while (connected) { rfbClientToServerMsg msg; vncdll_getinputdesktop( FALSE ); // Try to read a message ID if (!m_socket->ReadExact((char *)&msg.type, sizeof(msg.type))) { connected = FALSE; break; } // What to do is determined by the message id 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(""); // Save the server name/ip name = m_socket->GetSockName(); if (name != 0) m_server_name = strdup(name); else m_server_name = strdup(""); // 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 ®ion) { // 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 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 *)©recthdr, sizeof(copyrecthdr))) return FALSE; if (!m_socket->SendQueued((char *)©rectbody, 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; iSendQueued((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); } }