/* 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. */ // -=- CConnThread.cxx // A CConnThread instance is created for each new connection. // The CConnThread creates the corresponding CConn instance // and manages it. #include #include #include #include #include #include #include #include #include #include using namespace rfb; using namespace win32; static LogWriter vlog("CConnThread"); static std::set threads; static Mutex threadsLock; static Handle noMoreThreads(CreateEvent(0, TRUE, FALSE, 0)); CConnThread::CConnThread() : Thread("CConnThread"), isConfig(false), sock(0), reverse(false) { vlog.info("CConnThread (dialog)"); setDeleteAfterRun(); Lock l(threadsLock); threads.insert(this); start(); } CConnThread::CConnThread(const char* hostOrConfig_, bool isConfig_) : Thread("CConnThread"), hostOrConfig(strDup(hostOrConfig_)), isConfig(isConfig_), sock(0), reverse(false) { vlog.info("CConnThread (host/port)"); setDeleteAfterRun(); Lock l(threadsLock); threads.insert(this); start(); } CConnThread::CConnThread(network::Socket* sock_, bool reverse_) : Thread("CConnThread"), isConfig(false), sock(sock_), reverse(reverse_) { vlog.info("CConnThread (reverse connection)"); setDeleteAfterRun(); Lock l(threadsLock); threads.insert(this); start(); } CConnThread::~CConnThread() { Lock l(threadsLock); threads.erase(this); if (threads.empty()) SetEvent(noMoreThreads); delete sock; } void CConnThread::run() { CConnOptions options; bool reconnect; do { { CConn conn; reconnect = false; // If there is no socket object then set the host & port info if (!sock && !options.host.buf) { try { if (isConfig) { // A configuration file name was specified - load it CharArray filename(hostOrConfig.takeBuf()); options.readFromFile(filename.buf); } else { // An actual hostname (and possibly port) was specified options.host.replaceBuf(hostOrConfig.takeBuf()); } if (!options.host.buf) { // No host was specified - prompt for one ConnectionDialog connDlg(&conn); if (!connDlg.showDialog()) return; options = conn.getOptions(); options.setHost(CStr(connDlg.hostname.buf)); } } catch (rdr::Exception& e) { MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK); return; } } // Apply the connection options to the CConn conn.applyOptions(options); if (!sock) { // There is no existing connection - better make one const char* hostAndPort = conn.getOptions().host.buf; try { ConnectingDialog dlg; sock = dlg.connect(hostAndPort); // If the connection was cancelled by the user, just quit if (!sock) return; } catch(rdr::Exception& e) { MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK); return; } // Try to add the caller to the MRU MRU::addToMRU(hostAndPort); } // Run the RFB protocol over the connected socket conn.initialise(sock, reverse); while (!conn.isClosed()) { try { conn.getInStream()->check(1,1); conn.processMsg(); } catch (rdr::EndOfStream) { if (conn.state() == CConnection::RFBSTATE_NORMAL) conn.close(); else conn.close("The connection closed unexpectedly"); } catch (rfb::AuthCancelledException) { conn.close(); } catch (rfb::AuthFailureException& e) { // Clear the password, in case we auto-reconnect options = conn.getOptions(); options.password.replaceBuf(0); conn.applyOptions(options); conn.close(e.str()); } catch (rdr::Exception& e) { conn.close(e.str()); } } // If there is a cause for closing the connection logged then display it if (conn.closeReason()) { reconnect = !reverse && conn.getOptions().autoReconnect; if (!reconnect) { MsgBox(0, TStr(conn.closeReason()), MB_ICONINFORMATION | MB_OK); } else { options = conn.getOptions(); const char* format = "%s\nDo you wish to attempt to reconnect to %s?"; CharArray message(strlen(conn.closeReason()) + strlen(format) + strlen(conn.getOptions().host.buf)); sprintf(message.buf, format, conn.closeReason(), conn.getOptions().host.buf); if (MsgBox(0, TStr(message.buf), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) reconnect = false; } } } // Exit the CConn's scope, implicitly destroying it & making it safe to delete the TcpSocket // Clean up the old socket, if any delete sock; sock = 0; } while (reconnect); } BOOL CConnThread::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) { while (!PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) { DWORD result = MsgWaitForMultipleObjects(1, &noMoreThreads.h, FALSE, INFINITE, QS_ALLINPUT); if (result == WAIT_OBJECT_0) return FALSE; else if (result == WAIT_FAILED) throw rdr::SystemException("CConnThread::getMessage wait failed", GetLastError()); } return msg->message != WM_QUIT; }