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 2601bacde4SNicolas Bonnefon #if defined(_WIN64) 2701bacde4SNicolas Bonnefon # define GLOGG_OS "win64" 2801bacde4SNicolas Bonnefon #elif defined(_WIN32) 2901bacde4SNicolas Bonnefon # define GLOGG_OS "win32" 3001bacde4SNicolas Bonnefon #elif defined(__APPLE__) 3101bacde4SNicolas Bonnefon # define GLOGG_OS "OSX" 3201bacde4SNicolas Bonnefon #elif defined(__linux__) 3301bacde4SNicolas Bonnefon # define GLOGG_OS "linux" 3401bacde4SNicolas Bonnefon #else 3501bacde4SNicolas Bonnefon # define GLOGG_OS "other" 3601bacde4SNicolas Bonnefon #endif 3701bacde4SNicolas 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 ) ); 9501bacde4SNicolas 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 { 139*4fb0346eSAnton Filimonov QRegularExpression version_regex( "(\\d+)\\.(\\d+)\\.(\\d+)(-(\\S+))?" ); 140460de700SNicolas Bonnefon 141460de700SNicolas Bonnefon // Main version is the three first digits 142460de700SNicolas Bonnefon // Add is the part after '-' if there 143460de700SNicolas Bonnefon unsigned current_main_version = 0; 144460de700SNicolas Bonnefon unsigned current_add_version = 0; 145460de700SNicolas Bonnefon unsigned new_main_version = 0; 146460de700SNicolas Bonnefon unsigned new_add_version = 0; 147460de700SNicolas Bonnefon 148*4fb0346eSAnton Filimonov QRegularExpressionMatch currentMatch = version_regex.match( current ); 149*4fb0346eSAnton Filimonov if ( currentMatch.hasMatch() ) 150460de700SNicolas Bonnefon { 151*4fb0346eSAnton Filimonov current_main_version = currentMatch.captured(3).toInt() 152*4fb0346eSAnton Filimonov + currentMatch.captured(2).toInt() * 100 153*4fb0346eSAnton Filimonov + currentMatch.captured(1).toInt() * 10000; 154*4fb0346eSAnton Filimonov current_add_version = currentMatch.captured(5).isEmpty() ? 0 : 1; 155460de700SNicolas Bonnefon } 156460de700SNicolas Bonnefon 157*4fb0346eSAnton Filimonov QRegularExpressionMatch newMatch = version_regex.match( new_version ); 158460de700SNicolas Bonnefon if ( version_regex.indexIn( new_version ) != -1 ) 159460de700SNicolas Bonnefon { 160*4fb0346eSAnton Filimonov new_main_version = newMatch.captured(3).toInt() 161*4fb0346eSAnton Filimonov + newMatch.captured(2).toInt() * 100 162*4fb0346eSAnton Filimonov + newMatch.captured(1).toInt() * 10000; 163*4fb0346eSAnton Filimonov new_add_version = newMatch.captured(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