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