ip2cparser.cpp
1 //------------------------------------------------------------------------------
2 // ip2cparser.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) 2009 Braden "Blzut3" Obrzut <admin@maniacsvault.net>
22 //------------------------------------------------------------------------------
23 #include "ip2cparser.h"
24 
25 #include "datastreamoperatorwrapper.h"
26 #include "global.h"
27 #include "log.h"
28 #include <QBuffer>
29 #include <QElapsedTimer>
30 #include <QFile>
31 #include <QMap>
32 #include <QMutexLocker>
33 
34 IP2CParser::IP2CParser()
35 {
36  currentParsingThread = nullptr;
37  bIsParsing = false;
38 }
39 
40 bool IP2CParser::doReadDatabase(const QString &filePath)
41 {
42  QMutexLocker mutexLocker(&thisLock);
43 
44  // This will set proper state whenever and wherever this method finishes.
45  ConstructorDestructorParserStateSetter stateSetter(this);
46 
47  QFile dataBase(filePath);
48  gLog << tr("Parsing IP2C database: %1").arg(dataBase.fileName());
49 
50  if (!dataBase.exists()
51  || !dataBase.open(QIODevice::ReadOnly)
52  || !dataBase.isReadable())
53  {
54  gLog << tr("Unable to open IP2C file.");
55  return false;
56  }
57 
58  QElapsedTimer time;
59  time.start();
60 
61  QByteArray dataArray = dataBase.readAll();
62 
63  // Read version.
64  int pos = 4;
65  if (pos >= dataArray.size())
66  return false;
67 
68  const char *data = dataArray.constData();
69  unsigned short version = READINT16(&data[pos]);
70 
71  bool wasReadSuccessful = false;
72  switch (version)
73  {
74  case 2:
75  wasReadSuccessful = readDatabaseVersion2(dataArray);
76  break;
77 
78  default:
79  wasReadSuccessful = false;
80  break;
81  }
82 
83  if (!wasReadSuccessful)
84  return false;
85 
86  gLog << tr("IP2C database read in %1 ms; IP ranges: %2")
87  .arg(time.elapsed()).arg(ranges_.size());
88  return true;
89 }
90 
91 void IP2CParser::parsingThreadFinished()
92 {
93  bool bSuccessState = currentParsingThread->bSuccessState;
94  gLog << tr("IP2C parsing thread has finished.");
95 
96  delete currentParsingThread;
97  currentParsingThread = nullptr;
98 
99  emit parsingFinished(bSuccessState);
100 }
101 
102 bool IP2CParser::readDatabase(const QString &filePath)
103 {
104  bool bSuccess = doReadDatabase(filePath);
105  emit parsingFinished(bSuccess);
106 
107  return bSuccess;
108 }
109 
110 void IP2CParser::readDatabaseThreaded(const QString &filePath)
111 {
112  if (currentParsingThread != nullptr)
113  return;
114 
115  auto pParsingThread = new ParsingThread(this, filePath);
116  connect(pParsingThread, SIGNAL(finished()), this, SLOT(parsingThreadFinished()));
117 
118  currentParsingThread = pParsingThread;
119 
120  pParsingThread->start();
121 }
122 
123 bool IP2CParser::readDatabaseVersion2(const QByteArray &dataArray)
124 {
125  QBuffer buffer;
126  buffer.setData(dataArray);
127  buffer.open(QIODevice::ReadOnly);
128  QDataStream dstream(&buffer);
129  dstream.setByteOrder(QDataStream::LittleEndian);
130  DataStreamOperatorWrapper stream(&dstream);
131  stream.skipRawData(6); // skip file tag and version number
132 
133  // We need to store the addresses in such hash table to make sure they
134  // are ordered in proper, ascending order. Otherwise the whole library
135  // will not work!
136  QMap<unsigned, IP2CRange> hashTable;
137 
138  while (stream.hasRemaining())
139  {
140  // Discard the first field as it is the country name. As of Doomseeker
141  // 1.3.3, the country names and their alpha-3 codes are hardcoded.
142  QString::fromUtf8(stream.readRawUntilByte('\0'));
143  QString countryCode = QString::fromUtf8(stream.readRawUntilByte('\0'));
144 
145  // Base entry for each IP read from the file
146  IP2CRange baseEntry;
147  baseEntry.country = countryCode;
148  if (!stream.hasRemaining())
149  return false;
150 
151  quint32 numOfIpBlocks = stream.readQUInt32();
152 
153  for (quint32 x = 0; x < numOfIpBlocks; ++x)
154  {
155  // Create new entry from the base.
156  IP2CRange entry = baseEntry;
157 
158  entry.ipStart = stream.readQUInt32();
159  if (!stream.hasRemaining())
160  return false;
161  entry.ipEnd = stream.readQUInt32();
162 
163  hashTable[entry.ipStart] = entry;
164  }
165  }
166 
167  this->ranges_ = hashTable.values();
168 
169  return true;
170 }
171 
173 
174 IP2CParser::ConstructorDestructorParserStateSetter::ConstructorDestructorParserStateSetter(IP2CParser *pParser)
175 {
176  this->pParser = pParser;
177  pParser->bIsParsing = true;
178 }
179 
180 IP2CParser::ConstructorDestructorParserStateSetter::~ConstructorDestructorParserStateSetter()
181 {
182  pParser->bIsParsing = false;
183 }
184 
186 
187 void IP2CParser::ParsingThread::run()
188 {
189  bSuccessState = pParser->doReadDatabase(filePath);
190 }