blob: cfd269522f3c0c353ceed79fd8552f4a8fb6640d [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// -=- CConnThread.cxx
20
21// A CConnThread instance is created for each new connection.
22// The CConnThread creates the corresponding CConn instance
23// and manages it.
24
25#include <stdlib.h>
26#include <rfb/LogWriter.h>
27#include <rfb/Hostname.h>
28#include <rfb_win32/MsgBox.h>
29#include <network/TcpSocket.h>
30#include <vncviewer/CConnThread.h>
31#include <vncviewer/CConn.h>
32#include <vncviewer/ConnectionDialog.h>
33#include <vncviewer/ConnectingDialog.h>
34#include <vncviewer/UserPasswdDialog.h>
35#include <set>
36
37using namespace rfb;
38using namespace win32;
39
40static LogWriter vlog("CConnThread");
41
42static std::set<CConnThread*> threads;
43static Mutex threadsLock;
44static Handle noMoreThreads(CreateEvent(0, TRUE, FALSE, 0));
45
46
47CConnThread::CConnThread() : Thread("CConnThread"), isConfig(false),
48 sock(0), reverse(false) {
49 vlog.info("CConnThread (dialog)");
50 setDeleteAfterRun();
51 Lock l(threadsLock);
52 threads.insert(this);
53 start();
54}
55
56CConnThread::CConnThread(const char* hostOrConfig_, bool isConfig_)
57 : Thread("CConnThread"), hostOrConfig(strDup(hostOrConfig_)),
58 isConfig(isConfig_), sock(0), reverse(false) {
59 vlog.info("CConnThread (host/port)");
60 setDeleteAfterRun();
61 Lock l(threadsLock);
62 threads.insert(this);
63 start();
64}
65
66CConnThread::CConnThread(network::Socket* sock_, bool reverse_)
67 : Thread("CConnThread"), isConfig(false), sock(sock_), reverse(reverse_) {
68 vlog.info("CConnThread (reverse connection)");
69 setDeleteAfterRun();
70 Lock l(threadsLock);
71 threads.insert(this);
72 start();
73}
74
75CConnThread::~CConnThread() {
76 Lock l(threadsLock);
77 threads.erase(this);
78 if (threads.empty())
79 SetEvent(noMoreThreads);
80 delete sock;
81}
82
83
84void CConnThread::run() {
85 CConnOptions options;
86 bool reconnect;
87
88 do {
89 {
90 CConn conn;
91 reconnect = false;
92
93 // If there is no socket object then set the host & port info
94 if (!sock && !options.host.buf) {
95 try {
96 if (isConfig) {
97 // A configuration file name was specified - load it
98 CharArray filename = hostOrConfig.takeBuf();
99 options.readFromFile(filename.buf);
100 } else {
101 // An actual hostname (and possibly port) was specified
102 options.host.replaceBuf(hostOrConfig.takeBuf());
103 }
104
105 if (!options.host.buf) {
106 // No host was specified - prompt for one
107 ConnectionDialog connDlg(&conn);
108 if (!connDlg.showDialog())
109 return;
110 options = conn.getOptions();
111 options.setHost(CStr(connDlg.hostname.buf));
112 }
113 } catch (rdr::Exception& e) {
114 MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
115 return;
116 }
117 }
118
119 // Apply the connection options to the CConn
120 conn.applyOptions(options);
121
122 if (!sock) {
123 // There is no existing connection - better make one
124 const char* hostAndPort = conn.getOptions().host.buf;
125
126 try {
127 ConnectingDialog dlg;
128 sock = dlg.connect(hostAndPort);
129
130 // If the connection was cancelled by the user, just quit
131 if (!sock)
132 return;
133 } catch(rdr::Exception& e) {
134 MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
135 return;
136 }
137
138 // Try to add the caller to the MRU
139 MRU::addToMRU(hostAndPort);
140 }
141
142 // Run the RFB protocol over the connected socket
143 conn.initialise(sock, reverse);
144 while (!conn.isClosed()) {
145 try {
146 conn.getInStream()->check(1,1);
147 conn.processMsg();
148 } catch (rdr::EndOfStream) {
149 if (conn.state() == CConnection::RFBSTATE_NORMAL)
150 conn.close();
151 else
152 conn.close("The connection closed unexpectedly");
153 } catch (rfb::AuthCancelledException) {
154 conn.close();
155 } catch (rfb::AuthFailureException& e) {
156 // Clear the password, in case we auto-reconnect
157 options = conn.getOptions();
158 options.password.replaceBuf(0);
159 conn.applyOptions(options);
160 conn.close(e.str());
161 } catch (rdr::Exception& e) {
162 conn.close(e.str());
163 }
164 }
165
166 // If there is a cause for closing the connection logged then display it
167 if (conn.closeReason()) {
168 reconnect = !reverse && conn.getOptions().autoReconnect;
169 if (!reconnect) {
170 MsgBox(0, TStr(conn.closeReason()), MB_ICONINFORMATION | MB_OK);
171 } else {
172 options = conn.getOptions();
173 const char* format = "%s\nDo you wish to attempt to reconnect to %s?";
174 CharArray message(strlen(conn.closeReason()) + strlen(format) +
175 strlen(conn.getOptions().host.buf));
176 sprintf(message.buf, format, conn.closeReason(), conn.getOptions().host.buf);
177 if (MsgBox(0, TStr(message.buf), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES)
178 reconnect = false;
179 }
180 }
181 } // Exit the CConn's scope, implicitly destroying it & making it safe to delete the TcpSocket
182
183 // Clean up the old socket, if any
184 delete sock; sock = 0;
185 } while (reconnect);
186}
187
188
189BOOL CConnThread::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
190 while (!PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) {
191 DWORD result = MsgWaitForMultipleObjects(1, &noMoreThreads.h, FALSE, INFINITE, QS_ALLINPUT);
192 if (result == WAIT_OBJECT_0)
193 return FALSE;
194 else if (result == WAIT_FAILED)
195 throw rdr::SystemException("CConnThread::getMessage wait failed", GetLastError());
196 }
197 return msg->message != WM_QUIT;
198}