File indexing completed on 2024-04-28 15:27:33

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