add fervor updater and run during launch if OS X release

This commit is contained in:
Stephen Birarda 2013-06-26 10:36:32 -07:00
parent 800bfd097b
commit 0b243ec881
20 changed files with 2199 additions and 6 deletions

View file

@ -65,10 +65,14 @@ if (APPLE)
endif (APPLE)
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL)
find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL QtWebKit)
include(${QT_USE_FILE})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}")
set(QUAZIP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/quazip)
add_subdirectory(external/fervor/)
include_directories(external/fervor/)
# run qt moc on qt-enabled headers
qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h src/Webcam.h)
@ -108,7 +112,14 @@ include_directories(
${SPEEXDSP_INCLUDE_DIRS}
)
target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES})
target_link_libraries(
${TARGET_NAME}
${QT_LIBRARIES}
${OPENCV_LIBRARIES}
${ZLIB_LIBRARIES}
${SPEEXDSP_LIBRARIES}
fervor
)
if (APPLE)
# link in required OS X frameworks and include the right GL headers

View file

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 2.8)
project(Fervor)
find_package(Qt4 REQUIRED)
add_definitions(-DFV_GUI)
file(GLOB FERVOR_SOURCES *.cpp)
file(GLOB FERVOR_HEADERS *.h)
file(GLOB FERVOR_UI *.ui)
qt4_wrap_ui(FERVOR_WRAPPED_UI ${FERVOR_UI})
qt4_wrap_cpp(FERVOR_MOC_SOURCES ${FERVOR_HEADERS})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
find_package(Quazip REQUIRED)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${QUAZIP_INCLUDE_DIRS}
)
add_library(fervor ${FERVOR_SOURCES} ${FERVOR_HEADERS} ${FERVOR_MOC_SOURCES} ${FERVOR_WRAPPED_UI})
target_link_libraries(fervor ${QUAZIP_LIBRARIES})

View file

@ -0,0 +1,97 @@
#include "fvavailableupdate.h"
FvAvailableUpdate::FvAvailableUpdate(QObject *parent) :
QObject(parent)
{
// noop
}
QString FvAvailableUpdate::GetTitle()
{
return m_title;
}
void FvAvailableUpdate::SetTitle(QString title)
{
m_title = title;
}
QUrl FvAvailableUpdate::GetReleaseNotesLink()
{
return m_releaseNotesLink;
}
void FvAvailableUpdate::SetReleaseNotesLink(QUrl releaseNotesLink)
{
m_releaseNotesLink = releaseNotesLink;
}
void FvAvailableUpdate::SetReleaseNotesLink(QString releaseNotesLink)
{
SetReleaseNotesLink(QUrl(releaseNotesLink));
}
QString FvAvailableUpdate::GetPubDate()
{
return m_pubDate;
}
void FvAvailableUpdate::SetPubDate(QString pubDate)
{
m_pubDate = pubDate;
}
QUrl FvAvailableUpdate::GetEnclosureUrl()
{
return m_enclosureUrl;
}
void FvAvailableUpdate::SetEnclosureUrl(QUrl enclosureUrl)
{
m_enclosureUrl = enclosureUrl;
}
void FvAvailableUpdate::SetEnclosureUrl(QString enclosureUrl)
{
SetEnclosureUrl(QUrl(enclosureUrl));
}
QString FvAvailableUpdate::GetEnclosureVersion()
{
return m_enclosureVersion;
}
void FvAvailableUpdate::SetEnclosureVersion(QString enclosureVersion)
{
m_enclosureVersion = enclosureVersion;
}
QString FvAvailableUpdate::GetEnclosurePlatform()
{
return m_enclosurePlatform;
}
void FvAvailableUpdate::SetEnclosurePlatform(QString enclosurePlatform)
{
m_enclosurePlatform = enclosurePlatform;
}
unsigned long FvAvailableUpdate::GetEnclosureLength()
{
return m_enclosureLength;
}
void FvAvailableUpdate::SetEnclosureLength(unsigned long enclosureLength)
{
m_enclosureLength = enclosureLength;
}
QString FvAvailableUpdate::GetEnclosureType()
{
return m_enclosureType;
}
void FvAvailableUpdate::SetEnclosureType(QString enclosureType)
{
m_enclosureType = enclosureType;
}

51
interface/external/fervor/fvavailableupdate.h vendored Executable file
View file

@ -0,0 +1,51 @@
#ifndef FVAVAILABLEUPDATE_H
#define FVAVAILABLEUPDATE_H
#include <QObject>
#include <QUrl>
class FvAvailableUpdate : public QObject
{
Q_OBJECT
public:
explicit FvAvailableUpdate(QObject *parent = 0);
QString GetTitle();
void SetTitle(QString title);
QUrl GetReleaseNotesLink();
void SetReleaseNotesLink(QUrl releaseNotesLink);
void SetReleaseNotesLink(QString releaseNotesLink);
QString GetPubDate();
void SetPubDate(QString pubDate);
QUrl GetEnclosureUrl();
void SetEnclosureUrl(QUrl enclosureUrl);
void SetEnclosureUrl(QString enclosureUrl);
QString GetEnclosureVersion();
void SetEnclosureVersion(QString enclosureVersion);
QString GetEnclosurePlatform();
void SetEnclosurePlatform(QString enclosurePlatform);
unsigned long GetEnclosureLength();
void SetEnclosureLength(unsigned long enclosureLength);
QString GetEnclosureType();
void SetEnclosureType(QString enclosureType);
private:
QString m_title;
QUrl m_releaseNotesLink;
QString m_pubDate;
QUrl m_enclosureUrl;
QString m_enclosureVersion;
QString m_enclosurePlatform;
unsigned long m_enclosureLength;
QString m_enclosureType;
};
#endif // FVAVAILABLEUPDATE_H

View file

@ -0,0 +1,74 @@
#include "fvignoredversions.h"
#include "fvversioncomparator.h"
#include <QSettings>
#include <QCoreApplication>
#include <string>
// QSettings key for the latest skipped version
#define FV_IGNORED_VERSIONS_LATEST_SKIPPED_VERSION_KEY "FVLatestSkippedVersion"
FVIgnoredVersions::FVIgnoredVersions(QObject *parent) :
QObject(parent)
{
// noop
}
bool FVIgnoredVersions::VersionIsIgnored(QString version)
{
// We assume that variable 'version' contains either:
// 1) The current version of the application (ignore)
// 2) The version that was skipped before and thus stored in QSettings (ignore)
// 3) A newer version (don't ignore)
// 'version' is not likely to contain an older version in any case.
if (version == QCoreApplication::applicationVersion()) {
return true;
}
QSettings settings(QSettings::NativeFormat,
QSettings::UserScope,
QCoreApplication::organizationDomain(),
QCoreApplication::applicationName());
//QSettings settings;
if (settings.contains(FV_IGNORED_VERSIONS_LATEST_SKIPPED_VERSION_KEY)) {
QString lastSkippedVersion = settings.value(FV_IGNORED_VERSIONS_LATEST_SKIPPED_VERSION_KEY).toString();
if (version == lastSkippedVersion) {
// Implicitly skipped version - skip
return true;
}
}
std::string currentAppVersion = QCoreApplication::applicationVersion().toStdString();
std::string suggestedVersion = std::string(version.toStdString());
if (FvVersionComparator::CompareVersions(currentAppVersion, suggestedVersion) == FvVersionComparator::kAscending) {
// Newer version - do not skip
return false;
}
// Fallback - skip
return true;
}
void FVIgnoredVersions::IgnoreVersion(QString version)
{
if (version == QCoreApplication::applicationVersion()) {
// Don't ignore the current version
return;
}
if (version.isEmpty()) {
return;
}
QSettings settings(QSettings::NativeFormat,
QSettings::UserScope,
QCoreApplication::organizationDomain(),
QCoreApplication::applicationName());
settings.setValue(FV_IGNORED_VERSIONS_LATEST_SKIPPED_VERSION_KEY, version);
return;
}

19
interface/external/fervor/fvignoredversions.h vendored Executable file
View file

@ -0,0 +1,19 @@
#ifndef FVIGNOREDVERSIONS_H
#define FVIGNOREDVERSIONS_H
#include <QObject>
class FVIgnoredVersions : public QObject
{
Q_OBJECT
public:
static bool VersionIsIgnored(QString version);
static void IgnoreVersion(QString version);
private:
explicit FVIgnoredVersions(QObject *parent = 0);
};
#endif // FVIGNOREDVERSIONS_H

210
interface/external/fervor/fvplatform.cpp vendored Executable file
View file

@ -0,0 +1,210 @@
#include "fvplatform.h"
#include <QtGlobal>
#include <QDebug>
FvPlatform::FvPlatform(QObject *parent) :
QObject(parent)
{
// noop
}
bool FvPlatform::CurrentlyRunningOnPlatform(QString platform)
{
platform = platform.toUpper().trimmed();
if (platform.isEmpty()) {
return false;
}
// Defined on AIX.
#ifdef Q_OS_AIX
if (platform == "Q_OS_AIX") {
return true;
}
#endif
// Q_OS_BSD4 ("Defined on Any BSD 4.4 system") intentionally skipped.
// Defined on BSD/OS.
#ifdef Q_OS_BSDI
if (platform == "Q_OS_BSDI") {
return true;
}
#endif
// Defined on Cygwin.
#ifdef Q_OS_CYGWIN
if (platform == "Q_OS_CYGWIN") {
return true;
}
#endif
// Q_OS_DARWIN ("Defined on Darwin OS (synonym for Q_OS_MAC)") intentionally skipped.
// Defined on DG/UX.
#ifdef Q_OS_DGUX
if (platform == "Q_OS_DGUX") {
return true;
}
#endif
// Defined on DYNIX/ptx.
#ifdef Q_OS_DYNIX
if (platform == "Q_OS_DYNIX") {
return true;
}
#endif
// Defined on FreeBSD.
#ifdef Q_OS_FREEBSD
if (platform == "Q_OS_FREEBSD") {
return true;
}
#endif
// Defined on HP-UX.
#ifdef Q_OS_HPUX
if (platform == "Q_OS_HPUX") {
return true;
}
#endif
// Defined on GNU Hurd.
#ifdef Q_OS_HURD
if (platform == "Q_OS_HURD") {
return true;
}
#endif
// Defined on SGI Irix.
#ifdef Q_OS_IRIX
if (platform == "Q_OS_IRIX") {
return true;
}
#endif
// Defined on Linux.
#ifdef Q_OS_LINUX
if (platform == "Q_OS_LINUX") {
return true;
}
#endif
// Defined on LynxOS.
#ifdef Q_OS_LYNX
if (platform == "Q_OS_LYNX") {
return true;
}
#endif
// Defined on MAC OS (synonym for Darwin).
#ifdef Q_OS_MAC
if (platform == "Q_OS_MAC") {
return true;
}
#endif
// Q_OS_MSDOS ("Defined on MS-DOS and Windows") intentionally skipped.
// Defined on NetBSD.
#ifdef Q_OS_NETBSD
if (platform == "Q_OS_NETBSD") {
return true;
}
#endif
// Defined on OS/2.
#ifdef Q_OS_OS2
if (platform == "Q_OS_OS2") {
return true;
}
#endif
// Defined on OpenBSD.
#ifdef Q_OS_OPENBSD
if (platform == "Q_OS_OPENBSD") {
return true;
}
#endif
// Defined on XFree86 on OS/2 (not PM).
#ifdef Q_OS_OS2EMX
if (platform == "Q_OS_OS2EMX") {
return true;
}
#endif
// Defined on HP Tru64 UNIX.
#ifdef Q_OS_OSF
if (platform == "Q_OS_OSF") {
return true;
}
#endif
// Defined on QNX Neutrino.
#ifdef Q_OS_QNX
if (platform == "Q_OS_QNX") {
return true;
}
#endif
// Defined on Reliant UNIX.
#ifdef Q_OS_RELIANT
if (platform == "Q_OS_RELIANT") {
return true;
}
#endif
// Defined on SCO OpenServer 5.
#ifdef Q_OS_SCO
if (platform == "Q_OS_SCO") {
return true;
}
#endif
// Defined on Sun Solaris.
#ifdef Q_OS_SOLARIS
if (platform == "Q_OS_SOLARIS") {
return true;
}
#endif
// Defined on Symbian.
#ifdef Q_OS_SYMBIAN
if (platform == "Q_OS_SYMBIAN") {
return true;
}
#endif
// Defined on DEC Ultrix.
#ifdef Q_OS_ULTRIX
if (platform == "Q_OS_ULTRIX") {
return true;
}
#endif
// Q_OS_UNIX ("Defined on Any UNIX BSD/SYSV system") intentionally skipped.
// Defined on UnixWare 7, Open UNIX 8.
#ifdef Q_OS_UNIXWARE
if (platform == "Q_OS_UNIXWARE") {
return true;
}
#endif
// Defined on Windows CE (note: goes before Q_OS_WIN32)
#ifdef Q_OS_WINCE
if (platform == "Q_OS_WINCE") {
return true;
}
#endif
// Defined on all supported versions of Windows.
#ifdef Q_OS_WIN32
if (platform == "Q_OS_WIN32") {
return true;
}
#endif
// Fallback
return false;
}

18
interface/external/fervor/fvplatform.h vendored Executable file
View file

@ -0,0 +1,18 @@
#ifndef FVPLATFORM_H
#define FVPLATFORM_H
#include <QObject>
class FvPlatform : public QObject
{
Q_OBJECT
public:
static bool CurrentlyRunningOnPlatform(QString platform);
private:
explicit FvPlatform(QObject *parent = 0);
};
#endif // FVPLATFORM_H

View file

@ -0,0 +1,26 @@
#include "fvupdatedownloadprogress.h"
FvUpdateDownloadProgress::FvUpdateDownloadProgress(QWidget *parent)
: QWidget(parent, Qt::SplashScreen)
{
ui.setupUi(this);
ui.progress->setValue(0);
}
FvUpdateDownloadProgress::~FvUpdateDownloadProgress()
{
}
void FvUpdateDownloadProgress::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal )
{
ui.progress->setValue( ((float)bytesReceived / (float)bytesTotal) * 100 );
}
void FvUpdateDownloadProgress::close()
{
this->deleteLater();
QWidget::close();
}

View file

@ -0,0 +1,23 @@
#ifndef FVUPDATEDOWNLOADPROGRESS_H
#define FVUPDATEDOWNLOADPROGRESS_H
#include <QWidget>
#include "ui_fvupdatedownloadprogress.h"
class FvUpdateDownloadProgress : public QWidget
{
Q_OBJECT
public:
FvUpdateDownloadProgress(QWidget *parent = 0);
~FvUpdateDownloadProgress();
public slots:
void downloadProgress ( qint64 bytesReceived, qint64 bytesTotal );
void close();
private:
Ui::FvUpdateDownloadProgress ui;
};
#endif // FVUPDATEDOWNLOADPROGRESS_H

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FvUpdateDownloadProgress</class>
<widget class="QWidget" name="FvUpdateDownloadProgress">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>558</width>
<height>70</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>FvUpdateDownloadProgress</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::WinPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>8</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string>Downloading Update...</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progress">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

908
interface/external/fervor/fvupdater.cpp vendored Executable file
View file

@ -0,0 +1,908 @@
#include "fvupdater.h"
#include "fvplatform.h"
#include "fvignoredversions.h"
#include "fvavailableupdate.h"
#include <QCoreApplication>
#include <QtNetwork>
#include <QDebug>
#include <QSettings>
#include "quazip.h"
#include "quazipfile.h"
#ifdef Q_WS_MAC
#include "CoreFoundation/CoreFoundation.h"
#endif
#ifdef FV_GUI
#include "fvupdatewindow.h"
#include "fvupdatedownloadprogress.h"
#include <QMessageBox>
#include <QDesktopServices>
#else
// QSettings key for automatic update installation
#define FV_NEW_VERSION_POLICY_KEY "FVNewVersionPolicy"
#endif
#ifdef FV_DEBUG
// Unit tests
# include "fvversioncomparatortest.h"
#endif
extern QSettings* settings;
FvUpdater* FvUpdater::m_Instance = 0;
FvUpdater* FvUpdater::sharedUpdater()
{
static QMutex mutex;
if (! m_Instance) {
mutex.lock();
if (! m_Instance) {
m_Instance = new FvUpdater;
}
mutex.unlock();
}
return m_Instance;
}
void FvUpdater::drop()
{
static QMutex mutex;
mutex.lock();
delete m_Instance;
m_Instance = 0;
mutex.unlock();
}
FvUpdater::FvUpdater() : QObject(0)
{
m_reply = 0;
#ifdef FV_GUI
m_updaterWindow = 0;
#endif
m_proposedUpdate = 0;
m_requiredSslFingerprint = "";
htAuthUsername = "";
htAuthPassword = "";
skipVersionAllowed = true;
remindLaterAllowed = true;
connect(&m_qnam, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),this, SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*)));
// Translation mechanism
installTranslator();
#ifdef FV_DEBUG
// Unit tests
FvVersionComparatorTest* test = new FvVersionComparatorTest();
test->runAll();
delete test;
#endif
}
FvUpdater::~FvUpdater()
{
if (m_proposedUpdate) {
delete m_proposedUpdate;
m_proposedUpdate = 0;
}
#ifdef FV_GUI
hideUpdaterWindow();
#endif
}
void FvUpdater::installTranslator()
{
QTranslator translator;
QString locale = QLocale::system().name();
translator.load(QString("fervor_") + locale);
#if QT_VERSION < 0x050000
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
#endif
qApp->installTranslator(&translator);
}
#ifdef FV_GUI
void FvUpdater::showUpdaterWindowUpdatedWithCurrentUpdateProposal()
{
// Destroy window if already exists
hideUpdaterWindow();
// Create a new window
m_updaterWindow = new FvUpdateWindow(NULL, skipVersionAllowed, remindLaterAllowed);
m_updaterWindow->UpdateWindowWithCurrentProposedUpdate();
m_updaterWindow->show();
}
void FvUpdater::hideUpdaterWindow()
{
if (m_updaterWindow) {
if (! m_updaterWindow->close()) {
qWarning() << "Update window didn't close, leaking memory from now on";
}
// not deleting because of Qt::WA_DeleteOnClose
m_updaterWindow = 0;
}
}
void FvUpdater::updaterWindowWasClosed()
{
// (Re-)nullify a pointer to a destroyed QWidget or you're going to have a bad time.
m_updaterWindow = 0;
}
#endif
void FvUpdater::SetFeedURL(QUrl feedURL)
{
m_feedURL = feedURL;
}
void FvUpdater::SetFeedURL(QString feedURL)
{
SetFeedURL(QUrl(feedURL));
}
QString FvUpdater::GetFeedURL()
{
return m_feedURL.toString();
}
FvAvailableUpdate* FvUpdater::GetProposedUpdate()
{
return m_proposedUpdate;
}
void FvUpdater::InstallUpdate()
{
if(m_proposedUpdate==NULL)
{
qWarning() << "Abort Update: No update prososed! This should not happen.";
return;
}
// Prepare download
QUrl url = m_proposedUpdate->GetEnclosureUrl();
// Check SSL Fingerprint if required
if(url.scheme()=="https" && !m_requiredSslFingerprint.isEmpty())
if( !checkSslFingerPrint(url) ) // check failed
{
qWarning() << "Update aborted.";
return;
}
// Start Download
QNetworkReply* reply = m_qnam.get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(httpUpdateDownloadFinished()));
// Maybe Check request 's return value
if (reply->error() != QNetworkReply::NoError)
{
qDebug()<<"Unable to download the update: "<<reply->errorString();
return;
}
else
qDebug()<<"OK";
// Show download Window
#ifdef FV_GUI
FvUpdateDownloadProgress* dlwindow = new FvUpdateDownloadProgress(NULL);
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), dlwindow, SLOT(downloadProgress(qint64, qint64) ));
connect(&m_qnam, SIGNAL(finished(QNetworkReply*)), dlwindow, SLOT(close()));
dlwindow->show();
#endif
emit (updatedFinishedSuccessfully());
#ifdef FV_GUI
hideUpdaterWindow();
#endif
}
void FvUpdater::httpUpdateDownloadFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if(reply==NULL)
{
qWarning()<<"The slot httpUpdateDownloadFinished() should only be invoked by S&S.";
return;
}
if(reply->error() == QNetworkReply::NoError)
{
int httpstatuscode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt();
// no error received?
if (reply->error() == QNetworkReply::NoError)
{
if (reply->isReadable())
{
#ifdef Q_WS_MAC
CFURLRef appURLRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(appURLRef, TRUE, (UInt8 *)path, PATH_MAX)) {
// error!
}
CFRelease(appURLRef);
QString filePath = QString(path);
QString rootDirectory = filePath.left(filePath.lastIndexOf("/"));
#else
QString rootDirectory = QCoreApplication::applicationDirPath() + "/";
#endif
// Write download into File
QFileInfo fileInfo=reply->url().path();
QString fileName = rootDirectory + fileInfo.fileName();
//qDebug()<<"Writing downloaded file into "<<fileName;
QFile file(fileName);
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
// Retrieve List of updated files (Placed in an extra scope to avoid QuaZIP handles the archive permanently and thus avoids the deletion.)
{
QuaZip zip(fileName);
if (!zip.open(QuaZip::mdUnzip)) {
qWarning("testRead(): zip.open(): %d", zip.getZipError());
return;
}
zip.setFileNameCodec("IBM866");
QList<QuaZipFileInfo> updateFiles = zip.getFileInfoList();
// Rename all current files with available update.
for (int i=0;i<updateFiles.size();i++)
{
QString sourceFilePath = rootDirectory + "\\" + updateFiles[i].name;
QDir appDir( QCoreApplication::applicationDirPath() );
QFileInfo file( sourceFilePath );
if(file.exists())
{
//qDebug()<<tr("Moving file %1 to %2").arg(sourceFilePath).arg(sourceFilePath+".oldversion");
appDir.rename( sourceFilePath, sourceFilePath+".oldversion" );
}
}
}
// Install updated Files
unzipUpdate(fileName, rootDirectory);
// Delete update archive
while(QFile::remove(fileName) )
{
};
// Restart ap to clean up and start usual business
restartApplication();
}
else qDebug()<<"Error: QNetworkReply is not readable!";
}
else
{
qDebug()<<"Download errors ocurred! HTTP Error Code:"<<httpstatuscode;
}
reply->deleteLater();
} // If !reply->error END
} // httpUpdateDownloadFinished END
bool FvUpdater::unzipUpdate(const QString & filePath, const QString & extDirPath, const QString & singleFileName )
{
QuaZip zip(filePath);
if (!zip.open(QuaZip::mdUnzip)) {
qWarning()<<tr("Error: Unable to open zip archive %1 for unzipping: %2").arg(filePath).arg(zip.getZipError());
return false;
}
zip.setFileNameCodec("IBM866");
//qWarning("Update contains %d files\n", zip.getEntriesCount());
QuaZipFileInfo info;
QuaZipFile file(&zip);
QFile out;
QString name;
QDir appDir(extDirPath);
for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile())
{
if (!zip.getCurrentFileInfo(&info)) {
qWarning()<<tr("Error: Unable to retrieve fileInfo about the file to extract: %2").arg(zip.getZipError());
return false;
}
if (!singleFileName.isEmpty())
if (!info.name.contains(singleFileName))
continue;
if (!file.open(QIODevice::ReadOnly)) {
qWarning()<<tr("Error: Unable to open file %1 for unzipping: %2").arg(filePath).arg(file.getZipError());
return false;
}
name = QString("%1/%2").arg(extDirPath).arg(file.getActualFileName());
if (file.getZipError() != UNZ_OK) {
qWarning()<<tr("Error: Unable to retrieve zipped filename to unzip from %1: %2").arg(filePath).arg(file.getZipError());
return false;
}
QFileInfo fi(name);
appDir.mkpath(fi.absolutePath() ); // Ensure that subdirectories - if required - exist
out.setFileName(name);
out.open(QIODevice::WriteOnly);
out.write( file.readAll() );
out.close();
if (file.getZipError() != UNZ_OK) {
qWarning()<<tr("Error: Unable to unzip file %1: %2").arg(name).arg(file.getZipError());
return false;
}
if (!file.atEnd()) {
qWarning()<<tr("Error: Have read all available bytes, but pointer still does not show EOF: %1").arg(file.getZipError());
return false;
}
file.close();
if (file.getZipError() != UNZ_OK) {
qWarning()<<tr("Error: Unable to close zipped file %1: %2").arg(name).arg(file.getZipError());
return false;
}
}
zip.close();
if (zip.getZipError() != UNZ_OK) {
qWarning()<<tr("Error: Unable to close zip archive file %1: %2").arg(filePath).arg(file.getZipError());
return false;
}
return true;
}
void FvUpdater::SkipUpdate()
{
qDebug() << "Skip update";
FvAvailableUpdate* proposedUpdate = GetProposedUpdate();
if (! proposedUpdate) {
qWarning() << "Proposed update is NULL (shouldn't be at this point)";
return;
}
// Start ignoring this particular version
FVIgnoredVersions::IgnoreVersion(proposedUpdate->GetEnclosureVersion());
#ifdef FV_GUI
hideUpdaterWindow();
#endif
}
void FvUpdater::RemindMeLater()
{
//qDebug() << "Remind me later";
#ifdef FV_GUI
hideUpdaterWindow();
#endif
}
bool FvUpdater::CheckForUpdates(bool silentAsMuchAsItCouldGet)
{
if (m_feedURL.isEmpty()) {
qCritical() << "Please set feed URL via setFeedURL() before calling CheckForUpdates().";
return false;
}
m_silentAsMuchAsItCouldGet = silentAsMuchAsItCouldGet;
// Check if application's organization name and domain are set, fail otherwise
// (nowhere to store QSettings to)
if (QCoreApplication::organizationName().isEmpty()) {
qCritical() << "QCoreApplication::organizationName is not set. Please do that.";
return false;
}
if (QCoreApplication::organizationDomain().isEmpty()) {
qCritical() << "QCoreApplication::organizationDomain is not set. Please do that.";
return false;
}
if(QCoreApplication::applicationName().isEmpty()) {
qCritical() << "QCoreApplication::applicationName is not set. Please do that.";
return false;
}
// Set application version is not set yet
if (QCoreApplication::applicationVersion().isEmpty()) {
qCritical() << "QCoreApplication::applicationVersion is not set. Please do that.";
return false;
}
cancelDownloadFeed();
m_httpRequestAborted = false;
startDownloadFeed(m_feedURL);
return true;
}
bool FvUpdater::CheckForUpdatesSilent()
{
return CheckForUpdates(true);
}
bool FvUpdater::CheckForUpdatesNotSilent()
{
return CheckForUpdates(false);
}
void FvUpdater::startDownloadFeed(QUrl url)
{
m_xml.clear();
// Check SSL Fingerprint if required
if(url.scheme()=="https" && !m_requiredSslFingerprint.isEmpty())
if( !checkSslFingerPrint(url) ) // check failed
{
qWarning() << "Update aborted.";
return;
}
m_reply = m_qnam.get(QNetworkRequest(url));
connect(m_reply, SIGNAL(readyRead()), this, SLOT(httpFeedReadyRead()));
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(httpFeedUpdateDataReadProgress(qint64, qint64)));
connect(m_reply, SIGNAL(finished()), this, SLOT(httpFeedDownloadFinished()));
}
void FvUpdater::cancelDownloadFeed()
{
if (m_reply) {
m_httpRequestAborted = true;
m_reply->abort();
}
}
void FvUpdater::httpFeedReadyRead()
{
// this slot gets called every time the QNetworkReply has new data.
// We read all of its new data and write it into the file.
// That way we use less RAM than when reading it at the finished()
// signal of the QNetworkReply
m_xml.addData(m_reply->readAll());
}
void FvUpdater::httpFeedUpdateDataReadProgress(qint64 bytesRead,
qint64 totalBytes)
{
Q_UNUSED(bytesRead);
Q_UNUSED(totalBytes);
if (m_httpRequestAborted) {
return;
}
}
void FvUpdater::httpFeedDownloadFinished()
{
if (m_httpRequestAborted) {
m_reply->deleteLater();
return;
}
QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (m_reply->error()) {
// Error.
showErrorDialog(tr("Feed download failed: %1.").arg(m_reply->errorString()), false);
} else if (! redirectionTarget.isNull()) {
QUrl newUrl = m_feedURL.resolved(redirectionTarget.toUrl());
m_feedURL = newUrl;
m_reply->deleteLater();
startDownloadFeed(m_feedURL);
return;
} else {
// Done.
xmlParseFeed();
}
m_reply->deleteLater();
m_reply = 0;
}
bool FvUpdater::xmlParseFeed()
{
QString currentTag, currentQualifiedTag;
QString xmlTitle, xmlLink, xmlReleaseNotesLink, xmlPubDate, xmlEnclosureUrl,
xmlEnclosureVersion, xmlEnclosurePlatform, xmlEnclosureType;
unsigned long xmlEnclosureLength;
// Parse
while (! m_xml.atEnd()) {
m_xml.readNext();
if (m_xml.isStartElement()) {
currentTag = m_xml.name().toString();
currentQualifiedTag = m_xml.qualifiedName().toString();
if (m_xml.name() == "item") {
xmlTitle.clear();
xmlLink.clear();
xmlReleaseNotesLink.clear();
xmlPubDate.clear();
xmlEnclosureUrl.clear();
xmlEnclosureVersion.clear();
xmlEnclosurePlatform.clear();
xmlEnclosureLength = 0;
xmlEnclosureType.clear();
} else if (m_xml.name() == "enclosure") {
QXmlStreamAttributes attribs = m_xml.attributes();
if (attribs.hasAttribute("fervor:platform"))
{
xmlEnclosurePlatform = attribs.value("fervor:platform").toString().trimmed();
if (FvPlatform::CurrentlyRunningOnPlatform(xmlEnclosurePlatform))
{
xmlEnclosureUrl = attribs.hasAttribute("url") ? attribs.value("url").toString().trimmed() : "";
xmlEnclosureVersion = "";
if (attribs.hasAttribute("fervor:version"))
xmlEnclosureVersion = attribs.value("fervor:version").toString().trimmed();
if (attribs.hasAttribute("sparkle:version"))
xmlEnclosureVersion = attribs.value("sparkle:version").toString().trimmed();
xmlEnclosureLength = attribs.hasAttribute("length") ? attribs.value("length").toString().toLong() : 0;
xmlEnclosureType = attribs.hasAttribute("type") ? attribs.value("type").toString().trimmed() : "";
}
} // if hasAttribute flevor:platform END
} // IF encosure END
} else if (m_xml.isEndElement()) {
if (m_xml.name() == "item") {
// That's it - we have analyzed a single <item> and we'll stop
// here (because the topmost is the most recent one, and thus
// the newest version.
return searchDownloadedFeedForUpdates(xmlTitle,
xmlLink,
xmlReleaseNotesLink,
xmlPubDate,
xmlEnclosureUrl,
xmlEnclosureVersion,
xmlEnclosurePlatform,
xmlEnclosureLength,
xmlEnclosureType);
}
} else if (m_xml.isCharacters() && ! m_xml.isWhitespace()) {
if (currentTag == "title") {
xmlTitle += m_xml.text().toString().trimmed();
} else if (currentTag == "link") {
xmlLink += m_xml.text().toString().trimmed();
} else if (currentQualifiedTag == "sparkle:releaseNotesLink") {
xmlReleaseNotesLink += m_xml.text().toString().trimmed();
} else if (currentTag == "pubDate") {
xmlPubDate += m_xml.text().toString().trimmed();
}
}
if (m_xml.error() && m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
showErrorDialog(tr("Feed parsing failed: %1 %2.").arg(QString::number(m_xml.lineNumber()), m_xml.errorString()), false);
return false;
}
}
return false;
}
bool FvUpdater::searchDownloadedFeedForUpdates(QString xmlTitle,
QString xmlLink,
QString xmlReleaseNotesLink,
QString xmlPubDate,
QString xmlEnclosureUrl,
QString xmlEnclosureVersion,
QString xmlEnclosurePlatform,
unsigned long xmlEnclosureLength,
QString xmlEnclosureType)
{
Q_UNUSED(xmlTitle);
Q_UNUSED(xmlPubDate);
Q_UNUSED(xmlEnclosureLength);
Q_UNUSED(xmlEnclosureType);
// Validate
if (xmlReleaseNotesLink.isEmpty()) {
if (xmlLink.isEmpty()) {
showErrorDialog(tr("Feed error: \"release notes\" link is empty"), false);
return false;
} else {
xmlReleaseNotesLink = xmlLink;
}
} else {
xmlLink = xmlReleaseNotesLink;
}
if (! (xmlLink.startsWith("http://") || xmlLink.startsWith("https://"))) {
showErrorDialog(tr("Feed error: invalid \"release notes\" link"), false);
return false;
}
if (xmlEnclosureUrl.isEmpty() || xmlEnclosureVersion.isEmpty() || xmlEnclosurePlatform.isEmpty()) {
showErrorDialog(tr("Feed error: invalid \"enclosure\" with the download link"), false);
return false;
}
// Relevant version?
if (FVIgnoredVersions::VersionIsIgnored(xmlEnclosureVersion)) {
qDebug() << "Version '" << xmlEnclosureVersion << "' is ignored, too old or something like that.";
showInformationDialog(tr("No updates were found."), false);
return true; // Things have succeeded when you think of it.
}
//
// Success! At this point, we have found an update that can be proposed
// to the user.
//
if (m_proposedUpdate) {
delete m_proposedUpdate; m_proposedUpdate = 0;
}
m_proposedUpdate = new FvAvailableUpdate();
m_proposedUpdate->SetTitle(xmlTitle);
m_proposedUpdate->SetReleaseNotesLink(xmlReleaseNotesLink);
m_proposedUpdate->SetPubDate(xmlPubDate);
m_proposedUpdate->SetEnclosureUrl(xmlEnclosureUrl);
m_proposedUpdate->SetEnclosureVersion(xmlEnclosureVersion);
m_proposedUpdate->SetEnclosurePlatform(xmlEnclosurePlatform);
m_proposedUpdate->SetEnclosureLength(xmlEnclosureLength);
m_proposedUpdate->SetEnclosureType(xmlEnclosureType);
#ifdef FV_GUI
// Show "look, there's an update" window
showUpdaterWindowUpdatedWithCurrentUpdateProposal();
#else
// Decide ourselves what to do
decideWhatToDoWithCurrentUpdateProposal();
#endif
return true;
}
void FvUpdater::showErrorDialog(QString message, bool showEvenInSilentMode)
{
if (m_silentAsMuchAsItCouldGet) {
if (! showEvenInSilentMode) {
// Don't show errors in the silent mode
return;
}
}
#ifdef FV_GUI
QMessageBox dlFailedMsgBox;
dlFailedMsgBox.setIcon(QMessageBox::Critical);
dlFailedMsgBox.setText(tr("Error"));
dlFailedMsgBox.setInformativeText(message);
dlFailedMsgBox.exec();
#else
qCritical() << message;
#endif
}
void FvUpdater::showInformationDialog(QString message, bool showEvenInSilentMode)
{
if (m_silentAsMuchAsItCouldGet) {
if (! showEvenInSilentMode) {
// Don't show information dialogs in the silent mode
return;
}
}
#ifdef FV_GUI
QMessageBox dlInformationMsgBox;
dlInformationMsgBox.setIcon(QMessageBox::Information);
dlInformationMsgBox.setText(tr("Information"));
dlInformationMsgBox.setInformativeText(message);
dlInformationMsgBox.exec();
#else
qDebug() << message;
#endif
}
void FvUpdater::finishUpdate(QString pathToFinish)
{
pathToFinish = pathToFinish.isEmpty() ? QCoreApplication::applicationDirPath() : pathToFinish;
QDir appDir(pathToFinish);
appDir.setFilter( QDir::Files | QDir::Dirs );
QFileInfoList dirEntries = appDir.entryInfoList();
foreach (QFileInfo fi, dirEntries)
{
if ( fi.isDir() )
{
QString dirname = fi.fileName();
if ((dirname==".") || (dirname == ".."))
continue;
// recursive clean up subdirectory
finishUpdate(fi.filePath());
}
else
{
if(fi.suffix()=="oldversion")
if( !appDir.remove( fi.absoluteFilePath() ) )
qDebug()<<"Error: Unable to clean up file: "<<fi.absoluteFilePath();
}
} // For each dir entry END
}
void FvUpdater::restartApplication()
{
// Spawn a new instance of myApplication:
QString app = QApplication::applicationFilePath();
QStringList arguments = QApplication::arguments();
QString wd = QDir::currentPath();
qDebug() << app << arguments << wd;
QProcess::startDetached(app, arguments, wd);
QApplication::exit();
}
void FvUpdater::setRequiredSslFingerPrint(QString md5)
{
m_requiredSslFingerprint = md5.remove(":");
}
QString FvUpdater::getRequiredSslFingerPrint()
{
return m_requiredSslFingerprint;
}
bool FvUpdater::checkSslFingerPrint(QUrl urltoCheck)
{
if(urltoCheck.scheme()!="https")
{
qWarning()<<tr("SSL fingerprint check: The url %1 is not a ssl connection!").arg(urltoCheck.toString());
return false;
}
QSslSocket *socket = new QSslSocket(this);
socket->connectToHostEncrypted(urltoCheck.host(), 443);
if( !socket->waitForEncrypted(1000)) // waits until ssl emits encrypted(), max 1000msecs
{
qWarning()<<"SSL fingerprint check: Unable to connect SSL server: "<<socket->sslErrors();
return false;
}
QSslCertificate cert = socket->peerCertificate();
if(cert.isNull())
{
qWarning()<<"SSL fingerprint check: Unable to retrieve SSL server certificate.";
return false;
}
// COmpare digests
if(cert.digest().toHex() != m_requiredSslFingerprint)
{
qWarning()<<"SSL fingerprint check: FINGERPRINT MISMATCH! Server digest="<<cert.digest().toHex()<<", requiered ssl digest="<<m_requiredSslFingerprint;
return false;
}
return true;
}
void FvUpdater::authenticationRequired ( QNetworkReply * reply, QAuthenticator * authenticator )
{
if(reply==NULL || authenticator==NULL)
return;
if(!authenticator->user().isEmpty()) // If there is already a login user set but an authentication is still required: credentials must be wrong -> abort
{
reply->abort();
qWarning()<<"Http authentication: Wrong credentials!";
return;
}
authenticator->setUser(htAuthUsername);
authenticator->setPassword(htAuthPassword);
}
void FvUpdater::setHtAuthCredentials(QString user, QString pass)
{
htAuthUsername = user;
htAuthPassword = pass;
}
void FvUpdater::setHtAuthUsername(QString user)
{
htAuthUsername = user;
}
void FvUpdater::setHtAuthPassword(QString pass)
{
htAuthPassword = pass;
}
void FvUpdater::setSkipVersionAllowed(bool allowed)
{
skipVersionAllowed = allowed;
}
void FvUpdater::setRemindLaterAllowed(bool allowed)
{
remindLaterAllowed = allowed;
}
bool FvUpdater::getSkipVersionAllowed()
{
return skipVersionAllowed;
}
bool FvUpdater::getRemindLaterAllowed()
{
return remindLaterAllowed;
}
#ifndef FV_GUI
void FvUpdater::decideWhatToDoWithCurrentUpdateProposal()
{
QString policy = settings->value(FV_NEW_VERSION_POLICY_KEY).toString();
if(policy == "install")
InstallUpdate();
else if(policy == "skip")
SkipUpdate();
else
RemindMeLater();
}
#endif

183
interface/external/fervor/fvupdater.h vendored Executable file
View file

@ -0,0 +1,183 @@
#ifndef FVUPDATER_H
#define FVUPDATER_H
#include <QObject>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QXmlStreamReader>
class QNetworkReply;
class FvUpdateWindow;
class FvUpdateConfirmDialog;
class FvAvailableUpdate;
class FvUpdater : public QObject
{
Q_OBJECT
public:
// Singleton
static FvUpdater* sharedUpdater();
static void drop();
// Set / get feed URL
void SetFeedURL(QUrl feedURL);
void SetFeedURL(QString feedURL);
QString GetFeedURL();
void finishUpdate(QString pathToFinish = "");
void setRequiredSslFingerPrint(QString md5);
QString getRequiredSslFingerPrint(); // returns md5!
// HTTP Authentuication - for security reasons no getters are provided, only a setter
void setHtAuthCredentials(QString user, QString pass);
void setHtAuthUsername(QString user);
void setHtAuthPassword(QString pass);
void setSkipVersionAllowed(bool allowed);
void setRemindLaterAllowed(bool allowed);
bool getSkipVersionAllowed();
bool getRemindLaterAllowed();
public slots:
// Check for updates
bool CheckForUpdates(bool silentAsMuchAsItCouldGet = true);
// Aliases
bool CheckForUpdatesSilent();
bool CheckForUpdatesNotSilent();
//
// ---------------------------------------------------
// ---------------------------------------------------
// ---------------------------------------------------
// ---------------------------------------------------
//
protected:
friend class FvUpdateWindow; // Uses GetProposedUpdate() and others
friend class FvUpdateConfirmDialog; // Uses GetProposedUpdate() and others
FvAvailableUpdate* GetProposedUpdate();
protected slots:
// Update window button slots
void InstallUpdate();
void SkipUpdate();
void RemindMeLater();
private:
//
// Singleton business
//
// (we leave just the declarations, so the compiler will warn us if we try
// to use those two functions by accident)
FvUpdater(); // Hide main constructor
~FvUpdater(); // Hide main destructor
FvUpdater(const FvUpdater&); // Hide copy constructor
FvUpdater& operator=(const FvUpdater&); // Hide assign op
static FvUpdater* m_Instance; // Singleton instance
//
// Windows / dialogs
//
#ifdef FV_GUI
FvUpdateWindow* m_updaterWindow; // Updater window (NULL if not shown)
void showUpdaterWindowUpdatedWithCurrentUpdateProposal(); // Show updater window
void hideUpdaterWindow(); // Hide + destroy m_updaterWindow
void updaterWindowWasClosed(); // Sent by the updater window when it gets closed
#else
void decideWhatToDoWithCurrentUpdateProposal(); // Perform an action which is configured in settings
#endif
// Available update (NULL if not fetched)
FvAvailableUpdate* m_proposedUpdate;
// If true, don't show the error dialogs and the "no updates." dialog
// (silentAsMuchAsItCouldGet from CheckForUpdates() goes here)
// Useful for automatic update checking upon application startup.
bool m_silentAsMuchAsItCouldGet;
// Dialogs (notifications)
bool skipVersionAllowed;
bool remindLaterAllowed;
void showErrorDialog(QString message, bool showEvenInSilentMode = false); // Show an error message
void showInformationDialog(QString message, bool showEvenInSilentMode = false); // Show an informational message
//
// HTTP feed fetcher infrastructure
//
QUrl m_feedURL; // Feed URL that will be fetched
QNetworkAccessManager m_qnam;
QNetworkReply* m_reply;
int m_httpGetId;
bool m_httpRequestAborted;
void startDownloadFeed(QUrl url); // Start downloading feed
void cancelDownloadFeed(); // Stop downloading the current feed
//
// SSL Fingerprint Check infrastructure
//
QString m_requiredSslFingerprint;
bool checkSslFingerPrint(QUrl urltoCheck); // true=ssl Fingerprint accepted, false= ssl Fingerprint NOT accepted
//
// Htauth-Infrastructure
//
QString htAuthUsername;
QString htAuthPassword;
//
// XML parser
//
QXmlStreamReader m_xml; // XML data collector and parser
bool xmlParseFeed(); // Parse feed in m_xml
bool searchDownloadedFeedForUpdates(QString xmlTitle,
QString xmlLink,
QString xmlReleaseNotesLink,
QString xmlPubDate,
QString xmlEnclosureUrl,
QString xmlEnclosureVersion,
QString xmlEnclosurePlatform,
unsigned long xmlEnclosureLength,
QString xmlEnclosureType);
//
// Helpers
//
void installTranslator(); // Initialize translation mechanism
void restartApplication(); // Restarts application after update
private slots:
void authenticationRequired ( QNetworkReply * reply, QAuthenticator * authenticator );
void httpFeedReadyRead();
void httpFeedUpdateDataReadProgress(qint64 bytesRead,
qint64 totalBytes);
void httpFeedDownloadFinished();
//
// Download and install Update infrastructure
//
void httpUpdateDownloadFinished();
bool unzipUpdate(const QString & filePath, const QString & extDirPath, const QString & singleFileName = QString("")); // returns true on success
signals:
void updatedFinishedSuccessfully();
};
#endif // FVUPDATER_H

66
interface/external/fervor/fvupdatewindow.cpp vendored Executable file
View file

@ -0,0 +1,66 @@
#include "fvupdatewindow.h"
#include "ui_fvupdatewindow.h"
#include "fvupdater.h"
#include "fvavailableupdate.h"
#include <QApplication>
#include <QCloseEvent>
#include <QDebug>
FvUpdateWindow::FvUpdateWindow(QWidget *parent, bool skipVersionAllowed, bool remindLaterAllowed) :
QWidget(parent, Qt::CustomizeWindowHint),
m_ui(new Ui::FvUpdateWindow)
{
m_ui->setupUi(this);
m_appIconScene = 0;
if(!skipVersionAllowed)
m_ui->skipThisVersionButton->hide();
if(!remindLaterAllowed)
m_ui->remindMeLaterButton->hide();
// Delete on close
setAttribute(Qt::WA_DeleteOnClose, true);
// Set the "new version is available" string
QString newVersString = m_ui->newVersionIsAvailableLabel->text().arg(QApplication::applicationName());
m_ui->newVersionIsAvailableLabel->setText(newVersString);
// Connect buttons
connect(m_ui->installUpdateButton, SIGNAL(clicked()),
FvUpdater::sharedUpdater(), SLOT(InstallUpdate()));
connect(m_ui->skipThisVersionButton, SIGNAL(clicked()),
FvUpdater::sharedUpdater(), SLOT(SkipUpdate()));
connect(m_ui->remindMeLaterButton, SIGNAL(clicked()),
FvUpdater::sharedUpdater(), SLOT(RemindMeLater()));
}
FvUpdateWindow::~FvUpdateWindow()
{
m_ui->releaseNotesWebView->stop();
delete m_ui;
}
bool FvUpdateWindow::UpdateWindowWithCurrentProposedUpdate()
{
FvAvailableUpdate* proposedUpdate = FvUpdater::sharedUpdater()->GetProposedUpdate();
if (! proposedUpdate) {
return false;
}
QString downloadString = m_ui->wouldYouLikeToDownloadLabel->text()
.arg(QApplication::applicationName(), proposedUpdate->GetEnclosureVersion(), QApplication::applicationVersion());
m_ui->wouldYouLikeToDownloadLabel->setText(downloadString);
m_ui->releaseNotesWebView->stop();
m_ui->releaseNotesWebView->load(proposedUpdate->GetReleaseNotesLink());
return true;
}
void FvUpdateWindow::closeEvent(QCloseEvent* event)
{
FvUpdater::sharedUpdater()->updaterWindowWasClosed();
event->accept();
}

37
interface/external/fervor/fvupdatewindow.h vendored Executable file
View file

@ -0,0 +1,37 @@
#ifndef FVUPDATEWINDOW_H
#define FVUPDATEWINDOW_H
#if QT_VERSION >= 0x050000
#include <QtWidgets/QWidget>
#else
#include <QWidget>
#endif
class QGraphicsScene;
namespace Ui {
class FvUpdateWindow;
}
class FvUpdateWindow : public QWidget
{
Q_OBJECT
public:
explicit FvUpdateWindow(QWidget *parent, bool skipVersionAllowed, bool remindLaterAllowed);
~FvUpdateWindow();
// Update the current update proposal from FvUpdater
bool UpdateWindowWithCurrentProposedUpdate();
void closeEvent(QCloseEvent* event);
private:
Ui::FvUpdateWindow* m_ui;
QGraphicsScene* m_appIconScene;
};
#endif // FVUPDATEWINDOW_H

129
interface/external/fervor/fvupdatewindow.ui vendored Executable file
View file

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FvUpdateWindow</class>
<widget class="QWidget" name="FvUpdateWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Software Update</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="newVersionIsAvailableLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>A new version of %1 is available!</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="wouldYouLikeToDownloadLabel">
<property name="text">
<string>%1 %2 is now available - you have %3. Would you like to download it now?</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="title">
<string>Release Notes:</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QWebView" name="releaseNotesWebView">
<property name="minimumSize">
<size>
<width>160</width>
<height>80</height>
</size>
</property>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="skipThisVersionButton">
<property name="text">
<string>Skip This Version</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="remindMeLaterButton">
<property name="text">
<string>Remind Me Later</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="installUpdateButton">
<property name="text">
<string>Install Update</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKit/QWebView</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,164 @@
#include "fvversioncomparator.h"
#include <string>
#include <algorithm>
#include <cctype>
//
// Clone of Sparkle's SUStandardVersionComparator.m, so here's original author's
// copyright too:
//
// Copyright 2007 Andy Matuschak. All rights reserved.
//
// Everything's the same except for TypeOfCharacter()
// (because who knows how Foundation does isdigit() and such.)
//
FvVersionComparator::FvVersionComparator()
{
// noop
}
FvVersionComparator::CharacterType FvVersionComparator::TypeOfCharacter(std::string character)
{
if (character == ".") {
return kSeparatorType;
} else if (isdigit(character[0])) {
return kNumberType;
} else if (isspace(character[0])) {
return kSeparatorType;
} else if (ispunct(character[0])) {
return kSeparatorType;
} else {
return kStringType;
}
}
std::vector<std::string> FvVersionComparator::SplitVersionString(std::string version)
{
std::string character;
std::string s;
unsigned long i = 0, n = 0;
CharacterType oldType, newType;
std::vector<std::string> parts;
if (version.length() == 0) {
// Nothing to do here
return parts;
}
s = version.substr(0, 1);
oldType = TypeOfCharacter(s);
n = version.length() - 1;
for (i = 1; i <= n; ++i) {
character = version.substr(i, 1)[0];
newType = TypeOfCharacter(character);
if (oldType != newType || oldType == kSeparatorType) {
// We've reached a new segment
std::string aPart = s;
parts.push_back(aPart);
s = character;
} else {
// Add character to string and continue
s.append(character);
}
oldType = newType;
}
// Add the last part onto the array
parts.push_back(s);
return parts;
}
FvVersionComparator::ComparatorResult FvVersionComparator::CompareVersions(std::string versionA,
std::string versionB)
{
std::vector<std::string> partsA = SplitVersionString(versionA);
std::vector<std::string> partsB = SplitVersionString(versionB);
std::string partA = std::string(""), partB = std::string("");
unsigned long i = 0, n = 0;
int intA, intB;
CharacterType typeA, typeB;
n = std::min(partsA.size(), partsB.size());
for (i = 0; i < n; ++i) {
partA = partsA.at(i);
partB = partsB.at(i);
typeA = TypeOfCharacter(partA);
typeB = TypeOfCharacter(partB);
// Compare types
if (typeA == typeB) {
// Same type; we can compare
if (typeA == kNumberType) {
intA = atoi(partA.c_str());
intB = atoi(partB.c_str());
if (intA > intB) {
return kDescending;
} else if (intA < intB) {
return kAscending;
}
} else if (typeA == kStringType) {
short result = partA.compare(partB);
switch (result) {
case -1: return kAscending; break;
case 1: return kDescending; break;
case 0: /* do nothing */ break;
};
}
} else {
// Not the same type? Now we have to do some validity checking
if (typeA != kStringType && typeB == kStringType) {
// typeA wins
return kDescending;
} else if (typeA == kStringType && typeB != kStringType) {
// typeB wins
return kAscending;
} else {
// One is a number and the other is a period. The period is invalid
if (typeA == kNumberType) {
return kDescending;
} else {
return kAscending;
}
}
}
}
// The versions are equal up to the point where they both still have parts
// Lets check to see if one is larger than the other
if (partsA.size() != partsB.size()) {
// Yep. Lets get the next part of the larger
// n holds the index of the part we want.
std::string missingPart = std::string("");
CharacterType missingType;
ComparatorResult shorterResult, largerResult;
if (partsA.size() > partsB.size()) {
missingPart = partsA.at(n);
shorterResult = kAscending;
largerResult = kDescending;
} else {
missingPart = partsB.at(n);
shorterResult = kDescending;
largerResult = kAscending;
}
missingType = TypeOfCharacter(missingPart);
// Check the type
if (missingType == kStringType) {
// It's a string. Shorter version wins
return shorterResult;
} else {
// It's a number/period. Larger version wins
return largerResult;
}
}
// The 2 strings are identical
return kSame;
}

View file

@ -0,0 +1,36 @@
#ifndef FVVERSIONCOMPARATOR_H
#define FVVERSIONCOMPARATOR_H
#include <iosfwd>
#include <vector>
class FvVersionComparator
{
public:
typedef enum {
kSame = 0,
kDescending = 1,
kAscending = -1
} ComparatorResult;
static ComparatorResult CompareVersions(std::string versionA,
std::string versionB);
private:
FvVersionComparator();
typedef enum {
kNumberType,
kStringType,
kSeparatorType
} CharacterType;
static CharacterType TypeOfCharacter(std::string character);
static std::vector<std::string> SplitVersionString(std::string version);
};
#endif // FVVERSIONCOMPARATOR_H

View file

@ -0,0 +1,5 @@
[INFO]
name=interface
version=0.0.1
organizationName=High Fidelity
organizationDomain=highfidelity.io

View file

@ -61,6 +61,7 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
#include "ui/TextRenderer.h"
#include "fvupdater.h"
using namespace std;
@ -246,10 +247,28 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_window->setCentralWidget(_glWidget);
// these are used, for example, to identify the application settings
setApplicationName("Interface");
setOrganizationDomain("highfidelity.io");
setOrganizationName("High Fidelity");
#ifdef Q_WS_MAC
QString resourcesPath = QCoreApplication::applicationDirPath() + "/../Resources";
#else
QString resourcesPath = QCoreApplication::applicationDirPath() + "/resources";
#endif
// read the ApplicationInfo.ini file for Name/Version/Domain information
QSettings applicationInfo(resourcesPath + "/info/ApplicationInfo.ini", QSettings::IniFormat);
// set the associated application properties
applicationInfo.beginGroup("INFO");
setApplicationName(applicationInfo.value("name").toString());
setApplicationVersion(applicationInfo.value("version").toString());
setOrganizationName(applicationInfo.value("organizationName").toString());
setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
#if defined(Q_WS_MAC) && defined(QT_NO_DEBUG)
// if this is a release OS X build use fervor to check for an update
FvUpdater::sharedUpdater()->SetFeedURL("file:///Users/birarda/Desktop/Appcast.xml");
FvUpdater::sharedUpdater()->CheckForUpdatesNotSilent();
#endif
initMenu();