23 #include "ip2cupdater.h" 
   25 #include <QCryptographicHash> 
   27 #include <QNetworkAccessManager> 
   28 #include <QTemporaryFile> 
   34 IP2CUpdater::IP2CUpdater(QObject *parent)
 
   37         pCurrentNetworkReply = 
nullptr;
 
   38         pNetworkAccessManager = 
new QNetworkAccessManager();
 
   41 IP2CUpdater::~IP2CUpdater()
 
   45         if (pNetworkAccessManager != 
nullptr)
 
   46                 pNetworkAccessManager->deleteLater();
 
   49 void IP2CUpdater::abort()
 
   51         if (pCurrentNetworkReply != 
nullptr)
 
   53                 pCurrentNetworkReply->disconnect();
 
   54                 pCurrentNetworkReply->abort();
 
   55                 pCurrentNetworkReply->deleteLater();
 
   57         pCurrentNetworkReply = 
nullptr;
 
   60 void IP2CUpdater::checksumDownloadFinished()
 
   62         if (handleRedirect(*pCurrentNetworkReply, SLOT(checksumDownloadFinished())))
 
   65         if (pCurrentNetworkReply->error() != QNetworkReply::NoError)
 
   67                 gLog << tr(
"IP2C checksum check network error: %1").arg(pCurrentNetworkReply->errorString());
 
   73         QByteArray remoteMd5 = pCurrentNetworkReply->readAll();
 
   74         remoteMd5 = remoteMd5.trimmed();
 
   76         pCurrentNetworkReply->deleteLater();
 
   77         pCurrentNetworkReply = 
nullptr;
 
   80         QFile file(this->lastAsyncCallPath);
 
   81         if (file.open(QIODevice::ReadOnly))
 
   83                 localMd5 = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5);
 
   84                 localMd5 = localMd5.toHex().toLower();
 
   87         gLog << tr(
"Comparing IP2C hashes: local = %1, remote = %2").arg(
 
   88                         QString(localMd5)).arg(QString(remoteMd5));
 
   89         bool needed = localMd5 != remoteMd5;
 
   91                 gLog << tr(
"IP2C update needed.");
 
   95 const QUrl IP2CUpdater::dbChecksumUrl()
 
   97         return QUrl(
"https://doomseeker.drdteam.org/ip2c/md5");
 
  100 const QUrl IP2CUpdater::dbDownloadUrl()
 
  102         return QUrl(
"https://doomseeker.drdteam.org/ip2c/geolite2.gz");
 
  105 void IP2CUpdater::downloadDatabase(
const QString &savePath)
 
  107         this->lastAsyncCallPath = savePath;
 
  108         get(dbDownloadUrl(), SLOT(downloadFinished()));
 
  111 void IP2CUpdater::downloadFinished()
 
  113         if (handleRedirect(*pCurrentNetworkReply, SLOT(downloadFinished())))
 
  116         QByteArray data = pCurrentNetworkReply->readAll();
 
  118         pCurrentNetworkReply->deleteLater();
 
  119         pCurrentNetworkReply = 
nullptr;
 
  122         QTemporaryFile tmpFile;
 
  127                 QString tmpFilePath = tmpFile.fileName();
 
  129                 QByteArray uncompressedData;
 
  130                 gzFile gz = gzopen(tmpFilePath.toUtf8().constData(), 
"rb");
 
  133                         static const int CHUNK_SIZE = 128 * 1024;
 
  134                         char chunk[CHUNK_SIZE];
 
  136                         while ((bytesRead = gzread(gz, chunk, CHUNK_SIZE)) != 0)
 
  138                                 uncompressedData.append(QByteArray(chunk, bytesRead));
 
  142                         retrievedData = uncompressedData;
 
  149 bool IP2CUpdater::handleRedirect(QNetworkReply &reply, 
const char *finishedSlot)
 
  151         QUrl possibleRedirectUrl = reply.attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
 
  152         QUrl url = reply.request().url();
 
  153         if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != url)
 
  156                 if (possibleRedirectUrl.isRelative())
 
  157                         possibleRedirectUrl = url.resolved(possibleRedirectUrl);
 
  158                 get(possibleRedirectUrl, finishedSlot);
 
  164 void IP2CUpdater::get(
const QUrl &url, 
const char *finishedSlot)
 
  167         retrievedData.clear();
 
  169         QNetworkRequest request;
 
  173         pCurrentNetworkReply = pNetworkAccessManager->get(request);
 
  174         this->connect(pCurrentNetworkReply, SIGNAL(downloadProgress(qint64,qint64)),
 
  175                 SIGNAL(downloadProgress(qint64,qint64)));
 
  176         this->connect(pCurrentNetworkReply, SIGNAL(finished()), finishedSlot);
 
  181         rollbackData.clear();
 
  182         QFile file(databasePath);
 
  183         if (file.open(QIODevice::ReadOnly))
 
  185                 rollbackData = file.readAll();
 
  192 bool IP2CUpdater::isWorking()
 const 
  194         return pCurrentNetworkReply != 
nullptr;
 
  199         QFile file(filePath);
 
  206         this->lastAsyncCallPath = filePath;
 
  207         gLog << tr(
"Checking if IP2C database at '%1' needs updating.").arg(filePath);
 
  208         get(dbChecksumUrl(), SLOT(checksumDownloadFinished()));
 
  213         bool bSuccess = save(rollbackData, savePath);
 
  214         rollbackData.clear();
 
  219 bool IP2CUpdater::save(
const QByteArray &saveWhat, 
const QString &savePath)
 
  221         if (saveWhat.isEmpty())
 
  224         QFile file(savePath);
 
  225         if (file.open(QIODevice::WriteOnly))
 
  227                 file.write(saveWhat);
 
  236         return save(retrievedData, savePath);