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.) @@ -57,30 +57,23 @@ - + MD5: - - - - SHA1: - - - - - + + - + Calculate - + Qt::Horizontal @@ -93,7 +86,7 @@ - + Click to copy the checksum to the clipboard. @@ -108,24 +101,24 @@ - - + + - SHA256: + SHA1: - - + + - + Calculate - + Qt::Horizontal @@ -138,7 +131,7 @@ - + Click to copy the checksum to the clipboard. @@ -153,17 +146,24 @@ - - + + + + SHA256: + + + + + - + Calculate - + Qt::Horizontal @@ -176,7 +176,7 @@ - + Click to copy the checksum to the clipboard. @@ -191,6 +191,384 @@ + + + + + 0 + 22 + + + + Advanced + + + false + + + + + + MD4: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA224: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA384: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA512: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA3-224: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA3-256: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA3-384: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + + SHA3-512: + + + + + + + + + Calculate + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Click to copy the checksum to the clipboard. + + + Copy + + + + .. + + + + + + + + @@ -214,6 +592,13 @@ KSeparator QFrame
kseparator.h
+ 1 + + + KCollapsibleGroupBox + QWidget +
kcollapsiblegroupbox.h
+ 1
diff --git a/src/widgets/kpropertiesdialog.cpp b/src/widgets/kpropertiesdialog.cpp --- a/src/widgets/kpropertiesdialog.cpp +++ b/src/widgets/kpropertiesdialog.cpp @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -2647,44 +2649,49 @@ Ui::ChecksumsWidget m_ui; QFileSystemWatcher fileWatcher; - QString m_md5; - QString m_sha1; - QString m_sha256; + QMap cache; + mutable QMutex mutex; }; 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")); - - d->m_ui.md5CopyButton->hide(); - d->m_ui.sha1CopyButton->hide(); - d->m_ui.sha256CopyButton->hide(); + properties->addPage(&d->m_widget, i18nc("@title:tab", "Checksums")); 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.sha1CopyButton, &QPushButton::clicked, this, [=]() { - clipboard->setText(d->m_sha1); - }); - - connect(d->m_ui.sha256CopyButton, &QPushButton::clicked, this, [=]() { - clipboard->setText(d->m_sha256); - }); + for(const auto alg : { + 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 + }) { + auto buttons = getButtons(alg); + auto calculateButton = buttons.first; + auto copyButton = buttons.second; + connect(calculateButton, &QPushButton::clicked, this, [=]() { + showChecksum(alg, calculateButton, copyButton); + }); + copyButton->hide(); + connect(copyButton, &QPushButton::clicked, this, [=]() { + clipboard->setText(d->cache.value(alg, "")); + }); + } connect(d->m_ui.pasteButton, &QPushButton::clicked, this, [=]() { d->m_ui.lineEdit->setText(clipboard->text()); @@ -2704,56 +2711,63 @@ 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); + showChecksums(); - d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha1Button, label); - d->m_ui.sha1Button->hide(); + if (match) { + setMatchState(); + } else { + setMismatchState(); + } + }); - showChecksum(QCryptographicHash::Sha1, label, d->m_ui.sha1CopyButton); -} + std::function mapper = [this](QCryptographicHash::Algorithm alg) { + const QString cache = cachedChecksum(alg); + if (!cache.isEmpty()) { + return cache; + } -void KChecksumsPlugin::slotShowSha256() -{ - auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget); - label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + const QString checksum = computeChecksum(alg, properties->item().localPath()); + cacheChecksum(checksum, alg); + + return checksum; + }; - d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha256Button, label); - d->m_ui.sha256Button->hide(); + std::function reducer = [input](bool& match, const QString& checksum) { + match |= checksum == input; + }; - showChecksum(QCryptographicHash::Sha256, label, d->m_ui.sha256CopyButton); + QFuture future = QtConcurrent::mappedReduced(algorithms, mapper, reducer); + + 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 { @@ -2762,81 +2776,17 @@ 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) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { - return QString(); + return {}; } QCryptographicHash hash(algorithm); @@ -2845,22 +2795,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() @@ -2928,8 +2887,32 @@ d->m_ui.feedbackLabel->show(); } -void KChecksumsPlugin::showChecksum(QCryptographicHash::Algorithm algorithm, QLabel *label, QPushButton *copyButton) +void KChecksumsPlugin::showChecksums() { + for (const auto alg : d->cache.keys()) { + auto buttons = getButtons(alg); + showChecksum(alg, buttons.first, buttons.second); + } +} + +void KChecksumsPlugin::showChecksum(QCryptographicHash::Algorithm algorithm, QPushButton *calculateButton, QPushButton *copyButton) +{ + if (calculateButton == nullptr || copyButton == nullptr) { + return; + } + + auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget); + label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + + calculateButton->hide(); + calculateButton->parentWidget()->layout()->replaceWidget(calculateButton, label); + + // If it is placed in the "Advanced" KCollapsibleGroupBox then expand it + auto groupBox = dynamic_cast(label->parentWidget()); + if (groupBox) { + groupBox->expand(); + } + const QString checksum = cachedChecksum(algorithm); // Checksum in cache, nothing else to do. @@ -2954,37 +2937,25 @@ futureWatcher->setFuture(future); } -QString KChecksumsPlugin::cachedChecksum(QCryptographicHash::Algorithm algorithm) const +std::pair KChecksumsPlugin::getButtons(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; - } + QString name{QMetaEnum::fromType().valueToKey(algorithm)}; + // TODO: Remove after QCryptographicHash::Algorithm is renamed from RealSha3 to Sha3 + name.replace("Real", ""); + auto calculateBtn = d->m_widget.findChild("calculate" + name); + auto copyBtn = d->m_widget.findChild("copy" + name); + return std::make_pair(calculateBtn, copyBtn); +} - 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,27 @@ 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, QPushButton *calculateButton, QPushButton *copyButton); + void showChecksums(); + + std::pair getButtons(QCryptographicHash::Algorithm algorithm); QString cachedChecksum(QCryptographicHash::Algorithm algorithm) const; void cacheChecksum(const QString &checksum, QCryptographicHash::Algorithm algorithm);