1 /*
2     Copyright (C) 2009-2010 Collabora Ltd <info@collabora.co.uk>
3       @author George Goldberg <george.goldberg@collabora.co.uk>
4       @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
5     Copyright (C) 2007 Alessandro Praduroux <pradu@pradu.it>
6 
7     This program is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 #include "rfbclient.h"
21 #include "connectiondialog.h"
22 #include "krfbconfig.h"
23 #include "sockethelpers.h"
24 #include "eventsmanager.h"
25 #include <QSocketNotifier>
26 #include <poll.h>
27 #include <strings.h> //for bzero()
28 #include "krfbdebug.h"
29 
30 struct RfbClient::Private
31 {
PrivateRfbClient::Private32     Private(rfbClientPtr client) :
33         controlEnabled(KrfbConfig::allowDesktopControl()),
34         client(client)
35     {}
36 
37     bool controlEnabled;
38     rfbClientPtr client;
39     QSocketNotifier *notifier;
40     QSharedPointer<EventHandler> eventHandler;
41     QString remoteAddressString;
42 };
43 
RfbClient(rfbClientPtr client,QObject * parent)44 RfbClient::RfbClient(rfbClientPtr client, QObject* parent)
45     : QObject(parent), d(new Private(client))
46 {
47     d->remoteAddressString = peerAddress(d->client->sock) + QLatin1Char(':') +
48                              QString::number(peerPort(d->client->sock));
49 
50     d->notifier = new QSocketNotifier(client->sock, QSocketNotifier::Read, this);
51     d->notifier->setEnabled(false);
52     connect(d->notifier, &QSocketNotifier::activated, this, &RfbClient::onSocketActivated);
53 
54     d->eventHandler = EventsManager::instance()->eventHandler();
55 }
56 
~RfbClient()57 RfbClient::~RfbClient()
58 {
59     //qDebug();
60     delete d;
61 }
62 
name() const63 QString RfbClient::name() const
64 {
65     return d->remoteAddressString;
66 }
67 
68 //static
controlCanBeEnabled()69 bool RfbClient::controlCanBeEnabled()
70 {
71     return KrfbConfig::allowDesktopControl();
72 }
73 
controlEnabled() const74 bool RfbClient::controlEnabled() const
75 {
76     return d->controlEnabled;
77 }
78 
setControlEnabled(bool enabled)79 void RfbClient::setControlEnabled(bool enabled)
80 {
81     if (controlCanBeEnabled() && d->controlEnabled != enabled) {
82         d->controlEnabled = enabled;
83         Q_EMIT controlEnabledChanged(enabled);
84     }
85 }
86 
isOnHold() const87 bool RfbClient::isOnHold() const
88 {
89     return d->client->onHold ? true : false;
90 }
91 
setOnHold(bool onHold)92 void RfbClient::setOnHold(bool onHold)
93 {
94     if (isOnHold() != onHold) {
95         d->client->onHold = onHold;
96         d->notifier->setEnabled(!onHold);
97         Q_EMIT holdStatusChanged(onHold);
98     }
99 }
100 
closeConnection()101 void RfbClient::closeConnection()
102 {
103     d->notifier->setEnabled(false);
104     rfbCloseClient(d->client);
105     rfbClientConnectionGone(d->client);
106 }
107 
getRfbClientPtr()108 rfbClientPtr RfbClient::getRfbClientPtr()
109 {
110     return d->client;
111 }
112 
handleKeyboardEvent(bool down,rfbKeySym keySym)113 void RfbClient::handleKeyboardEvent(bool down, rfbKeySym keySym)
114 {
115     if (d->controlEnabled) {
116         d->eventHandler->handleKeyboard(down, keySym);
117     }
118 }
119 
handleMouseEvent(int buttonMask,int x,int y)120 void RfbClient::handleMouseEvent(int buttonMask, int x, int y)
121 {
122     if (d->controlEnabled) {
123         d->eventHandler->handlePointer(buttonMask, x, y);
124     }
125 }
126 
onSocketActivated()127 void RfbClient::onSocketActivated()
128 {
129     //Process not only one, but all pending messages.
130     //poll() idea/code copied from vino:
131     // Copyright (C) 2003 Sun Microsystems, Inc.
132     // License: GPL v2 or later
133     struct pollfd pollfd = { d->client->sock, POLLIN|POLLPRI, 0 };
134 
135     while(poll(&pollfd, 1, 0) == 1) {
136         rfbProcessClientMessage(d->client);
137 
138         //This is how we handle disconnection.
139         //if rfbProcessClientMessage() finds out that it can't read the socket,
140         //it closes it and sets it to -1. So, we just have to check this here
141         //and call rfbClientConnectionGone() if necessary. This will call
142         //the clientGoneHook which in turn will remove this RfbClient instance
143         //from the server manager and will call deleteLater() to delete it
144         if (d->client->sock == -1) {
145             //qDebug() << "disconnected from socket signal";
146             d->notifier->setEnabled(false);
147             rfbClientConnectionGone(d->client);
148             break;
149         }
150     }
151 }
152 
update()153 void RfbClient::update()
154 {
155     rfbUpdateClient(d->client);
156 
157     //This is how we handle disconnection.
158     //if rfbUpdateClient() finds out that it can't write to the socket,
159     //it closes it and sets it to -1. So, we just have to check this here
160     //and call rfbClientConnectionGone() if necessary. This will call
161     //the clientGoneHook which in turn will remove this RfbClient instance
162     //from the server manager and will call deleteLater() to delete it
163     if (d->client->sock == -1) {
164         //qDebug() << "disconnected during update";
165         d->notifier->setEnabled(false);
166         rfbClientConnectionGone(d->client);
167     }
168 }
169 
170 //*************
171 
PendingRfbClient(rfbClientPtr client,QObject * parent)172 PendingRfbClient::PendingRfbClient(rfbClientPtr client, QObject *parent)
173     : QObject(parent), m_rfbClient(client)
174     , m_notifier(new QSocketNotifier(client->sock, QSocketNotifier::Read, this))
175 {
176     m_rfbClient->clientData = this;
177 
178     m_notifier->setEnabled(true);
179     connect(m_notifier, &QSocketNotifier::activated,
180             this, &PendingRfbClient::onSocketActivated);
181 }
182 
~PendingRfbClient()183 PendingRfbClient::~PendingRfbClient()
184 {}
185 
accept(RfbClient * newClient)186 void PendingRfbClient::accept(RfbClient *newClient)
187 {
188     //qDebug() << "accepted connection";
189 
190     m_rfbClient->clientData = newClient;
191     newClient->setOnHold(false);
192 
193     Q_EMIT finished(newClient);
194     deleteLater();
195 }
196 
clientGoneHookNoop(rfbClientPtr cl)197 static void clientGoneHookNoop(rfbClientPtr cl) { Q_UNUSED(cl); }
198 
reject()199 void PendingRfbClient::reject()
200 {
201     //qDebug() << "refused connection";
202 
203     //override the clientGoneHook that was previously set by RfbServer
204     m_rfbClient->clientGoneHook = clientGoneHookNoop;
205     rfbCloseClient(m_rfbClient);
206     rfbClientConnectionGone(m_rfbClient);
207 
208     Q_EMIT finished(nullptr);
209     deleteLater();
210 }
211 
checkPassword(const QByteArray & encryptedPassword)212 bool PendingRfbClient::checkPassword(const QByteArray & encryptedPassword)
213 {
214     Q_UNUSED(encryptedPassword);
215 
216     return m_rfbClient->screen->authPasswdData == (void*)nullptr;
217 }
218 
vncAuthCheckPassword(const QByteArray & password,const QByteArray & encryptedPassword) const219 bool PendingRfbClient::vncAuthCheckPassword(const QByteArray& password, const QByteArray& encryptedPassword) const
220 {
221     if (password.isEmpty() && encryptedPassword.isEmpty()) {
222         return true;
223     }
224 
225     char passwd[MAXPWLEN+1]; // +1 to make sure there's a nullptr at the end
226     unsigned char challenge[CHALLENGESIZE];
227 
228     memcpy(challenge, m_rfbClient->authChallenge, CHALLENGESIZE);
229     memset(passwd, 0, sizeof(passwd));
230 
231     if (!password.isEmpty()) {
232         strncpy(passwd, password.constData(),
233                 (MAXPWLEN <= password.size()) ? MAXPWLEN : password.size());
234     }
235 
236     rfbEncryptBytes(challenge, passwd);
237     return memcmp(challenge, encryptedPassword.constData(), encryptedPassword.size()) == 0;
238 }
239 
onSocketActivated()240 void PendingRfbClient::onSocketActivated()
241 {
242     //Process not only one, but all pending messages.
243     //poll() idea/code copied from vino:
244     // Copyright (C) 2003 Sun Microsystems, Inc.
245     // License: GPL v2 or later
246     struct pollfd pollfd = { m_rfbClient->sock, POLLIN|POLLPRI, 0 };
247 
248     while(poll(&pollfd, 1, 0) == 1) {
249         if(m_rfbClient->state == rfbClientRec::RFB_INITIALISATION) {
250             m_notifier->setEnabled(false);
251             //Client is Authenticated
252             processNewClient();
253             break;
254         }
255         rfbProcessClientMessage(m_rfbClient);
256 
257         //This is how we handle disconnection.
258         //if rfbProcessClientMessage() finds out that it can't read the socket,
259         //it closes it and sets it to -1. So, we just have to check this here
260         //and call rfbClientConnectionGone() if necessary. This will call
261         //the clientGoneHook which in turn will remove this RfbClient instance
262         //from the server manager and will call deleteLater() to delete it
263         if (m_rfbClient->sock == -1) {
264             qCDebug(KRFB) << "disconnected from socket signal";
265             m_notifier->setEnabled(false);
266             rfbClientConnectionGone(m_rfbClient);
267             break;
268         }
269     }
270 }
271