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. Entries read: %2").arg(time.elapsed()).arg(parsedValues.size());
87  return true;
88 }
89 
90 void IP2CParser::parsingThreadFinished()
91 {
92  bool bSuccessState = currentParsingThread->bSuccessState;
93  gLog << tr("IP2C parsing thread has finished.");
94 
95  delete currentParsingThread;
96  currentParsingThread = nullptr;
97 
98  emit parsingFinished(bSuccessState);
99 }
100 
101 bool IP2CParser::readDatabase(const QString &filePath)
102 {
103  bool bSuccess = doReadDatabase(filePath);
104  emit parsingFinished(bSuccess);
105 
106  return bSuccess;
107 }
108 
109 void IP2CParser::readDatabaseThreaded(const QString &filePath)
110 {
111  if (currentParsingThread != nullptr)
112  return;
113 
114  auto pParsingThread = new ParsingThread(this, filePath);
115  connect(pParsingThread, SIGNAL(finished()), this, SLOT(parsingThreadFinished()));
116 
117  currentParsingThread = pParsingThread;
118 
119  pParsingThread->start();
120 }
121 
122 bool IP2CParser::readDatabaseVersion2(const QByteArray &dataArray)
123 {
124  QBuffer buffer;
125  buffer.setData(dataArray);
126  buffer.open(QIODevice::ReadOnly);
127  QDataStream dstream(&buffer);
128  dstream.setByteOrder(QDataStream::LittleEndian);
129  DataStreamOperatorWrapper stream(&dstream);
130  stream.skipRawData(6); // skip file tag and version number
131 
132  // We need to store the addresses in such hash table to make sure they
133  // are ordered in proper, ascending order. Otherwise the whole library
134  // will not work!
135  QMap<unsigned, IP2C::IP2CData> hashTable;
136 
137  while (stream.hasRemaining())
138  {
139  // Base entry for each IP read from the file
140  IP2C::IP2CData baseEntry;
141 
142  baseEntry.countryFullName = QString::fromUtf8(stream.readRawUntilByte('\0'));
143  baseEntry.country = QString::fromUtf8(stream.readRawUntilByte('\0'));
144  if (!stream.hasRemaining())
145  return false;
146 
147  quint32 numOfIpBlocks = stream.readQUInt32();
148 
149  for (quint32 x = 0; x < numOfIpBlocks; ++x)
150  {
151  // Create new entry from the base.
152  IP2C::IP2CData entry = baseEntry;
153 
154  entry.ipStart = stream.readQUInt32();
155  if (!stream.hasRemaining())
156  return false;
157  entry.ipEnd = stream.readQUInt32();
158 
159  hashTable[entry.ipStart] = entry;
160  }
161  }
162 
163  this->parsedValues = hashTable.values();
164 
165  return true;
166 }
167 
169 
170 IP2CParser::ConstructorDestructorParserStateSetter::ConstructorDestructorParserStateSetter(IP2CParser *pParser)
171 {
172  this->pParser = pParser;
173  pParser->bIsParsing = true;
174 }
175 
176 IP2CParser::ConstructorDestructorParserStateSetter::~ConstructorDestructorParserStateSetter()
177 {
178  pParser->bIsParsing = false;
179 }
180 
182 
183 void IP2CParser::ParsingThread::run()
184 {
185  bSuccessState = pParser->doReadDatabase(filePath);
186 }