/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "updatesinfo_p.h" #include "utils.h" #include #include #include #include #include #include using namespace KDUpdater; UpdatesInfoData::UpdatesInfoData() : error(UpdatesInfo::NotYetReadError) { } UpdatesInfoData::~UpdatesInfoData() { } void UpdatesInfoData::setInvalidContentError(const QString &detail) { error = UpdatesInfo::InvalidContentError; errorMessage = tr("Updates.xml contains invalid content: %1").arg(detail); } void UpdatesInfoData::parseFile(const QString &updateXmlFile) { QFile file(updateXmlFile); if (!file.open(QFile::ReadOnly)) { error = UpdatesInfo::CouldNotReadUpdateInfoFileError; errorMessage = tr("Cannot read \"%1\"").arg(updateXmlFile); return; } QDomDocument doc; QString parseErrorMessage; int parseErrorLine, parseErrorColumn; if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) { error = UpdatesInfo::InvalidXmlError; errorMessage = tr("Parse error in %1 at %2, %3: %4").arg(updateXmlFile, QString::number(parseErrorLine), QString::number(parseErrorColumn), parseErrorMessage); return; } QDomElement rootE = doc.documentElement(); if (rootE.tagName() != QLatin1String("Updates")) { setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(rootE.tagName())); return; } QDomNodeList childNodes = rootE.childNodes(); for(int i = 0; i < childNodes.count(); i++) { const QDomElement childE = childNodes.at(i).toElement(); if (childE.isNull()) continue; if (childE.tagName() == QLatin1String("ApplicationName")) applicationName = childE.text(); else if (childE.tagName() == QLatin1String("ApplicationVersion")) applicationVersion = childE.text(); else if (childE.tagName() == QLatin1String("PackageUpdate")) { if (!parsePackageUpdateElement(childE)) return; //error handled in subroutine } } if (applicationName.isEmpty()) { setInvalidContentError(tr("ApplicationName element is missing.")); return; } if (applicationVersion.isEmpty()) { setInvalidContentError(tr("ApplicationVersion element is missing.")); return; } errorMessage.clear(); error = UpdatesInfo::NoError; } bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE) { if (updateE.isNull()) return false; UpdateInfo info; QMap localizedDescriptions; for (int i = 0; i < updateE.childNodes().count(); i++) { QDomElement childE = updateE.childNodes().at(i).toElement(); if (childE.isNull()) continue; if (childE.tagName() == QLatin1String("ReleaseNotes")) { info.data[childE.tagName()] = QUrl(childE.text()); } else if (childE.tagName() == QLatin1String("Licenses")) { QHash licenseHash; const QDomNodeList licenseNodes = childE.childNodes(); for (int i = 0; i < licenseNodes.count(); ++i) { const QDomNode licenseNode = licenseNodes.at(i); if (licenseNode.nodeName() == QLatin1String("License")) { QDomElement element = licenseNode.toElement(); QVariantMap attributes; attributes.insert(QLatin1String("file"), element.attributeNode(QLatin1String("file")).value()); if (!element.attributeNode(QLatin1String("priority")).isNull()) attributes.insert(QLatin1String("priority"), element.attributeNode(QLatin1String("priority")).value()); else attributes.insert(QLatin1String("priority"), QLatin1String("0")); licenseHash.insert(element.attributeNode(QLatin1String("name")).value(), attributes); } } if (!licenseHash.isEmpty()) info.data.insert(QLatin1String("Licenses"), licenseHash); } else if (childE.tagName() == QLatin1String("Version")) { info.data.insert(QLatin1String("inheritVersionFrom"), childE.attribute(QLatin1String("inheritVersionFrom"))); info.data[childE.tagName()] = childE.text(); } else if (childE.tagName() == QLatin1String("DisplayName")) { processLocalizedTag(childE, info.data); } else if (childE.tagName() == QLatin1String("Description")) { if (!childE.hasAttribute(QLatin1String("xml:lang"))) info.data[QLatin1String("Description")] = childE.text(); QString languageAttribute = childE.attribute(QLatin1String("xml:lang"), QLatin1String("en")); localizedDescriptions.insert(languageAttribute.toLower(), childE.text()); } else if (childE.tagName() == QLatin1String("UpdateFile")) { info.data[QLatin1String("CompressedSize")] = childE.attribute(QLatin1String("CompressedSize")); info.data[QLatin1String("UncompressedSize")] = childE.attribute(QLatin1String("UncompressedSize")); } else if (childE.tagName() == QLatin1String("Operations")) { const QDomNodeList operationNodes = childE.childNodes(); QVariant operationListVariant = parseOperations(childE.childNodes()); info.data.insert(QLatin1String("Operations"), operationListVariant); } else { info.data[childE.tagName()] = childE.text(); } } QStringList candidates; foreach (const QString &lang, QLocale().uiLanguages()) candidates << QInstaller::localeCandidates(lang.toLower()); foreach (const QString &candidate, candidates) { if (localizedDescriptions.contains(candidate)) { info.data[QLatin1String("Description")] = localizedDescriptions.value(candidate); break; } } if (!info.data.contains(QLatin1String("Name"))) { setInvalidContentError(tr("PackageUpdate element without Name")); return false; } if (!info.data.contains(QLatin1String("Version"))) { setInvalidContentError(tr("PackageUpdate element without Version")); return false; } if (!info.data.contains(QLatin1String("ReleaseDate"))) { setInvalidContentError(tr("PackageUpdate element without ReleaseDate")); return false; } updateInfoList.append(info); return true; } void UpdatesInfoData::processLocalizedTag(const QDomElement &childE, QHash &info) const { QString languageAttribute = childE.attribute(QLatin1String("xml:lang")).toLower(); if (!info.contains(childE.tagName()) && (languageAttribute.isEmpty())) info[childE.tagName()] = childE.text(); // overwrite default if we have a language specific description if (QLocale().name().startsWith(languageAttribute, Qt::CaseInsensitive)) info[childE.tagName()] = childE.text(); } QVariant UpdatesInfoData::parseOperations(const QDomNodeList &operationNodes) { QVariant operationListVariant; QList> operationsList; for (int i = 0; i < operationNodes.count(); ++i) { const QDomNode operationNode = operationNodes.at(i); if (operationNode.nodeName() == QLatin1String("Operation")) { const QDomNodeList argumentNodes = operationNode.childNodes(); QStringList attributes; for (int i = 0; i < argumentNodes.count(); ++i) { const QDomNode argumentNode = argumentNodes.at(i); if (argumentNode.nodeName() == QLatin1String("Argument")) { QDomElement argumentElement = argumentNode.toElement(); attributes.append(argumentElement.text()); } } QPair pair; pair.first = operationNode.toElement().attributeNode(QLatin1String("name")).value(); pair.second = attributes; operationsList.append(pair); } } operationListVariant.setValue(operationsList); return operationListVariant; } // // UpdatesInfo // UpdatesInfo::UpdatesInfo() : d(new UpdatesInfoData) { } UpdatesInfo::~UpdatesInfo() { } bool UpdatesInfo::isValid() const { return d->error == NoError; } QString UpdatesInfo::errorString() const { return d->errorMessage; } void UpdatesInfo::setFileName(const QString &updateXmlFile) { if (d->updateXmlFile == updateXmlFile) return; d->applicationName.clear(); d->applicationVersion.clear(); d->updateInfoList.clear(); d->updateXmlFile = updateXmlFile; d->parseFile(d->updateXmlFile); } QString UpdatesInfo::fileName() const { return d->updateXmlFile; } QString UpdatesInfo::applicationName() const { return d->applicationName; } QString UpdatesInfo::applicationVersion() const { return d->applicationVersion; } int UpdatesInfo::updateInfoCount() const { return d->updateInfoList.count(); } UpdateInfo UpdatesInfo::updateInfo(int index) const { if (index < 0 || index >= d->updateInfoList.count()) return UpdateInfo(); return d->updateInfoList.at(index); } QList UpdatesInfo::updatesInfo() const { return d->updateInfoList; }