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
VersionCheckerConfig()48 VersionCheckerConfig::VersionCheckerConfig()
49 {
50 enabled_ = true;
51 next_deadline_ = 0;
52 }
53
retrieveFromStorage(QSettings & settings)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
saveToStorage(QSettings & settings) const62 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
VersionChecker()69 VersionChecker::VersionChecker() : QObject(), manager_( this )
70 {
71 }
72
~VersionChecker()73 VersionChecker::~VersionChecker()
74 {
75 }
76
startCheck()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
downloadFinished(QNetworkReply * reply)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 {
isVersionNewer(const QString & current,const QString & new_version)137 bool isVersionNewer( const QString& current, const QString& new_version )
138 {
139 QRegularExpression version_regex( "(\\d+)\\.(\\d+)\\.(\\d+)(-(\\S+))?" );
140
141 // Main version is the three first digits
142 // Add is the part after '-' if there
143 unsigned current_main_version = 0;
144 unsigned current_add_version = 0;
145 unsigned new_main_version = 0;
146 unsigned new_add_version = 0;
147
148 QRegularExpressionMatch currentMatch = version_regex.match( current );
149 if ( currentMatch.hasMatch() )
150 {
151 current_main_version = currentMatch.captured(3).toInt()
152 + currentMatch.captured(2).toInt() * 100
153 + currentMatch.captured(1).toInt() * 10000;
154 current_add_version = currentMatch.captured(5).isEmpty() ? 0 : 1;
155 }
156
157 QRegularExpressionMatch newMatch = version_regex.match( new_version );
158 if ( newMatch.hasMatch() )
159 {
160 new_main_version = newMatch.captured(3).toInt()
161 + newMatch.captured(2).toInt() * 100
162 + newMatch.captured(1).toInt() * 10000;
163 new_add_version = newMatch.captured(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