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 PERSON_REGEXP = "<td class=\"hTd2\">(.*)<b>(.*)</td>";
47 static const QString YELLOW_REGEXP = "<td class=\"hTd2\">(.*)<span class=\"gray\"\\}>(.*)</td>";
48 static const QString NUMBER_REGEXP = "<div class=\"callRow\">(.*)</div>";
49 static const QString LOGIN_CHECK = "<input class=\"inpTxt\" id=\"loginformUsername\"";
52 // Regexp used to remove numbers from string
53 QRegExp Eniro::numberCleaner_ = QRegExp("([^0-9]+)");
55 // Removes html tags from string
56 QRegExp Eniro::tagStripper_ = QRegExp("<([^>]+)>");
58 Eniro::Eniro(Site site, QObject *parent): QObject(parent), site_(site),
59 username_(""), password_(""), loggedIn_(false), error_(NO_ERROR),
60 errorString_(""), maxResults_(10), findNumber_(true),
61 pendingSearches_(), pendingNumberRequests_()
63 connect(&http_, SIGNAL(requestFinished(int, bool)), this, SLOT(httpReady(int, bool)));
75 for(searchMap::iterator sit = pendingSearches_.begin();
76 sit != pendingSearches_.end(); sit++)
85 pendingSearches_.clear();
87 for(numberMap::iterator nit = pendingNumberRequests_.begin();
88 nit != pendingNumberRequests_.end(); nit++)
97 pendingNumberRequests_.clear();
98 pendingLoginRequests_.clear();
101 void Eniro::setMaxResults(unsigned int value)
106 void Eniro::setFindNumber(bool value)
111 void Eniro::setSite(Eniro::Site site)
116 void Eniro::login(QString const& username,
117 QString const& password)
119 username_ = username;
120 password_ = password;
131 void Eniro::testLogin()
133 QUrl url = createUrl("", "");
135 url.addQueryItem("what", "mobwp");
136 http_.setHost(url.host(), url.port(80));
137 int id = http_.get(url.encodedPath() + '?' + url.encodedQuery());
139 pendingLoginRequests_.insert(id);
142 bool Eniro::search(SearchDetails const& details)
144 SearchType type = details.type;
146 // Only logged in users can use other than person search
152 QUrl url = createUrl(details.query, details.location);
177 url.addQueryItem("what", what);
179 http_.setHost(url.host(), url.port(80));
180 int id = http_.get(url.encodedPath() + '?' + url.encodedQuery());
182 QVector <Result> results;
184 // Store search data for later identification
185 SearchData* newData = new SearchData;
186 newData->details = details;
187 newData->results = results;
188 newData->foundNumbers = 0;
189 newData->numbersTotal = 0;
191 // Store request id so that it can be identified later
192 pendingSearches_[id] = newData;
197 Eniro::Error Eniro::error() const
202 const QString& Eniro::errorString() const
207 void Eniro::httpReady(int id, bool error)
211 qDebug() << "Error: " << http_.errorString();
214 searchMap::const_iterator searchIt;
215 numberMap::const_iterator numberIt;
217 // Check if request is pending search request
218 if((searchIt = pendingSearches_.find(id)) !=
219 pendingSearches_.end())
223 error_ = CONNECTION_FAILURE;
224 errorString_ = http_.errorString();
225 emitRequestFinished(id, searchIt.value(), true);
229 QString result(http_.readAll());
231 // Load results from html data
232 loadResults(id, result);
235 // Check if request is pending number requests
236 else if((numberIt = pendingNumberRequests_.find(id)) !=
237 pendingNumberRequests_.end())
241 error_ = CONNECTION_FAILURE;
242 errorString_ = http_.errorString();
243 delete pendingNumberRequests_[id];
244 pendingNumberRequests_.remove(id);
248 QString result(http_.readAll());
250 // Load number from html data
251 loadNumber(id, result);
254 // Check for login request
255 else if(pendingLoginRequests_.find(id) !=
256 pendingLoginRequests_.end())
262 QString result(http_.readAll());
264 // If html source contains LOGIN_CHECK, login failed
265 if(result.indexOf(LOGIN_CHECK) != -1)
275 emit loginStatus(success);
280 // Loads results from html source code
281 void Eniro::loadResults(int id, QString const& httpData)
283 searchMap::iterator it = pendingSearches_.find(id);
286 switch(it.value()->details.type)
289 expr = YELLOW_REGEXP;
292 expr = PERSON_REGEXP;
301 bool requestsPending = false;
306 while((pos = rx.indexIn(httpData, pos)) != -1)
308 pos += rx.matchedLength();
311 data = stripTags(data);
312 QStringList rows = data.split('\n');
314 for(int i = 0; i < rows.size(); i++)
316 // Remove white spaces
317 QString trimmed = rows.at(i).trimmed().toLower();
319 // Remove empty strings
320 if(trimmed.isEmpty())
327 // Convert words to uppercase
328 rows[i] = ucFirst(trimmed);
334 int size = rows.size();
339 result.name = rows[0];
343 result.name = rows[0];
344 result.city = rows[1];
348 result.name = rows[0];
349 result.street = rows[1];
350 result.city = rows[2];
354 result.name = rows[0];
355 // Remove slashes and spaces from number
356 result.number = cleanUpNumber(rows[1]);
357 result.street = rows[2];
358 result.city = rows[3];
366 it.value()->results.push_back(result);
368 unsigned int foundResults = ++(it.value()->numbersTotal);
370 // If phone number searh is enabled, we have to make another
371 // request to find it out
372 if(findNumber_ && size < 4 && loggedIn_ &&
373 it.value()->details.type != YELLOW_PAGES)
375 requestsPending = true;
376 getNumberForResult(id, it.value()->results.size() - 1, it.value()->details);
378 // Otherwise result is ready
381 emit resultAvailable(result, it.value()->details);
384 // Stop searching if max results is reached
385 if(maxResults_ && (foundResults >= maxResults_))
391 // If number there were no results or no phone numbers needed to
392 // be fetched, the whole request is ready
393 if(it.value()->numbersTotal == 0 || !requestsPending)
397 if(httpData.indexOf(LOGIN_CHECK) != -1)
399 error_ = INVALID_LOGIN;
400 errorString_ = INVALID_LOGIN_STRING;
404 emitRequestFinished(it.key(), it.value(), error);
408 // Loads phone number from html source
409 void Eniro::loadNumber(int id, QString const& result)
411 numberMap::iterator numberIt = pendingNumberRequests_.find(id);
413 // Make sure that id exists in pending number requests
414 if(numberIt == pendingNumberRequests_.end() || numberIt.value() == 0)
419 searchMap::iterator searchIt = pendingSearches_.find(numberIt.value()->searchId);
421 if(searchIt == pendingSearches_.end() || searchIt.value() == 0)
426 QRegExp rx(NUMBER_REGEXP);
432 if((pos = rx.indexIn(result, pos)) != -1)
434 QString data = rx.cap(1);
435 data = stripTags(data);
437 QString trimmed = data.trimmed();
439 if(!trimmed.isEmpty())
441 // Remove whitespaces from number
442 searchIt.value()->results[numberIt.value()->index].number = cleanUpNumber(trimmed);
444 emit resultAvailable(searchIt.value()->results[numberIt.value()->index], searchIt.value()->details);
446 unsigned int found = ++searchIt.value()->foundNumbers;
448 // Check if all numbers have been found
449 if(found >= searchIt.value()->numbersTotal)
451 emitRequestFinished(searchIt.key(), searchIt.value(), false);
454 // If number was found, there was no error
461 error_ = INVALID_LOGIN;
462 errorString_ = INVALID_LOGIN;
463 emitRequestFinished(searchIt.key(), searchIt.value(), true);
466 // Remove number request
467 int key = numberIt.key();
469 delete pendingNumberRequests_[key];
470 pendingNumberRequests_[key] = 0;
471 pendingNumberRequests_.remove(key);
475 QUrl Eniro::createUrl(QString const& query, QString const& location)
477 QUrl url(SITE_URLS[site_] + "query");
481 url.addQueryItem("search_word", query);
484 if(!location.isEmpty())
486 url.addQueryItem("geo_area", location);
491 url.addQueryItem("hpp", QString::number(maxResults_));
495 url.addQueryItem("login_name", username_);
496 url.addQueryItem("login_password", password_);
499 QByteArray path = url.encodedQuery().replace('+', "%2B");
500 url.setEncodedQuery(path);
505 // Creates a new request for phone number retrieval
506 void Eniro::getNumberForResult(int id, int index, SearchDetails const& details)
508 QUrl url = createUrl(details.query, details.location);
509 url.addQueryItem("what", "mobwpinfo");
510 url.addQueryItem("search_number", QString::number(index + 1));
512 http_.setHost(url.host(), url.port(80));
513 int requestId = http_.get(url.encodedPath() + '?' + url.encodedQuery());
514 NumberData* number = new NumberData;
515 number->searchId = id;
516 number->index = index;
517 pendingNumberRequests_[requestId] = number;
521 void Eniro::emitRequestFinished(int key, SearchData* data, bool error)
524 // Do not emit "Request aborted" error
525 if(!(error && (http_.error() == QHttp::Aborted)))
527 emit requestFinished(data->results, data->details, error);
530 delete pendingSearches_[key];
531 pendingSearches_[key] = 0;
532 pendingSearches_.remove(key);
535 QString Eniro::ucFirst(QString& str)
537 if (str.size() < 1) {
541 QStringList tokens = str.split(" ");
542 QList<QString>::iterator tokItr;
544 for (tokItr = tokens.begin(); tokItr != tokens.end(); ++tokItr)
546 (*tokItr) = (*tokItr).at(0).toUpper() + (*tokItr).mid(1);
549 return tokens.join(" ");
552 QString& Eniro::cleanUpNumber(QString& number)
554 return number.replace(numberCleaner_, "");
557 QString& Eniro::stripTags(QString& string)
559 return string.replace(tagStripper_, "");
562 QMap <Eniro::Site, Eniro::SiteDetails> Eniro::getSites()
564 QMap <Site, SiteDetails> sites;
567 for(int i = 0; i < SITE_COUNT; i++)
570 details.name = SITE_NAMES[i];
571 details.id = SITE_IDS[i];
572 sites[static_cast<Site>(i)] = details;
578 Eniro::Site Eniro::stringToSite(QString const& str)
581 QString lower = str.toLower();
583 for(int i = 0; i < SITE_COUNT; i++)
585 if(lower == SITE_NAMES[i] || lower == SITE_IDS[i])
587 site = static_cast <Site> (i);
594 Eniro::SearchDetails::SearchDetails(QString const& q,