1 /* pinentrydialog.cpp - A (not yet) secure Qt 4 dialog for PIN entry.
2 * Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB)
3 * Copyright 2007 Ingo Klöcker
4 * Copyright 2016 Intevation GmbH
5 *
6 * Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
7 * Modified by Andre Heinecke <aheinecke@intevation.de>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
21 * SPDX-License-Identifier: GPL-2.0+
22 */
23
memory_mapping_list_add_mapping_sorted(MemoryMappingList * list,MemoryMapping * mapping)24 #include "pinentrydialog.h"
25 #include <QGridLayout>
26
27 #include <QProgressBar>
28 #include <QApplication>
29 #include <QFontMetrics>
30 #include <QStyle>
31 #include <QPainter>
32 #include <QPushButton>
33 #include <QDialogButtonBox>
34 #include <QKeyEvent>
35 #include <QLabel>
36 #include <QPalette>
37 #include <QLineEdit>
38 #include <QAction>
39 #include <QCheckBox>
40 #include "pinlineedit.h"
41
42 #include <QDebug>
43
44 #ifdef Q_OS_WIN
45 #include <windows.h>
46 #if QT_VERSION >= 0x050700
47 #include <QtPlatformHeaders/QWindowsWindowFunctions>
48 #endif
49 #endif
50
51 void raiseWindow(QWidget *w)
52 {
53 #ifdef Q_OS_WIN
54 #if QT_VERSION >= 0x050700
55 QWindowsWindowFunctions::setWindowActivationBehavior(
56 QWindowsWindowFunctions::AlwaysActivateWindow);
57 #endif
58 #endif
59 w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
60 w->activateWindow();
61 w->raise();
62 }
63
64 QPixmap icon(QStyle::StandardPixmap which)
65 {
mapping_have_same_region(MemoryMapping * map,hwaddr phys_addr,ram_addr_t length)66 QPixmap pm = qApp->windowIcon().pixmap(48, 48);
67
68 if (which != QStyle::SP_CustomBase) {
69 const QIcon ic = qApp->style()->standardIcon(which);
70 QPainter painter(&pm);
71 const int emblemSize = 22;
72 painter.drawPixmap(pm.width() - emblemSize, 0,
73 ic.pixmap(emblemSize, emblemSize));
74 }
75
76 return pm;
77 }
78
mapping_conflict(MemoryMapping * map,hwaddr phys_addr,hwaddr virt_addr)79 void PinEntryDialog::slotTimeout()
80 {
81 _timed_out = true;
82 reject();
83 }
84
85 PinEntryDialog::PinEntryDialog(QWidget *parent, const char *name,
86 int timeout, bool modal, bool enable_quality_bar,
87 const QString &repeatString,
88 const QString &visibilityTT,
89 const QString &hideTT)
90 : QDialog(parent),
mapping_merge(MemoryMapping * map,hwaddr virt_addr,ram_addr_t length)91 mRepeat(NULL),
92 _grabbed(false),
93 _disable_echo_allowed(true),
94 mVisibilityTT(visibilityTT),
95 mHideTT(hideTT),
96 mVisiActionEdit(NULL),
97 mGenerateActionEdit(NULL),
98 mVisiCB(NULL)
99 {
100 _timed_out = false;
101
102 if (modal) {
103 setWindowModality(Qt::ApplicationModal);
104 }
105
106 _icon = new QLabel(this);
107 _icon->setPixmap(icon());
108
109 _error = new QLabel(this);
110 QPalette pal;
111 pal.setColor(QPalette::WindowText, Qt::red);
112 _error->setPalette(pal);
113 _error->hide();
114
115 _desc = new QLabel(this);
116 _desc->hide();
117
118 _prompt = new QLabel(this);
119 _prompt->hide();
120
121 _edit = new PinLineEdit(this);
122 _edit->setMaxLength(256);
123 _edit->setMinimumWidth(_edit->fontMetrics().averageCharWidth()*20 + 48);
124 _edit->setEchoMode(QLineEdit::Password);
125
126 _prompt->setBuddy(_edit);
127
128 if (enable_quality_bar) {
129 _quality_bar_label = new QLabel(this);
130 _quality_bar_label->setAlignment(Qt::AlignVCenter);
131 _quality_bar = new QProgressBar(this);
132 _quality_bar->setAlignment(Qt::AlignCenter);
133 _have_quality_bar = true;
134 } else {
135 _have_quality_bar = false;
136 }
137
138 QDialogButtonBox *const buttons = new QDialogButtonBox(this);
139 buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
140 _ok = buttons->button(QDialogButtonBox::Ok);
141 _cancel = buttons->button(QDialogButtonBox::Cancel);
142
143 _ok->setDefault(true);
144
145 if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) {
146 _ok->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton));
147 _cancel->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton));
148 }
149
150 if (timeout > 0) {
151 _timer = new QTimer(this);
152 connect(_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
153 _timer->start(timeout * 1000);
154 } else {
155 _timer = NULL;
156 }
157
158 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
159 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
160 connect(_edit, SIGNAL(textChanged(QString)),
161 this, SLOT(updateQuality(QString)));
162 connect(_edit, SIGNAL(textChanged(QString)),
163 this, SLOT(textChanged(QString)));
164 connect(_edit, SIGNAL(backspacePressed()),
165 this, SLOT(onBackspace()));
166
167 QGridLayout *const grid = new QGridLayout(this);
168 int row = 1;
169 grid->addWidget(_error, row++, 1, 1, 2);
170 grid->addWidget(_desc, row++, 1, 1, 2);
171 //grid->addItem( new QSpacerItem( 0, _edit->height() / 10, QSizePolicy::Minimum, QSizePolicy::Fixed ), 1, 1 );
172 grid->addWidget(_prompt, row, 1);
173 grid->addWidget(_edit, row++, 2);
174 if (!repeatString.isNull()) {
175 mRepeat = new QLineEdit;
176 mRepeat->setMaxLength(256);
177 mRepeat->setEchoMode(QLineEdit::Password);
178 connect(mRepeat, SIGNAL(textChanged(QString)),
179 this, SLOT(textChanged(QString)));
180 QLabel *repeatLabel = new QLabel(repeatString);
181 repeatLabel->setBuddy(mRepeat);
182 grid->addWidget(repeatLabel, row, 1);
183 grid->addWidget(mRepeat, row++, 2);
184 setTabOrder(_edit, mRepeat);
185 setTabOrder(mRepeat, _ok);
186 }
187 if (enable_quality_bar) {
188 grid->addWidget(_quality_bar_label, row, 1);
189 grid->addWidget(_quality_bar, row++, 2);
190 }
191 /* Set up the show password action */
192 const QIcon visibilityIcon = QIcon::fromTheme(QLatin1String("visibility"));
193 const QIcon hideIcon = QIcon::fromTheme(QLatin1String("hint"));
194 const QIcon generateIcon = QIcon(); /* Disabled for now
195 QIcon::fromTheme(QLatin1String("password-generate")); */
196 #if QT_VERSION >= 0x050200
197 if (!generateIcon.isNull()) {
198 mGenerateActionEdit = _edit->addAction(generateIcon,
199 QLineEdit::LeadingPosition);
200 mGenerateActionEdit->setToolTip(mGenerateTT);
201 connect(mGenerateActionEdit, SIGNAL(triggered()), this, SLOT(generatePin()));
202 }
203 if (!visibilityIcon.isNull() && !hideIcon.isNull()) {
204 mVisiActionEdit = _edit->addAction(visibilityIcon, QLineEdit::TrailingPosition);
205 mVisiActionEdit->setVisible(false);
206 mVisiActionEdit->setToolTip(mVisibilityTT);
207 connect(mVisiActionEdit, SIGNAL(triggered()), this, SLOT(toggleVisibility()));
208 } else
209 #endif
210 {
211 if (!mVisibilityTT.isNull()) {
212 mVisiCB = new QCheckBox(mVisibilityTT);
213 connect(mVisiCB, SIGNAL(toggled(bool)), this, SLOT(toggleVisibility()));
214 grid->addWidget(mVisiCB, row++, 1, 1, 2, Qt::AlignLeft);
215 }
216 }
217 grid->addWidget(buttons, ++row, 0, 1, 3);
218
219 grid->addWidget(_icon, 0, 0, row - 1, 1, Qt::AlignVCenter | Qt::AlignLeft);
220
221 grid->setSizeConstraint(QLayout::SetFixedSize);
222
223
224 connect(qApp, SIGNAL(focusChanged(QWidget *, QWidget *)),
225 this, SLOT(focusChanged(QWidget *, QWidget *)));
226
227 setWindowState(Qt::WindowMinimized);
228 QTimer::singleShot(0, this, [this] () {
229 raiseWindow (this);
230 });
231 }
232
233 void PinEntryDialog::showEvent(QShowEvent *event)
234 {
235 QDialog::showEvent(event);
236 _edit->setFocus();
237 }
238
239 void PinEntryDialog::setDescription(const QString &txt)
240 {
241 _desc->setVisible(!txt.isEmpty());
242 _desc->setText(txt);
243 #ifndef QT_NO_ACCESSIBILITY
244 _desc->setAccessibleDescription(txt);
245 #endif
246 _icon->setPixmap(icon());
247 setError(QString());
248 }
249
250 QString PinEntryDialog::description() const
251 {
252 return _desc->text();
253 }
254
255 void PinEntryDialog::setError(const QString &txt)
256 {
257 if (!txt.isNull()) {
258 _icon->setPixmap(icon(QStyle::SP_MessageBoxCritical));
259 }
260 _error->setText(txt);
261 #ifndef QT_NO_ACCESSIBILITY
262 _error->setAccessibleDescription(txt);
263 #endif
guest_phys_blocks_append(GuestPhysBlockList * list)264 _error->setVisible(!txt.isEmpty());
265 }
266
267 QString PinEntryDialog::error() const
268 {
269 return _error->text();
270 }
271
272 void PinEntryDialog::setPin(const QString &txt)
273 {
find_paging_enabled_cpu(CPUState * start_cpu)274 _edit->setText(txt);
275 }
276
277 QString PinEntryDialog::pin() const
278 {
279 return _edit->text();
280 }
281
282 void PinEntryDialog::setPrompt(const QString &txt)
283 {
284 _prompt->setText(txt);
285 _prompt->setVisible(!txt.isEmpty());
286 if (txt.contains("PIN"))
qemu_get_guest_memory_mapping(MemoryMappingList * list,const GuestPhysBlockList * guest_phys_blocks,Error ** errp)287 _disable_echo_allowed = false;
288 }
289
290 QString PinEntryDialog::prompt() const
291 {
292 return _prompt->text();
293 }
294
295 void PinEntryDialog::setOkText(const QString &txt)
296 {
297 _ok->setText(txt);
298 #ifndef QT_NO_ACCESSIBILITY
299 _ok->setAccessibleDescription(txt);
300 #endif
301 _ok->setVisible(!txt.isEmpty());
302 }
303
304 void PinEntryDialog::setCancelText(const QString &txt)
305 {
306 _cancel->setText(txt);
307 #ifndef QT_NO_ACCESSIBILITY
308 _cancel->setAccessibleDescription(txt);
309 #endif
310 _cancel->setVisible(!txt.isEmpty());
311 }
312
313 void PinEntryDialog::setQualityBar(const QString &txt)
314 {
315 if (_have_quality_bar) {
316 _quality_bar_label->setText(txt);
317 #ifndef QT_NO_ACCESSIBILITY
318 _quality_bar_label->setAccessibleDescription(txt);
319 #endif
qemu_get_guest_simple_memory_mapping(MemoryMappingList * list,const GuestPhysBlockList * guest_phys_blocks)320 }
321 }
322
323 void PinEntryDialog::setQualityBarTT(const QString &txt)
324 {
325 if (_have_quality_bar) {
326 _quality_bar->setToolTip(txt);
327 }
328 }
329
330 void PinEntryDialog::setGenpinLabel(const QString &txt)
memory_mapping_filter(MemoryMappingList * list,int64_t begin,int64_t length)331 {
332 if (!mGenerateActionEdit) {
333 return;
334 }
335 if (txt.isEmpty()) {
336 mGenerateActionEdit->setVisible(false);
337 } else {
338 mGenerateActionEdit->setText(txt);
339 mGenerateActionEdit->setVisible(true);
340 }
341 }
342
343 void PinEntryDialog::setGenpinTT(const QString &txt)
344 {
345 if (mGenerateActionEdit) {
346 mGenerateActionEdit->setToolTip(txt);
347 }
348 }
349
350 void PinEntryDialog::onBackspace()
351 {
352 if (_disable_echo_allowed) {
353 _edit->setEchoMode(QLineEdit::NoEcho);
354 if (mRepeat) {
355 mRepeat->setEchoMode(QLineEdit::NoEcho);
356 }
357 }
358 }
359
360 void PinEntryDialog::updateQuality(const QString &txt)
361 {
362 int length;
363 int percent;
364 QPalette pal;
365
366 if (_timer) {
367 _timer->stop();
368 }
369
370 _disable_echo_allowed = false;
371
372 if (!_have_quality_bar || !_pinentry_info) {
373 return;
374 }
375 const QByteArray utf8_pin = txt.toUtf8();
376 const char *pin = utf8_pin.constData();
377 length = strlen(pin);
378 percent = length ? pinentry_inq_quality(_pinentry_info, pin, length) : 0;
379 if (!length) {
380 _quality_bar->reset();
381 } else {
382 pal = _quality_bar->palette();
383 if (percent < 0) {
384 pal.setColor(QPalette::Highlight, QColor("red"));
385 percent = -percent;
386 } else {
387 pal.setColor(QPalette::Highlight, QColor("green"));
388 }
389 _quality_bar->setPalette(pal);
390 _quality_bar->setValue(percent);
391 }
392 }
393
394 void PinEntryDialog::setPinentryInfo(pinentry_t peinfo)
395 {
396 _pinentry_info = peinfo;
397 }
398
399 void PinEntryDialog::focusChanged(QWidget *old, QWidget *now)
400 {
401 // Grab keyboard. It might be a little weird to do it here, but it works!
402 // Previously this code was in showEvent, but that did not work in Qt4.
403 if (!_pinentry_info || _pinentry_info->grab) {
404 if (_grabbed && old && (old == _edit || old == mRepeat)) {
405 old->releaseKeyboard();
406 _grabbed = false;
407 }
408 if (!_grabbed && now && (now == _edit || now == mRepeat)) {
409 now->grabKeyboard();
410 _grabbed = true;
411 }
412 }
413
414 }
415
416 void PinEntryDialog::textChanged(const QString &text)
417 {
418 Q_UNUSED(text);
419 if (mRepeat && mRepeat->text() == _edit->text()) {
420 _ok->setEnabled(true);
421 _ok->setToolTip(QString());
422 } else if (mRepeat) {
423 _ok->setEnabled(false);
424 _ok->setToolTip(mRepeatError);
425 }
426
427 if (mVisiActionEdit && sender() == _edit) {
428 mVisiActionEdit->setVisible(!_edit->text().isEmpty());
429 }
430 if (mGenerateActionEdit) {
431 mGenerateActionEdit->setVisible(_edit->text().isEmpty() &&
432 _pinentry_info->genpin_label);
433 }
434 }
435
436 void PinEntryDialog::generatePin()
437 {
438 const char *pin = pinentry_inq_genpin(_pinentry_info);
439 if (pin) {
440 if (_edit->echoMode() == QLineEdit::Password) {
441 toggleVisibility();
442 }
443 const auto pinStr = QString::fromUtf8(pin);
444 _edit->setText(pinStr);
445 mRepeat->setText(pinStr);
446 }
447 }
448
449 void PinEntryDialog::toggleVisibility()
450 {
451 if (sender() != mVisiCB) {
452 if (_edit->echoMode() == QLineEdit::Password) {
453 mVisiActionEdit->setIcon(QIcon::fromTheme(QLatin1String("hint")));
454 mVisiActionEdit->setToolTip(mHideTT);
455 _edit->setEchoMode(QLineEdit::Normal);
456 if (mRepeat) {
457 mRepeat->setEchoMode(QLineEdit::Normal);
458 }
459 } else {
460 mVisiActionEdit->setIcon(QIcon::fromTheme(QLatin1String("visibility")));
461 mVisiActionEdit->setToolTip(mVisibilityTT);
462 _edit->setEchoMode(QLineEdit::Password);
463 if (mRepeat) {
464 mRepeat->setEchoMode(QLineEdit::Password);
465 }
466 }
467 } else {
468 if (mVisiCB->isChecked()) {
469 if (mRepeat) {
470 mRepeat->setEchoMode(QLineEdit::Normal);
471 }
472 _edit->setEchoMode(QLineEdit::Normal);
473 } else {
474 if (mRepeat) {
475 mRepeat->setEchoMode(QLineEdit::Password);
476 }
477 _edit->setEchoMode(QLineEdit::Password);
478 }
479 }
480 }
481
482 QString PinEntryDialog::repeatedPin() const
483 {
484 if (mRepeat) {
485 return mRepeat->text();
486 }
487 return QString();
488 }
489
490 bool PinEntryDialog::timedOut() const
491 {
492 return _timed_out;
493 }
494
495 void PinEntryDialog::setRepeatErrorText(const QString &err)
496 {
497 mRepeatError = err;
498 }
499 #include "pinentrydialog.moc"
500