File indexing completed on 2024-04-28 05:36:02

0001 /*
0002     SPDX-FileCopyrightText: 2005 Jean-Remy Falleri <jr.falleri@laposte.net>
0003     SPDX-FileCopyrightText: 2005-2007 Kevin Ottens <ervin@kde.org>
0004     SPDX-FileCopyrightText: 2007 Alexis Ménard <darktears31@gmail.com>
0005     SPDX-FileCopyrightText: 2011, 2014 Lukas Tinkl <ltinkl@redhat.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include "soliduiserver.h"
0011 #include "config-X11.h"
0012 #include "soliduiserver_debug.h"
0013 
0014 #include <QDBusInterface>
0015 #include <QDBusReply>
0016 #include <QDebug>
0017 #include <QIcon>
0018 
0019 #include <KLocalizedString>
0020 #include <KPluginFactory>
0021 #include <KUserTimestamp>
0022 
0023 #include <kpassworddialog.h>
0024 #include <kwallet.h>
0025 #include <kwindowsystem.h>
0026 #if HAVE_X11
0027 #include <KX11Extras>
0028 #endif
0029 
0030 // solid specific includes
0031 #include <solid/device.h>
0032 #include <solid/deviceinterface.h>
0033 #include <solid/devicenotifier.h>
0034 #include <solid/predicate.h>
0035 #include <solid/storagevolume.h>
0036 
0037 K_PLUGIN_CLASS_WITH_JSON(SolidUiServer, "soliduiserver.json")
0038 
0039 SolidUiServer::SolidUiServer(QObject *parent, const QList<QVariant> &)
0040     : KDEDModule(parent)
0041 {
0042 }
0043 
0044 SolidUiServer::~SolidUiServer()
0045 {
0046 }
0047 
0048 void SolidUiServer::showPassphraseDialog(const QString &udi, const QString &returnService, const QString &returnObject, uint wId, const QString &appId)
0049 {
0050     if (m_idToPassphraseDialog.contains(returnService + ':' + udi)) {
0051         KPasswordDialog *dialog = m_idToPassphraseDialog[returnService + ':' + udi];
0052         dialog->activateWindow();
0053         return;
0054     }
0055 
0056     Solid::Device device(udi);
0057 
0058     KPasswordDialog *dialog = new KPasswordDialog(nullptr, KPasswordDialog::ShowKeepPassword);
0059 
0060     QString label = device.vendor();
0061     if (!label.isEmpty())
0062         label += ' ';
0063     label += device.product();
0064 
0065     dialog->setPrompt(i18n("'%1' needs a password to be accessed. Please enter a password.", label));
0066     dialog->setIcon(QIcon::fromTheme(device.icon()));
0067     dialog->setProperty("soliduiserver.udi", udi);
0068     dialog->setProperty("soliduiserver.returnService", returnService);
0069     dialog->setProperty("soliduiserver.returnObject", returnObject);
0070 
0071     QString uuid;
0072     if (device.is<Solid::StorageVolume>())
0073         uuid = device.as<Solid::StorageVolume>()->uuid();
0074 
0075     // read the password from wallet and prefill it to the dialog
0076     if (!uuid.isEmpty()) {
0077         dialog->setProperty("soliduiserver.uuid", uuid);
0078 
0079         KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), (WId)wId);
0080         const QString folderName = QString::fromLatin1("SolidLuks");
0081         if (wallet && wallet->hasFolder(folderName)) {
0082             wallet->setFolder(folderName);
0083             QString savedPassword;
0084             if (wallet->readPassword(uuid, savedPassword) == 0) {
0085                 dialog->setKeepPassword(true);
0086                 dialog->setPassword(savedPassword);
0087             }
0088             wallet->closeWallet(wallet->walletName(), false);
0089         }
0090         delete wallet;
0091     }
0092 
0093     connect(dialog, &KPasswordDialog::gotPassword, this, &SolidUiServer::onPassphraseDialogCompleted);
0094     connect(dialog, &KPasswordDialog::rejected, this, &SolidUiServer::onPassphraseDialogRejected);
0095 
0096     m_idToPassphraseDialog[returnService + ':' + udi] = dialog;
0097 
0098     reparentDialog(dialog, (WId)wId, appId, true);
0099     dialog->show();
0100 }
0101 
0102 void SolidUiServer::onPassphraseDialogCompleted(const QString &pass, bool keep)
0103 {
0104     KPasswordDialog *dialog = qobject_cast<KPasswordDialog *>(sender());
0105 
0106     if (dialog) {
0107         QString returnService = dialog->property("soliduiserver.returnService").toString();
0108         QString returnObject = dialog->property("soliduiserver.returnObject").toString();
0109         QDBusInterface returnIface(returnService, returnObject);
0110 
0111         QDBusReply<void> reply = returnIface.call(QStringLiteral("passphraseReply"), pass);
0112 
0113         QString udi = dialog->property("soliduiserver.udi").toString();
0114         m_idToPassphraseDialog.remove(returnService + ':' + udi);
0115 
0116         if (!reply.isValid()) {
0117             qCWarning(SOLIDUISERVER_DEBUG) << "Impossible to send the passphrase to the application, D-Bus said: " << reply.error().name() << ", "
0118                                            << reply.error().message() << Qt::endl;
0119             return; // don't save into wallet if an error occurs
0120         }
0121 
0122         if (keep) { // save the password into the wallet
0123             KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0);
0124             if (wallet) {
0125                 const QString folderName = QString::fromLatin1("SolidLuks");
0126                 const QString uuid = dialog->property("soliduiserver.uuid").toString();
0127                 if (!wallet->hasFolder(folderName))
0128                     wallet->createFolder(folderName);
0129                 if (wallet->setFolder(folderName))
0130                     wallet->writePassword(uuid, pass);
0131                 wallet->closeWallet(wallet->walletName(), false);
0132                 delete wallet;
0133             }
0134         }
0135     }
0136 }
0137 
0138 void SolidUiServer::onPassphraseDialogRejected()
0139 {
0140     onPassphraseDialogCompleted(QString(), false);
0141 }
0142 
0143 void SolidUiServer::reparentDialog(QWidget *dialog, WId wId, const QString &appId, bool modal)
0144 {
0145     Q_UNUSED(appId);
0146     // Code borrowed from kwalletd
0147 
0148     dialog->setAttribute(Qt::WA_NativeWindow, true);
0149     KWindowSystem::setMainWindow(dialog->windowHandle(), wId); // correct, set dialog parent
0150 
0151 #if HAVE_X11
0152     if (KWindowSystem::isPlatformX11()) {
0153         if (modal) {
0154             KX11Extras::setState(dialog->winId(), NET::Modal);
0155         } else {
0156             KX11Extras::clearState(dialog->winId(), NET::Modal);
0157         }
0158     }
0159 #endif
0160 
0161     // allow dialog activation even if it interrupts, better than trying hacks
0162     // with keeping the dialog on top or on all desktops
0163     KUserTimestamp::updateUserTimestamp();
0164 }
0165 
0166 #include "soliduiserver.moc"