Compare commits

..

25 Commits

Author SHA1 Message Date
Christophe Dumez
2fd2b07b08 branched v2.2.2 release 2010-03-22 20:53:35 +00:00
Christophe Dumez
c5d92f3d69 FEATURE: DHT port can be set from Web UI 2010-03-22 20:41:36 +00:00
Christophe Dumez
54487c8247 Improvement to last commit 2010-03-22 19:42:47 +00:00
Christophe Dumez
be64008870 Bump to v2.2.2 2010-03-22 19:12:34 +00:00
Christophe Dumez
8113b150dd Updated french translation 2010-03-22 19:07:36 +00:00
Christophe Dumez
4a33367cb0 Fix to last commit 2010-03-22 19:04:48 +00:00
Christophe Dumez
0af5d82114 BUGFIX: Fix possible crash with folder scanning 2010-03-22 18:57:32 +00:00
Christophe Dumez
10c4fd330a I18N: Updated Polish translation (thanks Szymon Świerkosz) 2010-03-22 18:24:32 +00:00
Christophe Dumez
9a30d5a295 Properly raise SIGSEGV/SIGABRT after catching it 2010-03-20 21:44:11 +00:00
Christophe Dumez
724b47d999 Save fast resume data every 3 minutes 2010-03-20 21:25:31 +00:00
Christophe Dumez
2c0f7c33a2 Made M3U files previewable 2010-03-20 19:38:21 +00:00
Christophe Dumez
ce33e266fe Attempt to fix mac compilation 2010-03-20 19:14:17 +00:00
Christophe Dumez
2f291daefa Fix Ctrl+A in Web UI 2010-03-20 15:48:48 +00:00
Christophe Dumez
722f2aeb5d BUGFIX: Ask for user confirmation for recursive torrent download
BUGFIX: Fix "add file" dialog in torrent creation tool
2010-03-20 11:33:09 +00:00
Christophe Dumez
d5b9598b5b BUGFIX: Fix file filtering in complex torrents 2010-03-19 11:14:35 +00:00
Christophe Dumez
cc7d74b67c BUGFIX: Fix progress display with cleanlook style 2010-03-19 08:06:56 +00:00
Christophe Dumez
e853b0b736 Code optimization 2010-03-18 23:25:17 +00:00
Christophe Dumez
5e395b24a9 Code optimization 2010-03-18 23:13:41 +00:00
Christophe Dumez
9c3789f83f FEATURE: Display pieces that are being downloaded 2010-03-18 23:03:56 +00:00
Christophe Dumez
758595dc8c BUGFIX: Added back file prioritizing in Web UI 2010-03-18 20:07:10 +00:00
Christophe Dumez
01f9e989ef Bump to v2.2.1 2010-03-18 19:32:43 +00:00
Christophe Dumez
eb9f0cb559 Added back folder watching to Web UI 2010-03-18 19:27:23 +00:00
Christophe Dumez
2592948182 Fix Web UI compatiblity with Safari 2010-03-16 00:24:39 +00:00
Christophe Dumez
6f6ab1c439 Qt 4.4 compilation fix 2010-03-15 18:12:55 +00:00
Christophe Dumez
b10e606dda Branched v2.2.x 2010-03-14 20:44:17 +00:00
336 changed files with 131219 additions and 96027 deletions

11
AUTHORS
View File

@@ -3,19 +3,12 @@ Author:
Contributors:
* Stefanos Antaris <santaris@csd.auth.gr>
* Mohammad Dib <mdib@qbittorrent.org>
* Mirco Chinelli <infinity89@fastwebmail.it>
* Ishan Arora <ishan@qbittorrent.org>
* Arnaud Demaizière <arnaud@qbittorrent.org>
* Grigis Gaëtan <cipher16@gmail.com>
* Christian Kandeler <zambesi@users.sourceforge.net>
* Silvan Scherrer <silvan.scherrer@aroa.ch>
Code from other projects:
* files src/qtsingleapp/* src/lineedit/*
copyright: Nokia Corporation
license: LGPL
* files src/ico.cpp src/ico.h
copyright: Malte Starostik <malte@kde.org>
license: LGPL
@@ -67,13 +60,11 @@ Images Authors:
Translations authors:
* files: src/lang/*.ts
copyright:
- Arabic: SDERAWI (abz8868@msn.com) and sn51234 (nesseyan@gmail.com)
- Brazilian: Nick Marinho (nickmarinho@gmail.com)
- Bulgarian: Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)
- Catalan: Francisco Luque Contreras (frannoe@ya.com)
- Chinese (Simplified): Guo Yue (yue.guo0418@gmail.com)
- Chinese (Traditional): Yi-Shun Wang (dnextstep@gmail.com)
- Croatian: Oliver Mucafir (oliver.untwist@gmail.com)
- Czech: Jirka Vilim (web@tets.cz)
- Danish: Mathias Nielsen (comoneo@gmail.com)
- Dutch: Joost Schipper (heavyjoost@users.sourceforge.net)
@@ -83,7 +74,7 @@ Translations authors:
- German: Niels Hoffmann (zentralmaschine@users.sourceforge.net)
- Greek: Tsvetan Bankov (emerge_life@users.sourceforge.net) and Stephanos Antaris (santaris@csd.auth.gr)
- Hungarian: Majoros Péter (majoros.j.p@t-online.hu)
- Italian: Matteo Sechi (bu17714@gmail.com)
- Italian: Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)
- Japanese: Nardog (alphisation@gmail.com)
- Korean: Jin Woo Sin (jin828sin@users.sourceforge.net)
- Norwegian: Lars-Erik Labori (hamil@users.sourceforge.net)

View File

@@ -1,54 +1,21 @@
* Unreleased - Christophe Dumez <chris@qbittorrent.org> - v2.5.0
- FEATURE: qBittorrent can now act as a tracker
- FEATURE: New and improved RSS feed automated downloader
- FEATURE: Added feature to shutdown qbittorrent on torrents completion
- FEATURE: Added a torrent import assistant to seed or keep downloading outside torrents
- FEATURE: qBittorrent can update itself from Sourceforge (Windows/Mac OS X only)
- FEATURE: Added a transfer list column to display the current tracker
- FEATURE: Remember the last trackers used in the torrent creation tool
- FEATURE: The optimal piece size is now automatically computed in the torrent creation tool
- FEATURE: Bring up the connection settings when clicking on the connection status icon
- COSMETIC: Replaced message box by on-screen notification for download errors
- COSMETIC: Improved the torrent creation tool appearance
- OTHERS: Dropped support for Qt <= 4.4
* Mon Mar 22 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.2.2
- FEATURE: DHT port can be set from Web UI
- BUGFIX: Fix possible crash with folder scanning
- BUGFIX: Fix Mac compilation
- BUGFIX: Save fast resume data every 3 minutes (for robustness)
- I18N: Updated Polish translation (thanks Szymon Świerkosz)
* Tue Aug 24 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.0
- FEATURE: Added actions to "Move to top/bottom" of priority queue
- FEATURE: Auto-Shutdown on downloads completion
- FEATURE: Email notification on download completion
- FEATURE: Added button to password-lock the UI
- FEATURE: Added label-level Pause/Resume/Delete actions
- FEATURE: Torrents can now be filtered by name
- FEATURE: Run external program on torrent completion
- FEATURE: Detect executable updates in order to advise the user to restart
* Tue Jul 27 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.3.0
- FEATURE: Simplified torrent root folder renaming/truncating (< v2.3.0 is no longer forward compatible)
- FEATURE: Remember previous save paths in torrent addition dialog
- FEATURE: Max number of half-open connections can now be edited
- FEATURE: Added support for strict super seeding
- FEATURE: The user can force listening on a particular network interface
- FEATURE: Added cookie support for RSS feeds
- FEATURE: User can force tracker reannounce
- FEATURE: Added "No action" setting for double-click action
- FEATURE: Several torrents can be moved at once
- FEATURE: Added error state for torrents (error is displayed in a tooltip)
- FEATURE: Added filter for paused/error torrents
- FEATURE: Add Check/Uncheck all feature in Web UI
- FEATURE: Search engine can now be disabled
- FEATURE: Torrents can be automatically paused once they reach a given ratio
- FEATURE: Several files can now be disabled at once
- FEATURE: Added "Select All/None" buttons to files list
- FEATURE: Added support for BitComet links (bc://bt/...)
- BUGFIX: Hide seeding torrents files priorities in Web UI
- BUGFIX: The user can disable permanently recursive torrent download
- BUGFIX: Peer Exchange status is now correctly reported
- BUGFIX: Use an INI file instead of the registry on Windows (More reliable)
- BUGFIX: Removed client spoofing feature to avoid tracker blacklisting
- COSMETIC: Display peers country name in tooltip
- COSMETIC: Display number of torrents in transfers tab label
- COSMETIC: Simplified program preferences
- COSMETIC: Fix naming of actions opening new dialogs (use Name...)
* Sat Mar 20 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.2.1
- FEATURE: Display pieces that are being downloaded
- FEATURE: Added back folder watching in Web UI
- FEATURE: Added back file prioritizing in Web UI
- BUGFIX: Fix compilation with Qt 4.4
- BUGFIX: Fix Web UI compatibility with Safari
- BUGFIX: Fix progress display with cleanlook style
- BUGFIX: Fix file filtering in complex torrents
- BUGFIX: Ask for user confirmation for recursive torrent download
- BUGFIX: Fix "add file" dialog in torrent creation tool
- BUGFIX: Fix "Ctrl+A" in Web UI
* Sun Mar 14 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.2.0
- FEATURE: User can set alternative speed limits for fast toggling

View File

@@ -10,17 +10,15 @@ qBittorrent - A BitTorrent client in C++ / Qt4
will install and execute qBittorrent hopefully without any problems.
Dependencies:
- Qt >= 4.5.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
- Qt >= 4.4.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
- pkg-config executable
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.4 REQUIRED, compatible with v0.15.x)
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.4 REQUIRED, >= v0.15.0 ADVISED)
-> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name.
- libboost 1.34.x (libboost-filesystem, libboost-thread, libboost-date-time) + libasio
or
- libboost >= 1.35.x (libboost-system, libboost-filesystem, libboost-thread, libboost-date-time)
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
- python >= 2.3 (needed by search engine)
* Run time only dependency

View File

@@ -1,62 +0,0 @@
qBittorrent - A BitTorrent client in Qt4
------------------------------------------
This is the eComStation (OS/2) qBittorrent part of the readme. See also README for more general information.
Building qBittorrent
********************
Requirements
============
- gcc based build env (recommended gcc v4.4.2 or greater)
- Qt4 for eCS (OS/2) dev package (see http://svn.netlabs.org/qt4 for more information)
- libtorrent-rasterbar for eCS (OS/2) port (see http://svn.netlabs.org/ports for more information)
- boost for eCS (OS/2) port (see http://svn.netlabs.org/ports for more information)
How to build
============
First you need to create the conf.pri file in the same dir as this readme.os2 is.
the conf.pri file has the following content:
##### conf.pri content beginn #####
PREFIX = .
BINDIR = ./bin
INCDIR = ./include
LIBDIR = ./lib
DATADIR = ./share
CONFIG += staticlib
INCLUDEPATH += x:/trees/libtorrent/trunk/include
LIBS += -Lx:/trees/libtorrent/trunk/src/.libs \
-Lx:/trees/boost/trunk/stage/lib \
-Lx:/trees/openssl \
-Lx:/extras/lib
##### conf.pri content end #####
Of course all the above path references have to be adjusted to your build env.
It should now be easy to build qBittorrent:
Simply type:
$ qmake
Followed by:
$ make
If all works fine you should get a working qbittorrent executable.
If you have any question regarding the eCS (OS/2) port of qBittorrent you can meet me (_diver) on IRC:
#netlabs on irc.freenode.net
------------------------------------------
Silvan Scherrer <silvan.scherrer@aroa.ch>

176
configure vendored
View File

@@ -18,24 +18,14 @@ Main options:
--help This help text.
Dependency options:
--disable-gui Disable qBittorrent
Graphical user interface for
headless running
--with-libboost-inc=[path] Path to libboost include
files
--with-libboost-lib=[path] Path to libboost library
files
--disable-libnotify Disable use of libnotify
--disable-geoip-database Disable use of geoip-database
--with-geoip-database-embedded Geoip Database will be
embedded in qBittorrent
executable (please follow
instructions in
src/geoip/README)
--disable-qtsingleapplication Disable use of libboost
--with-qtsingleapplication=[system|shipped] Use the shipped
qtsingleapplication library
or the system one
--disable-gui Disable qBittorrent Graphical user
interface for headless running
--with-libboost-inc=[path] Path to libboost include files
--disable-libnotify Disable use of libnotify
--disable-geoip-database Disable use of geoip-database
--with-geoip-database-embedded Geoip Database will be embedded in
qBittorrent executable (please follow
instructions in src/geoip/README)
EOT
}
@@ -162,11 +152,6 @@ while [ $# -gt 0 ]; do
shift
;;
--with-libboost-lib=*)
QC_WITH_LIBBOOST_LIB=$optarg
shift
;;
--disable-libnotify)
QC_DISABLE_libnotify="Y"
shift
@@ -182,16 +167,6 @@ while [ $# -gt 0 ]; do
shift
;;
--disable-qtsingleapplication)
QC_DISABLE_qtsingleapplication="Y"
shift
;;
--with-qtsingleapplication=*)
QC_WITH_QTSINGLEAPPLICATION=$optarg
shift
;;
--verbose)
QC_VERBOSE="Y"
shift
@@ -215,12 +190,9 @@ echo DATADIR=$DATADIR
echo EX_QTDIR=$EX_QTDIR
echo QC_DISABLE_GUI=$QC_DISABLE_GUI
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
echo QC_WITH_LIBBOOST_LIB=$QC_WITH_LIBBOOST_LIB
echo QC_DISABLE_libnotify=$QC_DISABLE_libnotify
echo QC_DISABLE_geoip_database=$QC_DISABLE_geoip_database
echo QC_WITH_GEOIP_DATABASE_EMBEDDED=$QC_WITH_GEOIP_DATABASE_EMBEDDED
echo QC_DISABLE_qtsingleapplication=$QC_DISABLE_qtsingleapplication
echo QC_WITH_QTSINGLEAPPLICATION=$QC_WITH_QTSINGLEAPPLICATION
echo
fi
@@ -325,7 +297,7 @@ cat >$1/modules.cpp <<EOT
#line 1 "qt4.qcm"
/*
-----BEGIN QCMOD-----
name: Qt >= 4.5
name: Qt >= 4.4
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
-----END QCMOD-----
*/
@@ -333,14 +305,14 @@ class qc_qt4 : public ConfObj
{
public:
qc_qt4(Conf *c) : ConfObj(c) {}
QString name() const { return "Qt >= 4.5"; }
QString shortname() const { return "Qt 4.5"; }
QString name() const { return "Qt >= 4.4"; }
QString shortname() const { return "Qt 4.4"; }
bool exec()
{
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) {
conf->addDefine("DISABLE_GUI");
}
return(QT_VERSION >= 0x040500);
return(QT_VERSION >= 0x040400);
}
};
#line 1 "pkg-config.qcm"
@@ -376,15 +348,25 @@ public:
bool exec(){
QStringList incs;
QString req_ver = "0.14.4";
QString adv_ver = "0.15.0";
QString version, libs, other;
VersionMode mode = VersionMin;
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
return false;
for(int n = 0; n < incs.count(); ++n)
conf->addIncludePath(incs[n]);
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
conf->addLib("-lcrypto");
}
//if(!libs.isEmpty())
// conf->addLib(libs);
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, adv_ver, &version, &incs, &libs, &other))
printf("\nWarning: libtorrent-rasterbar v%s was detected. Some feature will be disabled because they require v%s.\n", version.toLocal8Bit().data(), adv_ver.toUtf8().data());
else
conf->addDefine("LIBTORRENT_0_15");
// Get linking parameters
QStringList params;
QByteArray staticlibs;
params << "--static" << "--libs" << "libtorrent-rasterbar";
conf->doCommand("pkg-config", params, &staticlibs);
conf->addLib(staticlibs.trimmed());
return true;
}
};
@@ -393,39 +375,14 @@ public:
-----BEGIN QCMOD-----
name: libboost
arg: with-libboost-inc=[path], Path to libboost include files
arg: with-libboost-lib=[path], Path to libboost library files
-----END QCMOD-----
*/
#include <boost/version.hpp>
class qc_libboost : public ConfObj
{
public:
qc_libboost(Conf *c) : ConfObj(c) {}
QString name() const { return "libboost"; }
QString shortname() const { return "libboost"; }
QString findBoostLib(QString path, QString lib) const {
QString name;
QDir libDir(path);
QStringList filters;
filters << "libboost_"+lib+"*-mt*.so";
QStringList result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
} else {
// Fall back to non -mt boost lib
filters.clear();
filters << "libboost_"+lib+"*.so";
result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
}
}
return name;
}
bool exec(){
QString s;
s = conf->getenv("QC_WITH_LIBBOOST_INC");
@@ -467,43 +424,6 @@ public:
}
}
conf->addIncludePath(s);
// Find library
s = conf->getenv("QC_WITH_LIBBOOST_LIB");
QStringList required_libs;
#if BOOST_VERSION >= 103500
required_libs << "system";
#endif
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
// Not required by nox
required_libs << "filesystem" << "thread";
}
QStringList libDirs;
libDirs << "/usr/lib/" << "/usr/lib64/" << "/usr/local/lib/" << "/usr/local/lib64/";
foreach(const QString& lib, required_libs) {
if(!s.isEmpty()) {
QString detected_name = findBoostLib(s, lib);
if(detected_name.isEmpty()) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
} else {
conf->addLib("-l"+detected_name);
}
} else {
bool found = false;
foreach(const QString& libDir, libDirs) {
QString detected_name = findBoostLib(libDir, lib);
if(!detected_name.isEmpty()) {
conf->addLib("-l"+detected_name);
found = true;
break;
}
}
if(!found) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
}
}
}
return true;
}
};
@@ -550,21 +470,10 @@ public:
} else {
return false;
}
QStringList incs3;
QString req_ver3 = "2.0";
QString version3, libs3, other3;
if(conf->findPkgConfig("gtk+-2.0", mode, req_ver3, &version3, &incs3, &libs3, &other3)) {
for(int n = 0; n < incs3.count(); ++n)
conf->addIncludePath(incs3[n]);
if(!libs3.isEmpty())
conf->addLib(libs3);
} else {
return false;
}
} else {
return false;
}
return true;
}
};
@@ -606,33 +515,6 @@ public:
#endif
}
};
#line 1 "qtsingleapplication.qcm"
/*
-----BEGIN QCMOD-----
name: libboost
arg: with-qtsingleapplication=[system|shipped], Use the shipped qtsingleapplication library or the system one
-----END QCMOD-----
*/
class qc_qtsingleapplication : public ConfObj
{
public:
qc_qtsingleapplication(Conf *c) : ConfObj(c) {}
QString name() const { return "qtsingleapplication library"; }
QString shortname() const { return "qtsingleapplication"; }
bool exec(){
QString s;
s = conf->getenv("QC_WITH_QTSINGLEAPPLICATION");
if(s.compare("system", Qt::CaseInsensitive) == 0) {
// System
conf->addDefine("USE_SYSTEM_QTSINGLEAPPLICATION");
printf(" [system] ");
} else {
printf(" [shipped] ");
}
return true;
}
};
EOT
cat >$1/modules_new.cpp <<EOT
@@ -654,9 +536,6 @@ cat >$1/modules_new.cpp <<EOT
o = new qc_geoip_database(conf);
o->required = false;
o->disabled = false;
o = new qc_qtsingleapplication(conf);
o->required = false;
o->disabled = false;
EOT
cat >$1/conf4.h <<EOT
@@ -1604,12 +1483,9 @@ export DATADIR
export EX_QTDIR
export QC_DISABLE_GUI
export QC_WITH_LIBBOOST_INC
export QC_WITH_LIBBOOST_LIB
export QC_DISABLE_libnotify
export QC_DISABLE_geoip_database
export QC_WITH_GEOIP_DATABASE_EMBEDDED
export QC_DISABLE_qtsingleapplication
export QC_WITH_QTSINGLEAPPLICATION
export QC_VERBOSE
rm -rf .qconftemp
(

View File

@@ -19,6 +19,4 @@
</dep>
<dep type='geoip-database'>
</dep>
<dep type='qtsingleapplication'>
</dep>
</qconf>

View File

@@ -2,39 +2,14 @@
-----BEGIN QCMOD-----
name: libboost
arg: with-libboost-inc=[path], Path to libboost include files
arg: with-libboost-lib=[path], Path to libboost library files
-----END QCMOD-----
*/
#include <boost/version.hpp>
class qc_libboost : public ConfObj
{
public:
qc_libboost(Conf *c) : ConfObj(c) {}
QString name() const { return "libboost"; }
QString shortname() const { return "libboost"; }
QString findBoostLib(QString path, QString lib) const {
QString name;
QDir libDir(path);
QStringList filters;
filters << "libboost_"+lib+"*-mt*.so";
QStringList result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
} else {
// Fall back to non -mt boost lib
filters.clear();
filters << "libboost_"+lib+"*.so";
result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
}
}
return name;
}
bool exec(){
QString s;
s = conf->getenv("QC_WITH_LIBBOOST_INC");
@@ -76,43 +51,6 @@ public:
}
}
conf->addIncludePath(s);
// Find library
s = conf->getenv("QC_WITH_LIBBOOST_LIB");
QStringList required_libs;
#if BOOST_VERSION >= 103500
required_libs << "system";
#endif
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
// Not required by nox
required_libs << "filesystem" << "thread";
}
QStringList libDirs;
libDirs << "/usr/lib/" << "/usr/lib64/" << "/usr/local/lib/" << "/usr/local/lib64/";
foreach(const QString& lib, required_libs) {
if(!s.isEmpty()) {
QString detected_name = findBoostLib(s, lib);
if(detected_name.isEmpty()) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
} else {
conf->addLib("-l"+detected_name);
}
} else {
bool found = false;
foreach(const QString& libDir, libDirs) {
QString detected_name = findBoostLib(libDir, lib);
if(!detected_name.isEmpty()) {
conf->addLib("-l"+detected_name);
found = true;
break;
}
}
if(!found) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
}
}
}
return true;
}
};

View File

@@ -40,21 +40,10 @@ public:
} else {
return false;
}
QStringList incs3;
QString req_ver3 = "2.0";
QString version3, libs3, other3;
if(conf->findPkgConfig("gtk+-2.0", mode, req_ver3, &version3, &incs3, &libs3, &other3)) {
for(int n = 0; n < incs3.count(); ++n)
conf->addIncludePath(incs3[n]);
if(!libs3.isEmpty())
conf->addLib(libs3);
} else {
return false;
}
} else {
return false;
}
return true;
}
};

View File

@@ -13,15 +13,25 @@ public:
bool exec(){
QStringList incs;
QString req_ver = "0.14.4";
QString adv_ver = "0.15.0";
QString version, libs, other;
VersionMode mode = VersionMin;
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
return false;
for(int n = 0; n < incs.count(); ++n)
conf->addIncludePath(incs[n]);
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
conf->addLib("-lcrypto");
}
//if(!libs.isEmpty())
// conf->addLib(libs);
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, adv_ver, &version, &incs, &libs, &other))
printf("\nWarning: libtorrent-rasterbar v%s was detected. Some feature will be disabled because they require v%s.\n", version.toLocal8Bit().data(), adv_ver.toUtf8().data());
else
conf->addDefine("LIBTORRENT_0_15");
// Get linking parameters
QStringList params;
QByteArray staticlibs;
params << "--static" << "--libs" << "libtorrent-rasterbar";
conf->doCommand("pkg-config", params, &staticlibs);
conf->addLib(staticlibs.trimmed());
return true;
}
};

View File

@@ -1,6 +1,6 @@
/*
-----BEGIN QCMOD-----
name: Qt >= 4.5
name: Qt >= 4.4
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
-----END QCMOD-----
*/
@@ -8,13 +8,13 @@ class qc_qt4 : public ConfObj
{
public:
qc_qt4(Conf *c) : ConfObj(c) {}
QString name() const { return "Qt >= 4.5"; }
QString shortname() const { return "Qt 4.5"; }
QString name() const { return "Qt >= 4.4"; }
QString shortname() const { return "Qt 4.4"; }
bool exec()
{
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) {
conf->addDefine("DISABLE_GUI");
}
return(QT_VERSION >= 0x040500);
return(QT_VERSION >= 0x040400);
}
};

View File

@@ -1,26 +0,0 @@
/*
-----BEGIN QCMOD-----
name: libboost
arg: with-qtsingleapplication=[system|shipped], Use the shipped qtsingleapplication library or the system one
-----END QCMOD-----
*/
class qc_qtsingleapplication : public ConfObj
{
public:
qc_qtsingleapplication(Conf *c) : ConfObj(c) {}
QString name() const { return "qtsingleapplication library"; }
QString shortname() const { return "qtsingleapplication"; }
bool exec(){
QString s;
s = conf->getenv("QC_WITH_QTSINGLEAPPLICATION");
if(s.compare("system", Qt::CaseInsensitive) == 0) {
// System
conf->addDefine("USE_SYSTEM_QTSINGLEAPPLICATION");
printf(" [system] ");
} else {
printf(" [shipped] ");
}
return true;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -37,10 +37,13 @@
#include "ui_mainwindow.h"
#include "qtorrenthandle.h"
class QBtSession;
enum TabIndex{TAB_TRANSFER, TAB_SEARCH, TAB_RSS};
class Bittorrent;
class QTimer;
class downloadFromURL;
class SearchEngine;
class QLocalServer;
class QCloseEvent;
class RSSImp;
class QShortcut;
@@ -54,24 +57,19 @@ class PropertiesWidget;
class StatusBar;
class consoleDlg;
class about;
class TorrentCreatorDlg;
class createtorrent;
class downloadFromURL;
class HidableTabWidget;
class LineEdit;
class QFileSystemWatcher;
class MainWindow : public QMainWindow, private Ui::MainWindow{
class GUI : public QMainWindow, private Ui::MainWindow{
Q_OBJECT
public:
// Construct / Destruct
MainWindow(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
~MainWindow();
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
~GUI();
// Methods
QWidget* getCurrentTabWidget() const;
int getCurrentTabIndex() const;
TransferListWidget* getTransferList() const { return transferList; }
QMenu* getTrayIconMenu();
PropertiesWidget *getProperties() const { return properties; }
public slots:
void trackerAuthenticationRequired(QTorrentHandle& h);
@@ -79,8 +77,6 @@ public slots:
void showNotificationBaloon(QString title, QString msg) const;
void downloadFromURLList(const QStringList& urls);
void updateAltSpeedsBtn(bool alternative);
void updateNbTorrents(unsigned int nb_downloading, unsigned int nb_seeding, unsigned int nb_active, unsigned int nb_inactive, unsigned int nb_paused);
void deleteBTSession();
protected slots:
// GUI related slots
@@ -92,6 +88,8 @@ protected slots:
void on_actionWebsite_triggered() const;
void on_actionBugReport_triggered() const;
void on_actionShow_console_triggered();
void readParamsOnSocket();
void acceptConnection();
void balloonClicked();
void writeSettings();
void readSettings();
@@ -101,11 +99,6 @@ protected slots:
void handleDownloadFromUrlFailure(QString, QString) const;
void createSystrayDelayed();
void tab_changed(int);
void on_actionLock_qBittorrent_triggered();
void defineUILockPassword();
bool unlockUI();
void notifyOfUpdate(QString);
void showConnectionSettings();
// Keyboard shortcuts
void createKeyboardShortcuts();
void displayTransferTab() const;
@@ -118,7 +111,6 @@ protected slots:
void on_actionOpen_triggered();
void updateGUI();
void loadPreferences(bool configure_session=true);
void processParams(const QString& params);
void processParams(const QStringList& params);
void addTorrent(QString path);
void addUnauthenticatedTracker(const QPair<QTorrentHandle,QString> &tracker);
@@ -130,42 +122,34 @@ protected slots:
void optionsSaved();
// HTTP slots
void on_actionDownload_from_URL_triggered();
#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
void handleUpdateCheckFinished(bool update_available, QString new_version);
void handleUpdateInstalled(QString error_msg);
#endif
protected:
void closeEvent(QCloseEvent *);
void showEvent(QShowEvent *);
bool event(QEvent * event);
void displayRSSTab(bool enable);
void displaySearchTab(bool enable);
private:
QFileSystemWatcher *executable_watcher;
// Bittorrent
QBtSession *BTSession;
Bittorrent *BTSession;
QList<QPair<QTorrentHandle,QString> > unauthenticated_trackers; // Still needed?
// GUI related
QTimer *guiUpdater;
HidableTabWidget *tabs;
QTabWidget *tabs;
StatusBar *status_bar;
QPointer<options_imp> options;
QPointer<consoleDlg> console;
QPointer<about> aboutDlg;
QPointer<TorrentCreatorDlg> createTorrentDlg;
QPointer<createtorrent> createTorrentDlg;
QPointer<downloadFromURL> downloadFromURLDialog;
QPointer<QSystemTrayIcon> systrayIcon;
QPointer<QTimer> systrayCreator;
QPointer<QMenu> myTrayIconMenu;
QMenu *myTrayIconMenu;
TransferListWidget *transferList;
TransferListFiltersWidget *transferListFilters;
PropertiesWidget *properties;
bool displaySpeedInTitle;
bool force_exit;
bool ui_locked;
LineEdit *search_filter;
// Keyboard shortcuts
QShortcut *switchSearchShortcut;
QShortcut *switchSearchShortcut2;
@@ -176,21 +160,12 @@ private:
QAction *prioSeparator2;
QSplitter *hSplitter;
QSplitter *vSplitter;
QMenu *lockMenu;
// Search
QPointer<SearchEngine> searchEngine;
SearchEngine *searchEngine;
// RSS
QPointer<RSSImp> rssWidget;
private slots:
void on_actionSearch_engine_triggered();
void on_actionRSS_Reader_triggered();
void on_actionSpeed_in_title_bar_triggered();
void on_actionTop_tool_bar_triggered();
void on_actionShutdown_when_downloads_complete_triggered();
void on_actionShutdown_qBittorrent_when_downloads_complete_triggered();
void on_action_Import_Torrent_triggered();
void on_actionDonate_money_triggered();
// Misc
QLocalServer *localServer;
};
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

BIN
src/Icons/locale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

BIN
src/Icons/oxygen/wallet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

View File

@@ -1,9 +1,8 @@
[Desktop Entry]
Categories=Qt;Network;P2P;
Comment=V2.5.0
Comment=V2.2.2
Exec=qbittorrent %f
GenericName=Bittorrent client
GenericName[ar]=العميل Bittorrent
GenericName[bg]=Торент клиент
GenericName[cs]=Bittorrent klient
GenericName[de]=Bittorren Client
@@ -11,7 +10,6 @@ GenericName[el]=Bittorrent πελάτης
GenericName[es]=Cliente Bittorrent
GenericName[fi]=Bittorrent-ohjelma
GenericName[fr]=Client Bittorrent
GenericName[hr]=Bittorrent klijent
GenericName[hu]=Bittorrent kliens
GenericName[it]=Client Bittorrent
GenericName[ja]=Bittorrent クライアント

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/Icons/skin/pausedUP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/Icons/skin/queuedUP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 78 KiB

BIN
src/Icons/skin/stalled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 910 B

BIN
src/Icons/slow48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 680 B

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>torrent</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>qBitTorrentDocument</string>
<key>CFBundleTypeName</key>
<string>BitTorrent Document</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-bittorrent</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>org.bittorrent.torrent</string>
</array>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>magnet</string>
</array>
<key>CFBundleURLName</key>
<string>BitTorrent Magnet URL</string>
</dict>
</array>
<key>CFBundleIconFile</key>
<string>qbittorrent_mac.icns</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleGetInfoString</key>
<string>2.5.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>qbittorrent</string>
<key>CFBundleIdentifier</key>
<string>org.qbittorrent</string>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
</dict>
</plist>

View File

@@ -1,6 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>gpl.html</file>
</qresource>
</RCC>

View File

@@ -32,7 +32,7 @@
#define ABOUT_H
#include "ui_about.h"
#include <QFile>
#include <QScrollBar>
class about : public QDialog, private Ui::AboutDlg{
Q_OBJECT
@@ -47,56 +47,399 @@ class about : public QDialog, private Ui::AboutDlg{
setAttribute(Qt::WA_DeleteOnClose);
// Set icons
logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/qbittorrent22.png")));
mascot_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/mascot.png")));
//Title
lb_name->setText(QString::fromUtf8("<b><h1>")+tr("qBittorrent")+QString::fromUtf8(" "VERSION"</h1></b>"));
// Thanks
QString thanks_txt;
thanks_txt += QString::fromUtf8("<p>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</p>");
thanks_txt += QString::fromUtf8("<p>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</p>");
thanks_txt += QString::fromUtf8("<p>I also want to thank Στέφανος Αντάρης (santaris@csd.auth.gr) and Mirco Chinelli (infinity89@fastwebmail.it) for working on Mac OS X packaging.</p>");
thanks_txt += QString::fromUtf8("<p>I am grateful to Peter Koeleman (peter@qbittorrent.org) and Mohammad Dib (mdib@qbittorrent.org) for working on qBittorrent port to Windows.</p>");
thanks_txt += QString::fromUtf8("<p>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</p>");
te_thanks->setHtml(thanks_txt);
te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
te_thanks->append(QString::fromUtf8("<ul><li>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</li>"));
te_thanks->append(QString::fromUtf8("<li>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</li>"));
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, for his help and support since the beginning of this project.</li>"));
te_thanks->append(QString::fromUtf8("<li>I am grateful to Peter Koeleman (peter@qbittorrent.org) for working on qBittorrent port to Windows.</li>"));
te_thanks->append(QString::fromUtf8("<li>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</li></ul><br><br>"));
te_thanks->scrollToAnchor(QString::fromUtf8("top"));
// Translation
QString trans_txt = "<p>"+tr("I would like to thank the following people who volunteered to translate qBittorrent:")+"</p>";
trans_txt += QString::fromUtf8("<ul><li><u>Arabic:</u> SDERAWI (abz8868@msn.com) and sn51234 (nesseyan@gmail.com)</li>\
<li><u>Brazilian:</u> Nick Marinho (nickmarinho@gmail.com)</li>\
<li><u>Bulgarian:</u> Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)</li>\
<li><u>Catalan:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\
<li><u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)</li>\
<li><u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com)</li>\
<li><u>Croatian:</u> Oliver Mucafir (oliver.untwist@gmail.com)</li>\
<li><u>Czech:</u> Jirka Vilim (web@tets.cz)</li>\
<li><u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)</li>\
<li><u>Dutch:</u> Joost Schipper (heavyjoost@users.sourceforge.net) and Peter Koeleman (peter@peerweb.nl)</li>\
<li><u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net) and Pekka Niemi (pekka.niemi@iki.fi)</li>\
<li><u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)</li>\
<li><u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net)</li>\
<li><u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)</li>\
<li><u>Italian:</u> Matteo Sechi (bu17714@gmail.com)</li>\
<li><u>Japanese:</u> Nardog (alphisation@gmail.com)</li>\
<li><u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)</li>\
<li><u>Norwegian:</u> Lars-Erik Labori (hamil@users.sourceforge.net)</li>\
<li><u>Polish:</u> Mariusz Fik (fisiu@opensuse.org)</li>\
<li><u>Portuguese:</u> Nick Marinho (nickmarinho@gmail.com)</li>\
<li><u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net)</li>\
<li><u>Russian:</u> Nick Khazov (m2k3d0n@users.sourceforge.net) and Alexey Morsov (samurai@ricom.ru)</li>\
<li><u>Serbian:</u> Anaximandar Milet (anaximandar@operamail.com)</li>\
<li><u>Slovak:</u> helix84</li>\
<li><u>Spanish:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\
<li><u>Swedish:</u> Daniel Nylander (po@danielnylander.se)</li>\
<li><u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)</li>\
<li><u>Ukrainian:</u> Andrey Shpachenko (masterfix@users.sourceforge.net) and Oleh Prypin (blaxpirit@gmail.com)</li></ul>");
trans_txt += "<p>"+tr("Please contact me if you would like to translate qBittorrent into your own language.")+"</p>";
te_translation->setHtml(trans_txt);
te_translation->append(QString::fromUtf8("<a name='top'></a>"));
te_translation->append(tr("I would like to thank the following people who volunteered to translate qBittorrent:")+QString::fromUtf8("<br>"));
te_translation->append(QString::fromUtf8(
"<i>- <u>Brazilian:</u> Nick Marinho (nickmarinho@gmail.com)<br>\
- <u>Bulgarian:</u> Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)<br>\
- <u>Catalan:</u> Francisco Luque Contreras (frannoe@ya.com)<br>\
- <u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)<br>\
- <u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com)<br>\
- <u>Czech:</u> Jirka Vilim (web@tets.cz)<br>\
- <u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)<br>\
- <u>Dutch:</u> Joost Schipper (heavyjoost@users.sourceforge.net) and Peter Koeleman (peter@peerweb.nl)<br>\
- <u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net) and Pekka Niemi (pekka.niemi@iki.fi)<br>\
- <u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)<br>\
- <u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net)<br>\
- <u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)<br>\
- <u>Italian:</u> Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)<br>\
- <u>Japanese:</u> Nardog (alphisation@gmail.com)<br>\
- <u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)<br>\
- <u>Norwegian:</u> Lars-Erik Labori (hamil@users.sourceforge.net)<br>\
- <u>Polish:</u> Mariusz Fik (fisiu@opensuse.org)<br>\
- <u>Portuguese:</u> Nick Marinho (nickmarinho@gmail.com)<br>\
- <u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net)<br>\
- <u>Russian:</u> Nick Khazov (m2k3d0n@users.sourceforge.net) and Alexey Morsov (samurai@ricom.ru)<br>\
- <u>Serbian:</u> Anaximandar Milet (anaximandar@operamail.com)<br>\
- <u>Slovak:</u> helix84<br>\
- <u>Spanish:</u> Francisco Luque Contreras (frannoe@ya.com)<br>\
- <u>Swedish:</u> Daniel Nylander (po@danielnylander.se)<br>\
- <u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)<br>\
- <u>Ukrainian:</u> Andrey Shpachenko (masterfix@users.sourceforge.net) and Oleh Prypin (blaxpirit@gmail.com)<br><br>"));
te_translation->append(tr("Please contact me if you would like to translate qBittorrent into your own language."));
te_translation->scrollToAnchor(QString::fromUtf8("top"));
// License
te_license->append(QString::fromUtf8("<a name='top'></a>"));
QFile licensefile(":/gpl.html");
if(licensefile.open(QIODevice::ReadOnly|QIODevice::Text)) {
te_license->setHtml(licensefile.readAll());
licensefile.close();
}
show();
te_license->append(QString::fromUtf8("qBittorrent is licensed under the GNU General Public License version 2 with the\
addition of the following special exception:\
<br><br>\
<i>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.</i>\
<br><br>\
<center><b>GNU GENERAL PUBLIC LICENSE</b></center><br>\
<center>Version 2, June 1991</center><br>\
Copyright (C) 1989, 1991 Free Software Foundation, Inc.<br>\
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>\
Everyone is permitted to copy and distribute verbatim copies<br>\
of this license document, but changing it is not allowed.<br>\
<br>\
<center><b>Preamble</b></center><br>\
The licenses for most software are designed to take away your<br>\
freedom to share and change it. By contrast, the GNU General Public<br>\
License is intended to guarantee your freedom to share and change free<br>\
software--to make sure the software is free for all its users. This<br>\
General Public License applies to most of the Free Software<br>\
Foundation's software and to any other program whose authors commit to<br>\
using it. (Some other Free Software Foundation software is covered by<br>\
the GNU Library General Public License instead.) You can apply it to<br>\
your programs, too.<br>\
<br>\
When we speak of free software, we are referring to freedom, not<br>\
price. Our General Public Licenses are designed to make sure that you<br>\
have the freedom to distribute copies of free software (and charge for<br>\
this service if you wish), that you receive source code or can get it<br>\
if you want it, that you can change the software or use pieces of it<br>\
in new free programs; and that you know you can do these things.<br>\
<br>\
To protect your rights, we need to make restrictions that forbid<br>\
anyone to deny you these rights or to ask you to surrender the rights.<br>\
These restrictions translate to certain responsibilities for you if you<br>\
distribute copies of the software, or if you modify it.<br>\
<br>\
For example, if you distribute copies of such a program, whether<br>\
gratis or for a fee, you must give the recipients all the rights that<br>\
you have. You must make sure that they, too, receive or can get the<br>\
source code. And you must show them these terms so they know their<br>\
rights.<br>\
<br>\
We protect your rights with two steps: (1) copyright the software, and<br>\
(2) offer you this license which gives you legal permission to copy,<br>\
distribute and/or modify the software.<br>\
<br>\
Also, for each author's protection and ours, we want to make certain<br>\
that everyone understands that there is no warranty for this free<br>\
software. If the software is modified by someone else and passed on, we<br>\
want its recipients to know that what they have is not the original, so<br>\
that any problems introduced by others will not reflect on the original<br>\
authors' reputations.<br>\
<br>\
Finally, any free program is threatened constantly by software<br>\
patents. We wish to avoid the danger that redistributors of a free<br>\
program will individually obtain patent licenses, in effect making the<br>\
program proprietary. To prevent this, we have made it clear that any<br>\
patent must be licensed for everyone's free use or not licensed at all.<br>\
<br>\
The precise terms and conditions for copying, distribution and<br>\
modification follow.<br>\
<br>\
<center><b>GNU GENERAL PUBLIC LICENSE</b></center><br>\
<center><b>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</b></center><br>\
0. This License applies to any program or other work which contains<br>\
a notice placed by the copyright holder saying it may be distributed<br>\
under the terms of this General Public License. The 'Program', below,<br>\
refers to any such program or work, and a 'work based on the Program'<br>\
means either the Program or any derivative work under copyright law:<br>\
that is to say, a work containing the Program or a portion of it,<br>\
either verbatim or with modifications and/or translated into another<br>\
language. (Hereinafter, translation is included without limitation in<br>\
the term 'modification'.) Each licensee is addressed as 'you'.<br>\
<br>\
Activities other than copying, distribution and modification are not<br>\
covered by this License; they are outside its scope. The act of<br>\
running the Program is not restricted, and the output from the Program<br>\
is covered only if its contents constitute a work based on the<br>\
Program (independent of having been made by running the Program).<br>\
Whether that is true depends on what the Program does.<br>\
<br>\
1. You may copy and distribute verbatim copies of the Program's<br>\
source code as you receive it, in any medium, provided that you<br>\
conspicuously and appropriately publish on each copy an appropriate<br>\
copyright notice and disclaimer of warranty; keep intact all the<br>\
notices that refer to this License and to the absence of any warranty;<br>\
and give any other recipients of the Program a copy of this License<br>\
along with the Program.<br>\
<br>\
You may charge a fee for the physical act of transferring a copy, and<br>\
you may at your option offer warranty protection in exchange for a fee.<br>\
<br>\
2. You may modify your copy or copies of the Program or any portion<br>\
of it, thus forming a work based on the Program, and copy and<br>\
distribute such modifications or work under the terms of Section 1<br>\
above, provided that you also meet all of these conditions:<br>\
<br>\
a) You must cause the modified files to carry prominent notices<br>\
stating that you changed the files and the date of any change.<br>\
<br>\
b) You must cause any work that you distribute or publish, that in<br>\
whole or in part contains or is derived from the Program or any<br>\
part thereof, to be licensed as a whole at no charge to all third<br>\
parties under the terms of this License.<br>\
<br>\
c) If the modified program normally reads commands interactively<br>\
when run, you must cause it, when started running for such<br>\
interactive use in the most ordinary way, to print or display an<br>\
announcement including an appropriate copyright notice and a<br>\
notice that there is no warranty (or else, saying that you provide<br>\
a warranty) and that users may redistribute the program under<br>\
these conditions, and telling the user how to view a copy of this<br>\
License. (Exception: if the Program itself is interactive but<br>\
does not normally print such an announcement, your work based on<br>\
the Program is not required to print an announcement.)<br>\
<br>\
These requirements apply to the modified work as a whole. If<br>\
identifiable sections of that work are not derived from the Program,<br>\
and can be reasonably considered independent and separate works in<br>\
themselves, then this License, and its terms, do not apply to those<br>\
sections when you distribute them as separate works. But when you<br>\
distribute the same sections as part of a whole which is a work based<br>\
on the Program, the distribution of the whole must be on the terms of<br>\
this License, whose permissions for other licensees extend to the<br>\
entire whole, and thus to each and every part regardless of who wrote it.<br>\
<br>\
Thus, it is not the intent of this section to claim rights or contest<br>\
your rights to work written entirely by you; rather, the intent is to<br>\
exercise the right to control the distribution of derivative or<br>\
collective works based on the Program.<br>\
<br>\
In addition, mere aggregation of another work not based on the Program<br>\
with the Program (or with a work based on the Program) on a volume of<br>\
a storage or distribution medium does not bring the other work under<br>\
the scope of this License.<br>\
<br>\
3. You may copy and distribute the Program (or a work based on it,<br>\
under Section 2) in object code or executable form under the terms of<br>\
Sections 1 and 2 above provided that you also do one of the following:<br>\
<br>\
a) Accompany it with the complete corresponding machine-readable<br>\
source code, which must be distributed under the terms of Sections<br>\
1 and 2 above on a medium customarily used for software interchange; or,<br>\
<br>\
b) Accompany it with a written offer, valid for at least three<br>\
years, to give any third party, for a charge no more than your<br>\
cost of physically performing source distribution, a complete<br>\
machine-readable copy of the corresponding source code, to be<br>\
distributed under the terms of Sections 1 and 2 above on a medium<br>\
customarily used for software interchange; or,<br>\
<br>\
c) Accompany it with the information you received as to the offer<br>\
to distribute corresponding source code. (This alternative is<br>\
allowed only for noncommercial distribution and only if you<br>\
received the program in object code or executable form with such<br>\
an offer, in accord with Subsection b above.)<br>\
<br>\
The source code for a work means the preferred form of the work for<br>\
making modifications to it. For an executable work, complete source<br>\
code means all the source code for all modules it contains, plus any<br>\
associated interface definition files, plus the scripts used to<br>\
control compilation and installation of the executable. However, as a<br>\
special exception, the source code distributed need not include<br>\
anything that is normally distributed (in either source or binary<br>\
form) with the major components (compiler, kernel, and so on) of the<br>\
operating system on which the executable runs, unless that component<br>\
itself accompanies the executable.<br>\
<br>\
If distribution of executable or object code is made by offering<br>\
access to copy from a designated place, then offering equivalent<br>\
access to copy the source code from the same place counts as<br>\
distribution of the source code, even though third parties are not<br>\
compelled to copy the source along with the object code.<br>\
<br>\
4. You may not copy, modify, sublicense, or distribute the Program<br>\
except as expressly provided under this License. Any attempt<br>\
otherwise to copy, modify, sublicense or distribute the Program is<br>\
void, and will automatically terminate your rights under this License.<br>\
However, parties who have received copies, or rights, from you under<br>\
this License will not have their licenses terminated so long as such<br>\
parties remain in full compliance.<br>\
<br>\
5. You are not required to accept this License, since you have not<br>\
signed it. However, nothing else grants you permission to modify or<br>\
distribute the Program or its derivative works. These actions are<br>\
prohibited by law if you do not accept this License. Therefore, by<br>\
modifying or distributing the Program (or any work based on the<br>\
Program), you indicate your acceptance of this License to do so, and<br>\
all its terms and conditions for copying, distributing or modifying<br>\
the Program or works based on it.<br>\
<br>\
6. Each time you redistribute the Program (or any work based on the<br>\
Program), the recipient automatically receives a license from the<br>\
original licensor to copy, distribute or modify the Program subject to<br>\
these terms and conditions. You may not impose any further<br>\
restrictions on the recipients' exercise of the rights granted herein.<br>\
You are not responsible for enforcing compliance by third parties to<br>\
this License.<br>\
<br>\
7. If, as a consequence of a court judgment or allegation of patent<br>\
infringement or for any other reason (not limited to patent issues),<br>\
conditions are imposed on you (whether by court order, agreement or<br>\
otherwise) that contradict the conditions of this License, they do not<br>\
excuse you from the conditions of this License. If you cannot<br>\
distribute so as to satisfy simultaneously your obligations under this<br>\
License and any other pertinent obligations, then as a consequence you<br>\
may not distribute the Program at all. For example, if a patent<br>\
license would not permit royalty-free redistribution of the Program by<br>\
all those who receive copies directly or indirectly through you, then<br>\
the only way you could satisfy both it and this License would be to<br>\
refrain entirely from distribution of the Program.<br>\
<br>\
If any portion of this section is held invalid or unenforceable under<br>\
any particular circumstance, the balance of the section is intended to<br>\
apply and the section as a whole is intended to apply in other<br>\
circumstances.<br>\
<br>\
It is not the purpose of this section to induce you to infringe any<br>\
patents or other property right claims or to contest validity of any<br>\
such claims; this section has the sole purpose of protecting the<br>\
integrity of the free software distribution system, which is<br>\
implemented by public license practices. Many people have made<br>\
generous contributions to the wide range of software distributed<br>\
through that system in reliance on consistent application of that<br>\
system; it is up to the author/donor to decide if he or she is willing<br>\
to distribute software through any other system and a licensee cannot<br>\
impose that choice.<br>\
<br>\
This section is intended to make thoroughly clear what is believed to<br>\
be a consequence of the rest of this License.<br>\
<br>\
8. If the distribution and/or use of the Program is restricted in<br>\
certain countries either by patents or by copyrighted interfaces, the<br>\
original copyright holder who places the Program under this License<br>\
may add an explicit geographical distribution limitation excluding<br>\
those countries, so that distribution is permitted only in or among<br>\
countries not thus excluded. In such case, this License incorporates<br>\
the limitation as if written in the body of this License.<br>\
<br>\
9. The Free Software Foundation may publish revised and/or new versions<br>\
of the General Public License from time to time. Such new versions will<br>\
be similar in spirit to the present version, but may differ in detail to<br>\
address new problems or concerns.<br>\
<br>\
Each version is given a distinguishing version number. If the Program<br>\
specifies a version number of this License which applies to it and 'any<br>\
later version', you have the option of following the terms and conditions<br>\
either of that version or of any later version published by the Free<br>\
Software Foundation. If the Program does not specify a version number of<br>\
this License, you may choose any version ever published by the Free Software<br>\
Foundation.<br>\
<br>\
10. If you wish to incorporate parts of the Program into other free<br>\
programs whose distribution conditions are different, write to the author<br>\
to ask for permission. For software which is copyrighted by the Free<br>\
Software Foundation, write to the Free Software Foundation; we sometimes<br>\
make exceptions for this. Our decision will be guided by the two goals<br>\
of preserving the free status of all derivatives of our free software and<br>\
of promoting the sharing and reuse of software generally.<br>\
<br>\
<center><b>NO WARRANTY</b></center><br>\
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY<br>\
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN<br>\
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES<br>\
PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED<br>\
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF<br>\
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS<br>\
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE<br>\
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,<br>\
REPAIR OR CORRECTION.<br>\
<br>\
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING<br>\
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR<br>\
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,<br>\
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING<br>\
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED<br>\
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY<br>\
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER<br>\
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE<br>\
POSSIBILITY OF SUCH DAMAGES.<br>\
<br>\
<center><b>END OF TERMS AND CONDITIONS</b></center><br>\
<center>How to Apply These Terms to Your New Programs</center><br>\
If you develop a new program, and you want it to be of the greatest<br>\
possible use to the public, the best way to achieve this is to make it<br>\
free software which everyone can redistribute and change under these terms.<br>\
<br>\
To do so, attach the following notices to the program. It is safest<br>\
to attach them to the start of each source file to most effectively<br>\
convey the exclusion of warranty; and each file should have at least<br>\
the 'copyright' line and a pointer to where the full notice is found.<br>\
<br>\
<one line to give the program's name and a brief idea of what it does.><br>\
Copyright (C) <year> <name of author><br>\
<br>\
This program is free software; you can redistribute it and/or modify<br>\
it under the terms of the GNU General Public License as published by<br>\
the Free Software Foundation; either version 2 of the License, or<br>\
(at your option) any later version.<br>\
<br>\
This program is distributed in the hope that it will be useful,<br>\
but WITHOUT ANY WARRANTY; without even the implied warranty of<br>\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>\
GNU General Public License for more details.<br>\
<br>\
You should have received a copy of the GNU General Public License<br>\
along with this program; if not, write to the Free Software<br>\
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>\
<br>\
<br>\
Also add information on how to contact you by electronic and paper mail.<br>\
<br>\
If the program is interactive, make it output a short notice like this<br>\
when it starts in an interactive mode:<br>\
<br>\
Gnomovision version 69, Copyright (C) year name of author<br>\
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.<br>\
This is free software, and you are welcome to redistribute it<br>\
under certain conditions; type `show c' for details.<br>\
<br>\
The hypothetical commands `show w' and `show c' should show the appropriate<br>\
parts of the General Public License. Of course, the commands you use may<br>\
be called something other than `show w' and `show c'; they could even be<br>\
mouse-clicks or menu items--whatever suits your program.<br>\
<br>\
You should also get your employer (if you work as a programmer) or your<br>\
school, if any, to sign a 'copyright disclaimer' for the program, if<br>\
necessary. Here is a sample; alter the names:<br>\
<br>\
Yoyodyne, Inc., hereby disclaims all copyright interest in the program<br>\
`Gnomovision' (which makes passes at compilers) written by James Hacker.<br>\
<br>\
'signature of Ty Coon', 1 April 1989<br>\
Ty Coon, President of Vice<br>\
<br>\
This General Public License does not permit incorporating your program into<br>\
proprietary programs. If your program is a subroutine library, you may<br>\
consider it more useful to permit linking proprietary applications with the<br>\
library. If this is what you want to do, use the GNU Library General<br>\
Public License instead of this License.<br>"));
te_license->scrollToAnchor(QString::fromUtf8("top"));
show();
}
};

View File

@@ -5,22 +5,18 @@
#include <QHeaderView>
#include <QSpinBox>
#include <QCheckBox>
#include <QComboBox>
#include <QNetworkInterface>
#include <libtorrent/version.hpp>
#include "preferences.h"
enum AdvSettingsCols {PROPERTY, VALUE};
enum AdvSettingsRows {DISK_CACHE, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, COUNT_OVERHEAD, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, PROGRAM_NOTIFICATIONS, TRACKER_STATUS, TRACKER_PORT };
#define ROW_COUNT 15
enum AdvSettingsRows {DISK_CACHE, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, COUNT_OVERHEAD, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS };
#define ROW_COUNT 9
class AdvancedSettings: public QTableWidget {
Q_OBJECT
private:
QSpinBox *spin_cache, *outgoing_ports_min, *outgoing_ports_max, *spin_list_refresh, *spin_maxhalfopen, *spin_tracker_port;
QCheckBox *cb_ignore_limits_lan, *cb_count_overhead, *cb_recheck_completed, *cb_resolve_countries, *cb_resolve_hosts, *cb_super_seeding, *cb_program_notifications, *cb_tracker_status;
QComboBox *combo_iface;
QSpinBox *spin_cache, *outgoing_ports_min, *outgoing_ports_max, *spin_list_refresh;
QCheckBox *cb_ignore_limits_lan, *cb_count_overhead, *cb_recheck_completed, *cb_resolve_countries, *cb_resolve_hosts;
public:
AdvancedSettings(QWidget *parent=0): QTableWidget(parent) {
@@ -49,12 +45,6 @@ public:
delete spin_list_refresh;
delete cb_resolve_countries;
delete cb_resolve_hosts;
delete spin_maxhalfopen;
delete cb_super_seeding;
delete combo_iface;
delete cb_program_notifications;
delete spin_tracker_port;
delete cb_tracker_status;
}
public slots:
@@ -75,24 +65,6 @@ public slots:
// Peer resolution
Preferences::resolvePeerCountries(cb_resolve_countries->isChecked());
Preferences::resolvePeerHostNames(cb_resolve_hosts->isChecked());
// Max Half-Open connections
Preferences::setMaxHalfOpenConnections(spin_maxhalfopen->value());
#if LIBTORRENT_VERSION_MINOR > 14
// Super seeding
Preferences::enableSuperSeeding(cb_super_seeding->isChecked());
#endif
// Network interface
if(combo_iface->currentIndex() == 0) {
// All interfaces (default)
Preferences::setNetworkInterface(QString::null);
} else {
Preferences::setNetworkInterface(combo_iface->currentText());
}
// Program notification
Preferences::useProgramNotification(cb_program_notifications->isChecked());
// Tracker
Preferences::setTrackerEnabled(cb_tracker_status->isChecked());
Preferences::setTrackerPort(spin_tracker_port->value());
}
protected slots:
@@ -161,59 +133,6 @@ protected slots:
connect(cb_resolve_hosts, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_resolve_hosts->setChecked(Preferences::resolvePeerHostNames());
setCellWidget(RESOLVE_HOSTS, VALUE, cb_resolve_hosts);
// Max Half Open connections
setItem(MAX_HALF_OPEN, PROPERTY, new QTableWidgetItem(tr("Maximum number of half-open connections [0: Disabled]")));
spin_maxhalfopen = new QSpinBox();
connect(spin_maxhalfopen, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
spin_maxhalfopen->setMinimum(0);
spin_maxhalfopen->setMaximum(99999);
spin_maxhalfopen->setValue(Preferences::getMaxHalfOpenConnections());
setCellWidget(MAX_HALF_OPEN, VALUE, spin_maxhalfopen);
// Super seeding
setItem(SUPER_SEEDING, PROPERTY, new QTableWidgetItem(tr("Strict super seeding")));
cb_super_seeding = new QCheckBox();
connect(cb_super_seeding, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
#if LIBTORRENT_VERSION_MINOR > 14
cb_super_seeding->setChecked(Preferences::isSuperSeedingEnabled());
#else
cb_super_seeding->setEnabled(false);
#endif
setCellWidget(SUPER_SEEDING, VALUE, cb_super_seeding);
// Network interface
setItem(NETWORK_IFACE, PROPERTY, new QTableWidgetItem(tr("Network Interface (requires restart)")));
combo_iface = new QComboBox;
combo_iface->addItem(tr("Any interface", "i.e. Any network interface"));
const QString current_iface = Preferences::getNetworkInterface();
int i = 1;
foreach(const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
if(iface.name() == "lo") continue;
combo_iface->addItem(iface.name());
if(!current_iface.isEmpty() && iface.name() == current_iface)
combo_iface->setCurrentIndex(i);
++i;
}
connect(combo_iface, SIGNAL(currentIndexChanged(int)), this, SLOT(emitSettingsChanged()));
setCellWidget(NETWORK_IFACE, VALUE, combo_iface);
// Program notifications
setItem(PROGRAM_NOTIFICATIONS, PROPERTY, new QTableWidgetItem(tr("Display program notification balloons")));
cb_program_notifications = new QCheckBox();
connect(cb_program_notifications, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_program_notifications->setChecked(Preferences::useProgramNotification());
setCellWidget(PROGRAM_NOTIFICATIONS, VALUE, cb_program_notifications);
// Tracker State
setItem(TRACKER_STATUS, PROPERTY, new QTableWidgetItem(tr("Enable embedded tracker")));
cb_tracker_status = new QCheckBox();
connect(cb_tracker_status, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_tracker_status->setChecked(Preferences::isTrackerEnabled());
setCellWidget(TRACKER_STATUS, VALUE, cb_tracker_status);
// Tracker port
setItem(TRACKER_PORT, PROPERTY, new QTableWidgetItem(tr("Embedded tracker port")));
spin_tracker_port = new QSpinBox();
connect(spin_tracker_port, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
spin_tracker_port->setMinimum(1);
spin_tracker_port->setMaximum(65535);
spin_tracker_port->setValue(Preferences::getTrackerPort());
setCellWidget(TRACKER_PORT, VALUE, spin_tracker_port);
}
void emitSettingsChanged() {

2338
src/bittorrent.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -43,13 +43,9 @@
#include <QPointer>
#include <QTimer>
#include <libtorrent/version.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp>
#include "qtracker.h"
#include "qtorrenthandle.h"
#include "trackerinfos.h"
using namespace libtorrent;
@@ -62,43 +58,65 @@ class HttpServer;
class BandwidthScheduler;
class ScanFoldersModel;
class QBtSession : public QObject {
Q_OBJECT
Q_DISABLE_COPY(QBtSession)
class TrackerInfos {
public:
QString name_or_url;
QString last_message;
unsigned long num_peers;
#ifndef LIBTORRENT_0_15
bool verified;
uint fail_count;
#endif
private:
explicit QBtSession();
static QBtSession* m_instance;
//TrackerInfos() {}
TrackerInfos(const TrackerInfos &b) {
name_or_url = b.name_or_url;
Q_ASSERT(!name_or_url.isEmpty());
last_message = b.last_message;
num_peers = b.num_peers;
#ifndef LIBTORRENT_0_15
verified = b.verified;
fail_count = b.fail_count;
#endif
}
TrackerInfos(QString name_or_url): name_or_url(name_or_url), last_message(""), num_peers(0) {
#ifndef LIBTORRENT_0_15
fail_count = 0;
verified = false;
#endif
}
};
class Bittorrent : public QObject {
Q_OBJECT
public:
static QBtSession* instance();
static void drop();
~QBtSession();
// Constructor / Destructor
Bittorrent();
~Bittorrent();
QTorrentHandle getTorrentHandle(QString hash) const;
std::vector<torrent_handle> getTorrents() const;
bool isFilePreviewPossible(QString fileHash) const;
bool isDHTEnabled() const;
bool isLSDEnabled() const;
float getPayloadDownloadRate() const;
float getPayloadUploadRate() const;
session_status getSessionStatus() const;
int getListenPort() const;
float getRealRatio(QString hash) const;
session* getSession() const;
QHash<QString, TrackerInfos> getTrackersInfo(QString hash) const;
bool hasActiveTorrents() const;
bool hasDownloadingTorrents() const;
//int getMaximumActiveDownloads() const;
//int getMaximumActiveTorrents() const;
bool isQueueingEnabled() const;
int getMaximumActiveDownloads() const;
int getMaximumActiveTorrents() const;
int loadTorrentPriority(QString hash);
QStringList getConsoleMessages() const;
QStringList getPeerBanMessages() const;
qlonglong getETA(QString hash);
inline QStringList getConsoleMessages() const { return consoleMessages; }
inline QStringList getPeerBanMessages() const { return peerBanMessages; }
inline session* getSession() const { return s; }
inline bool useTemporaryFolder() const { return !defaultTempPath.isEmpty(); }
inline QString getDefaultSavePath() const { return defaultSavePath; }
inline ScanFoldersModel* getScanFoldersModel() const { return m_scanFolders; }
inline bool isDHTEnabled() const { return DHTEnabled; }
inline bool isLSDEnabled() const { return LSDEnabled; }
inline bool isPexEnabled() const { return DHTEnabled; }
inline bool isQueueingEnabled() const { return queueingEnabled; }
bool useTemporaryFolder() const;
QString getDefaultSavePath() const;
ScanFoldersModel* getScanFoldersModel() const;
public slots:
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
@@ -117,13 +135,14 @@ public slots:
void resumeTorrent(QString hash);
void resumeAllTorrents();
/* End Web UI */
void saveDHTEntry();
void preAllocateAllFiles(bool b);
void saveFastResumeData();
void enableIPFilter(QString filter);
void disableIPFilter();
void setQueueingEnabled(bool enable);
void handleDownloadFailure(QString url, QString reason);
void downloadUrlAndSkipDialog(QString url, QString save_path, QString label);
void downloadUrlAndSkipDialog(QString url, QString save_path=QString::null);
// Session configuration - Setters
void setListeningPort(int port);
void setMaxConnections(int maxConnec);
@@ -131,7 +150,8 @@ public slots:
void setMaxUploadsPerTorrent(int max);
void setDownloadRateLimit(long rate);
void setUploadRateLimit(long rate);
void setMaxRatio(float ratio);
void setGlobalRatio(float ratio);
void setDeleteRatio(float ratio);
void setDHTPort(int dht_port);
void setPeerProxySettings(const proxy_settings &proxySettings);
void setHTTPProxySettings(const proxy_settings &proxySettings);
@@ -139,10 +159,10 @@ public slots:
void startTorrentsInPause(bool b);
void setDefaultTempPath(QString temppath);
void setAppendLabelToSavePath(bool append);
void appendLabelToTorrentSavePath(QTorrentHandle &h);
void changeLabelInTorrentSavePath(QTorrentHandle &h, QString old_label, QString new_label);
#if LIBTORRENT_VERSION_MINOR > 14
void appendqBextensionToTorrent(QTorrentHandle &h, bool append);
void appendLabelToTorrentSavePath(QTorrentHandle h);
void changeLabelInTorrentSavePath(QTorrentHandle h, QString old_label, QString new_label);
#ifdef LIBTORRENT_0_15
void appendqBextensionToTorrent(QTorrentHandle h, bool append);
void setAppendqBExtension(bool append);
#endif
void applyEncryptionSettings(pe_settings se);
@@ -166,28 +186,16 @@ public slots:
void recursiveTorrentDownload(const QTorrentHandle &h);
protected:
QString getSavePath(QString hash, bool fromScanDir = false, QString filePath = QString::null, QString root_folder=QString::null);
QString getSavePath(QString hash, bool fromScanDir = false, QString filePath = QString());
bool initWebUi(QString username, QString password, int port);
bool loadFastResumeData(QString hash, std::vector<char> &buf);
void loadTorrentSettings(QTorrentHandle h);
void loadTorrentTempData(QTorrentHandle h, QString savePath, bool magnet);
add_torrent_params initializeAddTorrentParams(QString hash);
protected slots:
void addTorrentsFromScanFolder(QStringList&);
void readAlerts();
void processBigRatios();
void deleteBigRatios();
void takeETASamples();
void exportTorrentFiles(QString path);
void saveTempFastResumeData();
void sendNotificationEmail(QTorrentHandle h);
void autoRunExternalProgram(QTorrentHandle h, bool async=true);
void cleanUpAutoRunProcess(int);
void mergeTorrents(QTorrentHandle h_ex, boost::intrusive_ptr<torrent_info> t);
void exportTorrentFile(QTorrentHandle h);
#if LIBTORRENT_VERSION_MINOR < 15
void saveDHTEntry();
#endif
signals:
void addedTorrent(QTorrentHandle& h);
@@ -213,9 +221,8 @@ private:
session *s;
QPointer<QTimer> timerAlerts;
QPointer<BandwidthScheduler> bd_scheduler;
QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl;
QMap<QUrl, QString> savepath_fromurl;
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
QHash<QString, QString> savePathsToRemove;
QStringList torrentsToPausedAfterChecking;
QTimer resumeDataTimer;
// Ratio
@@ -231,7 +238,6 @@ private:
bool preAllocateAll;
bool addInPause;
float ratio_limit;
int high_ratio_action;
bool UPnPEnabled;
bool NATPMPEnabled;
bool LSDEnabled;
@@ -241,7 +247,7 @@ private:
bool queueingEnabled;
bool appendLabelToSavePath;
bool torrentExport;
#if LIBTORRENT_VERSION_MINOR > 14
#ifdef LIBTORRENT_0_15
bool appendqBExtension;
#endif
QString defaultSavePath;
@@ -262,8 +268,6 @@ private:
bool geoipDBLoaded;
bool resolve_countries;
#endif
// Tracker
QPointer<QTracker> m_tracker;
};

View File

@@ -31,7 +31,7 @@
#ifndef CONSOLE_H
#define CONSOLE_H
#include "qbtsession.h"
#include "bittorrent.h"
#include "ui_console.h"
using namespace libtorrent;
@@ -39,13 +39,17 @@ using namespace libtorrent;
class consoleDlg : public QDialog, private Ui_ConsoleDlg{
Q_OBJECT
private:
Bittorrent *BTSession;
public:
consoleDlg(QWidget *parent) : QDialog(parent) {
consoleDlg(QWidget *parent, Bittorrent* _BTSession) : QDialog(parent) {
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setModal(true);
textConsole->setHtml(QBtSession::instance()->getConsoleMessages().join("<br>"));
textBannedPeers->setHtml(QBtSession::instance()->getPeerBanMessages().join("<br>"));
BTSession = _BTSession;
textConsole->setHtml(BTSession->getConsoleMessages().join("<br>"));
textBannedPeers->setHtml(BTSession->getPeerBanMessages().join("<br>"));
show();
}

View File

@@ -1,95 +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 arnaud@qbittorrent.org
*/
#include "cookiesdlg.h"
#include "ui_cookiesdlg.h"
#include <QNetworkCookie>
enum CookiesCols { COOKIE_KEY, COOKIE_VALUE};
CookiesDlg::CookiesDlg(QWidget *parent, const QList<QByteArray> &raw_cookies) :
QDialog(parent),
ui(new Ui::CookiesDlg)
{
ui->setupUi(this);
ui->infos_lbl->setText(tr("Common keys for cookies are : '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass"));
foreach(const QByteArray &raw_cookie, raw_cookies) {
QList<QByteArray> cookie_parts = raw_cookie.split('=');
if(cookie_parts.size() != 2) continue;
const int i = ui->cookiesTable->rowCount();
ui->cookiesTable->setRowCount(i+1);
ui->cookiesTable->setItem(i, COOKIE_KEY, new QTableWidgetItem(cookie_parts.first().data()));
ui->cookiesTable->setItem(i, COOKIE_VALUE, new QTableWidgetItem(cookie_parts.last().data()));
}
}
CookiesDlg::~CookiesDlg()
{
delete ui;
}
void CookiesDlg::on_add_btn_clicked() {
ui->cookiesTable->setRowCount(ui->cookiesTable->rowCount()+1);
// Edit first column
ui->cookiesTable->editItem(ui->cookiesTable->item(ui->cookiesTable->rowCount()-1, COOKIE_KEY));
}
void CookiesDlg::on_del_btn_clicked() {
// Get selected cookie
QList<QTableWidgetItem*> selection = ui->cookiesTable->selectedItems();
if(!selection.isEmpty()) {
ui->cookiesTable->removeRow(selection.first()->row());
}
}
QList<QByteArray> CookiesDlg::getCookies() const {
QList<QByteArray> ret;
for(int i=0; i<ui->cookiesTable->rowCount(); ++i) {
QString key = ui->cookiesTable->item(i, COOKIE_KEY)->text().trimmed();
QString value = ui->cookiesTable->item(i, COOKIE_VALUE)->text().trimmed();
if(!key.isEmpty() && !value.isEmpty()) {
const QString raw_cookie = key+"="+value;
qDebug("Cookie: %s", qPrintable(raw_cookie));
ret << raw_cookie.toLocal8Bit();
}
}
return ret;
}
QList<QByteArray> CookiesDlg::askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok) {
CookiesDlg dlg(parent, raw_cookies);
if(dlg.exec()) {
*ok = true;
return dlg.getCookies();
}
*ok = false;
return QList<QByteArray>();
}

View File

@@ -1,58 +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 arnaud@qbittorrent.org
*/
#ifndef COOKIESDLG_H
#define COOKIESDLG_H
#include <QDialog>
namespace Ui {
class CookiesDlg;
}
class CookiesDlg : public QDialog
{
Q_OBJECT
public:
explicit CookiesDlg(QWidget *parent = 0, const QList<QByteArray> &raw_cookies = QList<QByteArray>());
~CookiesDlg();
QList<QByteArray> getCookies() const;
static QList<QByteArray> askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok);
protected slots:
void on_add_btn_clicked();
void on_del_btn_clicked();
private:
Ui::CookiesDlg *ui;
};
#endif // COOKIESDLG_H

280
src/createtorrent_imp.cpp Normal file
View File

@@ -0,0 +1,280 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 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 <QFileDialog>
#include <QMessageBox>
#include <QInputDialog>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/bind.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/file.hpp>
#include <libtorrent/storage.hpp>
#include <libtorrent/hasher.hpp>
#include <libtorrent/file_pool.hpp>
#include <libtorrent/create_torrent.hpp>
#include "torrentpersistentdata.h"
#include "createtorrent_imp.h"
#include "misc.h"
using namespace libtorrent;
using namespace boost::filesystem;
// do not include files and folders whose
// name starts with a .
bool file_filter(boost::filesystem::path const& filename)
{
if (filename.leaf()[0] == '.') return false;
std::cerr << filename << std::endl;
return true;
}
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setModal(true);
creatorThread = new torrentCreatorThread(this);
connect(creatorThread, SIGNAL(creationSuccess(QString, const char*)), this, SLOT(handleCreationSuccess(QString, const char*)));
connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString)));
connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int)));
path::default_name_check(no_check);
show();
}
createtorrent::~createtorrent() {
delete creatorThread;
}
void createtorrent::on_addFolder_button_clicked(){
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
if(!dir.isEmpty())
textInputPath->setText(dir);
}
void createtorrent::on_addFile_button_clicked(){
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath());
if(!file.isEmpty())
textInputPath->setText(file);
}
void createtorrent::on_removeTracker_button_clicked() {
QModelIndexList selectedIndexes = trackers_list->selectionModel()->selectedIndexes();
for(int i=selectedIndexes.size()-1; i>=0; --i){
QListWidgetItem *item = trackers_list->takeItem(selectedIndexes.at(i).row());
delete item;
}
}
int createtorrent::getPieceSize() const {
switch(comboPieceSize->currentIndex()) {
case 0:
return 32*1024;
case 1:
return 64*1024;
case 2:
return 128*1024;
case 3:
return 256*1024;
case 4:
return 512*1024;
case 5:
return 1024*1024;
case 6:
return 2048*1024;
default:
return 256*1024;
}
}
void createtorrent::on_addTracker_button_clicked() {
bool ok;
QString URL = QInputDialog::getText(this, tr("Please type an announce URL"),
tr("Announce URL:", "Tracker URL"), QLineEdit::Normal,
"http://", &ok);
if(ok){
if(trackers_list->findItems(URL, Qt::MatchFixedString).size() == 0)
trackers_list->addItem(URL);
}
}
void createtorrent::on_removeURLSeed_button_clicked(){
QModelIndexList selectedIndexes = URLSeeds_list->selectionModel()->selectedIndexes();
for(int i=selectedIndexes.size()-1; i>=0; --i){
QListWidgetItem *item = URLSeeds_list->takeItem(selectedIndexes.at(i).row());
delete item;
}
}
void createtorrent::on_addURLSeed_button_clicked(){
bool ok;
QString URL = QInputDialog::getText(this, tr("Please type a web seed url"),
tr("Web seed URL:"), QLineEdit::Normal,
"http://", &ok);
if(ok){
if(URLSeeds_list->findItems(URL, Qt::MatchFixedString).size() == 0)
URLSeeds_list->addItem(URL);
}
}
QStringList createtorrent::allItems(QListWidget *list){
QStringList res;
unsigned int nbItems = list->count();
for(unsigned int i=0; i< nbItems; ++i){
res << list->item(i)->text();
}
return res;
}
// Main function that create a .torrent file
void createtorrent::on_createButton_clicked(){
QString input = textInputPath->text().trimmed();
if (input.endsWith(QDir::separator()))
input.chop(1);
if(input.isEmpty()){
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
return;
}
QStringList trackers = allItems(trackers_list);
/*if(!trackers.size()){
QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
return;
}*/
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
if(!destination.isEmpty()) {
if(!destination.endsWith(QString::fromUtf8(".torrent")))
destination += QString::fromUtf8(".torrent");
} else {
return;
}
QStringList url_seeds = allItems(URLSeeds_list);
QString comment = txt_comment->toPlainText();
creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize());
}
void createtorrent::handleCreationFailure(QString msg) {
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg));
}
void createtorrent::handleCreationSuccess(QString path, const char* branch_path) {
if(checkStartSeeding->isChecked()) {
// Create save path temp data
boost::intrusive_ptr<torrent_info> t;
try {
t = new torrent_info(path.toLocal8Bit().data());
} catch(std::exception&) {
QMessageBox::critical(0, tr("Torrent creation"), tr("Created torrent file is invalid. It won't be added to download list."));
return;
}
QString hash = misc::toQString(t->info_hash());
TorrentTempData::setSavePath(hash, QString(branch_path));
#ifdef LIBTORRENT_0_15
// Enable seeding mode (do not recheck the files)
TorrentTempData::setSeedingMode(hash, true);
#endif
emit torrent_to_seed(path);
}
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+path);
close();
}
void createtorrent::updateProgressBar(int progress) {
progressBar->setValue(progress);
}
//
// Torrent Creator Thread
//
void torrentCreatorThread::create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size) {
input_path = _input_path;
save_path = _save_path;
trackers = _trackers;
url_seeds = _url_seeds;
comment = _comment;
is_private = _is_private;
piece_size = _piece_size;
abort = false;
start();
}
void sendProgressUpdateSignal(int i, int num, torrentCreatorThread *parent){
parent->sendProgressSignal((int)(i*100./(float)num));
}
void torrentCreatorThread::sendProgressSignal(int progress) {
emit updateProgress(progress);
}
void torrentCreatorThread::run() {
emit updateProgress(0);
char const* creator_str = "qBittorrent "VERSION;
try {
file_storage fs;
file_pool fp;
path full_path = complete(path(input_path.toLocal8Bit().data()));
// Adding files to the torrent
add_files(fs, full_path, file_filter);
if(abort) return;
create_torrent t(fs, piece_size);
// Add url seeds
QString seed;
foreach(seed, url_seeds){
t.add_url_seed(seed.toLocal8Bit().data());
}
for(int i=0; i<trackers.size(); ++i){
t.add_tracker(trackers.at(i).toLocal8Bit().data());
}
if(abort) return;
// calculate the hash for all pieces
set_piece_hashes(t, full_path.branch_path(), boost::bind<void>(&sendProgressUpdateSignal, _1, t.num_pieces(), this));
// Set qBittorrent as creator and add user comment to
// torrent_info structure
t.set_creator(creator_str);
t.set_comment((const char*)comment.toLocal8Bit());
// Is private ?
t.set_priv(is_private);
if(abort) return;
// create the torrent and print it to out
ofstream out(complete(path((const char*)save_path.toLocal8Bit())), std::ios_base::binary);
bencode(std::ostream_iterator<char>(out), t.generate());
emit updateProgress(100);
emit creationSuccess(save_path, full_path.branch_path().string().c_str());
}
catch (std::exception& e){
emit creationFailure(QString::fromUtf8(e.what()));
}
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,16 +31,55 @@
#ifndef CREATE_TORRENT_IMP_H
#define CREATE_TORRENT_IMP_H
#include <QThread>
#include "ui_createtorrent.h"
class TorrentCreatorThread;
class TorrentCreatorDlg : public QDialog, private Ui::createTorrentDialog{
class torrentCreatorThread : public QThread {
Q_OBJECT
QString input_path;
QString save_path;
QStringList trackers;
QStringList url_seeds;
QString comment;
bool is_private;
int piece_size;
bool abort;
QDialog *parent;
public:
torrentCreatorThread(QDialog *_parent) {
parent = _parent;
}
~torrentCreatorThread() {
abort = true;
wait();
}
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
void sendProgressSignal(int progress);
protected:
void run();
signals:
void creationFailure(QString msg);
void creationSuccess(QString path, const char* branch_path);
signals:
void updateProgress(int progress);
};
class createtorrent : public QDialog, private Ui::createTorrentDialog{
Q_OBJECT
private:
torrentCreatorThread *creatorThread;
public:
TorrentCreatorDlg(QWidget *parent = 0);
~TorrentCreatorDlg();
createtorrent(QWidget *parent = 0);
~createtorrent();
QStringList allItems(QListWidget *list);
int getPieceSize() const;
signals:
@@ -48,24 +87,17 @@ class TorrentCreatorDlg : public QDialog, private Ui::createTorrentDialog{
public slots:
void updateProgressBar(int progress);
void on_cancelButton_clicked();
protected slots:
void on_createButton_clicked();
void on_addFile_button_clicked();
void on_addFolder_button_clicked();
void on_addTracker_button_clicked();
void on_removeTracker_button_clicked();
void on_addURLSeed_button_clicked();
void on_removeURLSeed_button_clicked();
void handleCreationFailure(QString msg);
void handleCreationSuccess(QString path, QString branch_path);
void setInteractionEnabled(bool enabled);
void showProgressBar(bool show);
void on_checkAutoPieceSize_clicked(bool checked);
void updateOptimalPieceSize();
void saveTrackerList();
void loadTrackerList();
private:
TorrentCreatorThread *creatorThread;
QList<int> m_piece_sizes;
void handleCreationSuccess(QString path, const char* branch_path);
};
#endif

View File

@@ -33,7 +33,6 @@
#include <QDialog>
#include "ui_confirmdeletiondlg.h"
#include "preferences.h"
#include "misc.h"
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
@@ -43,9 +42,6 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
DeletionConfirmationDlg(QWidget *parent=0): QDialog(parent) {
setupUi(this);
move(misc::screenCenter(this));
checkPermDelete->setChecked(Preferences::deleteTorrentFilesAsDefault());
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
buttonBox->setFocus();
}
bool shouldDeleteLocalFiles() const {
@@ -61,15 +57,6 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
return false;
}
private slots:
void updateRememberButtonState() {
rememberBtn->setEnabled(checkPermDelete->isChecked() != Preferences::deleteTorrentFilesAsDefault());
}
void on_rememberBtn_clicked() {
Preferences::setDeleteTorrentFilesAsDefault(checkPermDelete->isChecked());
rememberBtn->setEnabled(false);
}
};
#endif // DELETIONCONFIRMATIONDLG_H

View File

@@ -43,7 +43,6 @@ using namespace libtorrent;
class DownloadedPiecesBar: public QWidget {
Q_OBJECT
Q_DISABLE_COPY(DownloadedPiecesBar)
private:
QPixmap pixmap;
@@ -61,34 +60,31 @@ public:
pix.fill();
pixmap = pix;
} else {
const qulonglong nb_pieces = pieces.size();
const int nb_pieces = pieces.size();
// Reduce the number of pieces before creating the pixmap
// otherwise it can crash when there are too many pieces
const uint w = width();
if(nb_pieces > w) {
const uint ratio = floor(nb_pieces/(double)w);
bitfield scaled_pieces(ceil(nb_pieces/(double)ratio), false);
bitfield scaled_downloading(ceil(nb_pieces/(double)ratio), false);
uint scaled_index = 0;
for(qulonglong i=0; i<nb_pieces; i+= ratio) {
if(nb_pieces > width()) {
const int ratio = floor(nb_pieces/(double)width());
std::vector<bool> scaled_pieces;
std::vector<bool> scaled_downloading;
for(int i=0; i<nb_pieces; i+= ratio) {
bool have = true;
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
for(int j=i; j<qMin(i+ratio, nb_pieces); ++j) {
if(!pieces[i]) { have = false; break; }
}
scaled_pieces.push_back(have);
if(have) {
scaled_pieces.set_bit(scaled_index);
scaled_downloading.push_back(false);
} else {
bool downloading = false;
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
for(int j=i; j<qMin(i+ratio, nb_pieces); ++j) {
if(downloading_pieces[i]) { downloading = true; break; }
}
if(downloading)
scaled_downloading.set_bit(scaled_index);
scaled_downloading.push_back(downloading);
}
++scaled_index;
}
QPixmap pix = QPixmap(scaled_pieces.size(), 1);
//pix.fill();
pix.fill();
QPainter painter(&pix);
for(uint i=0; i<scaled_pieces.size(); ++i) {
if(scaled_pieces[i]) {
@@ -105,7 +101,7 @@ public:
pixmap = pix;
} else {
QPixmap pix = QPixmap(pieces.size(), 1);
//pix.fill();
pix.fill();
QPainter painter(&pix);
for(uint i=0; i<pieces.size(); ++i) {
if(pieces[i]) {

View File

@@ -51,7 +51,7 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL{
show();
// Paste clipboard if there is an URL in it
QString clip_txt = qApp->clipboard()->text();
if(clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive) || clip_txt.startsWith("magnet:", Qt::CaseInsensitive) || clip_txt.startsWith("bc://bt/", Qt::CaseInsensitive)) {
if(clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive) || clip_txt.startsWith("magnet:", Qt::CaseInsensitive)) {
textUrls->setText(clip_txt);
}
}

View File

@@ -28,32 +28,32 @@
* Contact : chris@qbittorrent.org
*/
#include "downloadthread.h"
#include <QTemporaryFile>
#include <QSettings>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QNetworkCookie>
#include <QNetworkCookieJar>
#include "downloadthread.h"
#include "preferences.h"
#include "qinisettings.h"
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5};
/** Download Thread **/
downloadThread::downloadThread(QObject* parent) : QObject(parent) {
connect(&networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
#ifndef QT_NO_OPENSSL
connect(&networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply*,QList<QSslError>)));
#endif
networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
}
downloadThread::~downloadThread(){
qDebug("Deleting network manager");
delete networkManager;
qDebug("Deleted network manager");
}
void downloadThread::processDlFinished(QNetworkReply* reply) {
QString url = reply->url().toEncoded().data();
qDebug("Download finished: %s", qPrintable(url));
QString url = reply->url().toString();
if(reply->error() != QNetworkReply::NoError) {
// Failure
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error())));
emit downloadFailure(url, errorCodeToString(reply->error()));
} else {
QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
@@ -70,27 +70,24 @@ void downloadThread::processDlFinished(QNetworkReply* reply) {
}
// Success
QString filePath;
QTemporaryFile *tmpfile = new QTemporaryFile;
tmpfile->setAutoRemove(false);
if (tmpfile->open()) {
filePath = tmpfile->fileName();
QTemporaryFile tmpfile;
tmpfile.setAutoRemove(false);
if (tmpfile.open()) {
filePath = tmpfile.fileName();
qDebug("Temporary filename is: %s", qPrintable(filePath));
if(reply->open(QIODevice::ReadOnly)) {
// TODO: Support GZIP compression
tmpfile->write(reply->readAll());
tmpfile.write(reply->readAll());
reply->close();
tmpfile->close();
delete tmpfile;
tmpfile.close();
// Send finished signal
emit downloadFinished(url, filePath);
} else {
// Error when reading the request
tmpfile->close();
delete tmpfile;
tmpfile.close();
emit downloadFailure(url, tr("I/O Error"));
}
} else {
delete tmpfile;
emit downloadFailure(url, tr("I/O Error"));
}
}
@@ -98,28 +95,7 @@ void downloadThread::processDlFinished(QNetworkReply* reply) {
reply->deleteLater();
}
void downloadThread::loadCookies(const QString &host_name, QString url) {
const QList<QByteArray> raw_cookies = Preferences::getHostNameCookies(host_name);
QNetworkCookieJar *cookie_jar = networkManager.cookieJar();
QList<QNetworkCookie> cookies;
qDebug("Loading cookies for host name: %s", qPrintable(host_name));
foreach(const QByteArray& raw_cookie, raw_cookies) {
QList<QByteArray> cookie_parts = raw_cookie.split('=');
if(cookie_parts.size() == 2) {
qDebug("Loading cookie: %s", raw_cookie.constData());
cookies << QNetworkCookie(cookie_parts.first(), cookie_parts.last());
}
}
cookie_jar->setCookiesFromUrl(cookies, url);
networkManager.setCookieJar(cookie_jar);
}
void downloadThread::downloadTorrentUrl(QString url){
// Load cookies
QString host_name = QUrl::fromEncoded(url.toLocal8Bit()).host();
if(!host_name.isEmpty())
loadCookies(host_name, url);
// Process request
QNetworkReply *reply = downloadUrl(url);
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
}
@@ -127,24 +103,14 @@ void downloadThread::downloadTorrentUrl(QString url){
QNetworkReply* downloadThread::downloadUrl(QString url){
// Update proxy settings
applyProxySettings();
// Load cookies
QString host_name = QUrl::fromEncoded(url.toLocal8Bit()).host();
if(!host_name.isEmpty())
loadCookies(host_name, url);
// Process download request
qDebug("url is %s", qPrintable(url));
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit());
QNetworkRequest request(qurl);
QNetworkRequest request;
request.setUrl(QUrl::fromEncoded(url.toLocal8Bit()));
// Spoof Firefox 3.5 user agent to avoid
// Web server banning
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5");
qDebug("Downloading %s...", request.url().toEncoded().data());
qDebug("%d cookies for this URL", networkManager.cookieJar()->cookiesForUrl(url).size());
for(int i=0; i<networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) {
qDebug("%s=%s", networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data());
qDebug("Domain: %s, Path: %s", qPrintable(networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(networkManager.cookieJar()->cookiesForUrl(url).at(i).path()));
}
return networkManager.get(request);
qDebug("Downloading %s...", qPrintable(request.url().toString()));
return networkManager->get(request);
}
void downloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) {
@@ -168,7 +134,7 @@ void downloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
void downloadThread::applyProxySettings() {
QNetworkProxy proxy;
QIniSettings settings("qBittorrent", "qBittorrent");
QSettings settings("qBittorrent", "qBittorrent");
int intValue = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt();
if(intValue > 0) {
// Proxy enabled
@@ -178,7 +144,7 @@ void downloadThread::applyProxySettings() {
qDebug("Using proxy: %s", qPrintable(IP));
proxy.setPort(port.toUShort());
// Default proxy type is HTTP, we must change if it is SOCKS5
if(intValue == Proxy::SOCKS5 || intValue == Proxy::SOCKS5_PW) {
if(intValue == SOCKS5 || intValue == SOCKS5_PW) {
qDebug("Proxy is SOCKS5, not HTTP");
proxy.setType(QNetworkProxy::Socks5Proxy);
} else {
@@ -196,7 +162,7 @@ void downloadThread::applyProxySettings() {
} else {
proxy.setType(QNetworkProxy::NoProxy);
}
networkManager.setProxy(proxy);
networkManager->setProxy(proxy);
}
QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
@@ -247,11 +213,3 @@ QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
return tr("Unknown error");
}
}
#ifndef QT_NO_OPENSSL
void downloadThread::ignoreSslErrors(QNetworkReply* reply,QList<QSslError> errors) {
Q_UNUSED(errors)
// Ignore all SSL errors
reply->ignoreSslErrors();
}
#endif

View File

@@ -34,7 +34,6 @@
#include <QNetworkReply>
#include <QObject>
#include <QHash>
#include <QSslError>
class QNetworkAccessManager;
@@ -42,7 +41,7 @@ class downloadThread : public QObject {
Q_OBJECT
private:
QNetworkAccessManager networkManager;
QNetworkAccessManager *networkManager;
QHash<QString, QString> redirect_mapping;
signals:
@@ -51,6 +50,7 @@ signals:
public:
downloadThread(QObject* parent);
~downloadThread();
QNetworkReply* downloadUrl(QString url);
void downloadTorrentUrl(QString url);
//void setProxy(QString IP, int port, QString username, QString password);
@@ -58,14 +58,10 @@ public:
protected:
QString errorCodeToString(QNetworkReply::NetworkError status);
void applyProxySettings();
void loadCookies(const QString &host_name, QString url);
protected slots:
void processDlFinished(QNetworkReply* reply);
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
#ifndef QT_NO_OPENSSL
void ignoreSslErrors(QNetworkReply*,QList<QSslError>);
#endif
};

View File

@@ -36,6 +36,7 @@
#include "pluginsource.h"
#include <QProcess>
#include <QHeaderView>
#include <QSettings>
#include <QMenu>
#include <QMessageBox>
#include <QFileDialog>
@@ -44,7 +45,7 @@
#include <QTemporaryFile>
enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID};
#define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/trunk/src/searchengine/nova/engines/"
#define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/trunk/src/search_engine/engines/"
engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) {
setupUi(this);
@@ -81,13 +82,8 @@ void engineSelectDlg::dropEvent(QDropEvent *event) {
QString file;
foreach(file, files) {
qDebug("dropped %s", qPrintable(file));
#ifdef Q_WS_WIN
file = file.replace("file:///", "");
#else
file = file.replace("file://", "");
#endif
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(file);
continue;
}
@@ -112,7 +108,6 @@ void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
void engineSelectDlg::on_updateButton_clicked() {
// Download version file from update server on sourceforge
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(QString(UPDATE_URL)+"versions.txt");
}
@@ -130,17 +125,18 @@ void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) {
void engineSelectDlg::displayContextMenu(const QPoint&) {
QMenu myContextMenu(this);
QModelIndex index;
// Enable/disable pause/start action given the DL state
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
bool has_enable = false, has_disable = false;
QTreeWidgetItem *item;
foreach(item, items) {
QString id = item->text(ENGINE_ID);
if(supported_engines->value(id)->isEnabled() && !has_disable) {
if(supported_engines->value(id)->isEnabled() and !has_disable) {
myContextMenu.addAction(actionDisable);
has_disable = true;
}
if(!supported_engines->value(id)->isEnabled() && !has_enable) {
if(!supported_engines->value(id)->isEnabled() and !has_enable) {
myContextMenu.addAction(actionEnable);
has_enable = true;
}
@@ -164,7 +160,7 @@ void engineSelectDlg::on_actionUninstall_triggered() {
int index = pluginsTree->indexOfTopLevelItem(item);
Q_ASSERT(index != -1);
QString id = item->text(ENGINE_ID);
if(QFile::exists(":/nova/engines/"+id+".py")) {
if(QFile::exists(":/search_engine/engines/"+id+".py")) {
error = true;
// Disable it instead
supported_engines->value(id)->setEnabled(false);
@@ -269,8 +265,7 @@ void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
if(QFile::exists(dest_path)) {
// Backup in case install fails
QFile::copy(dest_path, dest_path+".bak");
misc::safeRemove(dest_path);
misc::safeRemove(dest_path+"c");
QFile::remove(dest_path);
update = true;
}
// Copy the plugin
@@ -281,22 +276,22 @@ void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
if(!supported_engines->contains(plugin_name)) {
if(update) {
// Remove broken file
misc::safeRemove(dest_path);
QFile::remove(dest_path);
// restore backup
QFile::copy(dest_path+".bak", dest_path);
misc::safeRemove(dest_path+".bak");
QFile::remove(dest_path+".bak");
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name));
return;
} else {
// Remove broken file
misc::safeRemove(dest_path);
QFile::remove(dest_path);
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name));
return;
}
}
// Install was successful, remove backup
if(update) {
misc::safeRemove(dest_path+".bak");
QFile::remove(dest_path+".bak");
}
if(update) {
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name));
@@ -355,10 +350,8 @@ void engineSelectDlg::askForPluginUrl() {
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
tr("URL:"), QLineEdit::Normal,
"http://", &ok);
if (ok && !url.isEmpty()) {
setCursor(QCursor(Qt::WaitCursor));
if (ok && !url.isEmpty())
downloader->downloadUrl(url);
}
}
void engineSelectDlg::askForLocalPlugin() {
@@ -403,7 +396,6 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
if(isUpdateNeeded(plugin_name, version)) {
qDebug("Plugin: %s is outdated", qPrintable(plugin_name));
// Downloading update
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(UPDATE_URL+plugin_name+".py");
//downloader->downloadUrl(UPDATE_URL+plugin_name+".png");
updated = true;
@@ -414,7 +406,7 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
// Close file
versions.close();
// Clean up tmp file
misc::safeRemove(versions_file);
QFile::remove(versions_file);
if(file_correct && !updated) {
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
}
@@ -422,7 +414,6 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
}
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
setCursor(QCursor(Qt::ArrowCursor));
qDebug("engineSelectDlg received %s", qPrintable(url));
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
// Icon downloaded
@@ -444,27 +435,26 @@ void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
}
}
// Delete tmp file
misc::safeRemove(filePath);
QFile::remove(filePath);
return;
}
if(url.endsWith("versions.txt")) {
if(!parseVersionsFile(filePath)) {
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
}
misc::safeRemove(filePath);
QFile::remove(filePath);
return;
}
if(url.endsWith(".py", Qt::CaseInsensitive)) {
QString plugin_name = url.split('/').last();
plugin_name.replace(".py", "");
installPlugin(filePath, plugin_name);
misc::safeRemove(filePath);
QFile::remove(filePath);
return;
}
}
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
setCursor(QCursor(Qt::ArrowCursor));
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
qDebug("Could not download favicon: %s, reason: %s", qPrintable(url), qPrintable(reason));
return;

View File

@@ -29,19 +29,17 @@
*/
#include <libtorrent/version.hpp>
#include "eventmanager.h"
#include "qbtsession.h"
#include "bittorrent.h"
#include "scannedfoldersmodel.h"
#include "misc.h"
#include "preferences.h"
//#include "proplistdelegate.h"
#include "torrentpersistentdata.h"
#include <QDebug>
#include <QTranslator>
EventManager::EventManager(QObject *parent)
: QObject(parent)
EventManager::EventManager(QObject *parent, Bittorrent *BTSession)
: QObject(parent), BTSession(BTSession)
{
}
@@ -51,9 +49,9 @@ QList<QVariantMap> EventManager::getEventList() const {
QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
QList<QVariantMap> trackersInfo;
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
QHash<QString, TrackerInfos> trackers_data = QBtSession::instance()->getTrackersInfo(hash);
QHash<QString, TrackerInfos> trackers_data = BTSession->getTrackersInfo(hash);
std::vector<announce_entry> vect_trackers = h.trackers();
std::vector<announce_entry>::iterator it;
for(it = vect_trackers.begin(); it != vect_trackers.end(); it++) {
@@ -62,7 +60,7 @@ QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
tracker["url"] = tracker_url;
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed();
#if LIBTORRENT_VERSION_MINOR > 14
#ifdef LIBTORRENT_0_15
if(it->verified) {
tracker["status"] = tr("Working");
} else {
@@ -96,7 +94,7 @@ QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
QList<QVariantMap> files;
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(!h.is_valid() || !h.has_metadata()) return files;
std::vector<int> priorities = h.file_priorities();
std::vector<size_type> fp;
@@ -106,7 +104,7 @@ QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
int i=0;
for(fi=t.begin_files(); fi != t.end_files(); fi++) {
QVariantMap file;
QString path = QDir::cleanPath(misc::toQStringU(fi->path.string()));
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
QString name = path.split('/').last();
file["name"] = name;
file["size"] = misc::friendlyUnit((double)fi->size);
@@ -115,8 +113,6 @@ QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
else
file["progress"] = 1.; // Empty file...
file["priority"] = priorities[i];
if(i == 0)
file["is_seed"] = h.is_seed();
files << file;
++i;
}
@@ -125,20 +121,8 @@ QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
void EventManager::setGlobalPreferences(QVariantMap m) const {
// UI
if(m.contains("locale")) {
QString locale = m["locale"].toString();
if(Preferences::getLocale() != locale) {
QTranslator *translator = new QTranslator;
if(translator->load(QString::fromUtf8(":/lang/qbittorrent_") + locale)){
qDebug("%s locale recognized, using translation.", qPrintable(locale));
}else{
qDebug("%s locale unrecognized, using default (en_GB).", qPrintable(locale));
}
qApp->installTranslator(translator);
}
Preferences::setLocale(locale);
}
if(m.contains("locale"))
Preferences::setLocale(m["locale"].toString());
// Downloads
if(m.contains("save_path"))
Preferences::setSavePath(m["save_path"].toString());
@@ -147,11 +131,7 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
if(m.contains("temp_path"))
Preferences::setTempPath(m["temp_path"].toString());
if(m.contains("scan_dirs") && m.contains("download_in_scan_dirs")) {
QVariantList download_at_path_tmp = m["download_in_scan_dirs"].toList();
QList<bool> download_at_path;
foreach(QVariant var, download_at_path_tmp) {
download_at_path << var.toBool();
}
QVariantList download_at_path = m["download_in_scan_dirs"].toList();
QStringList old_folders = Preferences::getScanDirs();
QStringList new_folders = m["scan_dirs"].toStringList();
if(download_at_path.size() == new_folders.size()) {
@@ -160,15 +140,14 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
foreach(const QString &old_folder, old_folders) {
// Update deleted folders
if(!new_folders.contains(old_folder)) {
QBtSession::instance()->getScanFoldersModel()->removePath(old_folder);
BTSession->getScanFoldersModel()->removePath(old_folder);
}
}
int i = 0;
foreach(const QString &new_folder, new_folders) {
qDebug("New watched folder: %s", qPrintable(new_folder));
// Update new folders
if(!old_folders.contains(new_folder)) {
QBtSession::instance()->getScanFoldersModel()->addPath(new_folder, download_at_path.at(i));
BTSession->getScanFoldersModel()->addPath(new_folder, download_at_path.at(i).toBool());
}
++i;
}
@@ -176,16 +155,6 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
}
if(m.contains("export_dir"))
Preferences::setExportDir(m["export_dir"].toString());
if(m.contains("mail_notification_enabled"))
Preferences::setMailNotificationEnabled(m["mail_notification_enabled"].toBool());
if(m.contains("mail_notification_email"))
Preferences::setMailNotificationEmail(m["mail_notification_email"].toString());
if(m.contains("mail_notification_smtp"))
Preferences::setMailNotificationSMTP(m["mail_notification_smtp"].toString());
if(m.contains("autorun_enabled"))
Preferences::setAutoRunEnabled(m["autorun_enabled"].toBool());
if(m.contains("autorun_program"))
Preferences::setAutoRunProgram(m["autorun_program"].toString());
if(m.contains("preallocate_all"))
Preferences::preAllocateAllFiles(m["preallocate_all"].toBool());
if(m.contains("queueing_enabled"))
@@ -196,7 +165,7 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
Preferences::setMaxActiveTorrents(m["max_active_torrents"].toInt());
if(m.contains("max_active_uploads"))
Preferences::setMaxActiveUploads(m["max_active_uploads"].toInt());
#if LIBTORRENT_VERSION_MINOR > 14
#ifdef LIBTORRENT_0_15
if(m.contains("incomplete_files_ext"))
Preferences::useIncompleteFilesExtension(m["incomplete_files_ext"].toBool());
#endif
@@ -231,6 +200,12 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
Preferences::setLSDEnabled(m["lsd"].toBool());
if(m.contains("encryption"))
Preferences::setEncryptionSetting(m["encryption"].toInt());
if(m.contains("peer_id"))
Preferences::setPeerID(m["peer_id"].toString());
if(m.contains("peer_version"))
Preferences::setClientVersion(m["peer_version"].toString());
if(m.contains("peer_build"))
Preferences::setClientBuild(m["peer_build"].toString());
// Proxy
if(m.contains("proxy_type"))
Preferences::setPeerProxyType(m["proxy_type"].toInt());
@@ -269,7 +244,7 @@ void EventManager::setGlobalPreferences(QVariantMap m) const {
if(m.contains("web_ui_password"))
Preferences::setWebUiPassword(m["web_ui_password"].toString());
// Reload preferences
QBtSession::instance()->configureSession();
BTSession->configureSession();
}
QVariantMap EventManager::getGlobalPreferences() const {
@@ -281,24 +256,15 @@ QVariantMap EventManager::getGlobalPreferences() const {
data["temp_path_enabled"] = Preferences::isTempPathEnabled();
data["temp_path"] = Preferences::getTempPath();
data["scan_dirs"] = Preferences::getScanDirs();
QVariantList var_list;
foreach(bool b, Preferences::getDownloadInScanDirs()) {
var_list << b;
}
data["download_in_scan_dirs"] = var_list;
data["download_in_scan_dirs"] = Preferences::getDownloadInScanDirs();
data["export_dir_enabled"] = Preferences::isTorrentExportEnabled();
data["export_dir"] = Preferences::getExportDir();
data["mail_notification_enabled"] = Preferences::isMailNotificationEnabled();
data["mail_notification_email"] = Preferences::getMailNotificationEmail();
data["mail_notification_smtp"] = Preferences::getMailNotificationSMTP();
data["autorun_enabled"] = Preferences::isAutoRunEnabled();
data["autorun_program"] = Preferences::getAutoRunProgram();
data["preallocate_all"] = Preferences::preAllocateAllFiles();
data["queueing_enabled"] = Preferences::isQueueingSystemEnabled();
data["max_active_downloads"] = Preferences::getMaxActiveDownloads();
data["max_active_torrents"] = Preferences::getMaxActiveTorrents();
data["max_active_uploads"] = Preferences::getMaxActiveUploads();
#if LIBTORRENT_VERSION_MINOR > 14
#ifdef LIBTORRENT_0_15
data["incomplete_files_ext"] = Preferences::useIncompleteFilesExtension();
#endif
// Connection
@@ -317,6 +283,9 @@ QVariantMap EventManager::getGlobalPreferences() const {
data["pex"] = Preferences::isPeXEnabled();
data["lsd"] = Preferences::isLSDEnabled();
data["encryption"] = Preferences::getEncryptionSetting();
data["peer_id"] = Preferences::getPeerID();
data["peer_version"] = Preferences::getClientVersion();
data["peer_build"] = Preferences::getClientBuild();
// Proxy
data["proxy_type"] = Preferences::getPeerProxyType();
data["proxy_ip"] = Preferences::getPeerProxyIp();
@@ -342,7 +311,7 @@ QVariantMap EventManager::getGlobalPreferences() const {
QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
QVariantMap data;
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
// Save path
QString p = TorrentPersistentData::getSavePath(hash);
@@ -370,7 +339,7 @@ QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
data["time_elapsed"] = elapsed_txt;
data["nb_connections"] = QVariant(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")");
// Update ratio info
double ratio = QBtSession::instance()->getRealRatio(h.hash());
double ratio = BTSession->getRealRatio(h.hash());
if(ratio > 100.)
data["share_ratio"] = QString::fromUtf8("");
else
@@ -395,16 +364,12 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
QVariantMap event;
event["eta"] = QVariant(QString::fromUtf8(""));
if(h.is_paused()) {
if(h.has_error()) {
event["state"] = QVariant("error");
} else {
if(h.is_seed())
event["state"] = QVariant("pausedUP");
else
event["state"] = QVariant("pausedDL");
}
if(h.is_seed())
event["state"] = QVariant("pausedUP");
else
event["state"] = QVariant("pausedDL");
} else {
if(QBtSession::instance()->isQueueingEnabled() && h.is_queued()) {
if(BTSession->isQueueingEnabled() && h.is_queued()) {
if(h.is_seed())
event["state"] = QVariant("queuedUP");
else
@@ -436,7 +401,7 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
event["state"] = QVariant("downloading");
else
event["state"] = QVariant("stalledDL");
event["eta"] = misc::userFriendlyDuration(QBtSession::instance()->getETA(hash));
event["eta"] = misc::userFriendlyDuration(BTSession->getETA(hash));
break;
default:
qDebug("No status, should not happen!!! status is %d", h.state());
@@ -448,7 +413,7 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
event["size"] = QVariant(misc::friendlyUnit(h.actual_size()));
event["progress"] = QVariant((double)h.progress());
event["dlspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.download_payload_rate())));
if(QBtSession::instance()->isQueueingEnabled()) {
if(BTSession->isQueueingEnabled()) {
if(h.queue_position() >= 0)
event["priority"] = QVariant(QString::number(h.queue_position()));
else
@@ -466,7 +431,7 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
leechs += " ("+QString::number(h.num_incomplete())+")";
event["num_leechs"] = QVariant(leechs);
event["seed"] = QVariant(h.is_seed());
double ratio = QBtSession::instance()->getRealRatio(hash);
double ratio = BTSession->getRealRatio(hash);
if(ratio > 100.)
event["ratio"] = QString::fromUtf8("");
else

View File

@@ -36,30 +36,31 @@
#include <QHash>
#include <QVariant>
class Bittorrent;
class EventManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(EventManager)
Q_OBJECT
private:
QHash<QString, QVariantMap> event_list;
Bittorrent* BTSession;
private:
QHash<QString, QVariantMap> event_list;
protected:
void update(QVariantMap event);
protected:
void update(QVariantMap event);
public:
EventManager(QObject *parent, Bittorrent* BTSession);
QList<QVariantMap> getEventList() const;
QVariantMap getPropGeneralInfo(QString hash) const;
QList<QVariantMap> getPropTrackersInfo(QString hash) const;
QList<QVariantMap> getPropFilesInfo(QString hash) const;
QVariantMap getGlobalPreferences() const;
void setGlobalPreferences(QVariantMap m) const;
public:
EventManager(QObject *parent);
QList<QVariantMap> getEventList() const;
QVariantMap getPropGeneralInfo(QString hash) const;
QList<QVariantMap> getPropTrackersInfo(QString hash) const;
QList<QVariantMap> getPropFilesInfo(QString hash) const;
QVariantMap getGlobalPreferences() const;
void setGlobalPreferences(QVariantMap m) const;
public slots:
void addedTorrent(QTorrentHandle& h);
void deletedTorrent(QString hash);
void modifiedTorrent(QTorrentHandle h);
public slots:
void addedTorrent(QTorrentHandle& h);
void deletedTorrent(QString hash);
void modifiedTorrent(QTorrentHandle h);
};
#endif

221
src/feedList.h Normal file
View File

@@ -0,0 +1,221 @@
#ifndef FEEDLIST_H
#define FEEDLIST_H
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QDropEvent>
#include <QDragMoveEvent>
#include <QStringList>
#include <QHash>
#include <QUrl>
#include "rss.h"
class FeedList: public QTreeWidget {
Q_OBJECT
private:
RssManager *rssmanager;
QHash<QTreeWidgetItem*, RssFile*> mapping;
QHash<QString, QTreeWidgetItem*> feeds_items;
QTreeWidgetItem* current_feed;
QTreeWidgetItem *unread_item;
public:
FeedList(QWidget *parent, RssManager *rssmanager): QTreeWidget(parent), rssmanager(rssmanager) {
setContextMenuPolicy(Qt::CustomContextMenu);
setDragDropMode(QAbstractItemView::InternalMove);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setColumnCount(1);
QTreeWidgetItem *___qtreewidgetitem = headerItem();
___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8));
unread_item = new QTreeWidgetItem(this);
unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")"));
unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png")));
itemAdded(unread_item, rssmanager);
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
setCurrentItem(unread_item);
}
~FeedList() {
disconnect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
delete unread_item;
}
void itemAdded(QTreeWidgetItem *item, RssFile* file) {
mapping[item] = file;
if(file->getType() == RssFile::STREAM) {
feeds_items[file->getID()] = item;
}
}
void itemAboutToBeRemoved(QTreeWidgetItem *item) {
RssFile* file = mapping.take(item);
if(file->getType() == RssFile::STREAM) {
feeds_items.remove(file->getID());
} else {
QList<RssStream*> feeds = ((RssFolder*)file)->getAllFeeds();
foreach(RssStream* feed, feeds) {
feeds_items.remove(feed->getID());
}
}
}
bool hasFeed(QString url) const {
return feeds_items.contains(QUrl(url).toString());
}
QList<QTreeWidgetItem*> getAllFeedItems() const {
return feeds_items.values();
}
QTreeWidgetItem* getUnreadItem() const {
return unread_item;
}
QStringList getItemPath(QTreeWidgetItem* item) const {
QStringList path;
if(item) {
if(item->parent())
path << getItemPath(item->parent());
path.append(getRSSItem(item)->getID());
}
return path;
}
QList<QTreeWidgetItem*> getAllOpenFolders(QTreeWidgetItem *parent=0) const {
QList<QTreeWidgetItem*> open_folders;
int nbChildren;
if(parent)
nbChildren = parent->childCount();
else
nbChildren = topLevelItemCount();
for(int i=0; i<nbChildren; ++i) {
QTreeWidgetItem *item;
if(parent)
item = parent->child(i);
else
item = topLevelItem(i);
if(getItemType(item) == RssFile::FOLDER && item->isExpanded()) {
QList<QTreeWidgetItem*> open_subfolders = getAllOpenFolders(item);
if(!open_subfolders.empty()) {
open_folders << open_subfolders;
} else {
open_folders << item;
}
}
}
return open_folders;
}
QList<QTreeWidgetItem*> getAllFeedItems(QTreeWidgetItem* folder) {
QList<QTreeWidgetItem*> feeds;
int nbChildren = folder->childCount();
for(int i=0; i<nbChildren; ++i) {
QTreeWidgetItem *item = folder->child(i);
if(getItemType(item) == RssFile::STREAM) {
feeds << item;
} else {
feeds << getAllFeedItems(item);
}
}
return feeds;
}
RssFile* getRSSItem(QTreeWidgetItem *item) const {
return mapping.value(item, 0);
}
RssFile::FileType getItemType(QTreeWidgetItem *item) const {
return mapping.value(item)->getType();
}
QString getItemID(QTreeWidgetItem *item) const {
return mapping.value(item)->getID();
}
QTreeWidgetItem* getTreeItemFromUrl(QString url) const{
return feeds_items.value(url, 0);
}
RssStream* getRSSItemFromUrl(QString url) const {
return (RssStream*)getRSSItem(getTreeItemFromUrl(url));
}
QTreeWidgetItem* currentItem() const {
return current_feed;
}
QTreeWidgetItem* currentFeed() const {
return current_feed;
}
signals:
void foldersAltered(QList<QTreeWidgetItem*> folders);
void overwriteAttempt(QString filename);
protected slots:
void updateCurrentFeed(QTreeWidgetItem* new_item) {
if(!new_item) return;
if(!mapping.contains(new_item)) return;
if((getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
current_feed = new_item;
}
protected:
void dragMoveEvent(QDragMoveEvent * event) {
QTreeWidgetItem *item = itemAt(event->pos());
if(item == unread_item) {
event->ignore();
} else {
if(item && getItemType(item) != RssFile::FOLDER)
event->ignore();
else {
if(selectedItems().contains(unread_item)) {
event->ignore();
} else {
QTreeWidget::dragMoveEvent(event);
}
}
}
}
void dropEvent(QDropEvent *event) {
qDebug("dropEvent");
QList<QTreeWidgetItem*> folders_altered;
QTreeWidgetItem *dest_folder_item = itemAt(event->pos());
RssFolder *dest_folder;
if(dest_folder_item) {
dest_folder = (RssFolder*)getRSSItem(dest_folder_item);
folders_altered << dest_folder_item;
} else {
dest_folder = rssmanager;
}
QList<QTreeWidgetItem *> src_items = selectedItems();
// Check if there is not going to overwrite another file
foreach(QTreeWidgetItem *src_item, src_items) {
RssFile *file = getRSSItem(src_item);
if(dest_folder->hasChild(file->getID())) {
emit overwriteAttempt(file->getID());
return;
}
}
// Proceed with the move
foreach(QTreeWidgetItem *src_item, src_items) {
QTreeWidgetItem *parent_folder = src_item->parent();
if(parent_folder && !folders_altered.contains(parent_folder))
folders_altered << parent_folder;
// Actually move the file
RssFile *file = getRSSItem(src_item);
rssmanager->moveFile(file, dest_folder);
}
QTreeWidget::dropEvent(event);
if(dest_folder_item)
dest_folder_item->setExpanded(true);
// Emit signal for update
if(!folders_altered.empty())
emit foldersAltered(folders_altered);
}
};
#endif // FEEDLIST_H

514
src/feeddownloader.h Normal file
View File

@@ -0,0 +1,514 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 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
*/
#ifndef FEEDDOWNLOADER_H
#define FEEDDOWNLOADER_H
#include <QString>
#include <QSettings>
#include <QListWidget>
#include <QListWidgetItem>
#include <QInputDialog>
#include <QMessageBox>
#include <QRegExp>
#include <QMenu>
#include <QFile>
#include <QDataStream>
#include <QFileDialog>
#include "bittorrent.h"
#include "ui_feeddownloader.h"
#if QT_VERSION >= 0x040500
#include <QHash>
#else
#include <QMap>
#define QHash QMap
#define toHash toMap
#endif
class FeedFilter: public QHash<QString, QVariant> {
private:
bool valid;
public:
FeedFilter():valid(true) {}
FeedFilter(bool valid): valid(valid) {}
FeedFilter(QHash<QString, QVariant> filter): QHash<QString, QVariant>(filter), valid(true) {}
bool matches(QString s) {
QStringList match_tokens = getMatchingTokens();
foreach(const QString& token, match_tokens) {
if(token.isEmpty() || token == "")
continue;
QRegExp reg(token, Qt::CaseInsensitive, QRegExp::Wildcard);
//reg.setMinimal(false);
if(reg.indexIn(s) < 0) return false;
}
qDebug("Checking not matching tokens");
// Checking not matching
QStringList notmatch_tokens = getNotMatchingTokens();
foreach(const QString& token, notmatch_tokens) {
if(token.isEmpty()) continue;
QRegExp reg(token, Qt::CaseInsensitive, QRegExp::Wildcard);
if(reg.indexIn(s) > -1) return false;
}
return true;
}
bool isValid() const {
return valid;
}
QStringList getMatchingTokens() const {
QString matches = this->value("matches", "").toString();
return matches.split(" ");
}
QString getMatchingTokens_str() const {
return this->value("matches", "").toString();
}
void setMatchingTokens(QString tokens) {
tokens = tokens.trimmed();
if(tokens.isEmpty())
(*this)["matches"] = "";
else
(*this)["matches"] = tokens;
}
QStringList getNotMatchingTokens() const {
QString notmatching = this->value("not", "").toString();
return notmatching.split(" ");
}
QString getNotMatchingTokens_str() const {
return this->value("not", "").toString();
}
void setNotMatchingTokens(QString tokens) {
(*this)["not"] = tokens.trimmed();
}
QString getSavePath() const {
return this->value("save_path", "").toString();
}
void setSavePath(QString save_path) {
(*this)["save_path"] = save_path;
}
};
class FeedFilters : public QHash<QString, QVariant> {
private:
QString feed_url;
public:
FeedFilters() {}
FeedFilters(QString feed_url, QHash<QString, QVariant> filters): QHash<QString, QVariant>(filters), feed_url(feed_url) {}
bool hasFilter(QString name) const {
return this->contains(name);
}
FeedFilter* matches(QString s) {
if(!isDownloadingEnabled()) return 0;
if(this->size() == 0) return new FeedFilter(false);
foreach(QVariant var_hash_filter, this->values()) {
QHash<QString, QVariant> hash_filter = var_hash_filter.toHash();
FeedFilter *filter = new FeedFilter(hash_filter);
if(filter->matches(s))
return filter;
else
delete filter;
}
return 0;
}
QStringList names() const {
return this->keys();
}
FeedFilter getFilter(QString name) const {
if(this->contains(name))
return FeedFilter(this->value(name).toHash());
return FeedFilter();
}
void setFilter(QString name, FeedFilter f) {
(*this)[name] = f;
}
bool isDownloadingEnabled() const {
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
return feeds_w_downloader.value(feed_url, false).toBool();
}
void setDownloadingEnabled(bool enabled) {
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
feeds_w_downloader[feed_url] = enabled;
qBTRSS.setValue("downloader_on", feeds_w_downloader);
}
static FeedFilters getFeedFilters(QString url) {
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
return FeedFilters(url, all_feeds_filters.value(url, QHash<QString, QVariant>()).toHash());
}
void rename(QString old_name, QString new_name) {
Q_ASSERT(this->contains(old_name));
(*this)[new_name] = this->take(old_name);
}
bool serialize(QString path) {
QFile f(path);
if(f.open(QIODevice::WriteOnly)) {
QDataStream out(&f);
out.setVersion(QDataStream::Qt_4_3);
out << (QHash<QString, QVariant>)(*this);
f.close();
return true;
} else {
return false;
}
}
bool unserialize(QString path) {
QFile f(path);
if(f.open(QIODevice::ReadOnly)) {
QDataStream in(&f);
in.setVersion(QDataStream::Qt_4_3);
QHash<QString, QVariant> tmp;
in >> tmp;
qDebug("Unserialized %d filters", tmp.size());
foreach(const QString& key, tmp.keys()) {
(*this)[key] = tmp[key];
}
return true;
} else {
return false;
}
}
void save() {
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
qDebug("Saving filters for feed: %s (%d filters)", qPrintable(feed_url), (*this).size());
all_feeds_filters[feed_url] = *this;
qBTRSS.setValue("feed_filters", all_feeds_filters);
}
};
class FeedDownloaderDlg : public QDialog, private Ui_FeedDownloader{
Q_OBJECT
private:
QString feed_url;
QString feed_name;
FeedFilters filters;
Bittorrent *BTSession;
QString selected_filter; // name
public:
FeedDownloaderDlg(QWidget *parent, QString feed_url, QString feed_name, Bittorrent* BTSession): QDialog(parent), feed_url(feed_url), feed_name(feed_name), BTSession(BTSession), selected_filter(QString::null){
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
Q_ASSERT(!feed_name.isEmpty());
rssfeed_lbl->setText(feed_name);
filters = FeedFilters::getFeedFilters(feed_url);
// Connect Signals/Slots
connect(filtersList, SIGNAL(currentItemChanged(QListWidgetItem* , QListWidgetItem *)), this, SLOT(showFilterSettings(QListWidgetItem *)));
connect(filtersList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFiltersListMenu(const QPoint&)));
connect(actionAdd_filter, SIGNAL(triggered()), this, SLOT(addFilter()));
connect(actionRemove_filter, SIGNAL(triggered()), this, SLOT(deleteFilter()));
connect(actionRename_filter, SIGNAL(triggered()), this, SLOT(renameFilter()));
connect(del_button, SIGNAL(clicked(bool)), this, SLOT(deleteFilter()));
connect(add_button, SIGNAL(clicked(bool)), this, SLOT(addFilter()));
connect(enableDl_cb, SIGNAL(stateChanged(int)), this, SLOT(enableFilterBox(int)));
// Restore saved info
enableDl_cb->setChecked(filters.isDownloadingEnabled());
fillFiltersList();
if(filters.size() > 0) {
// Select first filter
filtersList->setCurrentItem(filtersList->item(0));
//showFilterSettings(filtersList->item(0));
}
// Show
show();
}
~FeedDownloaderDlg() {
if(enableDl_cb->isChecked())
emit filteringEnabled();
// Make sure we save everything
saveCurrentFilterSettings();
filters.save();
}
protected slots:
void saveCurrentFilterSettings() {
if(!selected_filter.isEmpty()) {
FeedFilter filter = filters.getFilter(selected_filter);
filter.setMatchingTokens(match_line->text());
filter.setNotMatchingTokens(notmatch_line->text());
QString save_path = savepath_line->text().trimmed();
if(save_path.isEmpty())
save_path = BTSession->getDefaultSavePath();
filter.setSavePath(save_path);
// Save updated filter
filters.setFilter(selected_filter, filter);
}
}
void on_browse_button_clicked() {
QString default_path = savepath_line->text();
if(default_path.isEmpty() || !QDir(default_path).exists()) {
default_path = QDir::homePath();
}
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
if(!dir.isNull() and QDir(dir).exists()) {
savepath_line->setText(dir);
}
}
void fillFiltersList() {
// Fill filter list
foreach(QString filter_name, filters.names()) {
new QListWidgetItem(filter_name, filtersList);
}
}
void displayFiltersListMenu(const QPoint&) {
QMenu myFiltersListMenu(this);
if(filtersList->selectedItems().size() > 0) {
myFiltersListMenu.addAction(actionRename_filter);
myFiltersListMenu.addAction(actionRemove_filter);
} else {
myFiltersListMenu.addAction(actionAdd_filter);
}
// Call menu
myFiltersListMenu.exec(QCursor::pos());
}
void showFilterSettings(QListWidgetItem *item) {
// First, save current filter settings
saveCurrentFilterSettings();
// Clear all fields
clearFields();
if(!item) {
qDebug("No new selected item");
return;
}
// Actually show filter settings
QString filter_name = item->text();
FeedFilter filter = filters.getFilter(filter_name);
filterSettingsBox->setEnabled(true);
match_line->setText(filter.getMatchingTokens_str());
if(match_line->text().trimmed().isEmpty()) {
match_line->setText(filter_name);
}
notmatch_line->setText(filter.getNotMatchingTokens_str());
QString save_path = filter.getSavePath();
if(save_path.isEmpty())
save_path = BTSession->getDefaultSavePath();
savepath_line->setText(save_path);
// Update selected filter
selected_filter = filter_name;
}
void deleteFilter() {
QList<QListWidgetItem *> items = filtersList->selectedItems();
if(items.size() == 1) {
QListWidgetItem * item = items.first();
filters.remove(item->text());
selected_filter = QString::null;
delete item;
// Reset Filter settings view
if(filters.size() == 0) {
clearFields();
filterSettingsBox->setEnabled(false);
}
}
}
void renameFilter() {
QList<QListWidgetItem *> items = filtersList->selectedItems();
if(items.size() == 1) {
QListWidgetItem *item = items.first();
QString current_name = item->text();
QString new_name;
bool validated = false;
do {
new_name = askFilterName(current_name);
if(new_name.isNull() || new_name == current_name) return;
if(!filters.hasFilter(new_name)) {
validated = true;
} else {
QMessageBox::warning(0, tr("Invalid filter name"), tr("This filter name is already in use."));
}
}while(!validated);
// Save the current filter
saveCurrentFilterSettings();
// Rename the filter
filters.rename(current_name, new_name);
if(selected_filter == current_name)
selected_filter = new_name;
item->setText(new_name);
}
}
void enableFilterBox(int state) {
if(state == Qt::Checked) {
filtersBox->setEnabled(true);
filters.setDownloadingEnabled(true);
} else {
filtersBox->setEnabled(false);
filters.setDownloadingEnabled(false);
}
}
QString askFilterName(QString name=QString::null) {
QString name_prop;
if(name.isEmpty())
name_prop = tr("New filter");
else
name_prop = name;
QString new_name;
bool validated = false;
do {
bool ok;
new_name = QInputDialog::getText(this, tr("Please choose a name for this filter"), tr("Filter name:"), QLineEdit::Normal, name_prop, &ok);
if(!ok) {
return QString::null;
}
// Validate filter name
new_name = new_name.trimmed();
if(new_name.isEmpty()) {
// Cannot be left empty
QMessageBox::warning(0, tr("Invalid filter name"), tr("The filter name cannot be left empty."));
} else {
validated = true;
}
} while(!validated);
return new_name;
}
void addFilter() {
QString filter_name = QString::null;
bool validated = false;
do {
filter_name = askFilterName();
if(filter_name.isNull()) return;
if(filters.hasFilter(filter_name)) {
// Filter alread exists
QMessageBox::warning(0, tr("Invalid filter name"), tr("This filter name is already in use."));
} else {
validated = true;
}
}while(!validated);
QListWidgetItem *it = new QListWidgetItem(filter_name, filtersList);
filtersList->setCurrentItem(it);
//showFilterSettings(it);
}
void clearFields() {
match_line->clear();
notmatch_line->clear();
savepath_line->clear();
test_res_lbl->setText("");
test_line->clear();
}
void on_testButton_clicked(bool) {
test_res_lbl->clear();
if(selected_filter.isEmpty()) {
qDebug("No filter is selected!!!");
return;
}
QString s = test_line->text().trimmed();
if(s.isEmpty()) {
QMessageBox::warning(0, tr("Filter testing error"), tr("Please specify a test torrent name."));
return;
}
// Get current filter
saveCurrentFilterSettings();
FeedFilter f = filters.getFilter(selected_filter);
if(f.matches(s))
test_res_lbl->setText("<b><font color=\"green\">"+tr("matches")+"</font></b>");
else
test_res_lbl->setText("<b><font color=\"red\">"+tr("does not match")+"</font></b>");
}
void on_importButton_clicked(bool) {
QString source = QFileDialog::getOpenFileName(0, tr("Select file to import"), QDir::homePath(), tr("Filters Files")+QString::fromUtf8(" (*.filters)"));
if(source.isEmpty()) return;
if(filters.unserialize(source)) {
// Clean up first
clearFields();
filtersList->clear();
selected_filter = QString::null;
fillFiltersList();
if(filters.size() > 0)
filtersList->setCurrentItem(filtersList->item(0));
QMessageBox::information(0, tr("Import successful"), tr("Filters import was successful."));
} else {
QMessageBox::warning(0, tr("Import failure"), tr("Filters could not be imported due to an I/O error."));
}
}
void on_exportButton_clicked(bool) {
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination file"), QDir::homePath(), tr("Filters Files")+QString::fromUtf8(" (*.filters)"));
if(destination.isEmpty()) return;
// Append file extension
if(!destination.endsWith(".filters"))
destination += ".filters";
/*if(QFile::exists(destination)) {
int ret = QMessageBox::question(0, tr("Overwriting confirmation"), tr("Are you sure you want to overwrite existing file?"), QMessageBox::Yes|QMessageBox::No);
if(ret != QMessageBox::Yes) return;
}*/
if(filters.serialize(destination))
QMessageBox::information(0, tr("Export successful"), tr("Filters export was successful."));
else
QMessageBox::warning(0, tr("Export failure"), tr("Filters could not be exported due to an I/O error."));
}
signals:
void filteringEnabled();
};
#undef QHash
#undef toHash
#endif // FEEDDOWNLOADER_H

View File

@@ -2,13 +2,12 @@
#define FILESYSTEMWATCHER_H
#include <QFileSystemWatcher>
#include <QDir>
#include <QTimer>
#include <QPointer>
#include <QStringList>
#include <QHash>
#ifndef Q_WS_WIN
#include <QTimer>
#include <QDir>
#include <QPointer>
#include <QStringList>
#include <QSet>
#include <iostream>
#include <errno.h>
@@ -20,8 +19,6 @@
#endif
#endif
#include "misc.h"
#ifndef CIFS_MAGIC_NUMBER
#define CIFS_MAGIC_NUMBER 0xFF534D42
#endif
@@ -30,9 +27,6 @@
#define NFS_SUPER_MAGIC 0x6969
#endif
const int WATCH_INTERVAL = 10000; // 10 sec
const int MAX_PARTIAL_RETRIES = 5;
/*
* Subclassing QFileSystemWatcher in order to support Network File
* System watching (NFS, CIFS) on Linux and Mac OS.
@@ -45,14 +39,11 @@ private:
QList<QDir> watched_folders;
QPointer<QTimer> watch_timer;
#endif
QStringList m_filters;
// Partial torrents
QHash<QString, int> m_partialTorrents;
QPointer<QTimer> m_partialTorrentTimer;
QStringList filters;
#ifndef Q_WS_WIN
private:
static bool isNetworkFileSystem(QString path) {
protected:
bool isNetworkFileSystem(QString path) {
QString file = path;
if(!file.endsWith(QDir::separator()))
file += QDir::separator();
@@ -108,7 +99,7 @@ private:
public:
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
m_filters << "*.torrent";
filters << "*.torrent";
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString)));
}
@@ -117,8 +108,6 @@ public:
if(watch_timer)
delete watch_timer;
#endif
if(m_partialTorrentTimer)
delete m_partialTorrentTimer;
}
QStringList directories() const {
@@ -148,7 +137,7 @@ public:
if (!watch_timer) {
watch_timer = new QTimer(this);
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders()));
watch_timer->start(WATCH_INTERVAL); // 5 sec
watch_timer->start(5000); // 5 sec
}
} else {
#endif
@@ -191,7 +180,6 @@ protected slots:
}
void scanNetworkFolders() {
#ifndef Q_WS_WIN
qDebug("scanNetworkFolders() called");
QStringList torrents;
// Network folders scan
@@ -204,75 +192,16 @@ protected slots:
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
emit torrentsAdded(torrents);
}
#endif
}
void processPartialTorrents() {
QStringList no_longer_partial;
// Check which torrents are still partial
foreach(const QString& torrent_path, m_partialTorrents.keys()) {
if(!QFile::exists(torrent_path)) {
m_partialTorrents.remove(torrent_path);
continue;
}
if(misc::isValidTorrentFile(torrent_path)) {
no_longer_partial << torrent_path;
m_partialTorrents.remove(torrent_path);
} else {
if(m_partialTorrents[torrent_path] >= MAX_PARTIAL_RETRIES) {
m_partialTorrents.remove(torrent_path);
QFile::rename(torrent_path, torrent_path+".invalid");
} else {
m_partialTorrents[torrent_path]++;
}
}
}
// Stop the partial timer if necessary
if(m_partialTorrents.empty()) {
m_partialTorrentTimer->stop();
m_partialTorrentTimer->deleteLater();
qDebug("No longer any partial torrent.");
} else {
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
m_partialTorrentTimer->start(WATCH_INTERVAL);
}
// Notify of new torrents
if(!no_longer_partial.isEmpty())
emit torrentsAdded(no_longer_partial);
}
signals:
void torrentsAdded(QStringList &pathList);
private:
void startPartialTorrentTimer() {
Q_ASSERT(!m_partialTorrents.isEmpty());
if(!m_partialTorrentTimer) {
m_partialTorrentTimer = new QTimer();
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
m_partialTorrentTimer->setSingleShot(true);
m_partialTorrentTimer->start(WATCH_INTERVAL);
}
}
void addTorrentsFromDir(const QDir &dir, QStringList &torrents) {
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
foreach(const QString &file, files) {
const QString file_abspath = dir.absoluteFilePath(file);
if(misc::isValidTorrentFile(file_abspath)) {
torrents << file_abspath;
} else {
if(!m_partialTorrents.contains(file_abspath)) {
qDebug("Partial torrent detected at: %s", qPrintable(file_abspath));
qDebug("Delay the file's processing...");
m_partialTorrents.insert(file_abspath, 0);
}
}
}
if(!m_partialTorrents.empty())
startPartialTorrentTimer();
const QStringList &files = dir.entryList(filters, QDir::Files, QDir::Unsorted);
foreach(const QString &file, files)
torrents << dir.canonicalPath() + '/' + file;
}
};

View File

@@ -34,8 +34,8 @@
#include <QThread>
#include <QFile>
#include <QDataStream>
#include <QRegExp>
#include <QStringList>
#include <QHostAddress>
#include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp>
@@ -46,364 +46,373 @@ using namespace std;
// P2B Stuff
#include <string.h>
#ifdef Q_WS_WIN
#include <Winsock2.h>
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#include <arpa/inet.h>
#endif
// End of P2B stuff
class FilterParserThread : public QThread {
Q_OBJECT
private:
session *s;
ip_filter filter;
bool abort;
QString filePath;
protected:
QString cleanupIPAddress(QString _ip) {
QHostAddress ip(_ip.trimmed());
if(ip.isNull()) {
return QString();
}
return ip.toString();
}
void run(){
qDebug("Processing filter file");
if(filePath.endsWith(".dat", Qt::CaseInsensitive)) {
// eMule DAT file
parseDATFilterFile(filePath);
} else {
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
// PeerGuardian p2p file
parseP2PFilterFile(filePath);
private:
session *s;
ip_filter filter;
bool abort;
QString filePath;
protected:
void run(){
qDebug("Processing filter file");
if(filePath.endsWith(".dat", Qt::CaseInsensitive)) {
// eMule DAT file
parseDATFilterFile(filePath);
} else {
if(filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
// PeerGuardian p2b file
parseP2BFilterFile(filePath);
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
// PeerGuardian p2p file
parseP2PFilterFile(filePath);
} else {
// Default: eMule DAT format
parseDATFilterFile(filePath);
if(filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
// PeerGuardian p2b file
parseP2BFilterFile(filePath);
} else {
// Default: eMule DAT format
parseDATFilterFile(filePath);
}
}
}
s->set_ip_filter(filter);
qDebug("IP Filter thread: finished parsing, filter applied");
}
s->set_ip_filter(filter);
qDebug("IP Filter thread: finished parsing, filter applied");
}
public:
FilterParserThread(QObject* parent, session *s) : QThread(parent), s(s), abort(false) {
}
~FilterParserThread(){
abort = true;
wait();
}
// Parser for eMule ip filter in DAT format
void parseDATFilterFile(QString filePath) {
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine();
// Ignoring empty lines
line = line.trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line should be splitted by commas
QList<QByteArray> partsList = line.split(',');
const uint nbElem = partsList.size();
// IP Range should be splitted by a dash
QList<QByteArray> IPs = partsList.first().split('-');
if(IPs.size() != 2) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("Line was %s", line.constData());
continue;
}
boost::system::error_code ec;
const QString strStartIP = cleanupIPAddress(IPs.at(0));
if(strStartIP.isEmpty()) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
continue;
}
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
if(ec) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
continue;
}
const QString strEndIP = cleanupIPAddress(IPs.at(1));
if(strEndIP.isEmpty()) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
continue;
}
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
if(ec) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
continue;
}
if(startAddr.is_v4() != endAddr.is_v4()) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("One IP is IPv4 and the other is IPv6!");
continue;
}
// Check if there is an access value (apparently not mandatory)
int nbAccess = 0;
if(nbElem > 1) {
// There is possibly one
nbAccess = partsList.at(1).trimmed().toInt();
}
if(nbAccess > 127) {
// Ignoring this rule because access value is too high
continue;
}
// Now Add to the filter
try {
filter.add_rule(startAddr, endAddr, ip_filter::blocked);
}catch(exception){
qDebug("Bad line in filter file, avoided crash...");
}
}
file.close();
public:
FilterParserThread(QObject* parent, session *s) : QThread(parent), s(s), abort(false) {
}
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2PFilterFile(QString filePath) {
QFile file(filePath);
QStringList IP;
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine().trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line is splitted by :
QList<QByteArray> partsList = line.split(':');
if(partsList.size() < 2){
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
// Get IP range
QList<QByteArray> IPs = partsList.last().split('-');
if(IPs.size() != 2) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("line was: %s", line.constData());
continue;
}
boost::system::error_code ec;
QString strStartIP = cleanupIPAddress(IPs.at(0));
if(strStartIP.isEmpty()) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
continue;
}
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
if(ec) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
continue;
}
QString strEndIP = cleanupIPAddress(IPs.at(1));
if(strEndIP.isEmpty()) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
continue;
}
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
if(ec) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
continue;
}
if(startAddr.is_v4() != endAddr.is_v4()) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Line was: %s", line.constData());
continue;
}
try {
filter.add_rule(startAddr, endAddr, ip_filter::blocked);
} catch(std::exception&) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Line was: %s", line.constData());
continue;
}
}
file.close();
}
}
int getlineInStream(QDataStream& stream, string& name, char delim) {
char c;
int total_read = 0;
int read;
do {
read = stream.readRawData(&c, 1);
total_read += read;
if(read > 0) {
if(c != delim) {
name += c;
} else {
// Delim found
return total_read;
}
}
} while(read > 0);
return total_read;
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2BFilterFile(QString filePath) {
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
QDataStream stream(&file);
// Read header
char buf[7];
unsigned char version;
if(
!stream.readRawData(buf, sizeof(buf)) ||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
!stream.readRawData((char*)&version, sizeof(version))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
if(version==1 || version==2) {
qDebug ("p2b version 1 or 2");
unsigned int start, end;
string name;
while(getlineInStream(stream, name, '\0') && !abort) {
if(
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
}
}
else if(version==3) {
qDebug ("p2b version 3");
unsigned int namecount;
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
namecount=ntohl(namecount);
// Reading names although, we don't really care about them
for(unsigned int i=0; i<namecount; i++) {
string name;
if(!getlineInStream(stream, name, '\0')) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
if(abort) return;
}
// Reading the ranges
unsigned int rangecount;
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
rangecount=ntohl(rangecount);
unsigned int name, start, end;
for(unsigned int i=0; i<rangecount; i++) {
if(
!stream.readRawData((char*)&name, sizeof(name)) ||
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
if(abort) return;
}
} else {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
file.close();
}
}
// Process ip filter file
// Supported formats:
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
void processFilterFile(QString _filePath){
// First, import current filter
filter = s->get_ip_filter();
if(isRunning()) {
// Already parsing a filter, abort first
~FilterParserThread(){
abort = true;
wait();
}
abort = false;
filePath = _filePath;
// Run it
start();
}
static void processFilterList(session *s, QStringList IPs) {
// First, import current filter
ip_filter filter = s->get_ip_filter();
foreach(const QString &ip, IPs) {
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
boost::system::error_code ec;
address_v4 addr = address_v4::from_string(ip.toLocal8Bit().constData(), ec);
Q_ASSERT(!ec);
if(!ec)
filter.add_rule(addr, addr, ip_filter::blocked);
// Parser for eMule ip filter in DAT format
void parseDATFilterFile(QString filePath) {
const QRegExp is_ipv6(QString::fromUtf8("^[0-9a-f]{4}(:[0-9a-f]{4}){7}$"), Qt::CaseInsensitive, QRegExp::RegExp);
const QRegExp is_ipv4(QString::fromUtf8("^(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}$"), Qt::CaseInsensitive, QRegExp::RegExp);
QString strStartIP, strEndIP;
bool IPv4 = true;
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine();
// Ignoring empty lines
line = line.trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line is not commented
QList<QByteArray> partsList = line.split(',');
unsigned int nbElem = partsList.size();
// IP Range can be splitted by a dash or a comma...
// Check if there is a dash in first part
QByteArray firstPart = partsList.at(0);
int nbAccess = 0;
if(firstPart.contains('-')) {
// Range is splitted by a dash
QList<QByteArray> IPs = firstPart.split('-');
if(IPs.size() != 2) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
continue;
}
strStartIP = IPs.at(0).trimmed();
strEndIP = IPs.at(1).trimmed();
// Check if IPs are correct
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
IPv4 = true;
} else {
if(strStartIP.contains(is_ipv6) && strEndIP.contains(is_ipv6)) {
IPv4 = false;
} else {
// Could not determine IP format
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
continue;
}
}
// Check if there is an access value (apparently not mandatory)
if(nbElem > 1) {
// There is possibly one
bool ok;
nbAccess = partsList.at(1).trimmed().toInt(&ok);
if(!ok){
nbAccess = 0;
}
}
} else {
// Range is probably splitted by a comma
unsigned int nbElem = partsList.size();
if(nbElem > 1) {
strStartIP = firstPart.trimmed();
strEndIP = partsList.at(1).trimmed();
// Check if IPs are correct
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
IPv4 = true;
} else {
if(strStartIP.contains(is_ipv6) && strEndIP.contains(is_ipv6)) {
IPv4 = false;
} else {
// Could not determine IP format
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
continue;
}
}
// Check if there is an access value (apparently not mandatory)
if(nbElem > 2) {
// There is possibly one
bool ok;
nbAccess = partsList.at(2).trimmed().toInt(&ok);
if(!ok){
nbAccess = 0;
}
}
}
}
if(nbAccess > 127) {
// Ignoring this rule because access value is too high
continue;
}
// Now Add to the filter
QStringList IP;
try {
if(IPv4) {
//IPv4 addresses
IP = strStartIP.split('.');
if(IP.size() != 4)
throw exception();
address_v4 start((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
IP = strEndIP.split('.');
if(IP.size() != 4)
throw exception();
address_v4 last((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
} else {
// IPv6, ex : 1fff:0000:0a88:85a3:0000:0000:ac1f:8001
IP = strStartIP.split(':');
address_v6 start = address_v6::from_string(strStartIP.remove(':', 0).toLocal8Bit().data());
IP = strEndIP.split(':');
address_v6 last = address_v6::from_string(strEndIP.remove(':', 0).toLocal8Bit().data());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
}
}catch(exception){
qDebug("Bad line in filter file, avoided crash...");
}
}
file.close();
}
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2PFilterFile(QString filePath) {
const QRegExp is_ipv4(QString::fromUtf8("^(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}$"), Qt::CaseInsensitive, QRegExp::RegExp);
QFile file(filePath);
QStringList IP;
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine();
// Ignoring empty lines
line = line.trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line is not commented
QList<QByteArray> partsList = line.split(':');
if(partsList.size() < 2){
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
// Get IP range
QList<QByteArray> IPs = partsList.last().split('-');
if(IPs.size() != 2) {
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
QString strStartIP = IPs.at(0).trimmed();
QString strEndIP = IPs.at(1).trimmed();
// Check IPs format (IPv4 only)
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
// IPv4
IP = strStartIP.split('.');
address_v4 start((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
IP = strEndIP.split('.');
address_v4 last((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
} else {
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
}
file.close();
}
}
int getlineInStream(QDataStream& stream, string& name, char delim) {
char c;
int total_read = 0;
int read;
do {
read = stream.readRawData(&c, 1);
total_read += read;
if(read > 0) {
if(c != delim) {
name += c;
} else {
// Delim found
return total_read;
}
}
} while(read > 0);
return total_read;
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2BFilterFile(QString filePath) {
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
QDataStream stream(&file);
// Read header
char buf[7];
unsigned char version;
if(
!stream.readRawData(buf, sizeof(buf)) ||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
!stream.readRawData((char*)&version, sizeof(version))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
if(version==1 || version==2) {
qDebug ("p2b version 1 or 2");
unsigned int start, end;
string name;
while(getlineInStream(stream, name, '\0') && !abort) {
if(
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
}
}
else if(version==3) {
qDebug ("p2b version 3");
unsigned int namecount;
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
namecount=ntohl(namecount);
// Reading names although, we don't really care about them
for(unsigned int i=0; i<namecount; i++) {
string name;
if(!getlineInStream(stream, name, '\0')) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
if(abort) return;
}
// Reading the ranges
unsigned int rangecount;
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
rangecount=ntohl(rangecount);
unsigned int name, start, end;
for(unsigned int i=0; i<rangecount; i++) {
if(
!stream.readRawData((char*)&name, sizeof(name)) ||
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
if(abort) return;
}
} else {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
file.close();
}
}
// Process ip filter file
// Supported formats:
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
void processFilterFile(QString _filePath){
// First, import current filter
filter = s->get_ip_filter();
if(isRunning()) {
// Already parsing a filter, abort first
abort = true;
wait();
}
abort = false;
filePath = _filePath;
// Run it
start();
}
static void processFilterList(session *s, QStringList IPs) {
// First, import current filter
ip_filter filter = s->get_ip_filter();
foreach(const QString &ip, IPs) {
qDebug("Manual ban of peer %s", ip.toLocal8Bit().data());
address_v4 addr = address_v4::from_string(ip.toLocal8Bit().data());
filter.add_rule(addr, addr, ip_filter::blocked);
}
s->set_ip_filter(filter);
}
s->set_ip_filter(filter);
}
};

View File

@@ -40,8 +40,7 @@
using namespace libtorrent;
class GeoIP : public QObject {
Q_OBJECT
class GeoIP {
protected:
#ifdef WITH_GEOIP_EMBEDDED
static QString geoipFolder(bool embedded=false) {
@@ -69,19 +68,13 @@ protected:
// 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;
}
if(!gfolder.mkpath(geoipFolder(false))) return;
}
// Remove destination files
if(QFile::exists(geoipDBpath(false)))
misc::safeRemove(geoipDBpath(false));
QFile::remove(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;
}
QFile::copy(geoipDBpath(true), geoipDBpath(false));
qDebug("Local Geoip database was updated");
}
}
@@ -104,7 +97,7 @@ public:
// TODO: Support more countries
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
static QIcon CountryISOCodeToIcon(const char* iso, QString &country_name) {
static QIcon CountryISOCodeToIcon(char* iso) {
switch(iso[0]) {
case 0:
case '-':
@@ -112,107 +105,106 @@ public:
//qDebug("Not returning any icon because iso is invalid: %s", iso);
return QIcon();
case 'A':
if(iso[1] == 'U') { country_name = tr("Australia"); return QIcon(":/Icons/flags/australia.png"); }
if(iso[1] == 'R') { country_name = tr("Argentina"); return QIcon(":/Icons/flags/argentina.png"); }
if(iso[1] == 'T') { country_name = tr("Austria"); return QIcon(":/Icons/flags/austria.png"); }
if(iso[1] == 'E') { country_name = tr("United Arab Emirates"); return QIcon(":/Icons/flags/united_arab_emirates.png"); }
if(iso[1] == 'U') return QIcon(":/Icons/flags/australia.png");
if(iso[1] == 'R') return QIcon(":/Icons/flags/argentina.png");
if(iso[1] == 'T') return QIcon(":/Icons/flags/austria.png");
if(iso[1] == 'E') return QIcon(":/Icons/flags/united_arab_emirates.png");
break;
case 'B':
if(iso[1] == 'R') { country_name = tr("Brazil"); return QIcon(":/Icons/flags/brazil.png"); }
if(iso[1] == 'G') { country_name = tr("Bulgaria"); return QIcon(":/Icons/flags/bulgaria.png"); }
if(iso[1] == 'Y') { country_name = tr("Belarus"); return QIcon(":/Icons/flags/belarus.png"); }
if(iso[1] == 'E') { country_name = tr("Belgium"); return QIcon(":/Icons/flags/belgium.png"); }
if(iso[1] == 'A') { country_name = tr("Bosnia"); return QIcon(":/Icons/flags/bosnia.png"); }
if(iso[1] == 'R') return QIcon(":/Icons/flags/brazil.png");
if(iso[1] == 'G') return QIcon(":/Icons/flags/bulgaria.png");
if(iso[1] == 'Y') return QIcon(":/Icons/flags/belarus.png");
if(iso[1] == 'E') return QIcon(":/Icons/flags/belgium.png");
if(iso[1] == 'A') return QIcon(":/Icons/flags/bosnia.png");
break;
case 'C':
if(iso[1] == 'A') { country_name = tr("Canada"); return QIcon(":/Icons/flags/canada.png"); }
if(iso[1] == 'Z') { country_name = tr("Czech Republic"); return QIcon(":/Icons/flags/czech.png"); }
if(iso[1] == 'N') { country_name = tr("China"); return QIcon(":/Icons/flags/china.png"); }
if(iso[1] == 'R') { country_name = tr("Costa Rica"); return QIcon(":/Icons/flags/costa_rica.png"); }
if(iso[1] == 'H') { country_name = tr("Switzerland"); return QIcon(":/Icons/flags/suisse.png"); }
if(iso[1] == 'A') return QIcon(":/Icons/flags/canada.png");
if(iso[1] == 'Z') return QIcon(":/Icons/flags/czech.png");
if(iso[1] == 'N') return QIcon(":/Icons/flags/china.png");
if(iso[1] == 'R') return QIcon(":/Icons/flags/costa_rica.png");
if(iso[1] == 'H') return QIcon(":/Icons/flags/suisse.png");
break;
case 'D':
if(iso[1] == 'E') { country_name = tr("Germany"); return QIcon(":/Icons/flags/germany.png"); }
if(iso[1] == 'K') { country_name = tr("Denmark"); return QIcon(":/Icons/flags/denmark.png"); }
if(iso[1] == 'Z') { country_name = tr("Algeria"); return QIcon(":/Icons/flags/algeria.png"); }
if(iso[1] == 'E') return QIcon(":/Icons/flags/germany.png");
if(iso[1] == 'K') return QIcon(":/Icons/flags/denmark.png");
if(iso[1] == 'Z') return QIcon(":/Icons/flags/algeria.png");
break;
case 'E':
if(iso[1] == 'S') { country_name = tr("Spain"); return QIcon(":/Icons/flags/spain.png"); }
if(iso[1] == 'G') { country_name = tr("Egypt"); return QIcon(":/Icons/flags/egypt.png"); }
if(iso[1] == 'S') return QIcon(":/Icons/flags/spain.png");
if(iso[1] == 'G') return QIcon(":/Icons/flags/egypt.png");
break;
case 'F':
if(iso[1] == 'I') { country_name = tr("Finland"); return QIcon(":/Icons/flags/finland.png"); }
if(iso[1] == 'R') { country_name = tr("France"); return QIcon(":/Icons/flags/france.png"); }
if(iso[1] == 'I') return QIcon(":/Icons/flags/finland.png");
if(iso[1] == 'R') return QIcon(":/Icons/flags/france.png");
break;
case 'G':
if(iso[1] == 'B') { country_name = tr("United Kingdom"); return QIcon(":/Icons/flags/united_kingdom.png"); }
if(iso[1] == 'R') { country_name = tr("Greece"); return QIcon(":/Icons/flags/greece.png"); }
if(iso[1] == 'E') { country_name = tr("Georgia"); return QIcon(":/Icons/flags/georgia.png"); }
if(iso[1] == 'B') return QIcon(":/Icons/flags/united_kingdom.png");
if(iso[1] == 'R') return QIcon(":/Icons/flags/greece.png");
if(iso[1] == 'E') return QIcon(":/Icons/flags/georgia.png");
break;
case 'H':
if(iso[1] == 'U') { country_name = tr("Hungary"); return QIcon(":/Icons/flags/hungary.png"); }
if(iso[1] == 'K') { country_name = tr("China"); return QIcon(":/Icons/flags/china.png"); }
if(iso[1] == 'R') { country_name = tr("Croatia"); return QIcon(":/Icons/flags/croatia.png"); }
if(iso[1] == 'U') return QIcon(":/Icons/flags/hungary.png");
if(iso[1] == 'K') return QIcon(":/Icons/flags/china.png");
if(iso[1] == 'R') return QIcon(":/Icons/flags/croatia.png");
break;
case 'I':
if(iso[1] == 'T') { country_name = tr("Italy"); return QIcon(":/Icons/flags/italy.png"); }
if(iso[1] == 'N') { country_name = tr("India"); return QIcon(":/Icons/flags/india.png"); }
if(iso[1] == 'L') { country_name = tr("Israel"); return QIcon(":/Icons/flags/israel.png"); }
if(iso[1] == 'E') { country_name = tr("Ireland"); return QIcon(":/Icons/flags/ireland.png"); }
if(iso[1] == 'S') { country_name = tr("Iceland"); return QIcon(":/Icons/flags/iceland.png"); }
if(iso[1] == 'D') { country_name = tr("Indonesia"); return QIcon(":/Icons/flags/indonesia.png"); }
if(iso[1] == 'T') return QIcon(":/Icons/flags/italy.png");
if(iso[1] == 'N') return QIcon(":/Icons/flags/india.png");
if(iso[1] == 'L') return QIcon(":/Icons/flags/israel.png");
if(iso[1] == 'E') return QIcon(":/Icons/flags/ireland.png");
if(iso[1] == 'S') return QIcon(":/Icons/flags/iceland.png");
if(iso[1] == 'D') return QIcon(":/Icons/flags/indonesia.png");
break;
case 'J':
if(iso[1] == 'P') { country_name = tr("Japan"); return QIcon(":/Icons/flags/japan.png"); }
if(iso[1] == 'P') return QIcon(":/Icons/flags/japan.png");
break;
case 'K':
if(iso[1] == 'R') { country_name = tr("South Korea"); return QIcon(":/Icons/flags/south_korea.png"); }
if(iso[1] == 'R') return QIcon(":/Icons/flags/south_korea.png");
break;
case 'L':
if(iso[1] == 'U') { country_name = tr("Luxembourg"); return QIcon(":/Icons/flags/luxembourg.png"); }
if(iso[1] == 'U') return QIcon(":/Icons/flags/luxembourg.png");
break;
case 'M':
if(iso[1] == 'Y') { country_name = tr("Malaysia"); return QIcon(":/Icons/flags/malaysia.png"); }
if(iso[1] == 'X') { country_name = tr("Mexico"); return QIcon(":/Icons/flags/mexico.png"); }
if(iso[1] == 'E') { country_name = tr("Serbia"); return QIcon(":/Icons/flags/serbia.png"); }
if(iso[1] == 'A') { country_name = tr("Morocco"); return QIcon(":/Icons/flags/morocco.png"); }
if(iso[1] == 'Y') return QIcon(":/Icons/flags/malaysia.png");
if(iso[1] == 'X') return QIcon(":/Icons/flags/mexico.png");
if(iso[1] == 'E') return QIcon(":/Icons/flags/serbia.png");
if(iso[1] == 'A') return QIcon(":/Icons/flags/morocco.png");
break;
case 'N':
if(iso[1] == 'L') { country_name = tr("Netherlands"); return QIcon(":/Icons/flags/netherlands.png"); }
if(iso[1] == 'O') { country_name = tr("Norway"); return QIcon(":/Icons/flags/norway.png"); }
if(iso[1] == 'Z') { country_name = tr("New Zealand"); return QIcon(":/Icons/flags/newzealand.png"); }
if(iso[1] == 'L') return QIcon(":/Icons/flags/netherlands.png");
if(iso[1] == 'O') return QIcon(":/Icons/flags/norway.png");
if(iso[1] == 'Z') return QIcon(":/Icons/flags/newzealand.png");
break;
case 'P':
if(iso[1] == 'T') { country_name = tr("Portugal"); return QIcon(":/Icons/flags/portugal.png"); }
if(iso[1] == 'L') { country_name = tr("Poland"); return QIcon(":/Icons/flags/poland.png"); }
if(iso[1] == 'K') { country_name = tr("Pakistan"); return QIcon(":/Icons/flags/pakistan.png"); }
if(iso[1] == 'H') { country_name = tr("Philippines"); return QIcon(":/Icons/flags/philippines.png"); }
if(iso[1] == 'T') return QIcon(":/Icons/flags/portugal.png");
if(iso[1] == 'L') return QIcon(":/Icons/flags/poland.png");
if(iso[1] == 'K') return QIcon(":/Icons/flags/pakistan.png");
if(iso[1] == 'H') return QIcon(":/Icons/flags/philippines.png");
break;
case 'R':
if(iso[1] == 'U') { country_name = tr("Russia"); return QIcon(":/Icons/flags/russia.png"); }
if(iso[1] == 'O') { country_name = tr("Romania"); return QIcon(":/Icons/flags/romania.png"); }
if(iso[1] == 'E') { country_name = tr("France (Reunion Island)"); return QIcon(":/Icons/flags/france.png"); }
if(iso[1] == 'S') { country_name = tr("Serbia"); return QIcon(":/Icons/flags/serbia.png"); }
if(iso[1] == 'U') return QIcon(":/Icons/flags/russia.png");
if(iso[1] == 'O') return QIcon(":/Icons/flags/romania.png");
if(iso[1] == 'E') return QIcon(":/Icons/flags/france.png");
if(iso[1] == 'S') return QIcon(":/Icons/flags/serbia.png");
break;
case 'S':
if(iso[1] == 'A') { country_name = tr("Saudi Arabia"); return QIcon(":/Icons/flags/saoudi_arabia.png"); }
if(iso[1] == 'E') { country_name = tr("Sweden"); return QIcon(":/Icons/flags/sweden.png"); }
if(iso[1] == 'K') { country_name = tr("Slovakia"); return QIcon(":/Icons/flags/slovakia.png"); }
if(iso[1] == 'G') { country_name = tr("Singapore"); return QIcon(":/Icons/flags/singapore.png"); }
if(iso[1] == 'I') { country_name = tr("Slovenia"); return QIcon(":/Icons/flags/slovenia.png"); }
if(iso[1] == 'E') return QIcon(":/Icons/flags/sweden.png");
if(iso[1] == 'K') return QIcon(":/Icons/flags/slovakia.png");
if(iso[1] == 'G') return QIcon(":/Icons/flags/singapore.png");
if(iso[1] == 'I') return QIcon(":/Icons/flags/slovenia.png");
break;
case 'T':
if(iso[1] == 'W') { country_name = tr("Taiwan"); return QIcon(":/Icons/flags/china.png"); }
if(iso[1] == 'R') { country_name = tr("Turkey"); return QIcon(":/Icons/flags/turkey.png"); }
if(iso[1] == 'H') { country_name = tr("Thailand"); return QIcon(":/Icons/flags/thailand.png"); }
if(iso[1] == 'W') return QIcon(":/Icons/flags/china.png");
if(iso[1] == 'R') return QIcon(":/Icons/flags/turkey.png");
if(iso[1] == 'H') return QIcon(":/Icons/flags/thailand.png");
break;
case 'U':
if(iso[1] == 'S') { country_name = tr("USA"); return QIcon(":/Icons/flags/usa.png"); }
if(iso[1] == 'M') { country_name = tr("USA"); return QIcon(":/Icons/flags/usa.png"); }
if(iso[1] == 'A') { country_name = tr("Ukraine"); return QIcon(":/Icons/flags/ukraine.png"); }
if(iso[1] == 'S') return QIcon(":/Icons/flags/usa.png");
if(iso[1] == 'M') return QIcon(":/Icons/flags/usa.png");
if(iso[1] == 'A') return QIcon(":/Icons/flags/ukraine.png");
break;
case 'Z':
if(iso[1] == 'A') { country_name = tr("South Africa"); return QIcon(":/Icons/flags/south_africa.png"); }
if(iso[1] == 'A') return QIcon(":/Icons/flags/south_africa.png");
break;
}
qDebug("Unrecognized country code: %c%c", iso[0], iso[1]);

View File

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

View File

@@ -1,508 +0,0 @@
<p>qBittorrent is licensed under the GNU General Public License version 2 with the
addition of the following special exception:</p>
<p>
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.</p>
----------
<h2>GNU General Public License, version 2</h2>
<hr>
<h3>Table of Contents</h3>
<ul>
<li><a name="TOC1" href="#SEC1">GNU GENERAL PUBLIC
LICENSE<!--TRANSLATORS: Don't translate the license; copy msgid's
verbatim!--></a>
<ul>
<li><a name="TOC2" href="#SEC2">Preamble</a></li>
<li><a name="TOC3" href="#SEC3">TERMS AND CONDITIONS
FOR COPYING, DISTRIBUTION AND MODIFICATION</a></li>
<li><a name="TOC4" href="#SEC4">How to Apply These
Terms to Your New Programs</a></li>
</ul></li>
</ul>
<hr>
<h3><a name="SEC1" href="#TOC1">GNU GENERAL PUBLIC LICENSE</a></h3>
<p>
Version 2, June 1991
</p>
<pre>
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
</pre>
<h3><a name="preamble"></a><a name="SEC2" href="#TOC2">Preamble</a></h3>
<p>
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
</p>
<p>
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
</p>
<p>
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
</p>
<p>
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
</p>
<p>
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
</p>
<p>
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
</p>
<p>
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
</p>
<p>
The precise terms and conditions for copying, distribution and
modification follow.
</p>
<h3><a name="terms"></a><a name="SEC3" href="#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
<a name="section0"></a><p>
<strong>0.</strong>
This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
</p>
<p>
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
</p>
<a name="section1"></a><p>
<strong>1.</strong>
You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
</p>
<p>
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
</p>
<a name="section2"></a><p>
<strong>2.</strong>
You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
</p>
<dl>
<dt></dt>
<dd>
<strong>a)</strong>
You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
</dd>
<dt></dt>
<dd>
<strong>b)</strong>
You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
</dd>
<dt></dt>
<dd>
<strong>c)</strong>
If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
</dd>
</dl>
<p>
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
</p>
<p>
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
</p>
<p>
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
</p>
<a name="section3"></a><p>
<strong>3.</strong>
You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
</p>
<!-- we use this doubled UL to get the sub-sections indented, -->
<!-- while making the bullets as unobvious as possible. -->
<dl>
<dt></dt>
<dd>
<strong>a)</strong>
Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
</dd>
<dt></dt>
<dd>
<strong>b)</strong>
Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
</dd>
<dt></dt>
<dd>
<strong>c)</strong>
Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
</dd>
</dl>
<p>
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
</p>
<p>
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
</p>
<a name="section4"></a><p>
<strong>4.</strong>
You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
</p>
<a name="section5"></a><p>
<strong>5.</strong>
You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
</p>
<a name="section6"></a><p>
<strong>6.</strong>
Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
</p>
<a name="section7"></a><p>
<strong>7.</strong>
If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
</p>
<p>
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
</p>
<p>
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
</p>
<p>
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
</p>
<a name="section8"></a><p>
<strong>8.</strong>
If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
</p>
<a name="section9"></a><p>
<strong>9.</strong>
The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
</p>
<p>
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
</p>
<a name="section10"></a><p>
<strong>10.</strong>
If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
</p>
<a name="section11"></a><p><strong>NO WARRANTY</strong></p>
<p>
<strong>11.</strong>
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
</p>
<a name="section12"></a><p>
<strong>12.</strong>
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
</p>
<h3>END OF TERMS AND CONDITIONS</h3>
<h3><a name="howto"></a><a name="SEC4" href="#TOC4">How to Apply These Terms to Your New Programs</a></h3>
<p>
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
</p>
<p>
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
</p>
<pre>
<var>one line to give the program's name and an idea of what it does.</var>
Copyright (C) <var>yyyy</var> <var>name of author</var>
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.
</pre>
<p>
Also add information on how to contact you by electronic and paper mail.
</p>
<p>
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
</p>
<pre>
Gnomovision version 69, Copyright (C) <var>year</var> <var>name of author</var>
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
type `show w'. This is free software, and you are welcome
to redistribute it under certain conditions; type `show c'
for details.
</pre>
<p>
The hypothetical commands <samp>`show w'</samp> and <samp>`show c'</samp> should show
the appropriate parts of the General Public License. Of course, the
commands you use may be called something other than <samp>`show w'</samp> and
<samp>`show c'</samp>; they could even be mouse-clicks or menu items--whatever
suits your program.
</p>
<p>
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
</p>
<pre>
Yoyodyne, Inc., hereby disclaims all copyright
interest in the program `Gnomovision'
(which makes passes at compilers) written
by James Hacker.
<var>signature of Ty Coon</var>, 1 April 1989
Ty Coon, President of Vice
</pre>
<p>
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the
<a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License</a>
instead of this License.
</p>

View File

@@ -33,10 +33,12 @@
#include <QObject>
#include <QCoreApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include "preferences.h"
#include "qbtsession.h"
#include "bittorrent.h"
class HeadlessLoader: public QObject {
class HeadlessLoader: QObject {
Q_OBJECT
public:
@@ -44,12 +46,26 @@ public:
// Enable Web UI
Preferences::setWebUiEnabled(true);
// Instanciate Bittorrent Object
BTSession = QBtSession::instance();
BTSession = new Bittorrent();
connect(BTSession, SIGNAL(newConsoleMessage(QString)), this, SLOT(displayConsoleMessage(QString)));
// Resume unfinished torrents
BTSession->startUpTorrents();
// Process command line parameters
processParams(torrentCmdLine);
// Use a tcp server to allow only one instance of qBittorrent
localServer = new QLocalServer();
const QString &uid = QString::number(getuid());
#ifdef Q_WS_X11
if(QFile::exists(QDir::tempPath()+QDir::separator()+QString("qBittorrent-")+uid)) {
// Socket was not closed cleanly
std::cerr << "Warning: Local domain socket was not closed cleanly, deleting file..." << std::endl;
QFile::remove(QDir::tempPath()+QDir::separator()+QString("qBittorrent-")+uid);
}
#endif
if (!localServer->listen("qBittorrent-"+uid)) {
std::cerr << "Couldn't create socket, single instance mode won't work..." << std::endl;
}
connect(localServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
// Display some information to the user
std::cout << std::endl << "******** " << qPrintable(tr("Information")) << " ********" << std::endl;
std::cout << qPrintable(tr("To control qBittorrent, access the Web UI at http://localhost:%1").arg(QString::number(Preferences::getWebUiPort()))) << std::endl;
@@ -61,7 +77,8 @@ public:
}
~HeadlessLoader() {
QBtSession::drop();
delete localServer;
delete BTSession;
}
public slots:
@@ -75,10 +92,6 @@ public slots:
std::cout << qPrintable(msg) << std::endl;
}
void processParams(const QString& params_str) {
processParams(params_str.split(" ", QString::SkipEmptyParts));
}
// As program parameters, we can get paths or urls.
// This function parse the parameters and call
// the right addTorrent function, considering
@@ -86,13 +99,10 @@ public slots:
void processParams(const QStringList& params) {
foreach(QString param, params) {
param = param.trimmed();
if(param.startsWith("--")) continue;
if(param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
BTSession->downloadFromUrl(param);
}else{
if(param.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Converting bc link to magnet link");
param = misc::bcLinkToMagnet(param);
}
if(param.startsWith("magnet:", Qt::CaseInsensitive)) {
BTSession->addMagnetUri(param);
} else {
@@ -102,8 +112,27 @@ public slots:
}
}
void acceptConnection() {
QLocalSocket *clientConnection = localServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()), this, SLOT(readParamsOnSocket()));
qDebug("accepted connection from another instance");
}
void readParamsOnSocket() {
QLocalSocket *clientConnection = static_cast<QLocalSocket*>(sender());
if(clientConnection) {
const QByteArray &params = clientConnection->readAll();
if(!params.isEmpty()) {
processParams(QString(params).split("\n"));
qDebug("Received parameters from another instance");
}
clientConnection->deleteLater();
}
}
private:
QBtSession *BTSession;
QLocalServer *localServer;
Bittorrent *BTSession;
};

View File

@@ -1,44 +0,0 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 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
*/
#ifndef HIDABLETABWIDGET_H
#define HIDABLETABWIDGET_H
#include <QTabWidget>
#include <QTabBar>
class HidableTabWidget : public QTabWidget {
public:
void showTabBar(bool show) {
tabBar()->setVisible(show);
}
};
#endif // HIDABLETABWIDGET_H

View File

@@ -34,7 +34,7 @@
#include "eventmanager.h"
#include "preferences.h"
#include "json.h"
#include "qbtsession.h"
#include "bittorrent.h"
#include "misc.h"
#include <QTcpSocket>
#include <QDateTime>
@@ -46,8 +46,8 @@
#include <QRegExp>
#include <QTemporaryFile>
HttpConnection::HttpConnection(QTcpSocket *socket, HttpServer *parent)
: QObject(parent), socket(socket), httpserver(parent)
HttpConnection::HttpConnection(QTcpSocket *socket, Bittorrent *BTSession, HttpServer *parent)
: QObject(parent), socket(socket), parent(parent), BTSession(BTSession)
{
socket->setParent(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(read()));
@@ -109,7 +109,7 @@ QString HttpConnection::translateDocument(QString data) {
bool found = false;
do {
found = false;
QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),%µ&\\-\\.]+)\\)"));
QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),µ\\-\\.]+)\\)"));
i = regex.indexIn(data, i);
if(i >= 0) {
//qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
@@ -120,8 +120,6 @@ QString HttpConnection::translateDocument(QString data) {
translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().constData(), 0, QCoreApplication::UnicodeUTF8, 1);
++context_index;
}while(translation == word && context_index < 13);
// Remove keyboard shortcuts
translation = translation.replace("&", "");
//qDebug("Translation is %s", translation.toUtf8().data());
data = data.replace(i, regex.matchedLength(), translation);
i += translation.length();
@@ -133,8 +131,8 @@ QString HttpConnection::translateDocument(QString data) {
void HttpConnection::respond() {
//qDebug("Respond called");
const QString peer_ip = socket->peerAddress().toString();
const int nb_fail = httpserver->NbFailedAttemptsForIp(peer_ip);
const QString &peer_ip = socket->peerAddress().toString();
const int nb_fail = parent->NbFailedAttemptsForIp(peer_ip);
if(nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) {
generator.setStatusLine(403, "Forbidden");
generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts."));
@@ -146,23 +144,23 @@ void HttpConnection::respond() {
// Return unauthorized header
qDebug("Auth is Empty...");
generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+httpserver->generateNonce()+"\", opaque=\""+httpserver->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
write();
return;
}
//qDebug("Auth: %s", qPrintable(auth.split(" ").first()));
if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !httpserver->isAuthorized(auth.toLocal8Bit(), parser.method())) {
qDebug("Auth: %s", qPrintable(auth.split(" ").first()));
if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth.toLocal8Bit(), parser.method())) {
// Update failed attempt counter
httpserver->increaseNbFailedAttemptsForIp(peer_ip);
parent->increaseNbFailedAttemptsForIp(peer_ip);
qDebug("client IP: %s (%d failed attempts)", qPrintable(peer_ip), nb_fail+1);
// Return unauthorized header
generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+httpserver->generateNonce()+"\", opaque=\""+httpserver->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", algorithm=\"MD5\", qop=\"auth\"");
write();
return;
}
// Client successfully authenticated, reset number of failed attempts
httpserver->resetNbFailedAttemptsForIp(peer_ip);
// Client sucessfuly authenticated, reset number of failed attempts
parent->resetNbFailedAttemptsForIp(peer_ip);
QString url = parser.url();
// Favicon
if(url.endsWith("favicon.ico")) {
@@ -231,18 +229,14 @@ void HttpConnection::respond() {
return;
}
}
if (list[0] == "images") {
if (list[0] == "images")
list[0] = "Icons";
} else {
if(list.last().endsWith(".html"))
list.prepend("html");
else
list.prepend("webui");
}
url = ":/" + list.join("/");
QFile file(url);
if(!file.open(QIODevice::ReadOnly))
{
qDebug("File %s was not found!", qPrintable(url));
respondNotFound();
return;
}
@@ -271,7 +265,7 @@ void HttpConnection::respondNotFound()
void HttpConnection::respondJson()
{
EventManager* manager = httpserver->eventManager();
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getEventList());
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
@@ -280,7 +274,7 @@ void HttpConnection::respondJson()
}
void HttpConnection::respondGenPropertiesJson(QString hash) {
EventManager* manager = httpserver->eventManager();
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropGeneralInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
@@ -289,7 +283,7 @@ void HttpConnection::respondGenPropertiesJson(QString hash) {
}
void HttpConnection::respondTrackersPropertiesJson(QString hash) {
EventManager* manager = httpserver->eventManager();
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropTrackersInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
@@ -298,7 +292,7 @@ void HttpConnection::respondTrackersPropertiesJson(QString hash) {
}
void HttpConnection::respondFilesPropertiesJson(QString hash) {
EventManager* manager = httpserver->eventManager();
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropFilesInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
@@ -307,7 +301,7 @@ void HttpConnection::respondFilesPropertiesJson(QString hash) {
}
void HttpConnection::respondPreferencesJson() {
EventManager* manager = httpserver->eventManager();
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getGlobalPreferences());
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
@@ -317,7 +311,7 @@ void HttpConnection::respondPreferencesJson() {
void HttpConnection::respondGlobalTransferInfoJson() {
QVariantMap info;
session_status sessionStatus = QBtSession::instance()->getSessionStatus();
session_status sessionStatus = BTSession->getSessionStatus();
info["DlInfos"] = tr("D: %1/s - T: %2", "Download speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_download_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_download));
info["UpInfos"] = tr("U: %1/s - T: %2", "Upload speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_upload_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_upload));
QString string = json::toJson(info);
@@ -336,10 +330,6 @@ void HttpConnection::respondCommand(QString command)
foreach(QString url, list){
url = url.trimmed();
if(!url.isEmpty()){
if(url.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Converting bc link to magnet link");
url = misc::bcLinkToMagnet(url);
}
if(url.startsWith("magnet:", Qt::CaseInsensitive)) {
emit MagnetReadyToBeDownloaded(url);
} else {
@@ -353,7 +343,7 @@ void HttpConnection::respondCommand(QString command)
if(command == "addTrackers") {
QString hash = parser.post("hash");
if(!hash.isEmpty()) {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
QString urls = parser.post("urls");
QStringList list = urls.split('\n');
@@ -407,14 +397,14 @@ void HttpConnection::respondCommand(QString command)
}
if(command == "setPreferences") {
QString json_str = parser.post("json");
EventManager* manager = httpserver->eventManager();
EventManager* manager = parent->eventManager();
manager->setGlobalPreferences(json::fromJson(json_str));
}
if(command == "setFilePrio") {
QString hash = parser.post("hash");
int file_id = parser.post("id").toInt();
int priority = parser.post("priority").toInt();
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
h.file_priority(file_id, priority);
}
@@ -422,18 +412,18 @@ void HttpConnection::respondCommand(QString command)
if(command == "getGlobalUpLimit") {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString::number(QBtSession::instance()->getSession()->upload_rate_limit()));
generator.setMessage(QString::number(BTSession->getSession()->upload_rate_limit()));
write();
}
if(command == "getGlobalDlLimit") {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
generator.setMessage(QString::number(QBtSession::instance()->getSession()->download_rate_limit()));
generator.setMessage(QString::number(BTSession->getSession()->download_rate_limit()));
write();
}
if(command == "getTorrentUpLimit") {
QString hash = parser.post("hash");
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
@@ -443,7 +433,7 @@ void HttpConnection::respondCommand(QString command)
}
if(command == "getTorrentDlLimit") {
QString hash = parser.post("hash");
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("html");
@@ -455,7 +445,7 @@ void HttpConnection::respondCommand(QString command)
QString hash = parser.post("hash");
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
h.set_upload_limit(limit);
}
@@ -464,7 +454,7 @@ void HttpConnection::respondCommand(QString command)
QString hash = parser.post("hash");
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
h.set_download_limit(limit);
}
@@ -472,13 +462,13 @@ void HttpConnection::respondCommand(QString command)
if(command == "setGlobalUpLimit") {
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
QBtSession::instance()->getSession()->set_upload_rate_limit(limit);
BTSession->getSession()->set_upload_rate_limit(limit);
Preferences::setGlobalUploadLimit(limit/1024.);
}
if(command == "setGlobalDlLimit") {
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
QBtSession::instance()->getSession()->set_download_rate_limit(limit);
BTSession->getSession()->set_download_rate_limit(limit);
Preferences::setGlobalDownloadLimit(limit/1024.);
}
if(command == "pause") {
@@ -494,25 +484,15 @@ void HttpConnection::respondCommand(QString command)
return;
}
if(command == "increasePrio") {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(parser.post("hash"));
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_up();
return;
}
if(command == "decreasePrio") {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(parser.post("hash"));
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_down();
return;
}
if(command == "topPrio") {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_top();
return;
}
if(command == "bottomPrio") {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_bottom();
return;
}
if(command == "recheck"){
recheckTorrent(parser.post("hash"));
return;
@@ -524,18 +504,18 @@ void HttpConnection::respondCommand(QString command)
}
void HttpConnection::recheckTorrent(QString hash) {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()){
QBtSession::instance()->recheckTorrent(h.hash());
BTSession->recheckTorrent(h.hash());
}
}
void HttpConnection::recheckAllTorrents() {
std::vector<torrent_handle> torrents = QBtSession::instance()->getTorrents();
std::vector<torrent_handle> torrents = BTSession->getTorrents();
std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
if(h.is_valid())
QBtSession::instance()->recheckTorrent(h.hash());
BTSession->recheckTorrent(h.hash());
}
}

View File

@@ -38,55 +38,55 @@
class QTcpSocket;
class HttpServer;
class Bittorrent;
class HttpConnection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(HttpConnection)
Q_OBJECT
private:
QTcpSocket *socket;
HttpServer *parent;
Bittorrent *BTSession;
private:
QTcpSocket *socket;
HttpServer *httpserver;
protected:
HttpRequestParser parser;
HttpResponseGenerator generator;
protected:
HttpRequestParser parser;
HttpResponseGenerator generator;
protected slots:
void write();
virtual void respond();
void respondJson();
void respondGenPropertiesJson(QString hash);
void respondTrackersPropertiesJson(QString hash);
void respondFilesPropertiesJson(QString hash);
void respondPreferencesJson();
void respondGlobalTransferInfoJson();
void respondCommand(QString command);
void respondNotFound();
void processDownloadedFile(QString, QString);
void handleDownloadFailure(QString, QString);
void recheckTorrent(QString hash);
void recheckAllTorrents();
protected slots:
void write();
void respond();
void respondJson();
void respondGenPropertiesJson(QString hash);
void respondTrackersPropertiesJson(QString hash);
void respondFilesPropertiesJson(QString hash);
void respondPreferencesJson();
void respondGlobalTransferInfoJson();
void respondCommand(QString command);
void respondNotFound();
void processDownloadedFile(QString, QString);
void handleDownloadFailure(QString, QString);
void recheckTorrent(QString hash);
void recheckAllTorrents();
public:
HttpConnection(QTcpSocket *socket, Bittorrent* BTSession, HttpServer *parent);
~HttpConnection();
QString translateDocument(QString data);
public:
HttpConnection(QTcpSocket *socket, HttpServer *httpserver);
~HttpConnection();
QString translateDocument(QString data);
private slots:
void read();
private slots:
void read();
signals:
void UrlReadyToBeDownloaded(QString url);
void MagnetReadyToBeDownloaded(QString uri);
void torrentReadyToBeDownloaded(QString, bool, QString, bool);
void deleteTorrent(QString hash, bool permanently);
void resumeTorrent(QString hash);
void pauseTorrent(QString hash);
void increasePrioTorrent(QString hash);
void decreasePrioTorrent(QString hash);
void resumeAllTorrents();
void pauseAllTorrents();
signals:
void UrlReadyToBeDownloaded(QString url);
void MagnetReadyToBeDownloaded(QString uri);
void torrentReadyToBeDownloaded(QString, bool, QString, bool);
void deleteTorrent(QString hash, bool permanently);
void resumeTorrent(QString hash);
void pauseTorrent(QString hash);
void increasePrioTorrent(QString hash);
void decreasePrioTorrent(QString hash);
void resumeAllTorrents();
void pauseAllTorrents();
};
#endif

View File

@@ -68,12 +68,12 @@ QByteArray HttpRequestParser::message() const
QString HttpRequestParser::get(const QString key) const
{
return getMap.value(key);
return getMap[key];
}
QString HttpRequestParser::post(const QString key) const
{
return postMap.value(key);
return postMap[key];
}
QByteArray HttpRequestParser::torrent() const

View File

@@ -39,8 +39,7 @@ void HttpResponseGenerator::setMessage(const QByteArray message)
void HttpResponseGenerator::setMessage(const QString message)
{
// This must be UTF-8!
setMessage(message.toUtf8());
setMessage(message.QString::toLocal8Bit());
}
void HttpResponseGenerator::stripMessage()
@@ -48,6 +47,11 @@ void HttpResponseGenerator::stripMessage()
message.clear();
}
QByteArray HttpResponseGenerator::toByteArray() const
{
return QHttpResponseHeader::toString().toLocal8Bit() + message;
}
void HttpResponseGenerator::setContentTypeByExt(const QString ext)
{
if(ext == "css")

View File

@@ -36,17 +36,15 @@
class HttpResponseGenerator : public QHttpResponseHeader
{
private:
QByteArray message;
public:
void setMessage(const QByteArray message);
void setMessage(const QString message);
void stripMessage();
void setContentTypeByExt(const QString ext);
inline QByteArray toByteArray() const { return QHttpResponseHeader::toString().toLocal8Bit() + message; }
private:
QByteArray message;
virtual QByteArray toByteArray() const;
};
#endif

View File

@@ -32,7 +32,7 @@
#include "httpserver.h"
#include "httpconnection.h"
#include "eventmanager.h"
#include "qbtsession.h"
#include "bittorrent.h"
#include <QTimer>
#include <QCryptographicHash>
#include <QTime>
@@ -80,22 +80,23 @@ void HttpServer::resetNbFailedAttemptsForIp(QString ip) {
client_failed_attempts.remove(ip);
}
HttpServer::HttpServer(int msec, QObject* parent) : QTcpServer(parent) {
HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) {
username = Preferences::getWebUiUsername().toLocal8Bit();
password_ha1 = Preferences::getWebUiPassword().toLocal8Bit();
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
manager = new EventManager(this);
BTSession = _BTSession;
manager = new EventManager(this, BTSession);
//add torrents
std::vector<torrent_handle> torrents = QBtSession::instance()->getTorrents();
std::vector<torrent_handle> torrents = BTSession->getTorrents();
std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
if(h.is_valid())
manager->addedTorrent(h);
}
//connect QBtSession::instance() to manager
connect(QBtSession::instance(), SIGNAL(addedTorrent(QTorrentHandle&)), manager, SLOT(addedTorrent(QTorrentHandle&)));
connect(QBtSession::instance(), SIGNAL(deletedTorrent(QString)), manager, SLOT(deletedTorrent(QString)));
//connect BTSession to manager
connect(BTSession, SIGNAL(addedTorrent(QTorrentHandle&)), manager, SLOT(addedTorrent(QTorrentHandle&)));
connect(BTSession, SIGNAL(deletedTorrent(QString)), manager, SLOT(deletedTorrent(QString)));
//set timer
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
@@ -137,21 +138,21 @@ void HttpServer::newHttpConnection()
QTcpSocket *socket;
while((socket = nextPendingConnection()))
{
HttpConnection *connection = new HttpConnection(socket, this);
//connect connection to QBtSession::instance()
connect(connection, SIGNAL(UrlReadyToBeDownloaded(QString)), QBtSession::instance(), SLOT(downloadUrlAndSkipDialog(QString)));
connect(connection, SIGNAL(MagnetReadyToBeDownloaded(QString)), QBtSession::instance(), SLOT(addMagnetSkipAddDlg(QString)));
connect(connection, SIGNAL(torrentReadyToBeDownloaded(QString, bool, QString, bool)), QBtSession::instance(), SLOT(addTorrent(QString, bool, QString, bool)));
connect(connection, SIGNAL(deleteTorrent(QString, bool)), QBtSession::instance(), SLOT(deleteTorrent(QString, bool)));
connect(connection, SIGNAL(pauseTorrent(QString)), QBtSession::instance(), SLOT(pauseTorrent(QString)));
connect(connection, SIGNAL(resumeTorrent(QString)), QBtSession::instance(), SLOT(resumeTorrent(QString)));
connect(connection, SIGNAL(pauseAllTorrents()), QBtSession::instance(), SLOT(pauseAllTorrents()));
connect(connection, SIGNAL(resumeAllTorrents()), QBtSession::instance(), SLOT(resumeAllTorrents()));
HttpConnection *connection = new HttpConnection(socket, BTSession, this);
//connect connection to BTSession
connect(connection, SIGNAL(UrlReadyToBeDownloaded(QString)), BTSession, SLOT(downloadUrlAndSkipDialog(QString)));
connect(connection, SIGNAL(MagnetReadyToBeDownloaded(QString)), BTSession, SLOT(addMagnetSkipAddDlg(QString)));
connect(connection, SIGNAL(torrentReadyToBeDownloaded(QString, bool, QString, bool)), BTSession, SLOT(addTorrent(QString, bool, QString, bool)));
connect(connection, SIGNAL(deleteTorrent(QString, bool)), BTSession, SLOT(deleteTorrent(QString, bool)));
connect(connection, SIGNAL(pauseTorrent(QString)), BTSession, SLOT(pauseTorrent(QString)));
connect(connection, SIGNAL(resumeTorrent(QString)), BTSession, SLOT(resumeTorrent(QString)));
connect(connection, SIGNAL(pauseAllTorrents()), BTSession, SLOT(pauseAllTorrents()));
connect(connection, SIGNAL(resumeAllTorrents()), BTSession, SLOT(resumeAllTorrents()));
}
}
void HttpServer::onTimer() {
std::vector<torrent_handle> torrents = QBtSession::instance()->getTorrents();
std::vector<torrent_handle> torrents = BTSession->getTorrents();
std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
@@ -176,12 +177,12 @@ void HttpServer::setAuthorization(QString _username, QString _password_ha1) {
// Parse HTTP AUTH string
// http://tools.ietf.org/html/rfc2617
bool HttpServer::isAuthorized(QByteArray auth, QString method) const {
//qDebug("AUTH string is %s", auth.data());
qDebug("AUTH string is %s", auth.data());
// Get user name
QRegExp regex_user(".*username=\"([^\"]+)\".*"); // Must be a quoted string
if(regex_user.indexIn(auth) < 0) return false;
QString prop_user = regex_user.cap(1);
//qDebug("AUTH: Proposed username is %s, real username is %s", prop_user.toLocal8Bit().data(), username.data());
qDebug("AUTH: Proposed username is %s, real username is %s", prop_user.toLocal8Bit().data(), username.data());
if(prop_user != username) {
// User name is invalid, we can reject already
qDebug("AUTH-PROB: Username is invalid");

View File

@@ -38,6 +38,7 @@
#include <QHash>
#include "preferences.h"
class Bittorrent;
class QTimer;
class EventManager;
@@ -45,10 +46,9 @@ const int MAX_AUTH_FAILED_ATTEMPTS = 5;
class HttpServer : public QTcpServer {
Q_OBJECT
Q_DISABLE_COPY(HttpServer)
public:
HttpServer(int msec, QObject* parent = 0);
HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0);
~HttpServer();
void setAuthorization(QString username, QString password_ha1);
bool isAuthorized(QByteArray auth, QString method) const;
@@ -66,6 +66,7 @@ private slots:
private:
QByteArray username;
QByteArray password_ha1;
Bittorrent *BTSession;
EventManager *manager;
QTimer *timer;
QHash<QString, int> client_failed_attempts;

View File

@@ -1,184 +1,182 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>Icons/url.png</file>
<file>Icons/loading.png</file>
<file>Icons/rss16.png</file>
<file>Icons/slow48.png</file>
<file>Icons/rss32.png</file>
<file>Icons/3-state-checkbox.gif</file>
<file>Icons/uparrow.png</file>
<file>Icons/sphere.png</file>
<file>Icons/downarrow.png</file>
<file>Icons/slow_off.png</file>
<file>Icons/sphere2.png</file>
<file>Icons/magnet.png</file>
<file>Icons/downarrow.png</file>
<file>Icons/url.png</file>
<file>Icons/locale.png</file>
<file>Icons/loading.png</file>
<file>Icons/slow.png</file>
<file>Icons/L.gif</file>
<file>Icons/skin/delete_perm22.png</file>
<file>Icons/skin/seeding.png</file>
<file>Icons/skin/splash.png</file>
<file>Icons/skin/preview.png</file>
<file>Icons/skin/tabs.gif</file>
<file>Icons/skin/delete.png</file>
<file>Icons/skin/increase.png</file>
<file>Icons/skin/qbittorrent16.png</file>
<file>Icons/skin/connected.png</file>
<file>Icons/skin/url.png</file>
<file>Icons/skin/qbittorrent22.png</file>
<file>Icons/skin/disconnected.png</file>
<file>Icons/skin/mascot.png</file>
<file>Icons/skin/info.png</file>
<file>Icons/skin/paused.png</file>
<file>Icons/skin/slider-area.gif</file>
<file>Icons/skin/pause22.png</file>
<file>Icons/skin/stalledDL.png</file>
<file>Icons/skin/play.png</file>
<file>Icons/skin/delete_all.png</file>
<file>Icons/skin/delete_perm.png</file>
<file>Icons/skin/qb_question.png</file>
<file>Icons/skin/error.png</file>
<file>Icons/skin/settings.png</file>
<file>Icons/skin/handle-icon-horizontal.gif</file>
<file>Icons/skin/pause.png</file>
<file>Icons/skin/qbittorrent32.png</file>
<file>Icons/skin/exit.png</file>
<file>Icons/skin/knob.gif</file>
<file>Icons/skin/open.png</file>
<file>Icons/skin/pause_all.png</file>
<file>Icons/skin/filterall.png</file>
<file>Icons/skin/uploading.png</file>
<file>Icons/skin/queued.png</file>
<file>Icons/skin/checking.png</file>
<file>Icons/skin/handle-icon.gif</file>
<file>Icons/skin/arrow-right.gif</file>
<file>Icons/skin/filterinactive.png</file>
<file>Icons/skin/decrease.png</file>
<file>Icons/skin/play22.png</file>
<file>Icons/skin/firewalled.png</file>
<file>Icons/skin/delete22.png</file>
<file>Icons/skin/new.png</file>
<file>Icons/skin/downloading.png</file>
<file>Icons/skin/play_all.png</file>
<file>Icons/skin/toolbox-divider.gif</file>
<file>Icons/skin/stalledUP.png</file>
<file>Icons/skin/filteractive.png</file>
<file>Icons/magnet.png</file>
<file>Icons/sphere.png</file>
<file>Icons/slow_off.png</file>
<file>Icons/uparrow.png</file>
<file>Icons/rss16.png</file>
<file>Icons/skin/checkingUP.png</file>
<file>Icons/skin/bg-handle-horizontal.gif</file>
<file>Icons/skin/play.png</file>
<file>Icons/skin/qbittorrent22.png</file>
<file>Icons/skin/queuedDL.png</file>
<file>Icons/skin/new.png</file>
<file>Icons/skin/queuedUP.png</file>
<file>Icons/skin/preview.png</file>
<file>Icons/skin/stalled.png</file>
<file>Icons/skin/delete.png</file>
<file>Icons/skin/handle-icon-horizontal.gif</file>
<file>Icons/skin/handle-icon.gif</file>
<file>Icons/skin/knob.gif</file>
<file>Icons/skin/url.png</file>
<file>Icons/skin/stalledUP.png</file>
<file>Icons/skin/delete_perm22.png</file>
<file>Icons/skin/filteractive.png</file>
<file>Icons/skin/connected.png</file>
<file>Icons/skin/pausedDL.png</file>
<file>Icons/skin/mascot.png</file>
<file>Icons/skin/pausedUP.png</file>
<file>Icons/skin/seeding.png</file>
<file>Icons/skin/increase.png</file>
<file>Icons/skin/qbittorrent32.png</file>
<file>Icons/skin/paused.png</file>
<file>Icons/skin/toolbox-divider.gif</file>
<file>Icons/skin/stalledDL.png</file>
<file>Icons/skin/qb_question.png</file>
<file>Icons/skin/download.png</file>
<file>Icons/flags/pakistan.png</file>
<file>Icons/flags/argentina.png</file>
<file>Icons/flags/netherlands.png</file>
<file>Icons/flags/australia.png</file>
<file>Icons/flags/finland.png</file>
<file>Icons/flags/croatia.png</file>
<file>Icons/flags/indonesia.png</file>
<file>Icons/flags/greece.png</file>
<file>Icons/flags/belarus.png</file>
<file>Icons/flags/morocco.png</file>
<file>Icons/flags/portugal.png</file>
<file>Icons/flags/egypt.png</file>
<file>Icons/flags/georgia.png</file>
<file>Icons/flags/costa_rica.png</file>
<file>Icons/flags/denmark.png</file>
<file>Icons/flags/bosnia.png</file>
<file>Icons/flags/newzealand.png</file>
<file>Icons/skin/open.png</file>
<file>Icons/skin/play22.png</file>
<file>Icons/skin/qbittorrent16.png</file>
<file>Icons/skin/slider-area.gif</file>
<file>Icons/skin/downloading.png</file>
<file>Icons/skin/filterinactive.png</file>
<file>Icons/skin/pause22.png</file>
<file>Icons/skin/pause_all.png</file>
<file>Icons/skin/delete22.png</file>
<file>Icons/skin/play_all.png</file>
<file>Icons/skin/pause.png</file>
<file>Icons/skin/firewalled.png</file>
<file>Icons/skin/properties.png</file>
<file>Icons/skin/info.png</file>
<file>Icons/skin/tabs.gif</file>
<file>Icons/skin/delete_perm.png</file>
<file>Icons/skin/checkingDL.png</file>
<file>Icons/skin/settings.png</file>
<file>Icons/skin/exit.png</file>
<file>Icons/skin/delete_all.png</file>
<file>Icons/skin/splash.png</file>
<file>Icons/skin/disconnected.png</file>
<file>Icons/skin/decrease.png</file>
<file>Icons/skin/uploading.png</file>
<file>Icons/skin/filterall.png</file>
<file>Icons/flags/czech.png</file>
<file>Icons/flags/serbia.png</file>
<file>Icons/flags/singapore.png</file>
<file>Icons/flags/italy.png</file>
<file>Icons/flags/brazil.png</file>
<file>Icons/flags/iceland.png</file>
<file>Icons/flags/china.png</file>
<file>Icons/flags/luxembourg.png</file>
<file>Icons/flags/newzealand.png</file>
<file>Icons/flags/austria.png</file>
<file>Icons/flags/indonesia.png</file>
<file>Icons/flags/united_arab_emirates.png</file>
<file>Icons/flags/norway.png</file>
<file>Icons/flags/india.png</file>
<file>Icons/flags/finland.png</file>
<file>Icons/flags/australia.png</file>
<file>Icons/flags/netherlands.png</file>
<file>Icons/flags/south_africa.png</file>
<file>Icons/flags/belarus.png</file>
<file>Icons/flags/georgia.png</file>
<file>Icons/flags/taiwan.png</file>
<file>Icons/flags/south_korea.png</file>
<file>Icons/flags/sweden.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/ireland.png</file>
<file>Icons/flags/singapore.png</file>
<file>Icons/flags/israel.png</file>
<file>Icons/flags/belgium.png</file>
<file>Icons/flags/usa.png</file>
<file>Icons/flags/costa_rica.png</file>
<file>Icons/flags/romania.png</file>
<file>Icons/flags/suisse.png</file>
<file>Icons/flags/croatia.png</file>
<file>Icons/flags/mexico.png</file>
<file>Icons/flags/denmark.png</file>
<file>Icons/flags/hungary.png</file>
<file>Icons/flags/slovenia.png</file>
<file>Icons/flags/ukraine.png</file>
<file>Icons/flags/turkey.png</file>
<file>Icons/flags/algeria.png</file>
<file>Icons/flags/japan.png</file>
<file>Icons/flags/luxembourg.png</file>
<file>Icons/flags/poland.png</file>
<file>Icons/flags/iceland.png</file>
<file>Icons/flags/greece.png</file>
<file>Icons/flags/morocco.png</file>
<file>Icons/flags/argentina.png</file>
<file>Icons/flags/spain.png</file>
<file>Icons/flags/saoudi_arabia.png</file>
<file>Icons/flags/norway.png</file>
<file>Icons/flags/portugal.png</file>
<file>Icons/flags/russia.png</file>
<file>Icons/flags/slovakia.png</file>
<file>Icons/flags/philippines.png</file>
<file>Icons/flags/thailand.png</file>
<file>Icons/flags/israel.png</file>
<file>Icons/flags/bulgaria.png</file>
<file>Icons/flags/czech.png</file>
<file>Icons/flags/usa.png</file>
<file>Icons/flags/malaysia.png</file>
<file>Icons/flags/belgium.png</file>
<file>Icons/flags/south_africa.png</file>
<file>Icons/flags/slovenia.png</file>
<file>Icons/flags/china.png</file>
<file>Icons/flags/germany.png</file>
<file>Icons/flags/ukraine.png</file>
<file>Icons/flags/india.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/romania.png</file>
<file>Icons/flags/united_kingdom.png</file>
<file>Icons/flags/united_arab_emirates.png</file>
<file>Icons/flags/sweden.png</file>
<file>Icons/flags/thailand.png</file>
<file>Icons/flags/poland.png</file>
<file>Icons/flags/germany.png</file>
<file>Icons/flags/bulgaria.png</file>
<file>Icons/flags/canada.png</file>
<file>Icons/flags/mexico.png</file>
<file>Icons/flags/hungary.png</file>
<file>Icons/flags/brazil.png</file>
<file>Icons/flags/france.png</file>
<file>Icons/flags/ireland.png</file>
<file>Icons/flags/austria.png</file>
<file>Icons/flags/suisse.png</file>
<file>Icons/oxygen/peer.png</file>
<file>Icons/oxygen/unavailable.png</file>
<file>Icons/oxygen/subscribe.png</file>
<file>Icons/oxygen/list-remove.png</file>
<file>Icons/oxygen/dialog-warning.png</file>
<file>Icons/oxygen/mail-folder-inbox.png</file>
<file>Icons/oxygen/folder.png</file>
<file>Icons/oxygen/edit-copy.png</file>
<file>Icons/oxygen/folder-documents.png</file>
<file>Icons/oxygen/urlseed.png</file>
<file>Icons/oxygen/go-up.png</file>
<file>Icons/oxygen/edit-cut.png</file>
<file>Icons/oxygen/gear32.png</file>
<file>Icons/oxygen/go-bottom.png</file>
<file>Icons/oxygen/user-group-delete.png</file>
<file>Icons/oxygen/unsubscribe.png</file>
<file>Icons/oxygen/tab-close.png</file>
<file>Icons/oxygen/file.png</file>
<file>Icons/oxygen/services.png</file>
<file>Icons/flags/slovakia.png</file>
<file>Icons/flags/pakistan.png</file>
<file>Icons/flags/egypt.png</file>
<file>Icons/flags/italy.png</file>
<file>Icons/flags/south_korea.png</file>
<file>Icons/flags/bosnia.png</file>
<file>Icons/flags/japan.png</file>
<file>Icons/flags/malaysia.png</file>
<file>Icons/flags/philippines.png</file>
<file>Icons/oxygen/mail-queue.png</file>
<file>Icons/oxygen/view-refresh.png</file>
<file>Icons/oxygen/feed-subscribe.png</file>
<file>Icons/oxygen/remove.png</file>
<file>Icons/oxygen/chronometer.png</file>
<file>Icons/oxygen/filter.png</file>
<file>Icons/oxygen/run-build.png</file>
<file>Icons/oxygen/button_ok.png</file>
<file>Icons/oxygen/user-group-new.png</file>
<file>Icons/oxygen/cookies.png</file>
<file>Icons/oxygen/network-server.png</file>
<file>Icons/oxygen/unsubscribe16.png</file>
<file>Icons/oxygen/encrypted32.png</file>
<file>Icons/oxygen/list-add.png</file>
<file>Icons/oxygen/edit-paste.png</file>
<file>Icons/oxygen/folder-remote.png</file>
<file>Icons/oxygen/help-about.png</file>
<file>Icons/oxygen/encrypted.png</file>
<file>Icons/oxygen/file.png</file>
<file>Icons/oxygen/folder-remote16.png</file>
<file>Icons/oxygen/go-top.png</file>
<file>Icons/oxygen/emblem-favorite.png</file>
<file>Icons/oxygen/edit_clear.png</file>
<file>Icons/oxygen/bug.png</file>
<file>Icons/oxygen/gear.png</file>
<file>Icons/oxygen/connection.png</file>
<file>Icons/oxygen/document-new.png</file>
<file>Icons/oxygen/browse.png</file>
<file>Icons/oxygen/proxy.png</file>
<file>Icons/oxygen/button_cancel.png</file>
<file>Icons/oxygen/preferences-desktop.png</file>
<file>Icons/oxygen/bt_settings.png</file>
<file>Icons/oxygen/go-down.png</file>
<file>Icons/oxygen/subscribe16.png</file>
<file>Icons/oxygen/download.png</file>
<file>Icons/oxygen/log.png</file>
<file>Icons/oxygen/mail-folder-inbox.png</file>
<file>Icons/oxygen/edit-find.png</file>
<file>Icons/oxygen/edit-clear.png</file>
<file>Icons/oxygen/webui.png</file>
<file>Icons/oxygen/folder-new.png</file>
<file>Icons/oxygen/folder-remote.png</file>
<file>Icons/oxygen/edit-paste.png</file>
<file>Icons/oxygen/run-build.png</file>
<file>Icons/oxygen/proxy.png</file>
<file>Icons/oxygen/services.png</file>
<file>Icons/oxygen/user-group-delete.png</file>
<file>Icons/oxygen/user-group-new.png</file>
<file>Icons/oxygen/log.png</file>
<file>Icons/oxygen/unavailable.png</file>
<file>Icons/oxygen/button_ok.png</file>
<file>Icons/oxygen/button_cancel.png</file>
<file>Icons/oxygen/edit-clear.png</file>
<file>Icons/oxygen/filter.png</file>
<file>Icons/oxygen/encrypted.png</file>
<file>Icons/oxygen/edit_clear.png</file>
<file>Icons/oxygen/download.png</file>
<file>Icons/oxygen/gear32.png</file>
<file>Icons/oxygen/gear.png</file>
<file>Icons/oxygen/remove.png</file>
<file>Icons/oxygen/dialog-warning.png</file>
<file>Icons/oxygen/peer.png</file>
<file>Icons/oxygen/browse.png</file>
<file>Icons/oxygen/unsubscribe16.png</file>
<file>Icons/oxygen/subscribe.png</file>
<file>Icons/oxygen/edit-copy.png</file>
<file>Icons/oxygen/chronometer.png</file>
<file>Icons/oxygen/bt_settings.png</file>
<file>Icons/oxygen/document-new.png</file>
<file>Icons/oxygen/preferences-desktop.png</file>
<file>Icons/oxygen/tab-close.png</file>
<file>Icons/oxygen/wallet.png</file>
<file>Icons/oxygen/webui.png</file>
<file>Icons/oxygen/list-remove.png</file>
<file>Icons/oxygen/connection.png</file>
<file>Icons/oxygen/bug.png</file>
<file>Icons/oxygen/help-about.png</file>
<file>Icons/oxygen/list-add.png</file>
<file>Icons/oxygen/network-server.png</file>
<file>Icons/oxygen/folder.png</file>
<file>Icons/oxygen/urlseed.png</file>
<file>Icons/oxygen/folder-documents.png</file>
<file>Icons/oxygen/edit-cut.png</file>
<file>Icons/oxygen/unsubscribe.png</file>
<file>Icons/oxygen/feed-subscribe.png</file>
<file>Icons/oxygen/subscribe16.png</file>
</qresource>
</RCC>

View File

@@ -47,7 +47,7 @@ namespace json {
case QVariant::LongLong:
case QVariant::UInt:
case QVariant::ULongLong:
//case QMetaType::Float:
case QMetaType::Float:
return v.value<QString>();
case QVariant::StringList:
case QVariant::List: {
@@ -109,7 +109,6 @@ namespace json {
}
QVariantMap fromJson(QString json) {
qDebug("JSON is %s", qPrintable(json));
QVariantMap m;
if(json.startsWith("{") && json.endsWith("}")) {
json.chop(1);
@@ -132,7 +131,6 @@ namespace json {
tmp += c;
}
}
if(!tmp.isEmpty()) couples << tmp;
foreach(QString couple, couples) {
QStringList parts = couple.split(":");
if(parts.size() != 2) continue;
@@ -146,7 +144,7 @@ namespace json {
if(value_str.startsWith("[") && value_str.endsWith("]")) {
value_str.chop(1);
value_str.replace(0, 1, "");
QStringList list_elems = value_str.split(",", QString::SkipEmptyParts);
QStringList list_elems = value_str.split(",");
QVariantList varlist;
foreach(QString list_val, list_elems) {
if(list_val.startsWith("\"") && list_val.endsWith("\"")) {

View File

@@ -1,34 +1,32 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>lang/qbittorrent_nl.qm</file>
<file>lang/qbittorrent_hu.qm</file>
<file>lang/qbittorrent_ru.qm</file>
<file>lang/qbittorrent_zh_TW.qm</file>
<file>lang/qbittorrent_tr.qm</file>
<file>lang/qbittorrent_fi.qm</file>
<file>lang/qbittorrent_es.qm</file>
<file>lang/qbittorrent_sk.qm</file>
<file>lang/qbittorrent_ja.qm</file>
<file>lang/qbittorrent_zh_TW.qm</file>
<file>lang/qbittorrent_pt.qm</file>
<file>lang/qbittorrent_sv.qm</file>
<file>lang/qbittorrent_pl.qm</file>
<file>lang/qbittorrent_it.qm</file>
<file>lang/qbittorrent_ko.qm</file>
<file>lang/qbittorrent_en.qm</file>
<file>lang/qbittorrent_ro.qm</file>
<file>lang/qbittorrent_bg.qm</file>
<file>lang/qbittorrent_ru.qm</file>
<file>lang/qbittorrent_nl.qm</file>
<file>lang/qbittorrent_nb.qm</file>
<file>lang/qbittorrent_fi.qm</file>
<file>lang/qbittorrent_uk.qm</file>
<file>lang/qbittorrent_cs.qm</file>
<file>lang/qbittorrent_pt_BR.qm</file>
<file>lang/qbittorrent_el.qm</file>
<file>lang/qbittorrent_ca.qm</file>
<file>lang/qbittorrent_pt.qm</file>
<file>lang/qbittorrent_it.qm</file>
<file>lang/qbittorrent_fr.qm</file>
<file>lang/qbittorrent_uk.qm</file>
<file>lang/qbittorrent_zh.qm</file>
<file>lang/qbittorrent_ko.qm</file>
<file>lang/qbittorrent_nb.qm</file>
<file>lang/qbittorrent_sv.qm</file>
<file>lang/qbittorrent_de.qm</file>
<file>lang/qbittorrent_sr.qm</file>
<file>lang/qbittorrent_pt_BR.qm</file>
<file>lang/qbittorrent_hu.qm</file>
<file>lang/qbittorrent_da.qm</file>
<file>lang/qbittorrent_cs.qm</file>
<file>lang/qbittorrent_pl.qm</file>
<file>lang/qbittorrent_bg.qm</file>
<file>lang/qbittorrent_ar.qm</file>
<file>lang/qbittorrent_es.qm</file>
<file>lang/qbittorrent_en.qm</file>
<file>lang/qbittorrent_hr.qm</file>
<file>lang/qbittorrent_ro.qm</file>
<file>lang/qbittorrent_de.qm</file>
<file>lang/qbittorrent_zh.qm</file>
<file>lang/qbittorrent_ja.qm</file>
<file>lang/qbittorrent_tr.qm</file>
<file>lang/qbittorrent_fr.qm</file>
</qresource>
</RCC>

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More