1 /* 2 * Copyright (C) 2014 Nicolas Bonnefon and other contributors 3 * 4 * This file is part of glogg. 5 * 6 * glogg is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * glogg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with glogg. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "versionchecker.h" 21 22 #include "persistentinfo.h" 23 24 #include "log.h" 25 26 #if defined(_WIN64) 27 # define GLOGG_OS "win64" 28 #elif defined(_WIN32) 29 # define GLOGG_OS "win32" 30 #elif defined(__APPLE__) 31 # define GLOGG_OS "OSX" 32 #elif defined(__linux__) 33 # define GLOGG_OS "linux" 34 #else 35 # define GLOGG_OS "other" 36 #endif 37 38 const char* VersionChecker::VERSION_URL = 39 "http://gloggversion.bonnefon.org/latest"; 40 41 const uint64_t VersionChecker::CHECK_INTERVAL_S = 42 3600 * 24 * 7; /* 7 days */ 43 44 namespace { 45 bool isVersionNewer( const QString& current, const QString& new_version ); 46 }; 47 48 VersionCheckerConfig::VersionCheckerConfig() 49 { 50 enabled_ = true; 51 next_deadline_ = 0; 52 } 53 54 void VersionCheckerConfig::retrieveFromStorage( QSettings& settings ) 55 { 56 if ( settings.contains( "versionchecker.enabled" ) ) 57 enabled_ = settings.value( "versionchecker.enabled" ).toBool(); 58 if ( settings.contains( "versionchecker.nextDeadline" ) ) 59 next_deadline_ = settings.value( "versionchecker.nextDeadline" ).toLongLong(); 60 } 61 62 void VersionCheckerConfig::saveToStorage( QSettings& settings ) const 63 { 64 settings.setValue( "versionchecker.enabled", enabled_ ); 65 settings.setValue( "versionchecker.nextDeadline", 66 static_cast<long long>( next_deadline_ ) ); 67 } 68 69 VersionChecker::VersionChecker() : QObject(), manager_( this ) 70 { 71 } 72 73 VersionChecker::~VersionChecker() 74 { 75 } 76 77 void VersionChecker::startCheck() 78 { 79 LOG(logDEBUG) << "VersionChecker::startCheck()"; 80 81 GetPersistentInfo().retrieve( "versionChecker" ); 82 83 auto config = Persistent<VersionCheckerConfig>( "versionChecker" ); 84 85 if ( config->versionCheckingEnabled() ) 86 { 87 // Check the deadline has been reached 88 if ( config->nextDeadline() < std::time( nullptr ) ) 89 { 90 connect( &manager_, SIGNAL( finished( QNetworkReply* ) ), 91 this, SLOT( downloadFinished( QNetworkReply* ) ) ); 92 93 QNetworkRequest request; 94 request.setUrl( QUrl( VERSION_URL ) ); 95 request.setRawHeader( "User-Agent", "glogg-" GLOGG_VERSION " (" GLOGG_OS ")" ); 96 manager_.get( request ); 97 } 98 else 99 { 100 LOG(logDEBUG) << "Deadline not reached yet, next check in " 101 << std::difftime( config->nextDeadline(), std::time( nullptr ) ); 102 } 103 } 104 } 105 106 void VersionChecker::downloadFinished( QNetworkReply* reply ) 107 { 108 LOG(logDEBUG) << "VersionChecker::downloadFinished()"; 109 110 if ( reply->error() == QNetworkReply::NoError ) 111 { 112 QString new_version = QString( reply->read( 256 ) ).remove( '\n' ); 113 114 LOG(logDEBUG) << "Latest version is " << new_version.toStdString(); 115 if ( isVersionNewer( QString( GLOGG_VERSION ), new_version ) ) 116 { 117 LOG(logDEBUG) << "Sending new version notification"; 118 emit newVersionFound( new_version ); 119 } 120 } 121 else 122 { 123 LOG(logWARNING) << "Download failed: err " << reply->error(); 124 } 125 126 reply->deleteLater(); 127 128 // Extend the deadline 129 auto config = Persistent<VersionCheckerConfig>( "versionChecker" ); 130 131 config->setNextDeadline( std::time( nullptr ) + CHECK_INTERVAL_S ); 132 133 GetPersistentInfo().save( "versionChecker" ); 134 } 135 136 namespace { 137 bool isVersionNewer( const QString& current, const QString& new_version ) 138 { 139 QRegExp version_regex( "(\\d+)\\.(\\d+)\\.(\\d+)(-(\\S+))?", 140 Qt::CaseSensitive, 141 QRegExp::RegExp2 ); 142 143 // Main version is the three first digits 144 // Add is the part after '-' if there 145 unsigned current_main_version = 0; 146 unsigned current_add_version = 0; 147 unsigned new_main_version = 0; 148 unsigned new_add_version = 0; 149 150 if ( version_regex.indexIn( current ) != -1 ) 151 { 152 current_main_version = version_regex.cap(3).toInt() 153 + version_regex.cap(2).toInt() * 100 154 + version_regex.cap(1).toInt() * 10000; 155 current_add_version = version_regex.cap(5).isEmpty() ? 0 : 1; 156 } 157 158 if ( version_regex.indexIn( new_version ) != -1 ) 159 { 160 new_main_version = version_regex.cap(3).toInt() 161 + version_regex.cap(2).toInt() * 100 162 + version_regex.cap(1).toInt() * 10000; 163 new_add_version = version_regex.cap(5).isEmpty() ? 0 : 1; 164 } 165 166 LOG(logDEBUG) << "Current version: " << current_main_version; 167 LOG(logDEBUG) << "New version: " << new_main_version; 168 169 // We only consider the main part for testing for now 170 return new_main_version > current_main_version; 171 } 172 }; 173