sledgehammer999
10 years ago
41 changed files with 1476 additions and 548 deletions
@ -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 @@ |
|||||||
|
/*
|
||||||
|
* 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 @@ |
|||||||
|
/*
|
||||||
|
* 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 |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/*
|
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GEOIPDATABASE_H |
||||||
|
#define GEOIPDATABASE_H |
||||||
|
|
||||||
|
#include <QtGlobal> |
||||||
|
|
||||||
|
class QHostAddress; |
||||||
|
class QString; |
||||||
|
class QByteArray; |
||||||
|
class QDateTime; |
||||||
|
|
||||||
|
struct GeoIPData; |
||||||
|
|
||||||
|
class GeoIPDatabase |
||||||
|
{ |
||||||
|
public: |
||||||
|
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: |
||||||
|
GeoIPDatabase(GeoIPData *geoIPData); |
||||||
|
|
||||||
|
GeoIPData *m_geoIPData; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // GEOIPDATABASE_H
|
@ -0,0 +1,142 @@ |
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent. |
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> |
||||||
|
* Copyright (C) 2006 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 <QByteArray> |
||||||
|
#include <zlib.h> |
||||||
|
|
||||||
|
#include "gzip.h" |
||||||
|
|
||||||
|
bool Utils::Gzip::compress(QByteArray src, QByteArray &dest) |
||||||
|
{ |
||||||
|
static const int BUFSIZE = 128 * 1024; |
||||||
|
char tmpBuf[BUFSIZE]; |
||||||
|
int ret; |
||||||
|
|
||||||
|
dest.clear(); |
||||||
|
|
||||||
|
z_stream strm; |
||||||
|
strm.zalloc = Z_NULL; |
||||||
|
strm.zfree = Z_NULL; |
||||||
|
strm.opaque = Z_NULL; |
||||||
|
strm.next_in = reinterpret_cast<uchar *>(src.data()); |
||||||
|
strm.avail_in = src.length(); |
||||||
|
strm.next_out = reinterpret_cast<uchar *>(tmpBuf); |
||||||
|
strm.avail_out = BUFSIZE; |
||||||
|
|
||||||
|
// windowBits = 15 + 16 to enable gzip
|
||||||
|
// From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits
|
||||||
|
// to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
|
||||||
|
ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); |
||||||
|
|
||||||
|
if (ret != Z_OK) |
||||||
|
return false; |
||||||
|
|
||||||
|
while (strm.avail_in != 0) { |
||||||
|
ret = deflate(&strm, Z_NO_FLUSH); |
||||||
|
if (ret != Z_OK) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (strm.avail_out == 0) { |
||||||
|
dest.append(tmpBuf, BUFSIZE); |
||||||
|
strm.next_out = reinterpret_cast<uchar *>(tmpBuf); |
||||||
|
strm.avail_out = BUFSIZE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int deflateRes = Z_OK; |
||||||
|
while (deflateRes == Z_OK) { |
||||||
|
if (strm.avail_out == 0) { |
||||||
|
dest.append(tmpBuf, BUFSIZE); |
||||||
|
strm.next_out = reinterpret_cast<uchar *>(tmpBuf); |
||||||
|
strm.avail_out = BUFSIZE; |
||||||
|
} |
||||||
|
|
||||||
|
deflateRes = deflate(&strm, Z_FINISH); |
||||||
|
} |
||||||
|
|
||||||
|
if (deflateRes != Z_STREAM_END) |
||||||
|
return false; |
||||||
|
|
||||||
|
dest.append(tmpBuf, BUFSIZE - strm.avail_out); |
||||||
|
deflateEnd(&strm); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool Utils::Gzip::uncompress(QByteArray src, QByteArray &dest) |
||||||
|
{ |
||||||
|
dest.clear(); |
||||||
|
|
||||||
|
if (src.size() <= 4) { |
||||||
|
qWarning("uncompress: Input data is truncated"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
z_stream strm; |
||||||
|
static const int CHUNK_SIZE = 1024; |
||||||
|
char out[CHUNK_SIZE]; |
||||||
|
|
||||||
|
/* allocate inflate state */ |
||||||
|
strm.zalloc = Z_NULL; |
||||||
|
strm.zfree = Z_NULL; |
||||||
|
strm.opaque = Z_NULL; |
||||||
|
strm.avail_in = static_cast<uint>(src.size()); |
||||||
|
strm.next_in = reinterpret_cast<uchar *>(src.data()); |
||||||
|
|
||||||
|
const int windowBits = 15; |
||||||
|
const int ENABLE_ZLIB_GZIP = 32; |
||||||
|
|
||||||
|
int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding
|
||||||
|
if (ret != Z_OK) |
||||||
|
return false; |
||||||
|
|
||||||
|
// run inflate()
|
||||||
|
do { |
||||||
|
strm.avail_out = CHUNK_SIZE; |
||||||
|
strm.next_out = reinterpret_cast<uchar *>(out); |
||||||
|
|
||||||
|
ret = inflate(&strm, Z_NO_FLUSH); |
||||||
|
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered
|
||||||
|
|
||||||
|
switch (ret) { |
||||||
|
case Z_NEED_DICT: |
||||||
|
case Z_DATA_ERROR: |
||||||
|
case Z_MEM_ERROR: |
||||||
|
inflateEnd(&strm); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
dest.append(out, CHUNK_SIZE - strm.avail_out); |
||||||
|
} |
||||||
|
while (!strm.avail_out); |
||||||
|
|
||||||
|
// clean up and return
|
||||||
|
inflateEnd(&strm); |
||||||
|
return true; |
||||||
|
} |
@ -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 @@ |
|||||||
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 @@ |
|||||||
<RCC> |
|
||||||
<qresource prefix="/geoip"> |
|
||||||
<file>GeoIP.dat</file> |
|
||||||
</qresource> |
|
||||||
</RCC> |
|
@ -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