File indexing completed on 2024-05-19 04:59:17

0001 /* ============================================================
0002 * KDEFrameworksIntegration - KDE support plugin for Falkon
0003 * Copyright (C) 2013-2018 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "kwalletpasswordbackend.h"
0019 #include "kdeframeworksintegrationplugin.h"
0020 #include "mainapplication.h"
0021 #include "browserwindow.h"
0022 #include "desktopnotificationsfactory.h"
0023 
0024 #include <QDateTime>
0025 
0026 #include <kwallet_version.h>
0027 #include <KWallet>
0028 
0029 static PasswordEntry decodeEntry(const QByteArray &data)
0030 {
0031     QDataStream stream(data);
0032     PasswordEntry entry;
0033     stream >> entry;
0034     return entry;
0035 }
0036 
0037 static QMap<QString, QString> encodeEntry(const PasswordEntry &entry)
0038 {
0039     QMap<QString, QString> data = {
0040         {QSL("host"), entry.host},
0041         {QSL("username"), entry.username},
0042         {QSL("password"), entry.password},
0043         {QSL("updated"), QString::number(entry.updated)},
0044         {QSL("data"), QString::fromUtf8(entry.data)}
0045     };
0046     return data;
0047 }
0048 
0049 KWalletPasswordBackend::KWalletPasswordBackend()
0050     : PasswordBackend()
0051     , m_wallet(nullptr)
0052 {
0053 }
0054 
0055 QString KWalletPasswordBackend::name() const
0056 {
0057     return KDEFrameworksIntegrationPlugin::tr("KWallet");
0058 }
0059 
0060 QVector<PasswordEntry> KWalletPasswordBackend::getEntries(const QUrl &url)
0061 {
0062     initialize();
0063 
0064     const QString host = PasswordManager::createHost(url);
0065 
0066     QVector<PasswordEntry> list;
0067 
0068     for (const PasswordEntry &entry : std::as_const(m_allEntries)) {
0069         if (entry.host == host) {
0070             list.append(entry);
0071         }
0072     }
0073 
0074     // Sort to prefer last updated entries
0075     std::sort(list.begin(), list.end());
0076 
0077     return list;
0078 }
0079 
0080 QVector<PasswordEntry> KWalletPasswordBackend::getAllEntries()
0081 {
0082     initialize();
0083 
0084     return m_allEntries;
0085 }
0086 
0087 // TODO QT6 - should we just start storing timestamps as 64-bit instead?
0088 static uint Q_DATETIME_TOTIME_T(const QDateTime &dateTime)
0089 {
0090     if (!dateTime.isValid())
0091         return uint(-1);
0092     qint64 retval = dateTime.toMSecsSinceEpoch() / 1000;
0093     if (quint64(retval) >= Q_UINT64_C(0xFFFFFFFF))
0094         return uint(-1);
0095     return uint(retval);
0096 }
0097 
0098 void KWalletPasswordBackend::addEntry(const PasswordEntry &entry)
0099 {
0100     initialize();
0101 
0102     if (!m_wallet) {
0103         showErrorNotification();
0104         return;
0105     }
0106 
0107     PasswordEntry stored = entry;
0108     stored.id = QSL("%1/%2").arg(entry.host, entry.username);
0109     stored.updated = Q_DATETIME_TOTIME_T(QDateTime::currentDateTime());
0110 
0111     m_wallet->writeMap(stored.id.toString(), encodeEntry(stored));
0112     m_allEntries.append(stored);
0113 }
0114 
0115 bool KWalletPasswordBackend::updateEntry(const PasswordEntry &entry)
0116 {
0117     initialize();
0118 
0119     if (!m_wallet) {
0120         showErrorNotification();
0121         return false;
0122     }
0123 
0124     m_wallet->removeEntry(entry.id.toString());
0125     m_wallet->writeMap(entry.id.toString(), encodeEntry(entry));
0126 
0127     int index = m_allEntries.indexOf(entry);
0128 
0129     if (index > -1) {
0130         m_allEntries[index] = entry;
0131     }
0132 
0133     return true;
0134 }
0135 
0136 void KWalletPasswordBackend::updateLastUsed(PasswordEntry &entry)
0137 {
0138     initialize();
0139 
0140     if (!m_wallet) {
0141         showErrorNotification();
0142         return;        
0143     }
0144 
0145     m_wallet->removeEntry(entry.id.toString());
0146 
0147     entry.updated = Q_DATETIME_TOTIME_T(QDateTime::currentDateTime());
0148 
0149     m_wallet->writeMap(entry.id.toString(), encodeEntry(entry));
0150 
0151     int index = m_allEntries.indexOf(entry);
0152 
0153     if (index > -1) {
0154         m_allEntries[index] = entry;
0155     }
0156 }
0157 
0158 void KWalletPasswordBackend::removeEntry(const PasswordEntry &entry)
0159 {
0160     initialize();
0161 
0162     if (!m_wallet) {
0163         showErrorNotification();
0164         return; 
0165     }
0166 
0167     m_wallet->removeEntry(entry.id.toString());
0168 
0169     int index = m_allEntries.indexOf(entry);
0170 
0171     if (index > -1) {
0172         m_allEntries.remove(index);
0173     }
0174 }
0175 
0176 void KWalletPasswordBackend::removeAll()
0177 {
0178     initialize();
0179 
0180     if (!m_wallet) {
0181         showErrorNotification();
0182         return; 
0183     }
0184 
0185     m_allEntries.clear();
0186 
0187     m_wallet->removeFolder(QSL("FalkonPasswords"));
0188     m_wallet->createFolder(QSL("FalkonPasswords"));
0189 }
0190 
0191 void KWalletPasswordBackend::showErrorNotification()
0192 {
0193     static bool initialized;
0194 
0195     if (!initialized) {
0196         initialized = true;
0197         mApp->desktopNotifications()->showNotification(KDEFrameworksIntegrationPlugin::tr("KWallet disabled"), KDEFrameworksIntegrationPlugin::tr("Please enable KWallet to save password."));
0198     }
0199 }
0200 
0201 void KWalletPasswordBackend::initialize()
0202 {
0203     if (m_wallet) {
0204         return;
0205     }
0206 
0207     WId wid = 0;
0208     BrowserWindow *w = mApp->getWindow();
0209     if (w && w->window()) {
0210         wid = w->window()->winId();
0211     }
0212     m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), wid);
0213 
0214     if (!m_wallet) {
0215         qWarning() << "KWalletPasswordBackend::initialize Cannot open wallet!";
0216         return;
0217     }
0218 
0219     bool migrationFalkon = !m_wallet->hasFolder(QSL("FalkonPasswords")) && m_wallet->hasFolder(QSL("Falkon"));
0220     bool migrateQupzilla = !m_wallet->hasFolder(QSL("FalkonPasswords")) && !m_wallet->hasFolder(QSL("Falkon")) && m_wallet->hasFolder(QSL("QupZilla"));
0221     bool migration = false;
0222 
0223     if (!m_wallet->hasFolder(QSL("FalkonPasswords")) && !m_wallet->createFolder(QSL("FalkonPasswords"))) {
0224         qWarning() << "KWalletPasswordBackend::initialize Cannot create folder \"FalkonPasswords\"!";
0225         return;
0226     }
0227 
0228     if (migrationFalkon) {
0229         if (!m_wallet->setFolder(QSL("Falkon"))) {
0230             qWarning() << "KWalletPasswordBackend::initialize Cannot set folder \"Falkon\"!";
0231             return;
0232         }
0233         migration = true;
0234     }
0235     else if (migrateQupzilla) {
0236         if (!m_wallet->setFolder(QSL("QupZilla"))) {
0237             qWarning() << "KWalletPasswordBackend::initialize Cannot set folder \"QupZilla\"!";
0238             return;
0239         }
0240         migration = true;
0241     }
0242     else {
0243         if (!m_wallet->setFolder(QSL("FalkonPasswords"))) {
0244             qWarning() << "KWalletPasswordBackend::initialize Cannot set folder \"FalkonPasswords\"!";
0245             return;
0246         }
0247     }
0248 
0249     if (migration) {
0250         QMap<QString, QByteArray> entries;
0251         bool ok = false;
0252         entries = m_wallet->entriesList(&ok);
0253         if (!ok) {
0254             qWarning() << "KWalletPasswordBackend::initialize Cannot read entries!";
0255             return;
0256         }
0257 
0258         QMap<QString, QByteArray>::const_iterator i = entries.constBegin();
0259         while (i != entries.constEnd()) {
0260             PasswordEntry entry = decodeEntry(i.value());
0261             if (entry.isValid()) {
0262                 m_allEntries.append(entry);
0263             }
0264             ++i;
0265         }
0266 
0267         if (!m_wallet->setFolder(QSL("FalkonPasswords"))) {
0268             qWarning() << "KWalletPasswordBackend::initialize Cannot set folder \"FalkonPasswords\"!";
0269             return;
0270         }
0271 
0272         for (const PasswordEntry &entry : std::as_const(m_allEntries)) {
0273             m_wallet->writeMap(entry.id.toString(), encodeEntry(entry));
0274         }
0275     }
0276     else {
0277         QMap<QString, QMap<QString, QString>> entriesMap;
0278         bool ok = false;
0279         entriesMap = m_wallet->mapList(&ok);
0280         QMap<QString, QMap<QString, QString>>::const_iterator j = entriesMap.constBegin();
0281         while (j != entriesMap.constEnd()) {
0282             PasswordEntry entry;
0283             entry.id = j.key();
0284             entry.host = j.value()[QSL("host")];
0285             entry.username = j.value()[QSL("username")];
0286             entry.password = j.value()[QSL("password")];
0287             entry.updated = j.value()[QSL("updated")].toInt();
0288             entry.data = j.value()[QSL("data")].toUtf8();
0289             if (entry.isValid()) {
0290                 m_allEntries.append(entry);
0291             }
0292             ++j;
0293         }
0294     }
0295 }
0296 
0297 KWalletPasswordBackend::~KWalletPasswordBackend()
0298 {
0299     delete m_wallet;
0300 }