File indexing completed on 2024-03-24 05:01:40

0001 /* This file is part of the KDE project
0002    Copyright (C) 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public
0006    License as published by the Free Software Foundation; either
0007    version 3 of the License, or (at your option) any later version.
0008 */
0009 
0010 #include <QApplication>
0011 #include <QCommandLineParser>
0012 #include <QCommandLineOption>
0013 #include <QDebug>
0014 #include <QTimer>
0015 #include <KNotification>
0016 #include <KLocalizedString>
0017 #include <KWindowSystem>
0018 #include <KAboutData>
0019 #include "sockethelpers.h"
0020 #include "krfb_version.h"
0021 #include "rfbserver.h"
0022 #include <signal.h>
0023 #include "rfbservermanager.h"
0024 
0025 class VirtualMonitorRfbClient : public RfbClient
0026 {
0027 public:
0028     explicit VirtualMonitorRfbClient(rfbClientPtr client, QObject *parent = nullptr)
0029         : RfbClient(client, parent)
0030     {}
0031 };
0032 
0033 class PendingVirtualMonitorRfbClient : public PendingRfbClient
0034 {
0035 public:
0036     explicit PendingVirtualMonitorRfbClient(rfbClientPtr client, QObject *parent = nullptr)
0037         : PendingRfbClient(client, parent)
0038     {}
0039     ~PendingVirtualMonitorRfbClient() override {}
0040 
0041     static QByteArray password;
0042 
0043 protected:
0044     void processNewClient() override {
0045         qDebug() << "new client!";
0046         const QString host = peerAddress(m_rfbClient->sock) + QLatin1Char(':') + QString::number(peerPort(m_rfbClient->sock));
0047 
0048         KNotification::event(QStringLiteral("NewConnectionAutoAccepted"),
0049                             i18n("Creating a Virtual Monitor from %1", host));
0050     }
0051     bool checkPassword(const QByteArray & encryptedPassword) override {
0052         bool b = vncAuthCheckPassword(password, encryptedPassword);
0053         if (b) {
0054             QTimer::singleShot(0, this, [this] {
0055                 accept(new VirtualMonitorRfbClient(m_rfbClient, parent()));
0056             });
0057         }
0058         return b;
0059     }
0060 };
0061 
0062 QByteArray PendingVirtualMonitorRfbClient::password;
0063 
0064 class VirtualMonitorRfbServer : public RfbServer
0065 {
0066 public:
0067     PendingRfbClient *newClient(rfbClientPtr client) override {
0068         qDebug() << "new client request";
0069         return new PendingVirtualMonitorRfbClient(client, this);
0070     }
0071 };
0072 
0073 int main(int argc, char *argv[])
0074 {
0075     QApplication app(argc, argv);
0076 
0077     KLocalizedString::setApplicationDomain("krfb");
0078 
0079     KAboutData aboutData(QStringLiteral("krfb-virtualmonitor"),
0080                          i18n("Remote Virtual Monitor"),
0081                          QStringLiteral(KRFB_VERSION_STRING),
0082                          i18n("Offer a Virtual Monitor that can be accessed remotely"),
0083                          KAboutLicense::GPL,
0084                          i18n("(c) 2009-2010, Collabora Ltd.\n"
0085                                "(c) 2007, Alessandro Praduroux\n"
0086                                "(c) 2001-2003, Tim Jansen\n"
0087                                "(c) 2001, Johannes E. Schindelin\n"
0088                                "(c) 2000-2001, Const Kaplinsky\n"
0089                                "(c) 2000, Tridia Corporation\n"
0090                                "(c) 1999, AT&T Laboratories Boston\n"));
0091     aboutData.addAuthor(QStringLiteral("Aleix Pol i Gonzalez"), i18n("Virtual Monitor implementation"), QStringLiteral("aleixpol@kde.org"));
0092     aboutData.addAuthor(i18n("George Kiagiadakis"), QString(), QStringLiteral("george.kiagiadakis@collabora.co.uk"));
0093     aboutData.addAuthor(i18n("Alessandro Praduroux"), i18n("KDE4 porting"), QStringLiteral("pradu@pradu.it"));
0094     aboutData.addAuthor(i18n("Tim Jansen"), i18n("Original author"), QStringLiteral("tim@tjansen.de"));
0095     aboutData.addCredit(i18n("Johannes E. Schindelin"),
0096                         i18n("libvncserver"));
0097     aboutData.addCredit(i18n("Const Kaplinsky"),
0098                         i18n("TightVNC encoder"));
0099     aboutData.addCredit(i18n("Tridia Corporation"),
0100                         i18n("ZLib encoder"));
0101     aboutData.addCredit(i18n("AT&T Laboratories Boston"),
0102                         i18n("original VNC encoders and "
0103                               "protocol design"));
0104     KAboutData::setApplicationData(aboutData);
0105 
0106     QCommandLineParser parser;
0107     aboutData.setupCommandLine(&parser);
0108     const QCommandLineOption resolutionOption({ QStringLiteral("resolution") }, i18n("Logical resolution of the new monitor"), i18n("resolution"));
0109     parser.addOption(resolutionOption);
0110     const QCommandLineOption nameOption({ QStringLiteral("name") }, i18n("Name of the monitor"), i18n("name"));
0111     parser.addOption(nameOption);
0112     const QCommandLineOption passwordOption({ QStringLiteral("password") }, i18n("Password for the client to connect to it"), i18n("password"));
0113     parser.addOption(passwordOption);
0114     const QCommandLineOption scaleOption({ QStringLiteral("scale") }, i18n("The device-pixel-ratio of the device, the scaling factor"), i18n("dpr"), QStringLiteral("1"));
0115     parser.addOption(scaleOption);
0116     const QCommandLineOption portOption({ QStringLiteral("port") }, i18n("The port we will be listening to"), i18n("number"), QStringLiteral("9999"));
0117     parser.addOption(portOption);
0118 
0119     parser.process(app);
0120     aboutData.processCommandLine(&parser);
0121 
0122     app.setQuitOnLastWindowClosed(false);
0123 
0124     if (!KWindowSystem::isPlatformWayland()) {
0125         qCritical() << "Virtual Monitors are only supported on Wayland";
0126         return 1;
0127     }
0128     if (!parser.isSet(nameOption)) {
0129         qCritical() << "error: please define --name";
0130         return 2;
0131     } else {
0132         if (!parser.isSet(passwordOption)) {
0133             qCritical() << "error: please define --password";
0134             return 3;
0135         }
0136         if (!parser.isSet(resolutionOption)) {
0137             qCritical() << "error: please define --resolution";
0138             return 4;
0139         }
0140     }
0141     if (!parser.isSet(portOption)) {
0142         qCritical() << "error: please define --port";
0143         return 5;
0144     }
0145     const QString res = parser.value(resolutionOption);
0146     const auto resSplit = res.split(QLatin1Char('x'));
0147     if (resSplit.size() != 2) {
0148         qCritical() << "error: the resolution should be formatted as WIDTHxHEIGHT (e.g. --resolution 1920x1080)";
0149         return 6;
0150     }
0151 
0152 
0153     if (parser.isSet(nameOption)) {
0154         RfbServerManager::s_pluginArgs = {
0155             { QStringLiteral("name"), parser.value(nameOption) },
0156             { QStringLiteral("resolution"), QSize(resSplit[0].toInt(), resSplit[1].toInt()) },
0157             { QStringLiteral("scale"), parser.value(scaleOption).toDouble() },
0158         };
0159     }
0160 
0161     VirtualMonitorRfbServer server;
0162     server.setPasswordRequired(true);
0163     server.setListeningPort(parser.value(portOption).toInt());
0164     PendingVirtualMonitorRfbClient::password = parser.value(passwordOption).toUtf8();
0165 
0166     sigset_t sigs;
0167     sigemptyset(&sigs);
0168     sigaddset(&sigs, SIGPIPE);
0169     sigprocmask(SIG_BLOCK, &sigs, nullptr);
0170     if (!server.start()) {
0171         return 1;
0172     }
0173 
0174     return app.exec();
0175 }