File indexing completed on 2024-05-12 04:58:19
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2017 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 "autofillmanager.h" 0019 #include "ui_autofillmanager.h" 0020 #include "autofill.h" 0021 #include "passwordmanager.h" 0022 #include "passwordbackends/passwordbackend.h" 0023 #include "mainapplication.h" 0024 #include "settings.h" 0025 #include "qztools.h" 0026 #include "sqldatabase.h" 0027 0028 #include <QUrl> 0029 #include <QMenu> 0030 #include <QTimer> 0031 #include <QMessageBox> 0032 #include <QInputDialog> 0033 #include <QFileDialog> 0034 #include <QClipboard> 0035 0036 AutoFillManager::AutoFillManager(QWidget* parent) 0037 : QWidget(parent) 0038 , ui(new Ui::AutoFillManager) 0039 , m_passwordManager(mApp->autoFill()->passwordManager()) 0040 , m_passwordsShown(false) 0041 { 0042 ui->setupUi(this); 0043 if (isRightToLeft()) { 0044 ui->treePass->headerItem()->setTextAlignment(0, Qt::AlignRight | Qt::AlignVCenter); 0045 ui->treePass->headerItem()->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter); 0046 ui->treePass->headerItem()->setTextAlignment(2, Qt::AlignRight | Qt::AlignVCenter); 0047 ui->treePass->setLayoutDirection(Qt::LeftToRight); 0048 ui->treeExcept->setLayoutDirection(Qt::LeftToRight); 0049 } 0050 0051 connect(ui->removePass, &QAbstractButton::clicked, this, &AutoFillManager::removePass); 0052 connect(ui->removeAllPass, &QAbstractButton::clicked, this, &AutoFillManager::removeAllPass); 0053 connect(ui->editPass, &QAbstractButton::clicked, this, &AutoFillManager::editPass); 0054 connect(ui->showPasswords, &QAbstractButton::clicked, this, &AutoFillManager::showPasswords); 0055 connect(ui->search, &QLineEdit::textChanged, ui->treePass, &TreeWidget::filterString); 0056 connect(ui->changeBackend, &QAbstractButton::clicked, this, &AutoFillManager::changePasswordBackend); 0057 connect(ui->backendOptions, &QAbstractButton::clicked, this, &AutoFillManager::showBackendOptions); 0058 connect(m_passwordManager, &PasswordManager::passwordBackendChanged, this, &AutoFillManager::currentPasswordBackendChanged); 0059 0060 connect(ui->removeExcept, &QAbstractButton::clicked, this, &AutoFillManager::removeExcept); 0061 connect(ui->removeAllExcept, &QAbstractButton::clicked, this, &AutoFillManager::removeAllExcept); 0062 0063 ui->treePass->setContextMenuPolicy(Qt::CustomContextMenu); 0064 connect(ui->treePass, &TreeWidget::customContextMenuRequested, this, &AutoFillManager::passwordContextMenu); 0065 0066 auto* menu = new QMenu(this); 0067 menu->addAction(tr("Import Passwords from File..."), this, &AutoFillManager::importPasswords); 0068 menu->addAction(tr("Export Passwords to File..."), this, &AutoFillManager::exportPasswords); 0069 ui->importExport->setMenu(menu); 0070 ui->search->setPlaceholderText(tr("Search")); 0071 0072 // Password backends 0073 ui->currentBackend->setText(QSL("<b>%1</b>").arg(m_passwordManager->activeBackend()->name())); 0074 ui->backendOptions->setVisible(m_passwordManager->activeBackend()->hasSettings()); 0075 0076 // Load passwords 0077 QTimer::singleShot(0, this, &AutoFillManager::loadPasswords); 0078 } 0079 0080 void AutoFillManager::loadPasswords() 0081 { 0082 ui->showPasswords->setText(tr("Show Passwords")); 0083 m_passwordsShown = false; 0084 0085 const QVector<PasswordEntry> allEntries = mApp->autoFill()->getAllFormData(); 0086 0087 ui->treePass->clear(); 0088 for (const PasswordEntry &entry : allEntries) { 0089 auto* item = new QTreeWidgetItem(ui->treePass); 0090 item->setText(0, entry.host); 0091 item->setText(1, entry.username); 0092 item->setText(2, QSL("*****")); 0093 0094 QVariant v; 0095 v.setValue(entry); 0096 item->setData(0, Qt::UserRole + 10, v); 0097 ui->treePass->addTopLevelItem(item); 0098 } 0099 0100 QSqlQuery query(SqlDatabase::instance()->database()); 0101 query.exec(QSL("SELECT server, id FROM autofill_exceptions")); 0102 ui->treeExcept->clear(); 0103 while (query.next()) { 0104 auto* item = new QTreeWidgetItem(ui->treeExcept); 0105 item->setText(0, query.value(0).toString()); 0106 item->setData(0, Qt::UserRole + 10, query.value(1).toString()); 0107 ui->treeExcept->addTopLevelItem(item); 0108 } 0109 0110 ui->treePass->sortByColumn(-1, Qt::AscendingOrder); 0111 ui->treeExcept->sortByColumn(-1, Qt::AscendingOrder); 0112 } 0113 0114 void AutoFillManager::changePasswordBackend() 0115 { 0116 QHash<QString, PasswordBackend*> backends = m_passwordManager->availableBackends(); 0117 QStringList items; 0118 0119 int current = 0; 0120 0121 QHashIterator<QString, PasswordBackend*> i(backends); 0122 while (i.hasNext()) { 0123 i.next(); 0124 if (i.value() == m_passwordManager->activeBackend()) { 0125 current = items.size(); 0126 } 0127 items << i.value()->name(); 0128 } 0129 0130 QString item = QInputDialog::getItem(this, tr("Change backend..."), tr("Change backend:"), items, current, false); 0131 0132 // Switch backends 0133 if (!item.isEmpty()) { 0134 PasswordBackend* backend = nullptr; 0135 0136 QHashIterator<QString, PasswordBackend*> i(backends); 0137 while (i.hasNext()) { 0138 i.next(); 0139 if (i.value()->name() == item) { 0140 backend = i.value(); 0141 break; 0142 } 0143 } 0144 0145 if (backend) { 0146 m_passwordManager->switchBackend(backends.key(backend)); 0147 } 0148 } 0149 } 0150 0151 void AutoFillManager::showBackendOptions() 0152 { 0153 PasswordBackend* backend = m_passwordManager->activeBackend(); 0154 0155 if (backend->hasSettings()) { 0156 backend->showSettings(this); 0157 } 0158 } 0159 0160 void AutoFillManager::showPasswords() 0161 { 0162 if (m_passwordsShown) { 0163 for (int i = 0; i < ui->treePass->topLevelItemCount(); i++) { 0164 QTreeWidgetItem* item = ui->treePass->topLevelItem(i); 0165 if (!item) { 0166 continue; 0167 } 0168 item->setText(2, QSL("*****")); 0169 } 0170 0171 ui->showPasswords->setText(tr("Show Passwords")); 0172 m_passwordsShown = false; 0173 0174 return; 0175 } 0176 0177 m_passwordsShown = true; 0178 0179 int result = QMessageBox::question(this, tr("Show Passwords"), tr("Are you sure that you want to show all passwords?"), 0180 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 0181 if (result != QMessageBox::Yes) { 0182 return; 0183 } 0184 0185 for (int i = 0; i < ui->treePass->topLevelItemCount(); i++) { 0186 QTreeWidgetItem* item = ui->treePass->topLevelItem(i); 0187 if (!item) { 0188 continue; 0189 } 0190 0191 item->setText(2, item->data(0, Qt::UserRole + 10).value<PasswordEntry>().password); 0192 } 0193 0194 ui->showPasswords->setText(tr("Hide Passwords")); 0195 } 0196 0197 void AutoFillManager::copyPassword() 0198 { 0199 QTreeWidgetItem* curItem = ui->treePass->currentItem(); 0200 if (!curItem) 0201 return; 0202 0203 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>(); 0204 QApplication::clipboard()->setText(entry.password); 0205 } 0206 0207 void AutoFillManager::copyUsername() 0208 { 0209 QTreeWidgetItem* curItem = ui->treePass->currentItem(); 0210 if (!curItem) 0211 return; 0212 0213 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>(); 0214 QApplication::clipboard()->setText(entry.username); 0215 } 0216 0217 void AutoFillManager::removePass() 0218 { 0219 QTreeWidgetItem* curItem = ui->treePass->currentItem(); 0220 if (!curItem) { 0221 return; 0222 } 0223 0224 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>(); 0225 mApp->autoFill()->removeEntry(entry); 0226 0227 delete curItem; 0228 } 0229 0230 void AutoFillManager::removeAllPass() 0231 { 0232 QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Confirmation"), 0233 tr("Are you sure you want to delete all passwords on your computer?"), QMessageBox::Yes | QMessageBox::No); 0234 if (button != QMessageBox::Yes) { 0235 return; 0236 } 0237 0238 mApp->autoFill()->removeAllEntries(); 0239 ui->treePass->clear(); 0240 } 0241 0242 void AutoFillManager::editPass() 0243 { 0244 QTreeWidgetItem* curItem = ui->treePass->currentItem(); 0245 if (!curItem) { 0246 return; 0247 } 0248 0249 PasswordEntry entry = curItem->data(0, Qt::UserRole + 10).value<PasswordEntry>(); 0250 0251 bool ok; 0252 QString text = QInputDialog::getText(this, tr("Edit password"), tr("Change password:"), QLineEdit::Normal, entry.password, &ok); 0253 0254 if (ok && !text.isEmpty() && text != entry.password) { 0255 QByteArray oldPass = "=" + PasswordManager::urlEncodePassword(entry.password); 0256 QByteArray newPass = "=" + PasswordManager::urlEncodePassword(text); 0257 entry.data.replace(oldPass, newPass); 0258 entry.password = text; 0259 0260 if (mApp->autoFill()->updateEntry(entry)) { 0261 QVariant v; 0262 v.setValue(entry); 0263 curItem->setData(0, Qt::UserRole + 10, v); 0264 0265 if (m_passwordsShown) { 0266 curItem->setText(2, text); 0267 } 0268 } 0269 } 0270 } 0271 0272 void AutoFillManager::removeExcept() 0273 { 0274 QTreeWidgetItem* curItem = ui->treeExcept->currentItem(); 0275 if (!curItem) { 0276 return; 0277 } 0278 QString id = curItem->data(0, Qt::UserRole + 10).toString(); 0279 QSqlQuery query(SqlDatabase::instance()->database()); 0280 query.prepare(QSL("DELETE FROM autofill_exceptions WHERE id=?")); 0281 query.addBindValue(id); 0282 query.exec(); 0283 0284 delete curItem; 0285 } 0286 0287 void AutoFillManager::removeAllExcept() 0288 { 0289 QSqlQuery query(SqlDatabase::instance()->database()); 0290 query.exec(QSL("DELETE FROM autofill_exceptions")); 0291 0292 ui->treeExcept->clear(); 0293 } 0294 0295 void AutoFillManager::showExceptions() 0296 { 0297 ui->tabWidget->setCurrentIndex(1); 0298 } 0299 0300 void AutoFillManager::importPasswords() 0301 { 0302 m_fileName = QzTools::getOpenFileName(QSL("AutoFill-ImportPasswords"), this, tr("Choose file..."), QDir::homePath() + QSL("/passwords.xml"), QSL("*.xml")); 0303 0304 if (m_fileName.isEmpty()) { 0305 return; 0306 } 0307 0308 QTimer::singleShot(0, this, &AutoFillManager::slotImportPasswords); 0309 } 0310 0311 void AutoFillManager::exportPasswords() 0312 { 0313 m_fileName = QzTools::getSaveFileName(QSL("AutoFill-ExportPasswords"), this, tr("Choose file..."), QDir::homePath() + QSL("/passwords.xml"), QSL("*.xml")); 0314 0315 if (m_fileName.isEmpty()) { 0316 return; 0317 } 0318 0319 QTimer::singleShot(0, this, &AutoFillManager::slotExportPasswords); 0320 } 0321 0322 void AutoFillManager::slotImportPasswords() 0323 { 0324 QFile file(m_fileName); 0325 0326 if (!file.open(QFile::ReadOnly)) { 0327 ui->importExportLabel->setText(tr("Cannot read file!")); 0328 return; 0329 } 0330 0331 QApplication::setOverrideCursor(Qt::WaitCursor); 0332 0333 bool status = mApp->autoFill()->importPasswords(file.readAll()); 0334 file.close(); 0335 0336 ui->importExportLabel->setText(status ? tr("Successfully imported") : tr("Error while importing!")); 0337 loadPasswords(); 0338 0339 QApplication::restoreOverrideCursor(); 0340 } 0341 0342 void AutoFillManager::slotExportPasswords() 0343 { 0344 QFile file(m_fileName); 0345 0346 if (!file.open(QFile::WriteOnly)) { 0347 ui->importExportLabel->setText(tr("Cannot write to file!")); 0348 return; 0349 } 0350 0351 QApplication::setOverrideCursor(Qt::WaitCursor); 0352 0353 file.write(mApp->autoFill()->exportPasswords()); 0354 file.close(); 0355 0356 ui->importExportLabel->setText(tr("Successfully exported")); 0357 0358 QApplication::restoreOverrideCursor(); 0359 } 0360 0361 void AutoFillManager::currentPasswordBackendChanged() 0362 { 0363 ui->currentBackend->setText(QSL("<b>%1</b>").arg(m_passwordManager->activeBackend()->name())); 0364 ui->backendOptions->setVisible(m_passwordManager->activeBackend()->hasSettings()); 0365 0366 QTimer::singleShot(0, this, &AutoFillManager::loadPasswords); 0367 } 0368 0369 void AutoFillManager::passwordContextMenu(const QPoint &pos) 0370 { 0371 auto *menu = new QMenu; 0372 menu->setAttribute(Qt::WA_DeleteOnClose); 0373 menu->addAction(tr("Copy Username"), this, &AutoFillManager::copyUsername); 0374 menu->addAction(tr("Copy Password"), this, &AutoFillManager::copyPassword); 0375 menu->addSeparator(); 0376 menu->addAction(tr("Edit Password"), this, &AutoFillManager::editPass); 0377 menu->popup(ui->treePass->viewport()->mapToGlobal(pos)); 0378 } 0379 0380 AutoFillManager::~AutoFillManager() 0381 { 0382 delete ui; 0383 }