Html entity handling improved. Fixed a bug in source that caused segmentation fault...
[jenirok] / src / common / mobil1881.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 "mobil1881.h"
21
22
23 Mobil1881::Mobil1881(QObject* parent): Source(parent)
24 {
25 }
26
27 Mobil1881::~Mobil1881()
28 {
29     abort();
30 }
31
32 void Mobil1881::abort()
33 {
34     Source::abort();
35
36     for(int i = 0; i < pendingSearches_.size(); i++)
37     {
38         delete pendingSearches_[i];
39         pendingSearches_[i] = 0;
40     }
41
42     pendingSearches_.clear();
43
44 }
45
46 void Mobil1881::search(Source::SearchDetails const& details)
47 {
48     resetTimeout();
49
50     int id = sendQuery(details, 1);
51
52     SearchData* newData = new SearchData;
53     newData->details = details;
54     newData->searchIds.insert(id);
55     newData->currentPage = 1;
56
57     pendingSearches_.push_back(newData);
58 }
59
60 void Mobil1881::handleHttpData(int id, QByteArray const& data)
61 {
62     QString decoded = QString::fromUtf8(data.data());
63
64     for(int i = 0; i < pendingSearches_.size(); i++)
65     {
66         if(pendingSearches_.at(i) && pendingSearches_.at(i)->searchIds.find(id) !=
67             pendingSearches_.at(i)->searchIds.end())
68         {
69             addNumbers(pendingSearches_.at(i), decoded, i);
70             break;
71         }
72     }
73 }
74
75 void Mobil1881::handleHttpError(int id)
76 {
77     for(int i = 0; i < pendingSearches_.size(); i++)
78     {
79         if(pendingSearches_.at(i) && pendingSearches_.at(i)->searchIds.find(id) !=
80             pendingSearches_.at(i)->searchIds.end())
81         {
82
83             setError(Source::CONNECTION_FAILURE, http_.errorString());
84             emitRequestFinished(pendingSearches_.at(i), true, i);
85             break;
86         }
87     }
88 }
89
90 void Mobil1881::addNumbers(SearchData* searchData,
91                            QString const& data,
92                            int index)
93 {
94     if(data.indexOf("<b>Last ned vCard</b>") > 0)
95     {
96         addOnlyNumber(searchData, data, index);
97         return;
98     }
99
100     int pos = 0;
101     QRegExp rx("<td valign=\"top\" width=\"99%\">(.*)</td>");
102     QRegExp name("<div class=\"srln\">(.*)</div>");
103     QRegExp address("<div class=\"srla\">(.*),<br/>(.*)</div>");
104     QRegExp number("<div class=\"srlp\">(.*)</div>");
105     rx.setMinimal(true);
106     name.setMinimal(true);
107     address.setMinimal(true);
108     number.setMinimal(true);
109
110     int maxResults = getMaxResults();
111
112     while((pos = rx.indexIn(data, pos)) != -1)
113     {
114         pos += rx.matchedLength();
115
116         if(searchData->results.size() >= maxResults)
117         {
118             break;
119         }
120
121         QString part = rx.cap(1);
122         Source::Result result;
123         QString nameStr;
124         QString numberStr;
125         QString streetStr;
126         QString cityStr;
127
128         if(name.indexIn(part) != -1)
129         {
130             nameStr = name.cap(1);
131         }
132
133         if(address.indexIn(part) != -1)
134         {
135             streetStr = address.cap(1);
136             cityStr = address.cap(2);
137         }
138
139         if(number.indexIn(part) != -1)
140         {
141             numberStr = number.cap(1);
142         }
143
144         if(formatResult(nameStr, numberStr, streetStr,
145                         cityStr, result))
146         {
147             emit resultAvailable(result, searchData->details);
148             searchData->results.push_back(result);
149         }
150
151     }
152
153     if(searchData->results.size() >= maxResults)
154     {
155         emitRequestFinished(searchData, false, index);
156     }
157     else
158     {
159
160         if(data.indexOf("Neste") > 0)
161         {
162             searchData->currentPage++;
163             int id = sendQuery(searchData->details, searchData->currentPage);
164             searchData->searchIds.insert(id);
165         }
166         else
167         {
168             emitRequestFinished(searchData, false, index);
169         }
170     }
171
172 }
173
174 void Mobil1881::addOnlyNumber(SearchData* searchData,
175                               QString const& data,
176                               int index)
177 {
178     QRegExp name("<div class=\"srsln\">(.*)</div>");
179     QRegExp number("class=\"srlttxt\"><b>(.*)</b>");
180     QRegExp address("class=\"srlttxt\"><span>(.*),<br/>(.*)</span>");
181     name.setMinimal(true);
182     number.setMinimal(true);
183     address.setMinimal(true);
184
185     Source::Result result;
186
187     QString nameStr;
188     QString numberStr;
189     QString streetStr;
190     QString cityStr;
191
192     if(name.indexIn(data) != -1)
193     {
194         nameStr = name.cap(1);
195     }
196
197     if(number.indexIn(data) != -1)
198     {
199         numberStr = number.cap(1);
200     }
201
202     if(address.indexIn(data) != -1)
203     {
204         streetStr = address.cap(1);
205         cityStr = address.cap(2);
206     }
207
208     if(formatResult(nameStr, numberStr, streetStr,
209                     cityStr, result))
210     {
211         searchData->results.push_back(result);
212         emit resultAvailable(result, searchData->details);
213     }
214
215     emitRequestFinished(searchData, false, index);
216 }
217
218 bool Mobil1881::formatResult(QString& name, QString& number,
219                              QString& street, QString& city,
220                              Source::Result& result)
221 {
222     name = stripTags(name);
223     name = htmlEntityDecode(name);
224     result.name = name.trimmed();
225     number = stripTags(number);
226     number = cleanUpNumber(number);
227     result.number = number.trimmed();
228     street = stripTags(street);
229     street = htmlEntityDecode(street);
230     city = stripTags(city);
231     city = htmlEntityDecode(city);
232     result.street = street.trimmed();
233     result.city = city.trimmed();
234
235     if(!result.name.isEmpty() && (!getFindNumber() || !result.number.isEmpty()))
236     {
237         return true;
238     }
239
240     return false;
241 }
242
243 void Mobil1881::emitRequestFinished(SearchData* data,
244                                     bool error, int index)
245 {
246     QVector<Source::Result> results = data->results;
247     Source::SearchDetails details = data->details;
248
249     emit requestFinished(results, details, error);
250     delete pendingSearches_[index];
251     pendingSearches_[index] = 0;
252     pendingSearches_.removeAt(index);
253 }
254
255 int Mobil1881::sendQuery(Source::SearchDetails const& details,
256                          int page)
257 {
258     QUrl url("http://wap.1881.no/");
259     url.addQueryItem("i", "4854");
260     url.addQueryItem("showonly", "1");
261     QString query = details.query;
262
263     if(!details.location.isEmpty())
264     {
265         query += " " + details.location;
266     }
267
268     url.addQueryItem("s", query);
269     if(details.type == Source::YELLOW_PAGES)
270     {
271         url.addQueryItem("t", "c");
272     }
273     else
274     {
275         url.addQueryItem("t", "p");
276     }
277
278     if(page > 1)
279     {
280         url.addQueryItem("p", QString::number(page));
281     }
282
283     fixUrl(url);
284
285     http_.setHost(url.host(), url.port(80));
286     return http_.get(url.encodedPath() + '?' + url.encodedQuery());
287 }