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