diff --git a/appveyor.yml b/.appveyor.yml
similarity index 93%
rename from appveyor.yml
rename to .appveyor.yml
index 07c733b95..5c8e3da42 100644
--- a/appveyor.yml
+++ b/.appveyor.yml
@@ -9,8 +9,8 @@ environment:
REPO_DIR: &REPO_DIR c:\qbittorrent
CACHE_DIR: &CACHE_DIR c:\qbt_cache
- QBT_VER_URL: http://builds.shiki.hu/appveyor/version
- QBT_LIB_URL: http://builds.shiki.hu/appveyor/qbt_libraries.7z
+ QBT_VER_URL: https://builds.shiki.hu/appveyor/version
+ QBT_LIB_URL: https://builds.shiki.hu/appveyor/qbt_libraries.7z
# project directory
clone_folder: *REPO_DIR
diff --git a/.travis.yml b/.travis.yml
index 7f388e13f..696d6173b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,7 @@ language: cpp
os:
- linux
- osx
-osx_image: xcode7
+osx_image: xcode7.3
env:
matrix:
@@ -30,10 +30,10 @@ notifications:
# container-based builds
#sudo: false
-# TODO: osx builder does not enable cache yet, see: https://github.com/travis-ci/travis-ci/issues/4011
-#cache:
- #directories:
- #- $HOME/.ccache
+cache:
+ ccache: true
+ directories:
+ - $HOME/hombebrew_cache
# opt-in Ubuntu Trusty
sudo: required
@@ -80,12 +80,6 @@ before_install:
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
- # ccache
- #if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
- #dpkg-query -L ccache && export PATH="/usr/lib/ccache/:$PATH" && export use_ccache=true ;
- #ccache -V && ccache --show-stats && ccache --zero-stats ;
- #fi ;
-
# setup virtual display for after_success target
if [ "$gui" = true ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ;
fi
@@ -117,27 +111,61 @@ install:
# Qt
if [ "$qt" = 4 ]; then sudo apt-get -qq install qt4-default libqt4-dev ; fi ;
if [ "$qt" = 5 ]; then sudo apt-get -qq install qt5-default qtbase5-dev qttools5-dev-tools ; fi ;
+
+ # ccache
+ if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
+ dpkg-query -L ccache && export use_ccache=true ;
+ ccache -V && ccache --show-stats && ccache --zero-stats ;
+ fi ;
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ mkdir -p "$HOME/hombebrew_cache" ;
+ wget https://builds.shiki.hu/homebrew/version ;
+ if ! cmp --quiet "version" "$HOME/hombebrew_cache/version" ; then
+ echo "Cached files are different from server. Downloading new ones." ;
+ cp "version" $HOME/hombebrew_cache ;
+ cd "$HOME/hombebrew_cache" ;
+ wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb ;
+ wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz ;
+ fi
+
# dependencies
- brew update > /dev/null && brew install colormake libtorrent-rasterbar ;
+ brew update > /dev/null ;
+ brew install colormake ccache ;
+ brew outdated "pkg-config" || brew upgrade "pkg-config" ;
+ # Copy custom libtorrent bottle to homebrew's cache so it can find and install it
+ # Also install our custom libtorrent formula by passing the local path to it
+ # These 2 files are restored from Travis' cache.
+ cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz" "$(brew --cache)" ;
+ brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb" ;
# Qt
- if [ "$qt" = 4 ]; then brew install qt ; fi ;
- if [ "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 ; fi ;
+ if [ "$qt" = 4 ]; then brew install qt && ln -s /usr/local/Cellar/qt/4.8.7_2/plugins /usr/local ; fi ;
+ if [ "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 && ln -s /usr/local/Cellar/qt5/5.7.0/plugins /usr/local ; fi ;
+
+ # ccache
+ if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
+ export PATH="/usr/local/opt/ccache/libexec:$PATH" && export use_ccache=true ;
+ ccache -V && ccache --show-stats && ccache --zero-stats ;
+ fi ;
fi
script:
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # skip usual build when running coverity scan
- cd "$TRAVIS_BUILD_DIR" && ./bootstrap.sh && ./configure $qbtconf
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs/' src/Makefile ; fi
+ - |
+ if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile ; # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
+ sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile ;
+ sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile ;
+ fi
- make && make install
after_success:
- if [ "$gui" = true ]; then qbt_exe="qbittorrent" ; else qbt_exe="qbittorrent-nox" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; fi
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS" ; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "$TRAVIS_BUILD_DIR/src/" && macdeployqt "$qbt_exe.app" && cd "$qbt_exe.app/Contents/MacOS" ; fi
- ./$qbt_exe --version
after_script:
diff --git a/5B7CC9A2.asc b/5B7CC9A2.asc
new file mode 100644
index 000000000..344f51ccf
--- /dev/null
+++ b/5B7CC9A2.asc
@@ -0,0 +1,92 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2
+
+mQINBFcNIIwBEACpGHvHW9ku7rwCSc2Dv4gh3MO3HPoP7Ba4RiEKwa7SCbPzc0DL
+JypV4gNfnrpiO7bWVh5v+otbZTkQeNXWbx6hDUa2e5GCCuJifIu3PxpmMcNJFvvF
+nk5QRf6dtz4Sm2x6joYprvsEUjyk+wHC016/0g7yhc/w0sclXlpKK+8Pl5DFrf5C
+i5uljy3oJgl54D2yYAvxu3BrdTVKhLVYADUf1Fl3b5pV7VJwr+9wGuTqkORe1rpi
+9NGWXUaTmKF8+XAJxlbYIUOZQpQ02clFxz0T7o/+m74N8tK9j7g8H2Q3QwtKi0q1
+gI48LqI/EuZHIaRz/3pEVISlIpWzGqBL/G4I/UtzJLHyvySsqWXAKllKpk97XX77
+XxFy3VL3fR7o4IohAj5fD083X8tuBIP2dxmHzxHTWveKBlEV6C4MdtVRow8ia3lu
+RKLz6PF0hBBpebAP4MWAN8cy4ePBCe9BvyI2+3tPgqtlC2tEZLnRru6mtagPi4sj
+Yo/iFkSQdTXrxeyrMJh161gsWl16JeAfz4Dq8IBoUA1hXIjfM9FcIv1rCY6Y8JwS
+TtWMGYtzIcqE71wZxqnJuyFZkgC14NDTLgUwLf8XJOTWlMW9CY+tStjjw+sNoIPf
+p7YQCmss4p5J8flnxH4xJ8ogOHxENidA+Z/J9mtGjxXIXHavPlO3IEg/DwARAQAB
+tHFzbGVkZ2VoYW1tZXI5OTkgKFVzZWQgZm9yIHNpZ25pbmcgcUJpdHRvcnJlbnQg
+c291cmNlIHRhcmJhbGxzIGFuZCBiaW5hcmllcyB2Mi4pIDxzbGVkZ2VoYW1tZXI5
+OTlAcWJpdHRvcnJlbnQub3JnPokCNwQTAQgAIQUCVw0gjAIbAwULCQgHAgYVCAkK
+CwIEFgIDAQIeAQIXgAAKCRBuSi0CW3zJojB2D/0bKlelRDQDtWzfRyxrdhe5pgAt
+x1AsN/Cl7h8zlbAw38bL+jQ2/GmtzwzEqPfQc7IFnbeg0PZ58p7Hikj9h6JEhkyA
+1qekkriclUmblEwDne3TjPixqgoBfNcDQu74dT08XpM8auFQo31/jJ104903o0O5
++CPOPn2KTdwpcSpwAVIj/3H96gZWegJDNpdByJUVbzYCt1erJ6I0ZURKhzU1VTJj
+ZdEGB2YsvYpt5rsi41IYZZG33jMsPxSDDNJ/MiLXxkn08ZawNET6fnkEJJ37n9Pw
+82lTZjFEFU+KTMT7dNjIejWCRgHVLgW8sO2lCPqMiFfWymD/N3sFpBO+UI86y5ds
+hfGFAWcgSq9pVjuW4sbX3PntBnoNd+geDD1Ic4rP3jHRe5HuYGhtHO6xv/r7HeY5
+HiShCTSSDBJqFmhfjrCo0nISKnzyxgO/rY9vFlwXsKkTyL7s53ONkjwK34WmGnya
+tXdjBWShzAiTfF5hephfBSszmoBG2C8Jcu6P5n4buBY4RCsEa+6jE0R1vCtmpVwx
+WrXOeN2kGYMpAkPK1L69Le0FofgUDKlaFMv7KRl4R367xNRukYrsKwVlontJ+Y72
+X5t1BeRn8VSp0IzhssNXM8a4bTE8lvs889DOS2vgWEHIi0iyIesJYWPs4AKUw4rG
+EDwWxtTS0a7Rfx3DxLRWc2xlZGdlaGFtbWVyOTk5IChVc2VkIGZvciBzaWduaW5n
+IGdpdCBjb21taXRzL3RhZ3MvZXRjLikgPGhhbW1lcmVkOTk5QHFiaXR0b3JyZW50
+Lm9yZz6JAh8EMAEIAAkFAlhie1ICHQAACgkQbkotAlt8yaILIhAAp25o1BbUG2Zk
+At3cSrTFnZSCA7nEygbSUv1Uek33JZfY0Apw5qEM8lQCMZk+mhdrSQCYUJcQlruN
+zJcJf4CH+VGE23xkI3Kf0nGp9Cjn/q6b1hLIPe5rimvw5pTAejFtebcYY/ZJIB8Z
+H1ebuzfqBZ/9k7eYTarZ/ZsgG8YptB0RXBQWOMaSEKwdeo2m7HXHgK3blQiqbuJJ
+uyPbid01Wus4AVN47/FKgDNswPs8irYZsu5yakgpi2KLycGDtSiN5XFHI4xbC0zM
+srR7Cz0/fC+klhGcuxbw0V0It7UUIitgCcTPHXkukUU8i2+AGMyKa1HjchsXDdLg
+DIs6KIurp2ve7znKOz7h1aX8cOBmB/QYeYAx9jRRkePMIRT8V1lRwfvJlJxx1+G3
+e2gJLjqTN8a08KHHjdY/S0ZFERxSlmOym2uf/y6di1ipDPxo8xvDuS5kDbdZLC0t
+XijlsH8ONK27KNuWhucG8zHzKQvnPw2qN06SZq4FjbSmAkkuYs56heLEXMzFr75k
+SE8rUoQQ+ABG9gU46GEvKlZxqSwXgGnb1X6K7h8svjMh/NlAU358p8Sra4Ru5tz4
+jUu9MoVEw5Lbjcrsnp6/4Kk1Q2ckBNt43nv8/+C7NsC3xi6BrOInuaKHZ4QsTuzJ
+m1/A4zlKRnUi6T98DXfIYnNuV9NSmAWJAjkEEwEIACMFAlhiemMCGwMHCwkIBwMC
+AQYVCAIJCgsEFgIDAQIeAQIXgAAKCRBuSi0CW3zJor6yD/9N2U0INx0nYpGkmvah
+yVG/vw2S6hhKK+03AN+RrtddNRg4aBf/gmOvRWQhAmFnXOBA7fO09wgcljaV5tVb
+MYyYZvHhK0o2/sli2p/M5N8ZxchRHypjxUSEyG9ZQ06QG5DVhh4HtM8nIN+UcwTV
+C5QjyoWZvHf+tNroyFeh7zT+w4kX1VxgynTQr5LGdYsrVA3CFyT3zsBWV3dMae23
+22CHOirsBBLwairHUsWW+BdThT3MkKYpTEV0jkH4OyAXhJYcS5IjjtKQ8UpZE9dw
+f4saJ0TnXNe7goPRZtH7UjPwfVbtYK4y8QklWUTRxgoBxNwSC5X7Flg+3xXxE/VU
+U4cehyRkH64i7MJDoFkqh5JtjkgIz+kuTTXb7xR0Wf+JXrGMybZTR8xth2TEMC20
+1FT5L5+0vH1WRzL7bhlaU3EXyCnoH8sDvMEClZbibbew+rf7fC3tFU41ohUT0HDl
+zlyfVjRvBHWMTgfpWKBV2m/qP941xTJ9VHxOlAB02XKUZYwFt07CpH+yjMOCOzA4
+cTPBD3mGRuft0V0BJ8bA5bcTly/GBciRX0Y5oIeHZGgq2czb0sywSYT6mPoQMFNM
+B+Cwr4pm90r1DMMfW518onF2itwyN/Id0FsWDhsLJHKluBJw52C3OnxCuToVutTm
+xntqpPVv62LaeVeWQqxIieTJErRQc2xlZGdlaGFtbWVyXzk5OSAoVXNlZCBmb3Ig
+c2lnbmluZyBnaXQgY29tbWl0cy90YWdzL2V0YykgPGhhbW1lcmVkOTk5QGdtYWls
+LmNvbT6JAjkEEwEIACMFAlhifeICGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIX
+gAAKCRBuSi0CW3zJolcCD/9xPBNEkFtnhTW89th0TFZnB5oykCQjyefquvQs8KWT
+C92/1VizHi4ZxDehHWP9IKVWT3ZJthj5ZXBSedyl1tHnwkyrUYBW9roQwtDWPncK
+pXl/HsE6p3q6EIus+g6YJo4UvYachJFAZATZp1WDBPIswziHGzaL0tndFWZuVM8V
+QD0tfPQsS1qCDVv6+B1JWZDnA1JzdSG/uzPhL95q/ff6JmNbfSAVedK2PyqYshnC
+KWBx6Yna/0ColBuDFho8+bDuHPQcM35xyjPosVD7moXQiY4yMAJ+VzwEBaCFleI0
+RBWw8/+qyoFqfIKwdq8G+7I9LjWpBiN2+uQBZ+OAvsMWyRShLopxt3JluPTtL6xb
+Ca6dglOdlaOS/A6FK7u05k/8kQMDS5Jq2/rpfTPRl1/weCaJZgfRIBosk1Mon/pR
+p1zd0abM4t7BcGQpwSkKAmqlKCrWf886EFQT0CJTBo8q7pzgpVraWWPVsmAOdkfU
+YcKBgz1A2uMSAxypkSzaDZkIVj6I7gwiGk7IMYx1OK7Ev46h/x4Z7kgT0y3DYYOq
+ggVEKQ+15Krn7bZ35s8vbZdfnVKPSXdCC8jkIMBmGmRX6cgZZ3OXZlrrHht5icgJ
+5Z2d1M4JUoEZVUr2xNZkkaMk01NAIpGgKvIS6yHuj6vE4GMJ+A/qEW6J60/3YHRe
+0bkCDQRXDSCMARAAqMIVJizEJp205c546IN75xeYiFszNXcs3768IY8bOoWj+rTw
+t2wIwtL/3O5K2dG79CSt2H5o6BPKmq43tOO60YW3Yk3m9BB/gnAVqk0QOPr5O8+y
+eBzdElU8CZh6y6zZMWugSkNmTDm6jZzPhgNjcjrit/dl9+0DGqJQcqoD8WzEWNcW
+rMHVz9cDewnLSVkwR758mZMaIiL7R10MZ++tNrC0j69UINqx+9z1r1J07+NNnxqS
+TxVRcbjPYtM9E+tUiVFS2HPWN9ShVDkBAEdoWh90qzRaMiFl2NGNGOD1iHx/xr06
+RMeGEEXt2vhSlhfMW2YQW+UD2jzlFbARf53v39MUKKscGuIpBhxGw3JCq4l6qLW/
+bDkgnoXlOhZDmhQm6OpsjAyk9IEdd3ponSc7yYD3mUkJKR9eTaALD5t6TQGyNHak
+b4UfoXtE2RR78cbPlLIwag7eQ8GsNA+dfjowmOZdojx3ROsHZdGQwb0YFLjuKAus
+A3TY+lCfbS6kzE2iI2DuaW+3dICcLrYuibbVb0CBNHyD+8KEtczdur/wm0lhqyVJ
+kGyZKZT8C2cPxywKgy1Rn6F8Yfmj0Lna3nvtaZu0ZUS4/8Lit5PcOso1lSmYBuD6
+yq+GEAMCnUmn1Pm8eZRMlxxQuTPvyJKQrRDhbtAAr472MSnoJKlS4SfaUF0AEQEA
+AYkCHwQYAQgACQUCVw0gjAIbDAAKCRBuSi0CW3zJomZDD/9IJmzd5hiEzntlp84p
+yIJcfyIRe4KImvldAy6T02OSIbF1HzCNnwmqIPob6MOdMZ+KNwMK0htRkrRr/zM0
+34+lBiWKZt+tVYHu49ioTYXEjAc5qDJE09Sq7HceQnhgE48f1n54XGT5G2w5gw+/
+a8Qn1SceE44VwXafL3E1gKaOrrsb1UH/AJhp+W4VMu+7bLXu7h1tN6v2PhvCYvBt
+3zyy8Q8xfJ2x7/D1lbF8ATJAiZ/km9x5bRm7OGRliVYaUe1nyR42fZOj3CBmAR0+
+lZLgjriqdMXrs+qlBbrmAhkn0XPQXAeaPifKoKIGDAUWIsqDHqM7imMGT+MR9APf
+Sw8M4enOJWL+HnKpVBEARCEDpaFpJ3u7QRucFybpEhvIymoNftyw+urId2Eg2K33
+NypeZo3M1K2LC65f2Ta7f/sZcIDUTbgW+m334fgVl1KptDA5DX3U9lTci7mi4uPu
+AFtbWrB1di4jYrxXYuzFm5g4xTb0Hw3kYIB6WXF+I7i0JaGOTHxPC5X5lIAZrYrk
+xh+1n1Y1CY+TC8JcTzwORJIbFFm9tD/BHXa4849k4DVvFYCZkhq+/56FKZfoVByh
+B+x+2GaMlsBm1uPniO4lAakFPpIi0kaap4UVayQ/7ak+BhscAIHZUy6NtgZkuvW3
+xdpwp07LYo2ilhMI8RnzmtoRmg==
+=UBeB
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eff858410..a049e0677 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.2)
-cmake_policy(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
+cmake_policy(VERSION 3.5)
project(qBittorrent VERSION 3.4.0.0)
diff --git a/CODING_GUIDELINES.md b/CODING_GUIDELINES.md
index 534cf476a..f7430b2ad 100644
--- a/CODING_GUIDELINES.md
+++ b/CODING_GUIDELINES.md
@@ -29,8 +29,10 @@ class MyOtherClass
{
public:
//code
+
protected:
//code
+
private:
//code
};
@@ -87,10 +89,14 @@ default:
}
```
-#### d. single-line blocks (lambdas, initializer lists etc.) ####
+#### d. Brace enclosed initializers ####
+Unlike single-line functions, you must not insert spaces between the brackets and concluded expressions.
+But you must insert a space between the variable name and initializer.
```c++
- {} // empty - space before {
- { body } // spaces around { and before }
+Class obj {}; // empty
+Class obj {expr};
+Class obj {expr1, /*...,*/ exprN};
+QVariantMap map {{"key1", 5}, {"key2", 10}};
```
### 2. If blocks ###
@@ -173,11 +179,11 @@ All names should be camelCased.
#### a. Type names and namespaces ####
Type names and namespaces start with Upper case letter (except POD types).
```c++
-class ClassName {}
+class ClassName {};
-struct StructName {}
+struct StructName {};
-enum EnumName {}
+enum EnumName {};
typedef QList SomeList;
@@ -201,7 +207,40 @@ class MyClass
}
```
-### 8. Misc.###
+### 8. Header inclusion order.###
+The headers should be placed in the following order:
+ 1. Module header (in .cpp)
+ 2. System/Qt/Boost etc. headers (splitted in subcategories if you have many).
+ 3. Application headers, starting from *Base* headers.
+
+The headers should be ordered alphabetically within each group (subgroup).
+
+Example:
+```c++
+// examplewidget.cpp
+
+#include "examplewidget.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "base/bittorrent/session.h"
+#include "base/bittorrent/infohash.h"
+#include "base/utils/fs.h"
+#include "base/utils/misc.h"
+#include "base/utils/string.h"
+#include "ui_examplewidget.h"
+
+```
+
+### 9. Misc.###
* Line breaks for long lines with operation:
@@ -211,47 +250,18 @@ a += "b"
+ "d";
```
-* Initializers
-
-We allow brace enclosed initializers only for aggregates and arrays/containers.
-Brace enclosed initializer MUST be used with equality sign if it follows the variable declaration.
-Brace enclosed initializer MUST be additionally enclosed in parentheses if it is used in constructor initialization list.
-Some valid use cases:
-```c++
-// aggregate
-Person john = { "John", "Smith", 21 };
-Person *john = new Person { "John", "Smith", 21 };
-
-// array
-int array[] = { 1, 2, 3, 4 };
-
-// container
-QHash map = {
- { "key1", "value1" },
- { "key2", "value2" }
-);
-
-// member array
-SomeClass::SomeClass(BaseClass *parent)
- : BaseClass(parent)
- , m_someArrayMember({ 1, 2, 3, 4 })
-{
-}
-
-// return from function
-Person getPersonByName(const QString &name)
-{
- // do something
- return { name, surname, age };
-}
-
-// function argument
-doSomething({ name, surname, age }, someOtherArg);
-```
-
* **auto** keyword
-We allow the use of the **auto** keyword only where it doesn't break the readability of the code (i.e. either we can gather enough information about the type from the right part of the expression, or we do not need to know the exact type), or where it is strictly necessary (for example, to compute the type of a lambda, etc.).
+We allow the use of the **auto** keyword only where it is strictly necessary
+(for example, to declare a lambda object, etc.), or where it **enhances** the readability of the code.
+Declarations for which one can gather enough information about the object interface (type) from its name
+or the usage pattern (an iterator or a loop variable are good examples of clear patterns)
+or the right part of the expression nicely fit here.
+
+When weighing whether to use an auto-typed variable please think about potential reviewers of your code,
+who will read it as a plain diff (on github.com, for instance). Please make sure that such reviewers can
+understand the code completely and without excessive effort.
+
Some valid use cases:
```c++
template
@@ -274,9 +284,17 @@ auto spinBox = static_cast(sender());
* Space around operations eg `a = b + c` or `a=b+c`:
-Before and after the assignment there should be a space. One exception could be: for loops.
+Before and after the assignment and other binary (and ternary) operators there should be a space.
+There should not be a space between increment/decrement and its operand.
+Some valid use cases:
```c++
-for (int a=0; a
- Copyright (C)
-
- 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 St, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- 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.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-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:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- , 1 April 1989
- Ty Coon, President of Vice
-
-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 GNU Library General
-Public License instead of this License.
+ END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
index 818624c12..79f7e2a9d 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,13 @@ qbittorrent
will install and execute qBittorrent hopefully without any problem.
+### Public key:
+Starting from v3.3.4 all source tarballs and binaries are signed.
+The key currently used is 4096R/[5B7CC9A2](https://pgp.mit.edu/pks/lookup?op=get&search=0x6E4A2D025B7CC9A2) with fingerprint `D8F3DA77AAC6741053599C136E4A2D025B7CC9A2`.
+You can also download it from [here](https://github.com/qbittorrent/qBittorrent/raw/master/5B7CC9A2.asc).
+**PREVIOUSLY** the following key was used to sign the v3.3.4 source tarballs and v3.3.4 Windows installer **only**: 4096R/[520EC6F6](https://pgp.mit.edu/pks/lookup?op=get&search=0xA1ACCAE4520EC6F6) with fingerprint `F4A5FD201B117B1C2AB590E2A1ACCAE4520EC6F6`.
+
+### Misc:
For more information please visit:
http://www.qbittorrent.org
diff --git a/bootstrap.sh b/bootstrap.sh
index 4b05c83f8..f005e2ec8 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
aclocal -I m4
autoconf
diff --git a/cmake/Modules/FindLibtorrentRasterbar.cmake b/cmake/Modules/FindLibtorrentRasterbar.cmake
index f1ece1be4..de96bd1ad 100644
--- a/cmake/Modules/FindLibtorrentRasterbar.cmake
+++ b/cmake/Modules/FindLibtorrentRasterbar.cmake
@@ -91,3 +91,16 @@ find_package_handle_standard_args(LibtorrentRasterbar DEFAULT_MSG
mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
LibtorrentRasterbar_ENCRYPTION_INDEX)
+
+if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::LibTorrent)
+ add_library(LibtorrentRasterbar::LibTorrent UNKNOWN IMPORTED)
+
+ set_target_properties(LibtorrentRasterbar::LibTorrent PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
+ IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
+ INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
+ INTERFACE_LINK_LIBRARIES "${LibtorrentRasterbar_LIBRARIES}"
+ INTERFACE_COMPILE_OPTIONS "${LibtorrentRasterbar_DEFINITIONS}"
+ )
+endif()
diff --git a/cmake/Modules/FindQtSingleApplication.cmake b/cmake/Modules/FindQtSingleApplication.cmake
index 4229e98b4..eb56607e9 100644
--- a/cmake/Modules/FindQtSingleApplication.cmake
+++ b/cmake/Modules/FindQtSingleApplication.cmake
@@ -79,3 +79,16 @@ ELSE (QTSINGLEAPPLICATION_FOUND)
ENDIF (QTSINGLEAPPLICATION_FOUND)
MARK_AS_ADVANCED(QTSINGLEAPPLICATION_INCLUDE_DIR QTSINGLEAPPLICATION_LIBRARY)
+
+if(NOT TARGET QtSingleApplication::QtSingleApplication)
+ add_library(QtSingleApplication::QtSingleApplication UNKNOWN IMPORTED)
+ set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
+ INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
+ )
+ if(EXISTS "${QTSINGLEAPPLICATION_LIBRARY}")
+ set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
+ IMPORTED_LOCATION "${QTSINGLEAPPLICATION_LIBRARY}")
+ endif()
+endif(NOT TARGET QtSingleApplication::QtSingleApplication)
diff --git a/cmake/Modules/QbtTargetSources.cmake b/cmake/Modules/QbtTargetSources.cmake
new file mode 100644
index 000000000..ced0e4b58
--- /dev/null
+++ b/cmake/Modules/QbtTargetSources.cmake
@@ -0,0 +1,17 @@
+# a helper function which appends source to the main qBt target
+# the target name is read from QBT_TARGET_NAME variable
+# sources file names are relative to the the ${qbt_executable_SOURCE_DIR}
+
+function (qbt_target_sources)
+ set (_sources_rel "")
+ foreach (_source IN ITEMS ${ARGN})
+ if (IS_ABSOLUTE "${_source}")
+ set(source_abs "${_source}")
+ else()
+ get_filename_component(_source_abs "${_source}" ABSOLUTE)
+ endif()
+ file (RELATIVE_PATH _source_rel "${qbt_executable_SOURCE_DIR}" "${_source_abs}")
+ list (APPEND _sources_rel "${_source_rel}")
+ endforeach()
+ target_sources (${QBT_TARGET_NAME} PRIVATE "${_sources_rel}")
+endfunction (qbt_target_sources)
diff --git a/cmake/Modules/winconf.cmake b/cmake/Modules/winconf.cmake
index 65889751b..7981e8a03 100644
--- a/cmake/Modules/winconf.cmake
+++ b/cmake/Modules/winconf.cmake
@@ -29,16 +29,17 @@ set(Boost_USE_STATIC_LIBS True)
# with usual unix subdirectories (bin, lib, include)
# if so, we just need to set CMAKE_SYSTEM_PREFIX_PATH
# If it is not the case, individual paths need to be specified manually (see below)
-set(COMMON_INSTALL_PREFIX "c:/usr")
+set(COMMON_INSTALL_PREFIX "c:/usr" CACHE PATH "Prefix used to install all the required libraries")
list(APPEND CMAKE_SYSTEM_PREFIX_PATH "${COMMON_INSTALL_PREFIX}")
# If two version of Qt are installed, separate prefixes are needed most likely
-set(QT4_INSTALL_PREFIX "${COMMON_INSTALL_PREFIX}/lib/qt4")
-set(QT5_INSTALL_PREFIX "${COMMON_INSTALL_PREFIX}/lib/qt5")
+set(QT4_INSTALL_PREFIX "${COMMON_INSTALL_PREFIX}/lib/qt4" CACHE PATH "Prefix where Qt4 is installed")
+set(QT5_INSTALL_PREFIX "${COMMON_INSTALL_PREFIX}/lib/qt5" CACHE PATH "Prefix where Qt5 is installed")
# it is safe to set Qt dirs even if their files are directly in the prefix
# Qt4
if(NOT QT5)
+ # for qt 4 we need qmake, Qt5 provides cmake config files
LIST(APPEND CMAKE_PROGRAM_PATH "${QT4_INSTALL_PREFIX}/bin/")
endif(NOT QT5)
diff --git a/conf.pri.in b/conf.pri.in
index e0b03c76f..09ce6d1e5 100644
--- a/conf.pri.in
+++ b/conf.pri.in
@@ -6,8 +6,16 @@ DATADIR = @EXPAND_DATADIR@
MANPREFIX = @EXPAND_MANDIR@
QMAKE_CXXFLAGS += @QBT_CONF_EXTRA_CFLAGS@
-INCLUDEPATH += @QBT_CONF_INCLUDES@
-LIBS += @LDFLAGS@ @LIBS@
+
+EXTERNAL_INCLUDES = @QBT_CONF_INCLUDES@
+EXTERNAL_INCLUDES -= $$QMAKE_DEFAULT_INCDIRS
+# added /usr/local/include due to Qt 5.7.0 bug on macOS
+macx: EXTERNAL_INCLUDES += "/usr/local/include"
+INCLUDEPATH += $$EXTERNAL_INCLUDES
+
+EXTERNAL_LIBS = @LDFLAGS@ @LIBS@
+EXTERNAL_LIBS -= $$QMAKE_DEFAULT_LIBDIRS
+LIBS += $$EXTERNAL_LIBS
CONFIG += @QBT_ADD_CONFIG@
CONFIG -= @QBT_REMOVE_CONFIG@
diff --git a/configure b/configure
index c8e29ed7a..2c72cb291 100755
--- a/configure
+++ b/configure
@@ -4257,6 +4257,17 @@ else
$as_echo "no" >&6; }
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether OS is macOS" >&5
+$as_echo_n "checking whether OS is macOS... " >&6; }
+if expr "$host_os" : ".*darwin.*" > /dev/null; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_qt_dbus=no
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
# Require 0.23 pkg-config
@@ -5090,7 +5101,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
int
main ()
{
-boost::system::system_category
+boost::system::error_category *a = 0;
;
return 0;
}
diff --git a/configure.ac b/configure.ac
index 1ac057207..c008c578e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,12 @@ AS_IF([expr "$host_os" : ".*freebsd.*" > /dev/null],
LIBS="-lexecinfo $LIBS"],
[AC_MSG_RESULT([no])])
+AC_MSG_CHECKING([whether OS is macOS])
+AS_IF([expr "$host_os" : ".*darwin.*" > /dev/null],
+ [AC_MSG_RESULT([yes])
+ enable_qt_dbus=no],
+ [AC_MSG_RESULT([no])])
+
# Require 0.23 pkg-config
PKG_PROG_PKG_CONFIG([0.23])
AS_IF([test "x$PKG_CONFIG" = "x"],
diff --git a/dist/mac/CMakeLists.txt b/dist/mac/CMakeLists.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/dist/mac/bundle.cmake b/dist/mac/bundle.cmake
new file mode 100644
index 000000000..7ef44a736
--- /dev/null
+++ b/dist/mac/bundle.cmake
@@ -0,0 +1,2 @@
+include(BundleUtilities)
+fixup_bundle("$ENV{DESTDIR}/${CMAKE_INSTALL_PREFIX}/qbittorrent.app" "" "")
diff --git a/dist/qt-translations/qt_ar.qm b/dist/qt-translations/qt_ar.qm
index e524d5b77..372c1ab8f 100644
Binary files a/dist/qt-translations/qt_ar.qm and b/dist/qt-translations/qt_ar.qm differ
diff --git a/dist/qt-translations/qt_ca.qm b/dist/qt-translations/qt_ca.qm
index 097360310..b9d29a1e0 100644
Binary files a/dist/qt-translations/qt_ca.qm and b/dist/qt-translations/qt_ca.qm differ
diff --git a/dist/qt-translations/qt_cs.qm b/dist/qt-translations/qt_cs.qm
index 51ac5694f..b04e24c6d 100644
Binary files a/dist/qt-translations/qt_cs.qm and b/dist/qt-translations/qt_cs.qm differ
diff --git a/dist/qt-translations/qt_da.qm b/dist/qt-translations/qt_da.qm
index 177595c8e..d198c7fd0 100644
Binary files a/dist/qt-translations/qt_da.qm and b/dist/qt-translations/qt_da.qm differ
diff --git a/dist/qt-translations/qt_de.qm b/dist/qt-translations/qt_de.qm
index 113bbcc39..3650eec59 100644
Binary files a/dist/qt-translations/qt_de.qm and b/dist/qt-translations/qt_de.qm differ
diff --git a/dist/qt-translations/qt_es.qm b/dist/qt-translations/qt_es.qm
index 519116090..7f721faca 100644
Binary files a/dist/qt-translations/qt_es.qm and b/dist/qt-translations/qt_es.qm differ
diff --git a/dist/qt-translations/qt_fa.qm b/dist/qt-translations/qt_fa.qm
new file mode 100644
index 000000000..0968c5d4e
Binary files /dev/null and b/dist/qt-translations/qt_fa.qm differ
diff --git a/dist/qt-translations/qt_fi.qm b/dist/qt-translations/qt_fi.qm
index dc70e8a07..c78467648 100644
Binary files a/dist/qt-translations/qt_fi.qm and b/dist/qt-translations/qt_fi.qm differ
diff --git a/dist/qt-translations/qt_fr.qm b/dist/qt-translations/qt_fr.qm
index 609857729..d40706bfa 100644
Binary files a/dist/qt-translations/qt_fr.qm and b/dist/qt-translations/qt_fr.qm differ
diff --git a/dist/qt-translations/qt_gl.qm b/dist/qt-translations/qt_gl.qm
index 36d81a093..4f7e3c433 100644
Binary files a/dist/qt-translations/qt_gl.qm and b/dist/qt-translations/qt_gl.qm differ
diff --git a/dist/qt-translations/qt_he.qm b/dist/qt-translations/qt_he.qm
index 4ff507e07..04da4c9fa 100644
Binary files a/dist/qt-translations/qt_he.qm and b/dist/qt-translations/qt_he.qm differ
diff --git a/dist/qt-translations/qt_hu.qm b/dist/qt-translations/qt_hu.qm
index fe78c348d..048744323 100644
Binary files a/dist/qt-translations/qt_hu.qm and b/dist/qt-translations/qt_hu.qm differ
diff --git a/dist/qt-translations/qt_it.qm b/dist/qt-translations/qt_it.qm
index 1e69f88fd..a4b9f2559 100644
Binary files a/dist/qt-translations/qt_it.qm and b/dist/qt-translations/qt_it.qm differ
diff --git a/dist/qt-translations/qt_ja.qm b/dist/qt-translations/qt_ja.qm
index c37cb08b2..99648eaa5 100644
Binary files a/dist/qt-translations/qt_ja.qm and b/dist/qt-translations/qt_ja.qm differ
diff --git a/dist/qt-translations/qt_ko.qm b/dist/qt-translations/qt_ko.qm
index a11ebe474..c01c33b73 100644
Binary files a/dist/qt-translations/qt_ko.qm and b/dist/qt-translations/qt_ko.qm differ
diff --git a/dist/qt-translations/qt_lt.qm b/dist/qt-translations/qt_lt.qm
index 338cd5075..8a2255325 100644
Binary files a/dist/qt-translations/qt_lt.qm and b/dist/qt-translations/qt_lt.qm differ
diff --git a/dist/qt-translations/qt_pl.qm b/dist/qt-translations/qt_pl.qm
index 8052d82ee..c2ea0947b 100644
Binary files a/dist/qt-translations/qt_pl.qm and b/dist/qt-translations/qt_pl.qm differ
diff --git a/dist/qt-translations/qt_pt.qm b/dist/qt-translations/qt_pt.qm
index 9494f6865..9ac3b08d9 100644
Binary files a/dist/qt-translations/qt_pt.qm and b/dist/qt-translations/qt_pt.qm differ
diff --git a/dist/qt-translations/qt_ru.qm b/dist/qt-translations/qt_ru.qm
index 1df39b1b7..d54bae8eb 100644
Binary files a/dist/qt-translations/qt_ru.qm and b/dist/qt-translations/qt_ru.qm differ
diff --git a/dist/qt-translations/qt_sk.qm b/dist/qt-translations/qt_sk.qm
index 84517643e..9e8f8622d 100644
Binary files a/dist/qt-translations/qt_sk.qm and b/dist/qt-translations/qt_sk.qm differ
diff --git a/dist/qt-translations/qt_sl.qm b/dist/qt-translations/qt_sl.qm
new file mode 100644
index 000000000..2575b3fab
Binary files /dev/null and b/dist/qt-translations/qt_sl.qm differ
diff --git a/dist/qt-translations/qt_sv.qm b/dist/qt-translations/qt_sv.qm
index f3556bcaa..294ae141c 100644
Binary files a/dist/qt-translations/qt_sv.qm and b/dist/qt-translations/qt_sv.qm differ
diff --git a/dist/qt-translations/qt_uk.qm b/dist/qt-translations/qt_uk.qm
index e2beb1726..2d9dabc61 100644
Binary files a/dist/qt-translations/qt_uk.qm and b/dist/qt-translations/qt_uk.qm differ
diff --git a/dist/qt-translations/qt_zh_CN.qm b/dist/qt-translations/qt_zh_CN.qm
index 623b8e33a..d6f3648ff 100644
Binary files a/dist/qt-translations/qt_zh_CN.qm and b/dist/qt-translations/qt_zh_CN.qm differ
diff --git a/dist/qt-translations/qt_zh_TW.qm b/dist/qt-translations/qt_zh_TW.qm
index a9a25b204..b391a6b69 100644
Binary files a/dist/qt-translations/qt_zh_TW.qm and b/dist/qt-translations/qt_zh_TW.qm differ
diff --git a/dist/qt-translations/qtbase_ca.qm b/dist/qt-translations/qtbase_ca.qm
new file mode 100644
index 000000000..7c7cf0b82
Binary files /dev/null and b/dist/qt-translations/qtbase_ca.qm differ
diff --git a/dist/qt-translations/qtbase_cs.qm b/dist/qt-translations/qtbase_cs.qm
new file mode 100644
index 000000000..1dca943f3
Binary files /dev/null and b/dist/qt-translations/qtbase_cs.qm differ
diff --git a/dist/qt-translations/qtbase_de.qm b/dist/qt-translations/qtbase_de.qm
new file mode 100644
index 000000000..d73d22032
Binary files /dev/null and b/dist/qt-translations/qtbase_de.qm differ
diff --git a/dist/qt-translations/qtbase_fi.qm b/dist/qt-translations/qtbase_fi.qm
new file mode 100644
index 000000000..980e087c3
Binary files /dev/null and b/dist/qt-translations/qtbase_fi.qm differ
diff --git a/dist/qt-translations/qtbase_fr.qm b/dist/qt-translations/qtbase_fr.qm
new file mode 100644
index 000000000..8353f0a93
Binary files /dev/null and b/dist/qt-translations/qtbase_fr.qm differ
diff --git a/dist/qt-translations/qtbase_he.qm b/dist/qt-translations/qtbase_he.qm
new file mode 100644
index 000000000..0ac1ed220
Binary files /dev/null and b/dist/qt-translations/qtbase_he.qm differ
diff --git a/dist/qt-translations/qtbase_hu.qm b/dist/qt-translations/qtbase_hu.qm
new file mode 100644
index 000000000..4b67de34e
Binary files /dev/null and b/dist/qt-translations/qtbase_hu.qm differ
diff --git a/dist/qt-translations/qtbase_it.qm b/dist/qt-translations/qtbase_it.qm
new file mode 100644
index 000000000..4c76a0b15
Binary files /dev/null and b/dist/qt-translations/qtbase_it.qm differ
diff --git a/dist/qt-translations/qtbase_ja.qm b/dist/qt-translations/qtbase_ja.qm
new file mode 100644
index 000000000..74409b1a5
Binary files /dev/null and b/dist/qt-translations/qtbase_ja.qm differ
diff --git a/dist/qt-translations/qtbase_ko.qm b/dist/qt-translations/qtbase_ko.qm
new file mode 100644
index 000000000..f960d8ac4
Binary files /dev/null and b/dist/qt-translations/qtbase_ko.qm differ
diff --git a/dist/qt-translations/qtbase_lv.qm b/dist/qt-translations/qtbase_lv.qm
new file mode 100644
index 000000000..c1dbfbd2e
Binary files /dev/null and b/dist/qt-translations/qtbase_lv.qm differ
diff --git a/dist/qt-translations/qtbase_pl.qm b/dist/qt-translations/qtbase_pl.qm
new file mode 100644
index 000000000..021088e4f
Binary files /dev/null and b/dist/qt-translations/qtbase_pl.qm differ
diff --git a/dist/qt-translations/qtbase_ru.qm b/dist/qt-translations/qtbase_ru.qm
new file mode 100644
index 000000000..a11b7c75a
Binary files /dev/null and b/dist/qt-translations/qtbase_ru.qm differ
diff --git a/dist/qt-translations/qtbase_sk.qm b/dist/qt-translations/qtbase_sk.qm
new file mode 100644
index 000000000..5f6b2f33c
Binary files /dev/null and b/dist/qt-translations/qtbase_sk.qm differ
diff --git a/dist/qt-translations/qtbase_uk.qm b/dist/qt-translations/qtbase_uk.qm
new file mode 100644
index 000000000..7d588e99f
Binary files /dev/null and b/dist/qt-translations/qtbase_uk.qm differ
diff --git a/dist/windows/README.txt b/dist/windows/README.txt
index d4593b391..fa8b745ad 100644
--- a/dist/windows/README.txt
+++ b/dist/windows/README.txt
@@ -1,12 +1,12 @@
TRANSLATORS:
-1. Use an editor that has NSIS syntax highlighting(eg Notepad++/Geany). This will
+1. Use an editor that has NSIS syntax highlighting(eg Notepad++/Geany). This will
make your life easier.
-2. Open the relevant .nsi file that exists in the folder named
+2. Open the relevant .nsi file that exists in the folder named
"installer-translations"
-3. Lines starting with ";" are considered comments. These include the
+3. Lines starting with ";" are considered comments. These include the
english message to help you with the translation.
-4. Edit only the part inside the quotation marks(""). Unless you know
+4. Edit only the part inside the quotation marks(""). Unless you know
what you are doing.
5. Save the files with utf8 encoding and BOM.
6. Submit your changes: 1) as a pull request to the official git repo or
@@ -16,17 +16,13 @@ TRANSLATORS:
PACKAGERS:
You will need NSIS and upx to make the installer. You need a unicode version of NSIS.
-I tested with NSIS 3.0a0.
+I tested with NSIS 3.0 (final).
1. Open the options.nsi file in an editor and change line that contains
- "!define PROG_VERSION "3.0.3"" to the version of qbittorrent you just built.
-2. Extract the plugins found in the folder "nsis plugins" into your
+ "!define PROG_VERSION "3.0.3"" to the version of qbittorrent you just built.
+2. Extract the plugins found in the folder "nsis plugins" into your
NSIS's unicode Plugin directory(usually C:\Program Files\NSIS\Plugins\x86-unicode).
- Only the *.dll files are needed. Don't use the .dll from UAC.zip, use the one from "UAC Unicode.zip".
- NOTE: To build the unicode version of UAC with MSVC2008 you need:
- a) the sources from UAC.zip
- b) apply the util.cpp.diff from "UAC Unicode.zip" to util.cpp
- c) in a msvc command prompt issue: cl.exe /O1s /GS- /GR- /EHs-c- /Zl /LD /DUNICODE RunAs.cpp uac.cpp util.cpp /link kernel32.lib user32.lib shell32.lib advapi32.lib ole32.lib /DLL /MANIFEST:NO /OUT:uac.dll
+ Only the *.dll files are needed. Use the unicode version of the dlls.
3. The script you need to compile is "qbittorrent.nsi". It includes all other necessary scripts.
4. The script expects the following file tree:
@@ -53,10 +49,11 @@ qbittorrent.exe
qbittorrent.nsi
qt.conf
translations.nsi
+UAC.nsh
uninstaller.nsi
-5. "license.txt" is a text file that contains the text rendered
+5. "license.txt" is a text file that contains the text rendered
from src\gui\gpl.html or the text contained in COPYING
6. "qbittorrent.exe" is the compiled binary file.
@@ -64,7 +61,7 @@ SCRIPT HACKERS:
If you add any new LangString variable to the scripts you NEED to provide
"translations" of it to all the .nsi files inside "installer-translations.
-You can always leave the english string but you have to use all the LANG_
-for the given variable. Otherwise, if the user chooses a language that you
-haven't provided a LANG_ for your variable then your string will be empty.
+You can always leave the english string but you have to use all the LANG_
+for the given variable. Otherwise, if the user chooses a language that you
+haven't provided a LANG_ for your variable then your string will be empty.
Don't worry though, NSIS throws warnings for this when compiling the scripts.
diff --git a/dist/windows/UAC.nsh b/dist/windows/UAC.nsh
index b496d7011..08979aba9 100644
--- a/dist/windows/UAC.nsh
+++ b/dist/windows/UAC.nsh
@@ -228,6 +228,8 @@ pop $_LOGICLIB_TEMP
!undef _UAC_ParseDefineFlags_orin_this
!ifdef _UAC_ParseDefineFlags_orin_f1
!undef _UAC_ParseDefineFlags_orin_f1
+!endif
+!ifdef _UAC_ParseDefineFlags_orin_f2
!undef _UAC_ParseDefineFlags_orin_f2
!endif
!macroend
diff --git a/dist/windows/installer-translations/portuguese.nsi b/dist/windows/installer-translations/portuguese.nsi
index db481a979..ee73f86c2 100644
--- a/dist/windows/installer-translations/portuguese.nsi
+++ b/dist/windows/installer-translations/portuguese.nsi
@@ -17,7 +17,7 @@ LangString inst_firewallinfo ${LANG_PORTUGUESE} "Adicionando regra à firewall d
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_PORTUGUESE} "O qBittorrent está a ser executado. Feche a aplicação antes de instalar esta versão."
;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings."
-LangString inst_uninstall_question ${LANG_PORTUGUESE} "A previous installation was detected. It will be uninstalled without deleting user settings."
+LangString inst_uninstall_question ${LANG_PORTUGUESE} "Uma antiga instalação foi encontrada.Essa mesma será desinstalada sem apagar as definições do usuário."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_PORTUGUESE} "A desinstalar versão anterior."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
diff --git a/dist/windows/installer-translations/portugueseBR.nsi b/dist/windows/installer-translations/portugueseBR.nsi
index 01b2e83d4..4e91210e7 100644
--- a/dist/windows/installer-translations/portugueseBR.nsi
+++ b/dist/windows/installer-translations/portugueseBR.nsi
@@ -17,7 +17,7 @@ LangString inst_firewallinfo ${LANG_PORTUGUESEBR} "Adicionando regra no firewall
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_PORTUGUESEBR} "qBittorrent está rodando. Por favor feche a aplicação antes de instalar."
;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings."
-LangString inst_uninstall_question ${LANG_PORTUGUESEBR} "A previous installation was detected. It will be uninstalled without deleting user settings."
+LangString inst_uninstall_question ${LANG_PORTUGUESEBR} "Uma instalação anterior foi detectada. Ela será desistalada sem deletar as configurações de usuário."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_PORTUGUESEBR} "Desinstalando versão anterior."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
diff --git a/dist/windows/installer-translations/turkish.nsi b/dist/windows/installer-translations/turkish.nsi
index c4d0a0c33..db0e90517 100644
--- a/dist/windows/installer-translations/turkish.nsi
+++ b/dist/windows/installer-translations/turkish.nsi
@@ -1,51 +1,51 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
-LangString inst_qbt_req ${LANG_TURKISH} "qBittorrent (required)"
+LangString inst_qbt_req ${LANG_TURKISH} "qBittorrent (zorunlu)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
-LangString inst_dekstop ${LANG_TURKISH} "Create Desktop Shortcut"
+LangString inst_dekstop ${LANG_TURKISH} "Masaüstü Kısayolu oluştur"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
-LangString inst_startmenu ${LANG_TURKISH} "Create Start Menu Shortcut"
+LangString inst_startmenu ${LANG_TURKISH} "Başlangıç Menüsü Kısayolu oluştur"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
-LangString inst_torrent ${LANG_TURKISH} "Open .torrent files with qBittorrent"
+LangString inst_torrent ${LANG_TURKISH} ".torrent dosyalarını qBittorrent ile aç"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
-LangString inst_magnet ${LANG_TURKISH} "Open magnet links with qBittorrent"
+LangString inst_magnet ${LANG_TURKISH} "Magnet bağlantılarını qBittorrent ile aç"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
-LangString inst_firewall ${LANG_TURKISH} "Add Windows Firewall rule"
+LangString inst_firewall ${LANG_TURKISH} "Windows Güvenlik Duvarı kuralı ekle"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
-LangString inst_firewallinfo ${LANG_TURKISH} "Adding Windows Firewall rule"
+LangString inst_firewallinfo ${LANG_TURKISH} "Windows Güvenlik Duvarı kuralı ekleniyor"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
-LangString inst_warning ${LANG_TURKISH} "qBittorrent is running. Please close the application before installing."
+LangString inst_warning ${LANG_TURKISH} "qBittorrent çalışıyor. Lütfen yüklemeden önce uygulamayı kapatın."
;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings."
-LangString inst_uninstall_question ${LANG_TURKISH} "A previous installation was detected. It will be uninstalled without deleting user settings."
+LangString inst_uninstall_question ${LANG_TURKISH} "Önceki bir kurulum algılandı. Kullanıcı ayarları silinmeden kaldırılacaktır."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
-LangString inst_unist ${LANG_TURKISH} "Uninstalling previous version."
+LangString inst_unist ${LANG_TURKISH} "Önceki sürüm kaldırılıyor."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
-LangString launch_qbt ${LANG_TURKISH} "Launch qBittorrent."
+LangString launch_qbt ${LANG_TURKISH} "qBittorrent'i çalıştır."
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
-LangString remove_files ${LANG_TURKISH} "Remove files"
+LangString remove_files ${LANG_TURKISH} "Dosyaları kaldır"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
-LangString remove_shortcuts ${LANG_TURKISH} "Remove shortcuts"
+LangString remove_shortcuts ${LANG_TURKISH} "Kısayolları kaldır"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
-LangString remove_associations ${LANG_TURKISH} "Remove file associations"
+LangString remove_associations ${LANG_TURKISH} "Dosya ilişkilendirmelerini kaldır"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
-LangString remove_registry ${LANG_TURKISH} "Remove registry keys"
+LangString remove_registry ${LANG_TURKISH} "Kayıt defteri anahtarlarını kaldır"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
-LangString remove_conf ${LANG_TURKISH} "Remove configuration files"
+LangString remove_conf ${LANG_TURKISH} "Yapılandırma dosyalarını kaldır"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
-LangString remove_firewall ${LANG_TURKISH} "Remove Windows Firewall rule"
+LangString remove_firewall ${LANG_TURKISH} "Windows Güvenlik Duvarı kuralını kaldır"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
-LangString remove_firewallinfo ${LANG_TURKISH} "Removing Windows Firewall rule"
+LangString remove_firewallinfo ${LANG_TURKISH} "Windows Güvenlik Duvarı kuralı kaldırılıyor"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
-LangString remove_cache ${LANG_TURKISH} "Remove torrents and cached data"
+LangString remove_cache ${LANG_TURKISH} "Torrentleri ve önbelleklenen verileri kaldır"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
-LangString uninst_warning ${LANG_TURKISH} "qBittorrent is running. Please close the application before uninstalling."
+LangString uninst_warning ${LANG_TURKISH} "qBittorrent çalışıyor. Lütfen kaldırmadan önce uygulamayı kapatın."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
-LangString uninst_tor_warn ${LANG_TURKISH} "Not removing .torrent association. It is associated with:"
+LangString uninst_tor_warn ${LANG_TURKISH} ".torrent ilişkilendirmesi kaldırılmıyor. Şununla ilişkilendirildi:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
-LangString uninst_mag_warn ${LANG_TURKISH} "Not removing magnet association. It is associated with:"
+LangString uninst_mag_warn ${LANG_TURKISH} "Magnet ilişkilendirmesi kaldırılmıyor. Şununla ilişkilendirildi:"
diff --git a/dist/windows/installer.nsi b/dist/windows/installer.nsi
index 1c7822456..eab18b639 100644
--- a/dist/windows/installer.nsi
+++ b/dist/windows/installer.nsi
@@ -30,12 +30,13 @@ Section $(inst_qbt_req) ;"qBittorrent (required)"
File "qbittorrent.pdb"
File "qt.conf"
File /oname=translations\qt_ar.qm "translations\qt_ar.qm"
+ File /oname=translations\qt_bg.qm "translations\qt_bg.qm"
File /oname=translations\qt_ca.qm "translations\qt_ca.qm"
File /oname=translations\qt_cs.qm "translations\qt_cs.qm"
File /oname=translations\qt_da.qm "translations\qt_da.qm"
File /oname=translations\qt_de.qm "translations\qt_de.qm"
- File /oname=translations\qt_en.qm "translations\qt_en.qm"
File /oname=translations\qt_es.qm "translations\qt_es.qm"
+ File /oname=translations\qt_eu.qm "translations\qt_eu.qm"
File /oname=translations\qt_fa.qm "translations\qt_fa.qm"
File /oname=translations\qt_fi.qm "translations\qt_fi.qm"
File /oname=translations\qt_fr.qm "translations\qt_fr.qm"
@@ -46,15 +47,33 @@ Section $(inst_qbt_req) ;"qBittorrent (required)"
File /oname=translations\qt_ja.qm "translations\qt_ja.qm"
File /oname=translations\qt_ko.qm "translations\qt_ko.qm"
File /oname=translations\qt_lt.qm "translations\qt_lt.qm"
+ File /oname=translations\qt_nl.qm "translations\qt_nl.qm"
File /oname=translations\qt_pl.qm "translations\qt_pl.qm"
File /oname=translations\qt_pt.qm "translations\qt_pt.qm"
+ File /oname=translations\qt_pt_BR.qm "translations\qt_pt_BR.qm"
File /oname=translations\qt_ru.qm "translations\qt_ru.qm"
File /oname=translations\qt_sk.qm "translations\qt_sk.qm"
File /oname=translations\qt_sl.qm "translations\qt_sl.qm"
File /oname=translations\qt_sv.qm "translations\qt_sv.qm"
+ File /oname=translations\qt_tr.qm "translations\qt_tr.qm"
File /oname=translations\qt_uk.qm "translations\qt_uk.qm"
File /oname=translations\qt_zh_CN.qm "translations\qt_zh_CN.qm"
File /oname=translations\qt_zh_TW.qm "translations\qt_zh_TW.qm"
+ File /oname=translations\qtbase_ca.qm "translations\qtbase_ca.qm"
+ File /oname=translations\qtbase_cs.qm "translations\qtbase_cs.qm"
+ File /oname=translations\qtbase_de.qm "translations\qtbase_de.qm"
+ File /oname=translations\qtbase_fi.qm "translations\qtbase_fi.qm"
+ File /oname=translations\qtbase_fr.qm "translations\qtbase_fr.qm"
+ File /oname=translations\qtbase_he.qm "translations\qtbase_he.qm"
+ File /oname=translations\qtbase_hu.qm "translations\qtbase_hu.qm"
+ File /oname=translations\qtbase_it.qm "translations\qtbase_it.qm"
+ File /oname=translations\qtbase_ja.qm "translations\qtbase_ja.qm"
+ File /oname=translations\qtbase_ko.qm "translations\qtbase_ko.qm"
+ File /oname=translations\qtbase_lv.qm "translations\qtbase_lv.qm"
+ File /oname=translations\qtbase_pl.qm "translations\qtbase_pl.qm"
+ File /oname=translations\qtbase_ru.qm "translations\qtbase_ru.qm"
+ File /oname=translations\qtbase_sk.qm "translations\qtbase_sk.qm"
+ File /oname=translations\qtbase_uk.qm "translations\qtbase_uk.qm"
; Write the installation path into the registry
WriteRegStr HKLM "Software\qBittorrent" "InstallLocation" "$INSTDIR"
diff --git a/dist/windows/nsis plugins/UAC Unicode.zip b/dist/windows/nsis plugins/UAC Unicode.zip
deleted file mode 100644
index f3c6d6628..000000000
Binary files a/dist/windows/nsis plugins/UAC Unicode.zip and /dev/null differ
diff --git a/dist/windows/nsis plugins/UAC.zip b/dist/windows/nsis plugins/UAC.zip
index 4e205d410..66489aacd 100644
Binary files a/dist/windows/nsis plugins/UAC.zip and b/dist/windows/nsis plugins/UAC.zip differ
diff --git a/dist/windows/qt.conf b/dist/windows/qt.conf
index 372600dc5..5041e27db 100644
--- a/dist/windows/qt.conf
+++ b/dist/windows/qt.conf
@@ -2,4 +2,4 @@
Translations = translations
[Platforms]
-WindowsArguments = dpiawareness=1
+WindowsArguments = dpiawareness=0
diff --git a/dist/windows/uninstaller.nsi b/dist/windows/uninstaller.nsi
index 1e07f7131..97dc7ce5d 100644
--- a/dist/windows/uninstaller.nsi
+++ b/dist/windows/uninstaller.nsi
@@ -6,12 +6,13 @@
Delete "$INSTDIR\qbittorrent.pdb"
Delete "$INSTDIR\qt.conf"
Delete "$INSTDIR\translations\qt_ar.qm"
+ Delete "$INSTDIR\translations\qt_bg.qm"
Delete "$INSTDIR\translations\qt_ca.qm"
Delete "$INSTDIR\translations\qt_cs.qm"
Delete "$INSTDIR\translations\qt_da.qm"
Delete "$INSTDIR\translations\qt_de.qm"
- Delete "$INSTDIR\translations\qt_en.qm"
Delete "$INSTDIR\translations\qt_es.qm"
+ Delete "$INSTDIR\translations\qt_eu.qm"
Delete "$INSTDIR\translations\qt_fa.qm"
Delete "$INSTDIR\translations\qt_fi.qm"
Delete "$INSTDIR\translations\qt_fr.qm"
@@ -22,15 +23,33 @@
Delete "$INSTDIR\translations\qt_ja.qm"
Delete "$INSTDIR\translations\qt_ko.qm"
Delete "$INSTDIR\translations\qt_lt.qm"
+ Delete "$INSTDIR\translations\qt_nl.qm"
Delete "$INSTDIR\translations\qt_pl.qm"
+ Delete "$INSTDIR\translations\qt_pt_BR.qm"
Delete "$INSTDIR\translations\qt_pt.qm"
Delete "$INSTDIR\translations\qt_ru.qm"
Delete "$INSTDIR\translations\qt_sk.qm"
Delete "$INSTDIR\translations\qt_sl.qm"
Delete "$INSTDIR\translations\qt_sv.qm"
+ Delete "$INSTDIR\translations\qt_tr.qm"
Delete "$INSTDIR\translations\qt_uk.qm"
Delete "$INSTDIR\translations\qt_zh_CN.qm"
Delete "$INSTDIR\translations\qt_zh_TW.qm"
+ Delete "$INSTDIR\translations\qtbase_ca.qm"
+ Delete "$INSTDIR\translations\qtbase_cs.qm"
+ Delete "$INSTDIR\translations\qtbase_de.qm"
+ Delete "$INSTDIR\translations\qtbase_fi.qm"
+ Delete "$INSTDIR\translations\qtbase_fr.qm"
+ Delete "$INSTDIR\translations\qtbase_he.qm"
+ Delete "$INSTDIR\translations\qtbase_hu.qm"
+ Delete "$INSTDIR\translations\qtbase_it.qm"
+ Delete "$INSTDIR\translations\qtbase_ja.qm"
+ Delete "$INSTDIR\translations\qtbase_ko.qm"
+ Delete "$INSTDIR\translations\qtbase_lv.qm"
+ Delete "$INSTDIR\translations\qtbase_pl.qm"
+ Delete "$INSTDIR\translations\qtbase_ru.qm"
+ Delete "$INSTDIR\translations\qtbase_sk.qm"
+ Delete "$INSTDIR\translations\qtbase_uk.qm"
Delete "$INSTDIR\uninst.exe"
; Remove directories used
diff --git a/m4/ax_boost_system.m4 b/m4/ax_boost_system.m4
index c4c45559d..43570a513 100644
--- a/m4/ax_boost_system.m4
+++ b/m4/ax_boost_system.m4
@@ -31,7 +31,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 17
+#serial 18
AC_DEFUN([AX_BOOST_SYSTEM],
[
@@ -68,9 +68,10 @@ AC_DEFUN([AX_BOOST_SYSTEM],
ax_cv_boost_system,
[AC_LANG_PUSH([C++])
CXXFLAGS_SAVE=$CXXFLAGS
+ CXXFLAGS=
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]],
- [[boost::system::system_category]])],
+ [[boost::system::error_category *a = 0;]])],
ax_cv_boost_system=yes, ax_cv_boost_system=no)
CXXFLAGS=$CXXFLAGS_SAVE
AC_LANG_POP([C++])
diff --git a/macxconf.pri b/macxconf.pri
index 554fdf38a..36a65930b 100644
--- a/macxconf.pri
+++ b/macxconf.pri
@@ -34,6 +34,8 @@ qt_translations.files = \
$$QT_LANG_PATH/qt_da.qm \
$$QT_LANG_PATH/qt_de.qm \
$$QT_LANG_PATH/qt_es.qm \
+ $$QT_LANG_PATH/qt_eu.qm \
+ $$QT_LANG_PATH/qt_fa.qm \
$$QT_LANG_PATH/qt_fi.qm \
$$QT_LANG_PATH/qt_fr.qm \
$$QT_LANG_PATH/qt_gl.qm \
@@ -49,11 +51,27 @@ qt_translations.files = \
$$QT_LANG_PATH/qt_pt_BR.qm \
$$QT_LANG_PATH/qt_ru.qm \
$$QT_LANG_PATH/qt_sk.qm \
+ $$QT_LANG_PATH/qt_sl.qm \
$$QT_LANG_PATH/qt_sv.qm \
$$QT_LANG_PATH/qt_tr.qm \
$$QT_LANG_PATH/qt_uk.qm \
$$QT_LANG_PATH/qt_zh_CN.qm \
- $$QT_LANG_PATH/qt_zh_TW.qm
+ $$QT_LANG_PATH/qt_zh_TW.qm \
+ $$QT_LANG_PATH/qtbase_ca.qm \
+ $$QT_LANG_PATH/qtbase_cs.qm \
+ $$QT_LANG_PATH/qtbase_de.qm \
+ $$QT_LANG_PATH/qtbase_fi.qm \
+ $$QT_LANG_PATH/qtbase_fr.qm \
+ $$QT_LANG_PATH/qtbase_he.qm \
+ $$QT_LANG_PATH/qtbase_hu.qm \
+ $$QT_LANG_PATH/qtbase_it.qm \
+ $$QT_LANG_PATH/qtbase_ja.qm \
+ $$QT_LANG_PATH/qtbase_ko.qm \
+ $$QT_LANG_PATH/qtbase_lv.qm \
+ $$QT_LANG_PATH/qtbase_pl.qm \
+ $$QT_LANG_PATH/qtbase_ru.qm \
+ $$QT_LANG_PATH/qtbase_sk.qm \
+ $$QT_LANG_PATH/qtbase_uk.qm
QMAKE_BUNDLE_DATA += qt_translations
ICON = $$DIST_PATH/qbittorrent_mac.icns
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 15ebe5068..bf3922325 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,15 +3,9 @@ set(CMAKE_CXX_STANDARD "11")
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
include(MacroLinkQtComponents)
+include(QbtTargetSources)
find_package(LibtorrentRasterbar REQUIRED)
-include_directories(SYSTEM ${LibtorrentRasterbar_INCLUDE_DIRS})
-add_compile_options(${LibtorrentRasterbar_DEFINITIONS})
-
-# Boost
-set(Boost_USE_MULTITHREADED ON)
-find_package(Boost 1.35 REQUIRED COMPONENTS system)
-include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
# Qt
if (QT5)
@@ -88,16 +82,22 @@ set(QBT_USES_QT5 ${QT5})
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+if (GUI)
+ set(QBT_TARGET_NAME qbittorrent)
+else (GUI)
+ set(QBT_TARGET_NAME qbittorrent-nox)
+endif (GUI)
-add_subdirectory(base)
if (SYSTEM_QTSINGLEAPPLICATION)
find_package(QtSingleApplication REQUIRED)
- include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIR})
else (SYSTEM_QTSINGLEAPPLICATION)
- include_directories(app/qtsingleapplication)
+ add_subdirectory(app/qtsingleapplication)
endif (SYSTEM_QTSINGLEAPPLICATION)
+add_subdirectory(app)
+add_subdirectory(base)
+
if (GUI)
add_subdirectory(gui)
endif (GUI)
@@ -106,4 +106,3 @@ if (WEBUI)
add_subdirectory(webui)
endif (WEBUI)
-add_subdirectory(app)
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 8eb474172..dffe9ffa1 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -1,11 +1,14 @@
+project(qbt_executable)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(QBT_APP_HEADERS
application.h
+filelogger.h
)
set(QBT_APP_SOURCES
application.cpp
+filelogger.cpp
main.cpp
)
@@ -84,39 +87,93 @@ list(APPEND QBT_APP_HEADERS upgrade.h)
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
if (GUI)
- set(QBT_TARGET_NAME qbittorrent)
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
include_directories(../gui
${CMAKE_CURRENT_BINARY_DIR}/../gui
)
-else (GUI)
- set(QBT_TARGET_NAME qbittorrent-nox)
endif (GUI)
if (WEBUI)
list(APPEND QBT_TARGET_LIBRARIES qbt_webui)
endif (WEBUI)
+# we have to include resources into the bundle
+if (APPLE)
+ set(OSX_RES_SRC_DIR "${qBittorrent_SOURCE_DIR}/dist/mac")
+ list(APPEND QBT_APP_RESOURCE_SOURCE
+ "${OSX_RES_SRC_DIR}/qt.conf"
+ "${OSX_RES_SRC_DIR}/qBitTorrentDocument.icns"
+ "${OSX_RES_SRC_DIR}/qbittorrent_mac.icns")
+ set_source_files_properties(
+ "${OSX_RES_SRC_DIR}/qt.conf"
+ "${OSX_RES_SRC_DIR}/qBitTorrentDocument.icns"
+ "${OSX_RES_SRC_DIR}/qbittorrent_mac.icns"
+ PROPERTIES
+ MACOSX_PACKAGE_LOCATION Resources)
+ set(QT_TR_DIR "${qBittorrent_SOURCE_DIR}/dist/qt-translations")
+ set(QT_TRANSLATIONS
+ ${QT_TR_DIR}/qt_ar.qm
+ ${QT_TR_DIR}/qt_bg.qm
+ ${QT_TR_DIR}/qt_ca.qm
+ ${QT_TR_DIR}/qt_cs.qm
+ ${QT_TR_DIR}/qt_da.qm
+ ${QT_TR_DIR}/qt_de.qm
+ ${QT_TR_DIR}/qt_es.qm
+ ${QT_TR_DIR}/qt_eu.qm
+ ${QT_TR_DIR}/qt_fi.qm
+ ${QT_TR_DIR}/qt_fr.qm
+ ${QT_TR_DIR}/qt_gl.qm
+ ${QT_TR_DIR}/qt_he.qm
+ ${QT_TR_DIR}/qt_hu.qm
+ ${QT_TR_DIR}/qt_it.qm
+ ${QT_TR_DIR}/qt_ja.qm
+ ${QT_TR_DIR}/qt_ko.qm
+ ${QT_TR_DIR}/qt_lt.qm
+ ${QT_TR_DIR}/qt_nl.qm
+ ${QT_TR_DIR}/qt_pl.qm
+ ${QT_TR_DIR}/qt_pt.qm
+ ${QT_TR_DIR}/qt_pt_BR.qm
+ ${QT_TR_DIR}/qt_ru.qm
+ ${QT_TR_DIR}/qt_sk.qm
+ ${QT_TR_DIR}/qt_sv.qm
+ ${QT_TR_DIR}/qt_tr.qm
+ ${QT_TR_DIR}/qt_uk.qm
+ ${QT_TR_DIR}/qt_zh_CN.qm
+ ${QT_TR_DIR}/qt_zh_TW.qm
+ )
+ list(APPEND QBT_APP_RESOURCE_SOURCE ${QT_TRANSLATIONS})
+ set_source_files_properties(${QT_TRANSLATIONS}
+ PROPERTIES MACOSX_PACKAGE_LOCATION translations)
+endif (APPLE)
+
add_executable(${QBT_TARGET_NAME} ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
-set_target_properties(${QBT_TARGET_NAME} PROPERTIES AUTOUIC True)
+set_target_properties(${QBT_TARGET_NAME}
+ PROPERTIES
+ AUTOUIC True
+ AUTORCC True
+ MACOSX_BUNDLE True
+)
-if (GUI)
- if (WIN32)
- set_target_properties(${QBT_TARGET_NAME} PROPERTIES WIN32_EXECUTABLE True)
- endif (WIN32)
- if (APPLE)
- set_target_properties(${QBT_TARGET_NAME} PROPERTIES MACOSX_BUNDLE True)
- endif (APPLE)
-endif (GUI)
+if (GUI AND WIN32)
+ set_target_properties(${QBT_TARGET_NAME} PROPERTIES WIN32_EXECUTABLE True)
+endif (GUI AND WIN32)
-target_link_libraries(${QBT_TARGET_NAME} ${QBT_TARGET_LIBRARIES})
+target_link_libraries(${QBT_TARGET_NAME} ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
-if (SYSTEM_QTSINGLEAPPLICATION)
- target_link_libraries(${QBT_TARGET_NAME} ${QTSINGLEAPPLICATION_LIBRARIES})
-else (SYSTEM_QTSINGLEAPPLICATION)
- add_subdirectory(qtsingleapplication)
- target_link_libraries(${QBT_TARGET_NAME} qtsingleapplication)
-endif (SYSTEM_QTSINGLEAPPLICATION)
+if (APPLE)
+ set(qbt_BUNDLE_NAME "${CMAKE_PROJECT_NAME}")
+ set_target_properties(${QBT_TARGET_NAME} PROPERTIES
+ MACOSX_BUNDLE_BUNDLE_NAME "${qbt_BUNDLE_NAME}"
+ MACOSX_BUNDLE_INFO_PLIST ${qBittorrent_SOURCE_DIR}/dist/mac/Info.plist
+ )
+endif (APPLE)
# installation
-install(TARGETS ${QBT_TARGET_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime)
+install(TARGETS ${QBT_TARGET_NAME}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ BUNDLE DESTINATION .
+ COMPONENT runtime)
+
+if (APPLE)
+ install(SCRIPT ${OSX_RES_SRC_DIR}/bundle.cmake)
+endif (APPLE)
diff --git a/src/app/application.cpp b/src/app/application.cpp
index 9c0d10bc5..85bf2de25 100644
--- a/src/app/application.cpp
+++ b/src/app/application.cpp
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
#ifndef DISABLE_GUI
#include "gui/guiiconprovider.h"
@@ -48,7 +49,7 @@
#endif // Q_OS_MAC
#include "mainwindow.h"
#include "addnewtorrentdialog.h"
-#include "shutdownconfirm.h"
+#include "shutdownconfirmdlg.h"
#else // DISABLE_GUI
#include
#endif // DISABLE_GUI
@@ -69,6 +70,7 @@
#include "base/net/smtp.h"
#include "base/net/downloadmanager.h"
#include "base/net/geoipmanager.h"
+#include "base/net/proxyconfigurationmanager.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
@@ -96,9 +98,7 @@ namespace
Application::Application(const QString &id, int &argc, char **argv)
: BaseApplication(id, argc, argv)
, m_running(false)
-#ifndef DISABLE_GUI
- , m_shutdownAct(ShutdownAction::None)
-#endif
+ , m_shutdownAct(ShutdownDialogAction::Exit)
{
Logger::initInstance();
SettingsStorage::initInstance();
@@ -132,6 +132,13 @@ Application::Application(const QString &id, int &argc, char **argv)
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
}
+#ifndef DISABLE_GUI
+QPointer Application::mainWindow()
+{
+ return m_window;
+}
+#endif
+
bool Application::isFileLoggerEnabled() const
{
return settings()->loadValue(KEY_FILELOGGER_ENABLED, true).toBool();
@@ -236,6 +243,50 @@ void Application::processMessage(const QString &message)
m_paramsQueue.append(params);
}
+void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) const
+{
+ QString program = Preferences::instance()->getAutoRunProgram();
+ program.replace("%N", torrent->name());
+ program.replace("%L", torrent->category());
+ program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
+ program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
+ program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
+ program.replace("%C", QString::number(torrent->filesCount()));
+ program.replace("%Z", QString::number(torrent->totalSize()));
+ program.replace("%T", torrent->currentTracker());
+ program.replace("%I", torrent->hash());
+
+ Logger *logger = Logger::instance();
+ logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name()).arg(program));
+
+#if defined(Q_OS_UNIX)
+ QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
+#elif defined(Q_OS_WIN) // test cmd: `echo "%F" > "c:\ab ba.txt"`
+ program.prepend(QLatin1String("\"")).append(QLatin1String("\""));
+ program.prepend(Utils::Misc::windowsSystemPath() + QLatin1String("\\cmd.exe /C "));
+ const int cmdMaxLength = 32768; // max length (incl. terminate char) for `lpCommandLine` in `CreateProcessW()`
+ if ((program.size() + 1) > cmdMaxLength) {
+ logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(cmdMaxLength), Log::CRITICAL);
+ return;
+ }
+
+ STARTUPINFOW si = {0};
+ si.cb = sizeof(si);
+ PROCESS_INFORMATION pi = {0};
+
+ WCHAR *arg = new WCHAR[program.size() + 1];
+ program.toWCharArray(arg);
+ arg[program.size()] = L'\0';
+ if (CreateProcessW(NULL, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ delete[] arg;
+#else
+ QProcess::startDetached(program);
+#endif
+}
+
void Application::sendNotificationEmail(BitTorrent::TorrentHandle *const torrent)
{
// Prepare mail content
@@ -260,75 +311,58 @@ void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent)
Preferences *const pref = Preferences::instance();
// AutoRun program
- if (pref->isAutoRunEnabled()) {
- QString program = pref->getAutoRunProgram();
-
- program.replace("%N", torrent->name());
- program.replace("%L", torrent->category());
- program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
- program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
- program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
- program.replace("%C", QString::number(torrent->filesCount()));
- program.replace("%Z", QString::number(torrent->totalSize()));
- program.replace("%T", torrent->currentTracker());
- program.replace("%I", torrent->hash());
-
- QProcess::startDetached(program);
- }
+ if (pref->isAutoRunEnabled())
+ runExternalProgram(torrent);
// Mail notification
- if (pref->isMailNotificationEnabled())
+ if (pref->isMailNotificationEnabled()) {
+ Logger::instance()->addMessage(tr("Torrent: %1, sending mail notification").arg(torrent->name()));
sendNotificationEmail(torrent);
+ }
}
void Application::allTorrentsFinished()
{
-#ifndef DISABLE_GUI
Preferences *const pref = Preferences::instance();
+ bool isExit = pref->shutdownqBTWhenDownloadsComplete();
+ bool isShutdown = pref->shutdownWhenDownloadsComplete();
+ bool isSuspend = pref->suspendWhenDownloadsComplete();
+ bool isHibernate = pref->hibernateWhenDownloadsComplete();
- bool will_shutdown = (pref->shutdownWhenDownloadsComplete()
- || pref->shutdownqBTWhenDownloadsComplete()
- || pref->suspendWhenDownloadsComplete()
- || pref->hibernateWhenDownloadsComplete());
+ bool haveAction = isExit || isShutdown || isSuspend || isHibernate;
+ if (!haveAction) return;
- // Auto-Shutdown
- if (will_shutdown) {
- bool suspend = pref->suspendWhenDownloadsComplete();
- bool hibernate = pref->hibernateWhenDownloadsComplete();
- bool shutdown = pref->shutdownWhenDownloadsComplete();
+ ShutdownDialogAction action = ShutdownDialogAction::Exit;
+ if (isSuspend)
+ action = ShutdownDialogAction::Suspend;
+ else if (isHibernate)
+ action = ShutdownDialogAction::Hibernate;
+ else if (isShutdown)
+ action = ShutdownDialogAction::Shutdown;
- // Confirm shutdown
- ShutdownAction action = ShutdownAction::None;
- if (suspend)
- action = ShutdownAction::Suspend;
- else if (hibernate)
- action = ShutdownAction::Hibernate;
- else if (shutdown)
- action = ShutdownAction::Shutdown;
-
- if ((action == ShutdownAction::None) && (!pref->dontConfirmAutoExit())) {
- if (!ShutdownConfirmDlg::askForConfirmation(action))
- return;
- }
- else { //exit and shutdown
- if (!ShutdownConfirmDlg::askForConfirmation(action))
- return;
- }
-
- // Actually shut down
- if (suspend || hibernate || shutdown) {
- qDebug("Preparing for auto-shutdown because all downloads are complete!");
- // Disabling it for next time
- pref->setShutdownWhenDownloadsComplete(false);
- pref->setSuspendWhenDownloadsComplete(false);
- pref->setHibernateWhenDownloadsComplete(false);
- // Make sure preferences are synced before exiting
- m_shutdownAct = action;
- }
- qDebug("Exiting the application");
- exit();
+#ifndef DISABLE_GUI
+ // ask confirm
+ if ((action == ShutdownDialogAction::Exit) && (pref->dontConfirmAutoExit())) {
+ // do nothing & skip confirm
+ }
+ else {
+ if (!ShutdownConfirmDlg::askForConfirmation(action)) return;
}
#endif // DISABLE_GUI
+
+ // Actually shut down
+ if (action != ShutdownDialogAction::Exit) {
+ qDebug("Preparing for auto-shutdown because all downloads are complete!");
+ // Disabling it for next time
+ pref->setShutdownWhenDownloadsComplete(false);
+ pref->setSuspendWhenDownloadsComplete(false);
+ pref->setHibernateWhenDownloadsComplete(false);
+ // Make sure preferences are synced before exiting
+ m_shutdownAct = action;
+ }
+
+ qDebug("Exiting the application");
+ exit();
}
bool Application::sendParams(const QStringList ¶ms)
@@ -362,6 +396,7 @@ void Application::processParams(const QStringList ¶ms)
int Application::exec(const QStringList ¶ms)
{
+ Net::ProxyConfigurationManager::initInstance();
Net::DownloadManager::initInstance();
#ifdef DISABLE_GUI
IconProvider::initInstance();
@@ -371,7 +406,7 @@ int Application::exec(const QStringList ¶ms)
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
- connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()));
+ connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()), Qt::QueuedConnection);
#ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::initInstance();
@@ -548,11 +583,9 @@ void Application::cleanup()
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
// cleanup() can be called multiple times during shutdown. We only need it once.
- static bool alreadyDone = false;
-
- if (alreadyDone)
+ static QAtomicInt alreadyDone;
+ if (!alreadyDone.testAndSetAcquire(0, 1))
return;
- alreadyDone = true;
#endif // Q_OS_WIN
// Hide the window and not leave it on screen as
@@ -590,11 +623,13 @@ void Application::cleanup()
Net::GeoIPManager::freeInstance();
#endif
Net::DownloadManager::freeInstance();
+ Net::ProxyConfigurationManager::freeInstance();
Preferences::freeInstance();
SettingsStorage::freeInstance();
delete m_fileLogger;
Logger::freeInstance();
IconProvider::freeInstance();
+
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
@@ -604,9 +639,10 @@ void Application::cleanup()
shutdownBRDestroy((HWND)m_window->effectiveWinId());
#endif // Q_OS_WIN
delete m_window;
- if (m_shutdownAct != ShutdownAction::None) {
+#endif // DISABLE_GUI
+
+ if (m_shutdownAct != ShutdownDialogAction::Exit) {
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
Utils::Misc::shutdownComputer(m_shutdownAct);
}
-#endif
}
diff --git a/src/app/application.h b/src/app/application.h
index f8f1de796..48a3c8b56 100644
--- a/src/app/application.h
+++ b/src/app/application.h
@@ -76,6 +76,10 @@ public:
int exec(const QStringList ¶ms);
bool sendParams(const QStringList ¶ms);
+#ifndef DISABLE_GUI
+ QPointer mainWindow();
+#endif
+
// FileLogger properties
bool isFileLoggerEnabled() const;
void setFileLoggerEnabled(bool value);
@@ -111,10 +115,10 @@ private slots:
private:
bool m_running;
+ ShutdownDialogAction m_shutdownAct;
#ifndef DISABLE_GUI
QPointer m_window;
- ShutdownAction m_shutdownAct;
#endif
#ifndef DISABLE_WEBUI
@@ -130,6 +134,7 @@ private:
void initializeTranslation();
void processParams(const QStringList ¶ms);
+ void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
};
diff --git a/src/app/main.cpp b/src/app/main.cpp
index f4848f408..0ace32ef3 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -135,6 +135,12 @@ int main(int argc, char *argv[])
// We must save it here because QApplication constructor may change it
bool isOneArg = (argc == 2);
+#ifdef Q_OS_MAC
+ // On macOS 10.12 Sierra, Apple changed the behaviour of CFPreferencesSetValue() https://bugreports.qt.io/browse/QTBUG-56344
+ // Due to this, we have to move from native plist to IniFormat
+ macMigratePlists();
+#endif
+
// Create Application
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
QScopedPointer app(new Application(appId, argc, argv));
@@ -230,6 +236,17 @@ int main(int argc, char *argv[])
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
#endif
+#if defined(Q_OS_MAC)
+{
+ // Since Apple made difficult for users to set PATH, we set here for convenience.
+ // Users are supposed to install Homebrew Python for search function.
+ // For more info see issue #5571.
+ QByteArray path = "/usr/local/bin:";
+ path += qgetenv("PATH");
+ qputenv("PATH", path.constData());
+}
+#endif
+
#ifndef DISABLE_GUI
if (!upgrade()) return EXIT_FAILURE;
#else
diff --git a/src/app/qtsingleapplication/CMakeLists.txt b/src/app/qtsingleapplication/CMakeLists.txt
index 982436b12..72001d671 100644
--- a/src/app/qtsingleapplication/CMakeLists.txt
+++ b/src/app/qtsingleapplication/CMakeLists.txt
@@ -1,3 +1,5 @@
+project(qtsingleapplication)
+
set(QBT_QTSINGLEAPPLICATION_HEADERS
qtlocalpeer.h
)
@@ -15,6 +17,7 @@ else (GUI)
endif (GUI)
add_library(qtsingleapplication ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
+target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
if (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt4::QtNetwork)
@@ -30,3 +33,4 @@ if (GUI)
endif(QT4_FOUND)
endif (GUI)
+add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)
diff --git a/src/app/stacktrace_win.h b/src/app/stacktrace_win.h
index 55e9494c1..ca86ebd9d 100644
--- a/src/app/stacktrace_win.h
+++ b/src/app/stacktrace_win.h
@@ -25,6 +25,7 @@
#include
#include
+#include
#include
#ifdef __MINGW32__
#include
@@ -41,6 +42,9 @@ namespace straceWin
#ifdef __MINGW32__
void demangle(QString& str);
#endif
+
+ QString getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr);
+ bool makeRelativePath(const QString& dir, QString& file);
}
#ifdef __MINGW32__
@@ -108,6 +112,65 @@ BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVO
}
+/**
+* Cuts off leading 'dir' path from 'file' path, otherwise leaves it unchanged
+* returns true if 'dir' is an ancestor of 'file', otherwise - false
+*/
+bool straceWin::makeRelativePath(const QString& dir, QString& file)
+{
+ QString d = QDir::toNativeSeparators(QDir(dir).absolutePath());
+ QString f = QDir::toNativeSeparators(QFileInfo(file).absoluteFilePath());
+
+ // append separator at the end of dir
+ QChar separator = QDir::separator();
+ if (!d.isEmpty() && (d[d.length() - 1] != separator))
+ d += separator;
+
+ if (f.startsWith(d, Qt::CaseInsensitive)) {
+ f.remove(0, d.length());
+ file.swap(f);
+
+ return true;
+ }
+
+ return false;
+}
+
+QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
+{
+ IMAGEHLP_LINE64 line = {0};
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ DWORD dwDisplacement = 0;
+
+ if (SymGetLineFromAddr64(hProcess, addr, &dwDisplacement, &line)) {
+ QString path(line.FileName);
+
+#if defined STACKTRACE_WIN_PROJECT_PATH || defined STACKTRACE_WIN_MAKEFILE_PATH
+
+#define STACKTRACE_WIN_QUOTE(x) #x
+#define STACKTRACE_WIN_STRING(x) STACKTRACE_WIN_QUOTE(x)
+
+ //prune leading project directory path or build target directory path
+
+ bool success = false;
+#ifdef STACKTRACE_WIN_PROJECT_PATH
+ QString projectPath(STACKTRACE_WIN_STRING(STACKTRACE_WIN_PROJECT_PATH));
+ success = makeRelativePath(projectPath, path);
+#endif
+
+#ifdef STACKTRACE_WIN_MAKEFILE_PATH
+ if (!success) {
+ QString targetPath(STACKTRACE_WIN_STRING(STACKTRACE_WIN_MAKEFILE_PATH));
+ makeRelativePath(targetPath, path);
+ }
+#endif
+#endif
+ return QString("%1 : %2").arg(path).arg(line.LineNumber);
+ }
+
+ return QString();
+}
+
#if defined( _M_IX86 ) && defined(Q_CC_MSVC)
// Disable global optimization and ignore /GS waning caused by
@@ -221,11 +284,16 @@ const QString straceWin::getBacktrace()
fileName = fileName.mid(slashPos + 1);
}
QString funcName;
+ QString sourceFile;
if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
funcName = QString(pSymbol->Name);
#ifdef __MINGW32__
demangle(funcName);
#endif
+
+ // now ihsf.InstructionOffset points to the instruction that follows CALL instuction
+ // decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
+ sourceFile = getSourcePathAndLineNumber(hProcess, ihsf.InstructionOffset - 1);
}
else {
funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
@@ -248,6 +316,9 @@ const QString straceWin::getBacktrace()
.arg(funcName)
#ifndef __MINGW32__
.arg(params.join(", "));
+
+ if (!sourceFile.isEmpty())
+ debugLine += QString("[ %1 ]").arg(sourceFile);
#else
;
#endif
@@ -262,6 +333,8 @@ const QString straceWin::getBacktrace()
//logStream << "\n\nList of linked Modules:\n";
//EnumModulesContext modulesContext(hProcess, logStream);
//SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
+ SymCleanup(hProcess);
+
logStream << "```";
return log;
}
diff --git a/src/app/upgrade.h b/src/app/upgrade.h
index 42928c2c5..7b76ec241 100644
--- a/src/app/upgrade.h
+++ b/src/app/upgrade.h
@@ -29,24 +29,31 @@
#ifndef UPGRADE_H
#define UPGRADE_H
-#include
-#include
+#include
+#if LIBTORRENT_VERSION_NUM >= 10100
+#include
+#endif
#include
+#include
+#if LIBTORRENT_VERSION_NUM < 10100
+#include
+#endif
+
-#include
#include
#include
-#include
#ifndef DISABLE_GUI
#include
#endif
+#include
+#include
#include "base/logger.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
-#include "base/qinisettings.h"
#include "base/preferences.h"
+#include "base/qinisettings.h"
bool userAcceptsUpgrade()
{
@@ -86,10 +93,16 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
QByteArray data = file1.readAll();
file1.close();
- libtorrent::lazy_entry fastOld;
libtorrent::error_code ec;
- libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
- if (ec || (fastOld.type() != libtorrent::lazy_entry::dict_t)) return false;
+#if LIBTORRENT_VERSION_NUM < 10100
+ libtorrent::lazy_entry fastOld;
+ libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
+ if (ec || (fastOld.type() != libtorrent::lazy_entry::dict_t)) return false;
+#else
+ libtorrent::bdecode_node fastOld;
+ libtorrent::bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
+ if (ec || (fastOld.type() != libtorrent::bdecode_node::dict_t)) return false;
+#endif
libtorrent::entry fastNew;
fastNew = fastOld;
@@ -143,7 +156,12 @@ bool upgrade(bool ask = true)
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
// ****************************************************************************************
+#ifdef Q_OS_MAC
+ // native .plist
+ QSettings *oldResumeSettings = new QSettings("qBittorrent", "qBittorrent-resume");
+#else
QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume");
+#endif
QString oldResumeFilename = oldResumeSettings->fileName();
QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash();
delete oldResumeSettings;
@@ -210,4 +228,34 @@ bool upgrade(bool ask = true)
return true;
}
+
+#ifdef Q_OS_MAC
+void migratePlistToIni(const QString &application)
+{
+ QIniSettings iniFile("qBittorrent", application);
+ if (!iniFile.allKeys().isEmpty()) return; // We copy the contents of plist, only if inifile does not exist(is empty).
+
+ QSettings *plistFile = new QSettings("qBittorrent", application);
+ plistFile->setFallbacksEnabled(false);
+ const QStringList plist = plistFile->allKeys();
+ if (!plist.isEmpty()) {
+ foreach (const QString &key, plist)
+ iniFile.setValue(key, plistFile->value(key));
+ plistFile->clear();
+ }
+
+ QString plistPath = plistFile->fileName();
+ delete plistFile;
+ Utils::Fs::forceRemove(plistPath);
+}
+
+void macMigratePlists()
+{
+ migratePlistToIni("qBittorrent-data");
+ migratePlistToIni("qBittorrent-rss");
+ migratePlistToIni("qBittorrent");
+}
+#endif // Q_OS_MAC
+
+
#endif // UPGRADE_H
diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt
index f1ca414f1..7efcf4d2f 100644
--- a/src/base/CMakeLists.txt
+++ b/src/base/CMakeLists.txt
@@ -1,5 +1,4 @@
find_package(ZLIB REQUIRED)
-include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
set(QBT_BASE_HEADERS
bittorrent/cachestatus.h
@@ -31,6 +30,7 @@ net/downloadmanager.h
net/geoipmanager.h
net/portforwarder.h
net/private/geoipdatabase.h
+net/proxyconfigurationmanager.h
net/reverseresolution.h
net/smtp.h
rss/private/rssparser.h
@@ -47,12 +47,14 @@ utils/misc.h
utils/string.h
filesystemwatcher.h
iconprovider.h
+indexrange.h
logger.h
preferences.h
qinisettings.h
scanfoldersmodel.h
searchengine.h
settingsstorage.h
+torrentfileguard.h
torrentfilter.h
tristatebool.h
types.h
@@ -87,6 +89,7 @@ net/downloadmanager.cpp
net/geoipmanager.cpp
net/portforwarder.cpp
net/private/geoipdatabase.cpp
+net/proxyconfigurationmanager.cpp
net/reverseresolution.cpp
net/smtp.cpp
rss/private/rssparser.cpp
@@ -108,12 +111,13 @@ preferences.cpp
scanfoldersmodel.cpp
searchengine.cpp
settingsstorage.cpp
+torrentfileguard.cpp
torrentfilter.cpp
tristatebool.cpp
)
add_library(qbt_base STATIC ${QBT_BASE_HEADERS} ${QBT_BASE_SOURCES})
-target_link_libraries(qbt_base ${ZLIB_LIBRARIES} ${LibtorrentRasterbar_LIBRARIES})
+target_link_libraries(qbt_base ZLIB::ZLIB LibtorrentRasterbar::LibTorrent)
target_link_qt_components(qbt_base Core Network Xml)
if (QT4_FOUND)
if (GUI)
@@ -124,3 +128,13 @@ else (QT4_FOUND)
target_link_libraries(qbt_base Qt5::Gui Qt5::Widgets)
endif (GUI)
endif (QT4_FOUND)
+
+if (DBUS)
+ target_link_qt_components(qbt_base DBus)
+endif ()
+
+if (APPLE)
+ find_library(IOKit_LIBRARY IOKit)
+ find_library(Carbon_LIBRARY Carbon)
+ target_link_libraries(qbt_base ${Carbon_LIBRARY} ${IOKit_LIBRARY})
+endif (APPLE)
diff --git a/src/base/base.pri b/src/base/base.pri
index d52c23445..b6310ae4f 100644
--- a/src/base/base.pri
+++ b/src/base/base.pri
@@ -5,7 +5,9 @@ HEADERS += \
$$PWD/qinisettings.h \
$$PWD/logger.h \
$$PWD/settingsstorage.h \
+ $$PWD/settingvalue.h \
$$PWD/preferences.h \
+ $$PWD/indexrange.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/connection.h \
@@ -19,6 +21,7 @@ HEADERS += \
$$PWD/net/downloadhandler.h \
$$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \
+ $$PWD/net/proxyconfigurationmanager.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.h \
@@ -51,6 +54,7 @@ HEADERS += \
$$PWD/utils/misc.h \
$$PWD/utils/string.h \
$$PWD/unicodestrings.h \
+ $$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h
@@ -72,6 +76,7 @@ SOURCES += \
$$PWD/net/downloadhandler.cpp \
$$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \
+ $$PWD/net/proxyconfigurationmanager.cpp \
$$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.cpp \
@@ -103,6 +108,7 @@ SOURCES += \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/string.cpp \
+ $$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp
diff --git a/src/base/bittorrent/infohash.cpp b/src/base/bittorrent/infohash.cpp
index b492fcb44..293e650fe 100644
--- a/src/base/bittorrent/infohash.cpp
+++ b/src/base/bittorrent/infohash.cpp
@@ -68,7 +68,6 @@ bool InfoHash::isValid() const
return m_valid;
}
-
InfoHash::operator libtorrent::sha1_hash() const
{
return m_nativeHash;
diff --git a/src/base/bittorrent/private/bandwidthscheduler.cpp b/src/base/bittorrent/private/bandwidthscheduler.cpp
index dcb0097d1..ee009ac55 100644
--- a/src/base/bittorrent/private/bandwidthscheduler.cpp
+++ b/src/base/bittorrent/private/bandwidthscheduler.cpp
@@ -31,13 +31,13 @@
#include
#include
+#include "base/bittorrent/session.h"
#include "base/preferences.h"
#include "bandwidthscheduler.h"
BandwidthScheduler::BandwidthScheduler(QObject *parent)
: QTimer(parent)
{
- Q_ASSERT(Preferences::instance()->isSchedulerEnabled());
// Single shot, we call start() again manually
setSingleShot(true);
// Connect Signals/Slots
@@ -47,8 +47,7 @@ BandwidthScheduler::BandwidthScheduler(QObject *parent)
void BandwidthScheduler::start()
{
const Preferences* const pref = Preferences::instance();
- Q_ASSERT(pref->isSchedulerEnabled());
- bool alt_bw_enabled = pref->isAltBandwidthEnabled();
+ bool alt_bw_enabled = BitTorrent::Session::instance()->isAltGlobalSpeedLimitEnabled();
QTime start = pref->getSchedulerStartTime();
QTime end = pref->getSchedulerEndTime();
diff --git a/src/base/bittorrent/private/filterparserthread.cpp b/src/base/bittorrent/private/filterparserthread.cpp
index e2c1564e9..250893e0f 100644
--- a/src/base/bittorrent/private/filterparserthread.cpp
+++ b/src/base/bittorrent/private/filterparserthread.cpp
@@ -55,10 +55,10 @@ FilterParserThread::~FilterParserThread()
}
// Parser for eMule ip filter in DAT format
-int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &filter)
+int FilterParserThread::parseDATFilterFile(QString filePath, libt::ip_filter &filter)
{
int ruleCount = 0;
- QFile file(m_filePath);
+ QFile file(filePath);
if (!file.exists()) return ruleCount;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -149,10 +149,10 @@ int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &
}
// Parser for PeerGuardian ip filter in p2p format
-int FilterParserThread::parseP2PFilterFile(QString m_filePath, libt::ip_filter &filter)
+int FilterParserThread::parseP2PFilterFile(QString filePath, libt::ip_filter &filter)
{
int ruleCount = 0;
- QFile file(m_filePath);
+ QFile file(filePath);
if (!file.exists()) return ruleCount;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -257,10 +257,10 @@ int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name,
}
// Parser for PeerGuardian ip filter in p2p format
-int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &filter)
+int FilterParserThread::parseP2BFilterFile(QString filePath, libt::ip_filter &filter)
{
int ruleCount = 0;
- QFile file(m_filePath);
+ QFile file(filePath);
if (!file.exists()) return ruleCount;
if (!file.open(QIODevice::ReadOnly)) {
@@ -369,7 +369,7 @@ int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &
// * 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 FilterParserThread::processFilterFile(QString _filePath)
+void FilterParserThread::processFilterFile(QString filePath)
{
if (isRunning()) {
// Already parsing a filter, m_abort first
@@ -378,30 +378,37 @@ void FilterParserThread::processFilterFile(QString _filePath)
}
m_abort = false;
- m_filePath = _filePath;
+ m_filePath = filePath;
// Run it
start();
}
-void FilterParserThread::processFilterList(libt::session *s, const QStringList &IPs)
-{
- // First, import current filter
- libt::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;
- libt::address addr = libt::address::from_string(ip.toLocal8Bit().constData(), ec);
- Q_ASSERT(!ec);
- if (!ec)
- filter.add_rule(addr, addr, libt::ip_filter::blocked);
- }
-
- s->set_ip_filter(filter);
-}
-
QString FilterParserThread::cleanupIPAddress(QString _ip)
{
- QHostAddress ip(_ip.trimmed());
+ _ip = _ip.trimmed();
+
+ // Emule .DAT files contain leading zeroes in IPv4 addresses
+ // eg 001.009.106.186
+ // We need to remove them because both QHostAddress and Boost.Asio fail to parse them.
+ QStringList octets = _ip.split('.', QString::SkipEmptyParts);
+ if (octets.size() == 4) {
+ QString octet; // it is faster to not recreate this object in the loop
+ for (int i = 0; i < 4; i++) {
+ octet = octets[i];
+ if ((octet[0] == QChar('0')) && (octet.count() > 1)) {
+ if ((octet[1] == QChar('0')) && (octet.count() > 2))
+ octet.remove(0, 2);
+ else
+ octet.remove(0, 1);
+
+ octets[i] = octet;
+ }
+ }
+
+ _ip = octets.join(".");
+ }
+
+ QHostAddress ip(_ip);
if (ip.isNull()) return QString();
return ip.toString();
diff --git a/src/base/bittorrent/private/filterparserthread.h b/src/base/bittorrent/private/filterparserthread.h
index b54520b0d..ed2bd13cf 100644
--- a/src/base/bittorrent/private/filterparserthread.h
+++ b/src/base/bittorrent/private/filterparserthread.h
@@ -54,8 +54,7 @@ public:
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter &filter);
int getlineInStream(QDataStream &stream, std::string &name, char delim);
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter &filter);
- void processFilterFile(QString _filePath);
- static void processFilterList(libtorrent::session *s, const QStringList &IPs);
+ void processFilterFile(QString filePath);
signals:
void IPFilterParsed(int ruleCount);
diff --git a/src/base/bittorrent/private/speedmonitor.cpp b/src/base/bittorrent/private/speedmonitor.cpp
index 49ce65f58..096f9b883 100644
--- a/src/base/bittorrent/private/speedmonitor.cpp
+++ b/src/base/bittorrent/private/speedmonitor.cpp
@@ -27,17 +27,21 @@
* exception statement from your version.
*/
-#include
#include "speedmonitor.h"
+SpeedMonitor::SpeedMonitor()
+ : m_speedSamples(MAX_SAMPLES)
+{
+}
+
void SpeedMonitor::addSample(const SpeedSample &sample)
{
+ if (m_speedSamples.size() >= MAX_SAMPLES) {
+ m_sum -= m_speedSamples.front();
+ }
+
m_speedSamples.push_back(sample);
m_sum += sample;
- if (m_speedSamples.size() > MAX_SAMPLES) {
- m_sum -= m_speedSamples.front();
- m_speedSamples.pop_front();
- }
}
SpeedSampleAvg SpeedMonitor::average() const
diff --git a/src/base/bittorrent/private/speedmonitor.h b/src/base/bittorrent/private/speedmonitor.h
index 61cb33baa..4bbc90c4e 100644
--- a/src/base/bittorrent/private/speedmonitor.h
+++ b/src/base/bittorrent/private/speedmonitor.h
@@ -30,7 +30,11 @@
#ifndef SPEEDMONITOR_H
#define SPEEDMONITOR_H
-template class QList;
+#ifndef Q_MOC_RUN
+#include
+#endif
+
+#include
template
struct Sample
@@ -71,13 +75,15 @@ typedef Sample SpeedSampleAvg;
class SpeedMonitor
{
public:
+ SpeedMonitor();
+
void addSample(const SpeedSample &sample);
SpeedSampleAvg average() const;
void reset();
private:
static const int MAX_SAMPLES = 30;
- QList m_speedSamples;
+ boost::circular_buffer m_speedSamples;
SpeedSample m_sum;
};
diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp
index e781b15a6..5ba1f70b7 100644
--- a/src/base/bittorrent/session.cpp
+++ b/src/base/bittorrent/session.cpp
@@ -28,83 +28,74 @@
* exception statement from your version.
*/
+#include "session.h"
+
+#include
+#include
#include
#include
-#include
-#include
-#include
-#include
+#include
#include
#include
-#include
+#include
#include
-#include
-#include
#include
+#include
+#include
+#include
+#include
#include
#include
-#include
-
-#include
-#include
+#include
+#if LIBTORRENT_VERSION_NUM >= 10100
+#include
+#endif
#include
#include
-#include
-#include
-#include
-#include
-#include
#include
#include
#include
#include
-//#include
+#include
+#include
+#if LIBTORRENT_VERSION_NUM < 10100
+#include
+#endif
+#include
+#include
+#include
+#include "base/logger.h"
+#include "base/net/downloadhandler.h"
+#include "base/net/downloadmanager.h"
+#include "base/net/portforwarder.h"
+#include "base/net/proxyconfigurationmanager.h"
+#include "base/torrentfileguard.h"
+#include "base/torrentfilter.h"
+#include "base/unicodestrings.h"
#include "base/utils/misc.h"
#include "base/utils/fs.h"
#include "base/utils/string.h"
-#include "base/unicodestrings.h"
-#include "base/logger.h"
-#include "base/settingsstorage.h"
-#include "base/preferences.h"
-#include "base/torrentfilter.h"
-#include "base/net/downloadmanager.h"
-#include "base/net/downloadhandler.h"
-#include "base/net/portforwarder.h"
-#include "base/utils/string.h"
+#include "cachestatus.h"
+#include "magneturi.h"
#include "private/filterparserthread.h"
#include "private/statistics.h"
#include "private/bandwidthscheduler.h"
#include "private/resumedatasavingmanager.h"
-#include "trackerentry.h"
-#include "tracker.h"
-#include "magneturi.h"
-#include "cachestatus.h"
#include "sessionstatus.h"
#include "torrenthandle.h"
-#include "session.h"
+#include "tracker.h"
+#include "trackerentry.h"
static const char PEER_ID[] = "qB";
static const char RESUME_FOLDER[] = "BT_backup";
+static const char USER_AGENT[] = "qBittorrent " VERSION;
namespace libt = libtorrent;
using namespace BitTorrent;
-#define SETTINGS_KEY(name) "BitTorrent/Session/" name
-const QString KEY_CATEGORIES = SETTINGS_KEY("Categories");
-const QString KEY_MAXRATIOACTION = SETTINGS_KEY("MaxRatioAction");
-const QString KEY_DEFAULTSAVEPATH = SETTINGS_KEY("DefaultSavePath");
-const QString KEY_TEMPPATH = SETTINGS_KEY("TempPath");
-const QString KEY_SUBCATEGORIESENABLED = SETTINGS_KEY("SubcategoriesEnabled");
-const QString KEY_TEMPPATHENABLED = SETTINGS_KEY("TempPathEnabled");
-const QString KEY_DISABLEASMBYDEFAULT = SETTINGS_KEY("DisableASMByDefault");
-const QString KEY_DISABLEASMONCATEGORYCHANGED = SETTINGS_KEY("DisableASMTriggers/CategoryChanged");
-const QString KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED = SETTINGS_KEY("DisableASMTriggers/DefaultSavePathChanged");
-const QString KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED = SETTINGS_KEY("DisableASMTriggers/CategorySavePathChanged");
-const QString KEY_ADDTORRENTPAUSED = SETTINGS_KEY("AddTorrentPaused");
-
namespace
{
bool readFile(const QString &path, QByteArray &buf);
@@ -115,6 +106,8 @@ namespace
void torrentQueuePositionTop(const libt::torrent_handle &handle);
void torrentQueuePositionBottom(const libt::torrent_handle &handle);
+ inline SettingsStorage *settings() { return SettingsStorage::instance(); }
+
QStringMap map_cast(const QVariantMap &map)
{
QStringMap result;
@@ -131,15 +124,21 @@ namespace
return result;
}
+ QString normalizePath(const QString &path)
+ {
+ QString tmp = Utils::Fs::fromNativePath(path.trimmed());
+ if (!tmp.isEmpty() && !tmp.endsWith('/'))
+ return tmp + '/';
+ return tmp;
+ }
+
QString normalizeSavePath(QString path, const QString &defaultPath = Utils::Fs::QDesktopServicesDownloadLocation())
{
- path = Utils::Fs::fromNativePath(path.trimmed());
+ path = path.trimmed();
if (path.isEmpty())
path = Utils::Fs::fromNativePath(defaultPath.trimmed());
- if (!path.isEmpty() && !path.endsWith('/'))
- path += '/';
- return path;
+ return normalizePath(path);
}
QStringMap expandCategories(const QStringMap &categories)
@@ -155,29 +154,135 @@ namespace
return expanded;
}
+
+ QStringList findAllFiles(const QString &dirPath)
+ {
+ QStringList files;
+ QDirIterator it(dirPath, QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext())
+ files << it.next();
+
+ return files;
+ }
+
+ template
+ struct LowerLimited
+ {
+ LowerLimited(T limit, T ret)
+ : m_limit(limit)
+ , m_ret(ret)
+ {
+ }
+
+ explicit LowerLimited(T limit)
+ : LowerLimited(limit, limit)
+ {
+ }
+
+ T operator()(T val)
+ {
+ return val <= m_limit ? m_ret : val;
+ }
+
+ private:
+ const T m_limit;
+ const T m_ret;
+ };
+
+ template
+ LowerLimited lowerLimited(T limit) { return LowerLimited(limit); }
+
+ template
+ LowerLimited lowerLimited(T limit, T ret) { return LowerLimited(limit, ret); }
}
// Session
Session *Session::m_instance = nullptr;
+#define BITTORRENT_KEY(name) "BitTorrent/" name
+#define BITTORRENT_SESSION_KEY(name) BITTORRENT_KEY("Session/") name
+
Session::Session(QObject *parent)
: QObject(parent)
- , m_settings(SettingsStorage::instance())
- , m_LSDEnabled(false)
- , m_DHTEnabled(false)
- , m_PeXEnabled(false)
- , m_queueingEnabled(false)
- , m_torrentExportEnabled(false)
- , m_finishedTorrentExportEnabled(false)
- , m_preAllocateAll(false)
- , m_globalMaxRatio(-1)
+ , m_deferredConfigureScheduled(false)
+ , m_IPFilteringChanged(false)
+#if LIBTORRENT_VERSION_NUM >= 10100
+ , m_listenInterfaceChanged(true)
+#endif
+ , m_isDHTEnabled(BITTORRENT_SESSION_KEY("DHTEnabled"), true)
+ , m_isLSDEnabled(BITTORRENT_SESSION_KEY("LSDEnabled"), true)
+ , m_isPeXEnabled(BITTORRENT_SESSION_KEY("PeXEnabled"), true)
+ , m_isTrackerExchangeEnabled(BITTORRENT_SESSION_KEY("TrackerExchangeEnabled"), false)
+ , m_isIPFilteringEnabled(BITTORRENT_SESSION_KEY("IPFilteringEnabled"), false)
+ , m_isTrackerFilteringEnabled(BITTORRENT_SESSION_KEY("TrackerFilteringEnabled"), false)
+ , m_IPFilterFile(BITTORRENT_SESSION_KEY("IPFilter"))
+ , m_announceToAllTrackers(BITTORRENT_SESSION_KEY("AnnounceToAllTrackers"), true)
+ , m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), 0)
+ , m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
+ , m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
+ , m_isAnonymousModeEnabled(BITTORRENT_SESSION_KEY("AnonymousModeEnabled"), false)
+ , m_isQueueingEnabled(BITTORRENT_SESSION_KEY("QueueingSystemEnabled"), true)
+ , m_maxActiveDownloads(BITTORRENT_SESSION_KEY("MaxActiveDownloads"), 3, lowerLimited(-1))
+ , m_maxActiveUploads(BITTORRENT_SESSION_KEY("MaxActiveUploads"), 3, lowerLimited(-1))
+ , m_maxActiveTorrents(BITTORRENT_SESSION_KEY("MaxActiveTorrents"), 5, lowerLimited(-1))
+ , m_ignoreSlowTorrentsForQueueing(BITTORRENT_SESSION_KEY("IgnoreSlowTorrentsForQueueing"), false)
+ , m_outgoingPortsMin(BITTORRENT_SESSION_KEY("OutgoingPortsMin"), 0)
+ , m_outgoingPortsMax(BITTORRENT_SESSION_KEY("OutgoingPortsMax"), 0)
+ , m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), true)
+ , m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
+ , m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
+ , m_isSuperSeedingEnabled(BITTORRENT_SESSION_KEY("SuperSeedingEnabled"), false)
+ , m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
+ , m_maxHalfOpenConnections(BITTORRENT_SESSION_KEY("MaxHalfOpenConnections"), 20, lowerLimited(0, -1))
+ , m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), -1, lowerLimited(0, -1))
+ , m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1))
+ , m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), -1, lowerLimited(0, -1))
+ , m_isUTPEnabled(BITTORRENT_SESSION_KEY("uTPEnabled"), true)
+ , m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
+ , m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
+ , m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
+ , m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r) { return r < 0 ? -1. : r;})
+ , m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false)
+ , m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false)
+ , m_refreshInterval(BITTORRENT_SESSION_KEY("RefreshInterval"), 1500)
+ , m_isPreallocationEnabled(BITTORRENT_SESSION_KEY("Preallocation"), false)
+ , m_torrentExportDirectory(BITTORRENT_SESSION_KEY("TorrentExportDirectory"))
+ , m_finishedTorrentExportDirectory(BITTORRENT_SESSION_KEY("FinishedTorrentExportDirectory"))
+ , m_globalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalDLSpeedLimit"), 0, lowerLimited(0))
+ , m_globalUploadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalUPSpeedLimit"), 0, lowerLimited(0))
+ , m_altGlobalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalDLSpeedLimit"), 10, lowerLimited(0))
+ , m_altGlobalUploadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalUPSpeedLimit"), 10, lowerLimited(0))
+ , m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
+ , m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
+ , m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 3)
+ , m_port(BITTORRENT_SESSION_KEY("Port"), 8999)
+ , m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
+ , m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
+ , m_networkInterfaceName(BITTORRENT_SESSION_KEY("InterfaceName"))
+ , m_networkInterfaceAddress(BITTORRENT_SESSION_KEY("InterfaceAddress"))
+ , m_isIPv6Enabled(BITTORRENT_SESSION_KEY("IPv6Enabled"), false)
+ , m_encryption(BITTORRENT_SESSION_KEY("Encryption"), 0)
+ , m_isForceProxyEnabled(BITTORRENT_SESSION_KEY("ForceProxy"), true)
+ , m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false)
+ , m_storedCategories(BITTORRENT_SESSION_KEY("Categories"))
+ , m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
+ , m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), Utils::Fs::QDesktopServicesDownloadLocation(), normalizePath)
+ , m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath)
+ , m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
+ , m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
+ , m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY("DisableAutoTMMByDefault"), true)
+ , m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategoryChanged"), false)
+ , m_isDisableAutoTMMWhenDefaultSavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/DefaultSavePathChanged"), true)
+ , m_isDisableAutoTMMWhenCategorySavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategorySavePathChanged"), true)
+ , m_isTrackerEnabled(BITTORRENT_KEY("TrackerEnabled"), false)
+ , m_bannedIPs("State/BannedIPs")
+ , m_wasPexEnabled(m_isPeXEnabled)
+ , m_wasTrackerExchangeEnabled(m_isTrackerExchangeEnabled)
, m_numResumeData(0)
, m_extraLimit(0)
- , m_appendExtension(false)
- , m_refreshInterval(0)
+ , m_useProxy(false)
{
- Preferences* const pref = Preferences::instance();
Logger* const logger = Logger::instance();
initResumeFolder();
@@ -186,13 +291,6 @@ Session::Session(QObject *parent)
m_bigRatioTimer->setInterval(10000);
connect(m_bigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios()));
- // Creating BitTorrent session
-
- // Construct session
- libt::fingerprint fingerprint(PEER_ID, VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, VERSION_BUILD);
- const unsigned short port = pref->getSessionPort();
- std::pair ports(port, port);
- const QString ip = getListeningIPs().first();
// Set severity level of libtorrent session
int alertMask = libt::alert::error_notification
| libt::alert::peer_notification
@@ -205,54 +303,114 @@ Session::Session(QObject *parent)
| libt::alert::stats_notification
;
- if (ip.isEmpty()) {
- logger->addMessage(tr("qBittorrent is trying to listen on any interface port: %1", "e.g: qBittorrent is trying to listen on any interface port: TCP/6881").arg(QString::number(port)), Log::INFO);
- m_nativeSession = new libt::session(fingerprint, ports, 0, 0, alertMask);
- }
- else {
- logger->addMessage(tr("qBittorrent is trying to listen on interface %1 port: %2", "e.g: qBittorrent is trying to listen on interface 192.168.0.1 port: TCP/6881").arg(ip).arg(port), Log::INFO);
- m_nativeSession = new libt::session(fingerprint, ports, ip.toLatin1().constData(), 0, alertMask);
- }
+#if LIBTORRENT_VERSION_NUM < 10100
+ libt::fingerprint fingerprint(PEER_ID, VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, VERSION_BUILD);
+ std::string peerId = fingerprint.to_string();
+ const ushort port = this->port();
+ std::pair ports(port, port);
+ const QString ip = getListeningIPs().first();
+ m_nativeSession = new libt::session(fingerprint, ports, ip.isEmpty() ? 0 : ip.toLatin1().constData(), 0, alertMask);
- logger->addMessage(tr("Peer ID: ") + Utils::String::fromStdString(fingerprint.to_string()));
+ libt::session_settings sessionSettings = m_nativeSession->settings();
+ sessionSettings.user_agent = USER_AGENT;
+ sessionSettings.upnp_ignore_nonrouters = true;
+ sessionSettings.use_dht_as_fallback = false;
+ // Disable support for SSL torrents for now
+ sessionSettings.ssl_listen = 0;
+ // To prevent ISPs from blocking seeding
+ sessionSettings.lazy_bitfields = true;
+ // Speed up exit
+ sessionSettings.stop_tracker_timeout = 1;
+ sessionSettings.auto_scrape_interval = 1200; // 20 minutes
+ sessionSettings.auto_scrape_min_interval = 900; // 15 minutes
+ sessionSettings.connection_speed = 20; // default is 10
+ sessionSettings.no_connect_privileged_ports = false;
+ sessionSettings.seed_choking_algorithm = libt::session_settings::fastest_upload;
+ configure(sessionSettings);
+ m_nativeSession->set_settings(sessionSettings);
+ configureListeningInterface();
+ m_nativeSession->set_alert_dispatch([this](std::auto_ptr alertPtr)
+ {
+ dispatchAlerts(alertPtr.release());
+ });
+#else
+ std::string peerId = libt::generate_fingerprint(PEER_ID, VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, VERSION_BUILD);
+ libt::settings_pack pack;
+ pack.set_int(libt::settings_pack::alert_mask, alertMask);
+ pack.set_str(libt::settings_pack::peer_fingerprint, peerId);
+ pack.set_bool(libt::settings_pack::listen_system_port_fallback, false);
+ pack.set_str(libt::settings_pack::user_agent, USER_AGENT);
+ pack.set_bool(libt::settings_pack::upnp_ignore_nonrouters, true);
+ pack.set_bool(libt::settings_pack::use_dht_as_fallback, false);
+ // Disable support for SSL torrents for now
+ pack.set_int(libt::settings_pack::ssl_listen, 0);
+ // To prevent ISPs from blocking seeding
+ pack.set_bool(libt::settings_pack::lazy_bitfields, true);
+ // Speed up exit
+ pack.set_int(libt::settings_pack::stop_tracker_timeout, 1);
+ pack.set_int(libt::settings_pack::auto_scrape_interval, 1200); // 20 minutes
+ pack.set_int(libt::settings_pack::auto_scrape_min_interval, 900); // 15 minutes
+ pack.set_int(libt::settings_pack::connection_speed, 20); // default is 10
+ pack.set_bool(libt::settings_pack::no_connect_privileged_ports, false);
+ pack.set_int(libt::settings_pack::seed_choking_algorithm, libt::settings_pack::fastest_upload);
+ configure(pack);
- m_nativeSession->set_alert_dispatch(boost::bind(&Session::dispatchAlerts, this, _1));
+ m_nativeSession = new libt::session(pack, 0);
+ m_nativeSession->set_alert_notify([this]()
+ {
+ QMetaObject::invokeMethod(this, "readAlerts", Qt::QueuedConnection);
+ });
+#endif
// Enabling plugins
//m_nativeSession->add_extension(&libt::create_metadata_plugin);
m_nativeSession->add_extension(&libt::create_ut_metadata_plugin);
- if (pref->trackerExchangeEnabled())
+ if (isTrackerExchangeEnabled())
m_nativeSession->add_extension(&libt::create_lt_trackers_plugin);
- m_PeXEnabled = pref->isPeXEnabled();
- if (m_PeXEnabled)
+ if (isPeXEnabled())
m_nativeSession->add_extension(&libt::create_ut_pex_plugin);
m_nativeSession->add_extension(&libt::create_smart_ban_plugin);
- m_categories = map_cast(m_settings->loadValue(KEY_CATEGORIES).toMap());
+ logger->addMessage(tr("Peer ID: ") + Utils::String::fromStdString(peerId));
+ logger->addMessage(tr("HTTP User-Agent is '%1'").arg(USER_AGENT));
+ logger->addMessage(tr("DHT support [%1]").arg(isDHTEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
+ logger->addMessage(tr("Local Peer Discovery support [%1]").arg(isLSDEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
+ logger->addMessage(tr("PeX support [%1]").arg(isPeXEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
+ logger->addMessage(tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
+ logger->addMessage(tr("Encryption support [%1]")
+ .arg(encryption() == 0 ? tr("ON") : encryption() == 1 ? tr("FORCED") : tr("OFF"))
+ , Log::INFO);
+
+ if (isIPFilteringEnabled())
+ enableIPFilter();
+ // Add the banned IPs
+ processBannedIPs();
+
+ m_categories = map_cast(m_storedCategories);
if (isSubcategoriesEnabled()) {
// if subcategories support changed manually
m_categories = expandCategories(m_categories);
- m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
+ m_storedCategories = map_cast(m_categories);
}
m_refreshTimer = new QTimer(this);
- m_refreshTimer->setInterval(2000);
+ m_refreshTimer->setInterval(refreshInterval());
connect(m_refreshTimer, SIGNAL(timeout()), SLOT(refresh()));
m_refreshTimer->start();
// Regular saving of fastresume data
m_resumeDataTimer = new QTimer(this);
+ m_resumeDataTimer->setInterval(saveResumeDataInterval() * 60 * 1000);
connect(m_resumeDataTimer, SIGNAL(timeout()), SLOT(generateResumeData()));
m_statistics = new Statistics(this);
- m_maxRatioAction = static_cast(m_settings->loadValue(KEY_MAXRATIOACTION, Pause).toInt());
- m_defaultSavePath = normalizeSavePath(m_settings->loadValue(KEY_DEFAULTSAVEPATH).toString());
- m_tempPath = normalizeSavePath(m_settings->loadValue(KEY_TEMPPATH).toString(), m_defaultSavePath + "temp");
+ updateRatioTimer();
+ populateAdditionalTrackers();
- // Apply user settings to BitTorrent session
- configure();
- connect(pref, SIGNAL(changed()), SLOT(configure()));
+ enableTracker(isTrackerEnabled());
+
+ connect(Net::ProxyConfigurationManager::instance(), SIGNAL(proxyConfigurationChanged()), SLOT(configureDeferred()));
// Network configuration monitor
connect(&m_networkManager, SIGNAL(onlineStateChanged(bool)), SLOT(networkOnlineStateChanged(bool)));
@@ -276,49 +434,151 @@ Session::Session(QObject *parent)
bool Session::isDHTEnabled() const
{
- return m_DHTEnabled;
+ return m_isDHTEnabled;
+}
+
+void Session::setDHTEnabled(bool enabled)
+{
+ if (enabled != m_isDHTEnabled) {
+ m_isDHTEnabled = enabled;
+ configureDeferred();
+ Logger::instance()->addMessage(
+ tr("DHT support [%1]").arg(enabled ? tr("ON") : tr("OFF")), Log::INFO);
+ }
}
bool Session::isLSDEnabled() const
{
- return m_LSDEnabled;
+ return m_isLSDEnabled;
}
-bool Session::isPexEnabled() const
+void Session::setLSDEnabled(bool enabled)
{
- return m_PeXEnabled;
+ if (enabled != m_isLSDEnabled) {
+ m_isLSDEnabled = enabled;
+ configureDeferred();
+ Logger::instance()->addMessage(
+ tr("Local Peer Discovery support [%1]").arg(enabled ? tr("ON") : tr("OFF"))
+ , Log::INFO);
+ }
}
-bool Session::isQueueingEnabled() const
+bool Session::isPeXEnabled() const
{
- return m_queueingEnabled;
+ return m_isPeXEnabled;
+}
+
+void Session::setPeXEnabled(bool enabled)
+{
+ m_isPeXEnabled = enabled;
+ if (m_wasPexEnabled != enabled)
+ Logger::instance()->addMessage(tr("Restart is required to toggle PeX support"), Log::WARNING);
+}
+
+bool Session::isTrackerExchangeEnabled() const
+{
+ return m_isTrackerExchangeEnabled;
+}
+
+void Session::setTrackerExchangeEnabled(bool enabled)
+{
+ m_isTrackerExchangeEnabled = enabled;
+ if (m_wasTrackerExchangeEnabled != enabled)
+ Logger::instance()->addMessage(tr("Restart is required to toggle Tracker Exchange support"), Log::WARNING);
}
bool Session::isTempPathEnabled() const
{
- return m_settings->loadValue(KEY_TEMPPATHENABLED, false).toBool();
+ return m_isTempPathEnabled;
}
void Session::setTempPathEnabled(bool enabled)
{
- m_settings->storeValue(KEY_TEMPPATHENABLED, enabled);
- foreach (TorrentHandle *const torrent, m_torrents)
- torrent->handleTempPathChanged();
+ if (enabled != isTempPathEnabled()) {
+ m_isTempPathEnabled = enabled;
+ foreach (TorrentHandle *const torrent, m_torrents)
+ torrent->handleTempPathChanged();
+ }
}
bool Session::isAppendExtensionEnabled() const
{
- return m_appendExtension;
+ return m_isAppendExtensionEnabled;
+}
+
+void Session::setAppendExtensionEnabled(bool enabled)
+{
+ if (isAppendExtensionEnabled() != enabled) {
+ // append or remove .!qB extension for incomplete files
+ foreach (TorrentHandle *const torrent, m_torrents)
+ torrent->handleAppendExtensionToggled();
+
+ m_isAppendExtensionEnabled = enabled;
+ }
+}
+
+uint Session::refreshInterval() const
+{
+ return m_refreshInterval;
+}
+
+void Session::setRefreshInterval(uint value)
+{
+ if (value != refreshInterval()) {
+ m_refreshTimer->setInterval(value);
+ m_refreshInterval = value;
+ }
+}
+
+bool Session::isPreallocationEnabled() const
+{
+ return m_isPreallocationEnabled;
+}
+
+void Session::setPreallocationEnabled(bool enabled)
+{
+ m_isPreallocationEnabled = enabled;
+}
+
+QString Session::torrentExportDirectory() const
+{
+ return Utils::Fs::fromNativePath(m_torrentExportDirectory);
+}
+
+void Session::setTorrentExportDirectory(QString path)
+{
+ path = Utils::Fs::fromNativePath(path);
+ if (path != torrentExportDirectory())
+ m_torrentExportDirectory = path;
+}
+
+QString Session::finishedTorrentExportDirectory() const
+{
+ return Utils::Fs::fromNativePath(m_finishedTorrentExportDirectory);
+}
+
+void Session::setFinishedTorrentExportDirectory(QString path)
+{
+ path = Utils::Fs::fromNativePath(path);
+ if (path != finishedTorrentExportDirectory())
+ m_finishedTorrentExportDirectory = path;
}
QString Session::defaultSavePath() const
{
- return m_defaultSavePath;
+ return Utils::Fs::fromNativePath(m_defaultSavePath);
}
QString Session::tempPath() const
{
- return m_tempPath;
+ return Utils::Fs::fromNativePath(m_tempPath);
+}
+
+QString Session::torrentTempPath(const InfoHash &hash) const
+{
+ return tempPath()
+ + static_cast(hash).left(7)
+ + "/";
}
bool Session::isValidCategoryName(const QString &name)
@@ -384,7 +644,7 @@ bool Session::addCategory(const QString &name, const QString &savePath)
}
m_categories[name] = savePath;
- m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
+ m_storedCategories = map_cast(m_categories);
emit categoryAdded(name);
return true;
@@ -396,10 +656,10 @@ bool Session::editCategory(const QString &name, const QString &savePath)
if (categorySavePath(name) == savePath) return false;
m_categories[name] = savePath;
- if (isDisableASMWhenCategorySavePathChanged()) {
+ if (isDisableAutoTMMWhenCategorySavePathChanged()) {
foreach (TorrentHandle *const torrent, torrents())
if (torrent->category() == name)
- torrent->setASMEnabled(false);
+ torrent->setAutoTMMEnabled(false);
}
else {
foreach (TorrentHandle *const torrent, torrents())
@@ -434,7 +694,7 @@ bool Session::removeCategory(const QString &name)
if (result) {
// update stored categories
- m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
+ m_storedCategories = map_cast(m_categories);
emit categoryRemoved(name);
}
@@ -443,7 +703,7 @@ bool Session::removeCategory(const QString &name)
bool Session::isSubcategoriesEnabled() const
{
- return m_settings->loadValue(KEY_SUBCATEGORIESENABLED, false).toBool();
+ return m_isSubcategoriesEnabled;
}
void Session::setSubcategoriesEnabled(bool value)
@@ -454,65 +714,78 @@ void Session::setSubcategoriesEnabled(bool value)
// expand categories to include all parent categories
m_categories = expandCategories(m_categories);
// update stored categories
- m_settings->storeValue(KEY_CATEGORIES, map_cast(m_categories));
+ m_storedCategories = map_cast(m_categories);
}
else {
// reload categories
- m_categories = map_cast(m_settings->loadValue(KEY_CATEGORIES).toMap());
+ m_categories = map_cast(m_storedCategories);
}
- m_settings->storeValue(KEY_SUBCATEGORIESENABLED, value);
+ m_isSubcategoriesEnabled = value;
emit subcategoriesSupportChanged();
}
-bool Session::isASMDisabledByDefault() const
+bool Session::isAutoTMMDisabledByDefault() const
{
- return m_settings->loadValue(KEY_DISABLEASMBYDEFAULT, true).toBool();
+ return m_isAutoTMMDisabledByDefault;
}
-void Session::setASMDisabledByDefault(bool value)
+void Session::setAutoTMMDisabledByDefault(bool value)
{
- m_settings->storeValue(KEY_DISABLEASMBYDEFAULT, value);
+ m_isAutoTMMDisabledByDefault = value;
}
-bool Session::isDisableASMWhenCategoryChanged() const
+bool Session::isDisableAutoTMMWhenCategoryChanged() const
{
- return m_settings->loadValue(KEY_DISABLEASMONCATEGORYCHANGED, false).toBool();
+ return m_isDisableAutoTMMWhenCategoryChanged;
}
-void Session::setDisableASMWhenCategoryChanged(bool value)
+void Session::setDisableAutoTMMWhenCategoryChanged(bool value)
{
- m_settings->storeValue(KEY_DISABLEASMONCATEGORYCHANGED, value);
+ m_isDisableAutoTMMWhenCategoryChanged = value;
}
-bool Session::isDisableASMWhenDefaultSavePathChanged() const
+bool Session::isDisableAutoTMMWhenDefaultSavePathChanged() const
{
- return m_settings->loadValue(KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED, true).toBool();
+ return m_isDisableAutoTMMWhenDefaultSavePathChanged;
}
-void Session::setDisableASMWhenDefaultSavePathChanged(bool value)
+void Session::setDisableAutoTMMWhenDefaultSavePathChanged(bool value)
{
- m_settings->storeValue(KEY_DISABLEASMONDEFAULTSAVEPATHCHANGED, value);
+ m_isDisableAutoTMMWhenDefaultSavePathChanged = value;
}
-bool Session::isDisableASMWhenCategorySavePathChanged() const
+bool Session::isDisableAutoTMMWhenCategorySavePathChanged() const
{
- return m_settings->loadValue(KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED, true).toBool();
+ return m_isDisableAutoTMMWhenCategorySavePathChanged;
}
-void Session::setDisableASMWhenCategorySavePathChanged(bool value)
+void Session::setDisableAutoTMMWhenCategorySavePathChanged(bool value)
{
- m_settings->storeValue(KEY_DISABLEASMONCATEGORYSAVEPATHCHANGED, value);
+ m_isDisableAutoTMMWhenCategorySavePathChanged = value;
}
bool Session::isAddTorrentPaused() const
{
- return m_settings->loadValue(KEY_ADDTORRENTPAUSED, false).toBool();
+ return m_isAddTorrentPaused;
}
void Session::setAddTorrentPaused(bool value)
{
- m_settings->storeValue(KEY_ADDTORRENTPAUSED, value);
+ m_isAddTorrentPaused = value;
+}
+
+bool Session::isTrackerEnabled() const
+{
+ return m_isTrackerEnabled;
+}
+
+void Session::setTrackerEnabled(bool enabled)
+{
+ if (isTrackerEnabled() != enabled) {
+ enableTracker(enabled);
+ m_isTrackerEnabled = enabled;
+ }
}
qreal Session::globalMaxRatio() const
@@ -520,6 +793,19 @@ qreal Session::globalMaxRatio() const
return m_globalMaxRatio;
}
+// Torrents will a ratio superior to the given value will
+// be automatically deleted
+void Session::setGlobalMaxRatio(qreal ratio)
+{
+ if (ratio < 0)
+ ratio = -1.;
+
+ if (ratio != globalMaxRatio()) {
+ m_globalMaxRatio = ratio;
+ updateRatioTimer();
+ }
+}
+
// Main destructor
Session::~Session()
{
@@ -547,8 +833,13 @@ Session::~Session()
void Session::initInstance()
{
- if (!m_instance)
+ if (!m_instance) {
m_instance = new Session;
+
+ // BandwidthScheduler::start() depends on Session being fully constructed
+ if (m_instance->isBandwidthSchedulerEnabled())
+ m_instance->enableBandwidthScheduler();
+ }
}
void Session::freeInstance()
@@ -564,52 +855,339 @@ Session *Session::instance()
return m_instance;
}
-void Session::setSessionSettings()
+void Session::adjustLimits()
+{
+ if (isQueueingSystemEnabled()) {
+#if LIBTORRENT_VERSION_NUM < 10100
+ libt::session_settings sessionSettings(m_nativeSession->settings());
+ adjustLimits(sessionSettings);
+ m_nativeSession->set_settings(sessionSettings);
+#else
+ libt::settings_pack settingsPack = m_nativeSession->get_settings();
+ adjustLimits(settingsPack);
+ m_nativeSession->apply_settings(settingsPack);
+#endif
+ }
+}
+
+// Set BitTorrent session configuration
+void Session::configure()
+{
+ qDebug("Configuring session");
+ if (!m_deferredConfigureScheduled) return; // Obtaining the lock is expensive, let's check early
+ QWriteLocker locker(&m_lock);
+ if (!m_deferredConfigureScheduled) return; // something might have changed while we were getting the lock
+#if LIBTORRENT_VERSION_NUM < 10100
+ libt::session_settings sessionSettings = m_nativeSession->settings();
+ configure(sessionSettings);
+ m_nativeSession->set_settings(sessionSettings);
+#else
+ libt::settings_pack settingsPack = m_nativeSession->get_settings();
+ configure(settingsPack);
+ m_nativeSession->apply_settings(settingsPack);
+#endif
+
+ if (m_IPFilteringChanged) {
+ if (isIPFilteringEnabled())
+ enableIPFilter();
+ else
+ disableIPFilter();
+ m_IPFilteringChanged = false;
+ }
+
+ m_deferredConfigureScheduled = false;
+ qDebug("Session configured");
+}
+
+void Session::processBannedIPs()
+{
+ // First, import current filter
+ libt::ip_filter filter = m_nativeSession->get_ip_filter();
+ foreach (const QString &ip, m_bannedIPs.value()) {
+ boost::system::error_code ec;
+ libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
+ Q_ASSERT(!ec);
+ if (!ec)
+ filter.add_rule(addr, addr, libt::ip_filter::blocked);
+ }
+
+ m_nativeSession->set_ip_filter(filter);
+}
+
+#if LIBTORRENT_VERSION_NUM >= 10100
+void Session::adjustLimits(libt::settings_pack &settingsPack)
+{
+ //Internally increase the queue limits to ensure that the magnet is started
+ int maxDownloads = maxActiveDownloads();
+ int maxActive = maxActiveTorrents();
+
+ settingsPack.set_int(libt::settings_pack::active_downloads
+ , maxDownloads > -1 ? maxDownloads + m_extraLimit : maxDownloads);
+ settingsPack.set_int(libt::settings_pack::active_limit
+ , maxActive > -1 ? maxActive + m_extraLimit : maxActive);
+}
+
+void Session::configure(libtorrent::settings_pack &settingsPack)
{
- Preferences* const pref = Preferences::instance();
Logger* const logger = Logger::instance();
- libt::session_settings sessionSettings = m_nativeSession->settings();
- sessionSettings.user_agent = "qBittorrent " VERSION;
- //std::cout << "HTTP User-Agent is " << sessionSettings.user_agent << std::endl;
- logger->addMessage(tr("HTTP User-Agent is '%1'").arg(Utils::String::fromStdString(sessionSettings.user_agent)));
+ if (m_listenInterfaceChanged) {
+ const ushort port = this->port();
+ std::pair ports(port, port);
+ settingsPack.set_int(libt::settings_pack::max_retry_port_bind, ports.second - ports.first);
+ foreach (QString ip, getListeningIPs()) {
+ libt::error_code ec;
+ std::string interfacesStr;
- sessionSettings.upnp_ignore_nonrouters = true;
- sessionSettings.use_dht_as_fallback = false;
- // Disable support for SSL torrents for now
- sessionSettings.ssl_listen = 0;
- // To prevent ISPs from blocking seeding
- sessionSettings.lazy_bitfields = true;
- // Speed up exit
- sessionSettings.stop_tracker_timeout = 1;
- sessionSettings.auto_scrape_interval = 1200; // 20 minutes
- bool announce_to_all = pref->announceToAllTrackers();
- sessionSettings.announce_to_all_trackers = announce_to_all;
- sessionSettings.announce_to_all_tiers = announce_to_all;
- sessionSettings.auto_scrape_min_interval = 900; // 15 minutes
- int cache_size = pref->diskCacheSize();
- sessionSettings.cache_size = cache_size ? cache_size * 64 : -1;
- sessionSettings.cache_expiry = pref->diskCacheTTL();
- qDebug() << "Using a disk cache size of" << cache_size << "MiB";
- libt::session_settings::io_buffer_mode_t mode = pref->osCache() ? libt::session_settings::enable_os_cache : libt::session_settings::disable_os_cache;
+ if (ip.isEmpty()) {
+ ip = QLatin1String("0.0.0.0");
+ interfacesStr = std::string((QString("%1:%2").arg(ip).arg(port)).toLatin1().constData());
+ logger->addMessage(tr("qBittorrent is trying to listen on any interface port: %1"
+ , "e.g: qBittorrent is trying to listen on any interface port: TCP/6881")
+ .arg(QString::number(port))
+ , Log::INFO);
+
+ settingsPack.set_str(libt::settings_pack::listen_interfaces, interfacesStr);
+ break;
+ }
+
+ libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
+ if (!ec) {
+ interfacesStr = std::string((addr.is_v6() ? QString("[%1]:%2") : QString("%1:%2"))
+ .arg(ip).arg(port).toLatin1().constData());
+ logger->addMessage(tr("qBittorrent is trying to listen on interface %1 port: %2"
+ , "e.g: qBittorrent is trying to listen on interface 192.168.0.1 port: TCP/6881")
+ .arg(ip).arg(port)
+ , Log::INFO);
+ settingsPack.set_str(libt::settings_pack::listen_interfaces, interfacesStr);
+ break;
+ }
+ }
+
+ m_listenInterfaceChanged = false;
+ }
+
+ const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled();
+ settingsPack.set_int(libt::settings_pack::download_rate_limit, altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit());
+ settingsPack.set_int(libt::settings_pack::upload_rate_limit, altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit());
+
+ // The most secure, rc4 only so that all streams are encrypted
+ settingsPack.set_int(libt::settings_pack::allowed_enc_level, libt::settings_pack::pe_rc4);
+ settingsPack.set_bool(libt::settings_pack::prefer_rc4, true);
+ switch (encryption()) {
+ case 0: //Enabled
+ settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_enabled);
+ settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_enabled);
+ break;
+ case 1: // Forced
+ settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_forced);
+ settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_forced);
+ break;
+ default: // Disabled
+ settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_disabled);
+ settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_disabled);
+ }
+
+ auto proxyManager = Net::ProxyConfigurationManager::instance();
+ Net::ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
+ if (m_useProxy || (proxyConfig.type != Net::ProxyType::None)) {
+ if (proxyConfig.type != Net::ProxyType::None) {
+ settingsPack.set_str(libt::settings_pack::proxy_hostname, Utils::String::toStdString(proxyConfig.ip));
+ settingsPack.set_int(libt::settings_pack::proxy_port, proxyConfig.port);
+ if (proxyManager->isAuthenticationRequired()) {
+ settingsPack.set_str(libt::settings_pack::proxy_username, Utils::String::toStdString(proxyConfig.username));
+ settingsPack.set_str(libt::settings_pack::proxy_password, Utils::String::toStdString(proxyConfig.password));
+ }
+ settingsPack.set_bool(libt::settings_pack::proxy_peer_connections, isProxyPeerConnectionsEnabled());
+ }
+
+ switch (proxyConfig.type) {
+ case Net::ProxyType::HTTP:
+ settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::http);
+ break;
+ case Net::ProxyType::HTTP_PW:
+ settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::http_pw);
+ break;
+ case Net::ProxyType::SOCKS4:
+ settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::socks4);
+ break;
+ case Net::ProxyType::SOCKS5:
+ settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::socks5);
+ break;
+ case Net::ProxyType::SOCKS5_PW:
+ settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::socks5_pw);
+ break;
+ default:
+ settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::none);
+ }
+
+ m_useProxy = (proxyConfig.type != Net::ProxyType::None);
+ }
+ settingsPack.set_bool(libt::settings_pack::force_proxy, m_useProxy ? isForceProxyEnabled() : false);
+
+ const bool announceToAll = announceToAllTrackers();
+ settingsPack.set_bool(libt::settings_pack::announce_to_all_trackers, announceToAll);
+ settingsPack.set_bool(libt::settings_pack::announce_to_all_tiers, announceToAll);
+
+ const int cacheSize = diskCacheSize();
+ settingsPack.set_int(libt::settings_pack::cache_size, (cacheSize > 0) ? cacheSize * 64 : -1);
+ settingsPack.set_int(libt::settings_pack::cache_expiry, diskCacheTTL());
+ qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
+
+ libt::settings_pack::io_buffer_mode_t mode = useOSCache() ? libt::settings_pack::enable_os_cache
+ : libt::settings_pack::disable_os_cache;
+ settingsPack.set_int(libt::settings_pack::disk_io_read_mode, mode);
+ settingsPack.set_int(libt::settings_pack::disk_io_write_mode, mode);
+
+ settingsPack.set_bool(libt::settings_pack::anonymous_mode, isAnonymousModeEnabled());
+
+ // Queueing System
+ if (isQueueingSystemEnabled()) {
+ adjustLimits(settingsPack);
+
+ settingsPack.set_int(libt::settings_pack::active_seeds, maxActiveUploads());
+ settingsPack.set_bool(libt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing());
+ }
+ else {
+ settingsPack.set_int(libt::settings_pack::active_downloads, -1);
+ settingsPack.set_int(libt::settings_pack::active_seeds, -1);
+ settingsPack.set_int(libt::settings_pack::active_limit, -1);
+ }
+ settingsPack.set_int(libt::settings_pack::active_tracker_limit, -1);
+ settingsPack.set_int(libt::settings_pack::active_dht_limit, -1);
+ settingsPack.set_int(libt::settings_pack::active_lsd_limit, -1);
+
+ // Outgoing ports
+ settingsPack.set_int(libt::settings_pack::outgoing_port, outgoingPortsMin());
+ settingsPack.set_int(libt::settings_pack::num_outgoing_ports, outgoingPortsMax() - outgoingPortsMin() + 1);
+
+ // Ignore limits on LAN
+ settingsPack.set_bool(libt::settings_pack::ignore_limits_on_local_network, ignoreLimitsOnLAN());
+ // Include overhead in transfer limits
+ settingsPack.set_bool(libt::settings_pack::rate_limit_ip_overhead, includeOverheadInLimits());
+ // IP address to announce to trackers
+ settingsPack.set_str(libt::settings_pack::announce_ip, Utils::String::toStdString(announceIP()));
+ // Super seeding
+ settingsPack.set_bool(libt::settings_pack::strict_super_seeding, isSuperSeedingEnabled());
+ // * Max Half-open connections
+ settingsPack.set_int(libt::settings_pack::half_open_limit, maxHalfOpenConnections());
+ // * Max connections limit
+ settingsPack.set_int(libt::settings_pack::connections_limit, maxConnections());
+ // * Global max upload slots
+ settingsPack.set_int(libt::settings_pack::unchoke_slots_limit, maxUploads());
+ // uTP
+ settingsPack.set_bool(libt::settings_pack::enable_incoming_utp, isUTPEnabled());
+ settingsPack.set_bool(libt::settings_pack::enable_outgoing_utp, isUTPEnabled());
+ // uTP rate limiting
+ settingsPack.set_bool(libt::settings_pack::rate_limit_utp, isUTPRateLimited());
+ settingsPack.set_int(libt::settings_pack::mixed_mode_algorithm, isUTPRateLimited()
+ ? libt::settings_pack::prefer_tcp
+ : libt::settings_pack::peer_proportional);
+
+ settingsPack.set_bool(libt::settings_pack::apply_ip_filter_to_trackers, isTrackerFilteringEnabled());
+
+ settingsPack.set_bool(libt::settings_pack::enable_dht, isDHTEnabled());
+ if (isDHTEnabled())
+ settingsPack.set_str(libt::settings_pack::dht_bootstrap_nodes, "dht.libtorrent.org:25401,router.bittorrent.com:6881,router.utorrent.com:6881,dht.transmissionbt.com:6881,dht.aelitis.com:6881");
+ settingsPack.set_bool(libt::settings_pack::enable_lsd, isLSDEnabled());
+}
+
+#else
+
+void Session::adjustLimits(libt::session_settings &sessionSettings)
+{
+ //Internally increase the queue limits to ensure that the magnet is started
+ int maxDownloads = maxActiveDownloads();
+ int maxActive = maxActiveTorrents();
+
+ sessionSettings.active_downloads = maxDownloads > -1 ? maxDownloads + m_extraLimit : maxDownloads;
+ sessionSettings.active_limit = maxActive > -1 ? maxActive + m_extraLimit : maxActive;
+}
+
+void Session::configure(libtorrent::session_settings &sessionSettings)
+{
+ const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled();
+ sessionSettings.download_rate_limit = altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit();
+ sessionSettings.upload_rate_limit = altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit();
+
+ // The most secure, rc4 only so that all streams are encrypted
+ libt::pe_settings encryptionSettings;
+ encryptionSettings.allowed_enc_level = libt::pe_settings::rc4;
+ encryptionSettings.prefer_rc4 = true;
+ switch (encryption()) {
+ case 0: //Enabled
+ encryptionSettings.out_enc_policy = libt::pe_settings::enabled;
+ encryptionSettings.in_enc_policy = libt::pe_settings::enabled;
+ break;
+ case 1: // Forced
+ encryptionSettings.out_enc_policy = libt::pe_settings::forced;
+ encryptionSettings.in_enc_policy = libt::pe_settings::forced;
+ break;
+ default: // Disabled
+ encryptionSettings.out_enc_policy = libt::pe_settings::disabled;
+ encryptionSettings.in_enc_policy = libt::pe_settings::disabled;
+ }
+ m_nativeSession->set_pe_settings(encryptionSettings);
+
+ auto proxyManager = Net::ProxyConfigurationManager::instance();
+ Net::ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
+ if (m_useProxy || (proxyConfig.type != Net::ProxyType::None)) {
+ libt::proxy_settings proxySettings;
+ if (proxyConfig.type != Net::ProxyType::None) {
+ proxySettings.hostname = Utils::String::toStdString(proxyConfig.ip);
+ proxySettings.port = proxyConfig.port;
+ if (proxyManager->isAuthenticationRequired()) {
+ proxySettings.username = Utils::String::toStdString(proxyConfig.username);
+ proxySettings.password = Utils::String::toStdString(proxyConfig.password);
+ }
+ proxySettings.proxy_peer_connections = isProxyPeerConnectionsEnabled();
+ }
+
+ switch (proxyConfig.type) {
+ case Net::ProxyType::HTTP:
+ proxySettings.type = libt::proxy_settings::http;
+ break;
+ case Net::ProxyType::HTTP_PW:
+ proxySettings.type = libt::proxy_settings::http_pw;
+ break;
+ case Net::ProxyType::SOCKS4:
+ proxySettings.type = libt::proxy_settings::socks4;
+ break;
+ case Net::ProxyType::SOCKS5:
+ proxySettings.type = libt::proxy_settings::socks5;
+ break;
+ case Net::ProxyType::SOCKS5_PW:
+ proxySettings.type = libt::proxy_settings::socks5_pw;
+ break;
+ default:
+ proxySettings.type = libt::proxy_settings::none;
+ }
+
+ m_nativeSession->set_proxy(proxySettings);
+ m_useProxy = (proxyConfig.type != Net::ProxyType::None);
+ }
+ sessionSettings.force_proxy = m_useProxy ? isForceProxyEnabled() : false;
+
+ bool announceToAll = announceToAllTrackers();
+ sessionSettings.announce_to_all_trackers = announceToAll;
+ sessionSettings.announce_to_all_tiers = announceToAll;
+ int cacheSize = diskCacheSize();
+ sessionSettings.cache_size = (cacheSize > 0) ? cacheSize * 64 : -1;
+ sessionSettings.cache_expiry = diskCacheTTL();
+ qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
+ libt::session_settings::io_buffer_mode_t mode = useOSCache() ? libt::session_settings::enable_os_cache
+ : libt::session_settings::disable_os_cache;
sessionSettings.disk_io_read_mode = mode;
sessionSettings.disk_io_write_mode = mode;
- m_resumeDataTimer->setInterval(pref->saveResumeDataInterval() * 60 * 1000);
-
- sessionSettings.anonymous_mode = pref->isAnonymousModeEnabled();
- if (sessionSettings.anonymous_mode)
- logger->addMessage(tr("Anonymous mode [ON]"), Log::INFO);
- else
- logger->addMessage(tr("Anonymous mode [OFF]"), Log::INFO);
+ sessionSettings.anonymous_mode = isAnonymousModeEnabled();
// Queueing System
- m_queueingEnabled = pref->isQueueingSystemEnabled();
- if (m_queueingEnabled) {
+ if (isQueueingSystemEnabled()) {
adjustLimits(sessionSettings);
- sessionSettings.active_seeds = pref->getMaxActiveUploads();
- sessionSettings.dont_count_slow_torrents = pref->ignoreSlowTorrentsForQueueing();
+ sessionSettings.active_seeds = maxActiveUploads();
+ sessionSettings.dont_count_slow_torrents = ignoreSlowTorrentsForQueueing();
}
else {
sessionSettings.active_downloads = -1;
@@ -621,279 +1199,58 @@ void Session::setSessionSettings()
sessionSettings.active_lsd_limit = -1;
// Outgoing ports
-#if LIBTORRENT_VERSION_NUM < 10100
- sessionSettings.outgoing_ports = std::make_pair(pref->outgoingPortsMin(), pref->outgoingPortsMax());
-#else
- sessionSettings.outgoing_port = pref->outgoingPortsMin();
- sessionSettings.num_outgoing_ports = pref->outgoingPortsMax() - pref->outgoingPortsMin();
-#endif
+ sessionSettings.outgoing_ports = std::make_pair(outgoingPortsMin(), outgoingPortsMax());
+
// Ignore limits on LAN
- qDebug() << "Ignore limits on LAN" << pref->getIgnoreLimitsOnLAN();
- sessionSettings.ignore_limits_on_local_network = pref->getIgnoreLimitsOnLAN();
+ sessionSettings.ignore_limits_on_local_network = ignoreLimitsOnLAN();
// Include overhead in transfer limits
- sessionSettings.rate_limit_ip_overhead = pref->includeOverheadInLimits();
+ sessionSettings.rate_limit_ip_overhead = includeOverheadInLimits();
// IP address to announce to trackers
- sessionSettings.announce_ip = Utils::String::toStdString(pref->getNetworkAddress());
+ sessionSettings.announce_ip = Utils::String::toStdString(announceIP());
// Super seeding
- sessionSettings.strict_super_seeding = pref->isSuperSeedingEnabled();
+ sessionSettings.strict_super_seeding = isSuperSeedingEnabled();
// * Max Half-open connections
- sessionSettings.half_open_limit = pref->getMaxHalfOpenConnections();
+ sessionSettings.half_open_limit = maxHalfOpenConnections();
// * Max connections limit
- sessionSettings.connections_limit = pref->getMaxConnecs();
+ sessionSettings.connections_limit = maxConnections();
// * Global max upload slots
- sessionSettings.unchoke_slots_limit = pref->getMaxUploads();
+ sessionSettings.unchoke_slots_limit = maxUploads();
// uTP
- sessionSettings.enable_incoming_utp = pref->isuTPEnabled();
- sessionSettings.enable_outgoing_utp = pref->isuTPEnabled();
+ sessionSettings.enable_incoming_utp = isUTPEnabled();
+ sessionSettings.enable_outgoing_utp = isUTPEnabled();
// uTP rate limiting
- sessionSettings.rate_limit_utp = pref->isuTPRateLimited();
- if (sessionSettings.rate_limit_utp)
- sessionSettings.mixed_mode_algorithm = libt::session_settings::prefer_tcp;
+ sessionSettings.rate_limit_utp = isUTPRateLimited();
+ sessionSettings.mixed_mode_algorithm = isUTPRateLimited()
+ ? libt::session_settings::prefer_tcp
+ : libt::session_settings::peer_proportional;
+
+ sessionSettings.apply_ip_filter_to_trackers = isTrackerFilteringEnabled();
+
+ if (isDHTEnabled()) {
+ // Add first the routers and then start DHT.
+ m_nativeSession->add_dht_router(std::make_pair(std::string("dht.libtorrent.org"), 25401));
+ m_nativeSession->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
+ m_nativeSession->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
+ m_nativeSession->add_dht_router(std::make_pair(std::string("dht.transmissionbt.com"), 6881));
+ m_nativeSession->add_dht_router(std::make_pair(std::string("dht.aelitis.com"), 6881)); // Vuze
+ m_nativeSession->start_dht();
+ }
+ else {
+ m_nativeSession->stop_dht();
+ }
+
+ if (isLSDEnabled())
+ m_nativeSession->start_lsd();
else
- sessionSettings.mixed_mode_algorithm = libt::session_settings::peer_proportional;
- sessionSettings.connection_speed = 20; //default is 10
- if (pref->isProxyEnabled())
- sessionSettings.force_proxy = pref->getForceProxy();
- else
- sessionSettings.force_proxy = false;
- sessionSettings.no_connect_privileged_ports = false;
- sessionSettings.seed_choking_algorithm = libt::session_settings::fastest_upload;
- qDebug() << "Set session settings";
- m_nativeSession->set_settings(sessionSettings);
+ m_nativeSession->stop_lsd();
}
+#endif
-void Session::adjustLimits()
+void Session::enableTracker(bool enable)
{
- if (m_queueingEnabled) {
- libt::session_settings sessionSettings(m_nativeSession->settings());
- adjustLimits(sessionSettings);
- m_nativeSession->set_settings(sessionSettings);
- }
-}
+ Logger *const logger = Logger::instance();
-void Session::adjustLimits(libt::session_settings &sessionSettings)
-{
- Preferences *const pref = Preferences::instance();
-
- //Internally increase the queue limits to ensure that the magnet is started
- int max_downloading = pref->getMaxActiveDownloads();
- int max_active = pref->getMaxActiveTorrents();
-
- if (max_downloading > -1)
- sessionSettings.active_downloads = max_downloading + m_extraLimit;
- else
- sessionSettings.active_downloads = max_downloading;
-
- if (max_active > -1)
- sessionSettings.active_limit = max_active + m_extraLimit;
- else
- sessionSettings.active_limit = max_active;
-}
-
-// Set BitTorrent session configuration
-void Session::configure()
-{
- qDebug("Configuring session");
- Preferences* const pref = Preferences::instance();
-
- const unsigned short oldListenPort = m_nativeSession->listen_port();
- const unsigned short newListenPort = pref->getSessionPort();
- if (oldListenPort != newListenPort) {
- qDebug("Session port changes in program preferences: %d -> %d", oldListenPort, newListenPort);
- setListeningPort();
- }
-
- uint newRefreshInterval = pref->getRefreshInterval();
- if (newRefreshInterval != m_refreshInterval) {
- m_refreshInterval = newRefreshInterval;
- m_refreshTimer->setInterval(m_refreshInterval);
- }
-
- setAppendExtension(pref->useIncompleteFilesExtension());
- preAllocateAllFiles(pref->preAllocateAllFiles());
-
- // * Torrent export directory
- const bool torrentExportEnabled = pref->isTorrentExportEnabled();
- if (m_torrentExportEnabled != torrentExportEnabled) {
- m_torrentExportEnabled = torrentExportEnabled;
- if (m_torrentExportEnabled) {
- qDebug("Torrent export is enabled, exporting the current torrents");
- for (auto torrent: m_torrents)
- exportTorrentFile(torrent);
- }
- }
-
- // * Finished Torrent export directory
- const bool finishedTorrentExportEnabled = pref->isFinishedTorrentExportEnabled();
- if (m_finishedTorrentExportEnabled != finishedTorrentExportEnabled)
- m_finishedTorrentExportEnabled = finishedTorrentExportEnabled;
-
- // Connection
- // * Global download limit
- const bool alternative_speeds = pref->isAltBandwidthEnabled();
- int down_limit;
- if (alternative_speeds)
- down_limit = pref->getAltGlobalDownloadLimit();
- else
- down_limit = pref->getGlobalDownloadLimit();
- if (down_limit <= 0) {
- // Download limit disabled
- setDownloadRateLimit(-1);
- }
- else {
- // Enabled
- setDownloadRateLimit(down_limit*1024);
- }
- int up_limit;
- if (alternative_speeds)
- up_limit = pref->getAltGlobalUploadLimit();
- else
- up_limit = pref->getGlobalUploadLimit();
- // * Global Upload limit
- if (up_limit <= 0) {
- // Upload limit disabled
- setUploadRateLimit(-1);
- }
- else {
- // Enabled
- setUploadRateLimit(up_limit*1024);
- }
-
- if (pref->isSchedulerEnabled()) {
- if (!m_bwScheduler) {
- m_bwScheduler = new BandwidthScheduler(this);
- connect(m_bwScheduler.data(), SIGNAL(switchToAlternativeMode(bool)), this, SLOT(switchToAlternativeMode(bool)));
- }
- m_bwScheduler->start();
- }
- else {
- delete m_bwScheduler;
- }
-
- Logger* const logger = Logger::instance();
-
- // * Session settings
- setSessionSettings();
-
- // Bittorrent
- // * Max connections per torrent limit
- setMaxConnectionsPerTorrent(pref->getMaxConnecsPerTorrent());
- // * Max uploads per torrent limit
- setMaxUploadsPerTorrent(pref->getMaxUploadsPerTorrent());
- // * DHT
- enableDHT(pref->isDHTEnabled());
-
- // * PeX
- if (m_PeXEnabled)
- logger->addMessage(tr("PeX support [ON]"), Log::INFO);
- else
- logger->addMessage(tr("PeX support [OFF]"), Log::CRITICAL);
- if (m_PeXEnabled != pref->isPeXEnabled())
- logger->addMessage(tr("Restart is required to toggle PeX support"), Log::CRITICAL);
-
- // * LSD
- if (pref->isLSDEnabled()) {
- enableLSD(true);
- logger->addMessage(tr("Local Peer Discovery support [ON]"), Log::INFO);
- }
- else {
- enableLSD(false);
- logger->addMessage(tr("Local Peer Discovery support [OFF]"), Log::INFO);
- }
-
- // * Encryption
- const int encryptionState = pref->getEncryptionSetting();
- // The most secure, rc4 only so that all streams and encrypted
- libt::pe_settings encryptionSettings;
- encryptionSettings.allowed_enc_level = libt::pe_settings::rc4;
- encryptionSettings.prefer_rc4 = true;
- switch(encryptionState) {
- case 0: //Enabled
- encryptionSettings.out_enc_policy = libt::pe_settings::enabled;
- encryptionSettings.in_enc_policy = libt::pe_settings::enabled;
- logger->addMessage(tr("Encryption support [ON]"), Log::INFO);
- break;
- case 1: // Forced
- encryptionSettings.out_enc_policy = libt::pe_settings::forced;
- encryptionSettings.in_enc_policy = libt::pe_settings::forced;
- logger->addMessage(tr("Encryption support [FORCED]"), Log::INFO);
- break;
- default: // Disabled
- encryptionSettings.out_enc_policy = libt::pe_settings::disabled;
- encryptionSettings.in_enc_policy = libt::pe_settings::disabled;
- logger->addMessage(tr("Encryption support [OFF]"), Log::INFO);
- }
-
- qDebug("Applying encryption settings");
- m_nativeSession->set_pe_settings(encryptionSettings);
-
- // * Add trackers
- m_additionalTrackers.clear();
- if (pref->isAddTrackersEnabled()) {
- foreach (QString tracker, pref->getTrackersList().split("\n")) {
- tracker = tracker.trimmed();
- if (!tracker.isEmpty())
- m_additionalTrackers << tracker;
- }
- }
-
- // * Maximum ratio
- setGlobalMaxRatio(pref->getGlobalMaxRatio());
-
- // Ip Filter
- if (pref->isFilteringEnabled())
- enableIPFilter(pref->getFilter());
- else
- disableIPFilter();
- // Add the banned IPs after the possibly disabled IPFilter
- // which creates an empty filter and overrides all previously
- // applied bans.
- FilterParserThread::processFilterList(m_nativeSession, pref->bannedIPs());
-
- // * Proxy settings
- libt::proxy_settings proxySettings;
- if (pref->isProxyEnabled()) {
- qDebug("Enabling P2P proxy");
- proxySettings.hostname = Utils::String::toStdString(pref->getProxyIp());
- qDebug("hostname is %s", proxySettings.hostname.c_str());
- proxySettings.port = pref->getProxyPort();
- qDebug("port is %d", proxySettings.port);
- if (pref->isProxyAuthEnabled()) {
- proxySettings.username = Utils::String::toStdString(pref->getProxyUsername());
- proxySettings.password = Utils::String::toStdString(pref->getProxyPassword());
- qDebug("username is %s", proxySettings.username.c_str());
- qDebug("password is %s", proxySettings.password.c_str());
- }
- }
-
- switch(pref->getProxyType()) {
- case Proxy::HTTP:
- qDebug("type: http");
- proxySettings.type = libt::proxy_settings::http;
- break;
- case Proxy::HTTP_PW:
- qDebug("type: http_pw");
- proxySettings.type = libt::proxy_settings::http_pw;
- break;
- case Proxy::SOCKS4:
- proxySettings.type = libt::proxy_settings::socks4;
- break;
- case Proxy::SOCKS5:
- qDebug("type: socks5");
- proxySettings.type = libt::proxy_settings::socks5;
- break;
- case Proxy::SOCKS5_PW:
- qDebug("type: socks5_pw");
- proxySettings.type = libt::proxy_settings::socks5_pw;
- break;
- default:
- proxySettings.type = libt::proxy_settings::none;
- }
-
- setProxySettings(proxySettings);
-
- // Tracker
- if (pref->isTrackerEnabled()) {
+ if (enable) {
if (!m_tracker)
m_tracker = new Tracker(this);
@@ -903,20 +1260,28 @@ void Session::configure()
logger->addMessage(tr("Failed to start the embedded tracker!"), Log::CRITICAL);
}
else {
- logger->addMessage(tr("Embedded Tracker [OFF]"));
+ logger->addMessage(tr("Embedded Tracker [OFF]"), Log::INFO);
if (m_tracker)
delete m_tracker;
}
-
- qDebug("Session configured");
}
-void Session::preAllocateAllFiles(bool b)
+void Session::enableBandwidthScheduler()
{
- const bool change = (m_preAllocateAll != b);
- if (change) {
- qDebug("PreAllocateAll changed, reloading all torrents!");
- m_preAllocateAll = b;
+ if (!m_bwScheduler) {
+ m_bwScheduler = new BandwidthScheduler(this);
+ connect(m_bwScheduler.data(), SIGNAL(switchToAlternativeMode(bool)), this, SLOT(switchToAlternativeMode(bool)));
+ }
+ m_bwScheduler->start();
+}
+
+void Session::populateAdditionalTrackers()
+{
+ m_additionalTrackerList.clear();
+ foreach (QString tracker, additionalTrackers().split("\n")) {
+ tracker = tracker.trimmed();
+ if (!tracker.isEmpty())
+ m_additionalTrackerList << tracker;
}
}
@@ -924,23 +1289,24 @@ void Session::processBigRatios()
{
qDebug("Process big ratios...");
+ qreal globalMaxRatio = this->globalMaxRatio();
foreach (TorrentHandle *const torrent, m_torrents) {
- if (torrent->isSeed() && (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT)) {
+ if (torrent->isSeed()
+ && (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT)
+ && !torrent->isForced()) {
const qreal ratio = torrent->realRatio();
qreal ratioLimit = torrent->ratioLimit();
if (ratioLimit == TorrentHandle::USE_GLOBAL_RATIO) {
// If Global Max Ratio is really set...
- if (m_globalMaxRatio >= 0)
- ratioLimit = m_globalMaxRatio;
- else
- continue;
+ ratioLimit = globalMaxRatio;
+ if (ratioLimit < 0) continue;
}
qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit);
Q_ASSERT(ratioLimit >= 0.f);
if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) {
Logger* const logger = Logger::instance();
- if (m_maxRatioAction == Remove) {
+ if (maxRatioAction() == Remove) {
logger->addMessage(tr("'%1' reached the maximum ratio you set. Removing...").arg(torrent->name()));
deleteTorrent(torrent->hash());
}
@@ -979,18 +1345,6 @@ void Session::handleDownloadFinished(const QString &url, const QString &filePath
Utils::Fs::forceRemove(filePath); // remove temporary file
}
-void Session::changeSpeedLimitMode(bool alternative)
-{
- Preferences* const pref = Preferences::instance();
- // Stop the scheduler when the user has manually changed the bandwidth mode
- if (pref->isSchedulerEnabled()) {
- pref->setSchedulerEnabled(false);
- delete m_bwScheduler;
- }
-
- changeSpeedLimitMode_impl(alternative);
-}
-
// Return the torrent handle, given its hash
TorrentHandle *Session::findTorrent(const InfoHash &hash) const
{
@@ -1017,8 +1371,19 @@ bool Session::hasUnfinishedTorrents() const
void Session::banIP(const QString &ip)
{
- FilterParserThread::processFilterList(m_nativeSession, QStringList(ip));
- Preferences::instance()->banIP(ip);
+ QStringList bannedIPs = m_bannedIPs;
+ if (!bannedIPs.contains(ip)) {
+ libt::ip_filter filter = m_nativeSession->get_ip_filter();
+ boost::system::error_code ec;
+ libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
+ Q_ASSERT(!ec);
+ if (ec) return;
+ filter.add_rule(addr, addr, libt::ip_filter::blocked);
+ m_nativeSession->set_ip_filter(filter);
+
+ bannedIPs << ip;
+ m_bannedIPs = bannedIPs;
+ }
}
// Delete a torrent from the session, given its hash
@@ -1210,6 +1575,8 @@ bool Session::addTorrent(QString source, const AddTorrentParams ¶ms)
m_downloadedTorrents[handler->url()] = params;
}
else {
+ TorrentFileGuard guard(source);
+ guard.markAsAddedToSession();
return addTorrent_impl(params, MagnetUri(), TorrentInfo::loadFromFile(source));
}
@@ -1225,11 +1592,11 @@ bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams
// Add a torrent to the BitTorrent session
bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
- const TorrentInfo &torrentInfo, const QByteArray &fastresumeData)
+ TorrentInfo torrentInfo, const QByteArray &fastresumeData)
{
addData.savePath = normalizeSavePath(
addData.savePath,
- ((!addData.resumed && isASMDisabledByDefault()) ? m_defaultSavePath : ""));
+ ((!addData.resumed && isAutoTMMDisabledByDefault()) ? defaultSavePath() : ""));
if (!addData.category.isEmpty()) {
if (!m_categories.contains(addData.category) && !addCategory(addData.category)) {
@@ -1243,6 +1610,12 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
std::vector buf(fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size());
std::vector filePriorities;
+ QString savePath;
+ if (addData.savePath.isEmpty()) // using Automatic mode
+ savePath = categorySavePath(addData.category);
+ else // using Manual mode
+ savePath = addData.savePath;
+
bool fromMagnetUri = magnetUri.isValid();
if (fromMagnetUri) {
hash = magnetUri.hash();
@@ -1271,6 +1644,8 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
}
else if (torrentInfo.isValid()) {
// Metadata
+ if (!addData.resumed && !addData.hasSeedStatus)
+ findIncompleteFiles(torrentInfo, savePath);
p.ti = torrentInfo.nativeInfo();
hash = torrentInfo.hash();
}
@@ -1309,7 +1684,7 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
qDebug(" -> Hash: %s", qPrintable(hash));
// Preallocation mode
- if (m_preAllocateAll)
+ if (isPreallocationEnabled())
p.storage_mode = libt::storage_mode_allocate;
else
p.storage_mode = libt::storage_mode_sparse;
@@ -1326,23 +1701,9 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
p.flags &= ~libt::add_torrent_params::flag_seed_mode;
// Limits
- Preferences *const pref = Preferences::instance();
- p.max_connections = pref->getMaxConnecsPerTorrent();
- p.max_uploads = pref->getMaxUploadsPerTorrent();
-
- QString savePath;
- // Set actual save path (e.g. temporary folder)
- if (isTempPathEnabled() && !addData.disableTempPath && !addData.hasSeedStatus)
- savePath = m_tempPath;
- else if (addData.savePath.isEmpty()) // using Advanced mode
- savePath = categorySavePath(addData.category);
- else // using Simple mode
- savePath = addData.savePath;
-
+ p.max_connections = maxConnectionsPerTorrent();
+ p.max_uploads = maxUploadsPerTorrent();
p.save_path = Utils::String::toStdString(Utils::Fs::toNativePath(savePath));
- // Check if save path exists, creating it otherwise
- if (!QDir(savePath).exists())
- QDir().mkpath(savePath);
m_addingTorrents.insert(hash, addData);
// Adding torrent to BitTorrent session
@@ -1350,6 +1711,53 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
return true;
}
+bool Session::findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const
+{
+ auto findInDir = [](const QString &dirPath, TorrentInfo &torrentInfo) -> bool
+ {
+ bool found = false;
+ if (torrentInfo.filesCount() == 1) {
+ const QString filePath = dirPath + torrentInfo.filePath(0);
+ if (QFile(filePath).exists()) {
+ found = true;
+ }
+ else if (QFile(filePath + QB_EXT).exists()) {
+ found = true;
+ torrentInfo.renameFile(0, torrentInfo.filePath(0) + QB_EXT);
+ }
+ }
+ else {
+ QSet allFiles;
+ int dirPathSize = dirPath.size();
+ foreach (const QString &file, findAllFiles(dirPath + torrentInfo.name()))
+ allFiles << file.mid(dirPathSize);
+ for (int i = 0; i < torrentInfo.filesCount(); ++i) {
+ QString filePath = torrentInfo.filePath(i);
+ if (allFiles.contains(filePath)) {
+ found = true;
+ }
+ else {
+ filePath += QB_EXT;
+ if (allFiles.contains(filePath)) {
+ found = true;
+ torrentInfo.renameFile(i, filePath);
+ }
+ }
+ }
+ }
+
+ return found;
+ };
+
+ bool found = findInDir(savePath, torrentInfo);
+ if (!found && isTempPathEnabled()) {
+ savePath = torrentTempPath(torrentInfo.hash());
+ found = findInDir(savePath, torrentInfo);
+ }
+
+ return found;
+}
+
// Add a torrent to the BitTorrent session in hidden mode
// and force it to load its metadata
bool Session::loadMetadata(const MagnetUri &magnetUri)
@@ -1373,21 +1781,17 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
// Flags
// Preallocation mode
- if (m_preAllocateAll)
+ if (isPreallocationEnabled())
p.storage_mode = libt::storage_mode_allocate;
else
p.storage_mode = libt::storage_mode_sparse;
- Preferences *const pref = Preferences::instance();
// Limits
- p.max_connections = pref->getMaxConnecsPerTorrent();
- p.max_uploads = pref->getMaxUploadsPerTorrent();
+ p.max_connections = maxConnectionsPerTorrent();
+ p.max_uploads = maxUploadsPerTorrent();
QString savePath = QString("%1/%2").arg(QDir::tempPath()).arg(hash);
p.save_path = Utils::String::toStdString(Utils::Fs::toNativePath(savePath));
- // Check if save path exists, creating it otherwise
- if (!QDir(savePath).exists())
- QDir().mkpath(savePath);
// Forced start
p.flags &= ~libt::add_torrent_params::flag_paused;
@@ -1410,14 +1814,14 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder)
{
- Q_ASSERT(((folder == TorrentExportFolder::Regular) && m_torrentExportEnabled) ||
- ((folder == TorrentExportFolder::Finished) && m_finishedTorrentExportEnabled));
+ Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) ||
+ ((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
QString torrentFilename = QString("%1.torrent").arg(torrent->hash());
QString torrentExportFilename = QString("%1.torrent").arg(validName);
QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename);
- QDir exportPath(folder == TorrentExportFolder::Regular ? Preferences::instance()->getTorrentExportDir() : Preferences::instance()->getFinishedTorrentExportDir());
+ QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) {
QString newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
int counter = 0;
@@ -1432,117 +1836,14 @@ void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolde
}
}
-void Session::setMaxConnectionsPerTorrent(int max)
-{
- qDebug() << Q_FUNC_INFO << max;
-
- // Apply this to all session torrents
- std::vector handles = m_nativeSession->get_torrents();
- std::vector::const_iterator it = handles.begin();
- std::vector::const_iterator itend = handles.end();
- for ( ; it != itend; ++it) {
- if (!it->is_valid()) continue;
- try {
- it->set_max_connections(max);
- }
- catch(std::exception) {}
- }
-}
-
-void Session::setMaxUploadsPerTorrent(int max)
-{
- qDebug() << Q_FUNC_INFO << max;
-
- // Apply this to all session torrents
- std::vector handles = m_nativeSession->get_torrents();
- std::vector::const_iterator it = handles.begin();
- std::vector::const_iterator itend = handles.end();
- for ( ; it != itend; ++it) {
- if (!it->is_valid()) continue;
- try {
- it->set_max_uploads(max);
- }
- catch(std::exception) {}
- }
-}
-
-void Session::enableLSD(bool enable)
-{
- if (enable) {
- if (!m_LSDEnabled) {
- qDebug("Enabling Local Peer Discovery");
- m_nativeSession->start_lsd();
- m_LSDEnabled = true;
- }
- }
- else {
- if (m_LSDEnabled) {
- qDebug("Disabling Local Peer Discovery");
- m_nativeSession->stop_lsd();
- m_LSDEnabled = false;
- }
- }
-}
-
-// Enable DHT
-void Session::enableDHT(bool enable)
-{
- Logger* const logger = Logger::instance();
-
- if (enable) {
- if (!m_DHTEnabled) {
- try {
- qDebug() << "Starting DHT...";
- Q_ASSERT(!m_nativeSession->is_dht_running());
- m_nativeSession->start_dht();
- m_nativeSession->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
- m_nativeSession->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
- m_nativeSession->add_dht_router(std::make_pair(std::string("dht.transmissionbt.com"), 6881));
- m_nativeSession->add_dht_router(std::make_pair(std::string("dht.aelitis.com"), 6881)); // Vuze
- m_DHTEnabled = true;
- logger->addMessage(tr("DHT support [ON]"), Log::INFO);
- qDebug("DHT enabled");
- }
- catch(std::exception &e) {
- qDebug("Could not enable DHT, reason: %s", e.what());
- logger->addMessage(tr("DHT support [OFF]. Reason: %1").arg(Utils::String::fromStdString(e.what())), Log::CRITICAL);
- }
- }
- }
- else {
- if (m_DHTEnabled) {
- m_DHTEnabled = false;
- m_nativeSession->stop_dht();
- logger->addMessage(tr("DHT support [OFF]"), Log::INFO);
- qDebug("DHT disabled");
- }
- }
-}
-
void Session::changeSpeedLimitMode_impl(bool alternative)
{
qDebug() << Q_FUNC_INFO << alternative;
- Preferences* const pref = Preferences::instance();
+ if (alternative == isAltGlobalSpeedLimitEnabled()) return;
// Save new state to remember it on startup
- pref->setAltBandwidthEnabled(alternative);
-
- // Apply settings to the bittorrent session
- int downLimit = alternative ? pref->getAltGlobalDownloadLimit() : pref->getGlobalDownloadLimit();
- if (downLimit <= 0)
- downLimit = -1;
- else
- downLimit *= 1024;
- setDownloadRateLimit(downLimit);
-
- // Upload rate
- int upLimit = alternative ? pref->getAltGlobalUploadLimit() : pref->getGlobalUploadLimit();
- if (upLimit <= 0)
- upLimit = -1;
- else
- upLimit *= 1024;
- setUploadRateLimit(upLimit);
-
+ m_isAltGlobalSpeedLimitEnabled = alternative;
+ configureDeferred();
// Notify
emit speedLimitModeChanged(alternative);
}
@@ -1571,7 +1872,7 @@ void Session::saveResumeData()
generateResumeData(true);
while (m_numResumeData > 0) {
- QVector alerts;
+ std::vector alerts;
getPendingAlerts(alerts, 30 * 1000);
if (alerts.empty()) {
std::cerr << " aborting with " << m_numResumeData
@@ -1580,7 +1881,7 @@ void Session::saveResumeData()
break;
}
- foreach (libt::alert *const a, alerts) {
+ for (const auto a: alerts) {
switch (a->type()) {
case libt::save_resume_data_failed_alert::alert_type:
case libt::save_resume_data_alert::alert_type:
@@ -1589,8 +1890,9 @@ void Session::saveResumeData()
torrent->handleAlert(a);
break;
}
-
+#if LIBTORRENT_VERSION_NUM < 10100
delete a;
+#endif
}
}
}
@@ -1598,14 +1900,13 @@ void Session::saveResumeData()
void Session::setDefaultSavePath(QString path)
{
path = normalizeSavePath(path);
- if (m_defaultSavePath == path) return;
+ if (path == m_defaultSavePath) return;
m_defaultSavePath = path;
- m_settings->storeValue(KEY_DEFAULTSAVEPATH, m_defaultSavePath);
- if (isDisableASMWhenDefaultSavePathChanged())
+ if (isDisableAutoTMMWhenDefaultSavePathChanged())
foreach (TorrentHandle *const torrent, torrents())
- torrent->setASMEnabled(false);
+ torrent->setAutoTMMEnabled(false);
else
foreach (TorrentHandle *const torrent, torrents())
torrent->handleCategorySavePathChanged();
@@ -1613,26 +1914,15 @@ void Session::setDefaultSavePath(QString path)
void Session::setTempPath(QString path)
{
- path = normalizeSavePath(path, m_defaultSavePath + "temp");
- if (m_tempPath == path) return;
+ path = normalizeSavePath(path, defaultSavePath() + "temp/");
+ if (path == m_tempPath) return;
m_tempPath = path;
- m_settings->storeValue(KEY_TEMPPATH, m_tempPath);
foreach (TorrentHandle *const torrent, m_torrents)
torrent->handleTempPathChanged();
}
-void Session::setAppendExtension(bool append)
-{
- if (m_appendExtension != append) {
- m_appendExtension = append;
- // append or remove .!qB extension for incomplete files
- foreach (TorrentHandle *const torrent, m_torrents)
- torrent->handleAppendExtensionToggled();
- }
-}
-
void Session::networkOnlineStateChanged(const bool online)
{
Logger::instance()->addMessage(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
@@ -1640,29 +1930,52 @@ void Session::networkOnlineStateChanged(const bool online)
void Session::networkConfigurationChange(const QNetworkConfiguration& cfg)
{
- const QString configuredInterfaceName = Preferences::instance()->getNetworkInterface();
+ const QString configuredInterfaceName = networkInterface();
// Empty means "Any Interface". In this case libtorrent has binded to 0.0.0.0 so any change to any interface will
// be automatically picked up. Otherwise we would rebinding here to 0.0.0.0 again.
- if (configuredInterfaceName.isEmpty())
- return;
+ if (configuredInterfaceName.isEmpty()) return;
+
const QString changedInterface = cfg.name();
+
+ // workaround for QTBUG-52633: check interface IPs, react only if the IPs have changed
+ // seems to be present only with NetworkManager, hence Q_OS_LINUX
+#if defined Q_OS_LINUX && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // && QT_VERSION <= QT_VERSION_CHECK(5, ?, ?)
+ static QStringList boundIPs = getListeningIPs();
+ const QStringList newBoundIPs = getListeningIPs();
+ if ((configuredInterfaceName == changedInterface) && (boundIPs != newBoundIPs)) {
+ boundIPs = newBoundIPs;
+#else
if (configuredInterfaceName == changedInterface) {
+#endif
Logger::instance()->addMessage(tr("Network configuration of %1 has changed, refreshing session binding", "e.g: Network configuration of tun0 has changed, refreshing session binding").arg(changedInterface), Log::INFO);
- setListeningPort();
+ configureListeningInterface();
}
}
const QStringList Session::getListeningIPs()
{
- Preferences* const pref = Preferences::instance();
Logger* const logger = Logger::instance();
QStringList IPs;
- const QString ifaceName = pref->getNetworkInterface();
- const bool listenIPv6 = pref->getListenIPv6();
+ const QString ifaceName = networkInterface();
+ const QString ifaceAddr = networkInterfaceAddress();
+ const bool listenIPv6 = isIPv6Enabled();
+
+ if (!ifaceAddr.isEmpty()) {
+ QHostAddress addr(ifaceAddr);
+ if (addr.isNull()) {
+ logger->addMessage(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.1568.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL);
+ IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
+ return IPs;
+ }
+ }
if (ifaceName.isEmpty()) {
- IPs.append(QString());
+ if (!ifaceAddr.isEmpty())
+ IPs.append(ifaceAddr);
+ else
+ IPs.append(QString());
+
return IPs;
}
@@ -1688,7 +2001,17 @@ const QStringList Session::getListeningIPs()
if ((!listenIPv6 && (protocol == QAbstractSocket::IPv6Protocol))
|| (listenIPv6 && (protocol == QAbstractSocket::IPv4Protocol)))
continue;
- IPs.append(ipString);
+
+ //If an iface address has been defined only allow ip's that match it to go through
+ if (!ifaceAddr.isEmpty()) {
+ if (ifaceAddr == ipString) {
+ IPs.append(ipString);
+ break;
+ }
+ }
+ else {
+ IPs.append(ipString);
+ }
}
// Make sure there is at least one IP
@@ -1704,14 +2027,15 @@ const QStringList Session::getListeningIPs()
// Set the ports range in which is chosen the port
// the BitTorrent session will listen to
-void Session::setListeningPort()
+void Session::configureListeningInterface()
{
- Preferences* const pref = Preferences::instance();
- const unsigned short port = pref->getSessionPort();
+#if LIBTORRENT_VERSION_NUM < 10100
+ const ushort port = this->port();
qDebug() << Q_FUNC_INFO << port;
+
Logger* const logger = Logger::instance();
- std::pair ports(port, port);
+ std::pair ports(port, port);
libt::error_code ec;
const QStringList IPs = getListeningIPs();
@@ -1732,38 +2056,687 @@ void Session::setListeningPort()
return;
}
}
+#else
+ m_listenInterfaceChanged = true;
+ configureDeferred();
+#endif
}
-// Set download rate limit
-// -1 to disable
-void Session::setDownloadRateLimit(int rate)
+int Session::globalDownloadSpeedLimit() const
{
- qDebug() << Q_FUNC_INFO << rate;
- Q_ASSERT((rate == -1) || (rate >= 0));
- libt::session_settings settings = m_nativeSession->settings();
- settings.download_rate_limit = rate;
- m_nativeSession->set_settings(settings);
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ return m_globalDownloadSpeedLimit * 1024;
}
-// Set upload rate limit
-// -1 to disable
-void Session::setUploadRateLimit(int rate)
+void Session::setGlobalDownloadSpeedLimit(int limit)
{
- qDebug() << Q_FUNC_INFO << rate;
- Q_ASSERT((rate == -1) || (rate >= 0));
- libt::session_settings settings = m_nativeSession->settings();
- settings.upload_rate_limit = rate;
- m_nativeSession->set_settings(settings);
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ limit /= 1024;
+ if (limit < 0) limit = 0;
+ if (limit == globalDownloadSpeedLimit()) return;
+
+ m_globalDownloadSpeedLimit = limit;
+ if (!isAltGlobalSpeedLimitEnabled())
+ configureDeferred();
}
-int Session::downloadRateLimit() const
+int Session::globalUploadSpeedLimit() const
{
- return m_nativeSession->settings().download_rate_limit;
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ return m_globalUploadSpeedLimit * 1024;
}
-int Session::uploadRateLimit() const
+void Session::setGlobalUploadSpeedLimit(int limit)
{
- return m_nativeSession->settings().upload_rate_limit;
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ limit /= 1024;
+ if (limit < 0) limit = 0;
+ if (limit == globalUploadSpeedLimit()) return;
+
+ m_globalUploadSpeedLimit = limit;
+ if (!isAltGlobalSpeedLimitEnabled())
+ configureDeferred();
+}
+
+int Session::altGlobalDownloadSpeedLimit() const
+{
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ return m_altGlobalDownloadSpeedLimit * 1024;
+}
+
+void Session::setAltGlobalDownloadSpeedLimit(int limit)
+{
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ limit /= 1024;
+ if (limit < 0) limit = 0;
+ if (limit == altGlobalDownloadSpeedLimit()) return;
+
+ m_altGlobalDownloadSpeedLimit = limit;
+ if (isAltGlobalSpeedLimitEnabled())
+ configureDeferred();
+}
+
+int Session::altGlobalUploadSpeedLimit() const
+{
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ return m_altGlobalUploadSpeedLimit * 1024;
+}
+
+void Session::setAltGlobalUploadSpeedLimit(int limit)
+{
+ // Unfortunately the value was saved as KiB instead of B.
+ // But it is better to pass it around internally(+ webui) as Bytes.
+ limit /= 1024;
+ if (limit < 0) limit = 0;
+ if (limit == altGlobalUploadSpeedLimit()) return;
+
+ m_altGlobalUploadSpeedLimit = limit;
+ if (isAltGlobalSpeedLimitEnabled())
+ configureDeferred();
+}
+
+int Session::downloadSpeedLimit() const
+{
+ return isAltGlobalSpeedLimitEnabled()
+ ? altGlobalDownloadSpeedLimit()
+ : globalDownloadSpeedLimit();
+}
+
+void Session::setDownloadSpeedLimit(int limit)
+{
+ if (isAltGlobalSpeedLimitEnabled())
+ setAltGlobalDownloadSpeedLimit(limit);
+ else
+ setGlobalDownloadSpeedLimit(limit);
+}
+
+int Session::uploadSpeedLimit() const
+{
+ return isAltGlobalSpeedLimitEnabled()
+ ? altGlobalUploadSpeedLimit()
+ : globalUploadSpeedLimit();
+}
+
+void Session::setUploadSpeedLimit(int limit)
+{
+ if (isAltGlobalSpeedLimitEnabled())
+ setAltGlobalUploadSpeedLimit(limit);
+ else
+ setGlobalUploadSpeedLimit(limit);
+}
+
+bool Session::isAltGlobalSpeedLimitEnabled() const
+{
+ return m_isAltGlobalSpeedLimitEnabled;
+}
+
+void Session::setAltGlobalSpeedLimitEnabled(bool enabled)
+{
+ // Stop the scheduler when the user has manually changed the bandwidth mode
+ if (isBandwidthSchedulerEnabled())
+ setBandwidthSchedulerEnabled(false);
+
+ changeSpeedLimitMode_impl(enabled);
+}
+
+bool Session::isBandwidthSchedulerEnabled() const
+{
+ return m_isBandwidthSchedulerEnabled;
+}
+
+void Session::setBandwidthSchedulerEnabled(bool enabled)
+{
+ if (enabled != isBandwidthSchedulerEnabled()) {
+ m_isBandwidthSchedulerEnabled = enabled;
+ if (enabled)
+ enableBandwidthScheduler();
+ else
+ delete m_bwScheduler;
+ }
+}
+
+uint Session::saveResumeDataInterval() const
+{
+ return m_saveResumeDataInterval;
+}
+
+void Session::setSaveResumeDataInterval(uint value)
+{
+ if (value != saveResumeDataInterval()) {
+ m_saveResumeDataInterval = value;
+ m_resumeDataTimer->setInterval(value * 60 * 1000);
+ }
+}
+
+int Session::port() const
+{
+ static int randomPort = rand() % 64512 + 1024;
+ if (useRandomPort())
+ return randomPort;
+ return m_port;
+}
+
+void Session::setPort(int port)
+{
+ if (port != this->port()) {
+ m_port = port;
+ configureListeningInterface();
+ }
+}
+
+bool Session::useRandomPort() const
+{
+ return m_useRandomPort;
+}
+
+void Session::setUseRandomPort(bool value)
+{
+ m_useRandomPort = value;
+}
+
+QString Session::networkInterface() const
+{
+ return m_networkInterface;
+}
+
+void Session::setNetworkInterface(const QString &interface)
+{
+ if (interface != networkInterface()) {
+ m_networkInterface = interface;
+ configureListeningInterface();
+ }
+}
+
+QString Session::networkInterfaceName() const
+{
+ return m_networkInterfaceName;
+}
+
+void Session::setNetworkInterfaceName(const QString &name)
+{
+ m_networkInterfaceName = name;
+}
+
+QString Session::networkInterfaceAddress() const
+{
+ return m_networkInterfaceAddress;
+}
+
+void Session::setNetworkInterfaceAddress(const QString &address)
+{
+ if (address != networkInterfaceAddress()) {
+ m_networkInterfaceAddress = address;
+ configureListeningInterface();
+ }
+}
+
+bool Session::isIPv6Enabled() const
+{
+ return m_isIPv6Enabled;
+}
+
+void Session::setIPv6Enabled(bool enabled)
+{
+ if (enabled != isIPv6Enabled()) {
+ m_isIPv6Enabled = enabled;
+ configureListeningInterface();
+ }
+}
+
+int Session::encryption() const
+{
+ return m_encryption;
+}
+
+void Session::setEncryption(int state)
+{
+ if (state != encryption()) {
+ m_encryption = state;
+ configureDeferred();
+ Logger::instance()->addMessage(
+ tr("Encryption support [%1]")
+ .arg(state == 0 ? tr("ON") : state == 1 ? tr("FORCED") : tr("OFF"))
+ , Log::INFO);
+ }
+}
+
+bool Session::isForceProxyEnabled() const
+{
+ return m_isForceProxyEnabled;
+}
+
+void Session::setForceProxyEnabled(bool enabled)
+{
+ if (enabled != isForceProxyEnabled()) {
+ m_isForceProxyEnabled = enabled;
+ configureDeferred();
+ }
+}
+
+bool Session::isProxyPeerConnectionsEnabled() const
+{
+ return m_isProxyPeerConnectionsEnabled;
+}
+
+void Session::setProxyPeerConnectionsEnabled(bool enabled)
+{
+ if (enabled != isProxyPeerConnectionsEnabled()) {
+ m_isProxyPeerConnectionsEnabled = enabled;
+ configureDeferred();
+ }
+}
+
+bool Session::isAddTrackersEnabled() const
+{
+ return m_isAddTrackersEnabled;
+}
+
+void Session::setAddTrackersEnabled(bool enabled)
+{
+ m_isAddTrackersEnabled = enabled;
+}
+
+QString Session::additionalTrackers() const
+{
+ return m_additionalTrackers;
+}
+
+void Session::setAdditionalTrackers(const QString &trackers)
+{
+ if (trackers != additionalTrackers()) {
+ m_additionalTrackers = trackers;
+ populateAdditionalTrackers();
+ }
+}
+
+bool Session::isIPFilteringEnabled() const
+{
+ return m_isIPFilteringEnabled;
+}
+
+void Session::setIPFilteringEnabled(bool enabled)
+{
+ if (enabled != m_isIPFilteringEnabled) {
+ m_isIPFilteringEnabled = enabled;
+ m_IPFilteringChanged = true;
+ configureDeferred();
+ }
+}
+
+QString Session::IPFilterFile() const
+{
+ return Utils::Fs::fromNativePath(m_IPFilterFile);
+}
+
+void Session::setIPFilterFile(QString path)
+{
+ path = Utils::Fs::fromNativePath(path);
+ if (path != IPFilterFile()) {
+ m_IPFilterFile = path;
+ m_IPFilteringChanged = true;
+ configureDeferred();
+ }
+}
+
+int Session::maxConnectionsPerTorrent() const
+{
+ return m_maxConnectionsPerTorrent;
+}
+
+void Session::setMaxConnectionsPerTorrent(int max)
+{
+ max = (max > 0) ? max : -1;
+ if (max != maxConnectionsPerTorrent()) {
+ m_maxConnectionsPerTorrent = max;
+
+ // Apply this to all session torrents
+ for (const auto &handle: m_nativeSession->get_torrents()) {
+ if (!handle.is_valid()) continue;
+ try {
+ handle.set_max_connections(max);
+ }
+ catch(std::exception) {}
+ }
+ }
+}
+
+int Session::maxUploadsPerTorrent() const
+{
+ return m_maxUploadsPerTorrent;
+}
+
+void Session::setMaxUploadsPerTorrent(int max)
+{
+ max = (max > 0) ? max : -1;
+ if (max != maxUploadsPerTorrent()) {
+ m_maxUploadsPerTorrent = max;
+
+ // Apply this to all session torrents
+ for (const auto &handle: m_nativeSession->get_torrents()) {
+ if (!handle.is_valid()) continue;
+ try {
+ handle.set_max_uploads(max);
+ }
+ catch(std::exception) {}
+ }
+ }
+}
+
+bool Session::announceToAllTrackers() const
+{
+ return m_announceToAllTrackers;
+}
+
+void Session::setAnnounceToAllTrackers(bool val)
+{
+ if (val != m_announceToAllTrackers) {
+ m_announceToAllTrackers = val;
+ configureDeferred();
+ }
+}
+
+uint Session::diskCacheSize() const
+{
+ uint size = m_diskCacheSize;
+ // These macros may not be available on compilers other than MSVC and GCC
+#if defined(__x86_64__) || defined(_M_X64)
+ size = qMin(size, 4096u); // 4GiB
+#else
+ // When build as 32bit binary, set the maximum at less than 2GB to prevent crashes
+ // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
+ size = qMin(size, 1536u);
+#endif
+ return size;
+}
+
+void Session::setDiskCacheSize(uint size)
+{
+#if defined(__x86_64__) || defined(_M_X64)
+ size = qMin(size, 4096u); // 4GiB
+#else
+ // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
+ size = qMin(size, 1536u);
+#endif
+ if (size != m_diskCacheSize) {
+ m_diskCacheSize = size;
+ configureDeferred();
+ }
+}
+
+uint Session::diskCacheTTL() const
+{
+ return m_diskCacheTTL;
+}
+
+void Session::setDiskCacheTTL(uint ttl)
+{
+ if (ttl != m_diskCacheTTL) {
+ m_diskCacheTTL = ttl;
+ configureDeferred();
+ }
+}
+
+bool Session::useOSCache() const
+{
+ return m_useOSCache;
+}
+
+void Session::setUseOSCache(bool use)
+{
+ if (use != m_useOSCache) {
+ m_useOSCache = use;
+ configureDeferred();
+ }
+}
+
+bool Session::isAnonymousModeEnabled() const
+{
+ return m_isAnonymousModeEnabled;
+}
+
+void Session::setAnonymousModeEnabled(bool enabled)
+{
+ if (enabled != m_isAnonymousModeEnabled) {
+ m_isAnonymousModeEnabled = enabled;
+ configureDeferred();
+ Logger::instance()->addMessage(
+ tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF"))
+ , Log::INFO);
+ }
+}
+
+bool Session::isQueueingSystemEnabled() const
+{
+ return m_isQueueingEnabled;
+}
+
+void Session::setQueueingSystemEnabled(bool enabled)
+{
+ if (enabled != m_isQueueingEnabled) {
+ m_isQueueingEnabled = enabled;
+ configureDeferred();
+ }
+}
+
+int Session::maxActiveDownloads() const
+{
+ return m_maxActiveDownloads;
+}
+
+void Session::setMaxActiveDownloads(int max)
+{
+ max = std::max(max, -1);
+ if (max != m_maxActiveDownloads) {
+ m_maxActiveDownloads = max;
+ configureDeferred();
+ }
+}
+
+int Session::maxActiveUploads() const
+{
+ return m_maxActiveUploads;
+}
+
+void Session::setMaxActiveUploads(int max)
+{
+ max = std::max(max, -1);
+ if (max != m_maxActiveUploads) {
+ m_maxActiveUploads = max;
+ configureDeferred();
+ }
+}
+
+int Session::maxActiveTorrents() const
+{
+ return m_maxActiveTorrents;
+}
+
+void Session::setMaxActiveTorrents(int max)
+{
+ max = std::max(max, -1);
+ if (max != m_maxActiveTorrents) {
+ m_maxActiveTorrents = max;
+ configureDeferred();
+ }
+}
+
+bool Session::ignoreSlowTorrentsForQueueing() const
+{
+ return m_ignoreSlowTorrentsForQueueing;
+}
+
+void Session::setIgnoreSlowTorrentsForQueueing(bool ignore)
+{
+ if (ignore != m_ignoreSlowTorrentsForQueueing) {
+ m_ignoreSlowTorrentsForQueueing = ignore;
+ configureDeferred();
+ }
+}
+
+uint Session::outgoingPortsMin() const
+{
+ return m_outgoingPortsMin;
+}
+
+void Session::setOutgoingPortsMin(uint min)
+{
+ if (min != m_outgoingPortsMin) {
+ m_outgoingPortsMin = min;
+ configureDeferred();
+ }
+}
+
+uint Session::outgoingPortsMax() const
+{
+ return m_outgoingPortsMax;
+}
+
+void Session::setOutgoingPortsMax(uint max)
+{
+ if (max != m_outgoingPortsMax) {
+ m_outgoingPortsMax = max;
+ configureDeferred();
+ }
+}
+
+bool Session::ignoreLimitsOnLAN() const
+{
+ return m_ignoreLimitsOnLAN;
+}
+
+void Session::setIgnoreLimitsOnLAN(bool ignore)
+{
+ if (ignore != m_ignoreLimitsOnLAN) {
+ m_ignoreLimitsOnLAN = ignore;
+ configureDeferred();
+ }
+}
+
+bool Session::includeOverheadInLimits() const
+{
+ return m_includeOverheadInLimits;
+}
+
+void Session::setIncludeOverheadInLimits(bool include)
+{
+ if (include != m_includeOverheadInLimits) {
+ m_includeOverheadInLimits = include;
+ configureDeferred();
+ }
+}
+
+QString Session::announceIP() const
+{
+ return m_announceIP;
+}
+
+void Session::setAnnounceIP(const QString &ip)
+{
+ if (ip != m_announceIP) {
+ m_announceIP = ip;
+ configureDeferred();
+ }
+}
+
+bool Session::isSuperSeedingEnabled() const
+{
+ return m_isSuperSeedingEnabled;
+}
+
+void Session::setSuperSeedingEnabled(bool enabled)
+{
+ if (enabled != m_isSuperSeedingEnabled) {
+ m_isSuperSeedingEnabled = enabled;
+ configureDeferred();
+ }
+}
+
+int Session::maxConnections() const
+{
+ return m_maxConnections;
+}
+
+void Session::setMaxConnections(int max)
+{
+ max = (max > 0) ? max : -1;
+ if (max != m_maxConnections) {
+ m_maxConnections = max;
+ configureDeferred();
+ }
+}
+
+int Session::maxHalfOpenConnections() const
+{
+ return m_maxHalfOpenConnections;
+}
+
+void Session::setMaxHalfOpenConnections(int max)
+{
+ max = (max > 0) ? max : -1;
+ if (max != m_maxHalfOpenConnections) {
+ m_maxHalfOpenConnections = max;
+ configureDeferred();
+ }
+}
+
+int Session::maxUploads() const
+{
+ return m_maxUploads;
+}
+
+void Session::setMaxUploads(int max)
+{
+ max = (max > 0) ? max : -1;
+ if (max != m_maxUploads) {
+ m_maxUploads = max;
+ configureDeferred();
+ }
+}
+
+bool Session::isUTPEnabled() const
+{
+ return m_isUTPEnabled;
+}
+
+void Session::setUTPEnabled(bool enabled)
+{
+ if (enabled != m_isUTPEnabled) {
+ m_isUTPEnabled = enabled;
+ configureDeferred();
+ }
+}
+
+bool Session::isUTPRateLimited() const
+{
+ return m_isUTPRateLimited;
+}
+
+void Session::setUTPRateLimited(bool limited)
+{
+ if (limited != m_isUTPRateLimited) {
+ m_isUTPRateLimited = limited;
+ configureDeferred();
+ }
+}
+
+bool Session::isTrackerFilteringEnabled() const
+{
+ return m_isTrackerFilteringEnabled;
+}
+
+void Session::setTrackerFilteringEnabled(bool enabled)
+{
+ if (enabled != m_isTrackerFilteringEnabled) {
+ m_isTrackerFilteringEnabled = enabled;
+ configureDeferred();
+ }
}
bool Session::isListening() const
@@ -1773,28 +2746,12 @@ bool Session::isListening() const
MaxRatioAction Session::maxRatioAction() const
{
- return m_maxRatioAction;
+ return static_cast(m_maxRatioAction.value());
}
void Session::setMaxRatioAction(MaxRatioAction act)
{
- if (m_maxRatioAction != act) {
- m_maxRatioAction = act;
- m_settings->storeValue(KEY_MAXRATIOACTION, act);
- }
-}
-
-// Torrents will a ratio superior to the given value will
-// be automatically deleted
-void Session::setGlobalMaxRatio(qreal ratio)
-{
- if (ratio < 0)
- ratio = -1.;
- if (m_globalMaxRatio != ratio) {
- m_globalMaxRatio = ratio;
- qDebug("* Set globalMaxRatio to %.1f", m_globalMaxRatio);
- updateRatioTimer();
- }
+ m_maxRatioAction = static_cast(act);
}
// If this functions returns true, we cannot add torrent to session,
@@ -1808,7 +2765,7 @@ bool Session::isKnownTorrent(const InfoHash &hash) const
void Session::updateRatioTimer()
{
- if ((m_globalMaxRatio == -1) && !hasPerTorrentRatioLimit()) {
+ if ((globalMaxRatio() == -1) && !hasPerTorrentRatioLimit()) {
if (m_bigRatioTimer->isActive())
m_bigRatioTimer->stop();
}
@@ -1890,7 +2847,7 @@ void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
QString torrentFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()));
if (torrent->saveTorrentFile(torrentFile)) {
// Copy the torrent file to the export folder
- if (m_torrentExportEnabled)
+ if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
}
@@ -1941,9 +2898,8 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent)
}
}
- Preferences *const pref = Preferences::instance();
// Move .torrent file to another folder
- if (pref->isFinishedTorrentExportEnabled())
+ if (!finishedTorrentExportDirectory().isEmpty())
exportTorrentFile(torrent, TorrentExportFolder::Finished);
if (!hasUnfinishedTorrents())
@@ -2015,8 +2971,18 @@ void Session::initResumeFolder()
}
}
+void Session::configureDeferred()
+{
+ if (m_deferredConfigureScheduled) return; // Obtaining the lock is expensive, let's check early
+ QWriteLocker locker(&m_lock);
+ if (m_deferredConfigureScheduled) return; // something might have changed while we were getting the lock
+
+ QMetaObject::invokeMethod(this, "configure", Qt::QueuedConnection);
+ m_deferredConfigureScheduled = true;
+}
+
// Enable IP Filtering
-void Session::enableIPFilter(const QString &filterPath, bool force)
+void Session::enableIPFilter()
{
qDebug("Enabling IPFilter");
if (!m_filterParser) {
@@ -2024,10 +2990,8 @@ void Session::enableIPFilter(const QString &filterPath, bool force)
connect(m_filterParser.data(), SIGNAL(IPFilterParsed(int)), SLOT(handleIPFilterParsed(int)));
connect(m_filterParser.data(), SIGNAL(IPFilterError()), SLOT(handleIPFilterError()));
}
- if (m_filterPath.isEmpty() || m_filterPath != Utils::Fs::fromNativePath(filterPath) || force) {
- m_filterPath = Utils::Fs::fromNativePath(filterPath);
- m_filterParser->processFilterFile(Utils::Fs::fromNativePath(filterPath));
- }
+
+ m_filterParser->processFilterFile(IPFilterFile());
}
// Disable IP Filtering
@@ -2039,7 +3003,11 @@ void Session::disableIPFilter()
disconnect(m_filterParser.data(), 0, this, 0);
delete m_filterParser;
}
- m_filterPath = "";
+
+ // Add the banned IPs after the IPFilter disabling
+ // which creates an empty filter and overrides all previously
+ // applied bans.
+ processBannedIPs();
}
void Session::recursiveTorrentDownload(const InfoHash &hash)
@@ -2074,54 +3042,6 @@ CacheStatus Session::cacheStatus() const
return m_nativeSession->get_cache_status();
}
-// Set Proxy
-void Session::setProxySettings(libt::proxy_settings proxySettings)
-{
- qDebug() << Q_FUNC_INFO;
-
- proxySettings.proxy_peer_connections = Preferences::instance()->proxyPeerConnections();
- m_nativeSession->set_proxy(proxySettings);
-
- // Define environment variables for urllib in search engine plugins
- if (Preferences::instance()->isProxyOnlyForTorrents()) {
- qputenv("http_proxy", QByteArray());
- qputenv("https_proxy", QByteArray());
- qputenv("sock_proxy", QByteArray());
- }
- else {
- QString proxy_str;
- switch(proxySettings.type) {
- case libt::proxy_settings::http_pw:
- proxy_str = QString("http://%1:%2@%3:%4").arg(Utils::String::fromStdString(proxySettings.username)).arg(Utils::String::fromStdString(proxySettings.password))
- .arg(Utils::String::fromStdString(proxySettings.hostname)).arg(proxySettings.port);
- break;
- case libt::proxy_settings::http:
- proxy_str = QString("http://%1:%2").arg(Utils::String::fromStdString(proxySettings.hostname)).arg(proxySettings.port);
- break;
- case libt::proxy_settings::socks5:
- proxy_str = QString("%1:%2").arg(Utils::String::fromStdString(proxySettings.hostname)).arg(proxySettings.port);
- break;
- case libt::proxy_settings::socks5_pw:
- proxy_str = QString("%1:%2@%3:%4").arg(Utils::String::fromStdString(proxySettings.username)).arg(Utils::String::fromStdString(proxySettings.password))
- .arg(Utils::String::fromStdString(proxySettings.hostname)).arg(proxySettings.port);
- break;
- default:
- qDebug("Disabling HTTP communications proxy");
- qputenv("http_proxy", QByteArray());
- qputenv("https_proxy", QByteArray());
- qputenv("sock_proxy", QByteArray());
- return;
- }
- qDebug("HTTP communications proxy string: %s", qPrintable(proxy_str));
- if ((proxySettings.type == libt::proxy_settings::socks5) || (proxySettings.type == libt::proxy_settings::socks5_pw))
- qputenv("sock_proxy", proxy_str.toLocal8Bit());
- else {
- qputenv("http_proxy", proxy_str.toLocal8Bit());
- qputenv("https_proxy", proxy_str.toLocal8Bit());
- }
- }
-}
-
// Will resume torrents in backup directory
void Session::startUpTorrents()
{
@@ -2206,50 +3126,60 @@ void Session::refresh()
void Session::handleIPFilterParsed(int ruleCount)
{
Logger::instance()->addMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
- emit ipFilterParsed(false, ruleCount);
+ emit IPFilterParsed(false, ruleCount);
}
void Session::handleIPFilterError()
{
Logger::instance()->addMessage(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL);
- emit ipFilterParsed(true, 0);
+ emit IPFilterParsed(true, 0);
}
-void Session::dispatchAlerts(std::auto_ptr alertPtr)
+#if LIBTORRENT_VERSION_NUM < 10100
+void Session::dispatchAlerts(libt::alert *alertPtr)
{
QMutexLocker lock(&m_alertsMutex);
- bool wasEmpty = m_alerts.isEmpty();
+ bool wasEmpty = m_alerts.empty();
- m_alerts.append(alertPtr.release());
+ m_alerts.push_back(alertPtr);
if (wasEmpty) {
m_alertsWaitCondition.wakeAll();
QMetaObject::invokeMethod(this, "readAlerts", Qt::QueuedConnection);
}
}
+#endif
-void Session::getPendingAlerts(QVector &out, ulong time)
+void Session::getPendingAlerts(std::vector &out, ulong time)
{
Q_ASSERT(out.empty());
+#if LIBTORRENT_VERSION_NUM < 10100
QMutexLocker lock(&m_alertsMutex);
if (m_alerts.empty())
m_alertsWaitCondition.wait(&m_alertsMutex, time);
m_alerts.swap(out);
+#else
+ if (time > 0)
+ m_nativeSession->wait_for_alert(libt::milliseconds(time));
+ m_nativeSession->pop_alerts(&out);
+#endif
}
// Read alerts sent by the BitTorrent session
void Session::readAlerts()
{
- QVector alerts;
+ std::vector alerts;
getPendingAlerts(alerts);
- foreach (libt::alert *const a, alerts) {
+ for (const auto a: alerts) {
handleAlert(a);
+#if LIBTORRENT_VERSION_NUM < 10100
delete a;
+#endif
}
}
@@ -2343,7 +3273,6 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, data);
m_torrents.insert(torrent->hash(), torrent);
- Preferences *const pref = Preferences::instance();
Logger *const logger = Logger::instance();
bool fromMagnetUri = !torrent->hasMetadata();
@@ -2365,7 +3294,7 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
const QString newFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()));
if (torrent->saveTorrentFile(newFile)) {
// Copy the torrent file to the export folder
- if (m_torrentExportEnabled)
+ if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
}
else {
@@ -2373,8 +3302,8 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
}
}
- if (pref->isAddTrackersEnabled() && !torrent->isPrivate())
- torrent->addTrackers(m_additionalTrackers);
+ if (isAddTrackersEnabled() && !torrent->isPrivate())
+ torrent->addTrackers(m_additionalTrackerList);
bool addPaused = data.addPaused;
if (data.addPaused == TriStateBool::Undefined)
@@ -2396,6 +3325,9 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
// Send torrent addition signal
emit torrentAdded(torrent);
+ // Send new torrent signal
+ if (!data.resumed)
+ emit torrentNew(torrent);
}
void Session::handleAddTorrentAlert(libt::add_torrent_alert *p)
@@ -2611,10 +3543,16 @@ namespace
torrentData.resumed = true;
torrentData.skipChecking = false;
- libt::lazy_entry fast;
libt::error_code ec;
+#if LIBTORRENT_VERSION_NUM < 10100
+ libt::lazy_entry fast;
libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec);
if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false;
+#else
+ libt::bdecode_node fast;
+ libt::bdecode(data.constData(), data.constData() + data.size(), fast, ec);
+ if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false;
+#endif
torrentData.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath")));
torrentData.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble();
diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h
index 072e478bd..0ec3dc038 100644
--- a/src/base/bittorrent/session.h
+++ b/src/base/bittorrent/session.h
@@ -30,17 +30,23 @@
#ifndef BITTORRENT_SESSION_H
#define BITTORRENT_SESSION_H
+#include
+#include
+
#include
#include
#include
-#include
-#include
+#if LIBTORRENT_VERSION_NUM < 10100
#include
-#include
+#endif
#include
+#include
+#include
+#include
+#include
+#include
-#include
-
+#include "base/settingvalue.h"
#include "base/tristatebool.h"
#include "base/types.h"
#include "torrentinfo.h"
@@ -52,19 +58,12 @@ namespace libtorrent
class entry;
struct add_torrent_params;
struct pe_settings;
- struct session_settings;
- struct session_status;
-
#if LIBTORRENT_VERSION_NUM < 10100
- struct proxy_settings;
+ struct session_settings;
#else
- namespace aux
- {
- struct proxy_settings;
- }
-
- typedef aux::proxy_settings proxy_settings;
+ struct settings_pack;
#endif
+ struct session_status;
class alert;
struct torrent_alert;
@@ -111,7 +110,6 @@ class FilterParserThread;
class BandwidthScheduler;
class Statistics;
class ResumeDataSavingManager;
-class SettingsStorage;
enum MaxRatioAction
{
@@ -172,19 +170,13 @@ namespace BitTorrent
static void freeInstance();
static Session *instance();
- bool isDHTEnabled() const;
- bool isLSDEnabled() const;
- bool isPexEnabled() const;
- bool isQueueingEnabled() const;
- qreal globalMaxRatio() const;
- bool isAppendExtensionEnabled() const;
-
QString defaultSavePath() const;
void setDefaultSavePath(QString path);
QString tempPath() const;
void setTempPath(QString path);
bool isTempPathEnabled() const;
void setTempPathEnabled(bool enabled);
+ QString torrentTempPath(const InfoHash &hash) const;
static bool isValidCategoryName(const QString &name);
// returns category itself and all top level categories
@@ -198,28 +190,144 @@ namespace BitTorrent
bool isSubcategoriesEnabled() const;
void setSubcategoriesEnabled(bool value);
- // Advanced Saving Management subsystem (ASM)
+ // Torrent Management Mode subsystem (TMM)
//
- // Each torrent can be either in Simple mode or in Advanced mode
- // In Simple mode torrent has explicit save path
- // In Advanced Mode torrent has implicit save path (based on Default
- // save path and Category save path)
- // In Advanced Mode torrent save path can be changed in following cases:
+ // Each torrent can be either in Manual mode or in Automatic mode
+ // In Manual Mode various torrent properties are set explicitly(eg save path)
+ // In Automatic Mode various torrent properties are set implicitly(eg save path)
+ // based on the associated category.
+ // In Automatic Mode torrent save path can be changed in following cases:
// 1. Default save path changed
// 2. Torrent category save path changed
// 3. Torrent category changed
// (unless otherwise is specified)
- bool isASMDisabledByDefault() const;
- void setASMDisabledByDefault(bool value);
- bool isDisableASMWhenCategoryChanged() const;
- void setDisableASMWhenCategoryChanged(bool value);
- bool isDisableASMWhenDefaultSavePathChanged() const;
- void setDisableASMWhenDefaultSavePathChanged(bool value);
- bool isDisableASMWhenCategorySavePathChanged() const;
- void setDisableASMWhenCategorySavePathChanged(bool value);
+ bool isAutoTMMDisabledByDefault() const;
+ void setAutoTMMDisabledByDefault(bool value);
+ bool isDisableAutoTMMWhenCategoryChanged() const;
+ void setDisableAutoTMMWhenCategoryChanged(bool value);
+ bool isDisableAutoTMMWhenDefaultSavePathChanged() const;
+ void setDisableAutoTMMWhenDefaultSavePathChanged(bool value);
+ bool isDisableAutoTMMWhenCategorySavePathChanged() const;
+ void setDisableAutoTMMWhenCategorySavePathChanged(bool value);
+ qreal globalMaxRatio() const;
+ void setGlobalMaxRatio(qreal ratio);
+ bool isDHTEnabled() const;
+ void setDHTEnabled(bool enabled);
+ bool isLSDEnabled() const;
+ void setLSDEnabled(bool enabled);
+ bool isPeXEnabled() const;
+ void setPeXEnabled(bool enabled);
+ bool isTrackerExchangeEnabled() const;
+ void setTrackerExchangeEnabled(bool enabled);
bool isAddTorrentPaused() const;
void setAddTorrentPaused(bool value);
+ bool isTrackerEnabled() const;
+ void setTrackerEnabled(bool enabled);
+ bool isAppendExtensionEnabled() const;
+ void setAppendExtensionEnabled(bool enabled);
+ uint refreshInterval() const;
+ void setRefreshInterval(uint value);
+ bool isPreallocationEnabled() const;
+ void setPreallocationEnabled(bool enabled);
+ QString torrentExportDirectory() const;
+ void setTorrentExportDirectory(QString path);
+ QString finishedTorrentExportDirectory() const;
+ void setFinishedTorrentExportDirectory(QString path);
+
+ int globalDownloadSpeedLimit() const;
+ void setGlobalDownloadSpeedLimit(int limit);
+ int globalUploadSpeedLimit() const;
+ void setGlobalUploadSpeedLimit(int limit);
+ int altGlobalDownloadSpeedLimit() const;
+ void setAltGlobalDownloadSpeedLimit(int limit);
+ int altGlobalUploadSpeedLimit() const;
+ void setAltGlobalUploadSpeedLimit(int limit);
+ int downloadSpeedLimit() const;
+ void setDownloadSpeedLimit(int limit);
+ int uploadSpeedLimit() const;
+ void setUploadSpeedLimit(int limit);
+ bool isAltGlobalSpeedLimitEnabled() const;
+ void setAltGlobalSpeedLimitEnabled(bool enabled);
+ bool isBandwidthSchedulerEnabled() const;
+ void setBandwidthSchedulerEnabled(bool enabled);
+
+ uint saveResumeDataInterval() const;
+ void setSaveResumeDataInterval(uint value);
+ int port() const;
+ void setPort(int port);
+ bool useRandomPort() const;
+ void setUseRandomPort(bool value);
+ QString networkInterface() const;
+ void setNetworkInterface(const QString &interface);
+ QString networkInterfaceName() const;
+ void setNetworkInterfaceName(const QString &name);
+ QString networkInterfaceAddress() const;
+ void setNetworkInterfaceAddress(const QString &address);
+ bool isIPv6Enabled() const;
+ void setIPv6Enabled(bool enabled);
+ int encryption() const;
+ void setEncryption(int state);
+ bool isForceProxyEnabled() const;
+ void setForceProxyEnabled(bool enabled);
+ bool isProxyPeerConnectionsEnabled() const;
+ void setProxyPeerConnectionsEnabled(bool enabled);
+ bool isAddTrackersEnabled() const;
+ void setAddTrackersEnabled(bool enabled);
+ QString additionalTrackers() const;
+ void setAdditionalTrackers(const QString &trackers);
+ bool isIPFilteringEnabled() const;
+ void setIPFilteringEnabled(bool enabled);
+ QString IPFilterFile() const;
+ void setIPFilterFile(QString path);
+ bool announceToAllTrackers() const;
+ void setAnnounceToAllTrackers(bool val);
+ uint diskCacheSize() const;
+ void setDiskCacheSize(uint size);
+ uint diskCacheTTL() const;
+ void setDiskCacheTTL(uint ttl);
+ bool useOSCache() const;
+ void setUseOSCache(bool use);
+ bool isAnonymousModeEnabled() const;
+ void setAnonymousModeEnabled(bool enabled);
+ bool isQueueingSystemEnabled() const;
+ void setQueueingSystemEnabled(bool enabled);
+ bool ignoreSlowTorrentsForQueueing() const;
+ void setIgnoreSlowTorrentsForQueueing(bool ignore);
+ uint outgoingPortsMin() const;
+ void setOutgoingPortsMin(uint min);
+ uint outgoingPortsMax() const;
+ void setOutgoingPortsMax(uint max);
+ bool ignoreLimitsOnLAN() const;
+ void setIgnoreLimitsOnLAN(bool ignore);
+ bool includeOverheadInLimits() const;
+ void setIncludeOverheadInLimits(bool include);
+ QString announceIP() const;
+ void setAnnounceIP(const QString &ip);
+ bool isSuperSeedingEnabled() const;
+ void setSuperSeedingEnabled(bool enabled);
+ int maxConnections() const;
+ void setMaxConnections(int max);
+ int maxHalfOpenConnections() const;
+ void setMaxHalfOpenConnections(int max);
+ int maxConnectionsPerTorrent() const;
+ void setMaxConnectionsPerTorrent(int max);
+ int maxUploads() const;
+ void setMaxUploads(int max);
+ int maxUploadsPerTorrent() const;
+ void setMaxUploadsPerTorrent(int max);
+ int maxActiveDownloads() const;
+ void setMaxActiveDownloads(int max);
+ int maxActiveUploads() const;
+ void setMaxActiveUploads(int max);
+ int maxActiveTorrents() const;
+ void setMaxActiveTorrents(int max);
+ bool isUTPEnabled() const;
+ void setUTPEnabled(bool enabled);
+ bool isUTPRateLimited() const;
+ void setUTPRateLimited(bool limited);
+ bool isTrackerFilteringEnabled() const;
+ void setTrackerFilteringEnabled(bool enabled);
TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash torrents() const;
@@ -230,19 +338,11 @@ namespace BitTorrent
CacheStatus cacheStatus() const;
quint64 getAlltimeDL() const;
quint64 getAlltimeUL() const;
- int downloadRateLimit() const;
- int uploadRateLimit() const;
bool isListening() const;
MaxRatioAction maxRatioAction() const;
void setMaxRatioAction(MaxRatioAction act);
- void changeSpeedLimitMode(bool alternative);
- void setDownloadRateLimit(int rate);
- void setUploadRateLimit(int rate);
- void setGlobalMaxRatio(qreal ratio);
- void enableIPFilter(const QString &filterPath, bool force = false);
- void disableIPFilter();
void banIP(const QString &ip);
bool isKnownTorrent(const InfoHash &hash) const;
@@ -284,6 +384,7 @@ namespace BitTorrent
void torrentsUpdated();
void addTorrentFailed(const QString &error);
void torrentAdded(BitTorrent::TorrentHandle *const torrent);
+ void torrentNew(BitTorrent::TorrentHandle *const torrent);
void torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent);
void torrentPaused(BitTorrent::TorrentHandle *const torrent);
void torrentResumed(BitTorrent::TorrentHandle *const torrent);
@@ -302,7 +403,7 @@ namespace BitTorrent
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent);
void speedLimitModeChanged(bool alternative);
- void ipFilterParsed(bool error, int ruleCount);
+ void IPFilterParsed(bool error, int ruleCount);
void trackersAdded(BitTorrent::TorrentHandle *const torrent, const QList &trackers);
void trackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList &trackers);
void trackersChanged(BitTorrent::TorrentHandle *const torrent);
@@ -314,7 +415,7 @@ namespace BitTorrent
void subcategoriesSupportChanged();
private slots:
- void configure();
+ void configureDeferred();
void readAlerts();
void refresh();
void processBigRatios();
@@ -339,25 +440,30 @@ namespace BitTorrent
void initResumeFolder();
// Session configuration
- void setSessionSettings();
- void setProxySettings(libtorrent::proxy_settings proxySettings);
- void adjustLimits();
+ Q_INVOKABLE void configure();
+#if LIBTORRENT_VERSION_NUM < 10100
+ void configure(libtorrent::session_settings &sessionSettings);
void adjustLimits(libtorrent::session_settings &sessionSettings);
+#else
+ void configure(libtorrent::settings_pack &settingsPack);
+ void adjustLimits(libtorrent::settings_pack &settingsPack);
+#endif
+ void adjustLimits();
+ void processBannedIPs();
const QStringList getListeningIPs();
- void setListeningPort();
- void preAllocateAllFiles(bool b);
- void setMaxConnectionsPerTorrent(int max);
- void setMaxUploadsPerTorrent(int max);
- void enableLSD(bool enable);
- void enableDHT(bool enable);
+ void configureListeningInterface();
void changeSpeedLimitMode_impl(bool alternative);
-
- void setAppendExtension(bool append);
+ void enableTracker(bool enable);
+ void enableBandwidthScheduler();
+ void populateAdditionalTrackers();
+ void enableIPFilter();
+ void disableIPFilter();
void startUpTorrents();
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
- const TorrentInfo &torrentInfo = TorrentInfo(),
+ TorrentInfo torrentInfo = TorrentInfo(),
const QByteArray &fastresumeData = QByteArray());
+ bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateRatioTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
@@ -385,34 +491,100 @@ namespace BitTorrent
void saveResumeData();
- void dispatchAlerts(std::auto_ptr alertPtr);
- void getPendingAlerts(QVector &out, ulong time = 0);
-
- SettingsStorage *m_settings;
+#if LIBTORRENT_VERSION_NUM < 10100
+ void dispatchAlerts(libtorrent::alert *alertPtr);
+#endif
+ void getPendingAlerts(std::vector &out, ulong time = 0);
// BitTorrent
libtorrent::session *m_nativeSession;
- bool m_LSDEnabled;
- bool m_DHTEnabled;
- bool m_PeXEnabled;
- bool m_queueingEnabled;
- bool m_torrentExportEnabled;
- bool m_finishedTorrentExportEnabled;
- bool m_preAllocateAll;
- qreal m_globalMaxRatio;
+ bool m_deferredConfigureScheduled;
+ bool m_IPFilteringChanged;
+#if LIBTORRENT_VERSION_NUM >= 10100
+ bool m_listenInterfaceChanged; // optimization
+#endif
+ CachedSettingValue m_isDHTEnabled;
+ CachedSettingValue m_isLSDEnabled;
+ CachedSettingValue m_isPeXEnabled;
+ CachedSettingValue m_isTrackerExchangeEnabled;
+ CachedSettingValue m_isIPFilteringEnabled;
+ CachedSettingValue m_isTrackerFilteringEnabled;
+ CachedSettingValue m_IPFilterFile;
+ CachedSettingValue m_announceToAllTrackers;
+ CachedSettingValue m_diskCacheSize;
+ CachedSettingValue m_diskCacheTTL;
+ CachedSettingValue m_useOSCache;
+ CachedSettingValue m_isAnonymousModeEnabled;
+ CachedSettingValue m_isQueueingEnabled;
+ CachedSettingValue m_maxActiveDownloads;
+ CachedSettingValue m_maxActiveUploads;
+ CachedSettingValue m_maxActiveTorrents;
+ CachedSettingValue m_ignoreSlowTorrentsForQueueing;
+ CachedSettingValue m_outgoingPortsMin;
+ CachedSettingValue m_outgoingPortsMax;
+ CachedSettingValue m_ignoreLimitsOnLAN;
+ CachedSettingValue m_includeOverheadInLimits;
+ CachedSettingValue m_announceIP;
+ CachedSettingValue m_isSuperSeedingEnabled;
+ CachedSettingValue m_maxConnections;
+ CachedSettingValue m_maxHalfOpenConnections;
+ CachedSettingValue m_maxUploads;
+ CachedSettingValue m_maxConnectionsPerTorrent;
+ CachedSettingValue m_maxUploadsPerTorrent;
+ CachedSettingValue m_isUTPEnabled;
+ CachedSettingValue m_isUTPRateLimited;
+ CachedSettingValue m_isAddTrackersEnabled;
+ CachedSettingValue m_additionalTrackers;
+ CachedSettingValue m_globalMaxRatio;
+ CachedSettingValue m_isAddTorrentPaused;
+ CachedSettingValue m_isAppendExtensionEnabled;
+ CachedSettingValue m_refreshInterval;
+ CachedSettingValue m_isPreallocationEnabled;
+ CachedSettingValue m_torrentExportDirectory;
+ CachedSettingValue m_finishedTorrentExportDirectory;
+ CachedSettingValue m_globalDownloadSpeedLimit;
+ CachedSettingValue m_globalUploadSpeedLimit;
+ CachedSettingValue m_altGlobalDownloadSpeedLimit;
+ CachedSettingValue m_altGlobalUploadSpeedLimit;
+ CachedSettingValue m_isAltGlobalSpeedLimitEnabled;
+ CachedSettingValue m_isBandwidthSchedulerEnabled;
+ CachedSettingValue m_saveResumeDataInterval;
+ CachedSettingValue m_port;
+ CachedSettingValue m_useRandomPort;
+ CachedSettingValue m_networkInterface;
+ CachedSettingValue m_networkInterfaceName;
+ CachedSettingValue m_networkInterfaceAddress;
+ CachedSettingValue m_isIPv6Enabled;
+ CachedSettingValue m_encryption;
+ CachedSettingValue m_isForceProxyEnabled;
+ CachedSettingValue m_isProxyPeerConnectionsEnabled;
+ CachedSettingValue m_storedCategories;
+ CachedSettingValue m_maxRatioAction;
+ CachedSettingValue m_defaultSavePath;
+ CachedSettingValue m_tempPath;
+ CachedSettingValue m_isSubcategoriesEnabled;
+ CachedSettingValue m_isTempPathEnabled;
+ CachedSettingValue m_isAutoTMMDisabledByDefault;
+ CachedSettingValue m_isDisableAutoTMMWhenCategoryChanged;
+ CachedSettingValue m_isDisableAutoTMMWhenDefaultSavePathChanged;
+ CachedSettingValue m_isDisableAutoTMMWhenCategorySavePathChanged;
+ CachedSettingValue m_isTrackerEnabled;
+ CachedSettingValue m_bannedIPs;
+
+ // Order is important. These need to be declared after their CachedSettingsValue
+ // counterparts, because they use them for initialization in the constructor
+ // initialization list.
+ const bool m_wasPexEnabled;
+ const bool m_wasTrackerExchangeEnabled;
+
int m_numResumeData;
int m_extraLimit;
- bool m_appendExtension;
- uint m_refreshInterval;
- MaxRatioAction m_maxRatioAction;
- QList m_additionalTrackers;
- QString m_defaultSavePath;
- QString m_tempPath;
- QString m_filterPath;
+ QList m_additionalTrackerList;
QString m_resumeFolderPath;
QFile m_resumeFolderLock;
QHash m_savePathsToRemove;
+ bool m_useProxy;
QTimer *m_refreshTimer;
QTimer *m_bigRatioTimer;
@@ -434,12 +606,16 @@ namespace BitTorrent
TorrentStatusReport m_torrentStatusReport;
QStringMap m_categories;
+#if LIBTORRENT_VERSION_NUM < 10100
QMutex m_alertsMutex;
QWaitCondition m_alertsWaitCondition;
- QVector m_alerts;
+ std::vector m_alerts;
+#endif
QNetworkConfigurationManager m_networkManager;
+ mutable QReadWriteLock m_lock;
+
static Session *m_instance;
};
}
diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp
index 7881fe21b..b03bf90af 100644
--- a/src/base/bittorrent/torrenthandle.cpp
+++ b/src/base/bittorrent/torrenthandle.cpp
@@ -60,7 +60,7 @@
#include "trackerentry.h"
#include "torrenthandle.h"
-static const char QB_EXT[] = ".!qB";
+const QString QB_EXT {".!qB"};
namespace libt = libtorrent;
using namespace BitTorrent;
@@ -199,10 +199,10 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_nativeHandle(nativeHandle)
, m_state(TorrentState::Unknown)
, m_renameCount(0)
+ , m_useAutoTMM(data.savePath.isEmpty())
, m_name(data.name)
, m_savePath(Utils::Fs::toNativePath(data.savePath))
, m_category(data.category)
- , m_useASM(data.savePath.isEmpty())
, m_hasSeedStatus(data.hasSeedStatus)
, m_ratioLimit(data.ratioLimit)
, m_tempPathDisabled(data.disableTempPath)
@@ -210,20 +210,14 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_pauseAfterRecheck(false)
, m_needSaveResumeData(false)
{
- if (m_useASM)
+ if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
updateStatus();
m_hash = InfoHash(m_nativeStatus.info_hash);
- adjustActualSavePath();
- if (!data.resumed) {
+ if (!data.resumed)
setSequentialDownload(data.sequential);
- if (hasMetadata()) {
- if (m_session->isAppendExtensionEnabled())
- appendExtensionsToIncompleteFiles();
- }
- }
}
TorrentHandle::~TorrentHandle() {}
@@ -332,19 +326,19 @@ QString TorrentHandle::contentPath(bool actual) const
return rootPath(actual);
}
-bool TorrentHandle::isASMEnabled() const
+bool TorrentHandle::isAutoTMMEnabled() const
{
- return m_useASM;
+ return m_useAutoTMM;
}
-void TorrentHandle::setASMEnabled(bool enabled)
+void TorrentHandle::setAutoTMMEnabled(bool enabled)
{
- if (m_useASM == enabled) return;
+ if (m_useAutoTMM == enabled) return;
- m_useASM = enabled;
+ m_useAutoTMM = enabled;
m_session->handleTorrentSavingModeChanged(this);
- if (m_useASM)
+ if (m_useAutoTMM)
move_impl(m_session->categorySavePath(m_category));
}
@@ -603,25 +597,6 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
return res;
}
-QPair TorrentHandle::fileExtremityPieces(int index) const
-{
- if (!hasMetadata()) return qMakePair(-1, -1);
-
- const int numPieces = piecesCount();
- const qlonglong pieceSize = pieceLength();
-
- // Determine the first and last piece of the file
- int firstPiece = floor((m_torrentInfo.fileOffset(index) + 1) / (float) pieceSize);
- Q_ASSERT((firstPiece >= 0) && (firstPiece < numPieces));
-
- int numPiecesInFile = ceil(fileSize(index) / (float) pieceSize);
- int lastPiece = firstPiece + numPiecesInFile - 1;
- Q_ASSERT((lastPiece >= 0) && (lastPiece < numPieces));
-
- Q_UNUSED(numPieces)
- return qMakePair(firstPiece, lastPiece);
-}
-
QVector TorrentHandle::filePriorities() const
{
std::vector fp;
@@ -739,13 +714,13 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
std::vector fp;
SAFE_GET(fp, file_priorities);
- QPair extremities;
+ TorrentInfo::PieceRange extremities;
bool found = false;
int count = static_cast(fp.size());
for (int i = 0; i < count; ++i) {
const QString ext = Utils::Fs::fileExtension(filePath(i));
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
- extremities = fileExtremityPieces(i);
+ extremities = info().filePieces(i);
found = true;
break;
}
@@ -755,8 +730,8 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
int first = 0;
int last = 0;
- SAFE_GET(first, piece_priority, extremities.first);
- SAFE_GET(last, piece_priority, extremities.second);
+ SAFE_GET(first, piece_priority, extremities.first());
+ SAFE_GET(last, piece_priority, extremities.last());
return ((first == 7) && (last == 7));
}
@@ -777,7 +752,7 @@ void TorrentHandle::updateState()
m_state = isSeed() ? TorrentState::PausedUploading : TorrentState::PausedDownloading;
}
else {
- if (m_session->isQueueingEnabled() && isQueued() && !isChecking()) {
+ if (m_session->isQueueingSystemEnabled() && isQueued() && !isChecking()) {
m_state = isSeed() ? TorrentState::QueuedUploading : TorrentState::QueuedDownloading;
}
else {
@@ -1156,11 +1131,11 @@ bool TorrentHandle::setCategory(const QString &category)
m_needSaveResumeData = true;
m_session->handleTorrentCategoryChanged(this, oldCategory);
- if (m_useASM) {
- if (!m_session->isDisableASMWhenCategoryChanged())
+ if (m_useAutoTMM) {
+ if (!m_session->isDisableAutoTMMWhenCategoryChanged())
move_impl(m_session->categorySavePath(m_category));
else
- setASMEnabled(false);
+ setAutoTMMEnabled(false);
}
}
@@ -1169,7 +1144,7 @@ bool TorrentHandle::setCategory(const QString &category)
void TorrentHandle::move(QString path)
{
- m_useASM = false;
+ m_useAutoTMM = false;
m_session->handleTorrentSavingModeChanged(this);
path = Utils::Fs::fromNativePath(path.trimmed());
@@ -1251,13 +1226,13 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
// Determine the priority to set
int prio = b ? 7 : fp[index];
- QPair extremities = fileExtremityPieces(index);
+ TorrentInfo::PieceRange extremities = info().filePieces(index);
// worst case: AVI index = 1% of total file size (at the end of the file)
int nNumPieces = ceil(fileSize(index) * 0.01 / pieceLength());
for (int i = 0; i < nNumPieces; ++i) {
- pp[extremities.first + i] = prio;
- pp[extremities.second - i] = prio;
+ pp[extremities.first() + i] = prio;
+ pp[extremities.last() - i] = prio;
}
}
}
@@ -1309,10 +1284,6 @@ void TorrentHandle::moveStorage(const QString &newPath)
if (QDir(oldPath) == QDir(newPath)) return;
qDebug("move storage: %s to %s", qPrintable(oldPath), qPrintable(newPath));
- // Create destination directory if necessary
- // or move_storage() will fail...
- QDir().mkpath(newPath);
-
try {
// Actually move the storage
m_nativeHandle.move_storage(newPath.toUtf8().constData());
@@ -1398,7 +1369,7 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
// Attempt to remove old folder if empty
QDir oldSaveDir(Utils::Fs::fromNativePath(m_oldPath));
- if ((oldSaveDir != QDir(m_session->defaultSavePath())) && (oldSaveDir != QDir(m_session->tempPath()))) {
+ if (oldSaveDir != QDir(m_session->defaultSavePath())) {
qDebug("Attempting to remove %s", qPrintable(m_oldPath));
QDir().rmpath(m_oldPath);
}
@@ -1475,6 +1446,7 @@ void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert
m_hasSeedStatus = true;
adjustActualSavePath();
+ manageIncompleteFiles();
if (m_pauseAfterRecheck) {
m_pauseAfterRecheck = false;
@@ -1496,13 +1468,19 @@ void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_aler
m_hasSeedStatus = true;
adjustActualSavePath();
- if (Preferences::instance()->recheckTorrentsOnCompletion())
- forceRecheck();
+ manageIncompleteFiles();
- if (isMoveInProgress() || m_renameCount > 0)
+ const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion();
+ if (isMoveInProgress() || m_renameCount > 0) {
+ if (recheckTorrentsOnCompletion)
+ m_moveFinishedTriggers.append(boost::bind(&TorrentHandle::forceRecheck, this));
m_moveFinishedTriggers.append(boost::bind(&Session::handleTorrentFinished, m_session, this));
- else
+ }
+ else {
+ if (recheckTorrentsOnCompletion)
+ forceRecheck();
m_session->handleTorrentFinished(this);
+ }
}
void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p)
@@ -1530,7 +1508,7 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
resumeData["qBt-paused"] = isPaused();
resumeData["qBt-forced"] = isForced();
}
- resumeData["qBt-savePath"] = m_useASM ? "" : Utils::String::toStdString(m_savePath);
+ resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Utils::String::toStdString(m_savePath);
resumeData["qBt-ratioLimit"] = Utils::String::toStdString(QString::number(m_ratioLimit));
resumeData["qBt-category"] = Utils::String::toStdString(m_category);
resumeData["qBt-name"] = Utils::String::toStdString(m_name);
@@ -1616,7 +1594,7 @@ void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p
QString name = filePath(p->index);
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
- name.chop(QString(QB_EXT).size());
+ name.chop(QB_EXT.size());
qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name));
renameFile(p->index, name);
}
@@ -1637,7 +1615,7 @@ void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p
qDebug("Metadata received for torrent %s.", qPrintable(name()));
updateStatus();
if (m_session->isAppendExtensionEnabled())
- appendExtensionsToIncompleteFiles();
+ manageIncompleteFiles();
m_session->handleTorrentMetadataReceived(this);
if (isPaused()) {
@@ -1655,7 +1633,7 @@ void TorrentHandle::handleTempPathChanged()
void TorrentHandle::handleCategorySavePathChanged()
{
- if (m_useASM)
+ if (m_useAutoTMM)
move_impl(m_session->categorySavePath(m_category));
}
@@ -1663,10 +1641,7 @@ void TorrentHandle::handleAppendExtensionToggled()
{
if (!hasMetadata()) return;
- if (m_session->isAppendExtensionEnabled())
- appendExtensionsToIncompleteFiles();
- else
- removeExtensionsFromIncompleteFiles();
+ manageIncompleteFiles();
}
void TorrentHandle::handleAlert(libtorrent::alert *a)
@@ -1723,30 +1698,26 @@ void TorrentHandle::handleAlert(libtorrent::alert *a)
}
}
-void TorrentHandle::appendExtensionsToIncompleteFiles()
+void TorrentHandle::manageIncompleteFiles()
{
+ const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
QVector fp = filesProgress();
for (int i = 0; i < filesCount(); ++i) {
- if ((fileSize(i) > 0) && (fp[i] < 1)) {
- const QString name = filePath(i);
+ QString name = filePath(i);
+ if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1)) {
if (!name.endsWith(QB_EXT)) {
const QString newName = name + QB_EXT;
- qDebug("Renaming %s to %s", qPrintable(name), qPrintable(newName));
+ qDebug() << "Renaming" << name << "to" << newName;
renameFile(i, newName);
}
}
- }
-}
-
-void TorrentHandle::removeExtensionsFromIncompleteFiles()
-{
- for (int i = 0; i < filesCount(); ++i) {
- QString name = filePath(i);
- if (name.endsWith(QB_EXT)) {
- const QString oldName = name;
- name.chop(QString(QB_EXT).size());
- qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name));
- renameFile(i, name);
+ else {
+ if (name.endsWith(QB_EXT)) {
+ const QString oldName = name;
+ name.chop(QB_EXT.size());
+ qDebug() << "Renaming" << oldName << "to" << name;
+ renameFile(i, name);
+ }
}
}
}
@@ -1769,8 +1740,8 @@ void TorrentHandle::adjustActualSavePath_impl()
}
else {
// Moving all downloading torrents to temporary save path
- path = m_session->tempPath();
- qDebug("Moving torrent to its temp save path: %s", qPrintable(path));
+ path = m_session->torrentTempPath(hash());
+ qDebug() << "Moving torrent to its temp save path:" << path;
}
moveStorage(Utils::Fs::toNativePath(path));
diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h
index fef582b00..aad66f001 100644
--- a/src/base/bittorrent/torrenthandle.h
+++ b/src/base/bittorrent/torrenthandle.h
@@ -54,6 +54,8 @@ class QBitArray;
class QStringList;
template struct QPair;
+extern const QString QB_EXT;
+
namespace libtorrent
{
class alert;
@@ -227,8 +229,8 @@ namespace BitTorrent
QString rootPath(bool actual = false) const;
QString contentPath(bool actual = false) const;
- bool isASMEnabled() const;
- void setASMEnabled(bool enabled);
+ bool isAutoTMMEnabled() const;
+ void setAutoTMMEnabled(bool enabled);
QString category() const;
bool belongsToCategory(const QString &category) const;
bool setCategory(const QString &category);
@@ -245,7 +247,6 @@ namespace BitTorrent
qlonglong fileSize(int index) const;
QStringList absoluteFilePaths() const;
QStringList absoluteFilePathsUnwanted() const;
- QPair fileExtremityPieces(int index) const;
QVector filePriorities() const;
TorrentInfo info() const;
@@ -386,8 +387,7 @@ namespace BitTorrent
void adjustActualSavePath_impl();
void move_impl(QString path);
void moveStorage(const QString &newPath);
- void appendExtensionsToIncompleteFiles();
- void removeExtensionsFromIncompleteFiles();
+ void manageIncompleteFiles();
bool addTracker(const TrackerEntry &tracker);
bool addUrlSeed(const QUrl &urlSeed);
bool removeUrlSeed(const QUrl &urlSeed);
@@ -411,7 +411,7 @@ namespace BitTorrent
QQueue m_moveFinishedTriggers;
int m_renameCount;
- bool m_useASM;
+ bool m_useAutoTMM;
// Persistent data
QString m_name;
diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp
index d0ee54ce3..f06be60f7 100644
--- a/src/base/bittorrent/torrentinfo.cpp
+++ b/src/base/bittorrent/torrentinfo.cpp
@@ -26,6 +26,7 @@
* exception statement from your version.
*/
+#include
#include
#include
#include
@@ -138,6 +139,12 @@ int TorrentInfo::pieceLength() const
return m_nativeInfo->piece_length();
}
+int TorrentInfo::pieceLength(int index) const
+{
+ if (!isValid()) return -1;
+ return m_nativeInfo->piece_size(index);
+}
+
int TorrentInfo::piecesCount() const
{
if (!isValid()) return -1;
@@ -178,7 +185,7 @@ qlonglong TorrentInfo::fileSize(int index) const
qlonglong TorrentInfo::fileOffset(int index) const
{
- if (!isValid()) return -1;
+ if (!isValid()) return -1;
return m_nativeInfo->file_at(index).offset;
}
@@ -213,24 +220,79 @@ QByteArray TorrentInfo::metadata() const
QStringList TorrentInfo::filesForPiece(int pieceIndex) const
{
- if (pieceIndex < 0)
- return QStringList();
+ // no checks here because fileIndicesForPiece() will return an empty list
+ QVector fileIndices = fileIndicesForPiece(pieceIndex);
- std::vector files(
- nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
QStringList res;
- for (const libtorrent::file_slice& s: files) {
- res.append(filePath(s.file_index));
- }
+ res.reserve(fileIndices.size());
+ std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
+ [this](int i) { return filePath(i); });
+
return res;
}
+QVector TorrentInfo::fileIndicesForPiece(int pieceIndex) const
+{
+ if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
+ return QVector();
+
+ std::vector files(
+ nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
+ QVector res;
+ res.reserve(int(files.size()));
+ std::transform(files.begin(), files.end(), std::back_inserter(res),
+ [](const libt::file_slice &s) { return s.file_index; });
+
+ return res;
+}
+
+TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
+{
+ if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
+ return {};
+
+ int index = fileIndex(file);
+ if (index == -1) {
+ qDebug() << "Filename" << file << "was not found in torrent" << name();
+ return {};
+ }
+ return filePieces(index);
+}
+
+TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
+{
+ if (!isValid())
+ return {};
+
+ if ((fileIndex < 0) || (fileIndex >= filesCount())) {
+ qDebug() << "File index (" << fileIndex << ") is out of range for torrent" << name();
+ return {};
+ }
+
+ const libt::file_storage &files = nativeInfo()->files();
+ const auto fileSize = files.file_size(fileIndex);
+ const auto firstOffset = files.file_offset(fileIndex);
+ return makeInterval(static_cast(firstOffset / pieceLength()),
+ static_cast