diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 148e6819be..f736101830 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -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 diff --git a/interface/external/fervor/CMakeLists.txt b/interface/external/fervor/CMakeLists.txt new file mode 100644 index 0000000000..9afeb6f4ec --- /dev/null +++ b/interface/external/fervor/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/interface/external/fervor/fvavailableupdate.cpp b/interface/external/fervor/fvavailableupdate.cpp new file mode 100755 index 0000000000..f0f6cb91cd --- /dev/null +++ b/interface/external/fervor/fvavailableupdate.cpp @@ -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; +} diff --git a/interface/external/fervor/fvavailableupdate.h b/interface/external/fervor/fvavailableupdate.h new file mode 100755 index 0000000000..6bf3cf9c97 --- /dev/null +++ b/interface/external/fervor/fvavailableupdate.h @@ -0,0 +1,51 @@ +#ifndef FVAVAILABLEUPDATE_H +#define FVAVAILABLEUPDATE_H + +#include +#include + +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 diff --git a/interface/external/fervor/fvignoredversions.cpp b/interface/external/fervor/fvignoredversions.cpp new file mode 100755 index 0000000000..c665f9cff7 --- /dev/null +++ b/interface/external/fervor/fvignoredversions.cpp @@ -0,0 +1,74 @@ +#include "fvignoredversions.h" +#include "fvversioncomparator.h" +#include +#include +#include + +// 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; +} \ No newline at end of file diff --git a/interface/external/fervor/fvignoredversions.h b/interface/external/fervor/fvignoredversions.h new file mode 100755 index 0000000000..4e85f1b565 --- /dev/null +++ b/interface/external/fervor/fvignoredversions.h @@ -0,0 +1,19 @@ +#ifndef FVIGNOREDVERSIONS_H +#define FVIGNOREDVERSIONS_H + +#include + +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 diff --git a/interface/external/fervor/fvplatform.cpp b/interface/external/fervor/fvplatform.cpp new file mode 100755 index 0000000000..1a90ab1dca --- /dev/null +++ b/interface/external/fervor/fvplatform.cpp @@ -0,0 +1,210 @@ +#include "fvplatform.h" +#include +#include + +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; +} diff --git a/interface/external/fervor/fvplatform.h b/interface/external/fervor/fvplatform.h new file mode 100755 index 0000000000..a527518097 --- /dev/null +++ b/interface/external/fervor/fvplatform.h @@ -0,0 +1,18 @@ +#ifndef FVPLATFORM_H +#define FVPLATFORM_H + +#include + +class FvPlatform : public QObject +{ + Q_OBJECT + +public: + static bool CurrentlyRunningOnPlatform(QString platform); + +private: + explicit FvPlatform(QObject *parent = 0); + +}; + +#endif // FVPLATFORM_H diff --git a/interface/external/fervor/fvupdatedownloadprogress.cpp b/interface/external/fervor/fvupdatedownloadprogress.cpp new file mode 100755 index 0000000000..0b6e7934ad --- /dev/null +++ b/interface/external/fervor/fvupdatedownloadprogress.cpp @@ -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(); +} \ No newline at end of file diff --git a/interface/external/fervor/fvupdatedownloadprogress.h b/interface/external/fervor/fvupdatedownloadprogress.h new file mode 100755 index 0000000000..1b6ab0d709 --- /dev/null +++ b/interface/external/fervor/fvupdatedownloadprogress.h @@ -0,0 +1,23 @@ +#ifndef FVUPDATEDOWNLOADPROGRESS_H +#define FVUPDATEDOWNLOADPROGRESS_H + +#include +#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 diff --git a/interface/external/fervor/fvupdatedownloadprogress.ui b/interface/external/fervor/fvupdatedownloadprogress.ui new file mode 100755 index 0000000000..264e8c9728 --- /dev/null +++ b/interface/external/fervor/fvupdatedownloadprogress.ui @@ -0,0 +1,94 @@ + + + FvUpdateDownloadProgress + + + Qt::ApplicationModal + + + + 0 + 0 + 558 + 70 + + + + + 0 + 0 + + + + FvUpdateDownloadProgress + + + + 0 + + + 0 + + + + + QFrame::WinPanel + + + QFrame::Raised + + + + 8 + + + + + + 12 + + + + Qt::LeftToRight + + + QFrame::NoFrame + + + QFrame::Plain + + + Downloading Update... + + + Qt::AlignCenter + + + + + + + + 8 + + + + 24 + + + false + + + false + + + + + + + + + + + + diff --git a/interface/external/fervor/fvupdater.cpp b/interface/external/fervor/fvupdater.cpp new file mode 100755 index 0000000000..66bdc29163 --- /dev/null +++ b/interface/external/fervor/fvupdater.cpp @@ -0,0 +1,908 @@ +#include "fvupdater.h" +#include "fvplatform.h" +#include "fvignoredversions.h" +#include "fvavailableupdate.h" +#include +#include +#include +#include +#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 +#include +#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: "<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(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 "<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 updateFiles = zip.getFileInfoList(); + + // Rename all current files with available update. + for (int i=0;ideleteLater(); + } // 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()<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 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: "<connectToHostEncrypted(urltoCheck.host(), 443); + if( !socket->waitForEncrypted(1000)) // waits until ssl emits encrypted(), max 1000msecs + { + qWarning()<<"SSL fingerprint check: Unable to connect SSL server: "<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="<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 + diff --git a/interface/external/fervor/fvupdater.h b/interface/external/fervor/fvupdater.h new file mode 100755 index 0000000000..48baf0c194 --- /dev/null +++ b/interface/external/fervor/fvupdater.h @@ -0,0 +1,183 @@ +#ifndef FVUPDATER_H +#define FVUPDATER_H + +#include +#include +#include +#include +#include +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 diff --git a/interface/external/fervor/fvupdatewindow.cpp b/interface/external/fervor/fvupdatewindow.cpp new file mode 100755 index 0000000000..75329a54f3 --- /dev/null +++ b/interface/external/fervor/fvupdatewindow.cpp @@ -0,0 +1,66 @@ +#include "fvupdatewindow.h" +#include "ui_fvupdatewindow.h" +#include "fvupdater.h" +#include "fvavailableupdate.h" +#include +#include +#include + + +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(); +} diff --git a/interface/external/fervor/fvupdatewindow.h b/interface/external/fervor/fvupdatewindow.h new file mode 100755 index 0000000000..b2e3feb443 --- /dev/null +++ b/interface/external/fervor/fvupdatewindow.h @@ -0,0 +1,37 @@ +#ifndef FVUPDATEWINDOW_H +#define FVUPDATEWINDOW_H + +#if QT_VERSION >= 0x050000 + #include +#else + #include +#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 + + diff --git a/interface/external/fervor/fvupdatewindow.ui b/interface/external/fervor/fvupdatewindow.ui new file mode 100755 index 0000000000..c59646bde7 --- /dev/null +++ b/interface/external/fervor/fvupdatewindow.ui @@ -0,0 +1,129 @@ + + + FvUpdateWindow + + + + 0 + 0 + 640 + 480 + + + + Software Update + + + + QLayout::SetFixedSize + + + + + + + + 75 + true + + + + A new version of %1 is available! + + + + + + + %1 %2 is now available - you have %3. Would you like to download it now? + + + + + + + + 75 + true + + + + Release Notes: + + + + + + + 160 + 80 + + + + + about:blank + + + + + + + + + + + + + Skip This Version + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remind Me Later + + + + + + + Install Update + + + true + + + true + + + + + + + + + + + + QWebView + QWidget +
QtWebKit/QWebView
+
+
+ + +
diff --git a/interface/external/fervor/fvversioncomparator.cpp b/interface/external/fervor/fvversioncomparator.cpp new file mode 100755 index 0000000000..dc4da8a086 --- /dev/null +++ b/interface/external/fervor/fvversioncomparator.cpp @@ -0,0 +1,164 @@ +#include "fvversioncomparator.h" +#include +#include +#include + +// +// 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 FvVersionComparator::SplitVersionString(std::string version) +{ + std::string character; + std::string s; + unsigned long i = 0, n = 0; + CharacterType oldType, newType; + std::vector 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 partsA = SplitVersionString(versionA); + std::vector 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; +} diff --git a/interface/external/fervor/fvversioncomparator.h b/interface/external/fervor/fvversioncomparator.h new file mode 100755 index 0000000000..f083fdfe03 --- /dev/null +++ b/interface/external/fervor/fvversioncomparator.h @@ -0,0 +1,36 @@ +#ifndef FVVERSIONCOMPARATOR_H +#define FVVERSIONCOMPARATOR_H + +#include +#include + + +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 SplitVersionString(std::string version); + +}; + +#endif // FVVERSIONCOMPARATOR_H diff --git a/interface/resources/info/ApplicationInfo.ini b/interface/resources/info/ApplicationInfo.ini new file mode 100644 index 0000000000..76e481ffce --- /dev/null +++ b/interface/resources/info/ApplicationInfo.ini @@ -0,0 +1,5 @@ +[INFO] +name=interface +version=0.0.1 +organizationName=High Fidelity +organizationDomain=highfidelity.io \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 34aef00950..6d94f361b3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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();