File indexing completed on 2024-12-08 03:40:51

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "widgetsuntrustedprogramhandler.h"
0009 
0010 #include <KIconLoader>
0011 #include <KJob>
0012 #include <KJobWidgets>
0013 #include <KLocalizedString>
0014 #include <KStandardGuiItem>
0015 
0016 #include <QApplication>
0017 #include <QDialog>
0018 #include <QDialogButtonBox>
0019 #include <QHBoxLayout>
0020 #include <QLabel>
0021 #include <QPlainTextEdit>
0022 #include <QPushButton>
0023 #include <QScreen>
0024 #include <QStyle>
0025 #include <QVBoxLayout>
0026 
0027 class KIO::WidgetsUntrustedProgramHandlerPrivate
0028 {
0029 public:
0030     QWidget *m_parentWidget = nullptr;
0031 };
0032 
0033 KIO::WidgetsUntrustedProgramHandler::WidgetsUntrustedProgramHandler(QObject *parent)
0034     : KIO::UntrustedProgramHandlerInterface(parent)
0035     , d(std::make_unique<WidgetsUntrustedProgramHandlerPrivate>())
0036 {
0037 }
0038 
0039 KIO::WidgetsUntrustedProgramHandler::~WidgetsUntrustedProgramHandler()
0040 {
0041 }
0042 
0043 // Simple QDialog that resizes the given text edit after being shown to more
0044 // or less fit the enclosed text.
0045 class SecureMessageDialog : public QDialog
0046 {
0047     Q_OBJECT
0048 public:
0049     explicit SecureMessageDialog(QWidget *parent)
0050         : QDialog(parent)
0051         , m_textEdit(nullptr)
0052     {
0053     }
0054 
0055     void setTextEdit(QPlainTextEdit *textEdit)
0056     {
0057         m_textEdit = textEdit;
0058     }
0059 
0060 protected:
0061     void showEvent(QShowEvent *e) override
0062     {
0063         if (e->spontaneous()) {
0064             return;
0065         }
0066 
0067         // Now that we're shown, use our width to calculate a good
0068         // bounding box for the text, and resize m_textEdit appropriately.
0069         QDialog::showEvent(e);
0070 
0071         if (!m_textEdit) {
0072             return;
0073         }
0074 
0075         QSize fudge(20, 24); // About what it sounds like :-/
0076 
0077         // Form rect with a lot of height for bounding.  Use no more than
0078         // 5 lines.
0079         QRect curRect(m_textEdit->rect());
0080         QFontMetrics metrics(fontMetrics());
0081         curRect.setHeight(5 * metrics.lineSpacing());
0082         curRect.setWidth(qMax(curRect.width(), 300)); // At least 300 pixels ok?
0083 
0084         QString text(m_textEdit->toPlainText());
0085         curRect = metrics.boundingRect(curRect, Qt::TextWordWrap | Qt::TextSingleLine, text);
0086 
0087         // Scroll bars interfere.  If we don't think there's enough room, enable
0088         // the vertical scrollbar however.
0089         m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0090         if (curRect.height() < m_textEdit->height()) { // then we've got room
0091             m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0092             m_textEdit->setMaximumHeight(curRect.height() + fudge.height());
0093         }
0094 
0095         m_textEdit->setMinimumSize(curRect.size() + fudge);
0096         m_textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
0097     }
0098 
0099 private:
0100     QPlainTextEdit *m_textEdit;
0101 };
0102 
0103 QDialog *KIO::WidgetsUntrustedProgramHandler::createDialog(QWidget *parentWidget, const QString &programName)
0104 {
0105     SecureMessageDialog *baseDialog = new SecureMessageDialog(parentWidget);
0106     baseDialog->setWindowTitle(i18nc("Warning about executing unknown program", "Warning"));
0107 
0108     QVBoxLayout *topLayout = new QVBoxLayout(baseDialog);
0109 
0110     // Dialog will have explanatory text with a disabled lineedit with the
0111     // Exec= to make it visually distinct.
0112     QWidget *baseWidget = new QWidget(baseDialog);
0113     QHBoxLayout *mainLayout = new QHBoxLayout(baseWidget);
0114 
0115     QLabel *iconLabel = new QLabel(baseWidget);
0116     const QIcon icon = baseDialog->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, baseDialog);
0117     const QPixmap warningIcon(icon.pixmap(KIconLoader::SizeHuge));
0118     mainLayout->addWidget(iconLabel);
0119     iconLabel->setPixmap(warningIcon);
0120 
0121     QVBoxLayout *contentLayout = new QVBoxLayout;
0122     QString warningMessage = i18nc("program name follows in a line edit below", "This will start the program:");
0123 
0124     QLabel *message = new QLabel(warningMessage, baseWidget);
0125     contentLayout->addWidget(message);
0126 
0127     QPlainTextEdit *textEdit = new QPlainTextEdit(baseWidget);
0128     textEdit->setPlainText(programName);
0129     textEdit->setReadOnly(true);
0130     contentLayout->addWidget(textEdit);
0131 
0132     QLabel *footerLabel = new QLabel(i18n("If you do not trust this program, click Cancel"));
0133     contentLayout->addWidget(footerLabel);
0134     contentLayout->addStretch(0); // Don't allow the text edit to expand
0135 
0136     mainLayout->addLayout(contentLayout);
0137 
0138     topLayout->addWidget(baseWidget);
0139     baseDialog->setTextEdit(textEdit);
0140 
0141     QDialogButtonBox *buttonBox = new QDialogButtonBox(baseDialog);
0142     buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0143     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::cont());
0144     buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true);
0145     buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
0146     QObject::connect(buttonBox, &QDialogButtonBox::accepted, baseDialog, &QDialog::accept);
0147     QObject::connect(buttonBox, &QDialogButtonBox::rejected, baseDialog, &QDialog::reject);
0148     topLayout->addWidget(buttonBox);
0149 
0150     // Constrain maximum size.  Minimum size set in
0151     // the dialog's show event.
0152     const QSize screenSize = baseDialog->screen()->size();
0153     baseDialog->resize(screenSize.width() / 4, 50);
0154     baseDialog->setMaximumHeight(screenSize.height() / 3);
0155     baseDialog->setMaximumWidth(screenSize.width() / 10 * 8);
0156 
0157     baseDialog->setAttribute(Qt::WA_DeleteOnClose);
0158     return baseDialog;
0159 }
0160 
0161 void KIO::WidgetsUntrustedProgramHandler::showUntrustedProgramWarning(KJob *job, const QString &programName)
0162 {
0163     QWidget *parentWidget = nullptr;
0164 
0165     if (job) {
0166         parentWidget = KJobWidgets::window(job);
0167     }
0168 
0169     if (!parentWidget) {
0170         parentWidget = d->m_parentWidget;
0171     }
0172 
0173     if (!parentWidget) {
0174         parentWidget = qApp->activeWindow();
0175     }
0176 
0177     QDialog *dialog = createDialog(parentWidget, programName);
0178     connect(dialog, &QDialog::accepted, this, [this]() {
0179         Q_EMIT result(true);
0180     });
0181     connect(dialog, &QDialog::rejected, this, [this]() {
0182         Q_EMIT result(false);
0183     });
0184     dialog->show();
0185 }
0186 
0187 bool KIO::WidgetsUntrustedProgramHandler::execUntrustedProgramWarning(QWidget *window, const QString &programName)
0188 {
0189     QDialog *dialog = createDialog(window, programName);
0190     return dialog->exec() == QDialog::Accepted;
0191 }
0192 
0193 void KIO::WidgetsUntrustedProgramHandler::setWindow(QWidget *window)
0194 {
0195     d->m_parentWidget = window;
0196 }
0197 
0198 #include "moc_widgetsuntrustedprogramhandler.cpp"
0199 #include "widgetsuntrustedprogramhandler.moc"