OSCAR-code/oscar/updateparser.cpp
2024-01-31 19:14:19 -05:00

334 lines
11 KiB
C++

/* UpdateParser Implementation (Autoupdater component)
*
* Copyright (c) 2019-2024 The OSCAR Team
* Copyright (c) 2011-2018 Mark Watkins
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the source code
* for more details. */
#include <QDebug>
#include <QXmlStreamAttribute>
#include "updateparser.h"
Update::Update()
{
size = 0;
}
Update::Update(const Update &copy)
{
type = copy.type;
version = copy.version;
platform = copy.platform;
date = copy.date;
filename = copy.filename;
url = copy.url;
hash = copy.hash;
size = copy.size;
notes = copy.notes;
unzipped_path = copy.unzipped_path;
}
Update::Update(QString _type, QString _version, QString _platform, QDate _date)
{
type = _type;
version = _version;
platform = _platform;
date = _date;
size = 0;
}
bool UpdateParser::startDocument()
{
inRelease = false;
inUpdate = false;
inNotes = false;
inUpdateNotes = false;
release = nullptr;
update = nullptr;
return true;
}
bool UpdateParser::endElement(const QString &namespaceURI, const QString &localName,
const QString &qName)
{
Q_UNUSED(namespaceURI)
Q_UNUSED(localName)
QString name = qName.toLower();
if (name == "release") {
inRelease = false;
release = nullptr;
} else if (inRelease && name == "update") {
inUpdate = false;
update = nullptr;
} else if (inUpdate && name == "notes") {
inUpdateNotes = false;
} else if (inRelease && name == "notes") {
inNotes = false;
}
return true;
}
bool UpdateParser::characters(const QString &ch)
{
if (inUpdateNotes) {
update->notes = ch;
} else if (inNotes) {
release->notes[platform] = ch;
}
return true;
}
UpdateStatus lookupUpdateStatus(QString stat)
{
UpdateStatus status = UPDATE_TESTING;
if (stat == "testing") { status = UPDATE_TESTING; }
else if (stat == "beta") { status = UPDATE_BETA; }
else if (stat == "stable") { status = UPDATE_STABLE; }
else if (stat == "critical") { status = UPDATE_CRITICAL; }
return status;
}
bool UpdateParser::startElement(const QString &namespaceURI, const QString &localName,
const QString &qName, const QXmlAttributes &atts)
{
Q_UNUSED(namespaceURI)
Q_UNUSED(localName)
QString name = qName.toLower();
if (inRelease && name == "update") {
QString ver, type;
UpdateStatus updatestatus = UPDATE_TESTING;
for (int i = 0; i < atts.count(); i++) {
if (atts.localName(i) == "type") {
type = atts.value(i).toLower();
}
if (atts.localName(i) == "version") {
ver = atts.value(i).toLower();
}
if (atts.localName(i) == "platform") {
platform = atts.value(i).toLower();
}
if (atts.localName(i) == "release_date") {
release_date = atts.value(i);
}
if (atts.localName(i) == "status") {
updatestatus = lookupUpdateStatus(atts.value(i).toLower());
}
}
QDate date = QDate::fromString(release_date, "yyyy-MM-dd");
if (!date.isValid()) { date = QDate::currentDate(); }
release->updates[platform].push_back(Update(type, ver, platform, date));
update = &release->updates[platform][release->updates[platform].size() - 1];
update->status = updatestatus;
inUpdate = true;
} else if (inRelease && name == "info") {
QString tmp = atts.value("url");
if (tmp.isEmpty()) { return false; }
release->info_url = tmp;
} else if (inUpdate && name == "file") {
for (int i = 0; i < atts.count(); i++) {
if (atts.localName(i) == "name") {
update->filename = atts.value(i);
}
if (atts.localName(i) == "size") {
bool ok;
update->size = atts.value(i).toLongLong(&ok);
//if (!ok) return false;
}
if (atts.localName(i) == "url") {
update->url = atts.value(i);
}
if (atts.localName(i) == "hash") {
update->hash = atts.value(i).toLower();
}
}
} else if (inUpdate && name == "notes") {
inUpdateNotes = true;
} else if (inRelease && name == "notes") {
platform = "";
if (atts.count() >= 1) {
if (atts.localName(0) == "platform") {
platform = atts.value(0);
}
}
inNotes = true;
} else if (name == "release") {
inRelease = true;
QString codename;
UpdateStatus status = UPDATE_TESTING;
for (int i = 0; i < atts.count(); i++) {
if (atts.localName(i) == "version") {
version = atts.value(i).toLower();
}
if (atts.localName(i) == "codename") {
codename = atts.value(i);
}
if (atts.localName(i) == "status") {
status = lookupUpdateStatus(atts.value(i).toLower());
}
}
releases[version] = Release(version, codename, status);
release = &releases[version];
if (version > latest_version) { latest_version = version; }
}
return true;
}
bool UpdateParser::endDocument()
{
/*for (QHash<QString,Release>::iterator r=releases.begin();r!=releases.end();r++) {
Release & rel=r.value();
qDebug() << "New Version" << r.key() << rel.codename << rel.notes;
for (QHash<QString,Update>::iterator u=rel.files.begin();u!=rel.files.end();u++) {
Update & up=u.value();
qDebug() << "Platform:" << u.key() << up.filename << up.date;
}
}*/
return true;
}
/////////////////////////////////////////////////////////////////////
// Updates Parser implementation
/////////////////////////////////////////////////////////////////////
UpdatesParser::UpdatesParser()
{
}
QString UpdatesParser::errorString() const
{
return QObject::tr("%1\nLine %2, column %3")
.arg(xml.errorString())
.arg(xml.lineNumber())
.arg(xml.columnNumber());
}
bool UpdatesParser::read(QIODevice *device)
{
xml.setDevice(device);
if (xml.readNextStartElement()) {
if (xml.name() == "Updates") { // && xml.attributes().value("version") == "1.0")
readUpdates();
} else {
xml.raiseError(QObject::tr("Could not parse Updates.xml file."));
}
}
return !xml.error();
}
void UpdatesParser::readUpdates()
{
if (!xml.isStartElement() || (xml.name() != "Updates")) {
qWarning() << "UpdatesParser::readUpdates() condition check failed";
}
// Q_ ASSERT(xml.isStartElement() && xml.name() == "Updates");
while (xml.readNextStartElement()) {
if (xml.name().compare(QLatin1String("PackageUpdate"),Qt::CaseInsensitive)==0) {
readPackageUpdate();
} else {
qDebug() << "Skipping Updates.xml tag" << xml.name();
xml.skipCurrentElement();
}
}
}
void UpdatesParser::readPackageUpdate()
{
if (!xml.isStartElement() || (xml.name().compare(QLatin1String("PackageUpdate"),Qt::CaseInsensitive)!=0)) {
qWarning() << "UpdatesParser::readPackageUpdate() condition check failed";
return;
}
package = PackageUpdate();
while (xml.readNextStartElement()) {
if (xml.name().compare(QLatin1String("Name"),Qt::CaseInsensitive)==0) {
package.name = xml.readElementText().toLower();
} else if (xml.name().compare(QLatin1String("DisplayName"),Qt::CaseInsensitive)==0) {
package.displayName = xml.readElementText();
} else if (xml.name().compare(QLatin1String("Description"),Qt::CaseInsensitive)==0) {
package.description = xml.readElementText();
} else if (xml.name().compare(QLatin1String("Version"),Qt::CaseInsensitive)==0) {
package.versionString = xml.readElementText();
} else if (xml.name().compare(QLatin1String("ReleaseDate"),Qt::CaseInsensitive)==0) {
package.releaseDate = QDate().fromString(xml.readElementText(), "yyyy-MM-dd");
} else if (xml.name().compare(QLatin1String("Default"),Qt::CaseInsensitive)==0) {
package.defaultInstall = xml.readElementText().compare("true") == 0;
} else if (xml.name().compare(QLatin1String("ForcedInstallation"),Qt::CaseInsensitive)==0) {
package.forcedInstall = xml.readElementText().compare("true") == 0;
} else if (xml.name().compare(QLatin1String("Script"),Qt::CaseInsensitive)==0) {
package.script = xml.readElementText();
} else if (xml.name().compare(QLatin1String("Dependencies"),Qt::CaseInsensitive)==0) {
package.dependencies = xml.readElementText().split(",");
} else if (xml.name().compare(QLatin1String("UpdateFile"),Qt::CaseInsensitive)==0) {
for (int i=0; i<xml.attributes().size(); ++i) {
const QXmlStreamAttribute & at = xml.attributes().at(i);
if (at.name().compare(QLatin1String("CompressedSize"), Qt::CaseInsensitive)==0) {
package.compressedSize = at.value().toLong();
} else if (at.name().compare(QLatin1String("UncompressedSize"),Qt::CaseInsensitive)==0) {
package.uncompressedSize = at.value().toLong();
} else if (at.name().compare(QLatin1String("OS"),Qt::CaseInsensitive)==0) {
package.os = at.value().toString();
}
}
xml.skipCurrentElement();
} else if (xml.name().compare(QLatin1String("DownloadableArchives"))==0) {
package.downloadArchives = xml.readElementText().split(",");
} else if (xml.name().compare(QLatin1String("Licenses"),Qt::CaseInsensitive)==0) {
while (xml.readNextStartElement()) {
if (xml.name().compare(QLatin1String("License"),Qt::CaseInsensitive)==0) {
QString name;
QString file;
for (int i=0; i<xml.attributes().size(); ++i) {
const QXmlStreamAttribute & at = xml.attributes().at(i);
if (at.name().compare(QLatin1String("name"), Qt::CaseInsensitive)==0) {
name = at.value().toString();
} else if (at.name().compare(QLatin1String("file"),Qt::CaseInsensitive)==0) {
file = at.value().toString();
}
}
package.license[name]=file;
xml.skipCurrentElement();
} else {
qDebug() << "Lic Skipping Updates.xml tag" << xml.name();
xml.skipCurrentElement();
}
}
} else if (xml.name().compare(QLatin1String("SHA1"),Qt::CaseInsensitive)==0) {
package.sha1 = xml.readElementText();
} else {
qDebug() << "UP Skipping Updates.xml tag" << xml.name();
xml.skipCurrentElement();
}
};
packages[package.name] = package;
}