File indexing completed on 2024-04-21 04:58:42

0001 /*
0002     Copyright (C) 2009-2010 Collabora Ltd <info@collabora.co.uk>
0003       @author George Goldberg <george.goldberg@collabora.co.uk>
0004       @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
0005     Copyright (C) 2007 Alessandro Praduroux <pradu@pradu.it>
0006 
0007     This program is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU General Public
0009     License as published by the Free Software Foundation; either
0010     version 2 of the License, or (at your option) any later version.
0011 
0012     This program is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015     GNU General Public License for more details.
0016 
0017     You should have received a copy of the GNU Lesser General Public License
0018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 #include "rfbserver.h"
0021 #include "rfbservermanager.h"
0022 #include "krfbdebug.h"
0023 #include <QSocketNotifier>
0024 #include <QApplication>
0025 #include <QClipboard>
0026 #include <QPointer>
0027 #include <QtGui/private/qtx11extras_p.h>
0028 
0029 struct RfbServer::Private
0030 {
0031     QByteArray listeningAddress;
0032     int listeningPort;
0033     bool passwordRequired;
0034     rfbScreenInfoPtr screen;
0035     QPointer<QSocketNotifier> ipv4notifier;
0036     QPointer<QSocketNotifier> ipv6notifier;
0037 };
0038 
0039 RfbServer::RfbServer(QObject *parent)
0040     : QObject(parent), d(new Private)
0041 {
0042     d->listeningAddress = "0.0.0.0";
0043     d->listeningPort = 0;
0044     d->passwordRequired = true;
0045     d->screen = nullptr;
0046 
0047     RfbServerManager::instance()->registerServer(this);
0048 }
0049 
0050 RfbServer::~RfbServer()
0051 {
0052     if (d->screen) {
0053         rfbScreenCleanup(d->screen);
0054     }
0055     delete d;
0056 
0057     RfbServerManager::instance()->unregisterServer(this);
0058 }
0059 
0060 QByteArray RfbServer::listeningAddress() const
0061 {
0062     return d->listeningAddress;
0063 }
0064 
0065 int RfbServer::listeningPort() const
0066 {
0067     return d->listeningPort;
0068 }
0069 
0070 bool RfbServer::passwordRequired() const
0071 {
0072     return d->passwordRequired;
0073 }
0074 
0075 void RfbServer::setListeningAddress(const QByteArray& address)
0076 {
0077     d->listeningAddress = address;
0078 }
0079 
0080 void RfbServer::setListeningPort(int port)
0081 {
0082     d->listeningPort = port;
0083 }
0084 
0085 void RfbServer::setPasswordRequired(bool passwordRequired)
0086 {
0087     d->passwordRequired = passwordRequired;
0088 }
0089 
0090 bool RfbServer::start()
0091 {
0092     if (!d->screen) {
0093         d->screen = RfbServerManager::instance()->newScreen();
0094         if (!d->screen) {
0095             qCDebug(KRFB) << "Unable to get rbfserver screen";
0096             return false;
0097         }
0098 
0099         // server hooks
0100         d->screen->screenData = this;
0101         d->screen->newClientHook = newClientHook;
0102         d->screen->kbdAddEvent = keyboardHook;
0103         d->screen->ptrAddEvent = pointerHook;
0104         d->screen->passwordCheck = passwordCheck;
0105         d->screen->setXCutText = clipboardHook;
0106     } else {
0107         //if we already have a screen, stop listening first
0108         rfbShutdownServer(d->screen, false);
0109     }
0110 
0111     if (listeningAddress() != "0.0.0.0") {
0112         strncpy(d->screen->thisHost, listeningAddress().constData(), 254);
0113     }
0114 
0115     if (listeningPort() == 0) {
0116         d->screen->autoPort = 1;
0117     }
0118 
0119     d->screen->port = listeningPort();
0120     d->screen->ipv6port = listeningPort();
0121 
0122     // Disable/Enable password checking
0123     if (passwordRequired()) {
0124         d->screen->authPasswdData = (void *)1;
0125     } else {
0126         d->screen->authPasswdData = (void *)nullptr;
0127     }
0128 
0129     qCDebug(KRFB) << "Starting server. Listen port:" << listeningPort()
0130              << "Listen Address:" << listeningAddress()
0131              << "Password enabled:" << passwordRequired();
0132 
0133     rfbInitServer(d->screen);
0134 
0135     if (!rfbIsActive(d->screen)) {
0136         qCDebug(KRFB) << "Failed to start server";
0137         rfbShutdownServer(d->screen, false);
0138         return false;
0139     };
0140 
0141     d->ipv4notifier = new QSocketNotifier(d->screen->listenSock, QSocketNotifier::Read, this);
0142     connect(d->ipv4notifier, &QSocketNotifier::activated, this, &RfbServer::onListenSocketActivated);
0143     if (d->screen->listen6Sock > 0) {
0144         // we're also listening on additional IPv6 socket, get events from there
0145         d->ipv6notifier = new QSocketNotifier(d->screen->listen6Sock, QSocketNotifier::Read, this);
0146         connect(d->ipv6notifier, &QSocketNotifier::activated, this, &RfbServer::onListenSocketActivated);
0147     }
0148 
0149     if (QX11Info::isPlatformX11()) {
0150         connect(QApplication::clipboard(), &QClipboard::dataChanged,
0151                 this, &RfbServer::krfbSendServerCutText);
0152     }
0153 
0154     return true;
0155 }
0156 
0157 void RfbServer::stop()
0158 {
0159     if (d->screen) {
0160         rfbShutdownServer(d->screen, true);
0161         for (auto notifier : {d->ipv4notifier, d->ipv6notifier}) {
0162             if (notifier) {
0163                 notifier->setEnabled(false);
0164                 notifier->deleteLater();
0165             }
0166         }
0167     }
0168 }
0169 
0170 void RfbServer::updateFrameBuffer(char *fb, int width, int height, int depth)
0171 {
0172     int bpp = depth >> 3;
0173 
0174     if (bpp != 1 && bpp != 2 && bpp != 4) {
0175         bpp = 4;
0176     }
0177 
0178     rfbNewFramebuffer(d->screen, fb, width, height, 8, 3, bpp);
0179 }
0180 
0181 void RfbServer::updateScreen(const QList<QRect> & modifiedTiles)
0182 {
0183     if (d->screen) {
0184         QList<QRect>::const_iterator it = modifiedTiles.constBegin();
0185         for(; it != modifiedTiles.constEnd(); ++it) {
0186             rfbMarkRectAsModified(d->screen, it->x(), it->y(), it->right(), it->bottom());
0187         }
0188     }
0189 }
0190 
0191 /*
0192  * Code copied from vino's bundled libvncserver:
0193  *  Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
0194  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
0195  *  License: GPL v2 or later
0196  */
0197 void krfb_rfbSetCursorPosition(rfbScreenInfoPtr screen, rfbClientPtr client, int x, int y)
0198 {
0199     rfbClientIteratorPtr iterator;
0200     rfbClientPtr cl;
0201 
0202     if (x == screen->cursorX || y == screen->cursorY)
0203         return;
0204 
0205     LOCK(screen->cursorMutex);
0206     screen->cursorX = x;
0207     screen->cursorY = y;
0208     UNLOCK(screen->cursorMutex);
0209 
0210     /* Inform all clients about this cursor movement. */
0211     iterator = rfbGetClientIterator(screen);
0212     while ((cl = rfbClientIteratorNext(iterator)) != nullptr) {
0213         cl->cursorWasMoved = true;
0214     }
0215     rfbReleaseClientIterator(iterator);
0216 
0217     /* The cursor was moved by this client, so don't send CursorPos. */
0218     if (client) {
0219         client->cursorWasMoved = false;
0220     }
0221 }
0222 
0223 void RfbServer::updateCursorPosition(const QPoint & position)
0224 {
0225     if (d->screen) {
0226         krfb_rfbSetCursorPosition(d->screen, nullptr, position.x(), position.y());
0227     }
0228 }
0229 
0230 void RfbServer::krfbSendServerCutText()
0231 {
0232     if(d->screen) {
0233         QString text = QApplication::clipboard()->text();
0234         rfbSendServerCutText(d->screen,
0235                 text.toLocal8Bit().data(),text.length());
0236     }
0237 }
0238 
0239 void RfbServer::onListenSocketActivated()
0240 {
0241     rfbProcessNewConnection(d->screen);
0242 }
0243 
0244 void RfbServer::pendingClientFinished(RfbClient *client)
0245 {
0246     //qDebug();
0247     if (client) {
0248         RfbServerManager::instance()->addClient(client);
0249         client->getRfbClientPtr()->clientGoneHook = clientGoneHook;
0250     }
0251 }
0252 
0253 //static
0254 rfbNewClientAction RfbServer::newClientHook(rfbClientPtr cl)
0255 {
0256     //qDebug() << "New client";
0257     auto server = static_cast<RfbServer*>(cl->screen->screenData);
0258 
0259     PendingRfbClient *pendingClient = server->newClient(cl);
0260     connect(pendingClient, &PendingRfbClient::finished,
0261             server, &RfbServer::pendingClientFinished);
0262 
0263     return RFB_CLIENT_ON_HOLD;
0264 }
0265 
0266 //static
0267 void RfbServer::clientGoneHook(rfbClientPtr cl)
0268 {
0269     //qDebug() << "client gone";
0270     auto client = static_cast<RfbClient*>(cl->clientData);
0271 
0272     RfbServerManager::instance()->removeClient(client);
0273     client->deleteLater();
0274 }
0275 
0276 //static
0277 rfbBool RfbServer::passwordCheck(rfbClientPtr cl, const char *encryptedPassword, int len)
0278 {
0279     auto client = static_cast<PendingRfbClient*>(cl->clientData);
0280     Q_ASSERT(client);
0281     return client->checkPassword(QByteArray::fromRawData(encryptedPassword, len));
0282 }
0283 
0284 //static
0285 void RfbServer::keyboardHook(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
0286 {
0287     auto client = static_cast<RfbClient*>(cl->clientData);
0288     client->handleKeyboardEvent(down ? true : false, keySym);
0289 }
0290 
0291 //static
0292 void RfbServer::pointerHook(int bm, int x, int y, rfbClientPtr cl)
0293 {
0294     auto client = static_cast<RfbClient*>(cl->clientData);
0295     client->handleMouseEvent(bm, x, y);
0296 }
0297 
0298 //static
0299 void RfbServer::clipboardHook(char *str, int len, rfbClientPtr /*cl*/)
0300 {
0301     QApplication::clipboard()->setText(QString::fromLocal8Bit(str,len));
0302 }