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