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 }