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