blob: 3b0e1a0a6d0f9d5fb31f04960d7b6612b0f7540b [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- WinVNC Version 4.0 Main Routine
20
21#include <winvnc/VNCServerWin32.h>
22#include <winvnc/resource.h>
23#include <winvnc/STrayIcon.h>
24#include <rfb_win32/ComputerName.h>
25#include <rfb_win32/CurrentUser.h>
26#include <rfb_win32/Service.h>
27#include <rfb/SSecurityFactoryStandard.h>
28#include <rfb/Hostname.h>
29#include <rfb/LogWriter.h>
30#include <rfb_win32/SFileTransferWin32.h>
31
32using namespace rfb;
33using namespace win32;
34using namespace winvnc;
35using namespace network;
36
37static LogWriter vlog("VNCServerWin32");
38
39
40const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TightVNC\\WinVNC4");
41
42// FIXME: Move into an .h file?
43extern const UINT VNCM_FT_DOWNLOAD;
44
45
46static IntParameter http_port("HTTPPortNumber",
47 "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
48static IntParameter port_number("PortNumber",
49 "TCP/IP port on which the server will accept connections", 5900);
50static StringParameter hosts("Hosts",
51 "Filter describing which hosts are allowed access to this server", "+0.0.0.0/0.0.0.0");
52static BoolParameter localHost("LocalHost",
53 "Only accept connections from via the local loop-back network interface", false);
54static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
55 "Only prompt for a local user to accept incoming connections if there is a user logged on", false);
56
57
58VNCServerWin32::VNCServerWin32()
59 : command(NoCommand), commandSig(commandLock),
60 commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
61 vncServer(CStr(ComputerName().buf), &desktop),
62 hostThread(0), runServer(false), isDesktopStarted(false),
63 httpServer(&vncServer), config(&sockMgr), trayIcon(0),
64 rfbSock(&sockMgr), httpSock(&sockMgr),
65 queryConnectDialog(0)
66{
67 // Initialise the desktop
68 desktop.setStatusLocation(&isDesktopStarted);
69
70 // Initialise the VNC server
71 vncServer.setQueryConnectionHandler(this);
72
73 // Register the desktop's event to be handled
74 sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
75
76 // Register the queued command event to be handled
77 sockMgr.addEvent(commandEvent, this);
78
79 vncServer.setFTManager((rfb::SFileTransferManager *)&m_FTManager);
80}
81
82VNCServerWin32::~VNCServerWin32() {
83 delete trayIcon;
84
85 // Stop the SDisplay from updating our state
86 desktop.setStatusLocation(0);
87
88 // Join the Accept/Reject dialog thread
89 if (queryConnectDialog)
90 delete queryConnectDialog->join();
91}
92
93
94void VNCServerWin32::processAddressChange(network::SocketListener* sock_) {
95 if (!trayIcon || (sock_ != rfbSock.sock))
96 return;
97
98 // Tool-tip prefix depends on server mode
99 const TCHAR* prefix = _T("VNC Server (User):");
100 if (isServiceProcess())
101 prefix = _T("VNC Server (Service):");
102
103 // Fetch the list of addresses
104 std::list<char*> addrs;
105 if (rfbSock.sock)
106 rfbSock.sock->getMyAddresses(&addrs);
107 else
108 addrs.push_front(strDup("Not accepting connections"));
109
110 // Allocate space for the new tip
111 std::list<char*>::iterator i, next_i;
112 int length = _tcslen(prefix)+1;
113 for (i=addrs.begin(); i!= addrs.end(); i++)
114 length += strlen(*i) + 1;
115
116 // Build the new tip
117 TCharArray toolTip(length);
118 _tcscpy(toolTip.buf, prefix);
119 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
120 next_i = i; next_i ++;
121 TCharArray addr = *i; // Assumes ownership of string
122 _tcscat(toolTip.buf, addr.buf);
123 if (next_i != addrs.end())
124 _tcscat(toolTip.buf, _T(","));
125 }
126
127 // Pass the new tip to the tray icon
128 vlog.info("Refreshing tray icon");
129 trayIcon->setToolTip(toolTip.buf);
130}
131
132void VNCServerWin32::regConfigChanged() {
133 // -=- Make sure we're listening on the right ports.
134 rfbSock.setServer(&vncServer);
135 rfbSock.setPort(port_number, localHost);
136 httpSock.setServer(&httpServer);
137 httpSock.setPort(http_port, localHost);
138
139 // -=- Update the Java viewer's web page port number.
140 httpServer.setRFBport(rfbSock.sock ? port_number : 0);
141
142 // -=- Update the TCP address filter for both ports, if open.
143 CharArray pattern(hosts.getData());
144 rfbSock.setFilter(pattern.buf);
145 httpSock.setFilter(pattern.buf);
146
147 // -=- Update the tray icon tooltip text with IP addresses
148 processAddressChange(rfbSock.sock);
149}
150
151
152int VNCServerWin32::run() {
153 { Lock l(runLock);
154 hostThread = Thread::self();
155 runServer = true;
156 }
157
158 // - Create the tray icon (if possible)
159 trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
160 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
161 IDR_TRAY);
162
163 // - Register for notification of configuration changes
164 config.setCallback(this);
165 if (isServiceProcess())
166 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
167 else
168 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
169
170 // - Set the address-changed handler for the RFB socket
171 rfbSock.setAddressChangeNotifier(this);
172
173 DWORD result = 0;
174 try {
175 vlog.debug("Entering message loop");
176
177 // - Run the server until we're told to quit
178 MSG msg;
179 int result = 0;
180 while (runServer) {
181 result = sockMgr.getMessage(&msg, NULL, 0, 0);
182 if (result < 0)
183 throw rdr::SystemException("getMessage", GetLastError());
184 if (!isServiceProcess() && (result == 0))
185 break;
186 if (msg.message == VNCM_FT_DOWNLOAD)
187 m_FTManager.processDownloadMsg(msg);
188 TranslateMessage(&msg);
189 DispatchMessage(&msg);
190 }
191
192 vlog.debug("Server exited cleanly");
193 } catch (rdr::SystemException &s) {
194 vlog.error(s.str());
195 result = s.err;
196 } catch (rdr::Exception &e) {
197 vlog.error(e.str());
198 }
199
200 { Lock l(runLock);
201 runServer = false;
202 hostThread = 0;
203 }
204
205 return result;
206}
207
208void VNCServerWin32::stop() {
209 Lock l(runLock);
210 runServer = false;
211 if (hostThread)
212 PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
213}
214
215
216bool VNCServerWin32::disconnectClients(const char* reason) {
217 return queueCommand(DisconnectClients, reason, 0);
218}
219
220bool VNCServerWin32::addNewClient(const char* client) {
221 TcpSocket* sock = 0;
222 try {
223 CharArray hostname;
224 int port;
225 getHostAndPort(client, &hostname.buf, &port, 5500);
226 vlog.error("port=%d", port);
227 sock = new TcpSocket(hostname.buf, port);
228 if (queueCommand(AddClient, sock, 0))
229 return true;
230 delete sock;
231 } catch (...) {
232 delete sock;
233 }
234 return false;
235}
236
237bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
238 return queueCommand(GetClientsInfo, LCInfo, 0);
239}
240
241bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
242 return queueCommand(SetClientsStatus, LCInfo, 0);
243}
244
245VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
246 const char* userName,
247 char** reason)
248{
249 if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn())
250 return VNCServerST::ACCEPT;
251 if (queryConnectDialog) {
252 *reason = rfb::strDup("Another connection is currently being queried.");
253 return VNCServerST::REJECT;
254 }
255 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
256 queryConnectDialog->startDialog();
257 return VNCServerST::PENDING;
258}
259
260void VNCServerWin32::queryConnectionComplete() {
261 queueCommand(QueryConnectionComplete, 0, 0, false);
262}
263
264
265bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
266 Lock l(commandLock);
267 while (command != NoCommand)
268 commandSig.wait();
269 command = cmd;
270 commandData = data;
271 commandDataLen = len;
272 SetEvent(commandEvent);
273 if (wait) {
274 while (command != NoCommand)
275 commandSig.wait();
276 commandSig.signal();
277 }
278 return true;
279}
280
281void VNCServerWin32::processEvent(HANDLE event_) {
282 ResetEvent(event_);
283
284 if (event_ == commandEvent.h) {
285 // If there is no command queued then return immediately
286 {
287 Lock l(commandLock);
288 if (command == NoCommand)
289 return;
290 }
291
292 // Perform the required command
293 switch (command) {
294
295 case DisconnectClients:
296 // Disconnect all currently active VNC Viewers
297 vncServer.closeClients((const char*)commandData);
298 break;
299
300 case AddClient:
301 // Make a reverse connection to a VNC Viewer
302 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
303 break;
304 case GetClientsInfo:
305 vncServer.getConnInfo((ListConnInfo*)commandData);
306 break;
307 case SetClientsStatus:
308 vncServer.setConnStatus((ListConnInfo*)commandData);
309 break;
310
311 case QueryConnectionComplete:
312 // The Accept/Reject dialog has completed
313 // Get the result, then clean it up
314 vncServer.approveConnection(queryConnectDialog->getSock(),
315 queryConnectDialog->isAccepted(),
316 "Connection rejected by user");
317 delete queryConnectDialog->join();
318 queryConnectDialog = 0;
319 break;
320
321 default:
322 vlog.error("unknown command %d queued", command);
323 };
324
325 // Clear the command and signal completion
326 {
327 Lock l(commandLock);
328 command = NoCommand;
329 commandSig.signal();
330 }
331 }
332}
333