1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "widgetsuntrustedprogramhandler.h"
9
10#include <KIconLoader>
11#include <KJob>
12#include <KJobWidgets>
13#include <KLocalizedString>
14#include <KMessageDialog>
15#include <KStandardGuiItem>
16
17#include <QApplication>
18#include <QDialog>
19#include <QDialogButtonBox>
20#include <QHBoxLayout>
21#include <QLabel>
22#include <QPlainTextEdit>
23#include <QPushButton>
24#include <QScreen>
25#include <QStyle>
26#include <QVBoxLayout>
27
28class KIO::WidgetsUntrustedProgramHandlerPrivate
29{
30public:
31 QWidget *m_parentWidget = nullptr;
32};
33
34KIO::WidgetsUntrustedProgramHandler::WidgetsUntrustedProgramHandler(QObject *parent)
35 : KIO::UntrustedProgramHandlerInterface(parent)
36 , d(std::make_unique<WidgetsUntrustedProgramHandlerPrivate>())
37{
38}
39
40KIO::WidgetsUntrustedProgramHandler::~WidgetsUntrustedProgramHandler()
41{
42}
43
44// Simple QDialog that resizes the given text edit after being shown to more
45// or less fit the enclosed text.
46class SecureMessageDialog : public QDialog
47{
48 Q_OBJECT
49public:
50 explicit SecureMessageDialog(QWidget *parent)
51 : QDialog(parent)
52 , m_textEdit(nullptr)
53 {
54 }
55
56 void setTextEdit(QPlainTextEdit *textEdit)
57 {
58 m_textEdit = textEdit;
59 }
60
61protected:
62 void showEvent(QShowEvent *e) override
63 {
64 if (e->spontaneous()) {
65 return;
66 }
67
68 // Now that we're shown, use our width to calculate a good
69 // bounding box for the text, and resize m_textEdit appropriately.
70 QDialog::showEvent(e);
71
72 if (!m_textEdit) {
73 return;
74 }
75
76 QSize fudge(20, 24); // About what it sounds like :-/
77
78 // Form rect with a lot of height for bounding. Use no more than
79 // 5 lines.
80 QRect curRect(m_textEdit->rect());
81 QFontMetrics metrics(fontMetrics());
82 curRect.setHeight(5 * metrics.lineSpacing());
83 curRect.setWidth(qMax(a: curRect.width(), b: 300)); // At least 300 pixels ok?
84
85 QString text(m_textEdit->toPlainText());
86 curRect = metrics.boundingRect(r: curRect, flags: Qt::TextWordWrap | Qt::TextSingleLine, text);
87
88 // Scroll bars interfere. If we don't think there's enough room, enable
89 // the vertical scrollbar however.
90 m_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
91 if (curRect.height() < m_textEdit->height()) { // then we've got room
92 m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
93 m_textEdit->setMaximumHeight(curRect.height() + fudge.height());
94 }
95
96 m_textEdit->setMinimumSize(curRect.size() + fudge);
97 m_textEdit->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Minimum);
98
99 // Mimicks a message box
100 KMessageDialog::beep(type: KMessageDialog::WarningContinueCancel, text, dialog: this);
101 }
102
103private:
104 QPlainTextEdit *m_textEdit;
105};
106
107QDialog *KIO::WidgetsUntrustedProgramHandler::createDialog(QWidget *parentWidget, const QString &programName)
108{
109 SecureMessageDialog *baseDialog = new SecureMessageDialog(parentWidget);
110 baseDialog->setWindowTitle(i18nc("Warning about executing unknown program", "Warning"));
111
112 QVBoxLayout *topLayout = new QVBoxLayout(baseDialog);
113
114 // Dialog will have explanatory text with a disabled lineedit with the
115 // Exec= to make it visually distinct.
116 QWidget *baseWidget = new QWidget(baseDialog);
117 QHBoxLayout *mainLayout = new QHBoxLayout(baseWidget);
118
119 QLabel *iconLabel = new QLabel(baseWidget);
120 const QIcon icon = baseDialog->style()->standardIcon(standardIcon: QStyle::SP_MessageBoxWarning, option: nullptr, widget: baseDialog);
121 const QPixmap warningIcon(icon.pixmap(extent: KIconLoader::SizeHuge));
122 mainLayout->addWidget(iconLabel);
123 iconLabel->setPixmap(warningIcon);
124
125 QVBoxLayout *contentLayout = new QVBoxLayout;
126 QString warningMessage = i18nc("program name follows in a line edit below", "This will start the program:");
127
128 QLabel *message = new QLabel(warningMessage, baseWidget);
129 contentLayout->addWidget(message);
130
131 QPlainTextEdit *textEdit = new QPlainTextEdit(baseWidget);
132 textEdit->setPlainText(programName);
133 textEdit->setReadOnly(true);
134 contentLayout->addWidget(textEdit);
135
136 QLabel *footerLabel = new QLabel(i18n("If you do not trust this program, click Cancel"));
137 contentLayout->addWidget(footerLabel);
138 contentLayout->addStretch(stretch: 0); // Don't allow the text edit to expand
139
140 mainLayout->addLayout(layout: contentLayout);
141
142 topLayout->addWidget(baseWidget);
143 baseDialog->setTextEdit(textEdit);
144
145 QDialogButtonBox *buttonBox = new QDialogButtonBox(baseDialog);
146 buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
147 KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::Ok), item: KStandardGuiItem::cont());
148 buttonBox->button(which: QDialogButtonBox::Cancel)->setDefault(true);
149 buttonBox->button(which: QDialogButtonBox::Cancel)->setFocus();
150 QObject::connect(sender: buttonBox, signal: &QDialogButtonBox::accepted, context: baseDialog, slot: &QDialog::accept);
151 QObject::connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: baseDialog, slot: &QDialog::reject);
152 topLayout->addWidget(buttonBox);
153
154 // Constrain maximum size. Minimum size set in
155 // the dialog's show event.
156 const QSize screenSize = baseDialog->screen()->size();
157 baseDialog->resize(w: screenSize.width() / 4, h: 50);
158 baseDialog->setMaximumHeight(screenSize.height() / 3);
159 baseDialog->setMaximumWidth(screenSize.width() / 10 * 8);
160
161 baseDialog->setAttribute(Qt::WA_DeleteOnClose);
162 return baseDialog;
163}
164
165void KIO::WidgetsUntrustedProgramHandler::showUntrustedProgramWarning(KJob *job, const QString &programName)
166{
167 QWidget *parentWidget = nullptr;
168
169 if (job) {
170 parentWidget = KJobWidgets::window(job);
171 }
172
173 if (!parentWidget) {
174 parentWidget = d->m_parentWidget;
175 }
176
177 if (!parentWidget) {
178 parentWidget = qApp->activeWindow();
179 }
180
181 QDialog *dialog = createDialog(parentWidget, programName);
182 connect(sender: dialog, signal: &QDialog::accepted, context: this, slot: [this]() {
183 Q_EMIT result(confirmed: true);
184 });
185 connect(sender: dialog, signal: &QDialog::rejected, context: this, slot: [this]() {
186 Q_EMIT result(confirmed: false);
187 });
188 dialog->show();
189}
190
191void KIO::WidgetsUntrustedProgramHandler::setWindow(QWidget *window)
192{
193 d->m_parentWidget = window;
194}
195
196#include "moc_widgetsuntrustedprogramhandler.cpp"
197#include "widgetsuntrustedprogramhandler.moc"
198

source code of kio/src/widgets/widgetsuntrustedprogramhandler.cpp