Merge pull request #3186 from glassez/geoip

New GeoIP manager.
This commit is contained in:
sledgehammer999 2015-07-22 23:59:00 +03:00
commit 5b7ea0e611
41 changed files with 1476 additions and 548 deletions

32
configure vendored
View file

@ -716,7 +716,6 @@ enable_dependency_tracking
enable_silent_rules
with_qt5
with_libtorrent_rasterbar0_16
with_geoip_database_embedded
with_qtsingleapplication
with_qjson
enable_debug
@ -1387,10 +1386,6 @@ Optional Packages:
--with-libtorrent-rasterbar0.16
Compile using libtorrent-rasterbar 0.16.x series
(default=no)
--with-geoip-database-embedded
Embed the GeoIP database in the qBittorrent
executable (please follow instructions in
src/gui/geoip/README) (default=no)
--with-qtsingleapplication=[system|shipped]
Use the shipped qtsingleapplication library or the
system one (default=shipped)
@ -4194,15 +4189,6 @@ fi
# Check whether --with-geoip-database-embedded was given.
if test "${with_geoip_database_embedded+set}" = set; then :
withval=$with_geoip_database_embedded;
else
with_geoip_database_embedded=no
fi
# Check whether --with-qtsingleapplication was given.
if test "${with_qtsingleapplication+set}" = set; then :
withval=$with_qtsingleapplication;
@ -4429,7 +4415,6 @@ $as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
enable_qt_dbus=no
enable_geoip_database=no
QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui" ;; #(
*) :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gui" >&5
@ -5493,23 +5478,6 @@ $as_echo "$with_libtorrent_rasterbar0_16" >&6; }
as_fn_error $? "Unknown option \"$with_libtorrent_rasterbar0_16\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to embed the GeoIP database" >&5
$as_echo_n "checking whether to embed the GeoIP database... " >&6; }
case "x$with_geoip_database_embedded" in #(
"xno") :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
QBT_REMOVE_DEFINES="$QBT_REMOVE_DEFINES WITH_GEOIP_EMBEDDED" ;; #(
"xyes") :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
QBT_ADD_DEFINES="$QBT_ADD_DEFINES WITH_GEOIP_EMBEDDED" ;; #(
*) :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_geoip_database_embedded" >&5
$as_echo "$with_geoip_database_embedded" >&6; }
as_fn_error $? "Unknown option \"$with_geoip_database_embedded\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which qtsingleapplication to use" >&5
$as_echo_n "checking which qtsingleapplication to use... " >&6; }
case "x$with_qtsingleapplication" in #(

View file

@ -24,12 +24,6 @@ AC_ARG_WITH(libtorrent-rasterbar0.16,
[],
[with_libtorrent_rasterbar0_16=no])
AC_ARG_WITH(geoip-database-embedded,
[AS_HELP_STRING([--with-geoip-database-embedded],
[Embed the GeoIP database in the qBittorrent executable (please follow instructions in src/gui/geoip/README) (default=no)])],
[],
[with_geoip_database_embedded=no])
AC_ARG_WITH(qtsingleapplication,
[AS_HELP_STRING([--with-qtsingleapplication=@<:@system|shipped@:>@],
[Use the shipped qtsingleapplication library or the system one (default=shipped)])],
@ -198,17 +192,6 @@ AS_CASE(["x$with_libtorrent_rasterbar0_16"],
[AC_MSG_RESULT([$with_libtorrent_rasterbar0_16])
AC_MSG_ERROR([Unknown option "$with_libtorrent_rasterbar0_16". Use either "yes" or "no".])])
AC_MSG_CHECKING([whether to embed the GeoIP database])
AS_CASE(["x$with_geoip_database_embedded"],
["xno"],
[AC_MSG_RESULT([no])
QBT_REMOVE_DEFINES="$QBT_REMOVE_DEFINES WITH_GEOIP_EMBEDDED"],
["xyes"],
[AC_MSG_RESULT([yes])
QBT_ADD_DEFINES="$QBT_ADD_DEFINES WITH_GEOIP_EMBEDDED"],
[AC_MSG_RESULT([$with_geoip_database_embedded])
AC_MSG_ERROR([Unknown option "$with_geoip_database_embedded". Use either "yes" or "no".])])
AC_MSG_CHECKING([which qtsingleapplication to use])
AS_CASE(["x$with_qtsingleapplication"],
["xshipped"],

View file

@ -56,6 +56,3 @@ QMAKE_BUNDLE_DATA += qt_translations
ICON = $$DIST_PATH/qbittorrent_mac.icns
QMAKE_INFO_PLIST = $$DIST_PATH/Info.plist
DEFINES += WITH_GEOIP_EMBEDDED
message("On Mac OS X, GeoIP database must be embedded.")

View file

@ -13,8 +13,4 @@ LIBS += \
RC_FILE = qbittorrent_os2.rc
# LIBTORRENT DEFINES
DEFINES += WITH_SHIPPED_GEOIP_H
DEFINES += BOOST_ASIO_DYN_LINK
DEFINES += WITH_GEOIP_EMBEDDED
message("On eCS(OS/2), GeoIP database must be embedded.")

View file

@ -65,6 +65,7 @@
#include "core/scanfoldersmodel.h"
#include "core/net/smtp.h"
#include "core/net/downloadmanager.h"
#include "core/net/geoipmanager.h"
#include "core/bittorrent/session.h"
#include "core/bittorrent/torrenthandle.h"
@ -232,10 +233,14 @@ int Application::exec(const QStringList &params)
#else
GuiIconProvider::initInstance();
#endif
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()));
#ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::initInstance();
#endif
ScanFoldersModel::initInstance(this);
#ifndef DISABLE_WEBUI
@ -446,6 +451,9 @@ void Application::cleanup()
ScanFoldersModel::freeInstance();
BitTorrent::Session::freeInstance();
#ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::freeInstance();
#endif
Preferences::freeInstance();
Logger::freeInstance();
IconProvider::freeInstance();

View file

@ -28,6 +28,7 @@
#include <libtorrent/version.hpp>
#include "core/net/geoipmanager.h"
#include "core/utils/string.h"
#include "peerinfo.h"
@ -69,11 +70,12 @@ bool PeerInfo::fromLSD() const
return (m_nativeInfo.source & libt::peer_info::lsd);
}
#ifndef DISABLE_COUNTRIES_RESOLUTION
QString PeerInfo::country() const
{
return QString(QByteArray(m_nativeInfo.country, 2));
return Net::GeoIPManager::instance()->lookup(address().ip);
}
#endif
bool PeerInfo::isInteresting() const
{

View file

@ -89,7 +89,9 @@ namespace BitTorrent
qlonglong totalDownload() const;
QBitArray pieces() const;
QString connectionType() const;
#ifndef DISABLE_COUNTRIES_RESOLUTION
QString country() const;
#endif
private:
libtorrent::peer_info m_nativeInfo;

View file

@ -50,9 +50,6 @@ struct SessionPrivate
virtual bool isTempPathEnabled() const = 0;
virtual bool isAppendExtensionEnabled() const = 0;
virtual bool useAppendLabelToSavePath() const = 0;
#ifndef DISABLE_COUNTRIES_RESOLUTION
virtual bool isResolveCountriesEnabled() const = 0;
#endif
virtual QString defaultSavePath() const = 0;
virtual QString tempPath() const = 0;
virtual qreal globalMaxRatio() const = 0;

View file

@ -42,9 +42,6 @@ struct TorrentHandlePrivate
virtual void handleDefaultSavePathChanged() = 0;
virtual void handleTempPathChanged() = 0;
virtual void handleAppendExtensionToggled() = 0;
#ifndef DISABLE_COUNTRIES_RESOLUTION
virtual void handleResolveCountriesToggled() = 0;
#endif
protected:
~TorrentHandlePrivate() {}

View file

@ -63,7 +63,7 @@ using namespace BitTorrent;
//#include <libtorrent/extensions/metadata_transfer.hpp>
#ifndef DISABLE_COUNTRIES_RESOLUTION
#include "geoipmanager.h"
#include "core/net/geoipmanager.h"
#endif
#include "core/utils/misc.h"
@ -141,10 +141,6 @@ Session::Session(QObject *parent)
, m_globalMaxRatio(-1)
, m_numResumeData(0)
, m_extraLimit(0)
#ifndef DISABLE_COUNTRIES_RESOLUTION
, m_geoipDBLoaded(false)
, m_resolveCountries(false)
#endif
, m_appendLabelToSavePath(false)
, m_appendExtension(false)
, m_refreshInterval(0)
@ -252,13 +248,6 @@ bool Session::useAppendLabelToSavePath() const
return m_appendLabelToSavePath;
}
#ifndef DISABLE_COUNTRIES_RESOLUTION
bool Session::isResolveCountriesEnabled() const
{
return m_resolveCountries;
}
#endif
QString Session::defaultSavePath() const
{
return m_defaultSavePath;
@ -577,25 +566,6 @@ void Session::configure()
delete m_bwScheduler;
}
#ifndef DISABLE_COUNTRIES_RESOLUTION
// Resolve countries
qDebug("Loading country resolution settings");
const bool new_resolv_countries = pref->resolvePeerCountries();
if (m_resolveCountries != new_resolv_countries) {
qDebug("in country resolution settings");
m_resolveCountries = new_resolv_countries;
if (m_resolveCountries && !m_geoipDBLoaded) {
qDebug("Loading geoip database");
GeoIPManager::loadDatabase(m_nativeSession);
m_geoipDBLoaded = true;
}
// Update torrent handles
foreach (TorrentHandlePrivate *const torrent, m_torrents)
torrent->handleResolveCountriesToggled();
}
#endif
Logger* const logger = Logger::instance();
// * Session settings
@ -1049,7 +1019,7 @@ bool Session::addTorrent(QString source, const AddTorrentParams &params)
if (Utils::Misc::isUrl(source)) {
Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
// Launch downloader
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, 10485760 /* 10MB */);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleDownloadFinished(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailed(QString, QString)));
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString)));

View file

@ -283,9 +283,6 @@ namespace BitTorrent
bool isTempPathEnabled() const;
bool isAppendExtensionEnabled() const;
bool useAppendLabelToSavePath() const;
#ifndef DISABLE_COUNTRIES_RESOLUTION
bool isResolveCountriesEnabled() const;
#endif
QString defaultSavePath() const;
QString tempPath() const;
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
@ -327,10 +324,6 @@ namespace BitTorrent
qreal m_globalMaxRatio;
int m_numResumeData;
int m_extraLimit;
#ifndef DISABLE_COUNTRIES_RESOLUTION
bool m_geoipDBLoaded;
bool m_resolveCountries;
#endif
bool m_appendLabelToSavePath;
bool m_appendExtension;
uint m_refreshInterval;

View file

@ -205,10 +205,6 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
{
initialize();
#ifndef DISABLE_COUNTRIES_RESOLUTION
resolveCountries(m_session->isResolveCountriesEnabled());
#endif
if (!data.resumed) {
setSequentialDownload(data.sequential);
if (hasMetadata()) {
@ -1644,13 +1640,6 @@ void TorrentHandle::handleAppendExtensionToggled()
removeExtensionsFromIncompleteFiles();
}
#ifndef DISABLE_COUNTRIES_RESOLUTION
void TorrentHandle::handleResolveCountriesToggled()
{
resolveCountries(m_session->isResolveCountriesEnabled());
}
#endif
void TorrentHandle::handleAlert(libtorrent::alert *a)
{
switch (a->type()) {
@ -1865,11 +1854,6 @@ QString TorrentHandle::toMagnetUri() const
return Utils::String::fromStdString(libt::make_magnet_uri(m_nativeHandle));
}
void TorrentHandle::resolveCountries(bool b)
{
SAFE_CALL(resolve_countries, b);
}
void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
{
qDebug() << Q_FUNC_INFO;

View file

@ -316,9 +316,6 @@ namespace BitTorrent
void handleDefaultSavePathChanged();
void handleTempPathChanged();
void handleAppendExtensionToggled();
#ifndef DISABLE_COUNTRIES_RESOLUTION
void handleResolveCountriesToggled();
#endif
void handleStorageMovedAlert(libtorrent::storage_moved_alert *p);
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p);
@ -341,7 +338,6 @@ namespace BitTorrent
bool useTempPath() const;
QString nativeActualSavePath() const;
void resolveCountries(bool b);
void adjustSavePath();
void adjustActualSavePath();
void adjustActualSavePath_impl();

View file

@ -16,9 +16,11 @@ HEADERS += \
$$PWD/net/dnsupdater.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/downloadhandler.h \
$$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/session.h \
$$PWD/bittorrent/sessionstatus.h \
@ -37,6 +39,7 @@ HEADERS += \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
$$PWD/utils/misc.h \
$$PWD/utils/string.h \
$$PWD/unicodestrings.h \
@ -57,9 +60,11 @@ SOURCES += \
$$PWD/net/dnsupdater.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/downloadhandler.cpp \
$$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \
$$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/sessionstatus.cpp \
@ -76,6 +81,7 @@ SOURCES += \
$$PWD/bittorrent/private/filterparserthread.cpp \
$$PWD/bittorrent/private/statistics.cpp \
$$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/string.cpp \
$$PWD/torrentfilter.cpp \

View file

@ -29,11 +29,9 @@
* Contact : chris@qbittorrent.org
*/
#include <zlib.h>
#include "core/utils/gzip.h"
#include "responsegenerator.h"
bool gCompress(QByteArray data, QByteArray& dest_buffer);
using namespace Http;
QByteArray ResponseGenerator::generate(Response response)
@ -44,7 +42,7 @@ QByteArray ResponseGenerator::generate(Response response)
// So we only benefit from gzip if the message is bigger than 23+26 = 49
// If the message is smaller than 49 bytes we actually send MORE data if we gzip
QByteArray dest_buf;
if ((response.content.size() > 49) && (gCompress(response.content, dest_buf)))
if ((response.content.size() > 49) && (Utils::Gzip::compress(response.content, dest_buf)))
response.content = dest_buf;
else
response.headers.remove(HEADER_CONTENT_ENCODING);
@ -67,58 +65,3 @@ QByteArray ResponseGenerator::generate(Response response)
return ret.toUtf8() + response.content;
}
bool gCompress(QByteArray data, QByteArray& dest_buffer)
{
static const int BUFSIZE = 128 * 1024;
char tmp_buf[BUFSIZE];
int ret;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.next_in = reinterpret_cast<unsigned char*>(data.data());
strm.avail_in = data.length();
strm.next_out = reinterpret_cast<unsigned char*>(tmp_buf);
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_buffer.append(tmp_buf, BUFSIZE);
strm.next_out = reinterpret_cast<unsigned char*>(tmp_buf);
strm.avail_out = BUFSIZE;
}
}
int deflate_res = Z_OK;
while (deflate_res == Z_OK) {
if (strm.avail_out == 0) {
dest_buffer.append(tmp_buf, BUFSIZE);
strm.next_out = reinterpret_cast<unsigned char*>(tmp_buf);
strm.avail_out = BUFSIZE;
}
deflate_res = deflate(&strm, Z_FINISH);
}
if (deflate_res != Z_STREAM_END)
return false;
dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out);
deflateEnd(&strm);
return true;
}

View file

@ -36,23 +36,23 @@
#include <QUrl>
#include <QDebug>
#include <zlib.h>
#include "core/utils/fs.h"
#include "core/utils/gzip.h"
#include "core/utils/misc.h"
#include "downloadmanager.h"
#include "downloadhandler.h"
static QString errorCodeToString(QNetworkReply::NetworkError status);
static QByteArray gUncompress(Bytef *inData, uInt len);
using namespace Net;
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, qint64 limit)
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
: QObject(manager)
, m_reply(reply)
, m_manager(manager)
, m_saveToFile(saveToFile)
, m_sizeLimit(limit)
, m_handleRedirectToMagnet(handleRedirectToMagnet)
, m_url(reply->url().toString())
{
init();
@ -90,11 +90,23 @@ void DownloadHandler::processFinishedDownload()
}
else {
// Success
QByteArray replyData = m_reply->readAll();
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
// uncompress gzip reply
Utils::Gzip::uncompress(replyData, replyData);
}
if (m_saveToFile) {
QString filePath;
if (saveToFile(filePath))
if (saveToFile(replyData, filePath))
emit downloadFinished(m_url, filePath);
else
emit downloadFailed(m_url, tr("I/O Error"));
}
else {
emit downloadFinished(m_url, replyData);
}
this->deleteLater();
}
}
@ -128,7 +140,7 @@ void DownloadHandler::init()
connect(m_reply, SIGNAL(finished()), this, SLOT(processFinishedDownload()));
}
bool DownloadHandler::saveToFile(QString &filePath)
bool DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath)
{
QTemporaryFile *tmpfile = new QTemporaryFile;
if (!tmpfile->open()) {
@ -140,11 +152,6 @@ bool DownloadHandler::saveToFile(QString &filePath)
filePath = tmpfile->fileName();
qDebug("Temporary filename is: %s", qPrintable(filePath));
if (m_reply->isOpen() || m_reply->open(QIODevice::ReadOnly)) {
QByteArray replyData = m_reply->readAll();
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
// uncompress gzip reply
replyData = gUncompress(reinterpret_cast<unsigned char*>(replyData.data()), static_cast<uInt>(replyData.length()));
}
tmpfile->write(replyData);
tmpfile->close();
// XXX: tmpfile needs to be deleted on Windows before using the file
@ -173,14 +180,20 @@ void DownloadHandler::handleRedirection(QUrl newUrl)
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
qDebug("Magnet redirect detected.");
m_reply->abort();
if (m_handleRedirectToMagnet)
emit redirectedToMagnet(m_url, newUrlString);
else
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI."));
this->deleteLater();
}
else {
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_sizeLimit);
m_reply->deleteLater();
m_reply = tmp->m_reply;
m_saveToFile = tmp->m_saveToFile;
m_sizeLimit = tmp->m_sizeLimit;
m_handleRedirectToMagnet = tmp->m_handleRedirectToMagnet;
init();
tmp->m_reply = 0;
delete tmp;
@ -236,55 +249,3 @@ QString errorCodeToString(QNetworkReply::NetworkError status)
return QObject::tr("Unknown error");
}
}
QByteArray gUncompress(Bytef *inData, uInt len)
{
if (len <= 4) {
qWarning("gUncompress: Input data is truncated");
return QByteArray();
}
QByteArray result;
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 = len;
strm.next_in = inData;
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 QByteArray();
// run inflate()
do {
strm.avail_out = CHUNK_SIZE;
strm.next_out = reinterpret_cast<unsigned char*>(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:
(void) inflateEnd(&strm);
return QByteArray();
}
result.append(out, CHUNK_SIZE - strm.avail_out);
}
while (!strm.avail_out);
// clean up and return
inflateEnd(&strm);
return result;
}

View file

@ -47,12 +47,13 @@ namespace Net
Q_OBJECT
public:
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, qint64 limit = 0);
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
~DownloadHandler();
QString url() const;
signals:
void downloadFinished(const QString &url, const QByteArray &data);
void downloadFinished(const QString &url, const QString &filePath);
void downloadFailed(const QString &url, const QString &reason);
void redirectedToMagnet(const QString &url, const QString &magnetUri);
@ -63,12 +64,14 @@ namespace Net
private:
void init();
bool saveToFile(QString &filePath);
bool saveToFile(const QByteArray &replyData, QString &filePath);
void handleRedirection(QUrl newUrl);
QNetworkReply *m_reply;
DownloadManager *m_manager;
bool m_saveToFile;
qint64 m_sizeLimit;
bool m_handleRedirectToMagnet;
QString m_url;
};
}

View file

@ -75,7 +75,7 @@ DownloadManager *DownloadManager::instance()
return m_instance;
}
DownloadHandler *DownloadManager::downloadUrl(const QString &url, qint64 limit)
DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
{
// Update proxy settings
applyProxySettings();
@ -91,7 +91,7 @@ DownloadHandler *DownloadManager::downloadUrl(const QString &url, qint64 limit)
qDebug("Downloading %s...", request.url().toEncoded().data());
// accept gzip
request.setRawHeader("Accept-Encoding", "gzip");
return new DownloadHandler(m_networkManager.get(request), this, limit);
return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet);
}
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QString &url) const

View file

@ -53,7 +53,7 @@ namespace Net
static void freeInstance();
static DownloadManager *instance();
DownloadHandler *downloadUrl(const QString &url, qint64 limit = 0);
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
QList<QNetworkCookie> cookiesForUrl(const QString &url) const;
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);

View file

@ -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);
}

View file

@ -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

View file

@ -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
}

View file

@ -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

142
src/core/utils/gzip.cpp Normal file
View file

@ -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;
}

View file

@ -1,6 +1,7 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* 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
@ -24,35 +25,20 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef GEOIPMANAGER_H
#define GEOIPMANAGER_H
#ifndef UTILS_GZIP_H
#define UTILS_GZIP_H
#include <QString>
#include <QIcon>
class QByteArray;
namespace libtorrent {
class session;
namespace Utils
{
namespace Gzip
{
bool compress(QByteArray src, QByteArray &dest);
bool uncompress(QByteArray src, QByteArray &dest);
}
}
class GeoIPManager : public QObject {
Q_OBJECT
public:
static void loadDatabase(libtorrent::session *s);
static QIcon CountryISOCodeToIcon(const QString &iso);
static QString CountryISOCodeToName(const QString &iso);
private:
static QString geoipFolder(bool embedded=false);
static QString geoipDBpath(bool embedded=false);
#ifdef WITH_GEOIP_EMBEDDED
static void exportEmbeddedDb();
#endif
};
#endif // GEOIP_H
#endif // UTILS_GZIP_H

View file

@ -135,7 +135,7 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent)
if (Utils::Misc::isUrl(source)) {
// Launch downloader
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, 10485760 /* 10MB */);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), dlg, SLOT(handleDownloadFinished(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), dlg, SLOT(handleDownloadFailed(QString, QString)));
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), dlg, SLOT(handleRedirectedToMagnet(QString, QString)));

View file

@ -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.

View 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.")
}

View file

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/geoip">
<file>GeoIP.dat</file>
</qresource>
</RCC>

View file

@ -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");
}

View file

@ -3,7 +3,6 @@ INCLUDEPATH += $$PWD
include(lineedit/lineedit.pri)
include(properties/properties.pri)
include(rss/rss.pri)
include(geoip/geoip.pri)
include(powermanagement/powermanagement.pri)
unix:!macx:dbus: include(qtnotify/qtnotify.pri)

View file

@ -68,6 +68,12 @@ QIcon GuiIconProvider::getIcon(const QString &iconId)
return QIcon(IconProvider::getIconPath(iconId));
}
QIcon GuiIconProvider::getFlagIcon(const QString &countryIsoCode)
{
if (countryIsoCode.isEmpty()) return QIcon();
return QIcon(":/icons/flags/" + countryIsoCode.toLower() + ".png");
}
// Makes sure the icon is at least available in 16px and 24px size
// It scales the icon from the theme if necessary
// Otherwise, the UI looks broken if the icon is not available

View file

@ -44,6 +44,7 @@ public:
static GuiIconProvider *instance();
QIcon getIcon(const QString &iconId);
QIcon getFlagIcon(const QString &countryIsoCode);
QString getIconPath(const QString &iconId);
private slots:

View file

@ -1626,7 +1626,7 @@ void MainWindow::installPython()
{
setCursor(QCursor(Qt::WaitCursor));
// Download python
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl("https://www.python.org/ftp/python/3.4.3/python-3.4.3.msi");
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl("https://www.python.org/ftp/python/3.4.3/python-3.4.3.msi", true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(pythonDownloadSuccess(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(pythonDownloadFailure(QString, QString)));
}

View file

@ -42,7 +42,7 @@
#include "core/preferences.h"
#include "core/logger.h"
#include "propertieswidget.h"
#include "geoipmanager.h"
#include "core/net/geoipmanager.h"
#include "peersadditiondlg.h"
#include "speedlimitdlg.h"
#include "guiiconprovider.h"
@ -141,8 +141,14 @@ void PeerListWidget::updatePeerCountryResolutionState()
{
if (Preferences::instance()->resolvePeerCountries() != m_displayFlags) {
m_displayFlags = !m_displayFlags;
if (m_displayFlags)
if (m_displayFlags) {
loadPeers(m_properties->getCurrentTorrent());
showColumn(PeerListDelegate::COUNTRY);
resizeColumnToContents(PeerListDelegate::COUNTRY);
}
else {
hideColumn(PeerListDelegate::COUNTRY);
}
}
}
@ -303,10 +309,10 @@ QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHan
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip);
if (m_displayFlags) {
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country());
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country());
const QString country_name = Net::GeoIPManager::CountryName(peer.country());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole);
} else {
m_missingFlags.insert(ip);
@ -331,10 +337,10 @@ void PeerListWidget::updatePeer(const QString &ip, BitTorrent::TorrentHandle *co
QStandardItem *item = m_peerItems.value(ip);
int row = item->row();
if (m_displayFlags) {
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country());
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country());
const QString country_name = Net::GeoIPManager::CountryName(peer.country());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole);
m_missingFlags.remove(ip);
}

View file

@ -59,7 +59,7 @@ QStringList TrackersAdditionDlg::newTrackers() const
void TrackersAdditionDlg::on_uTorrentListButton_clicked()
{
uTorrentListButton->setEnabled(false);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(QString("http://www.torrentz.com/announce_%1").arg(m_torrent->hash()));
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(QString("http://www.torrentz.com/announce_%1").arg(m_torrent->hash()), true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(parseUTorrentList(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(QString, QString)));
//Just to show that it takes times

View file

@ -66,7 +66,7 @@ RssFeed::RssFeed(RssManager* manager, RssFolder* parent, const QString& url):
connect(manager->rssParser(), SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleFeedParsingFinished(QString,QString)));
// Download the RSS Feed icon
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl());
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl(), true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
m_iconUrl = handler->url();
@ -167,7 +167,7 @@ bool RssFeed::refresh()
}
m_loading = true;
// Download the RSS again
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url, true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
m_url = handler->url(); // sync URL encoding

View file

@ -474,7 +474,7 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash)
trackerItem = new QListWidgetItem();
trackerItem->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("network-server"));
Net::DownloadHandler *h = Net::DownloadManager::instance()->downloadUrl(QString("http://%1/favicon.ico").arg(host));
Net::DownloadHandler *h = Net::DownloadManager::instance()->downloadUrl(QString("http://%1/favicon.ico").arg(host), true);
connect(h, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFavicoDownload(QString, QString)));
connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(QString, QString)));
}
@ -634,7 +634,7 @@ void TrackerFiltersList::handleFavicoDownload(const QString& url, const QString&
if (url.endsWith(".ico", Qt::CaseInsensitive)) {
Logger::instance()->addMessage(tr("Couldn't decode favicon for URL `%1`. Trying to download favicon in PNG format.").arg(url),
Log::WARNING);
Net::DownloadHandler *h = Net::DownloadManager::instance()->downloadUrl(url.left(url.size() - 4) + ".png");
Net::DownloadHandler *h = Net::DownloadManager::instance()->downloadUrl(url.left(url.size() - 4) + ".png", true);
connect(h, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFavicoDownload(QString, QString)));
connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(QString, QString)));
}

View file

@ -429,7 +429,7 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
void engineSelectDlg::downloadFromUrl(const QString &url)
{
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(url);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(url, true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
}

View file

@ -91,10 +91,3 @@ nogui:systemd {
# INSTALL
target.path = $$PREFIX/bin/
INSTALLS += target
!nogui {
# DEFINE added by configure
contains(DEFINES, WITH_GEOIP_EMBEDDED) {
message("You chose to embed GeoIP database in qBittorrent executable.")
}
}

View file

@ -32,7 +32,6 @@ DEFINES += _CRT_SECURE_NO_DEPRECATE
DEFINES += _SCL_SECURE_NO_DEPRECATE
DEFINES += __USE_W32_SOCKETS
DEFINES += _FILE_OFFSET_BITS=64
DEFINES += WITH_SHIPPED_GEOIP_H
CONFIG(debug, debug|release) {
DEFINES += TORRENT_DEBUG
@ -49,6 +48,3 @@ win32-g++ {
else {
include(winconf-msvc.pri)
}
DEFINES += WITH_GEOIP_EMBEDDED
message("On Windows, GeoIP database must be embedded.")