diff --git a/sleepyhead/UpdaterWindow.cpp b/sleepyhead/UpdaterWindow.cpp index a893306b..22b30f4d 100644 --- a/sleepyhead/UpdaterWindow.cpp +++ b/sleepyhead/UpdaterWindow.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "SleepLib/profiles.h" #include @@ -74,7 +75,7 @@ UpdaterWindow::~UpdaterWindow() void UpdaterWindow::checkForUpdates() { - QString filename = QApplication::applicationDirPath() + "/update.xml"; + QString filename = QApplication::applicationDirPath() + "/Updates.xml"; // Check updates.xml file if it's still recent.. if (QFile::exists(filename)) { @@ -82,7 +83,7 @@ void UpdaterWindow::checkForUpdates() QDateTime created = fi.created(); int age = created.secsTo(QDateTime::currentDateTime()); - if (age < 7200) { + if (age < 0) { // 7200) { QFile file(filename); file.open(QFile::ReadOnly); ParseUpdateXML(&file); @@ -94,7 +95,7 @@ void UpdaterWindow::checkForUpdates() mainwin->Notify(tr("Checking for SleepyHead Updates")); // language code? - update_url = QUrl("http://sourceforge.net/projects/sleepyhead/files/AutoUpdate/update.xml/download"); + update_url = QUrl(QString("http://sourceforge.net/projects/sleepyhead/files/AutoUpdate/%1/Updates.xml/download").arg(PlatformString)); downloadUpdateXML(); } @@ -189,6 +190,14 @@ void UpdaterWindow::requestFile() int checkVersionStatus(QString statusstr) { + bool ok; + // because Qt Install Framework is dumb and doesn't handle beta/release strings in version numbers, + // so we store them numerically instead + int v =statusstr.toInt(&ok); + if (ok) { + return v; + } + if (statusstr.compare("testing", Qt::CaseInsensitive) == 0) return 0; else if (statusstr.compare("beta", Qt::CaseInsensitive) == 0) return 1; else if (statusstr.compare("rc", Qt::CaseInsensitive) == 0) return 2; @@ -254,10 +263,10 @@ int compareVersion(QString version) return -1; } - short major = parts[0].toInt(&ok); + int major = parts[0].toInt(&ok); if (!ok) return -1; - short minor = parts[1].toInt(&ok); + int minor = parts[1].toInt(&ok); if (!ok) return -1; if (major > major_version) { @@ -272,13 +281,20 @@ int compareVersion(QString version) return -1; } + int build_index = 1; + int build = 0; + int status = 0; QStringList patchver = parts[2].split("-"); - if (patchver.size() < 3) { - // dodgy version string supplied. + if (patchver.size() >= 3) { + build_index = 2; + status = checkVersionStatus(patchver[1]); + + } else if (patchver.size() < 2) { return -1; + // dodgy version string supplied. } - short rev = patchver[0].toInt(&ok); + int rev = patchver[0].toInt(&ok); if (!ok) return -1; if (rev > revision_number) { return 1; @@ -286,12 +302,16 @@ int compareVersion(QString version) return -1; } - short build = patchver[2].toInt(&ok); + + build = patchver[build_index].toInt(&ok); if (!ok) return -1; - int status = checkVersionStatus(patchver[1]); int rstatus = checkVersionStatus(ReleaseStatus); + if (patchver.size() == 3) { + // read it if it's actually present. + } + if (status > rstatus) { return 1; } else if (status < rstatus) { @@ -308,6 +328,61 @@ int compareVersion(QString version) return 0; } +const QString UPDATE_SleepyHead = "com.jedimark.sleepyhead"; +const QString UPDATE_QT = "com.jedimark.sleepyhead.qtlibraries"; +const QString UPDATE_Translations = "com.jedimark.sleepyhead.translations"; + +bool SpawnApp(QString apppath, QStringList args) +{ +#ifdef Q_OS_MAC + // In Mac OS the full path of aplication binary is: + // /myApp.app/Contents/MacOS/myApp + + QStringList arglist; + arglist << "-n"; + arglist << apppath; + arglist.append(args); + + return QProcess::startDetached("/usr/bin/open", arglist); +#else + return QProcess::startDetached(apppath, args); +#endif +} + +void StartMaintenanceTool() +{ + +} + +//New, Qt Installer framework version +void UpdaterWindow::ParseUpdatesXML(QIODevice *dev) +{ + if (updatesparser.read(dev)) { + ui->plainTextEdit->appendPlainText(tr("XML update structure parsed cleanly")); + if (updatesparser.packages.contains(UPDATE_SleepyHead)) { + PackageUpdate & update = updatesparser.packages[UPDATE_SleepyHead]; + if (compareVersion(update.versionString) <= 0) { + mainwin->Notify(tr("No updates were found for your platform."), tr("SleepyHead Updates"), 5000); + PREF[STR_GEN_UpdatesLastChecked] = QDateTime::currentDateTime(); + close(); + return; + } else { + if (QMessageBox::question(mainwin, tr("SleepyHead Updates"), + tr("New SleepyHead version %1 was detected.").arg(update.versionString)+"\n\n"+ + tr("Would you like to download and install it now?"), + QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + + StartMaintenanceTool(); + QApplication::instance()->quit(); + } + } + } + } else { + qDebug() << "Couldn't parse Updates.xml file"; + } +} + +// Old void UpdaterWindow::ParseUpdateXML(QIODevice *dev) { QXmlInputSource src(dev); @@ -462,7 +537,7 @@ void UpdaterWindow::replyFinished(QNetworkReply *reply) } ui->plainTextEdit->appendPlainText(tr("%1 bytes received").arg(reply->size())); - QString filename = QApplication::applicationDirPath() + "/update.xml"; + QString filename = QApplication::applicationDirPath() + "/Updates.xml"; qDebug() << filename; QFile file(filename); file.open(QFile::WriteOnly); @@ -470,7 +545,7 @@ void UpdaterWindow::replyFinished(QNetworkReply *reply) file.close(); file.open(QFile::ReadOnly); //QTextStream ts(&file); - ParseUpdateXML(&file); + ParseUpdatesXML(&file); file.close(); reply->deleteLater(); } else if (requestmode == RM_GetFile) { diff --git a/sleepyhead/UpdaterWindow.h b/sleepyhead/UpdaterWindow.h index 7b93c89a..231a9dea 100644 --- a/sleepyhead/UpdaterWindow.h +++ b/sleepyhead/UpdaterWindow.h @@ -51,6 +51,7 @@ class UpdaterWindow : public QMainWindow \brief Parses the update.xml from either QFile or QNetworkReply source */ void ParseUpdateXML(QIODevice *dev); + void ParseUpdatesXML(QIODevice *dev); protected slots: //! \brief Network reply completed @@ -86,6 +87,9 @@ class UpdaterWindow : public QMainWindow //! \brief Holds the results of parsing the update.xml file UpdateParser updateparser; + // new parser + UpdatesParser updatesparser; + Ui::UpdaterWindow *ui; RequestMode requestmode; diff --git a/sleepyhead/icons/airsense10.png b/sleepyhead/icons/airsense10.png index 2b1a5631..b2f2f4f2 100644 Binary files a/sleepyhead/icons/airsense10.png and b/sleepyhead/icons/airsense10.png differ diff --git a/sleepyhead/main.cpp b/sleepyhead/main.cpp index 31d5ce0d..98da0244 100644 --- a/sleepyhead/main.cpp +++ b/sleepyhead/main.cpp @@ -510,7 +510,7 @@ retry_directory: } - //if (check_updates) { mainwin->CheckForUpdates(); } + // if (check_updates) { mainwin->CheckForUpdates(); } w.show(); diff --git a/sleepyhead/mainwindow.ui b/sleepyhead/mainwindow.ui index de6ba7f1..fc6ffba1 100644 --- a/sleepyhead/mainwindow.ui +++ b/sleepyhead/mainwindow.ui @@ -1896,8 +1896,8 @@ border: 2px solid #56789a; border-radius: 30px; 0 0 - 77 - 236 + 100 + 30 @@ -3044,7 +3044,7 @@ border-radius: 10px; 0 0 - 77 + 76 236 @@ -3172,10 +3172,10 @@ border-radius: 10px; - + diff --git a/sleepyhead/updateparser.cpp b/sleepyhead/updateparser.cpp index 201e13f7..4b002948 100644 --- a/sleepyhead/updateparser.cpp +++ b/sleepyhead/updateparser.cpp @@ -7,6 +7,7 @@ * distribution for more details. */ #include +#include #include "updateparser.h" @@ -215,3 +216,116 @@ bool UpdateParser::endDocument() 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() +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "Updates"); + + while (xml.readNextStartElement()) { + if (xml.name().compare("PackageUpdate",Qt::CaseInsensitive)==0) { + readPackageUpdate(); + } else { + qDebug() << "Skipping Updates.xml tag" << xml.name(); + xml.skipCurrentElement(); + } + } + +} + +void UpdatesParser::readPackageUpdate() +{ + Q_ASSERT(xml.isStartElement() && (xml.name().compare("PackageUpdate",Qt::CaseInsensitive)==0)); + package = PackageUpdate(); + + while (xml.readNextStartElement()) { + if (xml.name().compare("Name",Qt::CaseInsensitive)==0) { + package.name = xml.readElementText().toLower(); + } else if (xml.name().compare("DisplayName",Qt::CaseInsensitive)==0) { + package.displayName = xml.readElementText(); + } else if (xml.name().compare("Description",Qt::CaseInsensitive)==0) { + package.description = xml.readElementText(); + } else if (xml.name().compare("Version",Qt::CaseInsensitive)==0) { + package.versionString = xml.readElementText(); + } else if (xml.name().compare("ReleaseDate",Qt::CaseInsensitive)==0) { + package.releaseDate = QDate().fromString(xml.readElementText(), "yyyy-MM-dd"); + } else if (xml.name().compare("Default",Qt::CaseInsensitive)==0) { + package.defaultInstall = xml.readElementText().compare("true") == 0; + } else if (xml.name().compare("ForcedInstallation",Qt::CaseInsensitive)==0) { + package.forcedInstall = xml.readElementText().compare("true") == 0; + } else if (xml.name().compare("Script",Qt::CaseInsensitive)==0) { + package.script = xml.readElementText(); + } else if (xml.name().compare("Dependencies",Qt::CaseInsensitive)==0) { + package.dependencies = xml.readElementText().split(","); + } else if (xml.name().compare("UpdateFile",Qt::CaseInsensitive)==0) { + for (int i=0; i +#include #include #include @@ -90,6 +91,68 @@ class UpdateParser: public QXmlDefaultHandler bool inUpdateNotes; }; +class PackageUpdate { +public: + PackageUpdate() {} + PackageUpdate(const PackageUpdate & copy) { + // Seriously, why do I still have to do this crud by hand + // Where is the shortcut to save time here in the latest C++ extensions? + name = copy.name; + displayName = copy.displayName; + description = copy.description; + versionString = copy.versionString; + releaseDate = copy.releaseDate; + defaultInstall = copy.defaultInstall; + installScript = copy.installScript; + dependencies = copy.dependencies; + script = copy.script; + forcedInstall = copy.forcedInstall; + downloadArchives = copy.downloadArchives; + license = copy.license; + sha1 = copy.sha1; + compressedSize = copy.compressedSize; + uncompressedSize = copy.uncompressedSize; + os = copy.os; + } + + QString name; + QString displayName; + QString description; + QString versionString; + QDate releaseDate; + bool defaultInstall; + QString installScript; + QStringList dependencies; + QString script; + bool forcedInstall; + QStringList downloadArchives; + QHash license; + QString sha1; + unsigned int compressedSize; + unsigned int uncompressedSize; + QString os; +}; + + +/*! \class UpdatesParser + \brief New SAX XML parser for QT Installer Frameworks Updates.xml + */ +class UpdatesParser +{ + public: + UpdatesParser(); + bool read(QIODevice *device); + QString errorString() const; + QHash packages; + + private: + void readUpdates(); + void readPackageUpdate(); + + QXmlStreamReader xml; + PackageUpdate package; + QString currentTag; +}; #endif // UPDATEPARSER_H