VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIAddDiskEncryptionPasswordDialog.cpp@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.6 KB
Line 
1/* $Id: UIAddDiskEncryptionPasswordDialog.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIAddDiskEncryptionPasswordDialog class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef VBOX_WITH_PRECOMPILED_HEADERS
19# include <precomp.h>
20#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
21
22/* Qt includes: */
23# include <QAbstractTableModel>
24# include <QHeaderView>
25# include <QItemEditorFactory>
26# include <QLabel>
27# include <QLineEdit>
28# include <QPushButton>
29# include <QStandardItemEditorCreator>
30# include <QTableView>
31# include <QVBoxLayout>
32
33/* GUI includes: */
34# include "QIDialogButtonBox.h"
35# include "QIStyledItemDelegate.h"
36# include "QIWithRetranslateUI.h"
37# include "VBoxGlobal.h"
38# include "UIAddDiskEncryptionPasswordDialog.h"
39# include "UIIconPool.h"
40# include "UIMedium.h"
41# include "UIMessageCenter.h"
42
43/* Other VBox includes: */
44# include <iprt/assert.h>
45
46#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
47
48
49/** UIEncryptionDataTable field indexes. */
50enum UIEncryptionDataTableSection
51{
52 UIEncryptionDataTableSection_Id,
53 UIEncryptionDataTableSection_Password,
54 UIEncryptionDataTableSection_Max
55};
56
57template<class T>
58static QStringList toStringList(const QList<T> &list)
59{
60 QStringList l;
61 foreach(const T &t, list)
62 l << t.toString();
63 return l;
64}
65
66/** QLineEdit extension used as
67 * the embedded password editor for the UIEncryptionDataTable. */
68class UIPasswordEditor : public QLineEdit
69{
70 Q_OBJECT;
71
72 /** Holds the current password of the editor. */
73 Q_PROPERTY(QString password READ password WRITE setPassword USER true);
74
75signals:
76
77 /** Notifies listeners about data should be committed. */
78 void sigCommitData(QWidget *pThis);
79
80 /** Notifies listeners about Enter/Return key triggering. */
81 void sigEnterKeyTriggered();
82
83public:
84
85 /** Constructs password editor passing @a pParent to the base-class. */
86 UIPasswordEditor(QWidget *pParent);
87
88protected:
89
90 /** Handles key-press @a pEvent. */
91 virtual void keyPressEvent(QKeyEvent *pEvent) /* override */;
92
93private slots:
94
95 /** Commits data to the listeners. */
96 void sltCommitData() { emit sigCommitData(this); }
97
98private:
99
100 /** Prepares all. */
101 void prepare();
102
103 /** Property: Returns the current password of the editor. */
104 QString password() const { return QLineEdit::text(); }
105 /** Property: Defines the current @a strPassword of the editor. */
106 void setPassword(const QString &strPassword) { QLineEdit::setText(strPassword); }
107};
108
109
110/** QAbstractTableModel extension used as
111 * the data representation model for the UIEncryptionDataTable. */
112class UIEncryptionDataModel : public QAbstractTableModel
113{
114 Q_OBJECT;
115
116public:
117
118 /** Constructs model passing @a pParent to the base-class.
119 * @param encryptedMedia Brings the lists of medium ids (values) encrypted with passwords with ids (keys). */
120 UIEncryptionDataModel(QObject *pParent, const EncryptedMediumMap &encryptedMedia);
121
122 /** Returns the shallow copy of the encryption password map instance. */
123 EncryptionPasswordMap encryptionPasswords() const { return m_encryptionPasswords; }
124
125 /** Returns the row count, taking optional @a parent instead of root if necessary. */
126 virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
127 /** Returns the column count, taking optional @a parent instead of root if necessary. */
128 virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
129
130 /** Returns the @a index flags. */
131 virtual Qt::ItemFlags flags(const QModelIndex &index) const;
132
133 /** Returns the header data for the @a iSection, @a orientation and @a iRole. */
134 virtual QVariant headerData(int iSection, Qt::Orientation orientation, int iRole) const;
135
136 /** Returns the @a index data for the @a iRole. */
137 virtual QVariant data(const QModelIndex &index, int iRole = Qt::DisplayRole) const;
138 /** Defines the @a index data for the @a iRole as @a value. */
139 virtual bool setData(const QModelIndex &index, const QVariant &value, int iRole = Qt::EditRole);
140
141private:
142
143 /** Prepares all. */
144 void prepare();
145
146 /** Holds the encrypted medium map reference. */
147 const EncryptedMediumMap &m_encryptedMedia;
148
149 /** Holds the encryption password map instance. */
150 EncryptionPasswordMap m_encryptionPasswords;
151};
152
153
154/** QTableView extension used to
155 * allow the UIAddDiskEncryptionPasswordDialog to enter
156 * disk encryption passwords for particular password ids. */
157class UIEncryptionDataTable : public QTableView
158{
159 Q_OBJECT;
160
161signals:
162
163 /** Notifies listeners about editor's Enter/Return key triggering. */
164 void sigEditorEnterKeyTriggered();
165
166public:
167
168 /** Constructs table.
169 * @param encryptedMedia Brings the lists of medium ids (values) encrypted with passwords with ids (keys). */
170 UIEncryptionDataTable(const EncryptedMediumMap &encryptedMedia);
171
172 /** Returns the shallow copy of the encryption password map
173 * acquired from the UIEncryptionDataModel instance. */
174 EncryptionPasswordMap encryptionPasswords() const;
175
176 /** Initiates the editor for the first index available. */
177 void editFirstIndex();
178
179private:
180
181 /** Prepares all. */
182 void prepare();
183
184 /** Holds the encrypted medium map reference. */
185 const EncryptedMediumMap &m_encryptedMedia;
186
187 /** Holds the encryption-data model instance. */
188 UIEncryptionDataModel *m_pModelEncryptionData;
189};
190
191
192/*********************************************************************************************************************************
193* Class UIPasswordEditor implementation. *
194*********************************************************************************************************************************/
195
196UIPasswordEditor::UIPasswordEditor(QWidget *pParent)
197 : QLineEdit(pParent)
198{
199 /* Prepare: */
200 prepare();
201}
202
203void UIPasswordEditor::keyPressEvent(QKeyEvent *pEvent)
204{
205 /* Call to base-class: */
206 QLineEdit::keyPressEvent(pEvent);
207
208 /* Broadcast Enter/Return key press: */
209 switch (pEvent->key())
210 {
211 case Qt::Key_Enter:
212 case Qt::Key_Return:
213 emit sigEnterKeyTriggered();
214 pEvent->accept();
215 break;
216 default:
217 break;
218 }
219}
220
221void UIPasswordEditor::prepare()
222{
223 /* Set echo mode: */
224 setEchoMode(QLineEdit::Password);
225 /* Listen for the text changes: */
226 connect(this, &UIPasswordEditor::textChanged,
227 this, &UIPasswordEditor::sltCommitData);
228}
229
230
231/*********************************************************************************************************************************
232* Class UIEncryptionDataModel implementation. *
233*********************************************************************************************************************************/
234
235UIEncryptionDataModel::UIEncryptionDataModel(QObject *pParent, const EncryptedMediumMap &encryptedMedia)
236 : QAbstractTableModel(pParent)
237 , m_encryptedMedia(encryptedMedia)
238{
239 /* Prepare: */
240 prepare();
241}
242
243int UIEncryptionDataModel::rowCount(const QModelIndex &parent /* = QModelIndex() */) const
244{
245 Q_UNUSED(parent);
246 return m_encryptionPasswords.size();
247}
248
249int UIEncryptionDataModel::columnCount(const QModelIndex &parent /* = QModelIndex() */) const
250{
251 Q_UNUSED(parent);
252 return UIEncryptionDataTableSection_Max;
253}
254
255Qt::ItemFlags UIEncryptionDataModel::flags(const QModelIndex &index) const
256{
257 /* Check index validness: */
258 if (!index.isValid())
259 return Qt::NoItemFlags;
260 /* Depending on column index: */
261 switch (index.column())
262 {
263 case UIEncryptionDataTableSection_Id: return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
264 case UIEncryptionDataTableSection_Password: return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
265 default: break;
266 }
267 /* No flags by default: */
268 return Qt::NoItemFlags;
269}
270
271QVariant UIEncryptionDataModel::headerData(int iSection, Qt::Orientation orientation, int iRole) const
272{
273 /* Check argument validness: */
274 if (iRole != Qt::DisplayRole || orientation != Qt::Horizontal)
275 return QVariant();
276 /* Depending on column index: */
277 switch (iSection)
278 {
279 case UIEncryptionDataTableSection_Id: return UIAddDiskEncryptionPasswordDialog::tr("ID", "password table field");
280 case UIEncryptionDataTableSection_Password: return UIAddDiskEncryptionPasswordDialog::tr("Password", "password table field");
281 default: break;
282 }
283 /* Null value by default: */
284 return QVariant();
285}
286
287QVariant UIEncryptionDataModel::data(const QModelIndex &index, int iRole /* = Qt::DisplayRole */) const
288{
289 /* Check argument validness: */
290 if (!index.isValid())
291 return QVariant();
292 /* Depending on role: */
293 switch (iRole)
294 {
295 case Qt::DisplayRole:
296 {
297 /* Depending on column index: */
298 switch (index.column())
299 {
300 case UIEncryptionDataTableSection_Id:
301 return m_encryptionPasswords.keys().at(index.row());
302 case UIEncryptionDataTableSection_Password:
303 return QString().fill('*', m_encryptionPasswords.value(m_encryptionPasswords.keys().at(index.row())).size());
304 default:
305 return QVariant();
306 }
307 break;
308 }
309 case Qt::EditRole:
310 {
311 /* Depending on column index: */
312 switch (index.column())
313 {
314 case UIEncryptionDataTableSection_Password:
315 return m_encryptionPasswords.value(m_encryptionPasswords.keys().at(index.row()));
316 default:
317 return QVariant();
318 }
319 break;
320 }
321 case Qt::ToolTipRole:
322 {
323 /* We are generating tool-tip here and not in retranslateUi() because of the tricky plural form handling,
324 * but be quiet, it's safe enough because the tool-tip being re-acquired every time on mouse-hovering. */
325 const QList<QUuid> encryptedMedia = m_encryptedMedia.values(m_encryptionPasswords.keys().at(index.row()));
326 return UIAddDiskEncryptionPasswordDialog::tr("<nobr>Used by the following %n hard disk(s):</nobr><br>%1",
327 "This text is never used with n == 0. "
328 "Feel free to drop the %n where possible, "
329 "we only included it because of problems with Qt Linguist "
330 "(but the user can see how many hard drives are in the tool-tip "
331 "and doesn't need to be told).",
332 encryptedMedia.size())
333 .arg(toStringList(encryptedMedia).join("<br>"));
334 }
335 default:
336 break;
337 }
338 /* Null value by default: */
339 return QVariant();
340}
341
342bool UIEncryptionDataModel::setData(const QModelIndex &index, const QVariant &value, int iRole /* = Qt::EditRole */)
343{
344 /* Check argument validness: */
345 if (!index.isValid() || iRole != Qt::EditRole)
346 return false;
347 /* Depending on column index: */
348 switch (index.column())
349 {
350 case UIEncryptionDataTableSection_Password:
351 {
352 /* Update password: */
353 const int iRow = index.row();
354 const QString strPassword = value.toString();
355 const QString strKey = m_encryptionPasswords.keys().at(iRow);
356 m_encryptionPasswords[strKey] = strPassword;
357 break;
358 }
359 default:
360 break;
361 }
362 /* Nothing to set by default: */
363 return false;
364}
365
366void UIEncryptionDataModel::prepare()
367{
368 /* Populate the map of passwords and statuses: */
369 foreach (const QString &strPasswordId, m_encryptedMedia.keys())
370 m_encryptionPasswords.insert(strPasswordId, QString());
371}
372
373
374/*********************************************************************************************************************************
375* Class UIEncryptionDataTable implementation. *
376*********************************************************************************************************************************/
377
378UIEncryptionDataTable::UIEncryptionDataTable(const EncryptedMediumMap &encryptedMedia)
379 : m_encryptedMedia(encryptedMedia)
380 , m_pModelEncryptionData(0)
381{
382 /* Prepare: */
383 prepare();
384}
385
386EncryptionPasswordMap UIEncryptionDataTable::encryptionPasswords() const
387{
388 AssertPtrReturn(m_pModelEncryptionData, EncryptionPasswordMap());
389 return m_pModelEncryptionData->encryptionPasswords();
390}
391
392void UIEncryptionDataTable::editFirstIndex()
393{
394 AssertPtrReturnVoid(m_pModelEncryptionData);
395 /* Compose the password field index of the first available table record: */
396 const QModelIndex index = m_pModelEncryptionData->index(0, UIEncryptionDataTableSection_Password);
397 /* Navigate table to the corresponding index: */
398 setCurrentIndex(index);
399 /* Compose the fake mouse-event which will trigger the embedded editor: */
400 QMouseEvent event(QEvent::MouseButtonPress, QPoint(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
401 /* Initiate the embedded editor for the corresponding index: */
402 edit(index, QAbstractItemView::SelectedClicked, &event);
403}
404
405void UIEncryptionDataTable::prepare()
406{
407 /* Create encryption-data model: */
408 m_pModelEncryptionData = new UIEncryptionDataModel(this, m_encryptedMedia);
409 if (m_pModelEncryptionData)
410 {
411 /* Assign configured model to table: */
412 setModel(m_pModelEncryptionData);
413 }
414
415 /* Create item delegate: */
416 QIStyledItemDelegate *pStyledItemDelegate = new QIStyledItemDelegate(this);
417 if (pStyledItemDelegate)
418 {
419 /* Create item editor factory: */
420 QItemEditorFactory *pNewItemEditorFactory = new QItemEditorFactory;
421 if (pNewItemEditorFactory)
422 {
423 /* Create item editor creator: */
424 QStandardItemEditorCreator<UIPasswordEditor> *pQStringItemEditorCreator = new QStandardItemEditorCreator<UIPasswordEditor>();
425 if (pQStringItemEditorCreator)
426 {
427 /* Register UIPasswordEditor as the QString editor: */
428 pNewItemEditorFactory->registerEditor(QVariant::String, pQStringItemEditorCreator);
429 }
430
431 /* Assign configured item editor factory to table delegate: */
432 pStyledItemDelegate->setItemEditorFactory(pNewItemEditorFactory);
433 }
434
435 /* Assign configured item delegate to table: */
436 delete itemDelegate();
437 setItemDelegate(pStyledItemDelegate);
438
439 /* Configure item delegate: */
440 pStyledItemDelegate->setWatchForEditorDataCommits(true);
441 pStyledItemDelegate->setWatchForEditorEnterKeyTriggering(true);
442 connect(pStyledItemDelegate, &QIStyledItemDelegate::sigEditorEnterKeyTriggered,
443 this, &UIEncryptionDataTable::sigEditorEnterKeyTriggered);
444 }
445
446 /* Configure table: */
447 setTabKeyNavigation(false);
448 setContextMenuPolicy(Qt::CustomContextMenu);
449 setSelectionBehavior(QAbstractItemView::SelectRows);
450 setSelectionMode(QAbstractItemView::SingleSelection);
451 setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::SelectedClicked);
452
453 /* Configure headers: */
454 verticalHeader()->hide();
455 verticalHeader()->setDefaultSectionSize((int)(verticalHeader()->minimumSectionSize() * 1.33));
456 horizontalHeader()->setStretchLastSection(false);
457 horizontalHeader()->setSectionResizeMode(UIEncryptionDataTableSection_Id, QHeaderView::Interactive);
458 horizontalHeader()->setSectionResizeMode(UIEncryptionDataTableSection_Password, QHeaderView::Stretch);
459}
460
461
462/*********************************************************************************************************************************
463* Class UIAddDiskEncryptionPasswordDialog implementation. *
464*********************************************************************************************************************************/
465
466UIAddDiskEncryptionPasswordDialog::UIAddDiskEncryptionPasswordDialog(QWidget *pParent,
467 const QString &strMachineName,
468 const EncryptedMediumMap &encryptedMedia)
469 : QIWithRetranslateUI<QDialog>(pParent)
470 , m_strMachineName(strMachineName)
471 , m_encryptedMedia(encryptedMedia)
472 , m_pLabelDescription(0)
473 , m_pTableEncryptionData(0)
474 , m_pButtonBox(0)
475{
476 /* Prepare: */
477 prepare();
478 /* Apply language settings: */
479 retranslateUi();
480}
481
482EncryptionPasswordMap UIAddDiskEncryptionPasswordDialog::encryptionPasswords() const
483{
484 AssertPtrReturn(m_pTableEncryptionData, EncryptionPasswordMap());
485 return m_pTableEncryptionData->encryptionPasswords();
486}
487
488void UIAddDiskEncryptionPasswordDialog::retranslateUi()
489{
490 /* Translate the dialog title: */
491 setWindowTitle(tr("%1 - Disk Encryption").arg(m_strMachineName));
492
493 /* Translate the description label: */
494 AssertPtrReturnVoid(m_pLabelDescription);
495 m_pLabelDescription->setText(tr("This virtual machine is password protected. "
496 "Please enter the %n encryption password(s) below.",
497 "This text is never used with n == 0. "
498 "Feel free to drop the %n where possible, "
499 "we only included it because of problems with Qt Linguist "
500 "(but the user can see how many passwords are in the list "
501 "and doesn't need to be told).",
502 m_encryptedMedia.uniqueKeys().size()));
503}
504
505void UIAddDiskEncryptionPasswordDialog::accept()
506{
507 /* Validate passwords status: */
508 foreach (const QString &strPasswordId, m_encryptedMedia.uniqueKeys())
509 {
510 const QUuid uMediumId = m_encryptedMedia.values(strPasswordId).first();
511 const QString strPassword = m_pTableEncryptionData->encryptionPasswords().value(strPasswordId);
512 if (!isPasswordValid(uMediumId, strPassword))
513 {
514 msgCenter().warnAboutInvalidEncryptionPassword(strPasswordId, this);
515 AssertPtrReturnVoid(m_pTableEncryptionData);
516 m_pTableEncryptionData->setFocus();
517 m_pTableEncryptionData->editFirstIndex();
518 return;
519 }
520 }
521 /* Call to base-class: */
522 QIWithRetranslateUI<QDialog>::accept();
523}
524
525void UIAddDiskEncryptionPasswordDialog::prepare()
526{
527 /* Configure self: */
528 setWindowModality(Qt::WindowModal);
529
530 /* Create main-layout: */
531 QVBoxLayout *pMainLayout = new QVBoxLayout(this);
532 if (pMainLayout)
533 {
534 /* Create input-layout: */
535 QVBoxLayout *pInputLayout = new QVBoxLayout;
536 if (pInputLayout)
537 {
538 /* Create description label: */
539 m_pLabelDescription = new QLabel;
540 if (m_pLabelDescription)
541 {
542 /* Add label into layout: */
543 pInputLayout->addWidget(m_pLabelDescription);
544 }
545
546 /* Create encryption-data table: */
547 m_pTableEncryptionData = new UIEncryptionDataTable(m_encryptedMedia);
548 if (m_pTableEncryptionData)
549 {
550 /* Configure encryption-data table: */
551 connect(m_pTableEncryptionData, &UIEncryptionDataTable::sigEditorEnterKeyTriggered,
552 this, &UIAddDiskEncryptionPasswordDialog::sltEditorEnterKeyTriggered);
553 m_pTableEncryptionData->setFocus();
554 m_pTableEncryptionData->editFirstIndex();
555 /* Add label into layout: */
556 pInputLayout->addWidget(m_pTableEncryptionData);
557 }
558
559 /* Add layout into parent: */
560 pMainLayout->addLayout(pInputLayout);
561 }
562
563 /* Create button-box: */
564 m_pButtonBox = new QIDialogButtonBox;
565 if (m_pButtonBox)
566 {
567 /* Configure button-box: */
568 m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
569 connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UIAddDiskEncryptionPasswordDialog::accept);
570 connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIAddDiskEncryptionPasswordDialog::reject);
571
572 /* Add button-box into layout: */
573 pMainLayout->addWidget(m_pButtonBox);
574 }
575 }
576}
577
578/* static */
579bool UIAddDiskEncryptionPasswordDialog::isPasswordValid(const QUuid &uMediumId, const QString strPassword)
580{
581 /* Look for the medium with passed ID: */
582 const UIMedium uimedium = vboxGlobal().medium(uMediumId);
583 if (!uimedium.isNull())
584 {
585 /* Check wrapped medium for validity: */
586 const CMedium medium = uimedium.medium();
587 if (!medium.isNull())
588 {
589 /* Check whether the password is suitable for that medium: */
590 medium.CheckEncryptionPassword(strPassword);
591 return medium.isOk();
592 }
593 }
594 /* False by default: */
595 return false;
596}
597
598
599#include "UIAddDiskEncryptionPasswordDialog.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use