From 79976fbfcee4b6da2bea7d3a09db4dddceb6faaf Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Mon, 18 May 2015 17:02:48 +0300 Subject: [PATCH] Implement new GeoIPManager class. --- configure | 32 - configure.ac | 17 - macxconf.pri | 3 - os2conf.pri | 4 - src/app/application.cpp | 8 + src/core/bittorrent/peerinfo.cpp | 6 +- src/core/bittorrent/peerinfo.h | 2 + src/core/bittorrent/private/sessionprivate.h | 3 - .../bittorrent/private/torrenthandleprivate.h | 3 - src/core/bittorrent/session.cpp | 32 +- src/core/bittorrent/session.h | 7 - src/core/bittorrent/torrenthandle.cpp | 16 - src/core/bittorrent/torrenthandle.h | 4 - src/core/core.pri | 4 + src/core/net/geoipmanager.cpp | 464 +++++++++++++ src/core/net/geoipmanager.h | 77 +++ src/core/net/private/geoipdatabase.cpp | 631 ++++++++++++++++++ .../net/private/geoipdatabase.h} | 48 +- src/gui/geoip/README | 12 - src/gui/geoip/geoip.pri | 19 - src/gui/geoip/geoip.qrc | 5 - src/gui/geoip/geoipmanager.cpp | 203 ------ src/gui/gui.pri | 1 - src/gui/guiiconprovider.cpp | 6 + src/gui/guiiconprovider.h | 1 + src/gui/properties/peerlistwidget.cpp | 10 +- unixconf.pri | 7 - winconf.pri | 4 - 28 files changed, 1228 insertions(+), 401 deletions(-) create mode 100644 src/core/net/geoipmanager.cpp create mode 100644 src/core/net/geoipmanager.h create mode 100644 src/core/net/private/geoipdatabase.cpp rename src/{gui/geoip/geoipmanager.h => core/net/private/geoipdatabase.h} (66%) delete mode 100644 src/gui/geoip/README delete mode 100644 src/gui/geoip/geoip.pri delete mode 100644 src/gui/geoip/geoip.qrc delete mode 100644 src/gui/geoip/geoipmanager.cpp diff --git a/configure b/configure index 25973f792..a84d42eaf 100755 --- a/configure +++ b/configure @@ -716,7 +716,6 @@ enable_dependency_tracking enable_silent_rules with_qt5 with_libtorrent_rasterbar0_16 -with_geoip_database_embedded with_qtsingleapplication with_qjson enable_debug @@ -1387,10 +1386,6 @@ Optional Packages: --with-libtorrent-rasterbar0.16 Compile using libtorrent-rasterbar 0.16.x series (default=no) - --with-geoip-database-embedded - Embed the GeoIP database in the qBittorrent - executable (please follow instructions in - src/gui/geoip/README) (default=no) --with-qtsingleapplication=[system|shipped] Use the shipped qtsingleapplication library or the system one (default=shipped) @@ -4194,15 +4189,6 @@ fi -# Check whether --with-geoip-database-embedded was given. -if test "${with_geoip_database_embedded+set}" = set; then : - withval=$with_geoip_database_embedded; -else - with_geoip_database_embedded=no -fi - - - # Check whether --with-qtsingleapplication was given. if test "${with_qtsingleapplication+set}" = set; then : withval=$with_qtsingleapplication; @@ -4429,7 +4415,6 @@ $as_echo "yes" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } enable_qt_dbus=no - enable_geoip_database=no QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui" ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gui" >&5 @@ -5493,23 +5478,6 @@ $as_echo "$with_libtorrent_rasterbar0_16" >&6; } as_fn_error $? "Unknown option \"$with_libtorrent_rasterbar0_16\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to embed the GeoIP database" >&5 -$as_echo_n "checking whether to embed the GeoIP database... " >&6; } -case "x$with_geoip_database_embedded" in #( - "xno") : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - QBT_REMOVE_DEFINES="$QBT_REMOVE_DEFINES WITH_GEOIP_EMBEDDED" ;; #( - "xyes") : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - QBT_ADD_DEFINES="$QBT_ADD_DEFINES WITH_GEOIP_EMBEDDED" ;; #( - *) : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_geoip_database_embedded" >&5 -$as_echo "$with_geoip_database_embedded" >&6; } - as_fn_error $? "Unknown option \"$with_geoip_database_embedded\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking which qtsingleapplication to use" >&5 $as_echo_n "checking which qtsingleapplication to use... " >&6; } case "x$with_qtsingleapplication" in #( diff --git a/configure.ac b/configure.ac index 56eb79675..e365ecc0c 100644 --- a/configure.ac +++ b/configure.ac @@ -24,12 +24,6 @@ AC_ARG_WITH(libtorrent-rasterbar0.16, [], [with_libtorrent_rasterbar0_16=no]) -AC_ARG_WITH(geoip-database-embedded, - [AS_HELP_STRING([--with-geoip-database-embedded], - [Embed the GeoIP database in the qBittorrent executable (please follow instructions in src/gui/geoip/README) (default=no)])], - [], - [with_geoip_database_embedded=no]) - AC_ARG_WITH(qtsingleapplication, [AS_HELP_STRING([--with-qtsingleapplication=@<:@system|shipped@:>@], [Use the shipped qtsingleapplication library or the system one (default=shipped)])], @@ -198,17 +192,6 @@ AS_CASE(["x$with_libtorrent_rasterbar0_16"], [AC_MSG_RESULT([$with_libtorrent_rasterbar0_16]) AC_MSG_ERROR([Unknown option "$with_libtorrent_rasterbar0_16". Use either "yes" or "no".])]) -AC_MSG_CHECKING([whether to embed the GeoIP database]) -AS_CASE(["x$with_geoip_database_embedded"], - ["xno"], - [AC_MSG_RESULT([no]) - QBT_REMOVE_DEFINES="$QBT_REMOVE_DEFINES WITH_GEOIP_EMBEDDED"], - ["xyes"], - [AC_MSG_RESULT([yes]) - QBT_ADD_DEFINES="$QBT_ADD_DEFINES WITH_GEOIP_EMBEDDED"], - [AC_MSG_RESULT([$with_geoip_database_embedded]) - AC_MSG_ERROR([Unknown option "$with_geoip_database_embedded". Use either "yes" or "no".])]) - AC_MSG_CHECKING([which qtsingleapplication to use]) AS_CASE(["x$with_qtsingleapplication"], ["xshipped"], diff --git a/macxconf.pri b/macxconf.pri index c4261d742..a51b34405 100644 --- a/macxconf.pri +++ b/macxconf.pri @@ -56,6 +56,3 @@ QMAKE_BUNDLE_DATA += qt_translations ICON = $$DIST_PATH/qbittorrent_mac.icns QMAKE_INFO_PLIST = $$DIST_PATH/Info.plist - -DEFINES += WITH_GEOIP_EMBEDDED -message("On Mac OS X, GeoIP database must be embedded.") diff --git a/os2conf.pri b/os2conf.pri index b0678d4ff..c27a9e77e 100644 --- a/os2conf.pri +++ b/os2conf.pri @@ -13,8 +13,4 @@ LIBS += \ RC_FILE = qbittorrent_os2.rc # LIBTORRENT DEFINES -DEFINES += WITH_SHIPPED_GEOIP_H DEFINES += BOOST_ASIO_DYN_LINK - -DEFINES += WITH_GEOIP_EMBEDDED -message("On eCS(OS/2), GeoIP database must be embedded.") diff --git a/src/app/application.cpp b/src/app/application.cpp index 332082f5c..c8322a25e 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -65,6 +65,7 @@ #include "core/scanfoldersmodel.h" #include "core/net/smtp.h" #include "core/net/downloadmanager.h" +#include "core/net/geoipmanager.h" #include "core/bittorrent/session.h" #include "core/bittorrent/torrenthandle.h" @@ -232,10 +233,14 @@ int Application::exec(const QStringList ¶ms) #else GuiIconProvider::initInstance(); #endif + BitTorrent::Session::initInstance(); connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const))); connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished())); +#ifndef DISABLE_COUNTRIES_RESOLUTION + Net::GeoIPManager::initInstance(); +#endif ScanFoldersModel::initInstance(this); #ifndef DISABLE_WEBUI @@ -446,6 +451,9 @@ void Application::cleanup() ScanFoldersModel::freeInstance(); BitTorrent::Session::freeInstance(); +#ifndef DISABLE_COUNTRIES_RESOLUTION + Net::GeoIPManager::freeInstance(); +#endif Preferences::freeInstance(); Logger::freeInstance(); IconProvider::freeInstance(); diff --git a/src/core/bittorrent/peerinfo.cpp b/src/core/bittorrent/peerinfo.cpp index 60c116578..117b27c05 100644 --- a/src/core/bittorrent/peerinfo.cpp +++ b/src/core/bittorrent/peerinfo.cpp @@ -28,6 +28,7 @@ #include +#include "core/net/geoipmanager.h" #include "core/utils/string.h" #include "peerinfo.h" @@ -69,11 +70,12 @@ bool PeerInfo::fromLSD() const return (m_nativeInfo.source & libt::peer_info::lsd); } +#ifndef DISABLE_COUNTRIES_RESOLUTION QString PeerInfo::country() const { - return QString(QByteArray(m_nativeInfo.country, 2)); + return Net::GeoIPManager::instance()->lookup(address().ip); } - +#endif bool PeerInfo::isInteresting() const { diff --git a/src/core/bittorrent/peerinfo.h b/src/core/bittorrent/peerinfo.h index f04a2b9c8..48922ec8e 100644 --- a/src/core/bittorrent/peerinfo.h +++ b/src/core/bittorrent/peerinfo.h @@ -89,7 +89,9 @@ namespace BitTorrent qlonglong totalDownload() const; QBitArray pieces() const; QString connectionType() const; +#ifndef DISABLE_COUNTRIES_RESOLUTION QString country() const; +#endif private: libtorrent::peer_info m_nativeInfo; diff --git a/src/core/bittorrent/private/sessionprivate.h b/src/core/bittorrent/private/sessionprivate.h index be009e87b..9f77beafa 100644 --- a/src/core/bittorrent/private/sessionprivate.h +++ b/src/core/bittorrent/private/sessionprivate.h @@ -50,9 +50,6 @@ struct SessionPrivate virtual bool isTempPathEnabled() const = 0; virtual bool isAppendExtensionEnabled() const = 0; virtual bool useAppendLabelToSavePath() const = 0; -#ifndef DISABLE_COUNTRIES_RESOLUTION - virtual bool isResolveCountriesEnabled() const = 0; -#endif virtual QString defaultSavePath() const = 0; virtual QString tempPath() const = 0; virtual qreal globalMaxRatio() const = 0; diff --git a/src/core/bittorrent/private/torrenthandleprivate.h b/src/core/bittorrent/private/torrenthandleprivate.h index 31c2fde9c..db19933be 100644 --- a/src/core/bittorrent/private/torrenthandleprivate.h +++ b/src/core/bittorrent/private/torrenthandleprivate.h @@ -42,9 +42,6 @@ struct TorrentHandlePrivate virtual void handleDefaultSavePathChanged() = 0; virtual void handleTempPathChanged() = 0; virtual void handleAppendExtensionToggled() = 0; -#ifndef DISABLE_COUNTRIES_RESOLUTION - virtual void handleResolveCountriesToggled() = 0; -#endif protected: ~TorrentHandlePrivate() {} diff --git a/src/core/bittorrent/session.cpp b/src/core/bittorrent/session.cpp index 390cf6c55..cdbaad346 100644 --- a/src/core/bittorrent/session.cpp +++ b/src/core/bittorrent/session.cpp @@ -63,7 +63,7 @@ using namespace BitTorrent; //#include #ifndef DISABLE_COUNTRIES_RESOLUTION -#include "geoipmanager.h" +#include "core/net/geoipmanager.h" #endif #include "core/utils/misc.h" @@ -141,10 +141,6 @@ Session::Session(QObject *parent) , m_globalMaxRatio(-1) , m_numResumeData(0) , m_extraLimit(0) -#ifndef DISABLE_COUNTRIES_RESOLUTION - , m_geoipDBLoaded(false) - , m_resolveCountries(false) -#endif , m_appendLabelToSavePath(false) , m_appendExtension(false) , m_refreshInterval(0) @@ -252,13 +248,6 @@ bool Session::useAppendLabelToSavePath() const return m_appendLabelToSavePath; } -#ifndef DISABLE_COUNTRIES_RESOLUTION -bool Session::isResolveCountriesEnabled() const -{ - return m_resolveCountries; -} -#endif - QString Session::defaultSavePath() const { return m_defaultSavePath; @@ -577,25 +566,6 @@ void Session::configure() delete m_bwScheduler; } -#ifndef DISABLE_COUNTRIES_RESOLUTION - // Resolve countries - qDebug("Loading country resolution settings"); - const bool new_resolv_countries = pref->resolvePeerCountries(); - if (m_resolveCountries != new_resolv_countries) { - qDebug("in country resolution settings"); - m_resolveCountries = new_resolv_countries; - if (m_resolveCountries && !m_geoipDBLoaded) { - qDebug("Loading geoip database"); - GeoIPManager::loadDatabase(m_nativeSession); - m_geoipDBLoaded = true; - } - - // Update torrent handles - foreach (TorrentHandlePrivate *const torrent, m_torrents) - torrent->handleResolveCountriesToggled(); - } -#endif - Logger* const logger = Logger::instance(); // * Session settings diff --git a/src/core/bittorrent/session.h b/src/core/bittorrent/session.h index f8108a2ec..d80fb4a3e 100644 --- a/src/core/bittorrent/session.h +++ b/src/core/bittorrent/session.h @@ -283,9 +283,6 @@ namespace BitTorrent bool isTempPathEnabled() const; bool isAppendExtensionEnabled() const; bool useAppendLabelToSavePath() const; - #ifndef DISABLE_COUNTRIES_RESOLUTION - bool isResolveCountriesEnabled() const; - #endif QString defaultSavePath() const; QString tempPath() const; void handleTorrentRatioLimitChanged(TorrentHandle *const torrent); @@ -327,10 +324,6 @@ namespace BitTorrent qreal m_globalMaxRatio; int m_numResumeData; int m_extraLimit; - #ifndef DISABLE_COUNTRIES_RESOLUTION - bool m_geoipDBLoaded; - bool m_resolveCountries; - #endif bool m_appendLabelToSavePath; bool m_appendExtension; uint m_refreshInterval; diff --git a/src/core/bittorrent/torrenthandle.cpp b/src/core/bittorrent/torrenthandle.cpp index 2ca6d6a91..617c07b9e 100644 --- a/src/core/bittorrent/torrenthandle.cpp +++ b/src/core/bittorrent/torrenthandle.cpp @@ -205,10 +205,6 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle { initialize(); -#ifndef DISABLE_COUNTRIES_RESOLUTION - resolveCountries(m_session->isResolveCountriesEnabled()); -#endif - if (!data.resumed) { setSequentialDownload(data.sequential); if (hasMetadata()) { @@ -1644,13 +1640,6 @@ void TorrentHandle::handleAppendExtensionToggled() removeExtensionsFromIncompleteFiles(); } -#ifndef DISABLE_COUNTRIES_RESOLUTION -void TorrentHandle::handleResolveCountriesToggled() -{ - resolveCountries(m_session->isResolveCountriesEnabled()); -} -#endif - void TorrentHandle::handleAlert(libtorrent::alert *a) { switch (a->type()) { @@ -1865,11 +1854,6 @@ QString TorrentHandle::toMagnetUri() const return Utils::String::fromStdString(libt::make_magnet_uri(m_nativeHandle)); } -void TorrentHandle::resolveCountries(bool b) -{ - SAFE_CALL(resolve_countries, b); -} - void TorrentHandle::prioritizeFiles(const QVector &priorities) { qDebug() << Q_FUNC_INFO; diff --git a/src/core/bittorrent/torrenthandle.h b/src/core/bittorrent/torrenthandle.h index 3c998d61c..4fa8c9db3 100644 --- a/src/core/bittorrent/torrenthandle.h +++ b/src/core/bittorrent/torrenthandle.h @@ -316,9 +316,6 @@ namespace BitTorrent void handleDefaultSavePathChanged(); void handleTempPathChanged(); void handleAppendExtensionToggled(); - #ifndef DISABLE_COUNTRIES_RESOLUTION - void handleResolveCountriesToggled(); - #endif void handleStorageMovedAlert(libtorrent::storage_moved_alert *p); void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p); @@ -341,7 +338,6 @@ namespace BitTorrent bool useTempPath() const; QString nativeActualSavePath() const; - void resolveCountries(bool b); void adjustSavePath(); void adjustActualSavePath(); void adjustActualSavePath_impl(); diff --git a/src/core/core.pri b/src/core/core.pri index f91d7f059..2852c1236 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -16,9 +16,11 @@ HEADERS += \ $$PWD/net/dnsupdater.h \ $$PWD/net/downloadmanager.h \ $$PWD/net/downloadhandler.h \ + $$PWD/net/geoipmanager.h \ $$PWD/net/portforwarder.h \ $$PWD/net/reverseresolution.h \ $$PWD/net/smtp.h \ + $$PWD/net/private/geoipdatabase.h \ $$PWD/bittorrent/infohash.h \ $$PWD/bittorrent/session.h \ $$PWD/bittorrent/sessionstatus.h \ @@ -58,9 +60,11 @@ SOURCES += \ $$PWD/net/dnsupdater.cpp \ $$PWD/net/downloadmanager.cpp \ $$PWD/net/downloadhandler.cpp \ + $$PWD/net/geoipmanager.cpp \ $$PWD/net/portforwarder.cpp \ $$PWD/net/reverseresolution.cpp \ $$PWD/net/smtp.cpp \ + $$PWD/net/private/geoipdatabase.cpp \ $$PWD/bittorrent/infohash.cpp \ $$PWD/bittorrent/session.cpp \ $$PWD/bittorrent/sessionstatus.cpp \ diff --git a/src/core/net/geoipmanager.cpp b/src/core/net/geoipmanager.cpp new file mode 100644 index 000000000..7716a4264 --- /dev/null +++ b/src/core/net/geoipmanager.cpp @@ -0,0 +1,464 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2010 Christophe Dumez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include +#include +#include +#include + +#include "core/logger.h" +#include "core/preferences.h" +#include "core/utils/fs.h" +#include "core/utils/gzip.h" +#include "downloadmanager.h" +#include "downloadhandler.h" +#include "private/geoipdatabase.h" +#include "geoipmanager.h" + +static const char DATABASE_URL[] = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz"; +static const char GEOIP_FOLDER[] = "GeoIP"; +static const char GEOIP_FILENAME[] = "GeoLite2-Country.mmdb"; +static const int CACHE_SIZE = 1000; +static const int UPDATE_INTERVAL = 30; // Days between database updates + +using namespace Net; + +// GeoIPManager + +GeoIPManager *GeoIPManager::m_instance = 0; + +GeoIPManager::GeoIPManager() + : m_enabled(false) + , m_geoIPDatabase(0) + , m_cache(CACHE_SIZE) +{ + configure(); + connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); +} + +GeoIPManager::~GeoIPManager() +{ + if (m_geoIPDatabase) + delete m_geoIPDatabase; +} + +void GeoIPManager::initInstance() +{ + if (!m_instance) + m_instance = new GeoIPManager; +} + +void GeoIPManager::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = 0; + } +} + +GeoIPManager *GeoIPManager::instance() +{ + return m_instance; +} + +void GeoIPManager::loadDatabase() +{ + if (m_geoIPDatabase) { + delete m_geoIPDatabase; + m_geoIPDatabase = 0; + } + + QString filepath = Utils::Fs::expandPathAbs( + QString("%1%2/%3").arg(Utils::Fs::QDesktopServicesDataLocation()) + .arg(GEOIP_FOLDER).arg(GEOIP_FILENAME)); + + QString error; + m_geoIPDatabase = GeoIPDatabase::load(filepath, error); + if (m_geoIPDatabase) + Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.") + .arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()), + Log::INFO); + else + Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING); + + manageDatabaseUpdate(); +} + +void GeoIPManager::manageDatabaseUpdate() +{ + if (!m_geoIPDatabase || (m_geoIPDatabase->buildEpoch().daysTo(QDateTime::currentDateTimeUtc()) >= UPDATE_INTERVAL)) + downloadDatabaseFile(); +} + +void GeoIPManager::downloadDatabaseFile() +{ + DownloadHandler *handler = DownloadManager::instance()->downloadUrl(DATABASE_URL); + connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(downloadFinished(QString, QByteArray))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(downloadFailed(QString, QString))); +} + +QString GeoIPManager::lookup(const QHostAddress &hostAddr) const +{ + if (m_enabled && m_geoIPDatabase) { + QString *country = m_cache.object(hostAddr); + if (country) + return *country; + + QString code = m_geoIPDatabase->lookup(hostAddr); + m_cache.insert(hostAddr, new QString(code)); + return code; + } + + return QString(); +} + +// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm +QString GeoIPManager::CountryName(const QString &countryISOCode) +{ + static QHash countries; + static bool initialized = false; + + if (!initialized) { + countries[QString()] = "N/A"; + countries["AP"] = "Asia/Pacific Region"; + countries["EU"] = "Europe"; + countries["AD"] = "Andorra"; + countries["AE"] = "United Arab Emirates"; + countries["AF"] = "Afghanistan"; + countries["AG"] = "Antigua and Barbuda"; + countries["AI"] = "Anguilla"; + countries["AL"] = "Albania"; + countries["AM"] = "Armenia"; + countries["AN"] = "Netherlands Antilles"; + countries["AO"] = "Angola"; + countries["AQ"] = "Antarctica"; + countries["AR"] = "Argentina"; + countries["AS"] = "American Samoa"; + countries["AT"] = "Austria"; + countries["AU"] = "Australia"; + countries["AW"] = "Aruba"; + countries["AZ"] = "Azerbaijan"; + countries["BA"] = "Bosnia and Herzegovina"; + countries["BB"] = "Barbados"; + countries["BD"] = "Bangladesh"; + countries["BE"] = "Belgium"; + countries["BF"] = "Burkina Faso"; + countries["BG"] = "Bulgaria"; + countries["BH"] = "Bahrain"; + countries["BI"] = "Burundi"; + countries["BJ"] = "Benin"; + countries["BM"] = "Bermuda"; + countries["BN"] = "Brunei Darussalam"; + countries["BO"] = "Bolivia"; + countries["BR"] = "Brazil"; + countries["BS"] = "Bahamas"; + countries["BT"] = "Bhutan"; + countries["BV"] = "Bouvet Island"; + countries["BW"] = "Botswana"; + countries["BY"] = "Belarus"; + countries["BZ"] = "Belize"; + countries["CA"] = "Canada"; + countries["CC"] = "Cocos (Keeling) Islands"; + countries["CD"] = "Congo, The Democratic Republic of the"; + countries["CF"] = "Central African Republic"; + countries["CG"] = "Congo"; + countries["CH"] = "Switzerland"; + countries["CI"] = "Cote D'Ivoire"; + countries["CK"] = "Cook Islands"; + countries["CL"] = "Chile"; + countries["CM"] = "Cameroon"; + countries["CN"] = "China"; + countries["CO"] = "Colombia"; + countries["CR"] = "Costa Rica"; + countries["CU"] = "Cuba"; + countries["CV"] = "Cape Verde"; + countries["CX"] = "Christmas Island"; + countries["CY"] = "Cyprus"; + countries["CZ"] = "Czech Republic"; + countries["DE"] = "Germany"; + countries["DJ"] = "Djibouti"; + countries["DK"] = "Denmark"; + countries["DM"] = "Dominica"; + countries["DO"] = "Dominican Republic"; + countries["DZ"] = "Algeria"; + countries["EC"] = "Ecuador"; + countries["EE"] = "Estonia"; + countries["EG"] = "Egypt"; + countries["EH"] = "Western Sahara"; + countries["ER"] = "Eritrea"; + countries["ES"] = "Spain"; + countries["ET"] = "Ethiopia"; + countries["FI"] = "Finland"; + countries["FJ"] = "Fiji"; + countries["FK"] = "Falkland Islands (Malvinas)"; + countries["FM"] = "Micronesia, Federated States of"; + countries["FO"] = "Faroe Islands"; + countries["FR"] = "France"; + countries["FX"] = "France, Metropolitan"; + countries["GA"] = "Gabon"; + countries["GB"] = "United Kingdom"; + countries["GD"] = "Grenada"; + countries["GE"] = "Georgia"; + countries["GF"] = "French Guiana"; + countries["GH"] = "Ghana"; + countries["GI"] = "Gibraltar"; + countries["GL"] = "Greenland"; + countries["GM"] = "Gambia"; + countries["GN"] = "Guinea"; + countries["GP"] = "Guadeloupe"; + countries["GQ"] = "Equatorial Guinea"; + countries["GR"] = "Greece"; + countries["GS"] = "South Georgia and the South Sandwich Islands"; + countries["GT"] = "Guatemala"; + countries["GU"] = "Guam"; + countries["GW"] = "Guinea-Bissau"; + countries["GY"] = "Guyana"; + countries["HK"] = "Hong Kong"; + countries["HM"] = "Heard Island and McDonald Islands"; + countries["HN"] = "Honduras"; + countries["HR"] = "Croatia"; + countries["HT"] = "Haiti"; + countries["HU"] = "Hungary"; + countries["ID"] = "Indonesia"; + countries["IE"] = "Ireland"; + countries["IL"] = "Israel"; + countries["IN"] = "India"; + countries["IO"] = "British Indian Ocean Territory"; + countries["IQ"] = "Iraq"; + countries["IR"] = "Iran, Islamic Republic of"; + countries["IS"] = "Iceland"; + countries["IT"] = "Italy"; + countries["JM"] = "Jamaica"; + countries["JO"] = "Jordan"; + countries["JP"] = "Japan"; + countries["KE"] = "Kenya"; + countries["KG"] = "Kyrgyzstan"; + countries["KH"] = "Cambodia"; + countries["KI"] = "Kiribati"; + countries["KM"] = "Comoros"; + countries["KN"] = "Saint Kitts and Nevis"; + countries["KP"] = "Korea, Democratic People's Republic of"; + countries["KR"] = "Korea, Republic of"; + countries["KW"] = "Kuwait"; + countries["KY"] = "Cayman Islands"; + countries["KZ"] = "Kazakhstan"; + countries["LA"] = "Lao People's Democratic Republic"; + countries["LB"] = "Lebanon"; + countries["LC"] = "Saint Lucia"; + countries["LI"] = "Liechtenstein"; + countries["LK"] = "Sri Lanka"; + countries["LR"] = "Liberia"; + countries["LS"] = "Lesotho"; + countries["LT"] = "Lithuania"; + countries["LU"] = "Luxembourg"; + countries["LV"] = "Latvia"; + countries["LY"] = "Libyan Arab Jamahiriya"; + countries["MA"] = "Morocco"; + countries["MC"] = "Monaco"; + countries["MD"] = "Moldova, Republic of"; + countries["MG"] = "Madagascar"; + countries["MH"] = "Marshall Islands"; + countries["MK"] = "Macedonia"; + countries["ML"] = "Mali"; + countries["MM"] = "Myanmar"; + countries["MN"] = "Mongolia"; + countries["MO"] = "Macau"; + countries["MP"] = "Northern Mariana Islands"; + countries["MQ"] = "Martinique"; + countries["MR"] = "Mauritania"; + countries["MS"] = "Montserrat"; + countries["MT"] = "Malta"; + countries["MU"] = "Mauritius"; + countries["MV"] = "Maldives"; + countries["MW"] = "Malawi"; + countries["MX"] = "Mexico"; + countries["MY"] = "Malaysia"; + countries["MZ"] = "Mozambique"; + countries["NA"] = "Namibia"; + countries["NC"] = "New Caledonia"; + countries["NE"] = "Niger"; + countries["NF"] = "Norfolk Island"; + countries["NG"] = "Nigeria"; + countries["NI"] = "Nicaragua"; + countries["NL"] = "Netherlands"; + countries["NO"] = "Norway"; + countries["NP"] = "Nepal"; + countries["NR"] = "Nauru"; + countries["NU"] = "Niue"; + countries["NZ"] = "New Zealand"; + countries["OM"] = "Oman"; + countries["PA"] = "Panama"; + countries["PE"] = "Peru"; + countries["PF"] = "French Polynesia"; + countries["PG"] = "Papua New Guinea"; + countries["PH"] = "Philippines"; + countries["PK"] = "Pakistan"; + countries["PL"] = "Poland"; + countries["PM"] = "Saint Pierre and Miquelon"; + countries["PN"] = "Pitcairn Islands"; + countries["PR"] = "Puerto Rico"; + countries["PS"] = "Palestinian Territory"; + countries["PT"] = "Portugal"; + countries["PW"] = "Palau"; + countries["PY"] = "Paraguay"; + countries["QA"] = "Qatar"; + countries["RE"] = "Reunion"; + countries["RO"] = "Romania"; + countries["RU"] = "Russian Federation"; + countries["RW"] = "Rwanda"; + countries["SA"] = "Saudi Arabia"; + countries["SB"] = "Solomon Islands"; + countries["SC"] = "Seychelles"; + countries["SD"] = "Sudan"; + countries["SE"] = "Sweden"; + countries["SG"] = "Singapore"; + countries["SH"] = "Saint Helena"; + countries["SI"] = "Slovenia"; + countries["SJ"] = "Svalbard and Jan Mayen"; + countries["SK"] = "Slovakia"; + countries["SL"] = "Sierra Leone"; + countries["SM"] = "San Marino"; + countries["SN"] = "Senegal"; + countries["SO"] = "Somalia"; + countries["SR"] = "Suriname"; + countries["ST"] = "Sao Tome and Principe"; + countries["SV"] = "El Salvador"; + countries["SY"] = "Syrian Arab Republic"; + countries["SZ"] = "Swaziland"; + countries["TC"] = "Turks and Caicos Islands"; + countries["TD"] = "Chad"; + countries["TF"] = "French Southern Territories"; + countries["TG"] = "Togo"; + countries["TH"] = "Thailand"; + countries["TJ"] = "Tajikistan"; + countries["TK"] = "Tokelau"; + countries["TM"] = "Turkmenistan"; + countries["TN"] = "Tunisia"; + countries["TO"] = "Tonga"; + countries["TL"] = "Timor-Leste"; + countries["TR"] = "Turkey"; + countries["TT"] = "Trinidad and Tobago"; + countries["TV"] = "Tuvalu"; + countries["TW"] = "Taiwan"; + countries["TZ"] = "Tanzania, United Republic of"; + countries["UA"] = "Ukraine"; + countries["UG"] = "Uganda"; + countries["UM"] = "United States Minor Outlying Islands"; + countries["US"] = "United States"; + countries["UY"] = "Uruguay"; + countries["UZ"] = "Uzbekistan"; + countries["VA"] = "Holy See (Vatican City State)"; + countries["VC"] = "Saint Vincent and the Grenadines"; + countries["VE"] = "Venezuela"; + countries["VG"] = "Virgin Islands, British"; + countries["VI"] = "Virgin Islands, U.S."; + countries["VN"] = "Vietnam"; + countries["VU"] = "Vanuatu"; + countries["WF"] = "Wallis and Futuna"; + countries["WS"] = "Samoa"; + countries["YE"] = "Yemen"; + countries["YT"] = "Mayotte"; + countries["RS"] = "Serbia"; + countries["ZA"] = "South Africa"; + countries["ZM"] = "Zambia"; + countries["ME"] = "Montenegro"; + countries["ZW"] = "Zimbabwe"; + countries["A1"] = "Anonymous Proxy"; + countries["A2"] = "Satellite Provider"; + countries["O1"] = "Other"; + countries["AX"] = "Aland Islands"; + countries["GG"] = "Guernsey"; + countries["IM"] = "Isle of Man"; + countries["JE"] = "Jersey"; + countries["BL"] = "Saint Barthelemy"; + countries["MF"] = "Saint Martin"; + + initialized = true; + } + + return countries.value(countryISOCode, "N/A"); +} + +void GeoIPManager::configure() +{ + const bool enabled = Preferences::instance()->resolvePeerCountries(); + if (m_enabled != enabled) { + m_enabled = enabled; + if (m_enabled && !m_geoIPDatabase) + loadDatabase(); + } +} + +void GeoIPManager::downloadFinished(const QString &url, QByteArray data) +{ + Q_UNUSED(url); + + if (!Utils::Gzip::uncompress(data, data)) { + Logger::instance()->addMessage(tr("Could not uncompress GeoIP database file."), Log::WARNING); + return; + } + + QString error; + GeoIPDatabase *geoIPDatabase = GeoIPDatabase::load(data, error); + if (geoIPDatabase) { + if (!m_geoIPDatabase || (geoIPDatabase->buildEpoch() > m_geoIPDatabase->buildEpoch())) { + if (m_geoIPDatabase) + delete m_geoIPDatabase; + m_geoIPDatabase = geoIPDatabase; + Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.") + .arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()), + Log::INFO); + QString targetPath = Utils::Fs::expandPathAbs( + Utils::Fs::QDesktopServicesDataLocation() + GEOIP_FOLDER); + if (!QDir(targetPath).exists()) + QDir().mkpath(targetPath); + QFile targetFile(QString("%1/%2").arg(targetPath).arg(GEOIP_FILENAME)); + if (!targetFile.open(QFile::WriteOnly) || (targetFile.write(data) == -1)) { + Logger::instance()->addMessage( + tr("Couldn't save downloaded GeoIP database file."), Log::WARNING); + } + else { + Logger::instance()->addMessage(tr("Successfully updated GeoIP database."), Log::INFO); + } + } + else { + delete geoIPDatabase; + } + } + else { + Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING); + } +} + +void GeoIPManager::downloadFailed(const QString &url, const QString &reason) +{ + Q_UNUSED(url); + Logger::instance()->addMessage(tr("Couldn't download GeoIP database file. Reason: %1").arg(reason), Log::WARNING); +} diff --git a/src/core/net/geoipmanager.h b/src/core/net/geoipmanager.h new file mode 100644 index 000000000..d400bd849 --- /dev/null +++ b/src/core/net/geoipmanager.h @@ -0,0 +1,77 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2010 Christophe Dumez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef NET_GEOIPMANAGER_H +#define NET_GEOIPMANAGER_H + +#include +#include + +class QHostAddress; +class QString; + +class GeoIPDatabase; + +namespace Net +{ + class GeoIPManager : public QObject + { + Q_OBJECT + + public: + static void initInstance(); + static void freeInstance(); + static GeoIPManager *instance(); + + QString lookup(const QHostAddress &hostAddr) const; + + static QString CountryName(const QString &countryISOCode); + + private slots: + void configure(); + void downloadFinished(const QString &url, QByteArray data); + void downloadFailed(const QString &url, const QString &reason); + + private: + GeoIPManager(); + ~GeoIPManager(); + + void loadDatabase(); + void manageDatabaseUpdate(); + void downloadDatabaseFile(); + + bool m_enabled; + GeoIPDatabase *m_geoIPDatabase; + mutable QCache m_cache; + + static GeoIPManager *m_instance; + }; +} + +#endif // NET_GEOIPMANAGER_H diff --git a/src/core/net/private/geoipdatabase.cpp b/src/core/net/private/geoipdatabase.cpp new file mode 100644 index 000000000..2294f5433 --- /dev/null +++ b/src/core/net/private/geoipdatabase.cpp @@ -0,0 +1,631 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/types.h" +#include "geoipdatabase.h" + +struct Node +{ + quint32 left; + quint32 right; +}; + +struct GeoIPData +{ + // Metadata + quint16 ipVersion; + quint16 recordSize; + quint32 nodeCount; + QDateTime buildEpoch; + // Search data + QList index; + QHash countries; +}; + +namespace +{ + const quint32 __ENDIAN_TEST__ = 0x00000001; + const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast(&__ENDIAN_TEST__)[0] == 0x01); + + BEGIN_SCOPED_ENUM(DataType) + { + Unknown = 0, + Pointer = 1, + String = 2, + Double = 3, + Bytes = 4, + Integer16 = 5, + Integer32 = 6, + Map = 7, + SignedInteger32 = 8, + Integer64 = 9, + Integer128 = 10, + Array = 11, + DataCacheContainer = 12, + EndMarker = 13, + Boolean = 14, + Float = 15 + } + END_SCOPED_ENUM + + struct DataFieldDescriptor + { + DataType fieldType; + union + { + quint32 fieldSize; + quint32 offset; // Pointer + }; + }; + + const int MAX_FILE_SIZE = 10485760; // 10MB + const char DB_TYPE[] = "GeoLite2-Country"; + + const quint32 MAX_METADATA_SIZE = 131072; // 128KB + const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com"; + const char DATA_SECTION_SEPARATOR[16] = { 0 }; + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + Q_IPV6ADDR createMappedAddress(quint32 ip4); +#endif + + class Loader + { + Q_DECLARE_TR_FUNCTIONS(GeoIPDatabase) + + public: + GeoIPData *load(const QString &filename); + GeoIPData *load(const QByteArray &data); + QString error() const; + + private: + bool parseMetadata(const QVariantHash &metadata); + bool loadDB(); + QVariantHash readMetadata(); + QVariant readDataField(quint32 &offset); + bool readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor &out); + void fromBigEndian(uchar *buf, quint32 len); + QVariant readMapValue(quint32 &offset, quint32 count); + QVariant readArrayValue(quint32 &offset, quint32 count); + + template + QVariant readPlainValue(quint32 &offset, quint8 len) + { + T value = 0; + const uchar *const data = m_data + offset; + const quint32 availSize = m_size - offset; + + if ((len > 0) && (len <= sizeof(T) && (availSize >= len))) { + // copy input data to last 'len' bytes of 'value' + uchar *dst = reinterpret_cast(&value) + (sizeof(T) - len); + memcpy(dst, data, len); + fromBigEndian(reinterpret_cast(&value), sizeof(T)); + offset += len; + } + + return QVariant::fromValue(value); + } + + private: + const uchar *m_data; + quint32 m_size; + QString m_error; + GeoIPData *m_geoIPData; + }; +} + +// GeoIPDatabase + +GeoIPDatabase::GeoIPDatabase(GeoIPData *geoIPData) + : m_geoIPData(geoIPData) +{ +} + +GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error) +{ + GeoIPDatabase *db = 0; + + Loader loader; + GeoIPData *geoIPData = loader.load(filename); + if (!geoIPData) + error = loader.error(); + else + db = new GeoIPDatabase(geoIPData); + + return db; +} + +GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error) +{ + GeoIPDatabase *db = 0; + + Loader loader; + GeoIPData *geoIPData = loader.load(data); + if (!geoIPData) + error = loader.error(); + else + db = new GeoIPDatabase(geoIPData); + + return db; +} + +GeoIPDatabase::~GeoIPDatabase() +{ + delete m_geoIPData; +} + +QString GeoIPDatabase::type() const +{ + return DB_TYPE; +} + +quint16 GeoIPDatabase::ipVersion() const +{ + return m_geoIPData->ipVersion; +} + +QDateTime GeoIPDatabase::buildEpoch() const +{ + return m_geoIPData->buildEpoch; +} + +QString GeoIPDatabase::lookup(const QHostAddress &hostAddr) const +{ +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + Q_IPV6ADDR addr = hostAddr.protocol() == QAbstractSocket::IPv4Protocol + ? createMappedAddress(hostAddr.toIPv4Address()) + : hostAddr.toIPv6Address(); +#else + Q_IPV6ADDR addr = hostAddr.toIPv6Address(); +#endif + const quint32 nodeCount = static_cast(m_geoIPData->index.size()); + Node node = m_geoIPData->index[0]; + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 8; ++j) { + bool right = static_cast((addr[i] >> (7 - j)) & 1); + quint32 id = (right ? node.right : node.left); + if (id == nodeCount) + return QString(); + else if (id > nodeCount) + return m_geoIPData->countries[id]; + else + node = m_geoIPData->index[id]; + } + } + + return QString(); +} + +namespace +{ + // Loader + + GeoIPData *Loader::load(const QString &filename) + { + QFile file(filename); + if (file.size() > MAX_FILE_SIZE) { + m_error = tr("Unsupported database file size."); + return 0; + } + + if (!file.open(QFile::ReadOnly)) { + m_error = file.errorString(); + return 0; + } + + m_size = file.size(); + QScopedArrayPointer data(new uchar[m_size]); + m_data = data.data(); + if (file.read((char *)m_data, m_size) != m_size) { + m_error = file.errorString(); + return 0; + } + + QScopedPointer geoIPData(new GeoIPData); + m_geoIPData = geoIPData.data(); + if (!parseMetadata(readMetadata()) || !loadDB()) + return 0; + + return geoIPData.take(); + } + + GeoIPData *Loader::load(const QByteArray &data) + { + if (data.size() > MAX_FILE_SIZE) { + m_error = tr("Unsupported database file size."); + return 0; + } + + m_size = data.size(); + m_data = reinterpret_cast(data.constData()); + + QScopedPointer geoIPData(new GeoIPData); + m_geoIPData = geoIPData.data(); + if (!parseMetadata(readMetadata()) || !loadDB()) + return 0; + + return geoIPData.take(); + } + + QString Loader::error() const + { + return m_error; + } + +#define CHECK_METADATA_REQ(key, type) \ + if (!metadata.contains(#key)) { \ + m_error = errMsgNotFound.arg(#key); \ + return false; \ + } \ + else if (metadata.value(#key).userType() != QMetaType::type) { \ + m_error = errMsgInvalid.arg(#key); \ + return false; \ + } + +#define CHECK_METADATA_OPT(key, type) \ + if (metadata.contains(#key)) { \ + if (metadata.value(#key).userType() != QMetaType::type) { \ + m_error = errMsgInvalid.arg(#key); \ + return false; \ + } \ + } + + bool Loader::parseMetadata(const QVariantHash &metadata) + { + const QString errMsgNotFound = tr("Metadata error: '%1' entry not found."); + const QString errMsgInvalid = tr("Metadata error: '%1' entry has invalid type."); + + qDebug() << "Parsing MaxMindDB metadata..."; + + CHECK_METADATA_REQ(binary_format_major_version, UShort); + CHECK_METADATA_REQ(binary_format_minor_version, UShort); + uint versionMajor = metadata.value("binary_format_major_version").toUInt(); + uint versionMinor = metadata.value("binary_format_minor_version").toUInt(); + if (versionMajor != 2) { + m_error = tr("Unsupported database version: %1.%2").arg(versionMajor).arg(versionMinor); + return false; + } + + CHECK_METADATA_REQ(ip_version, UShort); + m_geoIPData->ipVersion = metadata.value("ip_version").value(); + if (m_geoIPData->ipVersion != 6) { + m_error = tr("Unsupported IP version: %1").arg(m_geoIPData->ipVersion); + return false; + } + + CHECK_METADATA_REQ(record_size, UShort); + m_geoIPData->recordSize = metadata.value("record_size").value(); + if (m_geoIPData->recordSize != 24) { + m_error = tr("Unsupported record size: %1").arg(m_geoIPData->recordSize); + return false; + } + + CHECK_METADATA_REQ(node_count, UInt); + m_geoIPData->nodeCount = metadata.value("node_count").value(); + + CHECK_METADATA_REQ(database_type, QString); + QString dbType = metadata.value("database_type").toString(); + if (dbType != DB_TYPE) { + m_error = tr("Invalid database type: %1").arg(dbType); + return false; + } + + CHECK_METADATA_REQ(build_epoch, ULongLong); + m_geoIPData->buildEpoch = QDateTime::fromTime_t(metadata.value("build_epoch").toULongLong()); + + CHECK_METADATA_OPT(languages, QVariantList); + CHECK_METADATA_OPT(description, QVariantHash); + + return true; + } + + bool Loader::loadDB() + { + qDebug() << "Parsing MaxMindDB index tree..."; + + const int nodeSize = m_geoIPData->recordSize / 4; // in bytes + const int indexSize = m_geoIPData->nodeCount * nodeSize; + if ((m_size < (indexSize + sizeof(DATA_SECTION_SEPARATOR))) + || (memcmp(m_data + indexSize, DATA_SECTION_SEPARATOR, sizeof(DATA_SECTION_SEPARATOR)) != 0)) { + m_error = tr("Database corrupted: no data section found."); + return false; + } + + m_geoIPData->index.reserve(m_geoIPData->nodeCount); + + const int recordBytes = nodeSize / 2; + const uchar *ptr = m_data; + bool left = true; + Node node; + for (quint32 i = 0; i < (2 * m_geoIPData->nodeCount); ++i) { + uchar buf[4] = { 0 }; + + memcpy(&buf[4 - recordBytes], ptr, recordBytes); + fromBigEndian(buf, 4); + quint32 id = *(reinterpret_cast(buf)); + + if ((id > m_geoIPData->nodeCount) && !m_geoIPData->countries.contains(id)) { + const quint32 offset = id - m_geoIPData->nodeCount - sizeof(DATA_SECTION_SEPARATOR); + quint32 tmp = offset + indexSize + sizeof(DATA_SECTION_SEPARATOR); + QVariant val = readDataField(tmp); + if (val.userType() == QMetaType::QVariantHash) { + m_geoIPData->countries[id] = val.toHash()["country"].toHash()["iso_code"].toString(); + } + else if (val.userType() == QVariant::Invalid) { + m_error = tr("Database corrupted: invalid data type at DATA@%1").arg(offset, 8, 16, QLatin1Char('0')); + return false; + } + else { + m_error = tr("Invalid database: unsupported data type at DATA@%1").arg(offset, 8, 16, QLatin1Char('0')); + return false; + } + } + + if (left) { + node.left = id; + } + else { + node.right = id; + m_geoIPData->index << node; + } + + left = !left; + ptr += recordBytes; + } + + return true; + } + + QVariantHash Loader::readMetadata() + { + const char *ptr = reinterpret_cast(m_data); + quint32 size = m_size; + if (m_size > MAX_METADATA_SIZE) { + ptr += m_size - MAX_METADATA_SIZE; + size = MAX_METADATA_SIZE; + } + + const QByteArray data = QByteArray::fromRawData(ptr, size); + int index = data.lastIndexOf(METADATA_BEGIN_MARK); + if (index >= 0) { + if (m_size > MAX_METADATA_SIZE) + index += (m_size - MAX_METADATA_SIZE); // from begin of all data + quint32 offset = static_cast(index + strlen(METADATA_BEGIN_MARK)); + QVariant metadata = readDataField(offset); + m_size = index; // truncate m_size to not contain metadata section + if (metadata.userType() == QMetaType::QVariantHash) + return metadata.toHash(); + } + + return QVariantHash(); + } + + QVariant Loader::readDataField(quint32 &offset) + { + DataFieldDescriptor descr; + if (!readDataFieldDescriptor(offset, descr)) + return QVariant(); + + quint32 locOffset = offset; + bool usePointer = false; + if (descr.fieldType == DataType::Pointer) { + usePointer = true; + // convert offset from data section to global + locOffset = descr.offset + (m_geoIPData->nodeCount * m_geoIPData->recordSize / 4) + sizeof(DATA_SECTION_SEPARATOR); + if (!readDataFieldDescriptor(locOffset, descr)) + return QVariant(); + } + + QVariant fieldValue; + switch (descr.fieldType) { + case DataType::Pointer: + qDebug() << "* Illegal Pointer using"; + break; + case DataType::String: + fieldValue = QString::fromUtf8(reinterpret_cast(m_data + locOffset), descr.fieldSize); + locOffset += descr.fieldSize; + break; + case DataType::Double: + if (descr.fieldSize == 8) + fieldValue = readPlainValue(locOffset, descr.fieldSize); + else + qDebug() << "* Invalid field size for type: Double"; + break; + case DataType::Bytes: + fieldValue = QByteArray(reinterpret_cast(m_data + locOffset), descr.fieldSize); + locOffset += descr.fieldSize; + break; + case DataType::Integer16: + fieldValue = readPlainValue(locOffset, descr.fieldSize); + break; + case DataType::Integer32: + fieldValue = readPlainValue(locOffset, descr.fieldSize); + break; + case DataType::Map: + fieldValue = readMapValue(locOffset, descr.fieldSize); + break; + case DataType::SignedInteger32: + fieldValue = readPlainValue(locOffset, descr.fieldSize); + break; + case DataType::Integer64: + fieldValue = readPlainValue(locOffset, descr.fieldSize); + break; + case DataType::Integer128: + qDebug() << "* Unsupported data type: Integer128"; + break; + case DataType::Array: + fieldValue = readArrayValue(locOffset, descr.fieldSize); + break; + case DataType::DataCacheContainer: + qDebug() << "* Unsupported data type: DataCacheContainer"; + break; + case DataType::EndMarker: + qDebug() << "* Unsupported data type: EndMarker"; + break; + case DataType::Boolean: + fieldValue = QVariant::fromValue(static_cast(descr.fieldSize)); + break; + case DataType::Float: + if (descr.fieldSize == 4) + fieldValue = readPlainValue(locOffset, descr.fieldSize); + else + qDebug() << "* Invalid field size for type: Float"; + break; + } + + if (!usePointer) + offset = locOffset; + return fieldValue; + } + + bool Loader::readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor &out) + { + const uchar *dataPtr = m_data + offset; + int availSize = m_size - offset; + if (availSize < 1) return false; + + out.fieldType = static_cast((dataPtr[0] & 0xE0) >> 5); + if (out.fieldType == DataType::Pointer) { + int size = ((dataPtr[0] & 0x18) >> 3); + if (availSize < (size + 2)) return false; + + if (size == 0) + out.offset = ((dataPtr[0] & 0x07) << 8) + dataPtr[1]; + else if (size == 1) + out.offset = ((dataPtr[0] & 0x07) << 16) + (dataPtr[1] << 8) + dataPtr[2] + 2048; + else if (size == 2) + out.offset = ((dataPtr[0] & 0x07) << 24) + (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3] + 526336; + else if (size == 3) + out.offset = (dataPtr[1] << 24) + (dataPtr[2] << 16) + (dataPtr[3] << 8) + dataPtr[4]; + + offset += size + 2; + return true; + } + + out.fieldSize = dataPtr[0] & 0x1F; + if (out.fieldSize <= 28) { + if (out.fieldType == DataType::Unknown) { + out.fieldType = static_cast(dataPtr[1] + 7); + if ((out.fieldType <= DataType::Map) || (out.fieldType > DataType::Float) || (availSize < 3)) + return false; + offset += 2; + } + else { + offset += 1; + } + } + else if (out.fieldSize == 29) { + if (availSize < 2) return false; + out.fieldSize = dataPtr[1] + 29; + offset += 2; + } + else if (out.fieldSize == 30) { + if (availSize < 3) return false; + out.fieldSize = (dataPtr[1] << 8) + dataPtr[2] + 285; + offset += 3; + } + else if (out.fieldSize == 31) { + if (availSize < 4) return false; + out.fieldSize = (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3] + 65821; + offset += 4; + } + + return true; + } + + void Loader::fromBigEndian(uchar *buf, quint32 len) + { + if (__IS_LITTLE_ENDIAN__) + std::reverse(buf, buf + len); + } + + QVariant Loader::readMapValue(quint32 &offset, quint32 count) + { + QVariantHash map; + + for (quint32 i = 0; i < count; ++i) { + QVariant field = readDataField(offset); + if (field.userType() != QMetaType::QString) + return QVariant(); + + QString key = field.toString(); + field = readDataField(offset); + if (field.userType() == QVariant::Invalid) + return QVariant(); + + map[key] = field; + } + + return map; + } + + QVariant Loader::readArrayValue(quint32 &offset, quint32 count) + { + QVariantList array; + + for (quint32 i = 0; i < count; ++i) { + QVariant field = readDataField(offset); + if (field.userType() == QVariant::Invalid) + return QVariant(); + + array.append(field); + } + + return array; + } + +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + Q_IPV6ADDR createMappedAddress(quint32 ip4) + { + Q_IPV6ADDR ip6; + memset(&ip6, 0, sizeof(ip6)); + + int i; + for (i = 15; ip4 != 0; i--) { + ip6[i] = ip4 & 0xFF; + ip4 >>= 8; + } + + ip6[11] = 0xFF; + ip6[10] = 0xFF; + + return ip6; + } +#endif +} diff --git a/src/gui/geoip/geoipmanager.h b/src/core/net/private/geoipdatabase.h similarity index 66% rename from src/gui/geoip/geoipmanager.h rename to src/core/net/private/geoipdatabase.h index 36a115029..520be4d81 100644 --- a/src/gui/geoip/geoipmanager.h +++ b/src/core/net/private/geoipdatabase.h @@ -1,6 +1,6 @@ /* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,35 +24,37 @@ * modify file(s), you may extend this exception to your version of the file(s), * but you are not obligated to do so. If you do not wish to do so, delete this * exception statement from your version. - * - * Contact : chris@qbittorrent.org */ -#ifndef GEOIPMANAGER_H -#define GEOIPMANAGER_H +#ifndef GEOIPDATABASE_H +#define GEOIPDATABASE_H -#include -#include +#include -namespace libtorrent { - class session; -} +class QHostAddress; +class QString; +class QByteArray; +class QDateTime; -class GeoIPManager : public QObject { - Q_OBJECT +struct GeoIPData; +class GeoIPDatabase +{ public: - static void loadDatabase(libtorrent::session *s); - static QIcon CountryISOCodeToIcon(const QString &iso); - static QString CountryISOCodeToName(const QString &iso); + static GeoIPDatabase *load(const QString &filename, QString &error); + static GeoIPDatabase *load(const QByteArray &data, QString &error); + + ~GeoIPDatabase(); + + QString type() const; + quint16 ipVersion() const; + QDateTime buildEpoch() const; + QString lookup(const QHostAddress &hostAddr) const; private: - static QString geoipFolder(bool embedded=false); - static QString geoipDBpath(bool embedded=false); -#ifdef WITH_GEOIP_EMBEDDED - static void exportEmbeddedDb(); -#endif -}; + GeoIPDatabase(GeoIPData *geoIPData); + GeoIPData *m_geoIPData; +}; -#endif // GEOIP_H +#endif // GEOIPDATABASE_H diff --git a/src/gui/geoip/README b/src/gui/geoip/README deleted file mode 100644 index 9d001dca3..000000000 --- a/src/gui/geoip/README +++ /dev/null @@ -1,12 +0,0 @@ -If you wish to embed GeoIP database into qBittorrent executable, please download put GeoIP.dat in this folder. - -GeoIP Database can be downloaded from here: -* http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz -Note that the database should be uncompressed. - -Embedding GeoIP database into qBittorrent executable is advised for: -* Windows -* Mac OS X -* Linux distributions that don't provide GeoIP database in a separate package - -On Linux operating system, since this is not the default behavior, you also need to pass --with-geoip-database-embedded parameter to the configure file. diff --git a/src/gui/geoip/geoip.pri b/src/gui/geoip/geoip.pri deleted file mode 100644 index 519fd0ff0..000000000 --- a/src/gui/geoip/geoip.pri +++ /dev/null @@ -1,19 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += $$PWD/geoipmanager.h - -SOURCES += $$PWD/geoipmanager.cpp - -# Add GeoIP resource file if the GeoIP database -# should be embedded in qBittorrent executable -contains(DEFINES, WITH_GEOIP_EMBEDDED) { - exists("GeoIP.dat") { - message("GeoIP.dat was found in src/gui/geoip/.") - RESOURCES += $$PWD/geoip.qrc - } else { - DEFINES -= WITH_GEOIP_EMBEDDED - error("GeoIP.dat was not found in src/gui/geoip/ folder, please follow instructions in src/gui/geoip/README.") - } -} else { - message("GeoIP database will not be embedded in qBittorrent executable.") -} diff --git a/src/gui/geoip/geoip.qrc b/src/gui/geoip/geoip.qrc deleted file mode 100644 index afad6b74f..000000000 --- a/src/gui/geoip/geoip.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - GeoIP.dat - - diff --git a/src/gui/geoip/geoipmanager.cpp b/src/gui/geoip/geoipmanager.cpp deleted file mode 100644 index a22394d91..000000000 --- a/src/gui/geoip/geoipmanager.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * In addition, as a special exception, the copyright holders give permission to - * link this program with the OpenSSL project's "OpenSSL" library (or with - * modified versions of it that use the same license as the "OpenSSL" library), - * and distribute the linked executables. You must obey the GNU General Public - * License in all respects for all of the code used other than "OpenSSL". If you - * modify file(s), you may extend this exception to your version of the file(s), - * but you are not obligated to do so. If you do not wish to do so, delete this - * exception statement from your version. - * - * Contact : chris@qbittorrent.org - */ - -#include "geoipmanager.h" - -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * In addition, as a special exception, the copyright holders give permission to - * link this program with the OpenSSL project's "OpenSSL" library (or with - * modified versions of it that use the same license as the "OpenSSL" library), - * and distribute the linked executables. You must obey the GNU General Public - * License in all respects for all of the code used other than "OpenSSL". If you - * modify file(s), you may extend this exception to your version of the file(s), - * but you are not obligated to do so. If you do not wish to do so, delete this - * exception statement from your version. - * - * Contact : chris@qbittorrent.org - */ - -#include -#include -#include - -#include "core/utils/fs.h" -#include - -using namespace libtorrent; - -QString GeoIPManager::geoipFolder(bool embedded) { -#ifdef WITH_GEOIP_EMBEDDED - if (embedded) - return ":/geoip/"; - return Utils::Fs::QDesktopServicesDataLocation()+"geoip"+"/"; -#else - Q_UNUSED(embedded); - if (QFile::exists("/usr/local/share/GeoIP/GeoIP.dat")) - return "/usr/local/share/GeoIP/"; - if (QFile::exists("/var/lib/GeoIP/GeoIP.dat")) - return "/var/lib/GeoIP/"; - return "/usr/share/GeoIP/"; -#endif -} - -QString GeoIPManager::geoipDBpath(bool embedded) { - return geoipFolder(embedded)+"GeoIP.dat"; -} - -#ifdef WITH_GEOIP_EMBEDDED -void GeoIPManager::exportEmbeddedDb() { - if (!QFile::exists(geoipDBpath(false)) || QFile(geoipDBpath(false)).size() != QFile(geoipDBpath(true)).size()) { // Export is required - qDebug("A local Geoip database update is required, proceeding..."); - // Create geoip folder is necessary - QDir gfolder(geoipFolder(false)); - if (!gfolder.exists()) { - if (!gfolder.mkpath(geoipFolder(false))) { - std::cerr << "Failed to create geoip folder at " << qPrintable(geoipFolder(false)) << std::endl; - return; - } - } - // Remove destination files - if (QFile::exists(geoipDBpath(false))) - Utils::Fs::forceRemove(geoipDBpath(false)); - // Copy from executable to hard disk - qDebug("%s -> %s", qPrintable(geoipDBpath(true)), qPrintable(geoipDBpath(false))); - if (!QFile::copy(geoipDBpath(true), geoipDBpath(false))) { - std::cerr << "ERROR: Failed to copy geoip.dat from executable to hard disk" << std::endl; - } - qDebug("Local Geoip database was updated"); - } -} -#endif - -void GeoIPManager::loadDatabase(session *s) { -#ifdef WITH_GEOIP_EMBEDDED - exportEmbeddedDb(); -#endif - if (QFile::exists(geoipDBpath(false))) { - qDebug("Loading GeoIP database from %s...", qPrintable(geoipDBpath(false))); - s->load_country_db(geoipDBpath(false).toLocal8Bit().constData()); - } else { - qDebug("ERROR: Impossible to find local Geoip Database"); - } -} - -const char country_code[253][3] = -{ "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN", - "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB", - "BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO", - "BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD", - "CF","CG","CH","CI","CK","CL","CM","CN","CO","CR", - "CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO", - "DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ", - "FK","FM","FO","FR","FX","GA","GB","GD","GE","GF", - "GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT", - "GU","GW","GY","HK","HM","HN","HR","HT","HU","ID", - "IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO", - "JP","KE","KG","KH","KI","KM","KN","KP","KR","KW", - "KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT", - "LU","LV","LY","MA","MC","MD","MG","MH","MK","ML", - "MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV", - "MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI", - "NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF", - "PG","PH","PK","PL","PM","PN","PR","PS","PT","PW", - "PY","QA","RE","RO","RU","RW","SA","SB","SC","SD", - "SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO", - "SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH", - "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW", - "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE", - "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA", - "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE", - "BL","MF"}; - -static const uint num_countries = (unsigned)(sizeof(country_code)/sizeof(country_code[0])); - -const char * country_name[253] = -{"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles", - "Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados", - "Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia", - "Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the", - "Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica", - "Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic", - "Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji", - "Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana", - "Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala", - "Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia", - "Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan", - "Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", - "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", - "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali", - "Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives", - "Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", - "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia", - "Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau", - "Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan", - "Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname", - "Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand", - "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", - "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", - "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", - "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", - "Saint Barthelemy","Saint Martin"}; - -QString GeoIPManager::CountryISOCodeToName(const QString &iso) { - if (iso.isEmpty()) return "N/A"; - - for (uint i = 0; i < num_countries; ++i) { - if (iso == country_code[i]) { - return QLatin1String(country_name[i]); - } - } - qDebug("GeoIPManager: Country name resolution failed for: %s", qPrintable(iso)); - return "N/A"; -} - -// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm -QIcon GeoIPManager::CountryISOCodeToIcon(const QString &iso) { - if (iso.isEmpty() || (iso[0] == '!')) return QIcon(); - return QIcon(":/icons/flags/" + iso.toLower() + ".png"); -} - diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 43474c28c..40267792d 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -3,7 +3,6 @@ INCLUDEPATH += $$PWD include(lineedit/lineedit.pri) include(properties/properties.pri) include(rss/rss.pri) -include(geoip/geoip.pri) include(powermanagement/powermanagement.pri) unix:!macx:dbus: include(qtnotify/qtnotify.pri) diff --git a/src/gui/guiiconprovider.cpp b/src/gui/guiiconprovider.cpp index c042f2d7f..8fdd65a80 100644 --- a/src/gui/guiiconprovider.cpp +++ b/src/gui/guiiconprovider.cpp @@ -68,6 +68,12 @@ QIcon GuiIconProvider::getIcon(const QString &iconId) return QIcon(IconProvider::getIconPath(iconId)); } +QIcon GuiIconProvider::getFlagIcon(const QString &countryIsoCode) +{ + if (countryIsoCode.isEmpty()) return QIcon(); + return QIcon(":/icons/flags/" + countryIsoCode.toLower() + ".png"); +} + // Makes sure the icon is at least available in 16px and 24px size // It scales the icon from the theme if necessary // Otherwise, the UI looks broken if the icon is not available diff --git a/src/gui/guiiconprovider.h b/src/gui/guiiconprovider.h index 1b7beca1a..80d672ae2 100644 --- a/src/gui/guiiconprovider.h +++ b/src/gui/guiiconprovider.h @@ -44,6 +44,7 @@ public: static GuiIconProvider *instance(); QIcon getIcon(const QString &iconId); + QIcon getFlagIcon(const QString &countryIsoCode); QString getIconPath(const QString &iconId); private slots: diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index a84125f8c..7ea52a93b 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -42,7 +42,7 @@ #include "core/preferences.h" #include "core/logger.h" #include "propertieswidget.h" -#include "geoipmanager.h" +#include "core/net/geoipmanager.h" #include "peersadditiondlg.h" #include "speedlimitdlg.h" #include "guiiconprovider.h" @@ -303,10 +303,10 @@ QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHan m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port); m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip); if (m_displayFlags) { - const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country()); + const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country()); if (!ico.isNull()) { m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole); - const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country()); + const QString country_name = Net::GeoIPManager::CountryName(peer.country()); m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole); } else { m_missingFlags.insert(ip); @@ -331,10 +331,10 @@ void PeerListWidget::updatePeer(const QString &ip, BitTorrent::TorrentHandle *co QStandardItem *item = m_peerItems.value(ip); int row = item->row(); if (m_displayFlags) { - const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country()); + const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country()); if (!ico.isNull()) { m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole); - const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country()); + const QString country_name = Net::GeoIPManager::CountryName(peer.country()); m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole); m_missingFlags.remove(ip); } diff --git a/unixconf.pri b/unixconf.pri index 408ced423..2738b5952 100644 --- a/unixconf.pri +++ b/unixconf.pri @@ -91,10 +91,3 @@ nogui:systemd { # INSTALL target.path = $$PREFIX/bin/ INSTALLS += target - -!nogui { - # DEFINE added by configure - contains(DEFINES, WITH_GEOIP_EMBEDDED) { - message("You chose to embed GeoIP database in qBittorrent executable.") - } -} diff --git a/winconf.pri b/winconf.pri index 0cc074f83..88023a284 100644 --- a/winconf.pri +++ b/winconf.pri @@ -32,7 +32,6 @@ DEFINES += _CRT_SECURE_NO_DEPRECATE DEFINES += _SCL_SECURE_NO_DEPRECATE DEFINES += __USE_W32_SOCKETS DEFINES += _FILE_OFFSET_BITS=64 -DEFINES += WITH_SHIPPED_GEOIP_H CONFIG(debug, debug|release) { DEFINES += TORRENT_DEBUG @@ -49,6 +48,3 @@ win32-g++ { else { include(winconf-msvc.pri) } - -DEFINES += WITH_GEOIP_EMBEDDED -message("On Windows, GeoIP database must be embedded.")