1 /* ============================================================
2 * Falkon - Qt web browser
3 * Copyright (C) 2013-2018 David Rosca <nowrep@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "passwordmanager.h"
19 #include "passwordbackends/passwordbackend.h"
20 #include "passwordbackends/databasepasswordbackend.h"
21 #include "passwordbackends/databaseencryptedpasswordbackend.h"
22 #include "settings.h"
23 
24 #include <QVector>
25 #include <QDataStream>
26 
27 static const int passwordEntryVersion = 2;
28 
operator <<(QDataStream & stream,const PasswordEntry & entry)29 QDataStream &operator <<(QDataStream &stream, const PasswordEntry &entry)
30 {
31     stream << passwordEntryVersion;
32     stream << entry.host;
33     stream << entry.id;
34     stream << entry.username;
35     stream << entry.password;
36     stream << entry.data;
37     stream << entry.updated;
38 
39     return stream;
40 }
41 
operator >>(QDataStream & stream,PasswordEntry & entry)42 QDataStream &operator >>(QDataStream &stream, PasswordEntry &entry)
43 {
44     int version;
45     stream >> version;
46 
47     if (version != passwordEntryVersion) {
48         return stream;
49     }
50 
51     stream >> entry.host;
52     stream >> entry.id;
53     stream >> entry.username;
54     stream >> entry.password;
55     stream >> entry.data;
56     stream >> entry.updated;
57 
58     return stream;
59 }
60 
PasswordManager(QObject * parent)61 PasswordManager::PasswordManager(QObject* parent)
62     : QObject(parent)
63     , m_loaded(false)
64     , m_backend(nullptr)
65     , m_databaseBackend(new DatabasePasswordBackend)
66     , m_databaseEncryptedBackend(new DatabaseEncryptedPasswordBackend)
67 {
68     m_backends[QSL("database")] = m_databaseBackend;
69     m_backends[QSL("database-encrypted")] = m_databaseEncryptedBackend;
70 }
71 
loadSettings()72 void PasswordManager::loadSettings()
73 {
74     Settings settings;
75     settings.beginGroup(QSL("PasswordManager"));
76     QString backendId = settings.value(QSL("Backend"), QSL("database")).toString();
77     settings.endGroup();
78 
79     if (m_backend) {
80         m_backend->setActive(false);
81     }
82     m_backend = m_backends[m_backends.contains(backendId) ? backendId : QSL("database")];
83     m_backend->setActive(true);
84 }
85 
getUsernames(const QUrl & url)86 QStringList PasswordManager::getUsernames(const QUrl &url)
87 {
88     ensureLoaded();
89     return m_backend->getUsernames(url);
90 }
91 
getEntries(const QUrl & url)92 QVector<PasswordEntry> PasswordManager::getEntries(const QUrl &url)
93 {
94     ensureLoaded();
95     return m_backend->getEntries(url);
96 }
97 
getAllEntries()98 QVector<PasswordEntry> PasswordManager::getAllEntries()
99 {
100     ensureLoaded();
101     return m_backend->getAllEntries();
102 }
103 
addEntry(const PasswordEntry & entry)104 void PasswordManager::addEntry(const PasswordEntry &entry)
105 {
106     ensureLoaded();
107     m_backend->addEntry(entry);
108 }
109 
updateEntry(const PasswordEntry & entry)110 bool PasswordManager::updateEntry(const PasswordEntry &entry)
111 {
112     ensureLoaded();
113     return m_backend->updateEntry(entry);
114 }
115 
updateLastUsed(PasswordEntry & entry)116 void PasswordManager::updateLastUsed(PasswordEntry &entry)
117 {
118     ensureLoaded();
119     m_backend->updateLastUsed(entry);
120 }
121 
removeEntry(const PasswordEntry & entry)122 void PasswordManager::removeEntry(const PasswordEntry &entry)
123 {
124     ensureLoaded();
125     m_backend->removeEntry(entry);
126 }
127 
removeAllEntries()128 void PasswordManager::removeAllEntries()
129 {
130     ensureLoaded();
131     m_backend->removeAll();
132 }
133 
availableBackends()134 QHash<QString, PasswordBackend*> PasswordManager::availableBackends()
135 {
136     ensureLoaded();
137     return m_backends;
138 }
139 
activeBackend()140 PasswordBackend* PasswordManager::activeBackend()
141 {
142     ensureLoaded();
143     return m_backend;
144 }
145 
switchBackend(const QString & backendID)146 void PasswordManager::switchBackend(const QString &backendID)
147 {
148     PasswordBackend* backend = m_backends.value(backendID);
149 
150     if (!backend) {
151         return;
152     }
153 
154     if (m_backend) {
155         m_backend->setActive(false);
156     }
157 
158     m_backend = backend;
159     m_backend->setActive(true);
160 
161     Settings settings;
162     settings.beginGroup(QSL("PasswordManager"));
163     settings.setValue(QSL("Backend"), backendID);
164     settings.endGroup();
165 
166     emit passwordBackendChanged();
167 }
168 
registerBackend(const QString & id,PasswordBackend * backend)169 bool PasswordManager::registerBackend(const QString &id, PasswordBackend* backend)
170 {
171     if (m_backends.contains(id)) {
172         return false;
173     }
174 
175     m_backends[id] = backend;
176     return true;
177 }
178 
unregisterBackend(PasswordBackend * backend)179 void PasswordManager::unregisterBackend(PasswordBackend* backend)
180 {
181     const QString key = m_backends.key(backend);
182     m_backends.remove(key);
183 
184     if (m_backend == backend) {
185         m_backend = m_databaseBackend;
186     }
187 }
188 
createHost(const QUrl & url)189 QString PasswordManager::createHost(const QUrl &url)
190 {
191     QString host = url.host();
192 
193     if (host.isEmpty()) {
194         host = url.toString();
195     }
196 
197     if (url.port() != -1) {
198         host.append(QLatin1Char(':'));
199         host.append(QString::number(url.port()));
200     }
201 
202     return host;
203 }
204 
urlEncodePassword(const QString & password)205 QByteArray PasswordManager::urlEncodePassword(const QString &password)
206 {
207     // Exclude space to properly decode to +
208     QByteArray encodedPass = QUrl::toPercentEncoding(password, " ");
209     encodedPass.replace(' ', '+'); // space has to be encoded to +
210     encodedPass.replace('~', "%7E"); // ~ is unreserved char, needs to be manually encoded
211     return encodedPass;
212 }
213 
ensureLoaded()214 void PasswordManager::ensureLoaded()
215 {
216     if (!m_loaded) {
217         loadSettings();
218         m_loaded = true;
219     }
220 }
221 
~PasswordManager()222 PasswordManager::~PasswordManager()
223 {
224     delete m_databaseBackend;
225     delete m_databaseEncryptedBackend;
226 }
227