ip2cupdater.cpp
1 //------------------------------------------------------------------------------
2 // ip2cupdater.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2009 "Blzut3" <admin@maniacsvault.net>
22 //------------------------------------------------------------------------------
23 #include "ip2cupdater.h"
24 
25 #include <QCryptographicHash>
26 #include <QFile>
27 #include <QTemporaryFile>
28 #include <zlib.h>
29 
30 #include <wadseeker/protocols/fixednetworkaccessmanager.h>
31 
32 #include "log.h"
33 #include "version.h"
34 
35 IP2CUpdater::IP2CUpdater(QObject *parent)
36  : QObject(parent)
37 {
38  pCurrentNetworkReply = NULL;
39  pNetworkAccessManager = new FixedNetworkAccessManager();
40 }
41 
42 IP2CUpdater::~IP2CUpdater()
43 {
44  abort();
45 
46  if (pNetworkAccessManager != NULL)
47  {
48  pNetworkAccessManager->deleteLater();
49  }
50 }
51 
52 void IP2CUpdater::abort()
53 {
54  if (pCurrentNetworkReply != NULL)
55  {
56  pCurrentNetworkReply->disconnect();
57  pCurrentNetworkReply->abort();
58  pCurrentNetworkReply->deleteLater();
59  }
60  pCurrentNetworkReply = NULL;
61 }
62 
63 void IP2CUpdater::checksumDownloadFinished()
64 {
65  if (handleRedirect(*pCurrentNetworkReply, SLOT(checksumDownloadFinished())))
66  {
67  return;
68  }
69 
70  if (pCurrentNetworkReply->error() != QNetworkReply::NoError)
71  {
72  gLog << tr("IP2C checksum check network error: %1").arg(pCurrentNetworkReply->errorString());
73  abort();
74  emit updateNeeded(UpdateCheckError);
75  return;
76  }
77 
78  QByteArray remoteMd5 = pCurrentNetworkReply->readAll();
79  remoteMd5 = remoteMd5.trimmed();
80 
81  pCurrentNetworkReply->deleteLater();
82  pCurrentNetworkReply = NULL;
83 
84  QFile file(this->pathToFile);
85  file.open(QIODevice::ReadOnly);
86  QByteArray localMd5 = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5);
87  localMd5 = localMd5.toHex().toLower();
88 
89  gLog << tr("Comparing IP2C hashes: local = %1, remote = %2").arg(
90  QString(localMd5)).arg(QString(remoteMd5));
91  bool needed = localMd5 != remoteMd5;
92  if (needed)
93  {
94  gLog << tr("IP2C update needed.");
95  }
96  emit updateNeeded(needed ? UpdateNeeded : UpToDate);
97 }
98 
99 const QUrl IP2CUpdater::dbChecksumUrl()
100 {
101  return QUrl("http://doomseeker.drdteam.org/ip2c/md5");
102 }
103 
104 const QUrl IP2CUpdater::dbDownloadUrl()
105 {
106  return QUrl("http://doomseeker.drdteam.org/ip2c/geolite2.gz");
107 }
108 
109 void IP2CUpdater::downloadDatabase()
110 {
111  get(dbDownloadUrl(), SLOT(downloadFinished()));
112 }
113 
114 void IP2CUpdater::downloadFinished()
115 {
116  if (handleRedirect(*pCurrentNetworkReply, SLOT(downloadFinished())))
117  {
118  return;
119  }
120 
121  QByteArray data = pCurrentNetworkReply->readAll();
122 
123  pCurrentNetworkReply->deleteLater();
124  pCurrentNetworkReply = NULL;
125 
126  // First we need to write it to a temporary file
127  QTemporaryFile tmpFile;
128  if(tmpFile.open())
129  {
130  tmpFile.write(data);
131 
132  QString tmpFilePath = tmpFile.fileName();
133 
134  QByteArray uncompressedData;
135  gzFile gz = gzopen(tmpFilePath.toUtf8().constData(), "rb");
136  if(gz != NULL)
137  {
138  char chunk[131072]; // 128k
139  int bytesRead = 0;
140  while((bytesRead = gzread(gz, chunk, 131072)) != 0)
141  {
142  uncompressedData.append(QByteArray(chunk, bytesRead));
143  }
144  gzclose(gz);
145 
146  retrievedData = uncompressedData;
147  }
148  }
149 
150  emit databaseDownloadFinished(retrievedData);
151 }
152 
153 bool IP2CUpdater::handleRedirect(QNetworkReply &reply, const char *finishedSlot)
154 {
155  QUrl possibleRedirectUrl = reply.attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
156  QUrl url = reply.request().url();
157  if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != url)
158  {
159  // Redirect.
160  if (possibleRedirectUrl.isRelative())
161  {
162  possibleRedirectUrl = url.resolved(possibleRedirectUrl);
163  }
164  get(possibleRedirectUrl, finishedSlot);
165  return true;
166  }
167  return false;
168 }
169 
170 void IP2CUpdater::get(const QUrl &url, const char *finishedSlot)
171 {
172  abort();
173  retrievedData.clear();
174 
175  QNetworkRequest request;
176  request.setUrl(url);
177  request.setRawHeader("User-Agent", Version::userAgent().toUtf8());
178 
179  pCurrentNetworkReply = pNetworkAccessManager->get(request);
180  this->connect(pCurrentNetworkReply, SIGNAL(downloadProgress(qint64, qint64)),
181  SIGNAL(downloadProgress(qint64, qint64)));
182  this->connect(pCurrentNetworkReply, SIGNAL(finished()), finishedSlot);
183 }
184 
186 {
187  rollbackData.clear();
188 
189  QFile file(pathToFile);
190  if (!file.exists())
191  {
192  return false;
193  }
194 
195  if (!file.open(QIODevice::ReadOnly))
196  {
197  return false;
198  }
199 
200  rollbackData = file.readAll();
201  file.close();
202 
203  return true;
204 }
205 
206 bool IP2CUpdater::isWorking() const
207 {
208  return pCurrentNetworkReply != NULL;
209 }
210 
211 void IP2CUpdater::needsUpdate(const QString& filePath)
212 {
213  QFile file(filePath);
214  if (!file.exists())
215  {
216  emit updateNeeded(UpdateNeeded);
217  return;
218  }
219 
220  this->pathToFile = filePath;
221  gLog << tr("Checking if IP2C database at '%1' needs updating.").arg(filePath);
222  get(dbChecksumUrl(), SLOT(checksumDownloadFinished()));
223 }
224 
226 {
227  bool bSuccess = save(rollbackData);
228  rollbackData.clear();
229 
230  return bSuccess;
231 }
232 
233 bool IP2CUpdater::save(const QByteArray& saveWhat)
234 {
235  if (saveWhat.isEmpty())
236  {
237  return false;
238  }
239 
240  QFile file(pathToFile);
241  if (!file.open(QIODevice::WriteOnly))
242  {
243  return false;
244  }
245 
246  file.write(saveWhat);
247  file.close();
248 
249  return true;
250 }
251 
253 {
254  return save(retrievedData);
255 }
bool getRollbackData()
Obtains rollback data from pathToFile file.
static QString userAgent()
WWW User Agent used for HTTP communications.
Definition: version.cpp:64
bool rollback()
Saves data to the pathToFile file. This data must be first obtained through the rollback method...
void needsUpdate(const QString &filePath)
Checks if IP2C file must be updated.
bool saveDownloadedData()
Saves recently downloaded data to the pathToFile file.