diff --git a/Authors.txt b/Authors.txt new file mode 100755 index 0000000..3b0e9a1 --- /dev/null +++ b/Authors.txt @@ -0,0 +1,4 @@ +QSIMPLEUPDATER AUTHORS +---------------------- + +Alex Spataru diff --git a/Example/example.cpp b/Example/example.cpp new file mode 100644 index 0000000..4a9a473 --- /dev/null +++ b/Example/example.cpp @@ -0,0 +1,96 @@ +#include "example.h" +#include "ui_example.h" + +int main (int argc, char *argv[]) { + QApplication app(argc, argv); + + // Create the dialog and show it + Example example; + example.show(); + + // Run the app + return app.exec(); +} + +Example::Example (QWidget *parent) : QDialog(parent), ui(new Ui::Example) { + // Create and configure the user interface + ui->setupUi(this); + + // Close the dialog when the close button is clicked + connect (ui->closeButton, SIGNAL(clicked()), this, SLOT(close())); + + // Check for updates when the updates button is clicked + connect (ui->updatesButton, SIGNAL(clicked()), this, SLOT(checkForUpdates())); + + // Initialize the updater + updater = new QSimpleUpdater(this); + + // When the updater finishes checking for updates, show a message box + // and show the change log of the latest version + connect (updater, SIGNAL(checkingFinished()), this, SLOT(onCheckingFinished())); +} + +Example::~Example() { + delete ui; +} + +void Example::checkForUpdates() { + // Disable the check for updates button while the updater + // is checking for updates + ui->updatesButton->setEnabled(false); + ui->updatesButton->setText("Checking for updates..."); + + // If the user changed the text of the versionLineEdit, then change the + // application version in the updater too + if (!ui->versionLineEdit->text().isEmpty()) { + updater->setApplicationVersion(ui->versionLineEdit->text()); + } + + // If the versionLineEdit is empty, then set the application version + // to "0.1" + else { + updater->setApplicationVersion("0.1"); + } + + // Tell the updater where we should download the changelog, note that + // the changelog can be any file you want, + // such as an HTML page or (as in this example), a text file + updater->setChangelogUrl("https://raw.githubusercontent.com/alex-97/" + "QSimpleUpdater/Files-for-example-project/changelog.txt"); + + // Tell the updater where we can find the file that tells us the latest version + // of the application + updater->setReferenceUrl("https://raw.githubusercontent.com/alex-97/" + "QSimpleUpdater/Files-for-example-project/current_version.txt"); + + // Finally, check for updates... + updater->checkForUpdates(); +} + +void Example::onCheckingFinished() { + // Enable the updatesButton and change its text to let the user know + // that he/she can check for updates again + ui->updatesButton->setEnabled(true); + ui->updatesButton->setText("Check for updates"); + + // There's a newer version of the application available, so we inform + // the user that there's a newer version available and we replace the text + // of the changelog text edit with the downloaded change log + if (updater->newerVersionAvailable()) { + ui->changelogTextEdit->setPlainText(updater->changeLog()); + QMessageBox::information(this, tr("Update available"), + tr("There's a newer version available! The latest version is ") + + updater->latestVersion()); + } + + // The installed version is equal or greater to the "official" latest version, + // so we inform the user and clear the text of the change log text edit + else { + ui->changelogTextEdit->setPlainText(""); + ui->changelogTextEdit->setPlaceholderText("The change log was not downloaded because you " + "are running the latest version of the application..."); + + QMessageBox::information(this, tr("No updates available"), + tr("Congratulations! You are running the latest version of the application!")); + } +} diff --git a/Example/example.h b/Example/example.h new file mode 100644 index 0000000..d94f3b4 --- /dev/null +++ b/Example/example.h @@ -0,0 +1,31 @@ +#ifndef EXAMPLE_H +#define EXAMPLE_H + +#include +#include +#include + +namespace Ui { +class Example; +} + +class Example : public QDialog +{ + Q_OBJECT + +public: + explicit Example(QWidget *parent = 0); + ~Example(); + +public slots: + void checkForUpdates(); + void onCheckingFinished(); + +private: + Ui::Example *ui; + + QString m_installed_version; + QSimpleUpdater *updater; +}; + +#endif diff --git a/Example/example.pro b/Example/example.pro new file mode 100755 index 0000000..e646f98 --- /dev/null +++ b/Example/example.pro @@ -0,0 +1,10 @@ +# Include the QSimpleUpdater files +include($$PWD/../QSimpleUpdater/qsimpleupdater.pri) + +# Include the QtWidgets module +QT += widgets + +# Define the source code +FORMS += example.ui +HEADERS += example.h +SOURCES += example.cpp diff --git a/Example/example.pro.user b/Example/example.pro.user new file mode 100644 index 0000000..cf83cf0 --- /dev/null +++ b/Example/example.pro.user @@ -0,0 +1,272 @@ + + + + + + EnvironmentId + {138987cd-8d79-4dd6-8bdf-352f0efeeac3} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + 1 + + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.3.2 GCC 64bit + Desktop Qt 5.3.2 GCC 64bit + {919246e3-cae5-44ff-ad50-bfa69059c261} + 1 + 0 + 0 + + /Users/Alex/Documents/Developer/Projects/qupdater/build-example-Desktop_Qt_5_3_2_GCC_64bit-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /Users/Alex/Documents/Developer/Projects/qupdater/build-example-Desktop_Qt_5_3_2_GCC_64bit-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + false + + + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + -w + -r + + true + clean + + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 2 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy locally + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + example + + Qt4ProjectManager.Qt4RunConfiguration:/Users/Alex/Documents/Developer/Projects/qupdater/Example/example.pro + + example.pro + false + false + + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 16 + + + Version + 16 + + diff --git a/Example/example.ui b/Example/example.ui new file mode 100644 index 0000000..66ad3f2 --- /dev/null +++ b/Example/example.ui @@ -0,0 +1,90 @@ + + + Example + + + + 0 + 0 + 400 + 300 + + + + QSimpleUpdater Example + + + + + + Set installed version (latest version is 1.0) + + + + + + + 0.1 (default) + + + true + + + + + + + Changelog of latest version + + + + + + + + Courier + 11 + + + + false + + + false + + + true + + + false + + + Click on the "Check for Updates" button to download the changelog of the latest version + + + + + + + + + + Close + + + + + + + Check for updates + + + + + + + + + + + diff --git a/LICENSE b/License.txt old mode 100644 new mode 100755 similarity index 99% rename from LICENSE rename to License.txt index fb6d90b..02bbb60 --- a/LICENSE +++ b/License.txt @@ -1,4 +1,4 @@ -GNU LESSER GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. @@ -162,5 +162,4 @@ General Public License ever published by the Free Software Foundation. whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the -Library. - +Library. \ No newline at end of file diff --git a/QSimpleUpdater/qsimpleupdater.pri b/QSimpleUpdater/qsimpleupdater.pri new file mode 100755 index 0000000..2d8a49d --- /dev/null +++ b/QSimpleUpdater/qsimpleupdater.pri @@ -0,0 +1,24 @@ +# +# This file is part of QSimpleUpdater +# +# Copyright (c) 2014 Alex Spataru +# +# Please check the license.txt file for more information. +# + +QT += network + +HEADERS += $$PWD/src/qsimpleupdater.h +SOURCES += $$PWD/src/qsimpleupdater.cpp +OTHER_FILES += $$PWD/src/QSimpleUpdater + +INCLUDEPATH += $$PWD/src + +macx || linux{ + LIBS += -lcrypto -lssl +} + +win32* { + CONFIG += openssl-linked + LIBS += -L$$PWD/dependencies/win32/ -llibeay32 +} diff --git a/QSimpleUpdater/src/QSimpleUpdater b/QSimpleUpdater/src/QSimpleUpdater new file mode 100644 index 0000000..79984d8 --- /dev/null +++ b/QSimpleUpdater/src/QSimpleUpdater @@ -0,0 +1 @@ +#include "qsimpleupdater.h" \ No newline at end of file diff --git a/QSimpleUpdater/src/qsimpleupdater.cpp b/QSimpleUpdater/src/qsimpleupdater.cpp new file mode 100755 index 0000000..9c6569f --- /dev/null +++ b/QSimpleUpdater/src/qsimpleupdater.cpp @@ -0,0 +1,260 @@ +// +// This file is part of QSimpleUpdater +// +// Copyright (c) 2014 Alex Spataru +// +// Please check the license.txt file for more information. +// + +#include "qsimpleupdater.h" + +QSimpleUpdater::QSimpleUpdater(QObject *parent) : QObject(parent) {} + +QString QSimpleUpdater::changeLog() { + // Return the contents of the downloaded changelog + if (!m_changelog.isEmpty()) { + return m_changelog; + } + + // If the changelog is empty, we issue a warning message in + // the console and return an empty string + else { + qWarning() << "QSimpleUpdater: change log is empty," + << "did you call setChangelogUrl() and checkForUpdates()?"; + return NULL; + } +} + +void QSimpleUpdater::checkForUpdates() { + // Only check for updates if we know which file should we download + if (!m_reference_url.isEmpty()) { + + // Create a new network access manager, which allows us to + // download our desired file + QNetworkAccessManager *_manager = new QNetworkAccessManager(this); + + // Compare the downloaded application version with the installed + // version when the download is finished + connect(_manager, SIGNAL(finished(QNetworkReply*)), + this, SLOT(checkDownloadedVersion(QNetworkReply*))); + + // Ignore any possible SSL errors + connect(_manager, SIGNAL(sslErrors(QNetworkReply*,QList)), + this, SLOT(ignoreSslErrors(QNetworkReply*,QList))); + + // Finally, download the file + _manager->get (QNetworkRequest (m_reference_url)); + } + + // Issue a warning message in the case that the reference URL is empty... + else { + qWarning() << "QSimpleUpdater: Invalid reference URL"; + } +} + +QString QSimpleUpdater::latestVersion() { + // Return the application version referenced by the string + // that we downloaded + if (!m_latest_version.isEmpty()) { + return m_latest_version; + } + + // Issue a warning message in the case that the downloaded + // application version string is empty + else { + qWarning() << "QSimpleUpdater: latest version is empty," + << "did you call checkForUpdates() and setReferenceUrl()?"; + return NULL; + } +} + +QString QSimpleUpdater::installedVersion() { + // Return the string issued by the user in the setApplicationVersion() function + if (!m_installed_version.isEmpty()) { + return m_installed_version; + } + + // Issue a warning message in the case that the installed application + // version is empty + else { + qWarning() << "QSimpleUpdater: installed version is empty," + << "did you call setApplicationVersion()?"; + return NULL; + } +} + +void QSimpleUpdater::downloadLatestVersion() { + // Open the download URL in a web browser + if (!m_download_url.isEmpty()) { + QDesktopServices::openUrl(m_download_url); + } + + // The m_download_url is empty, so we issue another warning message + else { + qWarning() << "QSimpleUpdater: cannot download latest version," + << "did you call setDownloadUrl() and checkForUpdates()?"; + } +} + +bool QSimpleUpdater::newerVersionAvailable() { + return m_new_version_available; +} + +void QSimpleUpdater::setDownloadUrl(const QString url) { + // Change the download URL if the issued URL is valid + if (!url.isEmpty()) { + m_download_url.setUrl(url); + } + + // The issued URL is ilegal, so we warn the user + else { + qWarning() << "QSimpleUpdater: input URL cannot be empty!"; + } +} + +void QSimpleUpdater::setReferenceUrl(const QString url) { + // Change the reference URL if the issued URL is valid + if (!url.isEmpty()) { + m_reference_url.setUrl(url); + } + + // The issued URL is ilegal, so we warn the user + else { + qWarning() << "QSimpleUpdater: input URL cannot be empty!"; + } +} + +void QSimpleUpdater::setChangelogUrl(const QString url) { + // Change the changelog URL if the issued URL is valid + if (!url.isEmpty()) { + m_changelog_url.setUrl(url); + } + + // The issued URL is ilegal, so we warn the user + else { + qWarning() << "QSimpleUpdater: input URL cannot be empty!"; + } +} + +void QSimpleUpdater::setApplicationVersion(const QString version) { + // Change the installed application version if the issued string is valid + if (!version.isEmpty()) { + m_installed_version = version; + } + + // The application version cannot be empty, so we warn the user + else { + qWarning() << "QSimpleUpdater: input string cannot be empty!"; + } +} + + +void QSimpleUpdater::checkDownloadedVersion(QNetworkReply *reply) { + bool _new_update = false; + + // Read the reply from the server and transform it + // to a QString + QString _reply = QString::fromUtf8 (reply->readAll()); + + // If the reply from the server is not empty, compare + // the downloaded version with the installed version + if (!_reply.isEmpty()) { + + // Replace the latest version string with the downloaded string + m_latest_version = _reply; + + // Separate the downloaded and installed version + // string by their dots. + // + // For example, 0.9.1 would become: + // 1: 0 + // 2: 9 + // 3: 1 + // + QStringList _download = m_latest_version.split ("."); + QStringList _installed = m_installed_version.split ("."); + + // Compare the major, minor, build, etc. numbers + for (int i = 0; i <= _download.count() - 1; ++i) { + + // Make sure that the number that we are goind to compare + // exists in both strings, for example, we will not compare + // 1.2.3 and 1.2.3.1 because we would crash the program + if (_download.count() >= i && _installed.count() >= i) { + + // The downloaded number is greater than the installed number + // in question. So there's a newer version of the application + // available. + if (_download.at (i) > _installed.at (i)) { + _new_update = true; + break; + } + } + + // If the number of dots are different, we can tell if + // there's a newer version by comparing the count of each + // version. For example, 1.2.3 is smaller than 1.2.3.1... + // Also, we will only reach this code when we finish comparing + // the "3" in both the downloaded and the installed version. + else { + if (_installed.count() > _download.count()) { + _new_update = true; + break; + } + } + } + } + + // Update the value of the m_new_version_avialable boolean + m_new_version_available = _new_update; + + // Notify our parent that we have finished downloading and comparing + // the application version + emit versionCheckFinished(); + + // If the changelog URL is valid, download the change log ONLY if + // there's a newer version available. + // Note that the processDownloadedChangeLog() function will + // notify our parent that we have finished checking for updates. + if (!m_changelog_url.isEmpty() && newerVersionAvailable()) { + QNetworkAccessManager *_manager = new QNetworkAccessManager(this); + + connect(_manager, SIGNAL(finished(QNetworkReply*)), + this, SLOT(processDownloadedChangelog(QNetworkReply*))); + + connect(_manager, SIGNAL(sslErrors(QNetworkReply*,QList)), + this, SLOT(ignoreSslErrors(QNetworkReply*,QList))); + + _manager->get (QNetworkRequest (m_changelog_url)); + } + + // We did not download the changelog, so we notify our parent + // that we have finished checking for updates + else { + emit checkingFinished(); + } +} + +void QSimpleUpdater::processDownloadedChangelog(QNetworkReply *reply) { + // Read the downloaded file and transform it to a QString + QString _reply = QString::fromUtf8(reply->readAll()); + + // Change the changelog string and notify our + // parent that the changelog was downlaoded + if (!_reply.isEmpty()) { + m_changelog = _reply; + emit changelogDownloadFinished(); + } + + // Issue a warning in the case that the changelog is empty + else { + qWarning() << "QSimpleUpdater: downloaded change log is empty!"; + } + + // Tell our parent that we are done checking for updates + emit checkingFinished(); +} + +void QSimpleUpdater::ignoreSslErrors (QNetworkReply *reply, QList error) { + reply->ignoreSslErrors (error); +} diff --git a/QSimpleUpdater/src/qsimpleupdater.h b/QSimpleUpdater/src/qsimpleupdater.h new file mode 100755 index 0000000..ededa7e --- /dev/null +++ b/QSimpleUpdater/src/qsimpleupdater.h @@ -0,0 +1,60 @@ +#ifndef Q_SIMPLE_UPDATER_H +#define Q_SIMPLE_UPDATER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QSimpleUpdater : public QObject { + Q_OBJECT + +public: + QSimpleUpdater(QObject *parent = 0); + + QString changeLog(); + void checkForUpdates(); + QString latestVersion(); + QString installedVersion(); + void downloadLatestVersion(); + bool newerVersionAvailable(); + +public slots: + void setDownloadUrl(const QString url); + void setReferenceUrl(const QString url); + void setChangelogUrl(const QString url); + void setApplicationVersion(const QString version); + +private slots: + void checkDownloadedVersion(QNetworkReply *reply); + void processDownloadedChangelog(QNetworkReply *reply); + void ignoreSslErrors(QNetworkReply *reply, QList error); + +signals: + void checkingFinished(); + void versionCheckFinished(); + void changelogDownloadFinished(); + +private: + QString m_changelog; + QString m_latest_version; + QString m_installed_version; + + QUrl m_download_url; + QUrl m_reference_url; + QUrl m_changelog_url; + + bool m_changelog_downloaded; + bool m_version_check_finished; + + bool m_new_version_available; +}; + +#endif diff --git a/Readme.md b/Readme.md new file mode 100755 index 0000000..519dc46 --- /dev/null +++ b/Readme.md @@ -0,0 +1,138 @@ +## QSimpleUpdater + +QSimpleUpdater is an implementation of an auto-updating system to be used with Qt projects. + +QSimpleUpdater is **free and open source [LGPL software](https://www.gnu.org/licenses/lgpl.html)**, which means that you can use it for both open source and propietary applications. + +## Using QSimpleUpdater + +#### 1. Import QSimpleUpdater to your project + +1. Copy the QSimpleUpdater folder in your "3rd-party" folder. +3. Include the QSimpleUpdater project include (pri) file using the include() function. +5. That's all! Check the example project as a reference. + +#### 2. Include QSimpleUpdater in your header file(s): + +
#include <QSimpleUpdater>
+ +#### 3. Declare a new instance of QSimpleUpdater in your header file (preferably as a private object). + +
QSimpleUpdater *updater;
+ +#### 4. Initialize and configure the updater when your class is created: + +
 MyClass::MyClass() { 
+    
+    // Initialize the updater
+    updater = new QSimpleUpdater(this);
+
+    // Define our application version....
+    // The string must contain the same number
+    // of dots as the one that we will download
+    QString app_version = "1.2.3"
+              
+    // This string will help us to specify which file
+    // should we download when a new version of the app
+    // is detected.
+    QString download_url;
+              
+    // The following code will help us to define from where
+    // we should download the binary installation file 
+    // in the case that the updater detects a newer version
+    // of your app.
+              
+    // Download the DMG file of your app 
+    #ifdef Q_OS_MAC
+        download_url = "http://myapp.com/downloads/latest.dmg";
+    #endif
+              
+    // Download the EXE setup for your app
+    #ifdef Q_OS_WIN
+        download_url = "http://myapp.com/downloads/latest.exe";
+    #endif
+              
+    // Download a *.tar.gz file for your app
+    #ifdef Q_OS_LINUX
+        download_url = "http://myapp.com/downloads/latest.tar.gz";
+    #endif
+             
+    // Version of the installed application (in this case, 1.2.3)
+    // The parameter must be a QString.... 
+    updater->setApplicationVersion(app_version);
+              
+    // Tell the updater from where we should download
+    // the installer of our application
+    updater->setDownloadUrl(QUrl(download_url));
+              
+    // The following text file should only contain the 
+    // latest application version, for example, 1.2.3 or 1.2.4
+    updater->setReferenceUrl(QUrl("http://myapp.com/latest.txt"));
+              
+    // Tell the updater where to download the changelog...
+    updater->setChangelogUrl(QUrl("http://myapp.com/changelog.txt"));
+              
+    // Check for updates....
+    updater->checkForUpdates();
+              
+    // Finally, do something when the updater finds a new version
+    // of your app.
+    connect(updater, SIGNAL(updateAvailable()), 
+            this, SLOT(onUpdateAvailable()));
+
+}
+
+ +#### 5. Define what your application should do when the updater finds a new version of your application. For example: + +
MyClass::onUpdateAvailable() {
+    qDebug() << "A new version of myApp is available!";
+    qDebug() << "The latest version is:" 
+             << updater->latestVersion();
+    qDebug() << "The change log of the new version is:\n" 
+             << updater->changeLog();
+            
+    char type;
+    while (true) {
+        cout << "Download the latest version [y/n]" << endl;
+        cin >> type;
+
+        if ((type == 'y') || (type == 'n')) {
+            break;
+        }
+    }
+            
+    if (type == 'y') {
+        updater->downloadLatestVersion();
+    }
+}
+
+ +#### Notes + + +## Running the example project + +1. Navigate to the Example folder and open example.pro with [Qt Creator](http://qt-project.org/wiki/Category:Tools::QtCreator). +2. Compile the project and play with it :) + + +## Useful Links + ++ [Project website](http://qsimpleupdater.sf.net) ++ [SourceForge Project](http://sf.net/p/qsimpleupdater) ++ [OpenHub Project](http://openhub.net/p/qsimpleupdater) ++ [Contact developer](mailto:alex.racotta@gmail.com) + +## Donate + +Donate [Bitcoins](http://bitcoin.org) to the project to keep it going! + +> 1BdxESMayJAengjAkjipMwfWkiqZUztyhU + + + + + + + \ No newline at end of file