Falkon Develop
Cross-platform Qt-based web browser
autofillmanager.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2017 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 "autofillmanager.h"
19#include "ui_autofillmanager.h"
20#include "autofill.h"
21#include "passwordmanager.h"
23#include "mainapplication.h"
24#include "settings.h"
25#include "qztools.h"
26#include "sqldatabase.h"
27
28#include <QUrl>
29#include <QMenu>
30#include <QTimer>
31#include <QMessageBox>
32#include <QInputDialog>
33#include <QFileDialog>
34#include <QClipboard>
35
37 : QWidget(parent)
38 , ui(new Ui::AutoFillManager)
39 , m_passwordManager(mApp->autoFill()->passwordManager())
40 , m_passwordsShown(false)
41{
42 ui->setupUi(this);
43 if (isRightToLeft()) {
44 ui->treePass->headerItem()->setTextAlignment(0, Qt::AlignRight | Qt::AlignVCenter);
45 ui->treePass->headerItem()->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
46 ui->treePass->headerItem()->setTextAlignment(2, Qt::AlignRight | Qt::AlignVCenter);
47 ui->treePass->setLayoutDirection(Qt::LeftToRight);
48 ui->treeExcept->setLayoutDirection(Qt::LeftToRight);
49 }
50
51 connect(ui->removePass, &QAbstractButton::clicked, this, &AutoFillManager::removePass);
52 connect(ui->removeAllPass, &QAbstractButton::clicked, this, &AutoFillManager::removeAllPass);
53 connect(ui->editPass, &QAbstractButton::clicked, this, &AutoFillManager::editPass);
54 connect(ui->showPasswords, &QAbstractButton::clicked, this, &AutoFillManager::showPasswords);
55 connect(ui->search, &QLineEdit::textChanged, ui->treePass, &TreeWidget::filterString);
56 connect(ui->changeBackend, &QAbstractButton::clicked, this, &AutoFillManager::changePasswordBackend);
57 connect(ui->backendOptions, &QAbstractButton::clicked, this, &AutoFillManager::showBackendOptions);
58 connect(m_passwordManager, &PasswordManager::passwordBackendChanged, this, &AutoFillManager::currentPasswordBackendChanged);
59
60 connect(ui->removeExcept, &QAbstractButton::clicked, this, &AutoFillManager::removeExcept);
61 connect(ui->removeAllExcept, &QAbstractButton::clicked, this, &AutoFillManager::removeAllExcept);
62
63 ui->treePass->setContextMenuPolicy(Qt::CustomContextMenu);
64 connect(ui->treePass, &TreeWidget::customContextMenuRequested, this, &AutoFillManager::passwordContextMenu);
65
66 auto* menu = new QMenu(this);
67 menu->addAction(tr("Import Passwords from File..."), this, &AutoFillManager::importPasswords);
68 menu->addAction(tr("Export Passwords to File..."), this, &AutoFillManager::exportPasswords);
69 ui->importExport->setMenu(menu);
70 ui->search->setPlaceholderText(tr("Search"));
71
72 // Password backends
73 ui->currentBackend->setText(QSL("<b>%1</b>").arg(m_passwordManager->activeBackend()->name()));
74 ui->backendOptions->setVisible(m_passwordManager->activeBackend()->hasSettings());
75
76 // Load passwords
77 QTimer::singleShot(0, this, &AutoFillManager::loadPasswords);
78}
79
80void AutoFillManager::loadPasswords()
81{
82 ui->showPasswords->setText(tr("Show Passwords"));
83 m_passwordsShown = false;
84
85 const QVector<PasswordEntry> allEntries = mApp->autoFill()->getAllFormData();
86
87 ui->treePass->clear();
88 for (const PasswordEntry &entry : allEntries) {
89 auto* item = new QTreeWidgetItem(ui->treePass);
90 item->setText(0, entry.host);
91 item->setText(1, entry.username);
92 item->setText(2, QSL("*****"));
93
94 QVariant v;
95 v.setValue(entry);
96 item->setData(0, Qt::UserRole + 10, v);
97 ui->treePass->addTopLevelItem(item);
98 }
99
100 QSqlQuery query(SqlDatabase::instance()->database());
101 query.exec(QSL("SELECT server, id FROM autofill_exceptions"));
102 ui->treeExcept->clear();
103 while (query.next()) {
104 auto* item = new QTreeWidgetItem(ui->treeExcept);
105 item->setText(0, query.value(0).toString());
106 item->setData(0, Qt::UserRole + 10, query.value(1).toString());
107 ui->treeExcept->addTopLevelItem(item);
108 }
109
110 ui->treePass->sortByColumn(-1, Qt::AscendingOrder);
111 ui->treeExcept->sortByColumn(-1, Qt::AscendingOrder);
112}
113
114void AutoFillManager::changePasswordBackend()
115{
116 QHash<QString, PasswordBackend*> backends = m_passwordManager->availableBackends();
117 QStringList items;
118
119 int current = 0;
120
121 QHashIterator<QString, PasswordBackend*> i(backends);
122 while (i.hasNext()) {
123 i.next();
124 if (i.value() == m_passwordManager->activeBackend()) {
125 current = items.size();
126 }
127 items << i.value()->name();
128 }
129
130 QString item = QInputDialog::getItem(this, tr("Change backend..."), tr("Change backend:"), items, current, false);
131
132 // Switch backends
133 if (!item.isEmpty()) {
134 PasswordBackend* backend = nullptr;
135
136 QHashIterator<QString, PasswordBackend*> i(backends);
137 while (i.hasNext()) {
138 i.next();
139 if (i.value()->name() == item) {
140 backend = i.value();
141 break;
142 }
143 }
144
145 if (backend) {
146 m_passwordManager->switchBackend(backends.key(backend));
147 }
148 }
149}
150
151void AutoFillManager::showBackendOptions()
152{
153 PasswordBackend* backend = m_passwordManager->activeBackend();
154
155 if (backend->hasSettings()) {
156 backend->showSettings(this);
157 }
158}
159
160void AutoFillManager::showPasswords()
161{
162 if (m_passwordsShown) {
163 for (int i = 0; i < ui->treePass->topLevelItemCount(); i++) {
164 QTreeWidgetItem* item = ui->treePass->topLevelItem(i);
165 if (!item) {
166 continue;
167 }
168 item->setText(2, QSL("*****"));
169 }
170
171 ui->showPasswords->setText(tr("Show Passwords"));
172 m_passwordsShown = false;
173
174 return;
175 }
176
177 m_passwordsShown = true;
178
179 int result = QMessageBox::question(this, tr("Show Passwords"), tr("Are you sure that you want to show all passwords?"),
180 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
181 if (result != QMessageBox::Yes) {
182 return;
183 }
184
185 for (int i = 0; i < ui->treePass->topLevelItemCount(); i++) {
186 QTreeWidgetItem* item = ui->treePass->topLevelItem(i);
187 if (!item) {
188 continue;
189 }
190
191 item->setText(2, item->data(0, Qt::UserRole + 10).value<PasswordEntry>().password);
192 }
193
194 ui->showPasswords->setText(tr("Hide Passwords"));
195}
196
197void AutoFillManager::copyPassword()
198{
199 QTreeWidgetItem* curItem = ui->treePass->currentItem();
200 if (!curItem)
201 return;
202
203 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>();
204 QApplication::clipboard()->setText(entry.password);
205}
206
207void AutoFillManager::copyUsername()
208{
209 QTreeWidgetItem* curItem = ui->treePass->currentItem();
210 if (!curItem)
211 return;
212
213 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>();
214 QApplication::clipboard()->setText(entry.username);
215}
216
217void AutoFillManager::removePass()
218{
219 QTreeWidgetItem* curItem = ui->treePass->currentItem();
220 if (!curItem) {
221 return;
222 }
223
224 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>();
225 mApp->autoFill()->removeEntry(entry);
226
227 delete curItem;
228}
229
230void AutoFillManager::removeAllPass()
231{
232 QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Confirmation"),
233 tr("Are you sure you want to delete all passwords on your computer?"), QMessageBox::Yes | QMessageBox::No);
234 if (button != QMessageBox::Yes) {
235 return;
236 }
237
238 mApp->autoFill()->removeAllEntries();
239 ui->treePass->clear();
240}
241
242void AutoFillManager::editPass()
243{
244 QTreeWidgetItem* curItem = ui->treePass->currentItem();
245 if (!curItem) {
246 return;
247 }
248
249 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>();
250
251 bool ok;
252 QString text = QInputDialog::getText(this, tr("Edit password"), tr("Change password:"), QLineEdit::Normal, entry.password, &ok);
253
254 if (ok && !text.isEmpty() && text != entry.password) {
255 QByteArray oldPass = "=" + PasswordManager::urlEncodePassword(entry.password);
256 QByteArray newPass = "=" + PasswordManager::urlEncodePassword(text);
257 entry.data.replace(oldPass, newPass);
258 entry.password = text;
259
260 if (mApp->autoFill()->updateEntry(entry)) {
261 QVariant v;
262 v.setValue(entry);
263 curItem->setData(0, Qt::UserRole + 10, v);
264
265 if (m_passwordsShown) {
266 curItem->setText(2, text);
267 }
268 }
269 }
270}
271
272void AutoFillManager::removeExcept()
273{
274 QTreeWidgetItem* curItem = ui->treeExcept->currentItem();
275 if (!curItem) {
276 return;
277 }
278 QString id = curItem->data(0, Qt::UserRole + 10).toString();
279 QSqlQuery query(SqlDatabase::instance()->database());
280 query.prepare(QSL("DELETE FROM autofill_exceptions WHERE id=?"));
281 query.addBindValue(id);
282 query.exec();
283
284 delete curItem;
285}
286
287void AutoFillManager::removeAllExcept()
288{
289 QSqlQuery query(SqlDatabase::instance()->database());
290 query.exec(QSL("DELETE FROM autofill_exceptions"));
291
292 ui->treeExcept->clear();
293}
294
296{
297 ui->tabWidget->setCurrentIndex(1);
298}
299
300void AutoFillManager::importPasswords()
301{
302 m_fileName = QzTools::getOpenFileName(QSL("AutoFill-ImportPasswords"), this, tr("Choose file..."), QDir::homePath() + QSL("/passwords.xml"), QSL("*.xml"));
303
304 if (m_fileName.isEmpty()) {
305 return;
306 }
307
308 QTimer::singleShot(0, this, &AutoFillManager::slotImportPasswords);
309}
310
311void AutoFillManager::exportPasswords()
312{
313 m_fileName = QzTools::getSaveFileName(QSL("AutoFill-ExportPasswords"), this, tr("Choose file..."), QDir::homePath() + QSL("/passwords.xml"), QSL("*.xml"));
314
315 if (m_fileName.isEmpty()) {
316 return;
317 }
318
319 QTimer::singleShot(0, this, &AutoFillManager::slotExportPasswords);
320}
321
322void AutoFillManager::slotImportPasswords()
323{
324 QFile file(m_fileName);
325
326 if (!file.open(QFile::ReadOnly)) {
327 ui->importExportLabel->setText(tr("Cannot read file!"));
328 return;
329 }
330
331 QApplication::setOverrideCursor(Qt::WaitCursor);
332
333 bool status = mApp->autoFill()->importPasswords(file.readAll());
334 file.close();
335
336 ui->importExportLabel->setText(status ? tr("Successfully imported") : tr("Error while importing!"));
337 loadPasswords();
338
339 QApplication::restoreOverrideCursor();
340}
341
342void AutoFillManager::slotExportPasswords()
343{
344 QFile file(m_fileName);
345
346 if (!file.open(QFile::WriteOnly)) {
347 ui->importExportLabel->setText(tr("Cannot write to file!"));
348 return;
349 }
350
351 QApplication::setOverrideCursor(Qt::WaitCursor);
352
353 file.write(mApp->autoFill()->exportPasswords());
354 file.close();
355
356 ui->importExportLabel->setText(tr("Successfully exported"));
357
358 QApplication::restoreOverrideCursor();
359}
360
361void AutoFillManager::currentPasswordBackendChanged()
362{
363 ui->currentBackend->setText(QSL("<b>%1</b>").arg(m_passwordManager->activeBackend()->name()));
364 ui->backendOptions->setVisible(m_passwordManager->activeBackend()->hasSettings());
365
366 QTimer::singleShot(0, this, &AutoFillManager::loadPasswords);
367}
368
369void AutoFillManager::passwordContextMenu(const QPoint &pos)
370{
371 auto *menu = new QMenu;
372 menu->setAttribute(Qt::WA_DeleteOnClose);
373 menu->addAction(tr("Copy Username"), this, &AutoFillManager::copyUsername);
374 menu->addAction(tr("Copy Password"), this, &AutoFillManager::copyPassword);
375 menu->addSeparator();
376 menu->addAction(tr("Edit Password"), this, &AutoFillManager::editPass);
377 menu->popup(ui->treePass->viewport()->mapToGlobal(pos));
378}
379
381{
382 delete ui;
383}
AutoFillManager(QWidget *parent=nullptr)
virtual void showSettings(QWidget *parent)
virtual QString name() const =0
virtual bool hasSettings() const
void switchBackend(const QString &backendID)
static QByteArray urlEncodePassword(const QString &password)
void passwordBackendChanged()
PasswordBackend * activeBackend()
QHash< QString, PasswordBackend * > availableBackends()
static QString getSaveFileName(const QString &name, QWidget *parent=nullptr, const QString &caption=QString(), const QString &dir=QString(), const QString &filter=QString(), QString *selectedFilter=nullptr, QFileDialog::Options options=QFileDialog::Options())
Definition: qztools.cpp:709
static QString getOpenFileName(const QString &name, QWidget *parent=nullptr, const QString &caption=QString(), const QString &dir=QString(), const QString &filter=QString(), QString *selectedFilter=nullptr, QFileDialog::Options options=QFileDialog::Options())
Definition: qztools.cpp:659
static SqlDatabase * instance()
void filterString(const QString &string)
Definition: treewidget.cpp:104
#define mApp
i
Definition: i18n.py:23
#define QSL(x)
Definition: qzcommon.h:40
QString password
QByteArray data
QString username