diff --git a/src/widgets/checksumswidget.ui b/src/widgets/checksumswidget.ui --- a/src/widgets/checksumswidget.ui +++ b/src/widgets/checksumswidget.ui @@ -23,7 +23,7 @@ - Expected checksum (MD5, SHA1 or SHA256)... + Expected checksum (MD5, SHA1-SHA512, etc.) @@ -55,141 +55,48 @@ - - - + + + - MD5: + Value - - + + - SHA1: + - - - - - - Calculate - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Click to copy the checksum to the clipboard. - - - Copy - - - - .. - - - - + + + + Algorithm + + - - + + - SHA256: + Calculate - - - - - - Calculate - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Click to copy the checksum to the clipboard. - - - Copy - - - - .. - - - - + + + + Copy + + + + .. + + - - - - - Calculate - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Click to copy the checksum to the clipboard. - - - Copy - - - - .. - - - - + @@ -214,6 +121,12 @@ KSeparator QFrame
kseparator.h
+ 1 + + + KComboBox + QComboBox +
kcombobox.h
diff --git a/src/widgets/kpropertiesdialog.cpp b/src/widgets/kpropertiesdialog.cpp --- a/src/widgets/kpropertiesdialog.cpp +++ b/src/widgets/kpropertiesdialog.cpp @@ -54,6 +54,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -2633,6 +2636,60 @@ emit leaveModality(); } +/** + * List model for displaying `QCryptographicHash::Algorithm`s in a combobox + */ +class KChecksumAlgorithmListModel: public QAbstractListModel { + QVector algorithms; + +public: + + KChecksumAlgorithmListModel(QObject *parent) + : QAbstractListModel(parent), algorithms({ + QCryptographicHash::Md4, + QCryptographicHash::Md5, + QCryptographicHash::Sha1, + QCryptographicHash::Sha224, + QCryptographicHash::Sha256, + QCryptographicHash::Sha384, + QCryptographicHash::Sha512, + QCryptographicHash::Sha3_224, + QCryptographicHash::Sha3_256, + QCryptographicHash::Sha3_384, + QCryptographicHash::Sha3_512 + }) {} + + int rowCount(const QModelIndex &parent) const override { + return algorithms.size(); + } + + QCryptographicHash::Algorithm at(int index) const { + return algorithms.at(index); + } + + int find(QCryptographicHash::Algorithm alg) const { + return algorithms.indexOf(alg); + } + + QVariant data(const QModelIndex &index, int role) const override { + if (!index.isValid()) + return {}; + + if (index.row() >= algorithms.size() || index.row() < 0) + return {}; + + if (role == Qt::DisplayRole) { + const auto alg = algorithms.at(index.row()); + const auto v = QVariant::fromValue(alg); + return QVariant::fromValue( + v.toString().replace("Real", "").replace('_', '-').toUpper() + ); + } + + return {}; + } +}; + class KChecksumsPlugin::KChecksumsPluginPrivate { public: @@ -2648,43 +2705,44 @@ Ui::ChecksumsWidget m_ui; QFileSystemWatcher fileWatcher; - QString m_md5; - QString m_sha1; - QString m_sha256; + QMap cache; + mutable QMutex mutex; + + KConfigGroup configGroup; }; KChecksumsPlugin::KChecksumsPlugin(KPropertiesDialog *dialog) : KPropertiesDialogPlugin(dialog), d(new KChecksumsPluginPrivate) { d->m_ui.setupUi(&d->m_widget); - properties->addPage(&d->m_widget, i18nc("@title:tab", "C&hecksums")); + properties->addPage(&d->m_widget, i18nc("@title:tab", "Checksums")); - d->m_ui.md5CopyButton->hide(); - d->m_ui.sha1CopyButton->hide(); - d->m_ui.sha256CopyButton->hide(); + d->configGroup = KConfigGroup(KSharedConfig::openConfig(), "KPropertiesDialog"); + + d->m_ui.algorithmComboBox->setModel(new KChecksumAlgorithmListModel(this)); + + setAlgorithm(readDefaultAlgorithm()); + + connect(d->m_ui.algorithmComboBox, static_cast(&QComboBox::currentIndexChanged), this, [=](int) { + writeDefaultAlgorithm(algorithm()); + }); connect(d->m_ui.lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) { slotVerifyChecksum(text.toLower()); }); - connect(d->m_ui.md5Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowMd5); - connect(d->m_ui.sha1Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowSha1); - connect(d->m_ui.sha256Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowSha256); - d->fileWatcher.addPath(properties->item().localPath()); connect(&d->fileWatcher, &QFileSystemWatcher::fileChanged, this, &KChecksumsPlugin::slotInvalidateCache); - auto clipboard = QApplication::clipboard(); - connect(d->m_ui.md5CopyButton, &QPushButton::clicked, this, [=]() { - clipboard->setText(d->m_md5); + connect(d->m_ui.calculateButton, &QPushButton::clicked, this, [=]() { + showChecksum(algorithm()); }); - connect(d->m_ui.sha1CopyButton, &QPushButton::clicked, this, [=]() { - clipboard->setText(d->m_sha1); - }); + auto clipboard = QApplication::clipboard(); - connect(d->m_ui.sha256CopyButton, &QPushButton::clicked, this, [=]() { - clipboard->setText(d->m_sha256); + d->m_ui.copyButton->hide(); + connect(d->m_ui.copyButton, &QPushButton::clicked, this, [=]() { + clipboard->setText(d->m_ui.valueLabel->text()); }); connect(d->m_ui.pasteButton, &QPushButton::clicked, this, [=]() { @@ -2705,56 +2763,62 @@ return false; } - const KFileItem item = items.first(); + const auto& item = items.first(); return item.isFile() && item.isLocalFile() && item.isReadable() && !item.isDesktopFile() && !item.isLink(); } void KChecksumsPlugin::slotInvalidateCache() { - d->m_md5 = QString(); - d->m_sha1 = QString(); - d->m_sha256 = QString(); + d->cache.clear(); } -void KChecksumsPlugin::slotShowMd5() +void KChecksumsPlugin::verifyChecksums(const QString input, QVector algorithms) { - auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget); - label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + if (algorithms.isEmpty()) { + return; + } - d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.md5Button, label); - d->m_ui.md5Button->hide(); + auto futureWatcher = new QFutureWatcher(this); - showChecksum(QCryptographicHash::Md5, label, d->m_ui.md5CopyButton); -} + connect(futureWatcher, &QFutureWatcher::finished, this, [=]() { + const bool match = futureWatcher->result(); + futureWatcher->deleteLater(); -void KChecksumsPlugin::slotShowSha1() -{ - auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget); - label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + if (match) { + showMatchedChecksum(); + setMatchState(); + } else { + setMismatchState(); + } + }); - d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha1Button, label); - d->m_ui.sha1Button->hide(); + std::function mapper = [this](QCryptographicHash::Algorithm alg) { + const QString cache = cachedChecksum(alg); + if (!cache.isEmpty()) { + return cache; + } - showChecksum(QCryptographicHash::Sha1, label, d->m_ui.sha1CopyButton); -} + const QString checksum = computeChecksum(alg, properties->item().localPath()); + cacheChecksum(checksum, alg); -void KChecksumsPlugin::slotShowSha256() -{ - auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget); - label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + return checksum; + }; + + std::function reducer = [input](bool& match, const QString& checksum) { + match |= checksum == input; + }; - d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha256Button, label); - d->m_ui.sha256Button->hide(); + QFuture future = QtConcurrent::mappedReduced(algorithms, mapper, reducer); - showChecksum(QCryptographicHash::Sha256, label, d->m_ui.sha256CopyButton); + futureWatcher->setFuture(future); } void KChecksumsPlugin::slotVerifyChecksum(const QString &input) { - auto algorithm = detectAlgorithm(input); + auto algorithms = detectAlgorithms(input); // Input is not a supported hash algorithm. - if (algorithm == QCryptographicHash::Md4) { + if (algorithms.isEmpty()) { if (input.isEmpty()) { setDefaultState(); } else { @@ -2763,74 +2827,10 @@ return; } - const QString checksum = cachedChecksum(algorithm); - - // Checksum alread in cache. - if (!checksum.isEmpty()) { - const bool isMatch = (checksum == input); - if (isMatch) { - setMatchState(); - } else { - setMismatchState(); - } - - return; - } - - // Calculate checksum in another thread. - auto futureWatcher = new QFutureWatcher(this); - connect(futureWatcher, &QFutureWatcher::finished, this, [=]() { - - const QString checksum = futureWatcher->result(); - futureWatcher->deleteLater(); - - cacheChecksum(checksum, algorithm); - - switch (algorithm) { - case QCryptographicHash::Md5: - slotShowMd5(); - break; - case QCryptographicHash::Sha1: - slotShowSha1(); - break; - case QCryptographicHash::Sha256: - slotShowSha256(); - break; - default: - break; - } - - const bool isMatch = (checksum == input); - if (isMatch) { - setMatchState(); - } else { - setMismatchState(); - } - }); - // Notify the user about the background computation. setVerifyState(); - auto future = QtConcurrent::run(&KChecksumsPlugin::computeChecksum, algorithm, properties->item().localPath()); - futureWatcher->setFuture(future); -} - -bool KChecksumsPlugin::isMd5(const QString &input) -{ - QRegularExpression regex(QStringLiteral("^[a-f0-9]{32}$")); - return regex.match(input).hasMatch(); -} - -bool KChecksumsPlugin::isSha1(const QString &input) -{ - QRegularExpression regex(QStringLiteral("^[a-f0-9]{40}$")); - return regex.match(input).hasMatch(); -} - -bool KChecksumsPlugin::isSha256(const QString &input) -{ - QRegularExpression regex(QStringLiteral("^[a-f0-9]{64}$")); - return regex.match(input).hasMatch(); + verifyChecksums(input, algorithms); } QString KChecksumsPlugin::computeChecksum(QCryptographicHash::Algorithm algorithm, const QString &path) @@ -2846,22 +2846,31 @@ return QString::fromLatin1(hash.result().toHex()); } -QCryptographicHash::Algorithm KChecksumsPlugin::detectAlgorithm(const QString &input) +QVector KChecksumsPlugin::detectAlgorithms(const QString &input) { - if (isMd5(input)) { - return QCryptographicHash::Md5; - } + static const QVector> regexes{ + {QCryptographicHash::Md4, QRegularExpression("^[a-f0-9]{32}$")}, + {QCryptographicHash::Md5, QRegularExpression("^[a-f0-9]{32}$")}, + {QCryptographicHash::Sha1, QRegularExpression("^[a-f0-9]{40}$")}, + {QCryptographicHash::Sha224, QRegularExpression("^[a-f0-9]{56}$")}, + {QCryptographicHash::Sha256, QRegularExpression("^[a-f0-9]{64}$")}, + {QCryptographicHash::Sha384, QRegularExpression("^[a-f0-9]{96}$")}, + {QCryptographicHash::Sha512, QRegularExpression("^[a-f0-9]{128}$")}, + {QCryptographicHash::Sha3_224, QRegularExpression("^[a-f0-9]{56}$")}, + {QCryptographicHash::Sha3_256, QRegularExpression("^[a-f0-9]{64}$")}, + {QCryptographicHash::Sha3_384, QRegularExpression("^[a-f0-9]{96}$")}, + {QCryptographicHash::Sha3_512, QRegularExpression("^[a-f0-9]{128}$")} + }; - if (isSha1(input)) { - return QCryptographicHash::Sha1; - } + QVector algorithms; - if (isSha256(input)) { - return QCryptographicHash::Sha256; + for (const auto &p : regexes) { + if (p.second.match(input).hasMatch()) { + algorithms.push_back(p.first); + } } - // Md4 used as negative error code. - return QCryptographicHash::Md4; + return algorithms; } void KChecksumsPlugin::setDefaultState() @@ -2929,8 +2938,23 @@ d->m_ui.feedbackLabel->show(); } -void KChecksumsPlugin::showChecksum(QCryptographicHash::Algorithm algorithm, QLabel *label, QPushButton *copyButton) +void KChecksumsPlugin::showMatchedChecksum() +{ + for (const auto alg : d->cache.keys()) { + if(d->m_ui.lineEdit->text() == d->cache.value(alg, "")) { + showChecksum(alg); + return; + } + } +} + +void KChecksumsPlugin::showChecksum(QCryptographicHash::Algorithm algorithm) { + setAlgorithm(algorithm); + + auto label = d->m_ui.valueLabel; + label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + const QString checksum = cachedChecksum(algorithm); // Checksum in cache, nothing else to do. @@ -2948,44 +2972,48 @@ label->setText(checksum); cacheChecksum(checksum, algorithm); - copyButton->show(); + d->m_ui.copyButton->show(); }); auto future = QtConcurrent::run(&KChecksumsPlugin::computeChecksum, algorithm, properties->item().localPath()); futureWatcher->setFuture(future); } -QString KChecksumsPlugin::cachedChecksum(QCryptographicHash::Algorithm algorithm) const +void KChecksumsPlugin::writeDefaultAlgorithm(QCryptographicHash::Algorithm algorithm) { - switch (algorithm) { - case QCryptographicHash::Md5: - return d->m_md5; - case QCryptographicHash::Sha1: - return d->m_sha1; - case QCryptographicHash::Sha256: - return d->m_sha256; - default: - break; + d->configGroup.writeEntry("algorithm", static_cast(algorithm)); +} + +QCryptographicHash::Algorithm KChecksumsPlugin::readDefaultAlgorithm() const +{ + return static_cast(d->configGroup.readEntry("algorithm", static_cast(QCryptographicHash::Sha256))); +} + +void KChecksumsPlugin::setAlgorithm(QCryptographicHash::Algorithm algorithm) +{ + const auto *model = d->m_ui.algorithmComboBox->model(); + const KChecksumAlgorithmListModel *m = dynamic_cast(model); + if (m) { + d->m_ui.algorithmComboBox->setCurrentIndex(m->find(algorithm)); } +} + +QCryptographicHash::Algorithm KChecksumsPlugin::algorithm() const +{ + const QAbstractItemModel *model = d->m_ui.algorithmComboBox->model(); + const KChecksumAlgorithmListModel *m = dynamic_cast(model); + return m->at(d->m_ui.algorithmComboBox->currentIndex()); +} - return QString(); +QString KChecksumsPlugin::cachedChecksum(QCryptographicHash::Algorithm algorithm) const +{ + return d->cache.value(algorithm, QString{}); } void KChecksumsPlugin::cacheChecksum(const QString &checksum, QCryptographicHash::Algorithm algorithm) { - switch (algorithm) { - case QCryptographicHash::Md5: - d->m_md5 = checksum; - break; - case QCryptographicHash::Sha1: - d->m_sha1 = checksum; - break; - case QCryptographicHash::Sha256: - d->m_sha256 = checksum; - break; - default: - return; - } + QMutexLocker locker(&d->mutex); + d->cache.insert(algorithm, checksum); } class KUrlPropsPlugin::KUrlPropsPluginPrivate diff --git a/src/widgets/kpropertiesdialog_p.h b/src/widgets/kpropertiesdialog_p.h --- a/src/widgets/kpropertiesdialog_p.h +++ b/src/widgets/kpropertiesdialog_p.h @@ -30,8 +30,10 @@ #include "kpropertiesdialog.h" +#include #include +class QPushButton; class QComboBox; class QLabel; @@ -173,27 +175,31 @@ private Q_SLOTS: void slotInvalidateCache(); - void slotShowMd5(); - void slotShowSha1(); - void slotShowSha256(); + /** * Compare @p input (required to be lowercase) with the checksum in cache. */ void slotVerifyChecksum(const QString &input); private: - static bool isMd5(const QString &input); - static bool isSha1(const QString &input); - static bool isSha256(const QString &input); + void verifyChecksums(const QString input, QVector algorithms); + static QString computeChecksum(QCryptographicHash::Algorithm algorithm, const QString &path); - static QCryptographicHash::Algorithm detectAlgorithm(const QString &input); + static QVector detectAlgorithms(const QString &input); void setDefaultState(); void setInvalidChecksumState(); void setMatchState(); void setMismatchState(); void setVerifyState(); - void showChecksum(QCryptographicHash::Algorithm algorithm, QLabel *label, QPushButton *copyButton); + void showChecksum(QCryptographicHash::Algorithm algorithm); + void showMatchedChecksum(); + + void writeDefaultAlgorithm(QCryptographicHash::Algorithm algorithm); + QCryptographicHash::Algorithm readDefaultAlgorithm() const; + + void setAlgorithm(QCryptographicHash::Algorithm algorithm); + QCryptographicHash::Algorithm algorithm() const; QString cachedChecksum(QCryptographicHash::Algorithm algorithm) const; void cacheChecksum(const QString &checksum, QCryptographicHash::Algorithm algorithm);