Better SSL support for Windows

This commit is contained in:
Alex Spataru 2014-11-09 22:40:16 -06:00
parent 07fb53334f
commit 0a7107c414
10 changed files with 336 additions and 291 deletions

0
License.txt → License-LGPL3.txt Executable file → Normal file
View File

View File

@ -23,8 +23,7 @@ macx || linux{
}
win32* {
CONFIG += openssl-linked
LIBS += -L$$PWD/dependencies/win32/ -llibeay32
LIBS += -L$$PWD/dependencies/OpenSSL-Win32/lib -llibeay32
}
RESOURCES += $$PWD/res/qsu_resources.qrc

31
QSimpleUpdater/readme.txt Normal file
View File

@ -0,0 +1,31 @@
--------------
PREFACE
--------------
Many websites today use the HTTPS protocol, which means that you will need SSL in order to communicate with them. If your project needs to access such a webiste (for example GitHub), you will need to carefully read the following information in order to ensure that QSimpleUpdater works with those websites (both in your machine and in the final users' machine).
This readme guide is extremely important for any developer wishing to deploy his or her applications under the Windows platform, because the application will depend on the libraries provided by QSimpleUpdater.
--------------
LINUX
--------------
Make sure that you have installed the following libraries in your system:
- lssl
- lcrypto
--------------
MAC OSX
--------------
The libraries required by QSimpleUpdater are the same as Linux, however, these libraries are installed by default in most Mac OS X installations.
--------------
WINDOWS
--------------
QSimpleUpdater makes use of the OpenSSL-Win32 project, make sure that you deploy the following DLLs allong your application (the DLLs are provided with the source of QSimpleUpdater):
- dependencies/OpenSSL-Win32/bin/libeay32.dll
- dependencies/OpenSSL-Win32/bin/ssleay32.dll

View File

@ -59,38 +59,42 @@ void DownloadDialog::beginDownload(const QUrl &url)
void DownloadDialog::openDownload()
{
if (!m_path.isEmpty()) {
if (!m_path.isEmpty())
{
QString url = m_path;
if (url.startsWith ("/"))
url = "file://" + url;
else
url = "file:///" + url;
QDesktopServices::openUrl (url);
}
else {
else
qWarning() << "QSimpleUpdater: cannot open downloaded file!";
}
}
void DownloadDialog::cancelDownload()
{
if (!m_reply->isFinished()) {
if (!m_reply->isFinished())
{
QMessageBox _message;
_message.setWindowTitle (tr ("Updater"));
_message.setIcon (QMessageBox::Question);
_message.setStandardButtons (QMessageBox::Yes | QMessageBox::No);
_message.setText (tr ("Are you sure you want to cancel the download?"));
if (_message.exec() == QMessageBox::Yes) {
if (_message.exec() == QMessageBox::Yes)
{
hide();
m_reply->abort();
}
} else {
hide();
}
else
hide();
}
void DownloadDialog::downloadFinished()
@ -101,32 +105,33 @@ void DownloadDialog::downloadFinished()
QByteArray data = m_reply->readAll();
if (!data.isEmpty()) {
if (!data.isEmpty())
{
QStringList list = m_reply->url().toString().split ("/");
QFile file (QDir::tempPath() + "/" + list.at (list.count() - 1));
if (file.open(QIODevice::WriteOnly)) {
if (file.open (QIODevice::WriteOnly))
{
file.write (data);
m_path = file.fileName();
}
else {
else
qWarning() << "QSimpleUpdater: cannot write downloaded data!";
}
file.close();
openDownload();
}
else {
else
qWarning() << "QSimpleUpdater: invalid download data!";
}
}
void DownloadDialog::updateProgress (qint64 received, qint64 total)
{
// We know the size of the download, so we can calculate the progress....
if (total > 0 && received > 0) {
if (total > 0 && received > 0)
{
ui->progressBar->setMinimum (0);
ui->progressBar->setMaximum (100);
@ -139,22 +144,32 @@ void DownloadDialog::updateProgress(qint64 received, qint64 total)
float _total = total;
float _received = received;
if (_total < 1024) {
if (_total < 1024)
_total_string = tr ("%1 bytes").arg (_total);
} else if (_total < 1024 * 1024) {
else if (_total < 1024 * 1024)
{
_total = roundNumber (_total / 1024);
_total_string = tr ("%1 KB").arg (_total);
} else {
}
else
{
_total = roundNumber (_total / (1024 * 1024));
_total_string = tr ("%1 MB").arg (_total);
}
if (_received < 1024) {
if (_received < 1024)
_received_string = tr ("%1 bytes").arg (_received);
} else if (received < 1024 * 1024) {
else if (received < 1024 * 1024)
{
_received = roundNumber (_received / 1024);
_received_string = tr ("%1 KB").arg (_received);
} else {
}
else
{
_received = roundNumber (_received / (1024 * 1024));
_received_string = tr ("%1 MB").arg (_received);
}
@ -165,30 +180,33 @@ void DownloadDialog::updateProgress(qint64 received, qint64 total)
uint _diff = QDateTime::currentDateTime().toTime_t() - m_start_time;
if (_diff > 0) {
if (_diff > 0)
{
QString _time_string;
float _time_remaining = total / (received / _diff);
if (_time_remaining > 7200) {
if (_time_remaining > 7200)
{
_time_remaining /= 3600;
_time_string = tr ("About %1 hours").arg (int (_time_remaining + 0.5));
}
else if (_time_remaining > 60) {
else if (_time_remaining > 60)
{
_time_remaining /= 60;
_time_string = tr ("About %1 minutes").arg (int (_time_remaining + 0.5));
}
else if (_time_remaining <= 60) {
else if (_time_remaining <= 60)
_time_string = tr ("%1 seconds").arg (int (_time_remaining + 0.5));
}
ui->timeLabel->setText (tr ("Time remaining") + ": " + _time_string);
}
}
// We do not know the size of the download, so we improvise...
else {
else
{
ui->progressBar->setValue (-1);
ui->progressBar->setMinimum (0);
ui->progressBar->setMaximum (0);

View File

@ -13,7 +13,8 @@
#include <math.h>
namespace Ui {
namespace Ui
{
class DownloadDialog;
}

View File

@ -17,71 +17,63 @@ QSimpleUpdater::QSimpleUpdater(QObject *parent)
m_downloadDialog = new DownloadDialog();
}
// Return the contents of the downloaded changelog
QString QSimpleUpdater::changeLog() const {
if (m_changelog.isEmpty()) {
QString QSimpleUpdater::changeLog() const
{
if (m_changelog.isEmpty())
{
qDebug() << "QSimpleUpdater: change log is empty,"
<< "did you call setChangelogUrl() and checkForUpdates()?";
}
return m_changelog;
}
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
void QSimpleUpdater::checkForUpdates()
{
if (!m_reference_url.isEmpty())
{
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<QSslError>)),
this, SLOT (ignoreSslErrors (QNetworkReply *, QList<QSslError>)));
// Finally, download the file
_manager->get (QNetworkRequest (m_reference_url));
}
// Issue a warning message in the case that the reference URL is empty...
else {
else
qDebug() << "QSimpleUpdater: Invalid reference URL";
}
}
void QSimpleUpdater::openDownloadLink() {
// Open the download URL in a web browser
if (!m_download_url.isEmpty()) {
void QSimpleUpdater::openDownloadLink()
{
if (!m_download_url.isEmpty())
QDesktopServices::openUrl (m_download_url);
}
// The m_download_url is empty, so we issue another warning message
else {
else
{
qDebug() << "QSimpleUpdater: cannot download latest version,"
<< "did you call setDownloadUrl() and checkForUpdates()?";
// Return the application version referenced by the string
// that we downloaded
}
}
// Return the application version referenced by the string
// that we downloaded
QString QSimpleUpdater::latestVersion() const {
if (m_latest_version.isEmpty()) {
QString QSimpleUpdater::latestVersion() const
{
if (m_latest_version.isEmpty())
{
qDebug() << "QSimpleUpdater: latest version is empty,"
<< "did you call checkForUpdates() and setReferenceUrl()?";
}
return m_latest_version;
}
// Return the string issued by the user in the setApplicationVersion() function
QString QSimpleUpdater::installedVersion() const {
if (m_installed_version.isEmpty()) {
QString QSimpleUpdater::installedVersion() const
{
if (m_installed_version.isEmpty())
{
qDebug() << "QSimpleUpdater: installed version is empty,"
<< "did you call setApplicationVersion()?";
}
@ -89,123 +81,102 @@ QString QSimpleUpdater::installedVersion() const {
return m_installed_version;
}
void QSimpleUpdater::downloadLatestVersion() {
// Show the download dialog
if (!m_download_url.isEmpty()) {
void QSimpleUpdater::downloadLatestVersion()
{
if (!m_download_url.isEmpty())
m_downloadDialog->beginDownload (m_download_url);
}
// The m_download_url is empty, so we issue another warning message
else {
else
{
qDebug() << "QSimpleUpdater: cannot download latest version,"
<< "did you call setDownloadUrl() and checkForUpdates()?";
}
}
bool QSimpleUpdater::newerVersionAvailable() const {
bool QSimpleUpdater::newerVersionAvailable() const
{
return m_new_version_available;
}
// Change the download URL if the issued URL is valid
void QSimpleUpdater::setDownloadUrl(const QString &url) {
void QSimpleUpdater::setDownloadUrl (const QString &url)
{
Q_ASSERT (!url.isEmpty());
if (!url.isEmpty()) {
if (!url.isEmpty())
m_download_url.setUrl (url);
} else {
else
qDebug() << "QSimpleUpdater: input URL cannot be empty!";
}
}
// Change the reference URL if the issued URL is valid
void QSimpleUpdater::setReferenceUrl(const QString &url) {
void QSimpleUpdater::setReferenceUrl (const QString &url)
{
Q_ASSERT (!url.isEmpty());
if (!url.isEmpty()) {
if (!url.isEmpty())
m_reference_url.setUrl (url);
} else {
else
qDebug() << "QSimpleUpdater: input URL cannot be empty!";
}
}
// Change the changelog URL if the issued URL is valid
void QSimpleUpdater::setChangelogUrl(const QString &url) {
void QSimpleUpdater::setChangelogUrl (const QString &url)
{
Q_ASSERT (!url.isEmpty());
if (!url.isEmpty()) {
if (!url.isEmpty())
m_changelog_url.setUrl (url);
} else {
else
qDebug() << "QSimpleUpdater: input URL cannot be empty!";
}
}
// Change the installed application version if the issued string is valid
void QSimpleUpdater::setApplicationVersion(const QString &version) {
void QSimpleUpdater::setApplicationVersion (const QString &version)
{
Q_ASSERT (!version.isEmpty());
if (!version.isEmpty()) {
if (!version.isEmpty())
m_installed_version = version;
} else {
else
qDebug() << "QSimpleUpdater: input string cannot be empty!";
}
}
void QSimpleUpdater::checkDownloadedVersion(QNetworkReply *reply) {
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());
_reply.replace (" ", "");
_reply.replace ("\n", "");
// If the reply from the server is not empty, compare
// the downloaded version with the installed version
if (!_reply.isEmpty() && _reply.contains(".")) {
// Replace the latest version string with the downloaded string
if (!_reply.isEmpty() && _reply.contains ("."))
{
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() - 1 >= i && _installed.count() - 1 >= 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)) {
for (int i = 0; i <= _download.count() - 1; ++i)
{
if (_download.count() - 1 >= i && _installed.count() - 1 >= i)
{
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()) {
else
{
if (_installed.count() < _download.count())
{
if (_installed.at (i - 1) == _download.at (i - 1))
break;
else {
else
{
_new_update = true;
break;
}
@ -214,18 +185,11 @@ void QSimpleUpdater::checkDownloadedVersion(QNetworkReply *reply) {
}
}
// 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()) {
if (!m_changelog_url.isEmpty() && newerVersionAvailable())
{
QNetworkAccessManager *_manager = new QNetworkAccessManager (this);
connect (_manager, SIGNAL (finished (QNetworkReply *)),
@ -237,33 +201,27 @@ void QSimpleUpdater::checkDownloadedVersion(QNetworkReply *reply) {
_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 {
else
emit checkingFinished();
}
}
void QSimpleUpdater::processDownloadedChangelog(QNetworkReply *reply) {
// Read the downloaded file and transform it to a QString
void QSimpleUpdater::processDownloadedChangelog (QNetworkReply *reply)
{
QString _reply = QString::fromUtf8 (reply->readAll());
// Change the changelog string and notify our
// parent that the changelog was downlaoded
if (!_reply.isEmpty()) {
if (!_reply.isEmpty())
{
m_changelog = _reply;
emit changelogDownloadFinished();
}
// Issue a warning in the case that the changelog is empty
else {
else
qDebug() << "QSimpleUpdater: downloaded change log is empty!";
}
// Tell our parent that we are done checking for updates
emit checkingFinished();
}
void QSimpleUpdater::ignoreSslErrors (QNetworkReply *reply, const QList<QSslError> &error) {
void QSimpleUpdater::ignoreSslErrors (QNetworkReply *reply, const QList<QSslError> &error)
{
reply->ignoreSslErrors (error);
}

View File

@ -16,24 +16,62 @@
#include "dialogs/download_dialog.h"
class QSimpleUpdater : public QObject {
class QSimpleUpdater : public QObject
{
Q_OBJECT
public:
QSimpleUpdater (QObject *parent = 0);
/// Returns the downloaded change log
QString changeLog() const;
void checkForUpdates();
void openDownloadLink();
/// Returns the downloaded version string
QString latestVersion() const;
/// Returns the local version, referenced by
/// the setApplicationVersion() function
QString installedVersion() const;
void downloadLatestVersion();
/// Returns \c true if there's a newer version available
bool newerVersionAvailable() const;
/// Checks for updates and calls the appropriate
/// signals when finished
void checkForUpdates();
/// Opens the download URL in a a web browser.
/// The URL is referenced by the \c setDownloadUrl() function
void openDownloadLink();
/// Shows a dialog that downloads the file in the
/// URL referenced by the \c setDownloadUrl() function
void downloadLatestVersion();
public slots:
/// Changes the URL that we can open in a web browser or
/// download. Its recommended to use fixed URLs if you
/// want to automatically download and install your updates
void setDownloadUrl (const QString &url);
/// Changes the reference URL, which contains ONLY the latest
/// version of your application as a plain text file.
/// Examples include:
/// - 1.2.3
/// - 5.4.0
/// - 0.1.2
/// - etc.
void setReferenceUrl (const QString &url);
/// Changes the change log URL, which contains the change log
/// of your application. The change log can be any file you
/// like, however, its recommended to write it in plain text,
/// such as TXT, HTML and RTF files.
void setChangelogUrl (const QString &url);
/// Tells the updater the version of the installed
/// copy of your application.
void setApplicationVersion (const QString &version);
private slots: