2 * This file is part of Jenirok.
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.
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.
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/>.
19 #include <QtCore/QDebug>
24 static const QString SITE_URLS[Eniro::SITE_COUNT] =
26 "http://wap.eniro.fi/",
27 "http://wap.eniro.se/",
28 "http://wap.eniro.dk/"
31 static const QString SITE_NAMES[Eniro::SITE_COUNT] =
38 static const QString SITE_IDS[Eniro::SITE_COUNT] =
45 static const QString INVALID_LOGIN_STRING = "Invalid login details";
46 static const QString TIMEOUT_STRING = "Request timed out";
47 static const QString PERSON_REGEXP = "<td class=\"hTd2\">(.*)<b>(.*)</td>";
48 static const QString YELLOW_REGEXP = "<td class=\"hTd2\">(.*)<span class=\"gray\"\\}>(.*)</td>";
49 static const QString NUMBER_REGEXP = "<div class=\"callRow\">(.*)</div>";
50 static const QString LOGIN_CHECK = "<input class=\"inpTxt\" id=\"loginformUsername\"";
53 Eniro::Eniro(QObject *parent): Source(parent), site_(Eniro::FI),
54 loggedIn_(false), username_(""), password_(""),
55 timerId_(0), pendingSearches_(), pendingNumberRequests_()
67 for(searchMap::iterator sit = pendingSearches_.begin();
68 sit != pendingSearches_.end(); sit++)
77 pendingSearches_.clear();
79 for(numberMap::iterator nit = pendingNumberRequests_.begin();
80 nit != pendingNumberRequests_.end(); nit++)
89 pendingNumberRequests_.clear();
90 pendingLoginRequests_.clear();
93 void Eniro::setSite(Eniro::Site site)
98 void Eniro::timerEvent(QTimerEvent* t)
102 int currentId = http_.currentId();
106 searchMap::const_iterator it = pendingSearches_.find(currentId);
108 if(it != pendingSearches_.end())
110 QVector <Eniro::Result> results = it.value()->results;
111 SearchDetails details = it.value()->details;
115 setError(TIMEOUT, TIMEOUT_STRING);
117 emit requestFinished(results, details, true);
122 void Eniro::login(QString const& username,
123 QString const& password)
125 username_ = username;
126 password_ = password;
137 void Eniro::search(SearchDetails const& details)
141 SearchType type = details.type;
143 // Only logged in users can use other than person search
149 QUrl url = createUrl(details.query, details.location);
174 url.addQueryItem("what", what);
176 http_.setHost(url.host(), url.port(80));
177 int id = http_.get(url.encodedPath() + '?' + url.encodedQuery());
179 QVector <Source::Result> results;
181 // Store search data for later identification
182 SearchData* newData = new SearchData;
183 newData->details = details;
184 newData->results = results;
185 newData->foundNumbers = 0;
186 newData->numbersTotal = 0;
188 // Store request id so that it can be identified later
189 pendingSearches_[id] = newData;
193 void Eniro::handleHttpData(int id, QByteArray const& data)
195 searchMap::const_iterator searchIt;
196 numberMap::const_iterator numberIt;
198 // Check if request is pending search request
199 if((searchIt = pendingSearches_.find(id)) !=
200 pendingSearches_.end())
202 // Load results from html data
203 loadResults(id, data);
206 // Check if request is pending number requests
207 else if((numberIt = pendingNumberRequests_.find(id)) !=
208 pendingNumberRequests_.end())
210 // Load number from html data
211 loadNumber(id, data);
214 // Check for login request
215 else if(pendingLoginRequests_.find(id) !=
216 pendingLoginRequests_.end())
220 // If html source contains LOGIN_CHECK, login failed
221 if(data.indexOf(LOGIN_CHECK) != -1)
226 emit loginStatus(success);
231 void Eniro::handleHttpError(int id)
233 searchMap::const_iterator searchIt;
234 numberMap::const_iterator numberIt;
236 // Check if request is pending search request
237 if((searchIt = pendingSearches_.find(id)) !=
238 pendingSearches_.end())
240 setError(CONNECTION_FAILURE, http_.errorString());
241 emitRequestFinished(id, searchIt.value(), true);
244 // Check if request is pending number requests
245 else if((numberIt = pendingNumberRequests_.find(id)) !=
246 pendingNumberRequests_.end())
248 setError(CONNECTION_FAILURE, http_.errorString());
249 delete pendingNumberRequests_[id];
250 pendingNumberRequests_.remove(id);
253 // Check for login request
254 else if(pendingLoginRequests_.find(id) !=
255 pendingLoginRequests_.end())
257 emit loginStatus(false);
262 // Loads results from html source code
263 void Eniro::loadResults(int id, QString const& httpData)
265 searchMap::iterator it = pendingSearches_.find(id);
268 switch(it.value()->details.type)
271 expr = YELLOW_REGEXP;
274 expr = PERSON_REGEXP;
283 bool requestsPending = false;
288 while((pos = rx.indexIn(httpData, pos)) != -1)
290 pos += rx.matchedLength();
293 data = stripTags(data);
294 QStringList rows = data.split('\n');
296 for(int i = 0; i < rows.size(); i++)
298 // Remove white spaces
299 QString trimmed = rows.at(i).trimmed().toLower();
301 // Remove empty strings
302 if(trimmed.isEmpty())
309 // Convert words to uppercase
310 rows[i] = ucFirst(trimmed);
316 int size = rows.size();
321 result.name = rows[0];
325 result.name = rows[0];
326 result.city = rows[1];
330 result.name = rows[0];
331 result.street = rows[1];
332 result.city = rows[2];
336 result.name = rows[0];
337 // Remove slashes and spaces from number
338 result.number = cleanUpNumber(rows[1]);
339 result.street = rows[2];
340 result.city = rows[3];
348 it.value()->results.push_back(result);
350 unsigned int foundResults = ++(it.value()->numbersTotal);
352 // If phone number searh is enabled, we have to make another
353 // request to find it out
354 if(getFindNumber() && size < 4 && loggedIn_ &&
355 it.value()->details.type != YELLOW_PAGES)
357 requestsPending = true;
358 getNumberForResult(id, it.value()->results.size() - 1, it.value()->details);
360 // Otherwise result is ready
363 emit resultAvailable(result, it.value()->details);
366 unsigned int maxResults = getMaxResults();
368 // Stop searching if max results is reached
369 if(maxResults && (foundResults >= maxResults))
375 // If there were no results or no phone numbers needed to
376 // be fetched, the whole request is ready
377 if(it.value()->numbersTotal == 0 || !requestsPending)
381 if(httpData.indexOf(LOGIN_CHECK) != -1)
383 setError(INVALID_LOGIN, INVALID_LOGIN_STRING),
387 emitRequestFinished(it.key(), it.value(), error);
391 // Loads phone number from html source
392 void Eniro::loadNumber(int id, QString const& result)
394 numberMap::iterator numberIt = pendingNumberRequests_.find(id);
396 // Make sure that id exists in pending number requests
397 if(numberIt == pendingNumberRequests_.end() || numberIt.value() == 0)
402 searchMap::iterator searchIt = pendingSearches_.find(numberIt.value()->searchId);
404 if(searchIt == pendingSearches_.end() || searchIt.value() == 0)
409 QRegExp rx(NUMBER_REGEXP);
415 if((pos = rx.indexIn(result, pos)) != -1)
417 QString data = rx.cap(1);
418 data = stripTags(data);
420 QString trimmed = data.trimmed();
422 if(!trimmed.isEmpty())
424 // Remove whitespaces from number
425 searchIt.value()->results[numberIt.value()->index].number = cleanUpNumber(trimmed);
427 emit resultAvailable(searchIt.value()->results[numberIt.value()->index], searchIt.value()->details);
429 unsigned int found = ++searchIt.value()->foundNumbers;
431 // Check if all numbers have been found
432 if(found >= searchIt.value()->numbersTotal)
434 emitRequestFinished(searchIt.key(), searchIt.value(), false);
437 // If number was found, there was no error
444 setError(INVALID_LOGIN, INVALID_LOGIN_STRING);
445 emitRequestFinished(searchIt.key(), searchIt.value(), true);
448 // Remove number request
449 int key = numberIt.key();
451 delete pendingNumberRequests_[key];
452 pendingNumberRequests_[key] = 0;
453 pendingNumberRequests_.remove(key);
457 QUrl Eniro::createUrl(QString const& query, QString const& location)
459 QUrl url(SITE_URLS[site_] + "query");
463 url.addQueryItem("search_word", query);
466 if(!location.isEmpty())
468 url.addQueryItem("geo_area", location);
471 unsigned int maxResults = getMaxResults();
475 url.addQueryItem("hpp", QString::number(maxResults));
479 url.addQueryItem("login_name", username_);
480 url.addQueryItem("login_password", password_);
488 // Creates a new request for phone number retrieval
489 void Eniro::getNumberForResult(int id, int index, SearchDetails const& details)
491 QUrl url = createUrl(details.query, details.location);
492 url.addQueryItem("what", "mobwpinfo");
493 url.addQueryItem("search_number", QString::number(index + 1));
495 http_.setHost(url.host(), url.port(80));
496 int requestId = http_.get(url.encodedPath() + '?' + url.encodedQuery());
497 NumberData* number = new NumberData;
498 number->searchId = id;
499 number->index = index;
500 pendingNumberRequests_[requestId] = number;
504 void Eniro::emitRequestFinished(int key, SearchData* data, bool error)
506 emit requestFinished(data->results, data->details, error);
507 delete pendingSearches_[key];
508 pendingSearches_[key] = 0;
509 pendingSearches_.remove(key);
513 QMap <Eniro::Site, Eniro::SiteDetails> Eniro::getSites()
515 QMap <Site, SiteDetails> sites;
518 for(int i = 0; i < SITE_COUNT; i++)
521 details.name = SITE_NAMES[i];
522 details.id = SITE_IDS[i];
523 sites[static_cast<Site>(i)] = details;
529 Eniro::Site Eniro::stringToSite(QString const& str)
532 QString lower = str.toLower();
534 for(int i = 0; i < SITE_COUNT; i++)
536 if(lower == SITE_NAMES[i] || lower == SITE_IDS[i])
538 site = static_cast <Site> (i);