Vladimir Golovnev (Glassez)
10 years ago
28 changed files with 1228 additions and 401 deletions
@ -0,0 +1,464 @@
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent. |
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> |
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org> |
||||
* |
||||
* 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 <QDebug> |
||||
#include <QFile> |
||||
#include <QDir> |
||||
#include <QHostAddress> |
||||
#include <QDateTime> |
||||
|
||||
#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<QString, QString> 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); |
||||
} |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent. |
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> |
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org> |
||||
* |
||||
* 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 <QObject> |
||||
#include <QCache> |
||||
|
||||
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<QHostAddress, QString> m_cache; |
||||
|
||||
static GeoIPManager *m_instance; |
||||
}; |
||||
} |
||||
|
||||
#endif // NET_GEOIPMANAGER_H
|
@ -0,0 +1,631 @@
@@ -0,0 +1,631 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent. |
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> |
||||
* |
||||
* 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 <QDebug> |
||||
#include <QCoreApplication> |
||||
#include <QVariant> |
||||
#include <QHash> |
||||
#include <QList> |
||||
#include <QScopedArrayPointer> |
||||
#include <QScopedPointer> |
||||
#include <QHostAddress> |
||||
#include <QDateTime> |
||||
#include <QFile> |
||||
|
||||
#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<Node> index; |
||||
QHash<quint32, QString> countries; |
||||
}; |
||||
|
||||
namespace |
||||
{ |
||||
const quint32 __ENDIAN_TEST__ = 0x00000001; |
||||
const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast<const uchar *>(&__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<typename T> |
||||
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<uchar *>(&value) + (sizeof(T) - len); |
||||
memcpy(dst, data, len); |
||||
fromBigEndian(reinterpret_cast<uchar *>(&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<quint32>(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<bool>((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<uchar> 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> 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<const uchar *>(data.constData()); |
||||
|
||||
QScopedPointer<GeoIPData> 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<quint16>(); |
||||
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<quint16>(); |
||||
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<quint32>(); |
||||
|
||||
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<quint32 *>(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<const char *>(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<quint32>(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<const char *>(m_data + locOffset), descr.fieldSize); |
||||
locOffset += descr.fieldSize; |
||||
break; |
||||
case DataType::Double: |
||||
if (descr.fieldSize == 8) |
||||
fieldValue = readPlainValue<double>(locOffset, descr.fieldSize); |
||||
else |
||||
qDebug() << "* Invalid field size for type: Double"; |
||||
break; |
||||
case DataType::Bytes: |
||||
fieldValue = QByteArray(reinterpret_cast<const char *>(m_data + locOffset), descr.fieldSize); |
||||
locOffset += descr.fieldSize; |
||||
break; |
||||
case DataType::Integer16: |
||||
fieldValue = readPlainValue<quint16>(locOffset, descr.fieldSize); |
||||
break; |
||||
case DataType::Integer32: |
||||
fieldValue = readPlainValue<quint32>(locOffset, descr.fieldSize); |
||||
break; |
||||
case DataType::Map: |
||||
fieldValue = readMapValue(locOffset, descr.fieldSize); |
||||
break; |
||||
case DataType::SignedInteger32: |
||||
fieldValue = readPlainValue<qint32>(locOffset, descr.fieldSize); |
||||
break; |
||||
case DataType::Integer64: |
||||
fieldValue = readPlainValue<quint64>(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<bool>(descr.fieldSize)); |
||||
break; |
||||
case DataType::Float: |
||||
if (descr.fieldSize == 4) |
||||
fieldValue = readPlainValue<float>(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<DataType>((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<DataType>(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 |
||||
} |
@ -1,12 +0,0 @@
@@ -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. |
@ -1,19 +0,0 @@
@@ -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.") |
||||
} |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
<RCC> |
||||
<qresource prefix="/geoip"> |
||||
<file>GeoIP.dat</file> |
||||
</qresource> |
||||
</RCC> |
@ -1,203 +0,0 @@
@@ -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 <QDir> |
||||
#include <QFile> |
||||
#include <QChar> |
||||
|
||||
#include "core/utils/fs.h" |
||||
#include <libtorrent/session.hpp> |
||||
|
||||
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"); |
||||
} |
||||
|
Loading…
Reference in new issue