Changed search to retry automatically couple of times before failing.
[jenirok] / src / common / source.cpp
1 /*
2  * This file is part of Jenirok.
3  *
4  * Jenirok is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Jenirok is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Jenirok.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18
19 #include <QtCore/QDebug>
20 #include <QtNetwork/QHttpResponseHeader>
21 #include "source.h"
22 #include "eniro.h"
23 #include "mobil1881.h"
24 #include "dastelefonbuch.h"
25
26 namespace
27 {
28     static const QString SOURCE_NAMES[Source::SOURCE_COUNT] =
29     {
30          "Eniro (FI, SE, DK)",
31          "1881 Mobil (NO)",
32          "Das Telefonbuch (DE)"
33     };
34
35     static const QString SOURCE_IDS[Source::SOURCE_COUNT] =
36     {
37          "eniro",
38          "1881mobil",
39          "dastelefonbuch"
40     };
41
42 }
43
44 // Regexp used to remove everything except numbers from string
45 QRegExp Source::numberCleaner_ = QRegExp("([^0-9]+)");
46
47 // Removes html tags from string
48 QRegExp Source::tagStripper_ = QRegExp("<([^>]+)>");
49
50 Source* Source::getSource(Source::SourceId id, QObject* parent)
51 {
52     switch(id)
53     {
54     case ENIRO:
55         return new Eniro(parent);
56         break;
57     case MOBIL1881:
58         return new Mobil1881(parent);
59         break;
60     case DASTELEFONBUCH:
61         return new DasTelefonbuch(parent);
62         break;
63     default:
64         qDebug() << "Unknown source:" << id;
65     }
66
67     return 0;
68 }
69
70 Source::SourceId Source::stringToId(QString const& str)
71 {
72     for(int i = 0; i < SOURCE_COUNT; i++)
73     {
74         if(SOURCE_IDS[i] == str || SOURCE_NAMES[i] == str)
75         {
76             return static_cast<SourceId>(i);
77         }
78     }
79
80     return ENIRO;
81 }
82
83 void Source::getSources(QList<SourceDetails>& list)
84 {
85     for(int i = 0; i < SOURCE_COUNT; i++)
86     {
87         SourceDetails details;
88         details.type = static_cast<SourceId>(i);
89         details.name = SOURCE_NAMES[i];
90         details.id = SOURCE_IDS[i];
91         list.push_back(details);
92     }
93 }
94
95 Source::Source(QObject* parent): QObject(parent),
96 maxResults_(DEFAULT_MAX_RESULTS), timeout_(0), timerId_(0), findNumber_(true),
97 error_(NO_ERROR), loggedIn_(false)
98 {
99     connect(&http_, SIGNAL(requestFinished(int, bool)), this, SLOT(httpReady(int, bool)));
100 }
101
102 Source::~Source()
103 {
104     abort();
105 }
106
107 void Source::abort()
108 {
109     http_.abort();
110 }
111
112 void Source::setMaxResults(unsigned int results)
113 {
114     maxResults_ = results;
115 }
116
117 unsigned int Source::getMaxResults() const
118 {
119     return maxResults_;
120 }
121
122 void Source::getSearchTypes(QList<SearchType>& types) const
123 {
124     types.clear();
125     types.push_back(PERSONS);
126     types.push_back(YELLOW_PAGES);
127 }
128
129 void Source::setTimeout(unsigned int timeout)
130 {
131     timeout_ = timeout;
132     resetTimeout();
133 }
134
135 unsigned int Source::getTimeout() const
136 {
137     return timeout_;
138 }
139
140 void Source::resetTimeout()
141 {
142     if(timerId_)
143     {
144         killTimer(timerId_);
145     }
146     if(timeout_)
147     {
148         timerId_ = startTimer(timeout_);
149     }
150 }
151
152 void Source::timerEvent(QTimerEvent* t)
153 {
154     Q_UNUSED(t);
155 }
156
157 void Source::setFindNumber(bool value)
158 {
159     findNumber_ = value;
160 }
161
162 bool Source::getFindNumber() const
163 {
164     return findNumber_;
165 }
166
167 Source::Error Source::error() const
168 {
169     return error_;
170 }
171
172 const QString& Source::errorString() const
173 {
174     return errorString_;
175 }
176
177 void Source::setError(Source::Error error, QString const& errorString)
178 {
179     error_ = error;
180     errorString_ = errorString;
181 }
182
183 void Source::httpReady(int id, bool error)
184 {
185     QByteArray result = http_.readAll();
186
187     if(error)
188     {
189         if(http_.error() == QHttp::Aborted)
190         {
191             return;
192         }
193
194         qDebug() << "Error: " << http_.errorString();
195         handleHttpError(id);
196     }
197     else
198     {
199         handleHttpData(id, result);
200     }
201 }
202
203 QString Source::ucFirst(QString& str)
204 {
205     if (str.size() < 1) {
206         return "";
207     }
208
209     QStringList tokens = str.split(" ");
210     QList<QString>::iterator tokItr;
211
212     for (tokItr = tokens.begin(); tokItr != tokens.end(); ++tokItr)
213     {
214         (*tokItr) = (*tokItr).at(0).toUpper() + (*tokItr).mid(1);
215     }
216
217     return tokens.join(" ");
218 }
219
220 QString& Source::cleanUpNumber(QString& number)
221 {
222     return number.replace(numberCleaner_, "");
223 }
224
225 QString& Source::stripTags(QString& string)
226 {
227     return string.replace(tagStripper_, "");
228 }
229
230 QString& Source::htmlEntityDecode(QString& string)
231 {
232     static const QString entities[] =
233     {
234         "quot",
235         "apos",
236         "amp",
237         "lt",
238         "gt",
239         "nbsp",
240         "Agrave",
241         "Aacute",
242         "Acirc",
243         "Atilde",
244         "Auml",
245         "Aring",
246         "AElig",
247         "Ccedil",
248         "Egrave",
249         "Eacute",
250         "Ecirc",
251         "Euml",
252         "Igrave",
253         "Iacute",
254         "Icirc",
255         "Iuml",
256         "ETH",
257         "Ntilde",
258         "Ograve",
259         "Oacute",
260         "Ocirc",
261         "Otilde",
262         "Ouml",
263         "Oslash",
264         "Ugrave",
265         "Uacute",
266         "Ucirc",
267         "Uuml",
268         "Yacute",
269         "THORN",
270         "szlig",
271         "agrave",
272         "aacute",
273         "acirc",
274         "atilde",
275         "auml",
276         "aring",
277         "aelig",
278         "ccedil",
279         "egrave",
280         "eacute",
281         "ecirc",
282         "euml",
283         "igrave",
284         "iacute",
285         "icirc",
286         "iuml",
287         "eth",
288         "ntilde",
289         "ograve",
290         "oacute",
291         "ocirc",
292         "otilde",
293         "ouml",
294         "oslash",
295         "ugrave",
296         "uacute",
297         "ucirc",
298         "uuml",
299         "yacute",
300         "thorn",
301         "yuml"
302     };
303
304     static const int entityValues[] =
305     {
306         34,
307         39,
308         38,
309         60,
310         62,
311         32,
312         192,
313         193,
314         194,
315         195,
316         196,
317         197,
318         198,
319         199,
320         200,
321         201,
322         202,
323         203,
324         204,
325         205,
326         206,
327         207,
328         208,
329         209,
330         210,
331         211,
332         212,
333         213,
334         214,
335         216,
336         217,
337         218,
338         219,
339         220,
340         221,
341         222,
342         223,
343         224,
344         225,
345         226,
346         227,
347         228,
348         229,
349         230,
350         231,
351         232,
352         233,
353         234,
354         235,
355         236,
356         237,
357         238,
358         239,
359         240,
360         241,
361         242,
362         243,
363         244,
364         245,
365         246,
366         248,
367         249,
368         250,
369         251,
370         252,
371         253,
372         254,
373         255
374     };
375
376     static int const COUNT = sizeof(entityValues) / sizeof(entityValues[0]);
377
378     for(int i = 0; i < COUNT; i++)
379     {
380         string = string.replace("&" + entities[i] + ";", QChar(entityValues[i]));
381     }
382
383     static QRegExp entityCleaner("&#([0-9]{1,3});");
384     entityCleaner.setMinimal(true);
385
386     int pos = 0;
387
388     while((pos = entityCleaner.indexIn(string, pos)) != -1)
389     {
390         QString match = entityCleaner.cap(1);
391
392         int value = match.toInt();
393
394         if(value >= 1 && value <= 255)
395         {
396             string = string.replace(pos, match.length() + 3, QChar(value));
397         }
398
399         pos += entityCleaner.matchedLength();
400     }
401
402
403    return string;
404 }
405
406 void Source::fixUrl(QUrl& url)
407 {
408     QByteArray path = url.encodedQuery().replace('+', "%2B");
409     url.setEncodedQuery(path);
410 }
411
412 bool Source::isPhoneNumber(QString const& string)
413 {
414     static QRegExp check("^([0-9 -]{7,25})$");
415     return check.exactMatch(string);
416 }
417
418 Source::SearchDetails::SearchDetails(QString const& q,
419                                      QString const& loc,
420                                      SearchType t)
421 {
422     query = q;
423     location = loc;
424     type = t;
425 }