ip2cloader.cpp
1 //------------------------------------------------------------------------------
2 // ip2cloader.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library 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 GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; 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) 2013 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "ip2cloader.h"
24 
25 #include "configuration/doomseekerconfig.h"
26 #include "doomseekerfilepaths.h"
27 #include "global.h"
28 #include "ip2c/ip2c.h"
29 #include "ip2c/ip2cparser.h"
30 #include "ip2c/ip2cupdater.h"
31 #include "log.h"
32 #include <QFile>
33 #include <QTimer>
34 
35 DClass<IP2CLoader>
36 {
37 public:
38  IP2CParser *ip2cParser;
39  IP2CUpdater *ip2cUpdater;
40  bool updateInProgress;
41  bool inFallbackMode;
42 };
43 
44 DPointered(IP2CLoader)
45 
46 
47 IP2CLoader::IP2CLoader(QObject *parent)
48  : QObject(parent)
49 {
50  d->updateInProgress = false;
51  d->inFallbackMode = false;
52 
53  d->ip2cParser = new IP2CParser();
54  this->connect(d->ip2cParser, SIGNAL(parsingFinished(bool)),
55  SLOT(ip2cFinishedParsing(bool)));
56 
57  d->ip2cUpdater = new IP2CUpdater();
58  this->connect(d->ip2cUpdater, SIGNAL(databaseDownloadFinished(const QByteArray&)),
59  SLOT(ip2cFinishUpdate(const QByteArray&)));
60  this->connect(d->ip2cUpdater, SIGNAL(downloadProgress(qint64,qint64)),
61  SIGNAL(downloadProgress(qint64,qint64)));
62  this->connect(d->ip2cUpdater, SIGNAL(updateNeeded(int)),
63  SLOT(onUpdateNeeded(int)));
64 }
65 
66 IP2CLoader::~IP2CLoader()
67 {
68  if (d->ip2cParser->isParsing())
69  {
70  gLog << tr("IP2C parser is still working, awaiting stop...");
71  while (d->ip2cParser->isParsing())
72  {
73  Sleep::sleep(1);
74  }
75  }
76 
77  delete d->ip2cParser;
78  delete d->ip2cUpdater;
79 }
80 
81 void IP2CLoader::load()
82 {
83  if (gConfig.doomseeker.bIP2CountryAutoUpdate)
84  d->ip2cUpdater->needsUpdate(DoomseekerFilePaths::ip2cDatabaseAny());
85  ip2cParseDatabase();
86 }
87 
88 void IP2CLoader::onUpdateNeeded(int status)
89 {
90  if (status == IP2CUpdater::UpdateNeeded)
91  update();
92  else
93  {
94  switch (status)
95  {
96  case IP2CUpdater::UpToDate:
97  gLog << tr("IP2C update not needed.");
98  break;
99  case IP2CUpdater::UpdateCheckError:
100  gLog << tr("IP2C update errored. See log for details.");
101  break;
102  default:
103  gLog << tr("IP2C update bugged out.");
104  break;
105  }
106  ip2cJobsFinished();
107  }
108 }
109 
110 void IP2CLoader::update()
111 {
112  d->updateInProgress = true;
113  if (!d->ip2cParser->isParsing())
114  {
115  gLog << tr("Starting IP2C update.");
116  IP2C::instance()->setDataAccessLockEnabled(true);
117  d->ip2cUpdater->downloadDatabase(DoomseekerFilePaths::ip2cDatabase());
118  }
119  else
120  {
121  // Delay in hope that parser finishes
122  gLog << tr("IP2C update must wait until parsing of current database finishes. "
123  "Waiting 1 second.");
124  QTimer::singleShot(1000, this, SLOT(update()));
125  }
126 }
127 
128 void IP2CLoader::ip2cFinishUpdate(const QByteArray &downloadedData)
129 {
130  d->updateInProgress = false;
131  if (!downloadedData.isEmpty())
132  {
133  gLog << tr("IP2C database finished downloading.");
134  QString filePath = DoomseekerFilePaths::ip2cDatabase();
135  d->ip2cUpdater->getRollbackData(filePath);
136  if (!d->ip2cUpdater->saveDownloadedData(filePath))
137  gLog << tr("Unable to save IP2C database at path: %1").arg(filePath);
138  ip2cParseDatabase();
139  }
140  else
141  {
142  gLog << tr("IP2C download has failed.");
143  ip2cJobsFinished();
144  }
145 }
146 
147 void IP2CLoader::ip2cFinishedParsing(bool bSuccess)
148 {
149  QString filePath = DoomseekerFilePaths::ip2cDatabase();
150 
151  if (!bSuccess)
152  {
153  if (d->inFallbackMode)
154  {
155  gLog << tr("Failed to read IP2C fallback. Stopping.");
156  ip2cJobsFinished();
157  return;
158  }
159  gLog << tr("Failed to read IP2C database. Reverting...");
160 
161  d->inFallbackMode = true;
162  if (d->ip2cUpdater == nullptr || !d->ip2cUpdater->hasRollbackData())
163  {
164  gLog << tr("IP2C revert attempt failed. Nothing to go back to.");
165 
166  // Delete file in this case.
167  QFile file(filePath);
168  file.remove();
169 
170  QString preinstalledDbPath = DoomseekerFilePaths::ip2cDatabaseAny();
171  if (!preinstalledDbPath.isEmpty())
172  {
173  gLog << tr("Trying to use preinstalled IP2C database.");
174  d->ip2cParser->readDatabaseThreaded(preinstalledDbPath);
175  }
176  else
177  ip2cJobsFinished();
178  }
179  else
180  {
181  // Revert to old content.
182  d->ip2cUpdater->rollback(filePath);
183 
184  // Must succeed now.
185  d->ip2cParser->readDatabaseThreaded(filePath);
186  }
187  }
188  else
189  {
190  gLog << tr("IP2C parsing finished.");
191  IP2C::instance()->setRangesDatabase(d->ip2cParser->ranges());
192  ip2cJobsFinished();
193  }
194 }
195 
196 void IP2CLoader::ip2cJobsFinished()
197 {
198  if (!d->ip2cUpdater->isWorking() && !d->ip2cParser->isParsing() && !d->updateInProgress)
199  {
200  IP2C::instance()->setDataAccessLockEnabled(false);
201  emit finished();
202  }
203 }
204 
205 void IP2CLoader::ip2cParseDatabase()
206 {
207  QString filePath = DoomseekerFilePaths::ip2cDatabaseAny();
208  if (!filePath.isEmpty())
209  {
210  gLog << tr("Please wait. IP2C database is being read. This may take some time.");
211  // Attempt to read IP2C database.
212 
213  d->inFallbackMode = false;
214  IP2C::instance()->setDataAccessLockEnabled(true);
215  d->ip2cParser->readDatabaseThreaded(filePath);
216  }
217  else
218  {
219  if (!gConfig.doomseeker.bIP2CountryAutoUpdate)
220  {
221  gLog << tr("Did not find any IP2C database. IP2C functionality will be disabled.");
222  gLog << tr("You may install an IP2C database from the \"File\" menu.");
223  }
224  ip2cJobsFinished();
225  }
226 }