Example #1
0
void addClntThrdCD::run( void * )
{
    HAB thab = WinInitialize( 0 );
    HMQ hmq = WinCreateMsgQueue( thab , 0 );

    // Attempt to create a new socket
    VSocket *tmpsock;
    tmpsock = new VSocket;
    if (tmpsock!=NULL)
    {
        // Connect out to the specified host on the VNCviewer listen port
        // To be really good, we should allow a display number here but
        // for now we'll just assume we're connecting to display zero
        tmpsock->Create();
        if (tmpsock->Connect(m_host, m_port))
        {
            // Add the new client to this server
            m_server->AddClient(tmpsock, TRUE, TRUE);
        }
        else
        {
            // Print up an error message
            WinMessageBox( HWND_DESKTOP , HWND_DESKTOP ,
                           (PSZ)"Failed to connect to listening VNC viewer",
                           (PSZ)"Outgoing Connection", 10001 , MB_ICONEXCLAMATION | MB_OK );
            delete tmpsock;
        }
    }

    WinDestroyMsgQueue( hmq );
    WinTerminate( thab );
}
Example #2
0
void
vncDesktopThread::PollWindow(rfb::Region2D &rgn, HWND hwnd)
{
	// Are we set to low-load polling?
	if (m_server->PollOnEventOnly())
	{
		// Yes, so only poll if the remote user has done something
		if (!m_server->RemoteEventReceived()) {
			return;
		}
	}

	// Does the client want us to poll only console windows?
	if (m_desktop->m_server->PollConsoleOnly())
	{
		char classname[20];

		// Yes, so check that this is a console window...
		if (GetClassName(hwnd, classname, sizeof(classname))) {
			if ((strcmp(classname, "tty") != 0) &&
				(strcmp(classname, "ConsoleWindowClass") != 0)) {
				return;
			}
		}
	}

	RECT rect;

	// Get the rectangle
	if (GetWindowRect(hwnd, &rect)) {
		rfb::Rect wrect = rfb::Rect(rect).intersect(m_desktop->m_bmrect);
		if (!wrect.is_empty()) {
			rgn = rgn.union_(rfb::Region2D(wrect));
		}
	}
}
Example #3
0
// Code to be executed by the thread
void *vncSockConnectThread::run_undetached(void * arg)
{
	vnclog.Print(LL_STATE, VNCLOG("started socket connection thread\n"));

	// Go into a loop, listening for connections on the given socket
	while (!m_shutdown) {
		// Accept an incoming connection
		VSocket *new_socket;
		if (!m_socket->TryAccept(&new_socket, 100))
			break;
		if (new_socket != NULL) {
			vnclog.Print(LL_CLIENTS, VNCLOG("accepted connection from %s\n"),
						 new_socket->GetPeerName());
			// Successful accept - start the client unauthenticated
			m_server->AddClient(new_socket, FALSE, FALSE);
		}
	}

	vnclog.Print(LL_STATE, VNCLOG("quitting socket connection thread\n"));
	return NULL;
}
Example #4
0
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.

	vnclog.Print(LL_CLIENTS, VNCLOG("client connected : %s (%hd)\n"),
								m_client->GetClientName(),
								m_client->GetClientId());

	// Save the handle to the thread's original desktop
	HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId());
	
	// To avoid people connecting and then halting the connection, set a timeout
	if (!m_socket->SetTimeout(30000))
		vnclog.Print(LL_INTERR, VNCLOG("failed to set socket timeout(%d)\n"), GetLastError());

	// 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.

	// GET PROTOCOL VERSION
	if (!InitVersion())
	{
		m_server->RemoveClient(m_client->GetClientId());
		return;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("negotiated version\n"));

	// AUTHENTICATE LINK
	if (!InitAuthenticate())
	{
		m_server->RemoveClient(m_client->GetClientId());
		return;
	}

	// Authenticated OK - remove from blacklist and remove timeout
	m_server->RemAuthHostsBlacklist(m_client->GetClientName());
	m_socket->SetTimeout(m_server->AutoIdleDisconnectTimeout()*1000);
	vnclog.Print(LL_INTINFO, VNCLOG("authenticated connection\n"));

	// INIT PIXEL FORMAT

	// Get the screen format
	m_client->m_fullscreen = m_client->m_encodemgr.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 (int x=0; x<strlen(desktopname); x++)
		{
			desktopname[x] = tolower(desktopname[x]);
		}
	}
	else
	{
		strcpy(desktopname, "WinVNC");
	}

	// Send the server format message to the client
	rfbServerInitMsg server_ini;
	server_ini.format = m_client->m_encodemgr.m_buffer->GetLocalFormat();

	// Endian swaps
	server_ini.framebufferWidth = Swap16IfLE(m_client->m_fullscreen.br.x);
	server_ini.framebufferHeight = Swap16IfLE(m_client->m_fullscreen.br.y);
	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, strlen(desktopname)))
	{
		m_server->RemoveClient(m_client->GetClientId());
		return;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("sent pixel format to client\n"));

	// UNLOCK INITIAL SETUP
	// Initial negotiation is complete, so set the protocol ready flag
	m_client->EnableProtocol();
	
	// Add a fullscreen update to the client's update list
  { omni_mutex_lock l(m_client->GetUpdateLock());
	  m_client->m_update_tracker.add_changed(m_client->m_fullscreen);
  }

	// Clear the CapsLock and NumLock keys
	if (m_client->m_keyboardenabled)
	{
		ClearKeyState(VK_CAPITAL);
		// *** JNW - removed because people complain it's wrong
		//ClearKeyState(VK_NUMLOCK);
		ClearKeyState(VK_SCROLL);
	}

	// MAIN LOOP

	// Set the input thread to a high priority
	set_priority(omni_thread::PRIORITY_HIGH);

	BOOL connected = TRUE;
	while (connected)
	{
		rfbClientToServerMsg msg;

		// Ensure that we're running in the correct desktop
		if (!vncService::InputDesktopSelected())
			if (!vncService::SelectDesktop(NULL))
				break;

		// 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);

			// Prevent updates while the pixel format is changed
			m_client->DisableProtocol();
				
			// Tell the buffer object of the change			
			if (!m_client->m_encodemgr.SetClientFormat(msg.spf.format))
			{
				vnclog.Print(LL_CONNERR, VNCLOG("remote pixel format invalid\n"));

				connected = FALSE;
			}

			// Set the palette-changed flag, just in case...
			m_client->m_palettechanged = TRUE;

			// Re-enable updates
			m_client->EnableProtocol();
			
			break;

		case rfbSetEncodings:
			// Read the rest of the message:
			if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbSetEncodingsMsg-1))
			{
				connected = FALSE;
				break;
			}

			// Prevent updates while the encoder is changed
			m_client->DisableProtocol();

			// Read in the preferred encodings
			msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings);
			{
				int x;
				BOOL encoding_set = FALSE;

				// By default, don't use copyrect!
				m_client->m_update_tracker.enable_copyrect(false);

				for (x=0; x<msg.se.nEncodings; x++)
				{
					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)
					{
						m_client->m_update_tracker.enable_copyrect(true);
						continue;
					}

					// Have we already found a suitable encoding?
					if (!encoding_set)
					{
						// No, so try the buffer to see if this encoding will work...
						if (m_client->m_encodemgr.SetEncoding(Swap32IfLE(encoding)))
							encoding_set = TRUE;
					}
				}

				// If no encoding worked then default to RAW!
				if (!encoding_set)
				{
					vnclog.Print(LL_INTINFO, VNCLOG("defaulting to raw encoder\n"));

					if (!m_client->m_encodemgr.SetEncoding(Swap32IfLE(rfbEncodingRaw)))
					{
						vnclog.Print(LL_INTERR, VNCLOG("failed to select raw encoder!\n"));

						connected = FALSE;
					}
				}
			}

			// Re-enable updates
			m_client->EnableProtocol();

			break;
			
		case rfbFramebufferUpdateRequest:
			// Read the rest of the message:
			if (!m_socket->ReadExact(((char *) &msg)+1, sz_rfbFramebufferUpdateRequestMsg-1))
			{
				connected = FALSE;
				break;
			}

			{
				rfb::Rect update;

				// Get the specified rectangle as the region to send updates for.
				update.tl.x = Swap16IfLE(msg.fur.x);
				update.tl.y = Swap16IfLE(msg.fur.y);
				update.br.x = update.tl.x + Swap16IfLE(msg.fur.w);
				update.br.y = update.tl.y + Swap16IfLE(msg.fur.h);
				rfb::Region2D update_rgn = update;
        if (update_rgn.is_empty()) {
          vnclog.Print(LL_INTERR, VNCLOG("FATAL! client update region is empty!\n"));
          connected = FALSE;
          break;
        }

				{	omni_mutex_lock l(m_client->GetUpdateLock());

					// Add the requested area to the incremental update cliprect
					m_client->m_incr_rgn = m_client->m_incr_rgn.union_(update_rgn);

					// Is this request for a full update?
					if (!msg.fur.incremental)
					{
						// Yes, so add the region to the update tracker
						m_client->m_update_tracker.add_changed(update_rgn);

						// Tell the desktop grabber to fetch the region's latest state
						m_client->m_encodemgr.m_buffer->m_desktop->QueueRect(update);
					}

          // Kick the update thread (and create it if not there already)
					m_client->TriggerUpdateThread();
				}
			}
			break;

		case rfbKeyEvent:
			// Read the rest of the message:
			if (m_socket->ReadExact(((char *) &msg)+1, sz_rfbKeyEventMsg-1))
			{				
				if (m_client->m_keyboardenabled)
				{
					msg.ke.key = Swap32IfLE(msg.ke.key);

					// Get the keymapper to do the work
          vncKeymap::keyEvent(msg.ke.key, msg.ke.down);

					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->m_pointerenabled)
				{
					// Convert the coords to Big Endian
					msg.pe.x = Swap16IfLE(msg.pe.x);
					msg.pe.y = Swap16IfLE(msg.pe.y);

					// Work out the flags for this event
					DWORD flags = MOUSEEVENTF_ABSOLUTE;
          long data = 0;
					if (msg.pe.x != m_client->m_ptrevent.x ||
						msg.pe.y != m_client->m_ptrevent.y)
						flags |= MOUSEEVENTF_MOVE;
					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;
					}
					if ( (msg.pe.buttonMask & rfbButton2Mask) != 
						(m_client->m_ptrevent.buttonMask & rfbButton2Mask) )
					{
						flags |= (msg.pe.buttonMask & rfbButton2Mask) 
						    ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
					}
					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;
					}

          // Mouse wheel support
          if (msg.pe.buttonMask & rfbWheelUpMask) {
            flags |= MOUSEEVENTF_WHEEL;
            data = WHEEL_DELTA;
          }
          if (msg.pe.buttonMask & rfbWheelDownMask) {
            flags |= MOUSEEVENTF_WHEEL;
            data = -WHEEL_DELTA;
          }

					// Generate coordinate values
					unsigned long x = (msg.pe.x *  65535) / (m_client->m_fullscreen.br.x);
					unsigned long y = (msg.pe.y * 65535) / (m_client->m_fullscreen.br.y);

					// Do the pointer event
					::mouse_event(flags, (DWORD) x, (DWORD) y, data, 0);

					// Save the old position
					m_client->m_ptrevent = msg.pe;

					// Flag that a remote event occurred
					m_client->m_remoteevent = TRUE;

					// Tell the desktop hook system to grab the screen...
					m_client->m_encodemgr.m_buffer->m_desktop->TriggerUpdate();
				}
			}
			
			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
				m_server->UpdateLocalClipText(text);

				// Free the clip text we read
				delete [] text;
			}
			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.
	vnclog.Print(LL_CLIENTS, VNCLOG("client disconnected : %s (%hd)\n"),
									m_client->GetClientName(),
									m_client->GetClientId());


	// Disable the protocol to ensure that the update thread
	// is not accessing the desktop and buffer objects
	m_client->DisableProtocol();

	// Finally, it's safe to kill the update thread here
	if (m_client->m_updatethread) {
		m_client->m_updatethread->Kill();
		m_client->m_updatethread->join(NULL);
	}

	// Remove the client from the server
	// This may result in the desktop and buffer being destroyed
	// It also guarantees that the client will not be passed further
	// updates
	m_server->RemoveClient(m_client->GetClientId());

}
Example #5
0
BOOL
vncClientThread::InitAuthenticate()
{
	// Retrieve the local password
	char password[MAXPWLEN];
	m_server->GetPassword(password);
	vncPasswd::ToText plain(password);

	// Verify the peer host name against the AuthHosts string
	vncServer::AcceptQueryReject verified;
	if (m_auth) {
		verified = vncServer::aqrAccept;
	} else {
		verified = m_server->VerifyHost(m_socket->GetPeerName());
	}
	
	// If necessary, query the connection with a timed dialog
	if (verified == vncServer::aqrQuery) {
		vncAcceptDialog *acceptDlg = new vncAcceptDialog(m_server->QueryTimeout(), m_socket->GetPeerName());
		if ((acceptDlg == 0) || (!(acceptDlg->DoDialog())))
			verified = vncServer::aqrReject;
	}
	if (verified == vncServer::aqrReject) {
		CARD32 auth_val = Swap32IfLE(rfbConnFailed);
		char *errmsg = "Your connection has been rejected.";
		CARD32 errlen = Swap32IfLE(strlen(errmsg));
		if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val)))
			return FALSE;
		if (!m_socket->SendExact((char *)&errlen, sizeof(errlen)))
			return FALSE;
		m_socket->SendExact(errmsg, strlen(errmsg));
		return FALSE;
	}

	// By default we disallow passwordless workstations!
	if ((strlen(plain) == 0) && m_server->AuthRequired())
	{
		vnclog.Print(LL_CONNERR, VNCLOG("no password specified for server - client rejected\n"));

		// Send an error message to the client
		CARD32 auth_val = Swap32IfLE(rfbConnFailed);
		char *errmsg =
			"This server does not have a valid password enabled.  "
			"Until a password is set, incoming connections cannot be accepted.";
		CARD32 errlen = Swap32IfLE(strlen(errmsg));

		if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val)))
			return FALSE;
		if (!m_socket->SendExact((char *)&errlen, sizeof(errlen)))
			return FALSE;
		m_socket->SendExact(errmsg, strlen(errmsg));

		return FALSE;
	}

	// By default we filter out local loop connections, because they're pointless
	if (!m_server->LoopbackOk())
	{
		char *localname = strdup(m_socket->GetSockName());
		char *remotename = strdup(m_socket->GetPeerName());

		// Check that the local & remote names are different!
		if ((localname != NULL) && (remotename != NULL))
		{
			BOOL ok = strcmp(localname, remotename) != 0;

			if (localname != NULL)
				free(localname);

			if (remotename != NULL)
				free(remotename);

			if (!ok)
			{
				vnclog.Print(LL_CONNERR, VNCLOG("loopback connection attempted - client rejected\n"));
				
				// Send an error message to the client
				CARD32 auth_val = Swap32IfLE(rfbConnFailed);
				char *errmsg = "Local loop-back connections are disabled.";
				CARD32 errlen = Swap32IfLE(strlen(errmsg));

				if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val)))
					return FALSE;
				if (!m_socket->SendExact((char *)&errlen, sizeof(errlen)))
					return FALSE;
				m_socket->SendExact(errmsg, strlen(errmsg));

				return FALSE;
			}
		}
	}

	// Authenticate the connection, if required
	if (m_auth || (strlen(plain) == 0))
	{
		// Send no-auth-required message
		CARD32 auth_val = Swap32IfLE(rfbNoAuth);
		if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val)))
			return FALSE;
	}
	else
	{
		// Send auth-required message
		CARD32 auth_val = Swap32IfLE(rfbVncAuth);
		if (!m_socket->SendExact((char *)&auth_val, sizeof(auth_val)))
			return FALSE;

		BOOL auth_ok = TRUE;
		{
			// Now create a 16-byte challenge
			char challenge[16];
			vncRandomBytes((BYTE *)&challenge);

			// 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
			for (int i=0; i<sizeof(challenge); i++)
			{
				if (challenge[i] != response[i])
				{
					auth_ok = FALSE;
					break;
				}
			}
		}

		// Did the authentication work?
		CARD32 authmsg;
		if (!auth_ok)
		{
			vnclog.Print(LL_CONNERR, VNCLOG("authentication failed\n"));

			authmsg = Swap32IfLE(rfbVncAuthFailed);
			m_socket->SendExact((char *)&authmsg, sizeof(authmsg));
			return FALSE;
		}
		else
		{
			// Tell the client we're ok
			authmsg = Swap32IfLE(rfbVncAuthOK);
			if (!m_socket->SendExact((char *)&authmsg, sizeof(authmsg)))
				return FALSE;
		}
	}

	// 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
			vnclog.Print(LL_INTINFO, VNCLOG("non-shared connection - disconnecting old clients\n"));
			m_server->KillAuthClients();
		} else if (m_server->ConnectPriority() > 1)
		{
			// Existing
			if (m_server->AuthClientCount() > 0)
			{
				vnclog.Print(LL_CLIENTS, VNCLOG("connections already exist - client rejected\n"));
				return FALSE;
			}
		}
	}

	// Tell the server that this client is ok
	return m_server->Authenticated(m_client->GetClientId());
}
Example #6
0
void *
vncDesktopThread::run_undetached(void *arg)
{
	// Save the thread's "home" desktop, under NT (no effect under 9x)
	HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId());

	// Attempt to initialise and return success or failure
	if (!m_desktop->Startup())
	{
		vncService::SelectHDESK(home_desktop);
		ReturnVal(FALSE);
		return NULL;
	}

  // Grab the initial display contents
  // *** m_desktop->m_buffer.GrabRegion(m_desktop->m_bmrect);
  // *** m_desktop->m_buffer.Clear(m_desktop->m_bmrect);

	// Succeeded to initialise ok
	ReturnVal(TRUE);

	// START PROCESSING DESKTOP MESSAGES

	// We set a flag inside the desktop handler here, to indicate it's now safe
	// to handle clipboard messages
	m_desktop->SetClipboardActive(TRUE);

	// All changes in the state of the display are stored in a local
	// UpdateTracker object, and are flushed to the vncServer whenever
	// client updates are about to be triggered
	rfb::SimpleUpdateTracker clipped_updates;
	rfb::ClippedUpdateTracker updates(clipped_updates, m_desktop->m_bmrect);
	clipped_updates.enable_copyrect(true);

	// Incoming update messages are collated into a single region cache
	// The region cache areas are checked for changes before an update
	// is triggered, and the changed areas are passed to the UpdateTracker
	rfb::Region2D rgncache = m_desktop->m_bmrect;

	// The previous cursor position is stored, to allow us to erase the
	// old instance whenever it moves.
	rfb::Point oldcursorpos;

	// Set the hook thread to a high priority
	// *** set_priority(omni_thread::PRIORITY_HIGH);

	BOOL idle_skip = TRUE;
	ULONG idle_skip_count = 0;
	MSG msg;
	while (TRUE)
	{
		if (!PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
			//
			// - MESSAGE QUEUE EMPTY
			// Whenever the message queue becomes empty, we check to see whether
			// there are updates to be passed to clients.
			if (idle_skip) {
				idle_skip = FALSE;
				if (idle_skip_count++ < 4) {
					Sleep(5);
					continue;
				}
			}
			idle_skip_count = 0;

			// Clear the triggered flag
			m_desktop->m_update_triggered = FALSE;

			//
			// CHECK SCREEN FORMAT
			// First, we must check that the screen hasnt changed too much.
			if (m_desktop->m_displaychanged || !vncService::InputDesktopSelected())
			{
				rfbServerInitMsg oldscrinfo = m_desktop->m_scrinfo;
				m_desktop->m_displaychanged = FALSE;

				// Attempt to close the old hooks
				if (!m_desktop->Shutdown())
				{
					m_server->KillAuthClients();
					break;
				}

				// Now attempt to re-install them!
				if (!m_desktop->Startup())
				{
					m_server->KillAuthClients();
					break;
				}

				// Check that the screen info hasn't changed
				//vnclog.Print(LL_INTINFO, VNCLOG("SCR: old screen format %dx%dx%d\n"),
				//	oldscrinfo.framebufferWidth,
				//	oldscrinfo.framebufferHeight,
				//	oldscrinfo.format.bitsPerPixel);
				//vnclog.Print(LL_INTINFO, VNCLOG("SCR: new screen format %dx%dx%d\n"),
				//	m_desktop->m_scrinfo.framebufferWidth,
				//	m_desktop->m_scrinfo.framebufferHeight,
				//	m_desktop->m_scrinfo.format.bitsPerPixel);
				if ((m_desktop->m_scrinfo.framebufferWidth != oldscrinfo.framebufferWidth) ||
					(m_desktop->m_scrinfo.framebufferHeight != oldscrinfo.framebufferHeight))
				{
					m_server->KillAuthClients();
					break;
				} else if (memcmp(&m_desktop->m_scrinfo.format, &oldscrinfo.format, sizeof(rfbPixelFormat)) != 0)
				{
					m_server->UpdateLocalFormat();
				}

				// Adjust the UpdateTracker clip region
				updates.set_clip_region(m_desktop->m_bmrect);

				// Add a full screen update to all the clients
				rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_bmrect));
				m_server->UpdatePalette();
			}
	
			//
			// CALCULATE CHANGES
			//

			if (m_desktop->m_server->UpdateWanted())
			{
				// POLL PROBLEM AREAS
				// We add specific areas of the screen to the region cache,
				// causing them to be fetched for processing.
				if (m_desktop->m_server->PollFullScreen())
				{
					rfb::Rect rect = m_desktop->m_qtrscreen;
					rect = rect.translate(rfb::Point(0, m_desktop->m_pollingcycle * m_desktop->m_qtrscreen.br.y));
					rgncache = rgncache.union_(rfb::Region2D(rect));
					m_desktop->m_pollingcycle = (m_desktop->m_pollingcycle + 1) % 4;
				}
				if (m_desktop->m_server->PollForeground())
				{
					// Get the window rectangle for the currently selected window
					HWND hwnd = GetForegroundWindow();
					if (hwnd != NULL)
						PollWindow(rgncache, hwnd);
				}
				if (m_desktop->m_server->PollUnderCursor())
				{
					// Find the mouse position
					POINT mousepos;
					if (GetCursorPos(&mousepos))
					{
						// Find the window under the mouse
						HWND hwnd = WindowFromPoint(mousepos);
						if (hwnd != NULL)
							PollWindow(rgncache, hwnd);
					}
				}

				// PROCESS THE MOUSE POINTER
				// Some of the hard work is done in clients, some here
				// This code fetches the desktop under the old pointer position
				// but the client is responsible for actually encoding and sending
				// it when required.
				// This code also renders the pointer and saves the rendered position
				// Clients include this when rendering updates.
				// The code is complicated in this way because we wish to avoid 
				// rendering parts of the screen the mouse moved through between
				// client updates, since in practice they will probably not have changed.

				// Re-render the mouse's old location if it's moved
				BOOL cursormoved = FALSE;
				POINT cursorpos;
				if (GetCursorPos(&cursorpos) && 
					((cursorpos.x != oldcursorpos.x) ||
					(cursorpos.y != oldcursorpos.y))) {
					cursormoved = TRUE;
					oldcursorpos = rfb::Point(cursorpos);
				}
				if (cursormoved) {
					if (!m_desktop->m_cursorpos.is_empty())
						rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos));
				}

				{
					// Prevent any clients from accessing the Buffer
					omni_mutex_lock l(m_desktop->m_update_lock);

					// CHECK FOR COPYRECTS
					// This actually just checks where the Foreground window is
					m_desktop->CalcCopyRects(updates);

					// GRAB THE DISPLAY
					// Fetch data from the display to our display cache.
					m_desktop->m_buffer.GrabRegion(rgncache);
				  // Render the mouse
				  m_desktop->m_buffer.GrabMouse();
					if (cursormoved) {
						// Inform clients that it has moved
						m_desktop->m_server->UpdateMouse();
						// Get the buffer to fetch the pointer bitmap
						if (!m_desktop->m_cursorpos.is_empty())
							rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos));
					}

					// SCAN THE CHANGED REGION FOR ACTUAL CHANGES
					// The hooks return hints as to areas that may have changed.
					// We check the suggested areas, and just send the ones that
					// have actually changed.
					// Note that we deliberately don't check the copyrect destination
					// here, to reduce the overhead & the likelihood of corrupting the
					// backbuffer contents.
					rfb::Region2D checkrgn = rgncache.subtract(clipped_updates.get_copied_region());
					rgncache = clipped_updates.get_copied_region();
					rfb::Region2D changedrgn;
					m_desktop->m_buffer.CheckRegion(changedrgn, checkrgn);

					// FLUSH UPDATES TO CLIENTS
					// Add the bits that have really changed to their update regions
					// Note that the cursor is NOT included - they must do that
					// themselves, for the reasons above.
					// This call implicitly kicks clients to update themselves

					updates.add_changed(changedrgn);
					clipped_updates.get_update(m_server->GetUpdateTracker());
				}

				// Clear the update tracker and region cache
				clipped_updates.clear();
			}

			// Now wait for more messages to be queued
			if (!WaitMessage()) {
				//vnclog.Print(LL_INTERR, VNCLOG("WaitMessage() failed\n"));
				break;
			}
		} else if (msg.message == RFB_SCREEN_UPDATE) {
			// Process an incoming update event

			// An area of the screen has changed
			rfb::Rect rect;
			rect.tl = rfb::Point((SHORT)LOWORD(msg.wParam), (SHORT)HIWORD(msg.wParam));
			rect.br = rfb::Point((SHORT)LOWORD(msg.lParam), (SHORT)HIWORD(msg.lParam));
			rect = rect.intersect(m_desktop->m_bmrect);
			if (!rect.is_empty()) {
				rgncache = rgncache.union_(rfb::Region2D(rect));
			}

			idle_skip = TRUE;
		} else if (msg.message == RFB_MOUSE_UPDATE) {
			// Process an incoming mouse event

			// Save the cursor ID
			m_desktop->SetCursor((HCURSOR) msg.wParam);

			idle_skip = TRUE;
		} else if (msg.message == WM_QUIT) {
			break;
		} else {
			// Process any other messages normally
			DispatchMessage(&msg);

			idle_skip = TRUE;
		}
	}

	m_desktop->SetClipboardActive(FALSE);
	
	//vnclog.Print(LL_INTINFO, VNCLOG("quitting desktop server thread\n"));

	// Clear all the hooks and close windows, etc.
	m_desktop->Shutdown();

	// Clear the shift modifier keys, now that there are no remote clients
	vncKeymap::ClearShiftKeys();

	// Switch back into our home desktop, under NT (no effect under 9x)
	vncService::SelectHDESK(home_desktop);

	return NULL;
}
void vncHTTPConnectThread::DoHTTP(VSocket *socket)
{
    char filename[1024];
    char *line;

    // Read in the HTTP header
    if ((line = ReadLine(socket, '\n', 1024)) == NULL)
        return;

    // Scan the header for the filename and free the storage
    int result = sscanf(line, "GET %s ", (char*)&filename);
    delete [] line;
    if ((result == 0) || (result == EOF))
        return;

    vnclog.Print(LL_CLIENTS, VNCLOG("file %s requested\n"), filename);

    // Read in the rest of the browser's request data and discard...
    BOOL emptyline=TRUE;

    for (;;)
    {
        char c;

        if (!socket->ReadExact(&c, 1))
            return;
        if (c=='\n')
        {
            if (emptyline)
                break;
            emptyline = TRUE;
        }
        else
            if (c >= ' ')
            {
                emptyline = FALSE;
            }
    }

    vnclog.Print(LL_INTINFO, VNCLOG("parameters read\n"));

    if (filename[0] != '/')
    {
        vnclog.Print(LL_CONNERR, VNCLOG("filename didn't begin with '/'\n"));
        socket->SendExact(HTTP_MSG_NOSUCHFILE, strlen(HTTP_MSG_NOSUCHFILE));
        return;
    }

    // Switch, dependent upon the filename:
    if (strcmp(filename, "/") == 0)
    {
        char indexpage[2048 + MAX_COMPUTERNAME_LENGTH + 1];

        vnclog.Print(LL_CLIENTS, VNCLOG("sending main page\n"));

        // Send the OK notification message to the client
        if (!socket->SendExact(HTTP_MSG_OK, strlen(HTTP_MSG_OK)))
            return;

        // Compose the index page
        if (m_server->SockConnected())
        {
            int width, height, depth;

            // Get the screen's dimensions
            m_server->GetScreenInfo(width, height, depth);

            // Get the name of this desktop
            char desktopname[MAX_COMPUTERNAME_LENGTH+1];
            if (GetCompName(desktopname, MAX_COMPUTERNAME_LENGTH))
            {
                // Make the name lowercase
                for (int x=0; x<strlen(desktopname); x++)
                {
                    desktopname[x] = tolower(desktopname[x]);
                }
            }
            else
            {
                strcpy(desktopname, szAppName);
            }

            // Send the java applet page
            sprintf(indexpage, HTTP_FMT_INDEX,
                desktopname, width, height+32,
                m_server->GetPort()
                );
        }
        else
        {
            // Send a "sorry, not allowed" page
            sprintf(indexpage, HTTP_MSG_NOSOCKCONN);
        }

        // Send the page
        if (socket->SendExact(indexpage, strlen(indexpage)))
            vnclog.Print(LL_INTINFO, VNCLOG("sent page\n"));

        return;
    }

    // File requested was not the index so check the mappings
    // list for a different file.

    // Now search the mappings for the desired file
    for (int x=0; x < filemappingsize; x++)
    {
        if (strcmp(filename, filemapping[x].filename) == 0)
        {
            VOID *resourceptr = NULL;

            vnclog.Print(LL_INTINFO, VNCLOG("requested file recognised\n"));

            // Load the resource
            APIRET rc = DosGetResource(NULLHANDLE, filemapping[x].type,
                                       filemapping[x].resourceID, &resourceptr);

            if ( (rc != 0) || (resourceptr==NULL) )
                return;

            vnclog.Print(LL_INTINFO, VNCLOG("sending file...\n"));

            // Send the OK message
            if (!socket->SendExact(HTTP_MSG_OK, strlen(HTTP_MSG_OK)))
            {
                DosFreeResource( resourceptr );
                return;
            }

            // Now send the entirety of the data to the client
            if (!socket->SendExact( (const char *)resourceptr, filemapping[x].rsize))
            {
                DosFreeResource( resourceptr );
                return;
            }

            vnclog.Print(LL_INTINFO, VNCLOG("file successfully sent\n"));
            DosFreeResource( resourceptr );

            return;
        }
    }

    // Send the NoSuchFile notification message to the client
    if (!socket->SendExact(HTTP_MSG_NOSUCHFILE, strlen(HTTP_MSG_NOSUCHFILE)))
        return;
}