File indexing completed on 2024-04-28 11:43:33

0001 /*
0002     This file is part of KNewStuff2.
0003     SPDX-FileCopyrightText: 2002 Cornelius Schumacher <schumacher@kde.org>
0004     SPDX-FileCopyrightText: 2003-2007 Josef Spillner <spillner@kde.org>
0005     SPDX-FileCopyrightText: 2009 Frederik Gladhorn <gladhorn@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 */
0009 
0010 #include "entryinternal.h"
0011 
0012 #include <QStringList>
0013 #include <QXmlStreamReader>
0014 #include <knewstuffcore_debug.h>
0015 
0016 #include "../entry_p.h" // For Entry::Status ONLY!
0017 #include "xmlloader.h"
0018 
0019 using namespace KNSCore;
0020 
0021 class KNSCore::EntryInternalPrivate : public QSharedData
0022 {
0023 public:
0024     EntryInternalPrivate()
0025         : mReleaseDate(QDate::currentDate())
0026         , mRating(0)
0027         , mNumberOfComments(0)
0028         , mDownloadCount(0)
0029         , mNumberFans(0)
0030         , mNumberKnowledgebaseEntries(0)
0031         , mStatus(KNS3::Entry::Invalid)
0032         , mSource(EntryInternal::Online)
0033         , mEntryType(EntryInternal::CatalogEntry)
0034     {
0035         qRegisterMetaType<KNSCore::EntryInternal::List>();
0036     }
0037 
0038     bool operator==(const EntryInternalPrivate &other) const
0039     {
0040         return mUniqueId == other.mUniqueId && mProviderId == other.mProviderId;
0041     }
0042 
0043     QString mUniqueId;
0044     QString mName;
0045     QUrl mHomepage;
0046     QString mCategory;
0047     QString mLicense;
0048     QString mVersion;
0049     QDate mReleaseDate;
0050 
0051     // Version and date if a newer version is found (updateable)
0052     QString mUpdateVersion;
0053     QDate mUpdateReleaseDate;
0054 
0055     Author mAuthor;
0056     int mRating;
0057     int mNumberOfComments;
0058     int mDownloadCount;
0059     int mNumberFans;
0060     int mNumberKnowledgebaseEntries;
0061     QString mKnowledgebaseLink;
0062     QString mSummary;
0063     QString mShortSummary;
0064     QString mChangelog;
0065     QString mPayload;
0066     QStringList mInstalledFiles;
0067     QString mProviderId;
0068     QStringList mUnInstalledFiles;
0069     QString mDonationLink;
0070     QStringList mTags;
0071 
0072     QString mChecksum;
0073     QString mSignature;
0074     KNS3::Entry::Status mStatus;
0075     EntryInternal::Source mSource;
0076     EntryInternal::EntryType mEntryType;
0077 
0078     QString mPreviewUrl[6];
0079     QImage mPreviewImage[6];
0080 
0081     QList<EntryInternal::DownloadLinkInformation> mDownloadLinkInformationList;
0082 };
0083 
0084 EntryInternal::EntryInternal()
0085     : d(new EntryInternalPrivate())
0086 {
0087 }
0088 
0089 EntryInternal::EntryInternal(const EntryInternal &other)
0090     : d(other.d)
0091 {
0092 }
0093 
0094 EntryInternal &EntryInternal::operator=(const EntryInternal &other)
0095 {
0096     d = other.d;
0097     return *this;
0098 }
0099 
0100 bool EntryInternal::operator<(const KNSCore::EntryInternal &other) const
0101 {
0102     return d->mUniqueId < other.d->mUniqueId;
0103 }
0104 
0105 bool EntryInternal::operator==(const KNSCore::EntryInternal &other) const
0106 {
0107     return d->mUniqueId == other.d->mUniqueId && d->mProviderId == other.d->mProviderId;
0108 }
0109 
0110 EntryInternal::~EntryInternal()
0111 {
0112 }
0113 
0114 bool EntryInternal::isValid() const
0115 {
0116     return !d->mUniqueId.isEmpty();
0117 }
0118 
0119 QString EntryInternal::name() const
0120 {
0121     return d->mName;
0122 }
0123 
0124 void EntryInternal::setName(const QString &name)
0125 {
0126     d->mName = name;
0127 }
0128 
0129 QString EntryInternal::uniqueId() const
0130 {
0131     return d->mUniqueId;
0132 }
0133 
0134 void EntryInternal::setUniqueId(const QString &id)
0135 {
0136     d->mUniqueId = id;
0137 }
0138 
0139 QString EntryInternal::providerId() const
0140 {
0141     return d->mProviderId;
0142 }
0143 
0144 void EntryInternal::setProviderId(const QString &id)
0145 {
0146     d->mProviderId = id;
0147 }
0148 
0149 QStringList KNSCore::EntryInternal::tags() const
0150 {
0151     return d->mTags;
0152 }
0153 
0154 void KNSCore::EntryInternal::setTags(const QStringList &tags)
0155 {
0156     d->mTags = tags;
0157 }
0158 
0159 QString EntryInternal::category() const
0160 {
0161     return d->mCategory;
0162 }
0163 
0164 void EntryInternal::setCategory(const QString &category)
0165 {
0166     d->mCategory = category;
0167 }
0168 
0169 QUrl EntryInternal::homepage() const
0170 {
0171     return d->mHomepage;
0172 }
0173 
0174 void EntryInternal::setHomepage(const QUrl &page)
0175 {
0176     d->mHomepage = page;
0177 }
0178 
0179 Author EntryInternal::author() const
0180 {
0181     return d->mAuthor;
0182 }
0183 
0184 void EntryInternal::setAuthor(const KNSCore::Author &author)
0185 {
0186     d->mAuthor = author;
0187 }
0188 
0189 QString EntryInternal::license() const
0190 {
0191     return d->mLicense;
0192 }
0193 
0194 void EntryInternal::setLicense(const QString &license)
0195 {
0196     d->mLicense = license;
0197 }
0198 
0199 QString EntryInternal::summary() const
0200 {
0201     return d->mSummary;
0202 }
0203 
0204 void EntryInternal::setSummary(const QString &summary)
0205 {
0206     d->mSummary = summary;
0207 }
0208 
0209 QString EntryInternal::shortSummary() const
0210 {
0211     return d->mShortSummary;
0212 }
0213 
0214 void EntryInternal::setShortSummary(const QString &summary)
0215 {
0216     d->mShortSummary = summary;
0217 }
0218 
0219 void EntryInternal::setChangelog(const QString &changelog)
0220 {
0221     d->mChangelog = changelog;
0222 }
0223 
0224 QString EntryInternal::changelog() const
0225 {
0226     return d->mChangelog;
0227 }
0228 
0229 QString EntryInternal::version() const
0230 {
0231     return d->mVersion;
0232 }
0233 
0234 void EntryInternal::setVersion(const QString &version)
0235 {
0236     d->mVersion = version;
0237 }
0238 
0239 QDate EntryInternal::releaseDate() const
0240 {
0241     return d->mReleaseDate;
0242 }
0243 
0244 void EntryInternal::setReleaseDate(const QDate &releasedate)
0245 {
0246     d->mReleaseDate = releasedate;
0247 }
0248 
0249 QString EntryInternal::payload() const
0250 {
0251     return d->mPayload;
0252 }
0253 
0254 void EntryInternal::setPayload(const QString &url)
0255 {
0256     d->mPayload = url;
0257 }
0258 
0259 QDate EntryInternal::updateReleaseDate() const
0260 {
0261     return d->mUpdateReleaseDate;
0262 }
0263 
0264 void EntryInternal::setUpdateReleaseDate(const QDate &releasedate)
0265 {
0266     d->mUpdateReleaseDate = releasedate;
0267 }
0268 
0269 QString EntryInternal::updateVersion() const
0270 {
0271     return d->mUpdateVersion;
0272 }
0273 
0274 void EntryInternal::setUpdateVersion(const QString &version)
0275 {
0276     d->mUpdateVersion = version;
0277 }
0278 
0279 QString EntryInternal::previewUrl(PreviewType type) const
0280 {
0281     return d->mPreviewUrl[type];
0282 }
0283 
0284 void EntryInternal::setPreviewUrl(const QString &url, PreviewType type)
0285 {
0286     d->mPreviewUrl[type] = url;
0287 }
0288 
0289 QImage EntryInternal::previewImage(PreviewType type) const
0290 {
0291     return d->mPreviewImage[type];
0292 }
0293 
0294 void EntryInternal::setPreviewImage(const QImage &image, PreviewType type)
0295 {
0296     d->mPreviewImage[type] = image;
0297 }
0298 
0299 int EntryInternal::rating() const
0300 {
0301     return d->mRating;
0302 }
0303 
0304 void EntryInternal::setRating(int rating)
0305 {
0306     d->mRating = rating;
0307 }
0308 
0309 int EntryInternal::numberOfComments() const
0310 {
0311     return d->mNumberOfComments;
0312 }
0313 
0314 void EntryInternal::setNumberOfComments(int comments)
0315 {
0316     d->mNumberOfComments = comments;
0317 }
0318 
0319 int EntryInternal::downloadCount() const
0320 {
0321     return d->mDownloadCount;
0322 }
0323 
0324 void EntryInternal::setDownloadCount(int downloads)
0325 {
0326     d->mDownloadCount = downloads;
0327 }
0328 
0329 int EntryInternal::numberFans() const
0330 {
0331     return d->mNumberFans;
0332 }
0333 
0334 void EntryInternal::setNumberFans(int fans)
0335 {
0336     d->mNumberFans = fans;
0337 }
0338 
0339 QString EntryInternal::donationLink() const
0340 {
0341     return d->mDonationLink;
0342 }
0343 
0344 void EntryInternal::setDonationLink(const QString &link)
0345 {
0346     d->mDonationLink = link;
0347 }
0348 
0349 int EntryInternal::numberKnowledgebaseEntries() const
0350 {
0351     return d->mNumberKnowledgebaseEntries;
0352 }
0353 void EntryInternal::setNumberKnowledgebaseEntries(int num)
0354 {
0355     d->mNumberKnowledgebaseEntries = num;
0356 }
0357 
0358 QString EntryInternal::knowledgebaseLink() const
0359 {
0360     return d->mKnowledgebaseLink;
0361 }
0362 void EntryInternal::setKnowledgebaseLink(const QString &link)
0363 {
0364     d->mKnowledgebaseLink = link;
0365 }
0366 
0367 EntryInternal::Source EntryInternal::source() const
0368 {
0369     return d->mSource;
0370 }
0371 
0372 void EntryInternal::setEntryType(EntryInternal::EntryType type)
0373 {
0374     d->mEntryType = type;
0375 }
0376 
0377 EntryInternal::EntryType EntryInternal::entryType() const
0378 {
0379     return d->mEntryType;
0380 }
0381 
0382 void EntryInternal::setSource(Source source)
0383 {
0384     d->mSource = source;
0385 }
0386 
0387 KNS3::Entry::Status EntryInternal::status() const
0388 {
0389     return d->mStatus;
0390 }
0391 
0392 void EntryInternal::setStatus(KNS3::Entry::Status status)
0393 {
0394     d->mStatus = status;
0395 }
0396 
0397 void KNSCore::EntryInternal::setInstalledFiles(const QStringList &files)
0398 {
0399     d->mInstalledFiles = files;
0400 }
0401 
0402 QStringList KNSCore::EntryInternal::installedFiles() const
0403 {
0404     return d->mInstalledFiles;
0405 }
0406 
0407 void KNSCore::EntryInternal::setUnInstalledFiles(const QStringList &files)
0408 {
0409     d->mUnInstalledFiles = files;
0410 }
0411 
0412 QStringList KNSCore::EntryInternal::uninstalledFiles() const
0413 {
0414     return d->mUnInstalledFiles;
0415 }
0416 
0417 int KNSCore::EntryInternal::downloadLinkCount() const
0418 {
0419     return d->mDownloadLinkInformationList.size();
0420 }
0421 
0422 QList<KNSCore::EntryInternal::DownloadLinkInformation> KNSCore::EntryInternal::downloadLinkInformationList() const
0423 {
0424     return d->mDownloadLinkInformationList;
0425 }
0426 
0427 void KNSCore::EntryInternal::appendDownloadLinkInformation(const KNSCore::EntryInternal::DownloadLinkInformation &info)
0428 {
0429     d->mDownloadLinkInformationList.append(info);
0430 }
0431 
0432 void EntryInternal::clearDownloadLinkInformation()
0433 {
0434     d->mDownloadLinkInformationList.clear();
0435 }
0436 
0437 static QXmlStreamReader::TokenType readNextSkipComments(QXmlStreamReader *xml)
0438 {
0439     do {
0440         xml->readNext();
0441     } while (xml->tokenType() == QXmlStreamReader::Comment || (xml->tokenType() == QXmlStreamReader::Characters && xml->text().trimmed().isEmpty()));
0442     return xml->tokenType();
0443 }
0444 
0445 static QString readText(QXmlStreamReader *xml)
0446 {
0447     Q_ASSERT(xml->tokenType() == QXmlStreamReader::StartElement);
0448     QString ret;
0449     const auto token = readNextSkipComments(xml);
0450     if (token == QXmlStreamReader::Characters) {
0451         ret = xml->text().toString();
0452     }
0453     return ret;
0454 }
0455 
0456 static QString readStringTrimmed(QXmlStreamReader *xml)
0457 {
0458     Q_ASSERT(xml->tokenType() == QXmlStreamReader::StartElement);
0459     QString ret = readText(xml).trimmed();
0460 
0461     if (xml->tokenType() == QXmlStreamReader::Characters) {
0462         readNextSkipComments(xml);
0463     }
0464     Q_ASSERT(xml->tokenType() == QXmlStreamReader::EndElement);
0465     return ret;
0466 }
0467 
0468 static int readInt(QXmlStreamReader *xml)
0469 {
0470     Q_ASSERT(xml->tokenType() == QXmlStreamReader::StartElement);
0471     int ret = readText(xml).toInt();
0472 
0473     xml->readNext();
0474     Q_ASSERT(xml->tokenType() == QXmlStreamReader::EndElement);
0475     return ret;
0476 }
0477 
0478 bool KNSCore::EntryInternal::setEntryXML(QXmlStreamReader &reader)
0479 {
0480     if (reader.name() != QLatin1String("stuff")) {
0481         qCWarning(KNEWSTUFFCORE) << "Parsing Entry from invalid XML. Reader tag name was expected to be \"stuff\", but was found as:" << reader.name();
0482         return false;
0483     }
0484 
0485     d->mCategory = reader.attributes().value(QStringLiteral("category")).toString();
0486 
0487     while (!reader.atEnd()) {
0488         const auto token = readNextSkipComments(&reader);
0489         if (token == QXmlStreamReader::EndElement) {
0490             break;
0491         } else if (token != QXmlStreamReader::StartElement) {
0492             continue;
0493         }
0494 
0495         if (reader.name() == QLatin1String("name")) {
0496             // TODO maybe do something with the language attribute? QString lang = e.attribute("lang");
0497             d->mName = reader.readElementText(QXmlStreamReader::SkipChildElements);
0498         } else if (reader.name() == QLatin1String("author")) {
0499             // ### careful, the following variables are string views that become invalid when we
0500             // proceed with reading from reader, such as the readStringTrimmed call below does!
0501             const auto email = reader.attributes().value(QStringLiteral("email"));
0502             const auto jabber = reader.attributes().value(QStringLiteral("im"));
0503             const auto homepage = reader.attributes().value(QStringLiteral("homepage"));
0504             d->mAuthor.setEmail(email.toString());
0505             d->mAuthor.setJabber(jabber.toString());
0506             d->mAuthor.setHomepage(homepage.toString());
0507             d->mAuthor.setName(readStringTrimmed(&reader));
0508         } else if (reader.name() == QLatin1String("providerid")) {
0509             d->mProviderId = reader.readElementText(QXmlStreamReader::SkipChildElements);
0510         } else if (reader.name() == QLatin1String("homepage")) {
0511             d->mHomepage = QUrl(reader.readElementText(QXmlStreamReader::SkipChildElements));
0512         } else if (reader.name() == QLatin1String("licence")) { // krazy:exclude=spelling
0513             d->mLicense = readStringTrimmed(&reader);
0514         } else if (reader.name() == QLatin1String("summary")) {
0515             d->mSummary = reader.readElementText(QXmlStreamReader::SkipChildElements);
0516         } else if (reader.name() == QLatin1String("changelog")) {
0517             d->mChangelog = reader.readElementText(QXmlStreamReader::SkipChildElements);
0518         } else if (reader.name() == QLatin1String("version")) {
0519             d->mVersion = readStringTrimmed(&reader);
0520         } else if (reader.name() == QLatin1String("releasedate")) {
0521             d->mReleaseDate = QDate::fromString(readStringTrimmed(&reader), Qt::ISODate);
0522         } else if (reader.name() == QLatin1String("preview")) {
0523             // TODO support for all 6 image links
0524             d->mPreviewUrl[PreviewSmall1] = readStringTrimmed(&reader);
0525         } else if (reader.name() == QLatin1String("previewBig")) {
0526             d->mPreviewUrl[PreviewBig1] = readStringTrimmed(&reader);
0527         } else if (reader.name() == QLatin1String("payload")) {
0528             d->mPayload = readStringTrimmed(&reader);
0529         } else if (reader.name() == QLatin1String("rating")) {
0530             d->mRating = readInt(&reader);
0531         } else if (reader.name() == QLatin1String("downloads")) {
0532             d->mDownloadCount = readInt(&reader);
0533         } else if (reader.name() == QLatin1String("category")) {
0534             d->mCategory = reader.readElementText(QXmlStreamReader::SkipChildElements);
0535         } else if (reader.name() == QLatin1String("signature")) {
0536             d->mSignature = reader.readElementText(QXmlStreamReader::SkipChildElements);
0537         } else if (reader.name() == QLatin1String("checksum")) {
0538             d->mChecksum = reader.readElementText(QXmlStreamReader::SkipChildElements);
0539         } else if (reader.name() == QLatin1String("installedfile")) {
0540             d->mInstalledFiles.append(reader.readElementText(QXmlStreamReader::SkipChildElements));
0541         } else if (reader.name() == QLatin1String("id")) {
0542             d->mUniqueId = reader.readElementText(QXmlStreamReader::SkipChildElements);
0543         } else if (reader.name() == QLatin1String("tags")) {
0544             d->mTags = reader.readElementText(QXmlStreamReader::SkipChildElements).split(QLatin1Char(','));
0545         } else if (reader.name() == QLatin1String("status")) {
0546             const auto statusText = readText(&reader);
0547             if (statusText == QLatin1String("installed")) {
0548                 qCDebug(KNEWSTUFFCORE) << "Found an installed entry in registry";
0549                 d->mStatus = KNS3::Entry::Installed;
0550             } else if (statusText == QLatin1String("updateable")) {
0551                 d->mStatus = KNS3::Entry::Updateable;
0552             }
0553             if (reader.tokenType() == QXmlStreamReader::Characters) {
0554                 readNextSkipComments(&reader);
0555             }
0556         }
0557         Q_ASSERT_X(reader.tokenType() == QXmlStreamReader::EndElement,
0558                    Q_FUNC_INFO,
0559                    QStringLiteral("token name was %1 and the type was %2").arg(reader.name().toString(), reader.tokenString()).toLocal8Bit().data());
0560     }
0561 
0562     // Validation
0563     if (d->mName.isEmpty()) {
0564         qWarning() << "Entry: no name given";
0565         return false;
0566     }
0567 
0568     if (d->mUniqueId.isEmpty()) {
0569         if (!d->mPayload.isEmpty()) {
0570             d->mUniqueId = d->mPayload;
0571         } else {
0572             d->mUniqueId = d->mName;
0573         }
0574     }
0575 
0576     if (d->mPayload.isEmpty()) {
0577         qWarning() << "Entry: no payload URL given for: " << d->mName << " - " << d->mUniqueId;
0578         return false;
0579     }
0580     return true;
0581 }
0582 
0583 bool KNSCore::EntryInternal::setEntryXML(const QDomElement &xmldata)
0584 {
0585     if (xmldata.tagName() != QLatin1String("stuff")) {
0586         qWarning() << "Parsing Entry from invalid XML";
0587         return false;
0588     }
0589 
0590     d->mCategory = xmldata.attribute(QStringLiteral("category"));
0591 
0592     QDomNode n;
0593     for (n = xmldata.firstChild(); !n.isNull(); n = n.nextSibling()) {
0594         QDomElement e = n.toElement();
0595         if (e.tagName() == QLatin1String("name")) {
0596             // TODO maybe do something with the language attribute? QString lang = e.attribute("lang");
0597             d->mName = e.text().trimmed();
0598         } else if (e.tagName() == QLatin1String("author")) {
0599             QString email = e.attribute(QStringLiteral("email"));
0600             QString jabber = e.attribute(QStringLiteral("im"));
0601             QString homepage = e.attribute(QStringLiteral("homepage"));
0602             d->mAuthor.setName(e.text().trimmed());
0603             d->mAuthor.setEmail(email);
0604             d->mAuthor.setJabber(jabber);
0605             d->mAuthor.setHomepage(homepage);
0606         } else if (e.tagName() == QLatin1String("providerid")) {
0607             d->mProviderId = e.text();
0608         } else if (e.tagName() == QLatin1String("homepage")) {
0609             d->mHomepage = QUrl(e.text());
0610         } else if (e.tagName() == QLatin1String("licence")) { // krazy:exclude=spelling
0611             d->mLicense = e.text().trimmed();
0612         } else if (e.tagName() == QLatin1String("summary")) {
0613             d->mSummary = e.text();
0614         } else if (e.tagName() == QLatin1String("changelog")) {
0615             d->mChangelog = e.text();
0616         } else if (e.tagName() == QLatin1String("version")) {
0617             d->mVersion = e.text().trimmed();
0618         } else if (e.tagName() == QLatin1String("releasedate")) {
0619             d->mReleaseDate = QDate::fromString(e.text().trimmed(), Qt::ISODate);
0620         } else if (e.tagName() == QLatin1String("preview")) {
0621             // TODO support for all 6 image links
0622             d->mPreviewUrl[PreviewSmall1] = e.text().trimmed();
0623         } else if (e.tagName() == QLatin1String("previewBig")) {
0624             d->mPreviewUrl[PreviewBig1] = e.text().trimmed();
0625         } else if (e.tagName() == QLatin1String("payload")) {
0626             d->mPayload = e.text().trimmed();
0627         } else if (e.tagName() == QLatin1String("rating")) {
0628             d->mRating = e.text().toInt();
0629         } else if (e.tagName() == QLatin1String("downloads")) {
0630             d->mDownloadCount = e.text().toInt();
0631         } else if (e.tagName() == QLatin1String("category")) {
0632             d->mCategory = e.text();
0633         } else if (e.tagName() == QLatin1String("signature")) {
0634             d->mSignature = e.text();
0635         } else if (e.tagName() == QLatin1String("checksum")) {
0636             d->mChecksum = e.text();
0637         } else if (e.tagName() == QLatin1String("installedfile")) {
0638             // TODO KF6 Add a "installeddirectory" entry to avoid
0639             // all the issues with the "/*" notation which is currently used as a workaround
0640             d->mInstalledFiles.append(e.text());
0641         } else if (e.tagName() == QLatin1String("id")) {
0642             d->mUniqueId = e.text();
0643         } else if (e.tagName() == QLatin1String("tags")) {
0644             d->mTags = e.text().split(QLatin1Char(','));
0645         } else if (e.tagName() == QLatin1String("status")) {
0646             QString statusText = e.text();
0647             if (statusText == QLatin1String("installed")) {
0648                 qCDebug(KNEWSTUFFCORE) << "Found an installed entry in registry";
0649                 d->mStatus = KNS3::Entry::Installed;
0650             } else if (statusText == QLatin1String("updateable")) {
0651                 d->mStatus = KNS3::Entry::Updateable;
0652             }
0653         }
0654     }
0655 
0656     // Validation
0657     if (d->mName.isEmpty()) {
0658         qWarning() << "Entry: no name given";
0659         return false;
0660     }
0661 
0662     if (d->mUniqueId.isEmpty()) {
0663         if (!d->mPayload.isEmpty()) {
0664             d->mUniqueId = d->mPayload;
0665         } else {
0666             d->mUniqueId = d->mName;
0667         }
0668     }
0669 
0670     if (d->mPayload.isEmpty()) {
0671         qWarning() << "Entry: no payload URL given for: " << d->mName << " - " << d->mUniqueId;
0672         return false;
0673     }
0674     return true;
0675 }
0676 
0677 /**
0678  * get the xml string for the entry
0679  */
0680 QDomElement KNSCore::EntryInternal::entryXML() const
0681 {
0682     Q_ASSERT(!d->mUniqueId.isEmpty());
0683     Q_ASSERT(!d->mProviderId.isEmpty());
0684 
0685     QDomDocument doc;
0686 
0687     QDomElement el = doc.createElement(QStringLiteral("stuff"));
0688     el.setAttribute(QStringLiteral("category"), d->mCategory);
0689 
0690     QString name = d->mName;
0691 
0692     QDomElement e;
0693     e = addElement(doc, el, QStringLiteral("name"), name);
0694     // todo: add language attribute
0695     (void)addElement(doc, el, QStringLiteral("providerid"), d->mProviderId);
0696 
0697     QDomElement author = addElement(doc, el, QStringLiteral("author"), d->mAuthor.name());
0698     if (!d->mAuthor.email().isEmpty()) {
0699         author.setAttribute(QStringLiteral("email"), d->mAuthor.email());
0700     }
0701     if (!d->mAuthor.homepage().isEmpty()) {
0702         author.setAttribute(QStringLiteral("homepage"), d->mAuthor.homepage());
0703     }
0704     if (!d->mAuthor.jabber().isEmpty()) {
0705         author.setAttribute(QStringLiteral("im"), d->mAuthor.jabber());
0706     }
0707     // FIXME: 'jabber' or 'im'? consult with kopete guys...
0708     addElement(doc, el, QStringLiteral("homepage"), d->mHomepage.url());
0709     (void)addElement(doc, el, QStringLiteral("licence"), d->mLicense); // krazy:exclude=spelling
0710     (void)addElement(doc, el, QStringLiteral("version"), d->mVersion);
0711     if ((d->mRating > 0) || (d->mDownloadCount > 0)) {
0712         (void)addElement(doc, el, QStringLiteral("rating"), QString::number(d->mRating));
0713         (void)addElement(doc, el, QStringLiteral("downloads"), QString::number(d->mDownloadCount));
0714     }
0715     if (!d->mSignature.isEmpty()) {
0716         (void)addElement(doc, el, QStringLiteral("signature"), d->mSignature);
0717     }
0718     if (!d->mChecksum.isEmpty()) {
0719         (void)addElement(doc, el, QStringLiteral("checksum"), d->mChecksum);
0720     }
0721     for (const QString &file : std::as_const(d->mInstalledFiles)) {
0722         (void)addElement(doc, el, QStringLiteral("installedfile"), file);
0723     }
0724     if (!d->mUniqueId.isEmpty()) {
0725         addElement(doc, el, QStringLiteral("id"), d->mUniqueId);
0726     }
0727 
0728     (void)addElement(doc, el, QStringLiteral("releasedate"), d->mReleaseDate.toString(Qt::ISODate));
0729 
0730     e = addElement(doc, el, QStringLiteral("summary"), d->mSummary);
0731     e = addElement(doc, el, QStringLiteral("changelog"), d->mChangelog);
0732     e = addElement(doc, el, QStringLiteral("preview"), d->mPreviewUrl[PreviewSmall1]);
0733     e = addElement(doc, el, QStringLiteral("previewBig"), d->mPreviewUrl[PreviewBig1]);
0734     e = addElement(doc, el, QStringLiteral("payload"), d->mPayload);
0735     e = addElement(doc, el, QStringLiteral("tags"), d->mTags.join(QLatin1Char(',')));
0736 
0737     if (d->mStatus == KNS3::Entry::Installed) {
0738         (void)addElement(doc, el, QStringLiteral("status"), QStringLiteral("installed"));
0739     }
0740     if (d->mStatus == KNS3::Entry::Updateable) {
0741         (void)addElement(doc, el, QStringLiteral("status"), QStringLiteral("updateable"));
0742     }
0743 
0744     return el;
0745 }
0746 
0747 KNSCore::EntryInternal EntryInternal::fromEntry(const KNS3::Entry &entry)
0748 {
0749     return entry.d->e;
0750 }
0751 
0752 QString KNSCore::replaceBBCode(const QString &unformattedText)
0753 {
0754     QString text(unformattedText);
0755     text.replace(QLatin1String("[b]"), QLatin1String("<b>"));
0756     text.replace(QLatin1String("[/b]"), QLatin1String("</b>"));
0757     text.replace(QLatin1String("[i]"), QLatin1String("<i>"));
0758     text.replace(QLatin1String("[/i]"), QLatin1String("</i>"));
0759     text.replace(QLatin1String("[u]"), QLatin1String("<i>"));
0760     text.replace(QLatin1String("[/u]"), QLatin1String("</i>"));
0761     text.replace(QLatin1String("\\\""), QLatin1String("\""));
0762     text.replace(QLatin1String("\\\'"), QLatin1String("\'"));
0763     text.replace(QLatin1String("[li]"), QLatin1String("* ")); // TODO: better replacement for list elements?
0764     text.remove(QStringLiteral("[/li]"));
0765     text.remove(QStringLiteral("[url]"));
0766     text.remove(QStringLiteral("[/url]"));
0767     return text;
0768 }
0769 
0770 #include "moc_entryinternal.cpp"