Falkon Develop
Cross-platform Qt-based web browser
autofill.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-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 "autofill.h"
19#include "browserwindow.h"
20#include "webpage.h"
21#include "sqldatabase.h"
22#include "popupwebview.h"
23#include "mainapplication.h"
25#include "settings.h"
26#include "passwordmanager.h"
27#include "qztools.h"
28#include "scripts.h"
29#include "webpage.h"
30
31#include <QXmlStreamWriter>
32#include <QXmlStreamReader>
33#include <QWebEngineProfile>
34#include <QWebEngineScriptCollection>
35#include <QUrlQuery>
36
37AutoFill::AutoFill(QObject* parent)
38 : QObject(parent)
39 , m_manager(new PasswordManager(this))
40{
42
43 // Setup AutoFill userscript
44 QWebEngineScript script;
45 script.setName(QSL("_falkon_autofill"));
46 script.setInjectionPoint(QWebEngineScript::DocumentReady);
47 script.setWorldId(WebPage::SafeJsWorld);
48 script.setRunsOnSubFrames(true);
49 script.setSourceCode(Scripts::setupFormObserver());
50 mApp->webProfile()->scripts()->insert(script);
51}
52
54{
55 return m_manager;
56}
57
59{
60 Settings settings;
61 settings.beginGroup(QStringLiteral("Web-Browser-Settings"));
62 m_isStoring = settings.value(QStringLiteral("SavePasswordsOnSites"), true).toBool();
63 m_isAutoComplete = settings.value(QStringLiteral("AutoCompletePasswords"), true).toBool();
64 settings.endGroup();
65}
66
67bool AutoFill::isStored(const QUrl &url)
68{
69 if (!isStoringEnabled(url)) {
70 return false;
71 }
72
73 return !m_manager->getUsernames(url).isEmpty();
74}
75
76bool AutoFill::isStoringEnabled(const QUrl &url)
77{
78 if (!m_isStoring) {
79 return false;
80 }
81
82 QString server = url.host();
83 if (server.isEmpty()) {
84 server = url.toString();
85 }
86
87 QSqlQuery query(SqlDatabase::instance()->database());
88 query.prepare(QStringLiteral("SELECT count(id) FROM autofill_exceptions WHERE server=?"));
89 query.addBindValue(server);
90 query.exec();
91
92 if (!query.next()) {
93 return false;
94 }
95
96 return query.value(0).toInt() <= 0;
97}
98
99void AutoFill::blockStoringforUrl(const QUrl &url)
100{
101 QString server = url.host();
102 if (server.isEmpty()) {
103 server = url.toString();
104 }
105
106 QSqlQuery query(SqlDatabase::instance()->database());
107 query.prepare(QStringLiteral("INSERT INTO autofill_exceptions (server) VALUES (?)"));
108 query.addBindValue(server);
109 query.exec();
110}
111
112QVector<PasswordEntry> AutoFill::getFormData(const QUrl &url)
113{
114 return m_manager->getEntries(url);
115}
116
117QVector<PasswordEntry> AutoFill::getAllFormData()
118{
119 return m_manager->getAllEntries();
120}
121
123{
124 m_manager->updateLastUsed(data);
125}
126
127// HTTP Authorization
128void AutoFill::addEntry(const QUrl &url, const QString &name, const QString &pass)
129{
130 PasswordEntry entry;
132 entry.username = name;
133 entry.password = pass;
134
135 m_manager->addEntry(entry);
136}
137
138// WEB Form
139void AutoFill::addEntry(const QUrl &url, const PageFormData &formData)
140{
141 PasswordEntry entry;
143 entry.username = formData.username;
144 entry.password = formData.password;
145 entry.data = formData.postData;
146
147 m_manager->addEntry(entry);
148}
149
150// HTTP Authorization
151void AutoFill::updateEntry(const QUrl &url, const QString &name, const QString &pass)
152{
153 PasswordEntry entry;
155 entry.username = name;
156 entry.password = pass;
157
158 m_manager->updateEntry(entry);
159}
160
161// WEB Form
163{
164 return m_manager->updateEntry(entry);
165}
166
168{
169 m_manager->removeEntry(entry);
170}
171
173{
174 m_manager->removeAllEntries();
175}
176
177void AutoFill::saveForm(WebPage *page, const QUrl &frameUrl, const PageFormData &formData)
178{
179 // Don't save in private browsing
180 if (mApp->isPrivate() || !page)
181 return;
182
183 if (!isStoringEnabled(frameUrl))
184 return;
185
186 PasswordEntry updateData;
187
188 if (isStored(frameUrl)) {
189 const QVector<PasswordEntry> &list = getFormData(frameUrl);
190
191 for (const PasswordEntry &data : list) {
192 if (data.username == formData.username) {
193 updateData = data;
194 updateLastUsed(updateData);
195
196 if (data.password == formData.password) {
197 updateData.password.clear();
198 return;
199 }
200
201 updateData.username = formData.username;
202 updateData.password = formData.password;
203 updateData.data = formData.postData;
204 break;
205 }
206 }
207 }
208
209 if (m_lastNotification && m_lastNotificationPage == page) {
210 m_lastNotification->close();
211 }
212
213 auto* aWidget = new AutoFillNotification(frameUrl, formData, updateData);
214 page->view()->addNotification(aWidget);
215
216 m_lastNotification = aWidget;
217 m_lastNotificationPage = page;
218}
219
220// Returns all saved passwords on this page
221QStringList AutoFill::completePage(WebPage *page, const QUrl &frameUrl)
222{
223 QStringList usernames;
224
225 if (!page || !isStored(frameUrl))
226 return usernames;
227
228 if (!m_isAutoComplete) {
229 return m_manager->getUsernames(frameUrl);
230 }
231
232 const auto entries = getFormData(frameUrl);
233
234 if (!entries.isEmpty()) {
235 PasswordEntry entry = entries.at(0);
236 updateLastUsed(entry);
237 page->runJavaScript(Scripts::completeFormData(entry.data), WebPage::SafeJsWorld);
238 }
239
240 usernames.reserve(entries.size());
241 for (const PasswordEntry &entry : entries) {
242 usernames.append(entry.username);
243 }
244 return usernames;
245}
246
248{
249 QByteArray output;
250
251 QXmlStreamWriter stream(&output);
252 stream.setAutoFormatting(true);
253
254 stream.writeStartDocument();
255 stream.writeStartElement(QStringLiteral("passwords"));
256 stream.writeAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
257
258 const QVector<PasswordEntry> entries = m_manager->getAllEntries();
259
260 for (const PasswordEntry &entry : entries) {
261 stream.writeStartElement(QStringLiteral("entry"));
262 stream.writeTextElement(QStringLiteral("server"), entry.host);
263 stream.writeTextElement(QStringLiteral("username"), entry.username);
264 stream.writeTextElement(QStringLiteral("password"), entry.password);
265 stream.writeTextElement(QStringLiteral("data"), entry.data);
266 stream.writeEndElement();
267 }
268
269 QSqlQuery query(SqlDatabase::instance()->database());
270 query.prepare(QStringLiteral("SELECT server FROM autofill_exceptions"));
271 query.exec();
272 while (query.next()) {
273 stream.writeStartElement(QStringLiteral("exception"));
274 stream.writeTextElement(QStringLiteral("server"), query.value(0).toString());
275 stream.writeEndElement();
276 }
277
278 stream.writeEndElement();
279 stream.writeEndDocument();
280
281 return output;
282}
283
284bool AutoFill::importPasswords(const QByteArray &data)
285{
286 QSqlDatabase db = QSqlDatabase::database();
287 db.transaction();
288
289 QXmlStreamReader xml(data);
290
291 while (!xml.atEnd()) {
292 xml.readNext();
293
294 if (xml.isStartElement()) {
295 if (xml.name() == QLatin1String("entry")) {
296 PasswordEntry entry;
297
298 while (xml.readNext()) {
299 if (xml.name() == QLatin1String("server")) {
300 entry.host = xml.readElementText();
301 }
302 else if (xml.name() == QLatin1String("username")) {
303 entry.username = xml.readElementText();
304 }
305 else if (xml.name() == QLatin1String("password")) {
306 entry.password = xml.readElementText();
307 }
308 else if (xml.name() == QLatin1String("data")) {
309 entry.data = xml.readElementText().toUtf8();
310 }
311
312 if (xml.isEndElement() && xml.name() == QLatin1String("entry")) {
313 break;
314 }
315 }
316
317 if (entry.isValid()) {
318 bool containsEntry = false;
319
320 const auto entries = m_manager->getEntries(QUrl(entry.host));
321 for (const PasswordEntry &e : entries) {
322 if (e.username == entry.username) {
323 containsEntry = true;
324 break;
325 }
326 }
327
328 if (!containsEntry) {
329 m_manager->addEntry(entry);
330 }
331 }
332 }
333 else if (xml.name() == QLatin1String("exception")) {
334 QString server;
335
336 while (xml.readNext()) {
337 if (xml.name() == QLatin1String("server")) {
338 server = xml.readElementText();
339 }
340
341 if (xml.isEndElement() && xml.name() == QLatin1String("exception")) {
342 break;
343 }
344 }
345
346 if (!server.isEmpty()) {
347 QSqlQuery query(SqlDatabase::instance()->database());
348 query.prepare(QStringLiteral("SELECT id FROM autofill_exceptions WHERE server=?"));
349 query.addBindValue(server);
350 query.exec();
351
352 if (!query.next()) {
353 query.prepare(QStringLiteral("INSERT INTO autofill_exceptions (server) VALUES (?)"));
354 query.addBindValue(server);
355 query.exec();
356 }
357 }
358 }
359 }
360 }
361
362 db.commit();
363
364 return !xml.hasError();
365}
void updateLastUsed(PasswordEntry &data)
Definition: autofill.cpp:122
QByteArray exportPasswords()
Definition: autofill.cpp:247
bool importPasswords(const QByteArray &data)
Definition: autofill.cpp:284
void addEntry(const QUrl &url, const QString &name, const QString &pass)
Definition: autofill.cpp:128
bool isStoringEnabled(const QUrl &url)
Definition: autofill.cpp:76
void saveForm(WebPage *page, const QUrl &frameUrl, const PageFormData &formData)
Definition: autofill.cpp:177
void updateEntry(const QUrl &url, const QString &name, const QString &pass)
Definition: autofill.cpp:151
QVector< PasswordEntry > getFormData(const QUrl &url)
Definition: autofill.cpp:112
void loadSettings()
Definition: autofill.cpp:58
void removeEntry(const PasswordEntry &entry)
Definition: autofill.cpp:167
AutoFill(QObject *parent=nullptr)
Definition: autofill.cpp:37
bool isStored(const QUrl &url)
Definition: autofill.cpp:67
QVector< PasswordEntry > getAllFormData()
Definition: autofill.cpp:117
void removeAllEntries()
Definition: autofill.cpp:172
void blockStoringforUrl(const QUrl &url)
Definition: autofill.cpp:99
PasswordManager * passwordManager() const
Definition: autofill.cpp:53
QStringList completePage(WebPage *page, const QUrl &frameUrl)
Definition: autofill.cpp:221
bool updateEntry(const PasswordEntry &entry)
static QString createHost(const QUrl &url)
void removeEntry(const PasswordEntry &entry)
QStringList getUsernames(const QUrl &url)
QVector< PasswordEntry > getEntries(const QUrl &url)
void updateLastUsed(PasswordEntry &entry)
QVector< PasswordEntry > getAllEntries()
void addEntry(const PasswordEntry &entry)
static QString completeFormData(const QByteArray &data)
Definition: scripts.cpp:250
static QString setupFormObserver()
Definition: scripts.cpp:74
void beginGroup(const QString &prefix)
Definition: settings.cpp:79
void endGroup()
Definition: settings.cpp:84
QVariant value(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:74
static SqlDatabase * instance()
WebView * view() const
Definition: webpage.cpp:140
@ SafeJsWorld
Definition: webpage.h:45
void addNotification(QWidget *notif)
Definition: webview.cpp:286
#define mApp
#define QSL(x)
Definition: qzcommon.h:40
QString password
Definition: autofill.h:38
QByteArray postData
Definition: autofill.h:39
QString username
Definition: autofill.h:37
QString password
QString host
QByteArray data
bool isValid() const
QString username