File indexing completed on 2024-05-12 04:57:55

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2010-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 "autofill.h"
0019 #include "browserwindow.h"
0020 #include "webpage.h"
0021 #include "sqldatabase.h"
0022 #include "popupwebview.h"
0023 #include "mainapplication.h"
0024 #include "autofillnotification.h"
0025 #include "settings.h"
0026 #include "passwordmanager.h"
0027 #include "qztools.h"
0028 #include "scripts.h"
0029 #include "webpage.h"
0030 
0031 #include <QXmlStreamWriter>
0032 #include <QXmlStreamReader>
0033 #include <QWebEngineProfile>
0034 #include <QWebEngineScriptCollection>
0035 #include <QUrlQuery>
0036 
0037 AutoFill::AutoFill(QObject* parent)
0038     : QObject(parent)
0039     , m_manager(new PasswordManager(this))
0040 {
0041     loadSettings();
0042 
0043     // Setup AutoFill userscript
0044     QWebEngineScript script;
0045     script.setName(QSL("_falkon_autofill"));
0046     script.setInjectionPoint(QWebEngineScript::DocumentReady);
0047     script.setWorldId(WebPage::SafeJsWorld);
0048     script.setRunsOnSubFrames(true);
0049     script.setSourceCode(Scripts::setupFormObserver());
0050     mApp->webProfile()->scripts()->insert(script);
0051 }
0052 
0053 PasswordManager* AutoFill::passwordManager() const
0054 {
0055     return m_manager;
0056 }
0057 
0058 void AutoFill::loadSettings()
0059 {
0060     Settings settings;
0061     settings.beginGroup(QStringLiteral("Web-Browser-Settings"));
0062     m_isStoring = settings.value(QStringLiteral("SavePasswordsOnSites"), true).toBool();
0063     m_isAutoComplete = settings.value(QStringLiteral("AutoCompletePasswords"), true).toBool();
0064     settings.endGroup();
0065 }
0066 
0067 bool AutoFill::isStored(const QUrl &url)
0068 {
0069     if (!isStoringEnabled(url)) {
0070         return false;
0071     }
0072 
0073     return !m_manager->getUsernames(url).isEmpty();
0074 }
0075 
0076 bool AutoFill::isStoringEnabled(const QUrl &url)
0077 {
0078     if (!m_isStoring) {
0079         return false;
0080     }
0081 
0082     QString server = url.host();
0083     if (server.isEmpty()) {
0084         server = url.toString();
0085     }
0086 
0087     QSqlQuery query(SqlDatabase::instance()->database());
0088     query.prepare(QStringLiteral("SELECT count(id) FROM autofill_exceptions WHERE server=?"));
0089     query.addBindValue(server);
0090     query.exec();
0091 
0092     if (!query.next()) {
0093         return false;
0094     }
0095 
0096     return query.value(0).toInt() <= 0;
0097 }
0098 
0099 void AutoFill::blockStoringforUrl(const QUrl &url)
0100 {
0101     QString server = url.host();
0102     if (server.isEmpty()) {
0103         server = url.toString();
0104     }
0105 
0106     QSqlQuery query(SqlDatabase::instance()->database());
0107     query.prepare(QStringLiteral("INSERT INTO autofill_exceptions (server) VALUES (?)"));
0108     query.addBindValue(server);
0109     query.exec();
0110 }
0111 
0112 QVector<PasswordEntry> AutoFill::getFormData(const QUrl &url)
0113 {
0114     return m_manager->getEntries(url);
0115 }
0116 
0117 QVector<PasswordEntry> AutoFill::getAllFormData()
0118 {
0119     return m_manager->getAllEntries();
0120 }
0121 
0122 void AutoFill::updateLastUsed(PasswordEntry &data)
0123 {
0124     m_manager->updateLastUsed(data);
0125 }
0126 
0127 // HTTP Authorization
0128 void AutoFill::addEntry(const QUrl &url, const QString &name, const QString &pass)
0129 {
0130     PasswordEntry entry;
0131     entry.host = PasswordManager::createHost(url);
0132     entry.username = name;
0133     entry.password = pass;
0134 
0135     m_manager->addEntry(entry);
0136 }
0137 
0138 // WEB Form
0139 void AutoFill::addEntry(const QUrl &url, const PageFormData &formData)
0140 {
0141     PasswordEntry entry;
0142     entry.host = PasswordManager::createHost(url);
0143     entry.username = formData.username;
0144     entry.password = formData.password;
0145     entry.data = formData.postData;
0146 
0147     m_manager->addEntry(entry);
0148 }
0149 
0150 // HTTP Authorization
0151 void AutoFill::updateEntry(const QUrl &url, const QString &name, const QString &pass)
0152 {
0153     PasswordEntry entry;
0154     entry.host = PasswordManager::createHost(url);
0155     entry.username = name;
0156     entry.password = pass;
0157 
0158     m_manager->updateEntry(entry);
0159 }
0160 
0161 // WEB Form
0162 bool AutoFill::updateEntry(const PasswordEntry &entry)
0163 {
0164     return m_manager->updateEntry(entry);
0165 }
0166 
0167 void AutoFill::removeEntry(const PasswordEntry &entry)
0168 {
0169     m_manager->removeEntry(entry);
0170 }
0171 
0172 void AutoFill::removeAllEntries()
0173 {
0174     m_manager->removeAllEntries();
0175 }
0176 
0177 void AutoFill::saveForm(WebPage *page, const QUrl &frameUrl, const PageFormData &formData)
0178 {
0179     // Don't save in private browsing
0180     if (mApp->isPrivate() || !page)
0181         return;
0182 
0183     if (!isStoringEnabled(frameUrl))
0184         return;
0185 
0186     PasswordEntry updateData;
0187 
0188     if (isStored(frameUrl)) {
0189         const QVector<PasswordEntry> &list = getFormData(frameUrl);
0190 
0191         for (const PasswordEntry &data : list) {
0192             if (data.username == formData.username) {
0193                 updateData = data;
0194                 updateLastUsed(updateData);
0195 
0196                 if (data.password == formData.password) {
0197                     updateData.password.clear();
0198                     return;
0199                 }
0200 
0201                 updateData.username = formData.username;
0202                 updateData.password = formData.password;
0203                 updateData.data = formData.postData;
0204                 break;
0205             }
0206         }
0207     }
0208 
0209     if (m_lastNotification && m_lastNotificationPage == page) {
0210         m_lastNotification->close();
0211     }
0212 
0213     auto* aWidget = new AutoFillNotification(frameUrl, formData, updateData);
0214     page->view()->addNotification(aWidget);
0215 
0216     m_lastNotification = aWidget;
0217     m_lastNotificationPage = page;
0218 }
0219 
0220 // Returns all saved passwords on this page
0221 QStringList AutoFill::completePage(WebPage *page, const QUrl &frameUrl)
0222 {
0223     QStringList usernames;
0224 
0225     if (!page || !isStored(frameUrl))
0226         return usernames;
0227 
0228     if (!m_isAutoComplete) {
0229         return m_manager->getUsernames(frameUrl);
0230     }
0231 
0232     const auto entries = getFormData(frameUrl);
0233 
0234     if (!entries.isEmpty()) {
0235         PasswordEntry entry = entries.at(0);
0236         updateLastUsed(entry);
0237         page->runJavaScript(Scripts::completeFormData(entry.data), WebPage::SafeJsWorld);
0238     }
0239 
0240     usernames.reserve(entries.size());
0241     for (const PasswordEntry &entry : entries) {
0242         usernames.append(entry.username);
0243     }
0244     return usernames;
0245 }
0246 
0247 QByteArray AutoFill::exportPasswords()
0248 {
0249     QByteArray output;
0250 
0251     QXmlStreamWriter stream(&output);
0252     stream.setAutoFormatting(true);
0253 
0254     stream.writeStartDocument();
0255     stream.writeStartElement(QStringLiteral("passwords"));
0256     stream.writeAttribute(QStringLiteral("version"), QStringLiteral("1.0"));
0257 
0258     const QVector<PasswordEntry> entries = m_manager->getAllEntries();
0259 
0260     for (const PasswordEntry &entry : entries) {
0261         stream.writeStartElement(QStringLiteral("entry"));
0262         stream.writeTextElement(QStringLiteral("server"), entry.host);
0263         stream.writeTextElement(QStringLiteral("username"), entry.username);
0264         stream.writeTextElement(QStringLiteral("password"), entry.password);
0265         stream.writeTextElement(QStringLiteral("data"), entry.data);
0266         stream.writeEndElement();
0267     }
0268 
0269     QSqlQuery query(SqlDatabase::instance()->database());
0270     query.prepare(QStringLiteral("SELECT server FROM autofill_exceptions"));
0271     query.exec();
0272     while (query.next()) {
0273         stream.writeStartElement(QStringLiteral("exception"));
0274         stream.writeTextElement(QStringLiteral("server"), query.value(0).toString());
0275         stream.writeEndElement();
0276     }
0277 
0278     stream.writeEndElement();
0279     stream.writeEndDocument();
0280 
0281     return output;
0282 }
0283 
0284 bool AutoFill::importPasswords(const QByteArray &data)
0285 {
0286     QSqlDatabase db = QSqlDatabase::database();
0287     db.transaction();
0288 
0289     QXmlStreamReader xml(data);
0290 
0291     while (!xml.atEnd()) {
0292         xml.readNext();
0293 
0294         if (xml.isStartElement()) {
0295             if (xml.name() == QLatin1String("entry")) {
0296                 PasswordEntry entry;
0297 
0298                 while (xml.readNext()) {
0299                     if (xml.name() == QLatin1String("server")) {
0300                         entry.host = xml.readElementText();
0301                     }
0302                     else if (xml.name() == QLatin1String("username")) {
0303                         entry.username = xml.readElementText();
0304                     }
0305                     else if (xml.name() == QLatin1String("password")) {
0306                         entry.password = xml.readElementText();
0307                     }
0308                     else if (xml.name() == QLatin1String("data")) {
0309                         entry.data = xml.readElementText().toUtf8();
0310                     }
0311 
0312                     if (xml.isEndElement() && xml.name() == QLatin1String("entry")) {
0313                         break;
0314                     }
0315                 }
0316 
0317                 if (entry.isValid()) {
0318                     bool containsEntry = false;
0319 
0320                     const auto entries = m_manager->getEntries(QUrl(entry.host));
0321                     for (const PasswordEntry &e : entries) {
0322                         if (e.username == entry.username) {
0323                             containsEntry = true;
0324                             break;
0325                         }
0326                     }
0327 
0328                     if (!containsEntry) {
0329                         m_manager->addEntry(entry);
0330                     }
0331                 }
0332             }
0333             else if (xml.name() == QLatin1String("exception")) {
0334                 QString server;
0335 
0336                 while (xml.readNext()) {
0337                     if (xml.name() == QLatin1String("server")) {
0338                         server = xml.readElementText();
0339                     }
0340 
0341                     if (xml.isEndElement() && xml.name() == QLatin1String("exception")) {
0342                         break;
0343                     }
0344                 }
0345 
0346                 if (!server.isEmpty()) {
0347                     QSqlQuery query(SqlDatabase::instance()->database());
0348                     query.prepare(QStringLiteral("SELECT id FROM autofill_exceptions WHERE server=?"));
0349                     query.addBindValue(server);
0350                     query.exec();
0351 
0352                     if (!query.next()) {
0353                         query.prepare(QStringLiteral("INSERT INTO autofill_exceptions (server) VALUES (?)"));
0354                         query.addBindValue(server);
0355                         query.exec();
0356                     }
0357                 }
0358             }
0359         }
0360     }
0361 
0362     db.commit();
0363 
0364     return !xml.hasError();
0365 }