/* * Copyright (C) 2008-2021 The QXmpp developers * * Author: * Linus Jahn * * Source: * https://github.com/qxmpp-project/qxmpp * * This file is a part of QXmpp library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * */ #include "QXmppBitsOfBinaryContentId.h" #include #include #include #define CONTENTID_URL QStringLiteral("cid:") #define CONTENTID_URL_LENGTH 4 #define CONTENTID_POSTFIX QStringLiteral("@bob.xmpp.org") #define CONTENTID_POSTFIX_LENGTH 13 #define CONTENTID_HASH_SEPARATOR QStringLiteral("+") static const QMap HASH_ALGORITHMS = { { QCryptographicHash::Sha1, QStringLiteral("sha1") }, #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 { QCryptographicHash::Md4, QStringLiteral("md4") }, { QCryptographicHash::Md5, QStringLiteral("md5") }, { QCryptographicHash::Sha224, QStringLiteral("sha224") }, { QCryptographicHash::Sha256, QStringLiteral("sha256") }, { QCryptographicHash::Sha384, QStringLiteral("sha384") }, { QCryptographicHash::Sha512, QStringLiteral("sha512") }, { QCryptographicHash::Sha3_224, QStringLiteral("sha3-224") }, { QCryptographicHash::Sha3_256, QStringLiteral("sha3-256") }, { QCryptographicHash::Sha3_384, QStringLiteral("sha3-384") }, { QCryptographicHash::Sha3_512, QStringLiteral("sha3-512") }, #endif }; class QXmppBitsOfBinaryContentIdPrivate : public QSharedData { public: QXmppBitsOfBinaryContentIdPrivate(); QCryptographicHash::Algorithm algorithm; QByteArray hash; }; QXmppBitsOfBinaryContentIdPrivate::QXmppBitsOfBinaryContentIdPrivate() : algorithm(QCryptographicHash::Sha1) { } /// /// \class QXmppBitsOfBinaryContentId /// /// QXmppBitsOfBinaryContentId represents a link to or an identifier of /// \xep{0231, Bits of Binary} data. /// /// \since QXmpp 1.2 /// /// /// Parses a \c QXmppBitsOfBinaryContentId from a \xep{0231, Bits of Binary} /// \c cid: URL /// /// In case parsing failed, the returned \c QXmppBitsOfBinaryContentId is /// empty. /// /// \see QXmppBitsOfBinaryContentId::fromContentId /// QXmppBitsOfBinaryContentId QXmppBitsOfBinaryContentId::fromCidUrl(const QString &input) { if (input.startsWith(CONTENTID_URL)) return fromContentId(input.mid(CONTENTID_URL_LENGTH)); return {}; } /// /// Parses a \c QXmppBitsOfBinaryContentId from a \xep{0231, Bits of Binary} /// content id /// /// In case parsing failed, the returned \c QXmppBitsOfBinaryContentId is /// empty. /// /// \note This does not allow \c cid: URLs to be passed. Use /// \c QXmppBitsOfBinaryContentId::fromCidUrl for that purpose. /// /// \see QXmppBitsOfBinaryContentId::fromCidUrl /// QXmppBitsOfBinaryContentId QXmppBitsOfBinaryContentId::fromContentId(const QString &input) { if (input.startsWith(CONTENTID_URL) || !input.endsWith(CONTENTID_POSTFIX)) return {}; // remove '@bob.xmpp.org' QString hashAndAlgoStr = input.left(input.size() - CONTENTID_POSTFIX_LENGTH); // get size of hash algo id QStringList algoAndHash = hashAndAlgoStr.split(CONTENTID_HASH_SEPARATOR); if (algoAndHash.size() != 2) return {}; QCryptographicHash::Algorithm algo = HASH_ALGORITHMS.key(algoAndHash.first(), QCryptographicHash::Algorithm(-1)); if (int(algo) == -1) return {}; QXmppBitsOfBinaryContentId cid; cid.setAlgorithm(algo); cid.setHash(QByteArray::fromHex(algoAndHash.last().toUtf8())); return cid; } /// /// Default contructor /// QXmppBitsOfBinaryContentId::QXmppBitsOfBinaryContentId() : d(new QXmppBitsOfBinaryContentIdPrivate) { } /// /// Returns true, if two \c QXmppBitsOfBinaryContentId equal /// bool QXmppBitsOfBinaryContentId::operator==(const QXmppBitsOfBinaryContentId &other) const { return d->algorithm == other.algorithm() && d->hash == other.hash(); } /// Default destructor QXmppBitsOfBinaryContentId::~QXmppBitsOfBinaryContentId() = default; /// Default copy-constructor QXmppBitsOfBinaryContentId::QXmppBitsOfBinaryContentId(const QXmppBitsOfBinaryContentId &cid) = default; /// Default assignment operator QXmppBitsOfBinaryContentId &QXmppBitsOfBinaryContentId::operator=(const QXmppBitsOfBinaryContentId &other) = default; /// /// Returns a \xep{0231, Bits of Binary} content id /// QString QXmppBitsOfBinaryContentId::toContentId() const { if (!isValid()) return {}; return HASH_ALGORITHMS.value(d->algorithm) + CONTENTID_HASH_SEPARATOR + d->hash.toHex() + CONTENTID_POSTFIX; } /// /// Returns a \xep{0231, Bits of Binary} \c cid: URL /// QString QXmppBitsOfBinaryContentId::toCidUrl() const { if (!isValid()) return {}; return toContentId().prepend(CONTENTID_URL); } /// /// Returns the hash value in binary form /// QByteArray QXmppBitsOfBinaryContentId::hash() const { return d->hash; } /// /// Sets the hash value in binary form /// void QXmppBitsOfBinaryContentId::setHash(const QByteArray &hash) { d->hash = hash; } /// /// Returns the hash algorithm used to calculate the \c hash value /// /// The default value is \c QCryptographicHash::Sha1. /// /// This currently supports MD4, MD5, SHA-1, SHA-2 (SHA224 - SHA512) and SHA-3 /// (SHA3-224 - SHA3-512). /// QCryptographicHash::Algorithm QXmppBitsOfBinaryContentId::algorithm() const { return d->algorithm; } /// /// Sets the hash algorithm used to calculate the \c hash value /// /// The default value is \c QCryptographicHash::Sha1. /// /// This currently supports MD4, MD5, SHA-1, SHA-2 (SHA224 - SHA512) and SHA-3 /// (SHA3-224 - SHA3-512). /// /// \note Only change this, if you know what you do. The XEP allows other /// hashing algorithms than SHA-1 to be used, but not all clients support this. /// Since in most cases the content id is not security relevant it is not a /// problem to continue using SHA-1. /// void QXmppBitsOfBinaryContentId::setAlgorithm(QCryptographicHash::Algorithm algo) { d->algorithm = algo; } /// /// Checks whether the content id is valid and can be serialized into a string. /// /// \note Checking the hash length requires QXmpp to be built with Qt 5.12.0 or /// later. /// /// \returns True, if the set hashing algorithm is supported, a hash value is /// set and its length is correct, false otherwise. /// bool QXmppBitsOfBinaryContentId::isValid() const { #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) return !d->hash.isEmpty() && HASH_ALGORITHMS.contains(d->algorithm) && d->hash.length() == QCryptographicHash::hashLength(d->algorithm); #else return !d->hash.isEmpty() && HASH_ALGORITHMS.contains(d->algorithm); #endif } /// /// Checks whether \c input is a Bits of Binary content id or \c cid: URL /// /// \param input The string to be checked. /// \param checkIsCidUrl If true, it only accepts \c cid: URLs. /// /// \returns True, if \c input is valid. /// bool QXmppBitsOfBinaryContentId::isBitsOfBinaryContentId(const QString &input, bool checkIsCidUrl) { return input.endsWith(CONTENTID_POSTFIX) && input.contains(CONTENTID_HASH_SEPARATOR) && (!checkIsCidUrl || input.startsWith(CONTENTID_URL)); }