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

0001 /* ============================================================
0002 * GnomeKeyringPasswords - gnome-keyring support plugin for Falkon
0003 * Copyright (C) 2013-2014  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 "gnomekeyringpasswordbackend.h"
0019 #include "gnomekeyringplugin.h"
0020 
0021 #include <QDateTime>
0022 
0023 extern "C" {
0024 #include "gnome-keyring.h"
0025 }
0026 
0027 // TODO QT6 - should we just start storing timestamps as 64-bit instead?
0028 static uint Q_DATETIME_TOTIME_T(const QDateTime &dateTime)
0029 {
0030     if (!dateTime.isValid())
0031         return uint(-1);
0032     qint64 retval = dateTime.toMSecsSinceEpoch() / 1000;
0033     if (quint64(retval) >= Q_UINT64_C(0xFFFFFFFF))
0034         return uint(-1);
0035     return uint(retval);
0036 }
0037 
0038 static PasswordEntry createEntry(GnomeKeyringFound* item)
0039 {
0040     PasswordEntry entry;
0041     entry.id = item->item_id;
0042     entry.password = QString::fromUtf8(item->secret);
0043 
0044     for (unsigned i = 0; i < item->attributes->len; ++i) {
0045         GnomeKeyringAttribute attr = g_array_index(item->attributes, GnomeKeyringAttribute, i);
0046 
0047         if (strcmp(attr.name, "host") == 0) {
0048             entry.host = QString::fromUtf8(attr.value.string);
0049         }
0050         else if (strcmp(attr.name, "username") == 0) {
0051             entry.username = QString::fromUtf8(attr.value.string);
0052         }
0053         else if (strcmp(attr.name, "data") == 0) {
0054             entry.data = attr.value.string;
0055         }
0056         else if (strcmp(attr.name, "updated") == 0) {
0057             entry.updated = attr.value.integer;
0058         }
0059     }
0060 
0061     entry.data.replace(QByteArray("___PASSWORD-VALUE___"), PasswordManager::urlEncodePassword(entry.password));
0062 
0063     return entry;
0064 }
0065 
0066 static GnomeKeyringAttributeList* createAttributes(const PasswordEntry &entry)
0067 {
0068     GnomeKeyringAttributeList* attributes = gnome_keyring_attribute_list_new();
0069 
0070     gnome_keyring_attribute_list_append_string(attributes, "application", "Falkon");
0071 
0072     QByteArray value = entry.username.toUtf8();
0073     gnome_keyring_attribute_list_append_string(attributes, "username", value.constData());
0074 
0075     value = entry.data;
0076     value.replace(PasswordManager::urlEncodePassword(entry.password), "___PASSWORD-VALUE___");
0077     gnome_keyring_attribute_list_append_string(attributes, "data", value.constData());
0078 
0079     value = entry.host.toUtf8();
0080     gnome_keyring_attribute_list_append_string(attributes, "host", value.constData());
0081 
0082     gnome_keyring_attribute_list_append_uint32(attributes, "updated", entry.updated);
0083 
0084     return attributes;
0085 }
0086 
0087 static void storeEntry(PasswordEntry &entry)
0088 {
0089     guint32 itemId;
0090     GnomeKeyringAttributeList* attributes = createAttributes(entry);
0091 
0092     QByteArray pass = entry.password.toUtf8();
0093     QByteArray host = entry.host.toUtf8();
0094 
0095     GnomeKeyringResult result = gnome_keyring_item_create_sync(GNOME_KEYRING_DEFAULT,
0096                                 GNOME_KEYRING_ITEM_GENERIC_SECRET,
0097                                 host.constData(),
0098                                 attributes,
0099                                 pass.constData(),
0100                                 TRUE, // Update if exists
0101                                 &itemId);
0102 
0103     gnome_keyring_attribute_list_free(attributes);
0104 
0105     if (result != GNOME_KEYRING_RESULT_OK) {
0106         qWarning() << "GnomeKeyringPasswordBackend::addEntry Cannot add entry to keyring!";
0107     }
0108 
0109     entry.id = itemId;
0110 }
0111 
0112 GnomeKeyringPasswordBackend::GnomeKeyringPasswordBackend()
0113     : PasswordBackend()
0114     , m_loaded(false)
0115 {
0116 }
0117 
0118 QString GnomeKeyringPasswordBackend::name() const
0119 {
0120     return GnomeKeyringPlugin::tr("Gnome Keyring");
0121 }
0122 
0123 QVector<PasswordEntry> GnomeKeyringPasswordBackend::getEntries(const QUrl &url)
0124 {
0125     initialize();
0126 
0127     const QString host = PasswordManager::createHost(url);
0128 
0129     QVector<PasswordEntry> list;
0130 
0131     for (const PasswordEntry &entry : std::as_const(m_allEntries)) {
0132         if (entry.host == host) {
0133             list.append(entry);
0134         }
0135     }
0136 
0137     // Sort to prefer last updated entries
0138     std::sort(list.begin(), list.end());
0139 
0140     return list;
0141 }
0142 
0143 QVector<PasswordEntry> GnomeKeyringPasswordBackend::getAllEntries()
0144 {
0145     initialize();
0146 
0147     return m_allEntries;
0148 }
0149 
0150 void GnomeKeyringPasswordBackend::addEntry(const PasswordEntry &entry)
0151 {
0152     initialize();
0153 
0154     PasswordEntry stored = entry;
0155     stored.updated = Q_DATETIME_TOTIME_T(QDateTime::currentDateTime());
0156 
0157     storeEntry(stored);
0158 
0159     m_allEntries.append(stored);
0160 }
0161 
0162 bool GnomeKeyringPasswordBackend::updateEntry(const PasswordEntry &entry)
0163 {
0164     initialize();
0165 
0166     // Update item attributes
0167     GnomeKeyringAttributeList* attributes = createAttributes(entry);
0168 
0169     GnomeKeyringResult result = gnome_keyring_item_set_attributes_sync(GNOME_KEYRING_DEFAULT,
0170                                 entry.id.toUInt(),
0171                                 attributes);
0172 
0173     gnome_keyring_attribute_list_free(attributes);
0174 
0175     if (result != GNOME_KEYRING_RESULT_OK) {
0176         qWarning() << "GnomeKeyringPasswordBackend::updateEntry Cannot updated entry attributes in keyring!";
0177         return false;
0178     }
0179 
0180     // Update secret
0181     GnomeKeyringItemInfo* info;
0182     result = gnome_keyring_item_get_info_full_sync(GNOME_KEYRING_DEFAULT, entry.id.toUInt(),
0183              GNOME_KEYRING_ITEM_INFO_SECRET, &info);
0184 
0185     if (result != GNOME_KEYRING_RESULT_OK) {
0186         qWarning() << "GnomeKeyringPasswordBackend::updateEntry Cannot get entry info from keyring!";
0187         return false;
0188     }
0189 
0190     QByteArray pass = entry.password.toUtf8();
0191     gnome_keyring_item_info_set_secret(info, pass.constData());
0192 
0193     result = gnome_keyring_item_set_info_sync(GNOME_KEYRING_DEFAULT, entry.id.toUInt(), info);
0194 
0195     gnome_keyring_item_info_free(info);
0196 
0197     if (result != GNOME_KEYRING_RESULT_OK) {
0198         qWarning() << "GnomeKeyringPasswordBackend::updateEntry Cannot set entry info in keyring!";
0199         return false;
0200     }
0201 
0202     int index = m_allEntries.indexOf(entry);
0203 
0204     if (index > -1) {
0205         m_allEntries[index] = entry;
0206     }
0207 
0208     return true;
0209 }
0210 
0211 void GnomeKeyringPasswordBackend::updateLastUsed(PasswordEntry &entry)
0212 {
0213     initialize();
0214 
0215     entry.updated = Q_DATETIME_TOTIME_T(QDateTime::currentDateTime());
0216 
0217     GnomeKeyringAttributeList* attributes = createAttributes(entry);
0218 
0219     GnomeKeyringResult result = gnome_keyring_item_set_attributes_sync(GNOME_KEYRING_DEFAULT,
0220                                 entry.id.toUInt(),
0221                                 attributes);
0222 
0223     gnome_keyring_attribute_list_free(attributes);
0224 
0225     if (result != GNOME_KEYRING_RESULT_OK) {
0226         qWarning() << "GnomeKeyringPasswordBackend::updateLastUsed Cannot updated entry in keyring!";
0227         return;
0228     }
0229 
0230     int index = m_allEntries.indexOf(entry);
0231 
0232     if (index > -1) {
0233         m_allEntries[index] = entry;
0234     }
0235 }
0236 
0237 void GnomeKeyringPasswordBackend::removeEntry(const PasswordEntry &entry)
0238 {
0239     initialize();
0240 
0241     GnomeKeyringResult result = gnome_keyring_item_delete_sync(GNOME_KEYRING_DEFAULT, entry.id.toUInt());
0242 
0243     if (result != GNOME_KEYRING_RESULT_OK) {
0244         qWarning() << "GnomeKeyringPasswordBackend::removeEntry Cannot remove entry from keyring!";
0245         return;
0246     }
0247 
0248     int index = m_allEntries.indexOf(entry);
0249 
0250     if (index > -1) {
0251         m_allEntries.remove(index);
0252     }
0253 }
0254 
0255 void GnomeKeyringPasswordBackend::removeAll()
0256 {
0257     initialize();
0258 
0259     for (const PasswordEntry &entry : std::as_const(m_allEntries)) {
0260         removeEntry(entry);
0261     }
0262 
0263     m_allEntries.clear();
0264 }
0265 
0266 void GnomeKeyringPasswordBackend::initialize()
0267 {
0268     if (m_loaded) {
0269         return;
0270     }
0271 
0272     GList* found;
0273     GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(GNOME_KEYRING_ITEM_GENERIC_SECRET, &found,
0274                                 "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, "Falkon",
0275                                 NULL);
0276 
0277     if (result != GNOME_KEYRING_RESULT_OK && result != GNOME_KEYRING_RESULT_NO_MATCH) {
0278         qWarning() << "GnomeKeyringPasswordBackend::initialize Cannot read items from keyring!";
0279         return;
0280     }
0281 
0282     bool migrate = false;
0283     if (result == GNOME_KEYRING_RESULT_NO_MATCH) {
0284         result = gnome_keyring_find_itemsv_sync(GNOME_KEYRING_ITEM_GENERIC_SECRET, &found,
0285                  "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, "QupZilla",
0286                  NULL);
0287 
0288         if (result != GNOME_KEYRING_RESULT_OK && result != GNOME_KEYRING_RESULT_NO_MATCH) {
0289             qWarning() << "GnomeKeyringPasswordBackend::initialize Cannot read items from keyring!";
0290             return;
0291         }
0292 
0293         if (result == GNOME_KEYRING_RESULT_OK) {
0294             migrate = true;
0295         }
0296     }
0297 
0298     GList* tmp = found;
0299 
0300     while (tmp) {
0301         GnomeKeyringFound* item = (GnomeKeyringFound*) tmp->data;
0302         m_allEntries.append(createEntry(item));
0303         tmp = tmp->next;
0304     }
0305 
0306     gnome_keyring_found_list_free(found);
0307 
0308     if (migrate) {
0309         for (PasswordEntry &entry : m_allEntries) {
0310             storeEntry(entry);
0311         }
0312     }
0313 
0314     m_loaded = true;
0315 }