Browse Source

Merge pull request #3186 from glassez/geoip

New GeoIP manager.
adaptive-webui-19844
sledgehammer999 10 years ago
parent
commit
5b7ea0e611
  1. 32
      configure
  2. 17
      configure.ac
  3. 3
      macxconf.pri
  4. 4
      os2conf.pri
  5. 8
      src/app/application.cpp
  6. 6
      src/core/bittorrent/peerinfo.cpp
  7. 2
      src/core/bittorrent/peerinfo.h
  8. 3
      src/core/bittorrent/private/sessionprivate.h
  9. 3
      src/core/bittorrent/private/torrenthandleprivate.h
  10. 34
      src/core/bittorrent/session.cpp
  11. 7
      src/core/bittorrent/session.h
  12. 16
      src/core/bittorrent/torrenthandle.cpp
  13. 4
      src/core/bittorrent/torrenthandle.h
  14. 6
      src/core/core.pri
  15. 61
      src/core/http/responsegenerator.cpp
  16. 97
      src/core/net/downloadhandler.cpp
  17. 7
      src/core/net/downloadhandler.h
  18. 4
      src/core/net/downloadmanager.cpp
  19. 2
      src/core/net/downloadmanager.h
  20. 464
      src/core/net/geoipmanager.cpp
  21. 77
      src/core/net/geoipmanager.h
  22. 631
      src/core/net/private/geoipdatabase.cpp
  23. 60
      src/core/net/private/geoipdatabase.h
  24. 142
      src/core/utils/gzip.cpp
  25. 42
      src/core/utils/gzip.h
  26. 2
      src/gui/addnewtorrentdialog.cpp
  27. 12
      src/gui/geoip/README
  28. 19
      src/gui/geoip/geoip.pri
  29. 5
      src/gui/geoip/geoip.qrc
  30. 203
      src/gui/geoip/geoipmanager.cpp
  31. 1
      src/gui/gui.pri
  32. 6
      src/gui/guiiconprovider.cpp
  33. 1
      src/gui/guiiconprovider.h
  34. 2
      src/gui/mainwindow.cpp
  35. 18
      src/gui/properties/peerlistwidget.cpp
  36. 2
      src/gui/properties/trackersadditiondlg.cpp
  37. 4
      src/gui/rss/rssfeed.cpp
  38. 4
      src/gui/transferlistfilterswidget.cpp
  39. 2
      src/searchengine/engineselectdlg.cpp
  40. 7
      unixconf.pri
  41. 4
      winconf.pri

32
configure vendored

@ -716,7 +716,6 @@ enable_dependency_tracking
enable_silent_rules enable_silent_rules
with_qt5 with_qt5
with_libtorrent_rasterbar0_16 with_libtorrent_rasterbar0_16
with_geoip_database_embedded
with_qtsingleapplication with_qtsingleapplication
with_qjson with_qjson
enable_debug enable_debug
@ -1387,10 +1386,6 @@ Optional Packages:
--with-libtorrent-rasterbar0.16 --with-libtorrent-rasterbar0.16
Compile using libtorrent-rasterbar 0.16.x series Compile using libtorrent-rasterbar 0.16.x series
(default=no) (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] --with-qtsingleapplication=[system|shipped]
Use the shipped qtsingleapplication library or the Use the shipped qtsingleapplication library or the
system one (default=shipped) 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. # Check whether --with-qtsingleapplication was given.
if test "${with_qtsingleapplication+set}" = set; then : if test "${with_qtsingleapplication+set}" = set; then :
withval=$with_qtsingleapplication; withval=$with_qtsingleapplication;
@ -4429,7 +4415,6 @@ $as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; } $as_echo "no" >&6; }
enable_qt_dbus=no enable_qt_dbus=no
enable_geoip_database=no
QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui" ;; #( QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui" ;; #(
*) : *) :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gui" >&5 { $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 ;; as_fn_error $? "Unknown option \"$with_libtorrent_rasterbar0_16\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
esac 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 "$as_me:${as_lineno-$LINENO}: checking which qtsingleapplication to use" >&5
$as_echo_n "checking which qtsingleapplication to use... " >&6; } $as_echo_n "checking which qtsingleapplication to use... " >&6; }
case "x$with_qtsingleapplication" in #( case "x$with_qtsingleapplication" in #(

17
configure.ac

@ -24,12 +24,6 @@ AC_ARG_WITH(libtorrent-rasterbar0.16,
[], [],
[with_libtorrent_rasterbar0_16=no]) [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, AC_ARG_WITH(qtsingleapplication,
[AS_HELP_STRING([--with-qtsingleapplication=@<:@system|shipped@:>@], [AS_HELP_STRING([--with-qtsingleapplication=@<:@system|shipped@:>@],
[Use the shipped qtsingleapplication library or the system one (default=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_RESULT([$with_libtorrent_rasterbar0_16])
AC_MSG_ERROR([Unknown option "$with_libtorrent_rasterbar0_16". Use either "yes" or "no".])]) 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]) AC_MSG_CHECKING([which qtsingleapplication to use])
AS_CASE(["x$with_qtsingleapplication"], AS_CASE(["x$with_qtsingleapplication"],
["xshipped"], ["xshipped"],

3
macxconf.pri

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

4
os2conf.pri

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

8
src/app/application.cpp

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

6
src/core/bittorrent/peerinfo.cpp

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

2
src/core/bittorrent/peerinfo.h

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

3
src/core/bittorrent/private/sessionprivate.h

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

3
src/core/bittorrent/private/torrenthandleprivate.h

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

34
src/core/bittorrent/session.cpp

@ -63,7 +63,7 @@ using namespace BitTorrent;
//#include <libtorrent/extensions/metadata_transfer.hpp> //#include <libtorrent/extensions/metadata_transfer.hpp>
#ifndef DISABLE_COUNTRIES_RESOLUTION #ifndef DISABLE_COUNTRIES_RESOLUTION
#include "geoipmanager.h" #include "core/net/geoipmanager.h"
#endif #endif
#include "core/utils/misc.h" #include "core/utils/misc.h"
@ -141,10 +141,6 @@ Session::Session(QObject *parent)
, m_globalMaxRatio(-1) , m_globalMaxRatio(-1)
, m_numResumeData(0) , m_numResumeData(0)
, m_extraLimit(0) , m_extraLimit(0)
#ifndef DISABLE_COUNTRIES_RESOLUTION
, m_geoipDBLoaded(false)
, m_resolveCountries(false)
#endif
, m_appendLabelToSavePath(false) , m_appendLabelToSavePath(false)
, m_appendExtension(false) , m_appendExtension(false)
, m_refreshInterval(0) , m_refreshInterval(0)
@ -252,13 +248,6 @@ bool Session::useAppendLabelToSavePath() const
return m_appendLabelToSavePath; return m_appendLabelToSavePath;
} }
#ifndef DISABLE_COUNTRIES_RESOLUTION
bool Session::isResolveCountriesEnabled() const
{
return m_resolveCountries;
}
#endif
QString Session::defaultSavePath() const QString Session::defaultSavePath() const
{ {
return m_defaultSavePath; return m_defaultSavePath;
@ -577,25 +566,6 @@ void Session::configure()
delete m_bwScheduler; 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(); Logger* const logger = Logger::instance();
// * Session settings // * Session settings
@ -1049,7 +1019,7 @@ bool Session::addTorrent(QString source, const AddTorrentParams &params)
if (Utils::Misc::isUrl(source)) { if (Utils::Misc::isUrl(source)) {
Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source)); Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
// Launch downloader // 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(downloadFinished(QString, QString)), this, SLOT(handleDownloadFinished(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailed(QString, QString))); connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailed(QString, QString)));
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString))); connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString)));

7
src/core/bittorrent/session.h

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

16
src/core/bittorrent/torrenthandle.cpp

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

4
src/core/bittorrent/torrenthandle.h

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

6
src/core/core.pri

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

61
src/core/http/responsegenerator.cpp

@ -29,11 +29,9 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include <zlib.h> #include "core/utils/gzip.h"
#include "responsegenerator.h" #include "responsegenerator.h"
bool gCompress(QByteArray data, QByteArray& dest_buffer);
using namespace Http; using namespace Http;
QByteArray ResponseGenerator::generate(Response response) 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 // 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 // If the message is smaller than 49 bytes we actually send MORE data if we gzip
QByteArray dest_buf; 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; response.content = dest_buf;
else else
response.headers.remove(HEADER_CONTENT_ENCODING); response.headers.remove(HEADER_CONTENT_ENCODING);
@ -67,58 +65,3 @@ QByteArray ResponseGenerator::generate(Response response)
return ret.toUtf8() + response.content; 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;
}

97
src/core/net/downloadhandler.cpp

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

7
src/core/net/downloadhandler.h

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

4
src/core/net/downloadmanager.cpp

@ -75,7 +75,7 @@ DownloadManager *DownloadManager::instance()
return m_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 // Update proxy settings
applyProxySettings(); applyProxySettings();
@ -91,7 +91,7 @@ DownloadHandler *DownloadManager::downloadUrl(const QString &url, qint64 limit)
qDebug("Downloading %s...", request.url().toEncoded().data()); qDebug("Downloading %s...", request.url().toEncoded().data());
// accept gzip // accept gzip
request.setRawHeader("Accept-Encoding", "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 QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QString &url) const

2
src/core/net/downloadmanager.h

@ -53,7 +53,7 @@ namespace Net
static void freeInstance(); static void freeInstance();
static DownloadManager *instance(); 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; QList<QNetworkCookie> cookiesForUrl(const QString &url) const;
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url); bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);

464
src/core/net/geoipmanager.cpp

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

77
src/core/net/geoipmanager.h

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

631
src/core/net/private/geoipdatabase.cpp

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

60
src/core/net/private/geoipdatabase.h

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

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

42
src/gui/geoip/geoipmanager.h → src/core/utils/gzip.h

@ -1,6 +1,7 @@
/* /*
* Bittorrent Client using Qt4 and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christophe Dumez * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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), * 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 * but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version. * exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/ */
#ifndef GEOIPMANAGER_H #ifndef UTILS_GZIP_H
#define GEOIPMANAGER_H #define UTILS_GZIP_H
#include <QString> class QByteArray;
#include <QIcon>
namespace libtorrent { namespace Utils
class session; {
namespace Gzip
{
bool compress(QByteArray src, QByteArray &dest);
bool uncompress(QByteArray src, QByteArray &dest);
}
} }
class GeoIPManager : public QObject { #endif // UTILS_GZIP_H
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

2
src/gui/addnewtorrentdialog.cpp

@ -135,7 +135,7 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent)
if (Utils::Misc::isUrl(source)) { if (Utils::Misc::isUrl(source)) {
// Launch downloader // 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(downloadFinished(QString, QString)), dlg, SLOT(handleDownloadFinished(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), dlg, SLOT(handleDownloadFailed(QString, QString))); connect(handler, SIGNAL(downloadFailed(QString, QString)), dlg, SLOT(handleDownloadFailed(QString, QString)));
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), dlg, SLOT(handleRedirectedToMagnet(QString, QString))); connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), dlg, SLOT(handleRedirectedToMagnet(QString, QString)));

12
src/gui/geoip/README

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

19
src/gui/geoip/geoip.pri

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

5
src/gui/geoip/geoip.qrc

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

203
src/gui/geoip/geoipmanager.cpp

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

1
src/gui/gui.pri

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

6
src/gui/guiiconprovider.cpp

@ -68,6 +68,12 @@ QIcon GuiIconProvider::getIcon(const QString &iconId)
return QIcon(IconProvider::getIconPath(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 // Makes sure the icon is at least available in 16px and 24px size
// It scales the icon from the theme if necessary // It scales the icon from the theme if necessary
// Otherwise, the UI looks broken if the icon is not available // Otherwise, the UI looks broken if the icon is not available

1
src/gui/guiiconprovider.h

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

2
src/gui/mainwindow.cpp

@ -1626,7 +1626,7 @@ void MainWindow::installPython()
{ {
setCursor(QCursor(Qt::WaitCursor)); setCursor(QCursor(Qt::WaitCursor));
// Download python // 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(downloadFinished(QString, QString)), this, SLOT(pythonDownloadSuccess(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(pythonDownloadFailure(QString, QString))); connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(pythonDownloadFailure(QString, QString)));
} }

18
src/gui/properties/peerlistwidget.cpp

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

2
src/gui/properties/trackersadditiondlg.cpp

@ -59,7 +59,7 @@ QStringList TrackersAdditionDlg::newTrackers() const
void TrackersAdditionDlg::on_uTorrentListButton_clicked() void TrackersAdditionDlg::on_uTorrentListButton_clicked()
{ {
uTorrentListButton->setEnabled(false); 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(downloadFinished(QString, QString)), this, SLOT(parseUTorrentList(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(QString, QString))); connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(QString, QString)));
//Just to show that it takes times //Just to show that it takes times

4
src/gui/rss/rssfeed.cpp

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

4
src/gui/transferlistfilterswidget.cpp

@ -474,7 +474,7 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash)
trackerItem = new QListWidgetItem(); trackerItem = new QListWidgetItem();
trackerItem->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("network-server")); 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(downloadFinished(QString, QString)), this, SLOT(handleFavicoDownload(QString, QString)));
connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(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)) { 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), Logger::instance()->addMessage(tr("Couldn't decode favicon for URL `%1`. Trying to download favicon in PNG format.").arg(url),
Log::WARNING); 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(downloadFinished(QString, QString)), this, SLOT(handleFavicoDownload(QString, QString)));
connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(QString, QString))); connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(QString, QString)));
} }

2
src/searchengine/engineselectdlg.cpp

@ -429,7 +429,7 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
void engineSelectDlg::downloadFromUrl(const QString &url) 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(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
} }

7
unixconf.pri

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

4
winconf.pri

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

Loading…
Cancel
Save