diff options
Diffstat (limited to 'win/winvnc/VNCServerWin32.cxx')
-rw-r--r-- | win/winvnc/VNCServerWin32.cxx | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/win/winvnc/VNCServerWin32.cxx b/win/winvnc/VNCServerWin32.cxx new file mode 100644 index 00000000..3b0e1a0a --- /dev/null +++ b/win/winvnc/VNCServerWin32.cxx @@ -0,0 +1,333 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- WinVNC Version 4.0 Main Routine + +#include <winvnc/VNCServerWin32.h> +#include <winvnc/resource.h> +#include <winvnc/STrayIcon.h> +#include <rfb_win32/ComputerName.h> +#include <rfb_win32/CurrentUser.h> +#include <rfb_win32/Service.h> +#include <rfb/SSecurityFactoryStandard.h> +#include <rfb/Hostname.h> +#include <rfb/LogWriter.h> +#include <rfb_win32/SFileTransferWin32.h> + +using namespace rfb; +using namespace win32; +using namespace winvnc; +using namespace network; + +static LogWriter vlog("VNCServerWin32"); + + +const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TightVNC\\WinVNC4"); + +// FIXME: Move into an .h file? +extern const UINT VNCM_FT_DOWNLOAD; + + +static IntParameter http_port("HTTPPortNumber", + "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800); +static IntParameter port_number("PortNumber", + "TCP/IP port on which the server will accept connections", 5900); +static StringParameter hosts("Hosts", + "Filter describing which hosts are allowed access to this server", "+0.0.0.0/0.0.0.0"); +static BoolParameter localHost("LocalHost", + "Only accept connections from via the local loop-back network interface", false); +static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn", + "Only prompt for a local user to accept incoming connections if there is a user logged on", false); + + +VNCServerWin32::VNCServerWin32() + : command(NoCommand), commandSig(commandLock), + commandEvent(CreateEvent(0, TRUE, FALSE, 0)), + vncServer(CStr(ComputerName().buf), &desktop), + hostThread(0), runServer(false), isDesktopStarted(false), + httpServer(&vncServer), config(&sockMgr), trayIcon(0), + rfbSock(&sockMgr), httpSock(&sockMgr), + queryConnectDialog(0) +{ + // Initialise the desktop + desktop.setStatusLocation(&isDesktopStarted); + + // Initialise the VNC server + vncServer.setQueryConnectionHandler(this); + + // Register the desktop's event to be handled + sockMgr.addEvent(desktop.getUpdateEvent(), &desktop); + + // Register the queued command event to be handled + sockMgr.addEvent(commandEvent, this); + + vncServer.setFTManager((rfb::SFileTransferManager *)&m_FTManager); +} + +VNCServerWin32::~VNCServerWin32() { + delete trayIcon; + + // Stop the SDisplay from updating our state + desktop.setStatusLocation(0); + + // Join the Accept/Reject dialog thread + if (queryConnectDialog) + delete queryConnectDialog->join(); +} + + +void VNCServerWin32::processAddressChange(network::SocketListener* sock_) { + if (!trayIcon || (sock_ != rfbSock.sock)) + return; + + // Tool-tip prefix depends on server mode + const TCHAR* prefix = _T("VNC Server (User):"); + if (isServiceProcess()) + prefix = _T("VNC Server (Service):"); + + // Fetch the list of addresses + std::list<char*> addrs; + if (rfbSock.sock) + rfbSock.sock->getMyAddresses(&addrs); + else + addrs.push_front(strDup("Not accepting connections")); + + // Allocate space for the new tip + std::list<char*>::iterator i, next_i; + int length = _tcslen(prefix)+1; + for (i=addrs.begin(); i!= addrs.end(); i++) + length += strlen(*i) + 1; + + // Build the new tip + TCharArray toolTip(length); + _tcscpy(toolTip.buf, prefix); + for (i=addrs.begin(); i!= addrs.end(); i=next_i) { + next_i = i; next_i ++; + TCharArray addr = *i; // Assumes ownership of string + _tcscat(toolTip.buf, addr.buf); + if (next_i != addrs.end()) + _tcscat(toolTip.buf, _T(",")); + } + + // Pass the new tip to the tray icon + vlog.info("Refreshing tray icon"); + trayIcon->setToolTip(toolTip.buf); +} + +void VNCServerWin32::regConfigChanged() { + // -=- Make sure we're listening on the right ports. + rfbSock.setServer(&vncServer); + rfbSock.setPort(port_number, localHost); + httpSock.setServer(&httpServer); + httpSock.setPort(http_port, localHost); + + // -=- Update the Java viewer's web page port number. + httpServer.setRFBport(rfbSock.sock ? port_number : 0); + + // -=- Update the TCP address filter for both ports, if open. + CharArray pattern(hosts.getData()); + rfbSock.setFilter(pattern.buf); + httpSock.setFilter(pattern.buf); + + // -=- Update the tray icon tooltip text with IP addresses + processAddressChange(rfbSock.sock); +} + + +int VNCServerWin32::run() { + { Lock l(runLock); + hostThread = Thread::self(); + runServer = true; + } + + // - Create the tray icon (if possible) + trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED, + IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE, + IDR_TRAY); + + // - Register for notification of configuration changes + config.setCallback(this); + if (isServiceProcess()) + config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath); + else + config.setKey(HKEY_CURRENT_USER, RegConfigPath); + + // - Set the address-changed handler for the RFB socket + rfbSock.setAddressChangeNotifier(this); + + DWORD result = 0; + try { + vlog.debug("Entering message loop"); + + // - Run the server until we're told to quit + MSG msg; + int result = 0; + while (runServer) { + result = sockMgr.getMessage(&msg, NULL, 0, 0); + if (result < 0) + throw rdr::SystemException("getMessage", GetLastError()); + if (!isServiceProcess() && (result == 0)) + break; + if (msg.message == VNCM_FT_DOWNLOAD) + m_FTManager.processDownloadMsg(msg); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + vlog.debug("Server exited cleanly"); + } catch (rdr::SystemException &s) { + vlog.error(s.str()); + result = s.err; + } catch (rdr::Exception &e) { + vlog.error(e.str()); + } + + { Lock l(runLock); + runServer = false; + hostThread = 0; + } + + return result; +} + +void VNCServerWin32::stop() { + Lock l(runLock); + runServer = false; + if (hostThread) + PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0); +} + + +bool VNCServerWin32::disconnectClients(const char* reason) { + return queueCommand(DisconnectClients, reason, 0); +} + +bool VNCServerWin32::addNewClient(const char* client) { + TcpSocket* sock = 0; + try { + CharArray hostname; + int port; + getHostAndPort(client, &hostname.buf, &port, 5500); + vlog.error("port=%d", port); + sock = new TcpSocket(hostname.buf, port); + if (queueCommand(AddClient, sock, 0)) + return true; + delete sock; + } catch (...) { + delete sock; + } + return false; +} + +bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) { + return queueCommand(GetClientsInfo, LCInfo, 0); +} + +bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) { + return queueCommand(SetClientsStatus, LCInfo, 0); +} + +VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock, + const char* userName, + char** reason) +{ + if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) + return VNCServerST::ACCEPT; + if (queryConnectDialog) { + *reason = rfb::strDup("Another connection is currently being queried."); + return VNCServerST::REJECT; + } + queryConnectDialog = new QueryConnectDialog(sock, userName, this); + queryConnectDialog->startDialog(); + return VNCServerST::PENDING; +} + +void VNCServerWin32::queryConnectionComplete() { + queueCommand(QueryConnectionComplete, 0, 0, false); +} + + +bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) { + Lock l(commandLock); + while (command != NoCommand) + commandSig.wait(); + command = cmd; + commandData = data; + commandDataLen = len; + SetEvent(commandEvent); + if (wait) { + while (command != NoCommand) + commandSig.wait(); + commandSig.signal(); + } + return true; +} + +void VNCServerWin32::processEvent(HANDLE event_) { + ResetEvent(event_); + + if (event_ == commandEvent.h) { + // If there is no command queued then return immediately + { + Lock l(commandLock); + if (command == NoCommand) + return; + } + + // Perform the required command + switch (command) { + + case DisconnectClients: + // Disconnect all currently active VNC Viewers + vncServer.closeClients((const char*)commandData); + break; + + case AddClient: + // Make a reverse connection to a VNC Viewer + sockMgr.addSocket((network::Socket*)commandData, &vncServer); + break; + case GetClientsInfo: + vncServer.getConnInfo((ListConnInfo*)commandData); + break; + case SetClientsStatus: + vncServer.setConnStatus((ListConnInfo*)commandData); + break; + + case QueryConnectionComplete: + // The Accept/Reject dialog has completed + // Get the result, then clean it up + vncServer.approveConnection(queryConnectDialog->getSock(), + queryConnectDialog->isAccepted(), + "Connection rejected by user"); + delete queryConnectDialog->join(); + queryConnectDialog = 0; + break; + + default: + vlog.error("unknown command %d queued", command); + }; + + // Clear the command and signal completion + { + Lock l(commandLock); + command = NoCommand; + commandSig.signal(); + } + } +} + |