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

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