2
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, either version 2 or (at your option)
7
* version 3 of the License.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "IconDownloaderDialog.h"
19
#include "ui_IconDownloaderDialog.h"
21
#include "core/Config.h"
22
#include "core/Database.h"
23
#include "core/Entry.h"
24
#include "core/Metadata.h"
25
#include "core/Tools.h"
26
#include "gui/IconDownloader.h"
27
#include "gui/IconModels.h"
29
#include "osutils/OSUtils.h"
31
#include "gui/osutils/macutils/MacUtils.h"
34
#include <QStandardItemModel>
36
IconDownloaderDialog::IconDownloaderDialog(QWidget* parent)
38
, m_ui(new Ui::IconDownloaderDialog())
39
, m_dataModel(new QStandardItemModel(this))
41
setWindowFlags(Qt::Window);
42
setAttribute(Qt::WA_DeleteOnClose);
45
showFallbackMessage(false);
48
m_dataModel->setHorizontalHeaderLabels({tr("URL"), tr("Status")});
50
m_ui->tableView->setModel(m_dataModel);
51
m_ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
53
connect(m_ui->cancelButton, SIGNAL(clicked()), SLOT(abortDownloads()));
54
connect(m_ui->closeButton, SIGNAL(clicked()), SLOT(close()));
57
IconDownloaderDialog::~IconDownloaderDialog()
62
void IconDownloaderDialog::downloadFavicons(const QSharedPointer<Database>& database,
63
const QList<Entry*>& entries,
67
m_urlToEntries.clear();
69
for (const auto& e : entries) {
70
// Only consider entries with a valid URL and without a custom icon
71
auto webUrl = e->webUrl();
72
if (!webUrl.isEmpty() && (force || e->iconUuid().isNull())) {
73
m_urlToEntries.insert(webUrl, e);
77
if (m_urlToEntries.count() > 0) {
79
macUtils()->raiseOwnWindow();
82
showFallbackMessage(false);
83
m_ui->progressLabel->setText(tr("Please wait, processing entry list…"));
85
QApplication::processEvents();
87
for (const auto& url : m_urlToEntries.uniqueKeys()) {
88
m_dataModel->appendRow(QList<QStandardItem*>()
89
<< new QStandardItem(url) << new QStandardItem(tr("Downloading…")));
90
m_activeDownloaders.append(createDownloader(url));
96
QApplication::processEvents();
98
// Start the downloads
99
for (auto downloader : m_activeDownloaders) {
100
downloader->download();
105
void IconDownloaderDialog::downloadFaviconInBackground(const QSharedPointer<Database>& database, Entry* entry)
108
m_urlToEntries.clear();
111
auto webUrl = entry->webUrl();
112
if (!webUrl.isEmpty()) {
113
m_urlToEntries.insert(webUrl, entry);
116
if (m_urlToEntries.count() > 0) {
117
m_activeDownloaders.append(createDownloader(webUrl));
118
m_activeDownloaders.first()->download();
122
IconDownloader* IconDownloaderDialog::createDownloader(const QString& url)
124
auto downloader = new IconDownloader();
126
SIGNAL(finished(const QString&, const QImage&)),
128
SLOT(downloadFinished(const QString&, const QImage&)));
130
downloader->setUrl(url);
134
void IconDownloaderDialog::downloadFinished(const QString& url, const QImage& icon)
136
// Prevent re-entrance from multiple calls finishing at the same time
137
QMutexLocker locker(&m_mutex);
139
// Cleanup the icon downloader that sent this signal
140
auto downloader = qobject_cast<IconDownloader*>(sender());
142
downloader->deleteLater();
143
m_activeDownloaders.removeAll(downloader);
147
updateCancelButton();
149
if (m_db && !icon.isNull()) {
150
// Don't add an icon larger than 128x128, but retain original size if smaller
151
constexpr auto maxIconSize = 128;
152
auto scaledIcon = icon;
153
if (icon.width() > maxIconSize || icon.height() > maxIconSize) {
154
scaledIcon = icon.scaled(maxIconSize, maxIconSize);
157
QByteArray serializedIcon = Icons::saveToBytes(scaledIcon);
158
QUuid uuid = m_db->metadata()->findCustomIcon(serializedIcon);
160
uuid = QUuid::createUuid();
161
m_db->metadata()->addCustomIcon(uuid, serializedIcon);
162
updateTable(url, tr("Ok"));
164
updateTable(url, tr("Already Exists"));
167
// Set the icon on all the entries associated with this url
168
for (const auto entry : m_urlToEntries.values(url)) {
169
entry->setIcon(uuid);
172
showFallbackMessage(true);
173
updateTable(url, tr("Download Failed"));
178
void IconDownloaderDialog::showFallbackMessage(bool state)
180
// Show fallback message if the option is not active
181
bool show = state && !config()->get(Config::Security_IconDownloadFallback).toBool();
182
m_ui->fallbackLabel->setVisible(show);
185
void IconDownloaderDialog::updateProgressBar()
187
int total = m_urlToEntries.uniqueKeys().count();
188
int value = total - m_activeDownloaders.count();
189
m_ui->progressBar->setValue(value);
190
m_ui->progressBar->setMaximum(total);
191
m_ui->progressLabel->setText(
192
tr("Downloading favicons (%1/%2)…").arg(QString::number(value), QString::number(total)));
195
void IconDownloaderDialog::updateCancelButton()
197
m_ui->cancelButton->setEnabled(!m_activeDownloaders.isEmpty());
200
void IconDownloaderDialog::updateTable(const QString& url, const QString& message)
202
for (int i = 0; i < m_dataModel->rowCount(); ++i) {
203
if (m_dataModel->item(i, 0)->text() == url) {
204
m_dataModel->item(i, 1)->setText(message);
209
void IconDownloaderDialog::abortDownloads()
211
for (auto* downloader : m_activeDownloaders) {
212
downloader->deleteLater();
214
m_activeDownloaders.clear();
216
updateCancelButton();