xref: /glogg/src/versionchecker.cpp (revision 6e2e573c451ec24d720c967fab68538eaa3f3ab5)
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 const char* VersionChecker::VERSION_URL =
27     "http://gloggversion.bonnefon.org/latest";
28 
29 const uint64_t VersionChecker::CHECK_INTERVAL_S =
30     3600 * 24 * 7; /* 7 days */
31 
32 namespace {
33     bool isVersionNewer( const QString& current, const QString& new_version );
34 };
35 
36 VersionCheckerConfig::VersionCheckerConfig()
37 {
38     enabled_ = true;
39     next_deadline_ = 0;
40 }
41 
42 void VersionCheckerConfig::retrieveFromStorage( QSettings& settings )
43 {
44     if ( settings.contains( "versionchecker.enabled" ) )
45         enabled_ = settings.value( "versionchecker.enabled" ).toBool();
46     if ( settings.contains( "versionchecker.nextDeadline" ) )
47         next_deadline_ = settings.value( "versionchecker.nextDeadline" ).toLongLong();
48 }
49 
50 void VersionCheckerConfig::saveToStorage( QSettings& settings ) const
51 {
52     settings.setValue( "versionchecker.enabled", enabled_ );
53     settings.setValue( "versionchecker.nextDeadline",
54             static_cast<long long>( next_deadline_ ) );
55 }
56 
57 VersionChecker::VersionChecker() : QObject(), manager_( this )
58 {
59 }
60 
61 VersionChecker::~VersionChecker()
62 {
63 }
64 
65 void VersionChecker::startCheck()
66 {
67     LOG(logDEBUG) << "VersionChecker::startCheck()";
68 
69     GetPersistentInfo().retrieve( "versionChecker" );
70 
71     auto config = Persistent<VersionCheckerConfig>( "versionChecker" );
72 
73     if ( config->versionCheckingEnabled() )
74     {
75         // Check the deadline has been reached
76         if ( config->nextDeadline() < std::time( nullptr ) )
77         {
78             connect( &manager_, SIGNAL( finished( QNetworkReply* ) ),
79                     this, SLOT( downloadFinished( QNetworkReply* ) ) );
80 
81             QNetworkRequest request;
82             request.setUrl( QUrl( VERSION_URL ) );
83             request.setRawHeader( "User-Agent", "glogg-" GLOGG_VERSION );
84             manager_.get( request );
85         }
86         else
87         {
88             LOG(logDEBUG) << "Deadline not reached yet, next check in "
89                 << std::difftime( config->nextDeadline(), std::time( nullptr ) );
90         }
91     }
92 }
93 
94 void VersionChecker::downloadFinished( QNetworkReply* reply )
95 {
96     LOG(logDEBUG) << "VersionChecker::downloadFinished()";
97 
98     if ( reply->error() == QNetworkReply::NoError )
99     {
100         QString new_version = QString( reply->read( 256 ) ).remove( '\n' );
101 
102         LOG(logDEBUG) << "Latest version is " << new_version.toStdString();
103         if ( isVersionNewer( QString( GLOGG_VERSION ), new_version ) )
104         {
105             LOG(logDEBUG) << "Sending new version notification";
106             emit newVersionFound( new_version );
107         }
108     }
109     else
110     {
111         LOG(logWARNING) << "Download failed: err " << reply->error();
112     }
113 
114     reply->deleteLater();
115 
116     // Extend the deadline
117     auto config = Persistent<VersionCheckerConfig>( "versionChecker" );
118 
119     config->setNextDeadline( std::time( nullptr ) + CHECK_INTERVAL_S );
120 
121     GetPersistentInfo().save( "versionChecker" );
122 }
123 
124 namespace {
125     bool isVersionNewer( const QString& current, const QString& new_version )
126     {
127         QRegExp version_regex( "(\\d+)\\.(\\d+)\\.(\\d+)(-(\\S+))?",
128                 Qt::CaseSensitive,
129                 QRegExp::RegExp2 );
130 
131         // Main version is the three first digits
132         // Add is the part after '-' if there
133         unsigned current_main_version = 0;
134         unsigned current_add_version = 0;
135         unsigned new_main_version = 0;
136         unsigned new_add_version = 0;
137 
138         if ( version_regex.indexIn( current ) != -1 )
139         {
140             current_main_version = version_regex.cap(3).toInt()
141                 + version_regex.cap(2).toInt() * 100
142                 + version_regex.cap(1).toInt() * 10000;
143             current_add_version = version_regex.cap(5).isEmpty() ? 0 : 1;
144         }
145 
146         if ( version_regex.indexIn( new_version ) != -1 )
147         {
148             new_main_version = version_regex.cap(3).toInt()
149                 + version_regex.cap(2).toInt() * 100
150                 + version_regex.cap(1).toInt() * 10000;
151             new_add_version = version_regex.cap(5).isEmpty() ? 0 : 1;
152         }
153 
154         LOG(logDEBUG) << "Current version: " << current_main_version;
155         LOG(logDEBUG) << "New version: " << new_main_version;
156 
157         // We only consider the main part for testing for now
158         return new_main_version > current_main_version;
159     }
160 };
161