File indexing completed on 2024-04-21 04:00:59

0001 /*
0002     This file is part of the syndication library
0003     SPDX-FileCopyrightText: 2006 Frank Osterfeld <osterfeld@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "content.h"
0009 
0010 #include <tools.h>
0011 
0012 #include <QByteArray>
0013 #include <QDomElement>
0014 #include <QStringList>
0015 
0016 namespace Syndication
0017 {
0018 namespace Atom
0019 {
0020 class SYNDICATION_NO_EXPORT Content::ContentPrivate
0021 {
0022 public:
0023     ContentPrivate()
0024         : format(Format::PlainText)
0025         , formatIdentified(false)
0026     {
0027     }
0028     mutable Format format;
0029     mutable bool formatIdentified;
0030 };
0031 
0032 Content::Content()
0033     : ElementWrapper()
0034     , d(new ContentPrivate)
0035 {
0036 }
0037 
0038 Content::Content(const QDomElement &element)
0039     : ElementWrapper(element)
0040     , d(new ContentPrivate)
0041 {
0042 }
0043 
0044 Content::Content(const Content &other)
0045     : ElementWrapper(other)
0046     , d(other.d)
0047 {
0048 }
0049 
0050 Content::~Content()
0051 {
0052 }
0053 
0054 Content &Content::operator=(const Content &other)
0055 {
0056     ElementWrapper::operator=(other);
0057     d = other.d;
0058     return *this;
0059 }
0060 
0061 QString Content::type() const
0062 {
0063     return attribute(QStringLiteral("type"));
0064 }
0065 
0066 QString Content::src() const
0067 {
0068     return completeURI(attribute(QStringLiteral("src")));
0069 }
0070 
0071 QByteArray Content::asByteArray() const
0072 {
0073     if (!isBinary()) {
0074         return QByteArray();
0075     }
0076     return QByteArray::fromBase64(text().trimmed().toLatin1());
0077 }
0078 
0079 Content::Format Content::mapTypeToFormat(const QString &typep, const QString &src)
0080 {
0081     QString type = typep;
0082     //"If neither the type attribute nor the src attribute is provided,
0083     // Atom Processors MUST behave as though the type attribute were
0084     // present with a value of "text""
0085     if (type.isNull() && src.isEmpty()) {
0086         type = QStringLiteral("text");
0087     }
0088 
0089     if (type == QLatin1String("html") || type == QLatin1String("text/html")) {
0090         return EscapedHTML;
0091     }
0092 
0093     /* clang-format off */
0094     if (type == QLatin1String("text")
0095         || (type.startsWith(QLatin1String("text/"), Qt::CaseInsensitive)
0096             && !type.startsWith(QLatin1String("text/xml"), Qt::CaseInsensitive))) { /* clang-format on */
0097         return PlainText;
0098     }
0099 
0100     static QStringList xmltypes;
0101     if (xmltypes.isEmpty()) {
0102         xmltypes.reserve(8);
0103         xmltypes.append(QStringLiteral("xhtml"));
0104         xmltypes.append(QStringLiteral("application/xhtml+xml"));
0105         // XML media types as defined in RFC3023:
0106         xmltypes.append(QStringLiteral("text/xml"));
0107         xmltypes.append(QStringLiteral("application/xml"));
0108         xmltypes.append(QStringLiteral("text/xml-external-parsed-entity"));
0109         xmltypes.append(QStringLiteral("application/xml-external-parsed-entity"));
0110         xmltypes.append(QStringLiteral("application/xml-dtd"));
0111         xmltypes.append(QStringLiteral("text/x-dtd")); // from shared-mime-info
0112     }
0113 
0114     /* clang-format off */
0115     if (xmltypes.contains(type)
0116         || type.endsWith(QLatin1String("+xml"), Qt::CaseInsensitive)
0117         || type.endsWith(QLatin1String("/xml"), Qt::CaseInsensitive)) { /* clang-format on */
0118         return XML;
0119     }
0120 
0121     return Binary;
0122 }
0123 
0124 Content::Format Content::format() const
0125 {
0126     if (d->formatIdentified == false) {
0127         d->format = mapTypeToFormat(type(), src());
0128         d->formatIdentified = true;
0129     }
0130     return d->format;
0131 }
0132 
0133 bool Content::isBinary() const
0134 {
0135     return format() == Binary;
0136 }
0137 
0138 bool Content::isContained() const
0139 {
0140     return src().isEmpty();
0141 }
0142 
0143 bool Content::isPlainText() const
0144 {
0145     return format() == PlainText;
0146 }
0147 
0148 bool Content::isEscapedHTML() const
0149 {
0150     return format() == EscapedHTML;
0151 }
0152 
0153 bool Content::isXML() const
0154 {
0155     return format() == XML;
0156 }
0157 
0158 QString Content::asString() const
0159 {
0160     Format f = format();
0161 
0162     if (f == PlainText) {
0163         return plainTextToHtml(text()).trimmed();
0164     } else if (f == EscapedHTML) {
0165         return text().trimmed();
0166     } else if (f == XML) {
0167         return childNodesAsXML().trimmed();
0168     }
0169 
0170     return QString();
0171 }
0172 
0173 QString Content::debugInfo() const
0174 {
0175     QString info = QLatin1String("### Content: ###################\n");
0176     info += QLatin1String("type: #") + type() + QLatin1String("#\n");
0177     if (!src().isNull()) {
0178         info += QLatin1String("src: #") + src() + QLatin1String("#\n");
0179     }
0180     if (!isBinary()) {
0181         info += QLatin1String("content: #") + asString() + QLatin1String("#\n");
0182     } else {
0183         info += QLatin1String("binary length: #") + QString::number(asByteArray().size()) + QLatin1String("#\n");
0184     }
0185     info += QLatin1String("### Content end ################\n");
0186 
0187     return info;
0188 }
0189 
0190 } // namespace Atom
0191 } // namespace Syndication