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

0001 /* This file is part of the KDE project
0002    Copyright (C) 2007 Alessandro Praduroux <pradu@pradu.it>
0003    Copyright (C) 2013 Amandeep Singh <aman.dedman@gmail.com>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 */
0010 #include "mainwindow.h"
0011 #include "invitationsrfbserver.h"
0012 
0013 #include "krfbconfig.h"
0014 #include "ui_configtcp.h"
0015 #include "ui_configsecurity.h"
0016 #include "ui_configframebuffer.h"
0017 
0018 #include <KConfigDialog>
0019 #include <KLocalizedString>
0020 #include <KMessageBox>
0021 #include <KMessageWidget>
0022 #include <KStandardAction>
0023 #include <KActionCollection>
0024 #include <KNewPasswordDialog>
0025 #include <KPluginMetaData>
0026 
0027 #include <QIcon>
0028 #include <QWidget>
0029 #include <QLineEdit>
0030 #include <QComboBox>
0031 #include <QSizePolicy>
0032 #include <QVector>
0033 #include <QSet>
0034 #include <QNetworkInterface>
0035 #include <QHostInfo>
0036 #include <QMessageBox>
0037 
0038 class TCP: public QWidget, public Ui::TCP
0039 {
0040 public:
0041     explicit TCP(QWidget *parent = nullptr) : QWidget(parent) {
0042         setupUi(this);
0043     }
0044 };
0045 
0046 class Security: public QWidget, public Ui::Security
0047 {
0048 public:
0049     explicit Security(QWidget *parent = nullptr) : QWidget(parent) {
0050         setupUi(this);
0051         walletWarning = new KMessageWidget(this);
0052         walletWarning->setText(i18n("Storing passwords in config file is insecure!"));
0053         walletWarning->setCloseButtonVisible(false);
0054         walletWarning->setMessageType(KMessageWidget::Warning);
0055         walletWarning->hide();
0056         vboxLayout->addWidget(walletWarning);
0057 
0058         // show warning when "noWallet" checkbox is checked
0059         QObject::connect(kcfg_noWallet, &QCheckBox::toggled, this, [this] (bool checked) {
0060             walletWarning->setVisible(checked);
0061         });
0062     }
0063 
0064     KMessageWidget *walletWarning = nullptr;
0065 };
0066 
0067 class ConfigFramebuffer: public QWidget, public Ui::Framebuffer
0068 {
0069 public:
0070     ConfigFramebuffer(QWidget *parent = nullptr) : QWidget(parent) {
0071         setupUi(this);
0072         // hide the line edit with framebuffer string
0073         kcfg_preferredFrameBufferPlugin->hide();
0074         // fill drop-down combo with a list of real existing plugins
0075         this->fillFrameBuffersCombo();
0076         // initialize combo with currently configured framebuffer plugin
0077         cb_preferredFrameBufferPlugin->setCurrentText(KrfbConfig::preferredFrameBufferPlugin());
0078         // connect signals between combo<->lineedit
0079         // if we change selection in combo, lineedit is updated
0080         QObject::connect(cb_preferredFrameBufferPlugin, &QComboBox::currentTextChanged,
0081                          kcfg_preferredFrameBufferPlugin, &QLineEdit::setText);
0082     }
0083 
0084     void fillFrameBuffersCombo() {
0085         const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("krfb/framebuffer"), {}, KPluginMetaData::AllowEmptyMetaData);
0086         for (const KPluginMetaData &metadata : plugins) {
0087             cb_preferredFrameBufferPlugin->addItem(metadata.pluginId());
0088         }
0089     }
0090 };
0091 
0092 
0093 MainWindow::MainWindow(QWidget *parent)
0094     : KXmlGuiWindow(parent)
0095 {
0096     setAttribute(Qt::WA_DeleteOnClose, false);
0097 
0098     m_passwordEditable = false;
0099     m_passwordLineEdit = new QLineEdit(this);
0100     m_passwordLineEdit->setVisible(false);
0101     m_passwordLineEdit->setAlignment(Qt::AlignHCenter);
0102 
0103     auto mainWidget = new QWidget;
0104     m_ui.setupUi(mainWidget);
0105     m_ui.krfbIconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("krfb")).pixmap(128));
0106     m_ui.enableUnattendedCheckBox->setChecked(
0107             InvitationsRfbServer::instance->allowUnattendedAccess());
0108 
0109     setCentralWidget(mainWidget);
0110 
0111     connect(m_ui.passwordEditButton, &QToolButton::clicked,
0112             this, &MainWindow::editPassword);
0113     connect(m_ui.enableSharingCheckBox, &QCheckBox::toggled,
0114             this, &MainWindow::toggleDesktopSharing);
0115     connect(m_ui.enableUnattendedCheckBox, &QCheckBox::toggled,
0116             InvitationsRfbServer::instance, &InvitationsRfbServer::toggleUnattendedAccess);
0117     connect(m_ui.unattendedPasswordButton, &QPushButton::clicked,
0118             this, &MainWindow::editUnattendedPassword);
0119     connect(m_ui.addressAboutButton, &QToolButton::clicked,
0120             this, &MainWindow::aboutConnectionAddress);
0121     connect(m_ui.unattendedAboutButton, &QToolButton::clicked,
0122             this, &MainWindow::aboutUnattendedMode);
0123     connect(InvitationsRfbServer::instance, &InvitationsRfbServer::passwordChanged,
0124             this, &MainWindow::passwordChanged);
0125 
0126     // Figure out the address
0127     int port = KrfbConfig::port();
0128     const QList<QNetworkInterface> interfaceList = QNetworkInterface::allInterfaces();
0129     for (const QNetworkInterface& interface : interfaceList) {
0130         if(interface.flags() & QNetworkInterface::IsLoopBack)
0131             continue;
0132 
0133         if(interface.flags() & QNetworkInterface::IsRunning &&
0134                 !interface.addressEntries().isEmpty()) {
0135             const QString hostName = QHostInfo::localHostName();
0136             const QString ipAddress = interface.addressEntries().constFirst().ip().toString();
0137             const QString addressLabelText = hostName.isEmpty()
0138                 ? QStringLiteral("%1 : %2").arg(ipAddress).arg(port)
0139                 : QStringLiteral("%1 (%2) : %3").arg(hostName, ipAddress).arg(port);
0140             m_ui.addressDisplayLabel->setText(addressLabelText);
0141         }
0142     }
0143 
0144     //Figure out the password
0145     m_ui.passwordDisplayLabel->setText(
0146             InvitationsRfbServer::instance->desktopPassword());
0147 
0148     KStandardAction::quit(QCoreApplication::instance(), SLOT(quit()), actionCollection());
0149     KStandardAction::preferences(this, SLOT(showConfiguration()), actionCollection());
0150 
0151     setupGUI();
0152 
0153     if (KrfbConfig::allowDesktopControl()) {
0154       m_ui.enableSharingCheckBox->setChecked(true);
0155     }
0156 
0157     setAutoSaveSettings();
0158 }
0159 
0160 MainWindow::~MainWindow()
0161 {
0162 }
0163 
0164 void MainWindow::editPassword()
0165 {
0166     if(m_passwordEditable) {
0167         m_passwordEditable = false;
0168         m_ui.passwordEditButton->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
0169         m_ui.passwordGridLayout->removeWidget(m_passwordLineEdit);
0170         InvitationsRfbServer::instance->setDesktopPassword(
0171                 m_passwordLineEdit->text());
0172         m_ui.passwordDisplayLabel->setText(
0173                 InvitationsRfbServer::instance->desktopPassword());
0174         m_passwordLineEdit->setVisible(false);
0175     } else {
0176         m_passwordEditable = true;
0177         m_ui.passwordEditButton->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
0178         m_ui.passwordGridLayout->addWidget(m_passwordLineEdit,0,0);
0179         m_passwordLineEdit->setText(
0180                 InvitationsRfbServer::instance->desktopPassword());
0181         m_passwordLineEdit->setVisible(true);
0182         m_passwordLineEdit->setFocus(Qt::MouseFocusReason);
0183     }
0184 }
0185 
0186 void MainWindow::editUnattendedPassword()
0187 {
0188     KNewPasswordDialog dialog(this);
0189     dialog.setPrompt(i18n("Enter a new password for Unattended Access"));
0190     if(dialog.exec()) {
0191         InvitationsRfbServer::instance->setUnattendedPassword(dialog.password());
0192     }
0193 }
0194 
0195 void MainWindow::toggleDesktopSharing(bool enable)
0196 {
0197     if(enable) {
0198         if(!InvitationsRfbServer::instance->start()) {
0199             KMessageBox::error(this,
0200                     i18n("Failed to start the krfb server. Desktop sharing "
0201                         "will not work. Try setting another port in the settings "
0202                         "and restart krfb."));
0203         }
0204     } else {
0205         InvitationsRfbServer::instance->stop();
0206         if(m_passwordEditable) {
0207             m_passwordEditable = false;
0208             m_passwordLineEdit->setVisible(false);
0209             m_ui.passwordEditButton->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
0210         }
0211     }
0212 }
0213 
0214 void MainWindow::passwordChanged(const QString& password)
0215 {
0216     m_passwordLineEdit->setText(password);
0217     m_ui.passwordDisplayLabel->setText(password);
0218 }
0219 
0220 void MainWindow::aboutConnectionAddress()
0221 {
0222     QMessageBox::about(this,
0223             i18n("KDE Desktop Sharing"),
0224             i18n("This field contains the address of your computer and the port number, separated by a colon.\n\nThe address is just a hint - you can use any address that can reach your computer.\n\nDesktop Sharing tries to guess your address from your network configuration, but does not always succeed in doing so.\n\nIf your computer is behind a firewall it may have a different address or be unreachable for other computers."));
0225 }
0226 
0227 void MainWindow::aboutUnattendedMode()
0228 {
0229     QMessageBox::about(this,
0230             i18n("KDE Desktop Sharing"),
0231             i18n("Any remote user with normal desktop sharing password will have to be authenticated.\n\nIf unattended access is on, and the remote user provides unattended mode password, desktop sharing access will be granted without explicit confirmation."));
0232 }
0233 
0234 void MainWindow::showConfiguration()
0235 {
0236     static QString s_prevFramebufferPlugin;
0237     static bool s_prevNoWallet;
0238     // ^^ needs to be static, because lambda will be called long time
0239     //    after showConfiguration() ends, so auto variable would go out of scope
0240     // save previously selected framebuffer plugin config
0241     s_prevFramebufferPlugin = KrfbConfig::preferredFrameBufferPlugin();
0242     s_prevNoWallet = KrfbConfig::noWallet();
0243 
0244     if (KConfigDialog::showDialog(QStringLiteral("settings"))) {
0245         return;
0246     }
0247 
0248     auto dialog = new KConfigDialog(this, QStringLiteral("settings"), KrfbConfig::self());
0249     dialog->addPage(new TCP, i18n("Network"), QStringLiteral("network-wired"));
0250     dialog->addPage(new Security, i18n("Security"), QStringLiteral("security-high"));
0251     dialog->addPage(new ConfigFramebuffer, i18n("Screen capture"), QStringLiteral("video-display"));
0252     dialog->show();
0253     connect(dialog, &KConfigDialog::settingsChanged, this, [this] () {
0254         // check if framebuffer plugin config has changed
0255         if (s_prevFramebufferPlugin != KrfbConfig::preferredFrameBufferPlugin()) {
0256             KMessageBox::information(this, i18n("To apply framebuffer plugin setting, "
0257                                                 "you need to restart the program."));
0258         }
0259         // check if kwallet config has changed
0260         if (s_prevNoWallet != KrfbConfig::noWallet()) {
0261             // try to apply settings immediately
0262             if (KrfbConfig::noWallet()) {
0263                 InvitationsRfbServer::instance->closeKWallet();
0264             } else {
0265                 InvitationsRfbServer::instance->openKWallet();
0266                 // erase stored passwords from krfbconfig file
0267                 KConfigGroup securityConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Security"));
0268                 securityConfigGroup.deleteEntry("desktopPassword");
0269                 securityConfigGroup.deleteEntry("unattendedPassword");
0270             }
0271         }
0272     });
0273 }
0274 
0275 void MainWindow::readProperties(const KConfigGroup& group)
0276 {
0277     if (group.readEntry("Visible", true)) {
0278         show();
0279     }
0280     KMainWindow::readProperties(group);
0281 }
0282 
0283 void MainWindow::saveProperties(KConfigGroup& group)
0284 {
0285     group.writeEntry("Visible", isVisible());
0286     KMainWindow::saveProperties(group);
0287 }