xref: /glogg/src/versionchecker.cpp (revision 4fb0346e73d7caa82d42531c8c8681b5eb607728)
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         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 ( version_regex.indexIn( new_version ) != -1 )
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