File indexing completed on 2024-05-19 05:55:47
0001 /* 0002 SPDX-FileCopyrightText: 2003-2005 George Staikos <staikos@kde.org> 0003 SPDX-FileCopyrightText: 2005 Isaac Clerencia <isaac@warp.es> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kwalleteditor.h" 0009 #include "kbetterthankdialogbase.h" 0010 #include "kwmapeditor.h" 0011 #include "allyourbase.h" 0012 0013 #include <QAction> 0014 #include <QDialog> 0015 #include <QInputDialog> 0016 #include <KIO/StoredTransferJob> 0017 #include <KJobWidgets> 0018 #include <KActionCollection> 0019 #include <KCodecs> 0020 #include <KMessageBox> 0021 #include <QMenu> 0022 #include <KSqueezedTextLabel> 0023 #include <KStandardAction> 0024 #include <KConfigGroup> 0025 0026 #include <QTemporaryFile> 0027 #include <KXMLGUIFactory> 0028 #include <KSharedConfig> 0029 0030 #include <KToolBar> 0031 #include <KLocalizedString> 0032 #include <QIcon> 0033 #include <KTreeWidgetSearchLine> 0034 0035 #include <QCheckBox> 0036 #include <QClipboard> 0037 #include <QDomDocument> 0038 #include <QDomElement> 0039 #include <QDomNode> 0040 #include <QFileDialog> 0041 #include <QList> 0042 #include <QPushButton> 0043 #include <QStack> 0044 #include <QSet> 0045 #include <QTimer> 0046 #include <QToolButton> 0047 #include <QVBoxLayout> 0048 #include <QXmlStreamWriter> 0049 0050 #include <cassert> 0051 #include <cstdlib> 0052 #include <kwidgetsaddons_version.h> 0053 0054 QAction *KWalletEditor::_newFolderAction = nullptr; 0055 QAction *KWalletEditor::_deleteFolderAction = nullptr; 0056 QAction *KWalletEditor::_exportAction = nullptr; 0057 QAction *KWalletEditor::_mergeAction = nullptr; 0058 QAction *KWalletEditor::_importAction = nullptr; 0059 QAction *KWalletEditor::_newEntryAction = nullptr; 0060 QAction *KWalletEditor::_renameEntryAction = nullptr; 0061 QAction *KWalletEditor::_deleteEntryAction = nullptr; 0062 QAction *KWalletEditor::_copyPassAction = nullptr; 0063 QAction *KWalletEditor::_alwaysShowContentsAction = nullptr; 0064 QAction *KWalletEditor::_alwaysHideContentsAction = nullptr; 0065 0066 RegisterCreateActionsMethod KWalletEditor::_registerCreateActionMethod(&KWalletEditor::createActions); 0067 0068 KWalletEditor::KWalletEditor(QWidget *parent, const QString &name) 0069 : QWidget(parent), _displayedItem(nullptr), _actionCollection(nullptr), _alwaysShowContents(false) 0070 { 0071 setupUi(this); 0072 setObjectName(name); 0073 _newWallet = false; 0074 _splitter->setStretchFactor(0, 1); 0075 _splitter->setStretchFactor(1, 2); 0076 _contextMenu = new QMenu(this); 0077 0078 _undoChanges->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); 0079 _saveChanges->setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); 0080 _hasUnsavedChanges = false; 0081 0082 auto box = new QVBoxLayout(_entryListFrame); 0083 box->setContentsMargins(0, 0, 0, 0); 0084 box->setSpacing(0); 0085 _entryList = new KWalletEntryList(_entryListFrame, QStringLiteral("Wallet Entry List")); 0086 _entryList->setContextMenuPolicy(Qt::CustomContextMenu); 0087 _entryList->setProperty("_breeze_borders_sides", QVariant::fromValue(QFlags{Qt::TopEdge})); 0088 auto lineWrapper = new QWidget(_entryList); 0089 auto lineWrapperLayout = new QVBoxLayout(lineWrapper); 0090 lineWrapperLayout->setContentsMargins( 0091 style()->pixelMetric(QStyle::PM_LayoutLeftMargin), 0092 style()->pixelMetric(QStyle::PM_LayoutTopMargin), 0093 style()->pixelMetric(QStyle::PM_LayoutRightMargin), 0094 style()->pixelMetric(QStyle::PM_LayoutBottomMargin) 0095 ); 0096 _searchLine = new KTreeWidgetSearchLine(lineWrapper, _entryList); 0097 _searchLine->setPlaceholderText(i18n("Search")); 0098 connect(_searchLine, &KTreeWidgetSearchLine::textChanged, this, &KWalletEditor::onSearchTextChanged); 0099 lineWrapperLayout->addWidget(_searchLine); 0100 box->addWidget(lineWrapper); 0101 box->addWidget(_entryList); 0102 0103 _entryStack->setEnabled(true); 0104 0105 box = new QVBoxLayout(_entryStack->widget(2)); 0106 box->setContentsMargins(0, 0, 0, 0); 0107 _mapEditorShowHide = new QCheckBox(i18n("&Show values"), _entryStack->widget(2)); 0108 connect(_mapEditorShowHide, &QCheckBox::toggled, this, &KWalletEditor::showHideMapEditorValue); 0109 _mapEditor = new KWMapEditor(_currentMap, _entryStack->widget(2)); 0110 box->addWidget(_mapEditorShowHide); 0111 box->addWidget(_mapEditor); 0112 0113 // load splitter size 0114 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("WalletEditor")); 0115 QList<int> splitterSize = cg.readEntry("SplitterSize", QList<int>()); 0116 if (splitterSize.size() != 2) { 0117 splitterSize.clear(); 0118 splitterSize.append(_splitter->width() / 2); 0119 splitterSize.append(_splitter->width() / 2); 0120 } 0121 _splitter->setSizes(splitterSize); 0122 _alwaysShowContents = cg.readEntry("AlwaysShowContents", false); 0123 0124 _searchLine->setFocus(); 0125 0126 connect(_entryList, &KWalletEntryList::currentItemChanged, this, &KWalletEditor::entrySelectionChanged); 0127 connect(_entryList, &KWalletEntryList::customContextMenuRequested, this, &KWalletEditor::listContextMenuRequested); 0128 connect(_entryList, &KWalletEntryList::itemChanged, this, &KWalletEditor::listItemChanged); 0129 0130 connect(_passwordValue, &QTextEdit::textChanged, this, &KWalletEditor::entryEditted); 0131 connect(_mapEditor, &KWMapEditor::dirty, this, &KWalletEditor::entryEditted); 0132 0133 connect(_undoChanges, &QPushButton::clicked, this, &KWalletEditor::restoreEntry); 0134 connect(_saveChanges, &QPushButton::clicked, this, &KWalletEditor::saveEntry); 0135 0136 connect(_showContents, &QToolButton::clicked, this, &KWalletEditor::showPasswordContents); 0137 connect(_hideContents, &QToolButton::clicked, this, &KWalletEditor::hidePasswordContents); 0138 0139 _passwordValue->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0140 0141 _binaryViewShow->setChecked(_alwaysShowContents); 0142 // createActions(); 0143 // TODO: remove kwalleteditor.rc file 0144 } 0145 0146 KWalletEditor::~KWalletEditor() 0147 { 0148 Q_EMIT enableFolderActions(false); 0149 Q_EMIT enableWalletActions(false); 0150 Q_EMIT enableContextFolderActions(false); 0151 // save splitter size 0152 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("WalletEditor")); 0153 cg.writeEntry("SplitterSize", _splitter->sizes()); 0154 cg.writeEntry("AlwaysShowContents", _alwaysShowContents); 0155 cg.sync(); 0156 0157 delete _w; 0158 _w = nullptr; 0159 if (_nonLocal) { 0160 KWallet::Wallet::closeWallet(_walletName, true); 0161 } 0162 delete _contextMenu; 0163 _contextMenu = nullptr; 0164 } 0165 0166 void KWalletEditor::setWallet(KWallet::Wallet *wallet, bool isPath) 0167 { 0168 Q_ASSERT(wallet != nullptr); 0169 _walletName = wallet->walletName(); 0170 _nonLocal = isPath; 0171 0172 _w = wallet; 0173 _entryList->setWallet(_w); 0174 connect(_w, &KWallet::Wallet::walletOpened, this, &KWalletEditor::walletOpened); 0175 connect(_w, &KWallet::Wallet::walletClosed, this, &KWalletEditor::walletClosed); 0176 connect(_w, &KWallet::Wallet::folderUpdated, this, &KWalletEditor::updateEntries); 0177 connect(_w, SIGNAL(folderListUpdated()), this, SLOT(updateFolderList())); 0178 updateFolderList(); 0179 0180 Q_EMIT enableFolderActions(true); 0181 Q_EMIT enableWalletActions(true); 0182 Q_EMIT enableContextFolderActions(true); 0183 0184 _mapEditorShowHide->setChecked(false); 0185 showHideMapEditorValue(false); 0186 0187 setFocus(); 0188 _searchLine->setFocus(); 0189 } 0190 0191 bool KWalletEditor::isOpen() const 0192 { 0193 return _w != nullptr; 0194 } 0195 0196 KActionCollection *KWalletEditor::actionCollection() 0197 { 0198 if (!_actionCollection) { 0199 _actionCollection = new KActionCollection(this); 0200 } 0201 return _actionCollection; 0202 } 0203 0204 void KWalletEditor::createActions(KActionCollection *actionCollection) 0205 { 0206 _newFolderAction = actionCollection->addAction(QStringLiteral("create_folder")); 0207 _newFolderAction->setText(i18n("&New Folder...")); 0208 _newFolderAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); 0209 0210 _deleteFolderAction = actionCollection->addAction(QStringLiteral("delete_folder")); 0211 _deleteFolderAction->setText(i18n("&Delete Folder")); 0212 0213 _mergeAction = actionCollection->addAction(QStringLiteral("wallet_merge")); 0214 _mergeAction->setText(i18n("&Import a wallet...")); 0215 _mergeAction->setEnabled(false); 0216 0217 _importAction = actionCollection->addAction(QStringLiteral("wallet_import")); 0218 _importAction->setText(i18n("&Import XML...")); 0219 _importAction->setEnabled(false); 0220 0221 _exportAction = actionCollection->addAction(QStringLiteral("wallet_export")); 0222 _exportAction->setText(i18n("&Export as XML...")); 0223 _exportAction->setEnabled(false); 0224 0225 _copyPassAction = actionCollection->addAction(QStringLiteral("copy_action")); 0226 _copyPassAction->setText(i18n("&Copy")); 0227 actionCollection->setDefaultShortcut(_copyPassAction, Qt::Key_C | Qt::CTRL); 0228 _copyPassAction->setEnabled(false); 0229 0230 _newEntryAction = actionCollection->addAction(QStringLiteral("new_entry")); 0231 _newEntryAction->setText(i18n("&New...")); 0232 actionCollection->setDefaultShortcut(_newEntryAction, Qt::Key_Insert); 0233 _newEntryAction->setEnabled(false); 0234 0235 _renameEntryAction = actionCollection->addAction(QStringLiteral("rename_entry")); 0236 _renameEntryAction->setText(i18n("&Rename")); 0237 actionCollection->setDefaultShortcut(_renameEntryAction, Qt::Key_F2); 0238 _renameEntryAction->setEnabled(false); 0239 0240 _deleteEntryAction = actionCollection->addAction(QStringLiteral("delete_entry")); 0241 _deleteEntryAction->setText(i18n("&Delete")); 0242 actionCollection->setDefaultShortcut(_deleteEntryAction, Qt::Key_Delete); 0243 _deleteEntryAction->setEnabled(false); 0244 0245 _alwaysShowContentsAction = actionCollection->addAction(QStringLiteral("always_show_contents")); 0246 _alwaysShowContentsAction->setText(i18n("Always show contents")); 0247 _alwaysShowContentsAction->setCheckable(true); 0248 0249 _alwaysHideContentsAction = actionCollection->addAction(QStringLiteral("always_hide_contents")); 0250 _alwaysHideContentsAction->setText(i18n("Always hide contents")); 0251 _alwaysHideContentsAction->setCheckable(true); 0252 } 0253 0254 void KWalletEditor::connectActions() 0255 { 0256 connect(_newFolderAction, &QAction::triggered, this, &KWalletEditor::createFolder); 0257 connect(this, &KWalletEditor::enableFolderActions, _newFolderAction, &QAction::setEnabled); 0258 0259 connect(_deleteFolderAction, &QAction::triggered, this, &KWalletEditor::deleteFolder); 0260 connect(this, &KWalletEditor::enableContextFolderActions, _deleteFolderAction, &QAction::setEnabled); 0261 connect(this, &KWalletEditor::enableFolderActions, _deleteFolderAction, &QAction::setEnabled); 0262 0263 connect(_mergeAction, &QAction::triggered, this, &KWalletEditor::importWallet); 0264 connect(this, &KWalletEditor::enableWalletActions, _mergeAction, &QAction::setEnabled); 0265 0266 connect(_importAction, &QAction::triggered, this, &KWalletEditor::importXML); 0267 connect(this, &KWalletEditor::enableWalletActions, _importAction, &QAction::setEnabled); 0268 0269 connect(_exportAction, &QAction::triggered, this, &KWalletEditor::exportXML); 0270 connect(this, &KWalletEditor::enableWalletActions, _exportAction, &QAction::setEnabled); 0271 0272 connect(_newEntryAction, &QAction::triggered, this, &KWalletEditor::newEntry); 0273 0274 connect(_renameEntryAction, &QAction::triggered, this, &KWalletEditor::renameEntry); 0275 0276 connect(_deleteEntryAction, &QAction::triggered, this, &KWalletEditor::deleteEntry); 0277 0278 connect(_copyPassAction, &QAction::triggered, this, &KWalletEditor::copyPassword); 0279 connect(this, &KWalletEditor::enableWalletActions, _copyPassAction, &QAction::setEnabled); 0280 0281 _showContents->addAction(_alwaysShowContentsAction); 0282 _alwaysShowContentsAction->setChecked(_alwaysShowContents); 0283 connect(_alwaysShowContentsAction, &QAction::triggered, this, &KWalletEditor::onAlwaysShowContents); 0284 0285 _hideContents->addAction(_alwaysHideContentsAction); 0286 _alwaysHideContentsAction->setChecked(!_alwaysShowContents); 0287 connect(_alwaysHideContentsAction, &QAction::triggered, this, &KWalletEditor::onAlwaysHideContents); 0288 } 0289 0290 void KWalletEditor::disconnectActions() 0291 { 0292 disconnect(_newFolderAction, &QAction::triggered, this, &KWalletEditor::createFolder); 0293 disconnect(this, &KWalletEditor::enableFolderActions, _newFolderAction, &QAction::setEnabled); 0294 0295 disconnect(_deleteFolderAction, &QAction::triggered, this, &KWalletEditor::deleteFolder); 0296 disconnect(this, &KWalletEditor::enableContextFolderActions, _deleteFolderAction, &QAction::setEnabled); 0297 disconnect(this, &KWalletEditor::enableFolderActions, _deleteFolderAction, &QAction::setEnabled); 0298 0299 disconnect(_mergeAction, &QAction::triggered, this, &KWalletEditor::importWallet); 0300 disconnect(this, &KWalletEditor::enableWalletActions, _mergeAction, &QAction::setEnabled); 0301 0302 disconnect(_importAction, &QAction::triggered, this, &KWalletEditor::importXML); 0303 disconnect(this, &KWalletEditor::enableWalletActions, _importAction, &QAction::setEnabled); 0304 0305 disconnect(_exportAction, &QAction::triggered, this, &KWalletEditor::exportXML); 0306 disconnect(this, &KWalletEditor::enableWalletActions, _exportAction, &QAction::setEnabled); 0307 0308 disconnect(_newEntryAction, &QAction::triggered, this, &KWalletEditor::newEntry); 0309 0310 disconnect(_renameEntryAction, &QAction::triggered, this, &KWalletEditor::renameEntry); 0311 0312 disconnect(_deleteEntryAction, &QAction::triggered, this, &KWalletEditor::deleteEntry); 0313 0314 disconnect(_copyPassAction, &QAction::triggered, this, &KWalletEditor::copyPassword); 0315 disconnect(this, &KWalletEditor::enableWalletActions, _copyPassAction, &QAction::setEnabled); 0316 0317 disconnect(_alwaysShowContentsAction, &QAction::triggered, this, &KWalletEditor::onAlwaysShowContents); 0318 disconnect(_alwaysHideContentsAction, &QAction::triggered, this, &KWalletEditor::onAlwaysHideContents); 0319 } 0320 0321 void KWalletEditor::walletClosed() 0322 { 0323 _w = nullptr; 0324 setEnabled(false); 0325 Q_EMIT enableWalletActions(false); 0326 Q_EMIT enableFolderActions(false); 0327 } 0328 0329 void KWalletEditor::updateFolderList(bool checkEntries) 0330 { 0331 const QStringList fl = _w->folderList(); 0332 QStack<QTreeWidgetItem *> trash; 0333 0334 for (int i = 0; i < _entryList->topLevelItemCount(); ++i) { 0335 auto fi = dynamic_cast<KWalletFolderItem *>(_entryList->topLevelItem(i)); 0336 if (!fi) { 0337 continue; 0338 } 0339 if (!fl.contains(fi->name())) { 0340 trash.push(fi); 0341 } 0342 } 0343 0344 qDeleteAll(trash); 0345 trash.clear(); 0346 0347 for (QStringList::const_iterator i = fl.begin(); i != fl.end(); ++i) { 0348 if (_entryList->existsFolder(*i)) { 0349 if (checkEntries) { 0350 updateEntries(*i); 0351 } 0352 continue; 0353 } 0354 0355 _w->setFolder(*i); 0356 const QStringList entries = _w->entryList(); 0357 auto item = new KWalletFolderItem(_w, _entryList, *i, entries.count()); 0358 0359 auto pi = new KWalletContainerItem(item, i18n("Passwords"), KWallet::Wallet::Password); 0360 auto mi = new KWalletContainerItem(item, i18n("Maps"), KWallet::Wallet::Map); 0361 auto bi = new KWalletContainerItem(item, i18n("Binary Data"), KWallet::Wallet::Stream); 0362 auto ui = new KWalletContainerItem(item, i18n("Unknown"), KWallet::Wallet::Unknown); 0363 0364 for (QStringList::const_iterator j = entries.begin(); j != entries.end(); ++j) { 0365 switch (_w->entryType(*j)) { 0366 case KWallet::Wallet::Password: 0367 new KWalletEntryItem(_w, pi, *j); 0368 break; 0369 case KWallet::Wallet::Stream: 0370 new KWalletEntryItem(_w, bi, *j); 0371 break; 0372 case KWallet::Wallet::Map: 0373 new KWalletEntryItem(_w, mi, *j); 0374 break; 0375 case KWallet::Wallet::Unknown: 0376 default: 0377 new QTreeWidgetItem(ui, QStringList() << *j); 0378 break; 0379 } 0380 } 0381 _entryList->setEnabled(true); 0382 } 0383 0384 //check if the current folder has been removed 0385 if (!fl.contains(_currentFolder)) { 0386 _currentFolder.clear(); 0387 _entryTitle->clear(); 0388 _iconTitle->clear(); 0389 } 0390 } 0391 0392 void KWalletEditor::deleteFolder() 0393 { 0394 if (_w) { 0395 QTreeWidgetItem *i = _entryList->currentItem(); 0396 if (i) { 0397 auto fi = dynamic_cast<KWalletFolderItem *>(i); 0398 if (!fi) { 0399 return; 0400 } 0401 0402 KMessageBox::ButtonCode buttonCode = KMessageBox::warningContinueCancel(this, i18n("Are you sure you wish to delete the folder '%1' from the wallet?", fi->name()), QString(), KStandardGuiItem::del()); 0403 if (buttonCode == KMessageBox::Continue) { 0404 bool rc = _w->removeFolder(fi->name()); 0405 if (!rc) { 0406 KMessageBox::error(this, i18n("Error deleting folder.")); 0407 return; 0408 } 0409 _currentFolder.clear(); 0410 _entryTitle->clear(); 0411 _iconTitle->clear(); 0412 updateFolderList(); 0413 } 0414 } 0415 } 0416 } 0417 0418 void KWalletEditor::createFolder() 0419 { 0420 if (_w) { 0421 QString n; 0422 bool ok; 0423 0424 do { 0425 n = QInputDialog::getText(this, i18n("New Folder"), 0426 i18n("Please choose a name for the new folder:"), 0427 QLineEdit::Normal, QString(), 0428 &ok); 0429 0430 if (!ok) { 0431 return; 0432 } 0433 0434 if (_entryList->existsFolder(n)) { 0435 int rc = KMessageBox::questionTwoActions(this, 0436 i18n("Sorry, that folder name is in use. Try again?"), 0437 QString(), 0438 KGuiItem(i18n("Try Again")), 0439 KGuiItem(i18n("Do Not Try"))); 0440 if (rc == KMessageBox::ButtonCode::PrimaryAction) { 0441 continue; 0442 } 0443 n.clear(); 0444 } 0445 break; 0446 } while (true); 0447 0448 _w->createFolder(n); 0449 updateFolderList(); 0450 } 0451 } 0452 0453 void KWalletEditor::saveEntry() 0454 { 0455 int rc = 1; 0456 QTreeWidgetItem *item = _displayedItem; // _entryList->currentItem(); 0457 _saveChanges->setEnabled(false); 0458 _undoChanges->setEnabled(false); 0459 _hasUnsavedChanges = false; 0460 if (item && _w && item->parent()) { 0461 auto ci = dynamic_cast<KWalletContainerItem *>(item->parent()); 0462 if (ci) { 0463 if (ci->entryType() == KWallet::Wallet::Password) { 0464 rc = _w->writePassword(item->text(0), _passwordValue->toPlainText()); 0465 } else if (ci->entryType() == KWallet::Wallet::Map) { 0466 _mapEditor->saveMap(); 0467 rc = _w->writeMap(item->text(0), _currentMap); 0468 } else { 0469 return; 0470 } 0471 0472 if (rc == 0) { 0473 return; 0474 } 0475 } 0476 } 0477 0478 KMessageBox::error(this, i18n("Error saving entry. Error code: %1", rc)); 0479 } 0480 0481 void KWalletEditor::restoreEntry() 0482 { 0483 entrySelectionChanged(_entryList->currentItem()); 0484 _hasUnsavedChanges = false; 0485 } 0486 0487 void KWalletEditor::entryEditted() 0488 { 0489 _saveChanges->setEnabled(true); 0490 _undoChanges->setEnabled(true); 0491 _hasUnsavedChanges = true; 0492 } 0493 0494 void KWalletEditor::entrySelectionChanged(QTreeWidgetItem *item) 0495 { 0496 // do not forget to save changes 0497 if (_saveChanges->isEnabled() && _displayedItem && (_displayedItem != item)) { 0498 if (KMessageBox::ButtonCode::PrimaryAction 0499 == KMessageBox::questionTwoActions(this, 0500 i18n("The contents of the current item has changed.\nDo you want to save changes?"), 0501 {}, 0502 KStandardGuiItem::save(), 0503 KStandardGuiItem::cancel())) { 0504 saveEntry(); 0505 } else { 0506 _saveChanges->setEnabled(false); 0507 _undoChanges->setEnabled(false); 0508 _hasUnsavedChanges = false; 0509 } 0510 } 0511 0512 KWalletContainerItem *ci = nullptr; 0513 KWalletFolderItem *fi = nullptr; 0514 0515 // clear the context menu 0516 _contextMenu->clear(); 0517 _contextMenu->setEnabled(true); 0518 // disable the entry actions (reenable them on adding) 0519 _newEntryAction->setEnabled(false); 0520 _renameEntryAction->setEnabled(false); 0521 _deleteEntryAction->setEnabled(false); 0522 0523 if (item) { 0524 // set the context menu's title 0525 _contextMenu->addSection(_contextMenu->fontMetrics().elidedText( 0526 item->text(0), Qt::ElideMiddle, 200)); 0527 0528 // TODO rtti 0529 switch (item->type()) { 0530 case KWalletEntryItemClass: 0531 ci = dynamic_cast<KWalletContainerItem *>(item->parent()); 0532 if (!ci) { 0533 return; 0534 } 0535 fi = dynamic_cast<KWalletFolderItem *>(ci->parent()); 0536 if (!fi) { 0537 return; 0538 } 0539 _w->setFolder(fi->name()); 0540 _deleteFolderAction->setEnabled(false); 0541 0542 // add standard menu items 0543 _contextMenu->addAction(_newEntryAction); 0544 _contextMenu->addAction(_renameEntryAction); 0545 _contextMenu->addAction(_deleteEntryAction); 0546 _newEntryAction->setEnabled(true); 0547 _renameEntryAction->setEnabled(true); 0548 _deleteEntryAction->setEnabled(true); 0549 0550 if (ci->entryType() == KWallet::Wallet::Password) { 0551 QString pass; 0552 if (_w->readPassword(item->text(0), pass) == 0) { 0553 _entryStack->setCurrentIndex(4); 0554 _entryName->setText(i18n("Password: %1", 0555 item->text(0))); 0556 _passwordValue->setText(pass); 0557 _saveChanges->setEnabled(false); 0558 _undoChanges->setEnabled(false); 0559 _hasUnsavedChanges = false; 0560 } 0561 // add a context-menu action for copying passwords 0562 _contextMenu->addSeparator(); 0563 _contextMenu->addAction(_copyPassAction); 0564 if (_alwaysShowContents) { 0565 QTimer::singleShot(0, this, &KWalletEditor::showPasswordContents); 0566 } 0567 } else if (ci->entryType() == KWallet::Wallet::Map) { 0568 _entryStack->setCurrentIndex(2); 0569 if (_w->readMap(item->text(0), _currentMap) == 0) { 0570 _mapEditor->reload(); 0571 _entryName->setText(i18n("Name-Value Map: %1", item->text(0))); 0572 _saveChanges->setEnabled(false); 0573 _undoChanges->setEnabled(false); 0574 _hasUnsavedChanges = false; 0575 showHideMapEditorValue(_mapEditorShowHide->isChecked()); 0576 } 0577 } else if (ci->entryType() == KWallet::Wallet::Stream) { 0578 _entryStack->setCurrentIndex(3); 0579 QByteArray ba; 0580 if (_w->readEntry(item->text(0), ba) == 0) { 0581 _entryName->setText(i18n("Binary Data: %1", 0582 item->text(0))); 0583 _saveChanges->setEnabled(false); 0584 _undoChanges->setEnabled(false); 0585 _hasUnsavedChanges = false; 0586 _binaryView->setData(ba); 0587 } 0588 } 0589 break; 0590 0591 case KWalletContainerItemClass: 0592 ci = dynamic_cast<KWalletContainerItem *>(item); 0593 if (!ci) { 0594 return; 0595 } 0596 if (ci->entryType() == KWallet::Wallet::Unknown) { 0597 // disable context menu on unknown items 0598 _contextMenu->setEnabled(false); 0599 } else { 0600 // add the context menu action 0601 _contextMenu->addAction(_newEntryAction); 0602 _newEntryAction->setEnabled(true); 0603 } 0604 0605 fi = dynamic_cast<KWalletFolderItem *>(item->parent()); 0606 if (!fi) { 0607 return; 0608 } 0609 _w->setFolder(fi->name()); 0610 _deleteFolderAction->setEnabled(false); 0611 _entryName->clear(); 0612 _entryStack->setCurrentIndex(0); 0613 break; 0614 0615 case KWalletFolderItemClass: 0616 // add the context menu actions 0617 _contextMenu->addAction(_newFolderAction); 0618 _contextMenu->addAction(_deleteFolderAction); 0619 0620 fi = dynamic_cast<KWalletFolderItem *>(item); 0621 if (!fi) { 0622 return; 0623 } 0624 _w->setFolder(fi->name()); 0625 _deleteFolderAction->setEnabled(true); 0626 _entryName->clear(); 0627 _entryStack->setCurrentIndex(0); 0628 break; 0629 0630 default: 0631 // all items but Unknown entries return have their 0632 // rtti set. Unknown items can only be deleted. 0633 _contextMenu->addAction(_deleteEntryAction); 0634 _deleteEntryAction->setEnabled(true); 0635 break; 0636 } 0637 } else { 0638 // no item selected. add the "new folder" action to the context menu 0639 _contextMenu->addAction(_newFolderAction); 0640 } 0641 0642 if (fi) { 0643 _currentFolder = fi->name(); 0644 _entryTitle->setText(QStringLiteral("<font size=\"+1\">%1</font>").arg(fi->text(0))); 0645 _iconTitle->setPixmap(fi->getFolderIcon().pixmap(style()->pixelMetric(QStyle::PixelMetric::PM_ToolBarIconSize))); 0646 } 0647 0648 _displayedItem = item; 0649 } 0650 0651 void KWalletEditor::updateEntries(const QString &folder) 0652 { 0653 QStack<QTreeWidgetItem *> trash; 0654 0655 _w->setFolder(folder); 0656 const QStringList entries = _w->entryList(); 0657 0658 KWalletFolderItem *fi = _entryList->getFolder(folder); 0659 0660 if (!fi) { 0661 return; 0662 } 0663 0664 KWalletContainerItem *pi = fi->getContainer(KWallet::Wallet::Password); 0665 KWalletContainerItem *mi = fi->getContainer(KWallet::Wallet::Map); 0666 KWalletContainerItem *bi = fi->getContainer(KWallet::Wallet::Stream); 0667 KWalletContainerItem *ui = fi->getContainer(KWallet::Wallet::Unknown); 0668 0669 // Remove deleted entries 0670 for (int i = 0; i < pi->childCount(); ++i) { 0671 QTreeWidgetItem *twi = pi->child(i); 0672 if (!entries.contains(twi->text(0))) { 0673 if (twi == _entryList->currentItem()) { 0674 entrySelectionChanged(nullptr); 0675 } 0676 trash.push(twi); 0677 } 0678 } 0679 0680 for (int i = 0; i < mi->childCount(); ++i) { 0681 QTreeWidgetItem *twi = mi->child(i); 0682 if (!entries.contains(twi->text(0))) { 0683 if (twi == _entryList->currentItem()) { 0684 entrySelectionChanged(nullptr); 0685 } 0686 trash.push(twi); 0687 } 0688 } 0689 0690 for (int i = 0; i < bi->childCount(); ++i) { 0691 QTreeWidgetItem *twi = bi->child(i); 0692 if (!entries.contains(twi->text(0))) { 0693 if (twi == _entryList->currentItem()) { 0694 entrySelectionChanged(nullptr); 0695 } 0696 trash.push(twi); 0697 } 0698 } 0699 0700 for (int i = 0; i < ui->childCount(); ++i) { 0701 QTreeWidgetItem *twi = ui->child(i); 0702 if (!entries.contains(twi->text(0))) { 0703 if (twi == _entryList->currentItem()) { 0704 entrySelectionChanged(nullptr); 0705 } 0706 trash.push(twi); 0707 } 0708 } 0709 0710 qDeleteAll(trash); 0711 trash.clear(); 0712 0713 // Add new entries 0714 for (QStringList::const_iterator i = entries.begin(), end = entries.end(); i != end; ++i) { 0715 if (fi->contains(*i)) { 0716 continue; 0717 } 0718 0719 switch (_w->entryType(*i)) { 0720 case KWallet::Wallet::Password: 0721 new KWalletEntryItem(_w, pi, *i); 0722 break; 0723 case KWallet::Wallet::Stream: 0724 new KWalletEntryItem(_w, bi, *i); 0725 break; 0726 case KWallet::Wallet::Map: 0727 new KWalletEntryItem(_w, mi, *i); 0728 break; 0729 case KWallet::Wallet::Unknown: 0730 default: 0731 new QTreeWidgetItem(ui, QStringList() << *i); 0732 break; 0733 } 0734 } 0735 fi->refresh(); 0736 if (fi->name() == _currentFolder) { 0737 _entryTitle->setText(QStringLiteral("<font size=\"+1\">%1</font>").arg(fi->text(0))); 0738 } 0739 if (!_entryList->currentItem()) { 0740 _entryName->clear(); 0741 _entryStack->setCurrentIndex(0); 0742 } 0743 } 0744 0745 void KWalletEditor::listContextMenuRequested(const QPoint &pos) 0746 { 0747 if (!_contextMenu->isEnabled()) { 0748 return; 0749 } 0750 0751 _contextMenu->popup(_entryList->mapToGlobal(pos)); 0752 } 0753 0754 void KWalletEditor::copyPassword() 0755 { 0756 QTreeWidgetItem *item = _entryList->currentItem(); 0757 if (_w && item) { 0758 QString pass; 0759 if (_w->readPassword(item->text(0), pass) == 0) { 0760 QApplication::clipboard()->setText(pass); 0761 } 0762 } 0763 } 0764 0765 void KWalletEditor::newEntry() 0766 { 0767 QTreeWidgetItem *item = _entryList->currentItem(); 0768 QString n; 0769 bool ok; 0770 0771 KWalletFolderItem *fi; 0772 0773 //set the folder where we're trying to create the new entry 0774 if (_w && item) { 0775 QTreeWidgetItem *p = item; 0776 if (p->type() == KWalletEntryItemClass) { 0777 p = item->parent(); 0778 } 0779 fi = dynamic_cast<KWalletFolderItem *>(p->parent()); 0780 if (!fi) { 0781 return; 0782 } 0783 _w->setFolder(fi->name()); 0784 } else { 0785 return; 0786 } 0787 0788 do { 0789 n = QInputDialog::getText(this, i18n("New Entry"), 0790 i18n("Please choose a name for the new entry:"), 0791 QLineEdit::Normal, QString(), 0792 &ok); 0793 0794 if (!ok) { 0795 return; 0796 } 0797 0798 // FIXME: prohibits the use of the subheadings 0799 if (fi->contains(n)) { 0800 int rc = KMessageBox::questionTwoActions(this, 0801 i18n("Sorry, that entry already exists. Try again?"), 0802 QString(), 0803 KGuiItem(i18n("Try Again")), 0804 KGuiItem(i18n("Do Not Try"))); 0805 if (rc == KMessageBox::ButtonCode::PrimaryAction) { 0806 continue; 0807 } 0808 n.clear(); 0809 } 0810 break; 0811 } while (true); 0812 0813 if (_w && item && !n.isEmpty()) { 0814 QTreeWidgetItem *p = item; 0815 if (p->type() == KWalletEntryItemClass) { 0816 p = item->parent(); 0817 } 0818 0819 auto fi = dynamic_cast<KWalletFolderItem *>(p->parent()); 0820 if (!fi) { 0821 KMessageBox::error(this, i18n("An unexpected error occurred trying to add the new entry")); 0822 return; 0823 } 0824 _w->setFolder(fi->name()); 0825 0826 auto ni = new KWalletEntryItem(_w, p, n); 0827 0828 auto ci = dynamic_cast<KWalletContainerItem *>(p); 0829 if (!ci) { 0830 KMessageBox::error(this, i18n("An unexpected error occurred trying to add the new entry")); 0831 delete ni; 0832 return; 0833 } 0834 if (ci->entryType() == KWallet::Wallet::Password) { 0835 _w->writePassword(n, QString()); 0836 } else if (ci->entryType() == KWallet::Wallet::Map) { 0837 _w->writeMap(n, QMap<QString, QString>()); 0838 } else if (ci->entryType() == KWallet::Wallet::Stream) { 0839 _w->writeEntry(n, QByteArray()); 0840 } else { 0841 abort(); 0842 } 0843 0844 _entryList->setCurrentItem(ni); 0845 _entryList->scrollToItem(ni); 0846 0847 fi->refresh(); 0848 _entryTitle->setText(QStringLiteral("<font size=\"+1\">%1</font>").arg(fi->text(0))); 0849 } 0850 } 0851 0852 void KWalletEditor::renameEntry() 0853 { 0854 QTreeWidgetItem *item = _entryList->currentItem(); 0855 if (_w && item) { 0856 _entryList->editItem(item, 0); 0857 } 0858 } 0859 0860 // Only supports renaming of KWalletEntryItem derived classes. 0861 void KWalletEditor::listItemChanged(QTreeWidgetItem *item, int column) 0862 { 0863 if (item && column == 0) { 0864 auto i = dynamic_cast<KWalletEntryItem *>(item); 0865 if (!i) { 0866 return; 0867 } 0868 0869 const QString t = item->text(0); 0870 if (t == i->name()) { 0871 return; 0872 } 0873 if (!_w || t.isEmpty()) { 0874 i->restoreName(); 0875 return; 0876 } 0877 0878 if (_w->renameEntry(i->name(), t) == 0) { 0879 i->setName(t); 0880 auto ci = dynamic_cast<KWalletContainerItem *>(item->parent()); 0881 if (!ci) { 0882 KMessageBox::error(this, i18n("An unexpected error occurred trying to rename the entry")); 0883 return; 0884 } 0885 if (ci->entryType() == KWallet::Wallet::Password) { 0886 _entryName->setText(i18n("Password: %1", item->text(0))); 0887 } else if (ci->entryType() == KWallet::Wallet::Map) { 0888 _entryName->setText(i18n("Name-Value Map: %1", item->text(0))); 0889 } else if (ci->entryType() == KWallet::Wallet::Stream) { 0890 _entryName->setText(i18n("Binary Data: %1", item->text(0))); 0891 } 0892 } else { 0893 i->restoreName(); 0894 } 0895 } 0896 } 0897 0898 void KWalletEditor::deleteEntry() 0899 { 0900 QTreeWidgetItem *item = _entryList->currentItem(); 0901 if (_w && item) { 0902 int rc = KMessageBox::warningContinueCancel(this, i18n("Are you sure you wish to delete the item '%1'?", item->text(0)), QString(), KStandardGuiItem::del()); 0903 if (rc == KMessageBox::Continue) { 0904 auto fi = dynamic_cast<KWalletFolderItem *>(item->parent()->parent()); 0905 if (!fi) { 0906 KMessageBox::error(this, i18n("An unexpected error occurred trying to delete the entry")); 0907 return; 0908 } 0909 _displayedItem = nullptr; 0910 _w->removeEntry(item->text(0)); 0911 delete item; 0912 entrySelectionChanged(_entryList->currentItem()); 0913 fi->refresh(); 0914 _entryTitle->setText(QStringLiteral("<font size=\"+1\">%1</font>").arg(fi->text(0))); 0915 } 0916 } 0917 } 0918 0919 void KWalletEditor::changePassword() 0920 { 0921 KWallet::Wallet::changePassword(_walletName, effectiveWinId()); 0922 } 0923 0924 void KWalletEditor::walletOpened(bool success) 0925 { 0926 if (success) { 0927 Q_EMIT enableFolderActions(true); 0928 Q_EMIT enableContextFolderActions(false); 0929 Q_EMIT enableWalletActions(true); 0930 updateFolderList(); 0931 _entryList->setWallet(_w); 0932 } else { 0933 if (!_newWallet) { 0934 KMessageBox::error(this, i18n("Unable to open the requested wallet.")); 0935 } 0936 } 0937 } 0938 0939 void KWalletEditor::hidePasswordContents() 0940 { 0941 _entryStack->setCurrentIndex(4); 0942 } 0943 0944 void KWalletEditor::showPasswordContents() 0945 { 0946 _entryStack->setCurrentIndex(1); 0947 } 0948 0949 void KWalletEditor::showHideMapEditorValue(bool show) 0950 { 0951 if (show) { 0952 _mapEditor->showColumn(2); 0953 } else { 0954 _mapEditor->hideColumn(2); 0955 } 0956 } 0957 0958 enum MergePlan { Prompt = 0, Always = 1, Never = 2, Yes = 3, No = 4 }; 0959 0960 void KWalletEditor::importWallet() 0961 { 0962 QUrl url = QFileDialog::getOpenFileUrl(this, QString(), QUrl(), QStringLiteral("*.kwl")); 0963 0964 if (url.isEmpty()) { 0965 return; 0966 } 0967 0968 QTemporaryFile tmpFile; 0969 if (!tmpFile.open()) { 0970 KMessageBox::error(this, i18n("Unable to create temporary file for downloading '<b>%1</b>'.", url.toDisplayString())); 0971 return; 0972 } 0973 0974 KIO::StoredTransferJob *job = KIO::storedGet(url); 0975 KJobWidgets::setWindow(job, this); 0976 if (!job->exec()) { 0977 KMessageBox::error(this, i18n("Unable to access wallet '<b>%1</b>'.", url.toDisplayString())); 0978 return; 0979 } 0980 tmpFile.write(job->data()); 0981 tmpFile.flush(); 0982 0983 KWallet::Wallet *w = KWallet::Wallet::openWallet(tmpFile.fileName(), effectiveWinId(), KWallet::Wallet::Path); 0984 if (w && w->isOpen()) { 0985 MergePlan mp = Prompt; 0986 QStringList fl = w->folderList(); 0987 for (QStringList::ConstIterator f = fl.constBegin(); f != fl.constEnd(); ++f) { 0988 if (!w->setFolder(*f)) { 0989 continue; 0990 } 0991 0992 if (!_w->hasFolder(*f)) { 0993 _w->createFolder(*f); 0994 } 0995 0996 _w->setFolder(*f); 0997 0998 bool readMap = false; 0999 QMap<QString, QMap<QString, QString>> map = w->mapList(&readMap); 1000 QSet<QString> mergedkeys; // prevents re-merging already merged entries. 1001 if (readMap) { 1002 QMap<QString, QMap<QString, QString> >::ConstIterator me; 1003 for (me = map.constBegin(); me != map.constEnd(); ++me) { 1004 bool hasEntry = _w->hasEntry(me.key()); 1005 if (hasEntry && mp == Prompt) { 1006 KBetterThanKDialogBase *bd; 1007 bd = new KBetterThanKDialogBase(this); 1008 bd->setLabel(i18n("Folder '<b>%1</b>' already contains an entry '<b>%2</b>'. Do you wish to replace it?", f->toHtmlEscaped(), me.key().toHtmlEscaped())); 1009 mp = static_cast<MergePlan>(bd->exec()); 1010 delete bd; 1011 bool ok = false; 1012 if (mp == Always || mp == Yes) { 1013 ok = true; 1014 } 1015 if (mp == Yes || mp == No) { 1016 // reset mp 1017 mp = Prompt; 1018 } 1019 if (!ok) { 1020 continue; 1021 } 1022 } else if (hasEntry && mp == Never) { 1023 continue; 1024 } 1025 _w->writeMap(me.key(), me.value()); 1026 mergedkeys.insert(me.key()); // remember this key has been merged 1027 } 1028 } 1029 1030 bool readPassList = false; 1031 QMap<QString, QString> pwd = w->passwordList(&readPassList); 1032 if (readPassList) { 1033 QMap<QString, QString>::ConstIterator pe; 1034 for (pe = pwd.constBegin(); pe != pwd.constEnd(); ++pe) { 1035 bool hasEntry = _w->hasEntry(pe.key()); 1036 if (hasEntry && mp == Prompt) { 1037 auto bd = new KBetterThanKDialogBase(this); 1038 bd->setLabel(i18n("Folder '<b>%1</b>' already contains an entry '<b>%2</b>'. Do you wish to replace it?", f->toHtmlEscaped(), pe.key().toHtmlEscaped())); 1039 mp = static_cast<MergePlan>(bd->exec()); 1040 delete bd; 1041 bool ok = false; 1042 if (mp == Always || mp == Yes) { 1043 ok = true; 1044 } 1045 if (mp == Yes || mp == No) { 1046 // reset mp 1047 mp = Prompt; 1048 } 1049 if (!ok) { 1050 continue; 1051 } 1052 } else if (hasEntry && mp == Never) { 1053 continue; 1054 } 1055 _w->writePassword(pe.key(), pe.value()); 1056 mergedkeys.insert(pe.key()); // remember this key has been merged 1057 } 1058 } 1059 1060 bool readEntries = false; 1061 QMap<QString, QByteArray> ent = w->entriesList(&readEntries); 1062 if (readEntries) { 1063 QMap<QString, QByteArray>::ConstIterator ee; 1064 for (ee = ent.constBegin(); ee != ent.constEnd(); ++ee) { 1065 // prevent re-merging already merged entries. 1066 if (mergedkeys.contains(ee.key())) { 1067 continue; 1068 } 1069 bool hasEntry = _w->hasEntry(ee.key()); 1070 if (hasEntry && mp == Prompt) { 1071 auto bd = new KBetterThanKDialogBase(this); 1072 bd->setLabel(i18n("Folder '<b>%1</b>' already contains an entry '<b>%2</b>'. Do you wish to replace it?", f->toHtmlEscaped(), ee.key().toHtmlEscaped())); 1073 mp = static_cast<MergePlan>(bd->exec()); 1074 delete bd; 1075 bool ok = false; 1076 if (mp == Always || mp == Yes) { 1077 ok = true; 1078 } 1079 if (mp == Yes || mp == No) { 1080 // reset mp 1081 mp = Prompt; 1082 } 1083 if (!ok) { 1084 continue; 1085 } 1086 } else if (hasEntry && mp == Never) { 1087 continue; 1088 } 1089 _w->writeEntry(ee.key(), ee.value()); 1090 } 1091 } 1092 } 1093 } 1094 1095 delete w; 1096 1097 updateFolderList(true); 1098 restoreEntry(); 1099 } 1100 1101 void KWalletEditor::importXML() 1102 { 1103 const QUrl url = QFileDialog::getOpenFileUrl(this, QString(), QUrl(), QStringLiteral("*.xml")); 1104 1105 if (url.isEmpty()) { 1106 return; 1107 } 1108 1109 KIO::StoredTransferJob *job = KIO::storedGet(url); 1110 KJobWidgets::setWindow(job, this); 1111 if (!job->exec()) { 1112 KMessageBox::error(this, i18n("Unable to access XML file '<b>%1</b>'.", url.toDisplayString())); 1113 return; 1114 } 1115 1116 QDomDocument doc; 1117 if (!doc.setContent(job->data())) { 1118 KMessageBox::error(this, i18n("Error reading XML file '<b>%1</b>' for input.", url.toDisplayString())); 1119 return; 1120 } 1121 1122 QDomElement top = doc.documentElement(); 1123 if (top.tagName().toLower() != QLatin1String("wallet")) { 1124 KMessageBox::error(this, i18n("Error: XML file does not contain a wallet.")); 1125 return; 1126 } 1127 1128 QDomNode n = top.firstChild(); 1129 MergePlan mp = Prompt; 1130 while (!n.isNull()) { 1131 QDomElement e = n.toElement(); 1132 if (e.tagName().toLower() != QLatin1String("folder")) { 1133 n = n.nextSibling(); 1134 continue; 1135 } 1136 1137 QString fname = e.attribute(QStringLiteral("name")); 1138 if (fname.isEmpty()) { 1139 n = n.nextSibling(); 1140 continue; 1141 } 1142 if (!_w->hasFolder(fname)) { 1143 _w->createFolder(fname); 1144 } 1145 _w->setFolder(fname); 1146 QDomNode enode = e.firstChild(); 1147 while (!enode.isNull()) { 1148 e = enode.toElement(); 1149 QString type = e.tagName().toLower(); 1150 QString ename = e.attribute(QStringLiteral("name")); 1151 bool hasEntry = _w->hasEntry(ename); 1152 if (hasEntry && mp == Prompt) { 1153 auto bd = new KBetterThanKDialogBase(this); 1154 bd->setLabel(i18n("Folder '<b>%1</b>' already contains an entry '<b>%2</b>'. Do you wish to replace it?", fname.toHtmlEscaped(), ename.toHtmlEscaped())); 1155 mp = static_cast<MergePlan>(bd->exec()); 1156 delete bd; 1157 bool ok = false; 1158 if (mp == Always || mp == Yes) { 1159 ok = true; 1160 } 1161 if (mp == Yes || mp == No) { // reset mp 1162 mp = Prompt; 1163 } 1164 if (!ok) { 1165 enode = enode.nextSibling(); 1166 continue; 1167 } 1168 } else if (hasEntry && mp == Never) { 1169 enode = enode.nextSibling(); 1170 continue; 1171 } 1172 1173 if (type == QLatin1String("password")) { 1174 _w->writePassword(ename, e.text()); 1175 } else if (type == QLatin1String("stream")) { 1176 _w->writeEntry(ename, KCodecs::base64Decode(e.text().toLatin1())); 1177 } else if (type == QLatin1String("map")) { 1178 QMap<QString, QString> map; 1179 QDomNode mapNode = e.firstChild(); 1180 while (!mapNode.isNull()) { 1181 QDomElement mape = mapNode.toElement(); 1182 if (mape.tagName().toLower() == QLatin1String("mapentry")) { 1183 map[mape.attribute(QStringLiteral("name"))] = mape.text(); 1184 } 1185 mapNode = mapNode.nextSibling(); 1186 } 1187 _w->writeMap(ename, map); 1188 } 1189 enode = enode.nextSibling(); 1190 } 1191 n = n.nextSibling(); 1192 } 1193 1194 updateFolderList(true); 1195 restoreEntry(); 1196 } 1197 1198 void KWalletEditor::exportXML() 1199 { 1200 QTemporaryFile tf; 1201 tf.open(); 1202 QXmlStreamWriter xml(&tf); 1203 xml.setAutoFormatting(true); 1204 xml.writeStartDocument(); 1205 const QStringList fl = _w->folderList(); 1206 1207 xml.writeStartElement(QStringLiteral("wallet")); 1208 xml.writeAttribute(QStringLiteral("name"), _walletName); 1209 for (QStringList::const_iterator i = fl.constBegin(), flEnd(fl.constEnd()); i != flEnd; ++i) { 1210 xml.writeStartElement(QStringLiteral("folder")); 1211 xml.writeAttribute(QStringLiteral("name"), *i); 1212 _w->setFolder(*i); 1213 QStringList entries = _w->entryList(); 1214 for (QStringList::const_iterator j = entries.constBegin(), entriesEnd(entries.constEnd()); j != entriesEnd; ++j) { 1215 switch (_w->entryType(*j)) { 1216 case KWallet::Wallet::Password: { 1217 QString pass; 1218 if (_w->readPassword(*j, pass) == 0) { 1219 xml.writeStartElement(QStringLiteral("password")); 1220 xml.writeAttribute(QStringLiteral("name"), *j); 1221 xml.writeCharacters(pass); 1222 xml.writeEndElement(); 1223 } 1224 break; 1225 } 1226 case KWallet::Wallet::Stream: { 1227 QByteArray ba; 1228 if (_w->readEntry(*j, ba) == 0) { 1229 xml.writeStartElement(QStringLiteral("stream")); 1230 xml.writeAttribute(QStringLiteral("name"), *j); 1231 xml.writeCharacters(QLatin1String(KCodecs::base64Encode(ba))); 1232 xml.writeEndElement(); 1233 } 1234 break; 1235 } 1236 case KWallet::Wallet::Map: { 1237 QMap<QString, QString> map; 1238 if (_w->readMap(*j, map) == 0) { 1239 xml.writeStartElement(QStringLiteral("map")); 1240 xml.writeAttribute(QStringLiteral("name"), *j); 1241 for (QMap<QString, QString>::ConstIterator k = map.constBegin(), mapEnd(map.constEnd()); k != mapEnd; ++k) { 1242 xml.writeStartElement(QStringLiteral("mapentry")); 1243 xml.writeAttribute(QStringLiteral("name"), k.key()); 1244 xml.writeCharacters(k.value()); 1245 xml.writeEndElement(); 1246 } 1247 xml.writeEndElement(); 1248 } 1249 break; 1250 } 1251 case KWallet::Wallet::Unknown: 1252 default: 1253 break; 1254 } 1255 } 1256 xml.writeEndElement(); 1257 } 1258 1259 xml.writeEndElement(); 1260 xml.writeEndDocument(); 1261 tf.flush(); 1262 1263 QUrl url = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), QStringLiteral("*.xml")); 1264 1265 if (url.isEmpty()) { 1266 return; 1267 } 1268 1269 // #368314: QTemporaryFiles are open in ReadWrite mode, so the QIODevice's position needs to be rewind. 1270 tf.seek(0); 1271 1272 KIO::StoredTransferJob *putJob = KIO::storedPut(&tf, url, -1); 1273 KJobWidgets::setWindow(putJob, this); 1274 if (!putJob->exec()) { 1275 KMessageBox::error(this, i18n("Unable to store to '<b>%1</b>'.", url.toDisplayString())); 1276 } 1277 } 1278 1279 void KWalletEditor::setNewWallet(bool x) 1280 { 1281 _newWallet = x; 1282 } 1283 1284 void KWalletEditor::hideEvent(QHideEvent *) 1285 { 1286 Q_EMIT enableContextFolderActions(false); 1287 Q_EMIT enableFolderActions(false); 1288 Q_EMIT enableWalletActions(false); 1289 disconnectActions(); 1290 } 1291 1292 void KWalletEditor::showEvent(QShowEvent *) 1293 { 1294 connectActions(); 1295 Q_EMIT enableContextFolderActions(true); 1296 Q_EMIT enableFolderActions(true); 1297 Q_EMIT enableWalletActions(true); 1298 } 1299 1300 void KWalletEditor::onSearchTextChanged(const QString &text) 1301 { 1302 static bool treeIsExpanded = false; 1303 if (text.isEmpty()) { 1304 if (treeIsExpanded) { 1305 _entryList->setCurrentItem(nullptr); 1306 // NOTE: the 300 ms here is a value >200 ms used internally by KTreeWidgetSearchLine 1307 // TODO: replace this timer with a connection to KTreeWidgetSearchLine::searchUpdated signal introduced with KF5 1308 QTimer::singleShot(300, _entryList, &KWalletEntryList::collapseAll); 1309 treeIsExpanded = false; 1310 } 1311 } else { 1312 if (!treeIsExpanded) { 1313 _entryList->expandAll(); 1314 treeIsExpanded = true; 1315 } 1316 // NOTE: the 300 ms here is a value >200 ms used internally by KTreeWidgetSearchLine 1317 // TODO: replace this timer with a connection to KTreeWidgetSearchLine::searchUpdated signal introduced with KF5 1318 QTimer::singleShot(300, _entryList, &KWalletEntryList::selectFirstVisible); 1319 } 1320 // TODO: reduce timer count when KTreeWidgetSearchLine::searchUpdated signal will be there 1321 QTimer::singleShot(300, _entryList, &KWalletEntryList::refreshItemsCount); 1322 } 1323 1324 void KWalletEditor::onAlwaysShowContents(bool checked) 1325 { 1326 _alwaysShowContents = checked; 1327 _alwaysHideContentsAction->setChecked(!_alwaysShowContents); 1328 showPasswordContents(); 1329 } 1330 1331 void KWalletEditor::onAlwaysHideContents(bool checked) 1332 { 1333 _alwaysShowContents = !checked; 1334 _alwaysShowContentsAction->setChecked(_alwaysShowContents); 1335 if (checked) { 1336 hidePasswordContents(); 1337 } 1338 } 1339 1340 bool KWalletEditor::hasUnsavedChanges() const 1341 { 1342 return _hasUnsavedChanges; 1343 } 1344 1345 #include "moc_kwalleteditor.cpp"