1431d01deSNicolas Bonnefon /* 2431d01deSNicolas Bonnefon * Copyright (C) 2014 Nicolas Bonnefon and other contributors 3431d01deSNicolas Bonnefon * 4431d01deSNicolas Bonnefon * This file is part of glogg. 5431d01deSNicolas Bonnefon * 6431d01deSNicolas Bonnefon * glogg is free software: you can redistribute it and/or modify 7431d01deSNicolas Bonnefon * it under the terms of the GNU General Public License as published by 8431d01deSNicolas Bonnefon * the Free Software Foundation, either version 3 of the License, or 9431d01deSNicolas Bonnefon * (at your option) any later version. 10431d01deSNicolas Bonnefon * 11431d01deSNicolas Bonnefon * glogg is distributed in the hope that it will be useful, 12431d01deSNicolas Bonnefon * but WITHOUT ANY WARRANTY; without even the implied warranty of 13431d01deSNicolas Bonnefon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14431d01deSNicolas Bonnefon * GNU General Public License for more details. 15431d01deSNicolas Bonnefon * 16431d01deSNicolas Bonnefon * You should have received a copy of the GNU General Public License 17431d01deSNicolas Bonnefon * along with glogg. If not, see <http://www.gnu.org/licenses/>. 18431d01deSNicolas Bonnefon */ 19431d01deSNicolas Bonnefon 20431d01deSNicolas Bonnefon #include "versionchecker.h" 21431d01deSNicolas Bonnefon 22a3f81147SNicolas Bonnefon #include "persistentinfo.h" 23a3f81147SNicolas Bonnefon 24431d01deSNicolas Bonnefon #include "log.h" 25431d01deSNicolas Bonnefon 26*01bacde4SNicolas Bonnefon #if defined(_WIN64) 27*01bacde4SNicolas Bonnefon # define GLOGG_OS "win64" 28*01bacde4SNicolas Bonnefon #elif defined(_WIN32) 29*01bacde4SNicolas Bonnefon # define GLOGG_OS "win32" 30*01bacde4SNicolas Bonnefon #elif defined(__APPLE__) 31*01bacde4SNicolas Bonnefon # define GLOGG_OS "OSX" 32*01bacde4SNicolas Bonnefon #elif defined(__linux__) 33*01bacde4SNicolas Bonnefon # define GLOGG_OS "linux" 34*01bacde4SNicolas Bonnefon #else 35*01bacde4SNicolas Bonnefon # define GLOGG_OS "other" 36*01bacde4SNicolas Bonnefon #endif 37*01bacde4SNicolas Bonnefon 38431d01deSNicolas Bonnefon const char* VersionChecker::VERSION_URL = 39431d01deSNicolas Bonnefon "http://gloggversion.bonnefon.org/latest"; 40431d01deSNicolas Bonnefon 41a3f81147SNicolas Bonnefon const uint64_t VersionChecker::CHECK_INTERVAL_S = 42a3f81147SNicolas Bonnefon 3600 * 24 * 7; /* 7 days */ 43a3f81147SNicolas Bonnefon 4492bcda3bSNicolas Bonnefon namespace { 4592bcda3bSNicolas Bonnefon bool isVersionNewer( const QString& current, const QString& new_version ); 4692bcda3bSNicolas Bonnefon }; 4792bcda3bSNicolas Bonnefon 48a3f81147SNicolas Bonnefon VersionCheckerConfig::VersionCheckerConfig() 49a3f81147SNicolas Bonnefon { 50a3f81147SNicolas Bonnefon enabled_ = true; 51a3f81147SNicolas Bonnefon next_deadline_ = 0; 52a3f81147SNicolas Bonnefon } 53a3f81147SNicolas Bonnefon 54a3f81147SNicolas Bonnefon void VersionCheckerConfig::retrieveFromStorage( QSettings& settings ) 55a3f81147SNicolas Bonnefon { 56a3f81147SNicolas Bonnefon if ( settings.contains( "versionchecker.enabled" ) ) 57a3f81147SNicolas Bonnefon enabled_ = settings.value( "versionchecker.enabled" ).toBool(); 58a3f81147SNicolas Bonnefon if ( settings.contains( "versionchecker.nextDeadline" ) ) 59a3f81147SNicolas Bonnefon next_deadline_ = settings.value( "versionchecker.nextDeadline" ).toLongLong(); 60a3f81147SNicolas Bonnefon } 61a3f81147SNicolas Bonnefon 62a3f81147SNicolas Bonnefon void VersionCheckerConfig::saveToStorage( QSettings& settings ) const 63a3f81147SNicolas Bonnefon { 64a3f81147SNicolas Bonnefon settings.setValue( "versionchecker.enabled", enabled_ ); 65a3f81147SNicolas Bonnefon settings.setValue( "versionchecker.nextDeadline", 66a3f81147SNicolas Bonnefon static_cast<long long>( next_deadline_ ) ); 67a3f81147SNicolas Bonnefon } 68a3f81147SNicolas Bonnefon 69431d01deSNicolas Bonnefon VersionChecker::VersionChecker() : QObject(), manager_( this ) 70431d01deSNicolas Bonnefon { 71431d01deSNicolas Bonnefon } 72431d01deSNicolas Bonnefon 73431d01deSNicolas Bonnefon VersionChecker::~VersionChecker() 74431d01deSNicolas Bonnefon { 75431d01deSNicolas Bonnefon } 76431d01deSNicolas Bonnefon 77431d01deSNicolas Bonnefon void VersionChecker::startCheck() 78431d01deSNicolas Bonnefon { 79431d01deSNicolas Bonnefon LOG(logDEBUG) << "VersionChecker::startCheck()"; 80431d01deSNicolas Bonnefon 81a3f81147SNicolas Bonnefon GetPersistentInfo().retrieve( "versionChecker" ); 82a3f81147SNicolas Bonnefon 83a3f81147SNicolas Bonnefon auto config = Persistent<VersionCheckerConfig>( "versionChecker" ); 84a3f81147SNicolas Bonnefon 85a3f81147SNicolas Bonnefon if ( config->versionCheckingEnabled() ) 86a3f81147SNicolas Bonnefon { 87a3f81147SNicolas Bonnefon // Check the deadline has been reached 88a3f81147SNicolas Bonnefon if ( config->nextDeadline() < std::time( nullptr ) ) 89a3f81147SNicolas Bonnefon { 90431d01deSNicolas Bonnefon connect( &manager_, SIGNAL( finished( QNetworkReply* ) ), 91431d01deSNicolas Bonnefon this, SLOT( downloadFinished( QNetworkReply* ) ) ); 92431d01deSNicolas Bonnefon 93431d01deSNicolas Bonnefon QNetworkRequest request; 94431d01deSNicolas Bonnefon request.setUrl( QUrl( VERSION_URL ) ); 95*01bacde4SNicolas Bonnefon request.setRawHeader( "User-Agent", "glogg-" GLOGG_VERSION " (" GLOGG_OS ")" ); 96431d01deSNicolas Bonnefon manager_.get( request ); 97431d01deSNicolas Bonnefon } 98a3f81147SNicolas Bonnefon else 99a3f81147SNicolas Bonnefon { 100a3f81147SNicolas Bonnefon LOG(logDEBUG) << "Deadline not reached yet, next check in " 101a3f81147SNicolas Bonnefon << std::difftime( config->nextDeadline(), std::time( nullptr ) ); 102a3f81147SNicolas Bonnefon } 103a3f81147SNicolas Bonnefon } 104a3f81147SNicolas Bonnefon } 105431d01deSNicolas Bonnefon 106431d01deSNicolas Bonnefon void VersionChecker::downloadFinished( QNetworkReply* reply ) 107431d01deSNicolas Bonnefon { 108431d01deSNicolas Bonnefon LOG(logDEBUG) << "VersionChecker::downloadFinished()"; 10992bcda3bSNicolas Bonnefon 11092bcda3bSNicolas Bonnefon if ( reply->error() == QNetworkReply::NoError ) 11192bcda3bSNicolas Bonnefon { 112460de700SNicolas Bonnefon QString new_version = QString( reply->read( 256 ) ).remove( '\n' ); 113460de700SNicolas Bonnefon 11492bcda3bSNicolas Bonnefon LOG(logDEBUG) << "Latest version is " << new_version.toStdString(); 11592bcda3bSNicolas Bonnefon if ( isVersionNewer( QString( GLOGG_VERSION ), new_version ) ) 11692bcda3bSNicolas Bonnefon { 117460de700SNicolas Bonnefon LOG(logDEBUG) << "Sending new version notification"; 11892bcda3bSNicolas Bonnefon emit newVersionFound( new_version ); 119431d01deSNicolas Bonnefon } 12092bcda3bSNicolas Bonnefon } 12192bcda3bSNicolas Bonnefon else 12292bcda3bSNicolas Bonnefon { 12392bcda3bSNicolas Bonnefon LOG(logWARNING) << "Download failed: err " << reply->error(); 12492bcda3bSNicolas Bonnefon } 12592bcda3bSNicolas Bonnefon 12692bcda3bSNicolas Bonnefon reply->deleteLater(); 127a3f81147SNicolas Bonnefon 128a3f81147SNicolas Bonnefon // Extend the deadline 129a3f81147SNicolas Bonnefon auto config = Persistent<VersionCheckerConfig>( "versionChecker" ); 130a3f81147SNicolas Bonnefon 131a3f81147SNicolas Bonnefon config->setNextDeadline( std::time( nullptr ) + CHECK_INTERVAL_S ); 132a3f81147SNicolas Bonnefon 133a3f81147SNicolas Bonnefon GetPersistentInfo().save( "versionChecker" ); 13492bcda3bSNicolas Bonnefon } 13592bcda3bSNicolas Bonnefon 13692bcda3bSNicolas Bonnefon namespace { 13792bcda3bSNicolas Bonnefon bool isVersionNewer( const QString& current, const QString& new_version ) 13892bcda3bSNicolas Bonnefon { 139460de700SNicolas Bonnefon QRegExp version_regex( "(\\d+)\\.(\\d+)\\.(\\d+)(-(\\S+))?", 140460de700SNicolas Bonnefon Qt::CaseSensitive, 141460de700SNicolas Bonnefon QRegExp::RegExp2 ); 142460de700SNicolas Bonnefon 143460de700SNicolas Bonnefon // Main version is the three first digits 144460de700SNicolas Bonnefon // Add is the part after '-' if there 145460de700SNicolas Bonnefon unsigned current_main_version = 0; 146460de700SNicolas Bonnefon unsigned current_add_version = 0; 147460de700SNicolas Bonnefon unsigned new_main_version = 0; 148460de700SNicolas Bonnefon unsigned new_add_version = 0; 149460de700SNicolas Bonnefon 150460de700SNicolas Bonnefon if ( version_regex.indexIn( current ) != -1 ) 151460de700SNicolas Bonnefon { 152460de700SNicolas Bonnefon current_main_version = version_regex.cap(3).toInt() 153460de700SNicolas Bonnefon + version_regex.cap(2).toInt() * 100 154460de700SNicolas Bonnefon + version_regex.cap(1).toInt() * 10000; 155460de700SNicolas Bonnefon current_add_version = version_regex.cap(5).isEmpty() ? 0 : 1; 156460de700SNicolas Bonnefon } 157460de700SNicolas Bonnefon 158460de700SNicolas Bonnefon if ( version_regex.indexIn( new_version ) != -1 ) 159460de700SNicolas Bonnefon { 160460de700SNicolas Bonnefon new_main_version = version_regex.cap(3).toInt() 161460de700SNicolas Bonnefon + version_regex.cap(2).toInt() * 100 162460de700SNicolas Bonnefon + version_regex.cap(1).toInt() * 10000; 163460de700SNicolas Bonnefon new_add_version = version_regex.cap(5).isEmpty() ? 0 : 1; 164460de700SNicolas Bonnefon } 165460de700SNicolas Bonnefon 166460de700SNicolas Bonnefon LOG(logDEBUG) << "Current version: " << current_main_version; 167460de700SNicolas Bonnefon LOG(logDEBUG) << "New version: " << new_main_version; 168460de700SNicolas Bonnefon 169460de700SNicolas Bonnefon // We only consider the main part for testing for now 170460de700SNicolas Bonnefon return new_main_version > current_main_version; 17192bcda3bSNicolas Bonnefon } 17292bcda3bSNicolas Bonnefon }; 173