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"