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