mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-20 07:27:22 -06:00
Compare commits
314 Commits
release-3.
...
release-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6fe1d3620 | ||
|
|
73f08dd6e1 | ||
|
|
22dc71f365 | ||
|
|
455cc29dad | ||
|
|
9d39a2cddc | ||
|
|
efeb46571b | ||
|
|
a7b564bb2e | ||
|
|
10df509bce | ||
|
|
7b394d6e99 | ||
|
|
f1968de2c9 | ||
|
|
b5db0eeec1 | ||
|
|
3ed803f59b | ||
|
|
ff10702bfd | ||
|
|
73d3664f92 | ||
|
|
aa6025aa87 | ||
|
|
a344886ef6 | ||
|
|
c181019f40 | ||
|
|
5962efde23 | ||
|
|
d43466d466 | ||
|
|
29fb8e8085 | ||
|
|
b076ff68ac | ||
|
|
5f2da3a529 | ||
|
|
576fbe5dc1 | ||
|
|
8fc7f3fdc0 | ||
|
|
becd67ac84 | ||
|
|
7761a2604a | ||
|
|
c6546db138 | ||
|
|
f0dd4d5673 | ||
|
|
7f245b63d7 | ||
|
|
af17f4df9e | ||
|
|
3ec2f94b27 | ||
|
|
d7f1beb7f1 | ||
|
|
8c7a4ab86c | ||
|
|
ce2d42a264 | ||
|
|
dc011a5599 | ||
|
|
3c6b79805c | ||
|
|
7e7055f2ff | ||
|
|
18c9a65340 | ||
|
|
f5ce39a36a | ||
|
|
58e4f9d38e | ||
|
|
5bd7dce396 | ||
|
|
d9d49b6d0b | ||
|
|
cc48ca1fdc | ||
|
|
2dc6002064 | ||
|
|
1478b21e8d | ||
|
|
726c2fd56d | ||
|
|
df86d66702 | ||
|
|
223415fb69 | ||
|
|
a4c9b667a7 | ||
|
|
5f8e05ba50 | ||
|
|
ed2689de15 | ||
|
|
9e124527e1 | ||
|
|
59971aa577 | ||
|
|
164ca0289a | ||
|
|
c0aa50d74b | ||
|
|
66d7dc751c | ||
|
|
c1738f97f1 | ||
|
|
105874613a | ||
|
|
d6829b253b | ||
|
|
c1291539bf | ||
|
|
3060c979f4 | ||
|
|
d71a18b945 | ||
|
|
4078fc5e2d | ||
|
|
b7cb53a251 | ||
|
|
275a775769 | ||
|
|
1c49ff1df6 | ||
|
|
fc77fdbcb5 | ||
|
|
e0e757b610 | ||
|
|
33fe9b6d87 | ||
|
|
fca78d1a3f | ||
|
|
92794a786f | ||
|
|
08d9ad7f80 | ||
|
|
6023093329 | ||
|
|
c070193c30 | ||
|
|
633fb7a7de | ||
|
|
14a37e8d45 | ||
|
|
d03a71899e | ||
|
|
283338f5f3 | ||
|
|
b600253313 | ||
|
|
c97b5ab617 | ||
|
|
9016b698c9 | ||
|
|
eb7a7b9c04 | ||
|
|
9e0a3ee692 | ||
|
|
c899ed5b50 | ||
|
|
a6c50aff95 | ||
|
|
ab4a608342 | ||
|
|
e7cfd7a31d | ||
|
|
c4ea13b284 | ||
|
|
5d09639109 | ||
|
|
885f5b4bee | ||
|
|
3cd06c457a | ||
|
|
373e22660d | ||
|
|
92f58a40e7 | ||
|
|
dbf5a264bd | ||
|
|
145dcf9efe | ||
|
|
f05e25c750 | ||
|
|
fc02377171 | ||
|
|
3816052b0a | ||
|
|
fae583e0da | ||
|
|
1114c198ca | ||
|
|
c5f9567c0b | ||
|
|
e9a5768e4f | ||
|
|
858e5f8db8 | ||
|
|
0afa83dbfa | ||
|
|
fc95ce310e | ||
|
|
e2c9e7b877 | ||
|
|
b1e62ca006 | ||
|
|
a063ebd396 | ||
|
|
728dd744bb | ||
|
|
a0c1ee76a2 | ||
|
|
c7b15b9cc5 | ||
|
|
0770fe8b09 | ||
|
|
cf98220c40 | ||
|
|
2eb393ae9a | ||
|
|
d65d11d64c | ||
|
|
f4fdb80cc9 | ||
|
|
50a0ce1da2 | ||
|
|
2e9370481d | ||
|
|
c60c58b092 | ||
|
|
ba5dded076 | ||
|
|
eb36949e87 | ||
|
|
b7358a3039 | ||
|
|
d0f082e238 | ||
|
|
b301444f5e | ||
|
|
900a80adc1 | ||
|
|
d4887ee736 | ||
|
|
e23566cde1 | ||
|
|
b5f30a6efb | ||
|
|
deb1d54999 | ||
|
|
7779efbc30 | ||
|
|
acd65e3185 | ||
|
|
f2dd050ade | ||
|
|
9c28a48f2e | ||
|
|
3e8af95d30 | ||
|
|
cba9d222de | ||
|
|
b137eb01ed | ||
|
|
fd156580a0 | ||
|
|
21c17f2a81 | ||
|
|
5f19cd2c0e | ||
|
|
9279fedd49 | ||
|
|
2361d6f12d | ||
|
|
78bfbc5669 | ||
|
|
6101f11673 | ||
|
|
15361a6c16 | ||
|
|
835a999464 | ||
|
|
b9b0739efd | ||
|
|
a34c072b7a | ||
|
|
72e88ba7ec | ||
|
|
afb143cad1 | ||
|
|
1ca5d10abb | ||
|
|
67675b6cdc | ||
|
|
ef47983d8d | ||
|
|
cf47517ee3 | ||
|
|
33199bbd74 | ||
|
|
4fb735c6d1 | ||
|
|
99ca42bf48 | ||
|
|
f6b4b5f6f0 | ||
|
|
6f73c32fcc | ||
|
|
c1611988b4 | ||
|
|
fbe1d2d78a | ||
|
|
34fec15ceb | ||
|
|
1977e4f24d | ||
|
|
5774d27c7b | ||
|
|
3a6a857371 | ||
|
|
e2f88feaf9 | ||
|
|
16fec04b6a | ||
|
|
fbac4afa2d | ||
|
|
d6e9736af1 | ||
|
|
76b3c72c86 | ||
|
|
ef954fea55 | ||
|
|
11d085712e | ||
|
|
e4bc7852e5 | ||
|
|
3d107e0588 | ||
|
|
8e5c31ae82 | ||
|
|
3c03ccc14a | ||
|
|
0795320086 | ||
|
|
bd6877a0bd | ||
|
|
b47292c39f | ||
|
|
3d2b1b876b | ||
|
|
843472e663 | ||
|
|
e952e3167e | ||
|
|
67f05edf71 | ||
|
|
3ab76cb985 | ||
|
|
509d97b0ad | ||
|
|
67ae08df3c | ||
|
|
38de6b1e41 | ||
|
|
72b179805d | ||
|
|
beeda5e0b2 | ||
|
|
356db2f2f7 | ||
|
|
19acbf587f | ||
|
|
5eee3f7357 | ||
|
|
8f990d5d7e | ||
|
|
f0ec94c31c | ||
|
|
e37dfa96f9 | ||
|
|
4b48db3273 | ||
|
|
545002a809 | ||
|
|
e2e9470e10 | ||
|
|
0bf1abba6a | ||
|
|
24165856e9 | ||
|
|
657f0640b9 | ||
|
|
faffefc4ff | ||
|
|
0619aacf1f | ||
|
|
9edbbb6473 | ||
|
|
87ee720c0c | ||
|
|
b7ca036bc3 | ||
|
|
c4442c98b9 | ||
|
|
fee8036a7a | ||
|
|
94bd4308c7 | ||
|
|
5e5785435a | ||
|
|
d7f02a7ee7 | ||
|
|
e6480f9dff | ||
|
|
e9f6cfc2e8 | ||
|
|
4f68d263d4 | ||
|
|
6a672472a2 | ||
|
|
3590ac2997 | ||
|
|
98fe5e11dd | ||
|
|
9df5c0292b | ||
|
|
1f2d25a1ff | ||
|
|
f1dd7a091c | ||
|
|
5457bde8d0 | ||
|
|
2e325d9506 | ||
|
|
0264a7bf58 | ||
|
|
32fe930b88 | ||
|
|
72883ffb73 | ||
|
|
f2c24dd8c3 | ||
|
|
8904139c6d | ||
|
|
571f46886f | ||
|
|
0cd691e167 | ||
|
|
4f65e2d468 | ||
|
|
35981f6ef5 | ||
|
|
dc493880f3 | ||
|
|
872e78ca21 | ||
|
|
7b601796d7 | ||
|
|
09ef552aea | ||
|
|
b4c9cae0d1 | ||
|
|
6d2a0ae83b | ||
|
|
238a925000 | ||
|
|
cce01cfb8e | ||
|
|
b2db1972f3 | ||
|
|
8b851fe2b9 | ||
|
|
e3c9488fb0 | ||
|
|
c27fb110f8 | ||
|
|
ec61f24099 | ||
|
|
130ee5a71e | ||
|
|
cb3e7e6bd6 | ||
|
|
7fd65d5428 | ||
|
|
442f521bf5 | ||
|
|
016052aea1 | ||
|
|
411982e2b0 | ||
|
|
4b93ccd4e4 | ||
|
|
6603a8947a | ||
|
|
031e354577 | ||
|
|
ec7fb331e0 | ||
|
|
a232b77104 | ||
|
|
8c11245469 | ||
|
|
60857d3b8e | ||
|
|
2fe6b76968 | ||
|
|
6c7350fce0 | ||
|
|
c770f4d0bc | ||
|
|
876e96911f | ||
|
|
5620fd120e | ||
|
|
ea7f6046b4 | ||
|
|
fddac5d679 | ||
|
|
2c4bc68af1 | ||
|
|
7676f49612 | ||
|
|
e879279019 | ||
|
|
84b7680718 | ||
|
|
be180140a3 | ||
|
|
c051c279d4 | ||
|
|
964dcc4d8a | ||
|
|
64cf93b889 | ||
|
|
365737afe1 | ||
|
|
2cf14f0120 | ||
|
|
46bb25ba9f | ||
|
|
ee5a72c570 | ||
|
|
18b56f4d0a | ||
|
|
f626276218 | ||
|
|
e28554f85c | ||
|
|
a0a3447b2e | ||
|
|
4049ca7308 | ||
|
|
c28151ba92 | ||
|
|
81e1a050a2 | ||
|
|
34d5824c4a | ||
|
|
bb875df400 | ||
|
|
61f47d366a | ||
|
|
8347eb157d | ||
|
|
dd22c9b138 | ||
|
|
7f6ad55042 | ||
|
|
55b06ab9ba | ||
|
|
21f0a5eb76 | ||
|
|
16ed11623f | ||
|
|
c184cf8d7d | ||
|
|
6a90214eb2 | ||
|
|
226ec0610a | ||
|
|
694bd7cb95 | ||
|
|
9e807e7151 | ||
|
|
78fe7fcf9d | ||
|
|
c2465f931e | ||
|
|
8d50325961 | ||
|
|
570a651a59 | ||
|
|
0eaa2aeef2 | ||
|
|
2c7e309493 | ||
|
|
ded3cf5798 | ||
|
|
4edac3e974 | ||
|
|
53885fb5e4 | ||
|
|
3942c095f6 | ||
|
|
94be3b930d | ||
|
|
09bc14cc57 | ||
|
|
51b93b4284 | ||
|
|
9c50ea14cb | ||
|
|
42a74ea78e | ||
|
|
1ac68a9192 | ||
|
|
29b5d460ea | ||
|
|
a441bca4de |
127
.travis.yml
127
.travis.yml
@@ -14,18 +14,10 @@ env:
|
||||
- lt_branch=RC_1_0 qt=5 gui=false
|
||||
- lt_branch=RC_1_0 qt=4 gui=true
|
||||
- lt_branch=RC_1_0 qt=4 gui=false
|
||||
|
||||
global:
|
||||
- secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
|
||||
- coverity_branch: coverity_scan
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: linux
|
||||
env: lt_branch=RC_1_0 qt=5 gui=true
|
||||
- os: linux
|
||||
env: lt_branch=RC_1_0 qt=5 gui=false
|
||||
|
||||
branches:
|
||||
except:
|
||||
- search_encoding_windows
|
||||
@@ -37,87 +29,124 @@ notifications:
|
||||
on_failure: change
|
||||
|
||||
# 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
|
||||
#sudo: false
|
||||
cache: ccache
|
||||
|
||||
# opt-in Ubuntu Trusty
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
addons:
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "qbittorrent/qBittorrent"
|
||||
description: "Build submitted via Travis CI"
|
||||
build_command_prepend: "./bootstrap.sh && ./configure $qbtconf && echo QMAKE_CC=$CC >> conf.pri && echo QMAKE_CXX=$CXX >> conf.pri"
|
||||
build_command_prepend: "./bootstrap.sh && ./configure $qbtconf"
|
||||
build_command: make
|
||||
branch_pattern: $coverity_branch
|
||||
notification_email: sledgehammer999@qbittorrent.org
|
||||
apt:
|
||||
sources:
|
||||
#sources:
|
||||
# sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
|
||||
- ubuntu-toolchain-r-test
|
||||
- boost-latest
|
||||
#- ubuntu-toolchain-r-test
|
||||
#- boost-latest
|
||||
packages:
|
||||
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
|
||||
- autoconf
|
||||
- automake
|
||||
- colormake
|
||||
- g++-4.8
|
||||
- libssl-dev
|
||||
- libboost1.55-dev
|
||||
- libboost-system1.55-dev
|
||||
- libqt4-dev
|
||||
# Uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
|
||||
- libboost-dev
|
||||
- libboost-system-dev
|
||||
# uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
|
||||
#- libtorrent-rasterbar6
|
||||
|
||||
before_install:
|
||||
# Only allow specific build for coverity scan, others will stop
|
||||
# only allow specific build for coverity scan, others will stop
|
||||
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" = "RC_1_0" -a "$gui" = true ]; then exit ; fi
|
||||
|
||||
- shopt -s expand_aliases
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then export CC=gcc-4.8 CXX=g++-4.8 ; fi
|
||||
- if [ "$TRAVIS_BRANCH" != "$coverity_branch" -a "$TRAVIS_OS_NAME" = "linux" ]; then dpkg-query -L ccache && export PATH="/usr/lib/ccache/:$PATH" ; fi
|
||||
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
|
||||
|
||||
- libt_path="$HOME/libt_install"
|
||||
#- libt_path="$HOME/libt_install"
|
||||
#- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
|
||||
- qbt_path="$HOME/qbt_install"
|
||||
- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
|
||||
- qbtconf="$qbtconf --prefix="$qbt_path" --with-qt4 PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":$PKG_CONFIG_PATH"
|
||||
- qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":$PKG_CONFIG_PATH"
|
||||
|
||||
# Options for specific branches
|
||||
# Also setup a virtual display for after_success target when gui == true
|
||||
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ;
|
||||
elif [ "$TRAVIS_OS_NAME" = "linux" ]; 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 ;
|
||||
# options for specific branches
|
||||
- if [ "$qt" = 4 ]; then qbtconf="$qbtconf --with-qt4" ; fi
|
||||
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
# 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
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||
qbtconf="$qbtconf --disable-qt-dbus" ;
|
||||
fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then qbtconf="$qbtconf --disable-qt-dbus" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$qt" = 4 ]; then brew install qt; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$qt" = 5 ]; then qbtconf="$qbtconf --with-qt4=no" ; fi
|
||||
|
||||
# Print settings
|
||||
# print settings
|
||||
- echo $lt_branch
|
||||
- echo $gui
|
||||
- echo $ltconf
|
||||
- echo $qbtconf
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ccache -V && ccache --show-stats && ccache --zero-stats ; fi
|
||||
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" != "dist" ]; then cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" != "dist" ]; then cd libtorrent && ./autotool.sh && ./configure $ltconf && make install && cd "$TRAVIS_BUILD_DIR" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update > /dev/null && brew install colormake libtorrent-rasterbar ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 ; fi
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
# libtorrent
|
||||
sudo add-apt-repository --yes ppa:qbittorrent-team/qbittorrent-stable ;
|
||||
sudo apt-get update -qq ;
|
||||
sudo apt-get install -qq libtorrent-rasterbar-dev ;
|
||||
|
||||
# build libtorrent from source
|
||||
#if [ "$lt_branch" != "dist" ]; then
|
||||
#cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ;
|
||||
#cd libtorrent && ./autotool.sh && ./configure $ltconf && make install ;
|
||||
#fi ;
|
||||
|
||||
# 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
|
||||
# dependencies
|
||||
brew update > /dev/null && brew install colormake ccache libtorrent-rasterbar ;
|
||||
|
||||
# Qt
|
||||
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.6.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
|
||||
- ./bootstrap.sh && ./configure $qbtconf
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then echo "QMAKE_CC=$CC" >> conf.pri && echo "QMAKE_CXX=$CXX" >> conf.pri ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs/' src/Makefile ; fi
|
||||
- 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/^\(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" && export LD_PRELOAD="$libt_path/lib/libtorrent-rasterbar.so:$LD_PRELOAD" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "src/$qbt_exe.app/Contents/MacOS" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; 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:
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ccache --show-stats ; fi
|
||||
- if [ "$use_ccache" = true ]; then ccache --show-stats ; fi
|
||||
|
||||
@@ -3,6 +3,7 @@ host = https://www.transifex.com
|
||||
|
||||
[qbittorrent.qbittorrent_v3_3_x]
|
||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||
lang_map = pt: pt_PT
|
||||
source_file = src/lang/qbittorrent_en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
|
||||
53
5B7CC9A2.asc
Normal file
53
5B7CC9A2.asc
Normal file
@@ -0,0 +1,53 @@
|
||||
-----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
|
||||
EDwWxtTS0a7Rfx3DxLkCDQRXDSCMARAAqMIVJizEJp205c546IN75xeYiFszNXcs
|
||||
3768IY8bOoWj+rTwt2wIwtL/3O5K2dG79CSt2H5o6BPKmq43tOO60YW3Yk3m9BB/
|
||||
gnAVqk0QOPr5O8+yeBzdElU8CZh6y6zZMWugSkNmTDm6jZzPhgNjcjrit/dl9+0D
|
||||
GqJQcqoD8WzEWNcWrMHVz9cDewnLSVkwR758mZMaIiL7R10MZ++tNrC0j69UINqx
|
||||
+9z1r1J07+NNnxqSTxVRcbjPYtM9E+tUiVFS2HPWN9ShVDkBAEdoWh90qzRaMiFl
|
||||
2NGNGOD1iHx/xr06RMeGEEXt2vhSlhfMW2YQW+UD2jzlFbARf53v39MUKKscGuIp
|
||||
BhxGw3JCq4l6qLW/bDkgnoXlOhZDmhQm6OpsjAyk9IEdd3ponSc7yYD3mUkJKR9e
|
||||
TaALD5t6TQGyNHakb4UfoXtE2RR78cbPlLIwag7eQ8GsNA+dfjowmOZdojx3ROsH
|
||||
ZdGQwb0YFLjuKAusA3TY+lCfbS6kzE2iI2DuaW+3dICcLrYuibbVb0CBNHyD+8KE
|
||||
tczdur/wm0lhqyVJkGyZKZT8C2cPxywKgy1Rn6F8Yfmj0Lna3nvtaZu0ZUS4/8Li
|
||||
t5PcOso1lSmYBuD6yq+GEAMCnUmn1Pm8eZRMlxxQuTPvyJKQrRDhbtAAr472MSno
|
||||
JKlS4SfaUF0AEQEAAYkCHwQYAQgACQUCVw0gjAIbDAAKCRBuSi0CW3zJomZDD/9I
|
||||
Jmzd5hiEzntlp84pyIJcfyIRe4KImvldAy6T02OSIbF1HzCNnwmqIPob6MOdMZ+K
|
||||
NwMK0htRkrRr/zM034+lBiWKZt+tVYHu49ioTYXEjAc5qDJE09Sq7HceQnhgE48f
|
||||
1n54XGT5G2w5gw+/a8Qn1SceE44VwXafL3E1gKaOrrsb1UH/AJhp+W4VMu+7bLXu
|
||||
7h1tN6v2PhvCYvBt3zyy8Q8xfJ2x7/D1lbF8ATJAiZ/km9x5bRm7OGRliVYaUe1n
|
||||
yR42fZOj3CBmAR0+lZLgjriqdMXrs+qlBbrmAhkn0XPQXAeaPifKoKIGDAUWIsqD
|
||||
HqM7imMGT+MR9APfSw8M4enOJWL+HnKpVBEARCEDpaFpJ3u7QRucFybpEhvIymoN
|
||||
ftyw+urId2Eg2K33NypeZo3M1K2LC65f2Ta7f/sZcIDUTbgW+m334fgVl1KptDA5
|
||||
DX3U9lTci7mi4uPuAFtbWrB1di4jYrxXYuzFm5g4xTb0Hw3kYIB6WXF+I7i0JaGO
|
||||
THxPC5X5lIAZrYrkxh+1n1Y1CY+TC8JcTzwORJIbFFm9tD/BHXa4849k4DVvFYCZ
|
||||
khq+/56FKZfoVByhB+x+2GaMlsBm1uPniO4lAakFPpIi0kaap4UVayQ/7ak+Bhsc
|
||||
AIHZUy6NtgZkuvW3xdpwp07LYo2ilhMI8RnzmtoRmg==
|
||||
=tDGM
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
65
CMakeLists.txt
Normal file
65
CMakeLists.txt
Normal file
@@ -0,0 +1,65 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_policy(VERSION 3.5)
|
||||
|
||||
project(qBittorrent VERSION 3.4.0.0)
|
||||
|
||||
set(VER_MAJOR ${qBittorrent_VERSION_MAJOR})
|
||||
set(VER_MINOR ${qBittorrent_VERSION_MINOR})
|
||||
set(VER_BUGFIX ${qBittorrent_VERSION_PATCH})
|
||||
set(VER_BUILD ${qBittorrent_VERSION_TWEAK})
|
||||
set(VER_STATUS "alpha") # Should be empty for stable releases!
|
||||
|
||||
# Don't touch the rest part
|
||||
set(PROJECT_VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUGFIX}")
|
||||
|
||||
if (NOT VER_BUILD EQUAL 0)
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION}.${VER_BUILD}")
|
||||
endif()
|
||||
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION}${VER_STATUS}")
|
||||
|
||||
add_definitions(-DVERSION_MAJOR=${VER_MAJOR})
|
||||
add_definitions(-DVERSION_MINOR=${VER_MINOR})
|
||||
add_definitions(-DVERSION_BUGFIX=${VER_BUGFIX})
|
||||
add_definitions(-DVERSION_BUILD=${VER_BUILD})
|
||||
|
||||
# os2 {
|
||||
# DEFINES += VERSION=\'\"v$${PROJECT_VERSION}\"\'
|
||||
# } else {
|
||||
add_definitions(-DVERSION="v${PROJECT_VERSION}")
|
||||
# }
|
||||
list(APPEND CMAKE_MODULE_PATH ${qBittorrent_SOURCE_DIR}/cmake/Modules)
|
||||
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og")
|
||||
if (UNIX AND NOT APPLE)
|
||||
include(GNUInstallDirs)
|
||||
endif (UNIX AND NOT APPLE)
|
||||
|
||||
if(WIN32)
|
||||
include(winconf)
|
||||
endif(WIN32)
|
||||
|
||||
# we need options here, because they are used not only in "src" subdir, but in the "dist" dir too
|
||||
include(CMakeDependentOption)
|
||||
|
||||
option(QT5 "Compile using Qt5" ON)
|
||||
option(SYSTEM_QTSINGLEAPPLICATION
|
||||
"Use the system qtsingleapplication library or shipped one otherwise")
|
||||
cmake_dependent_option(SYSTEM_QJSON
|
||||
"Use the shipped qjson library or the system one (Qt4 only)" OFF "NOT QT5" OFF)
|
||||
|
||||
option(GUI "Allows to disable GUI for headless running. Disables QtDBus and the GeoIP Database" ON)
|
||||
|
||||
option(WEBUI "Allows to disable the WebUI." ON)
|
||||
|
||||
if (WIN32)
|
||||
option(STACKTRACE_WIN "")
|
||||
else (WIN32)
|
||||
cmake_dependent_option(SYSTEMD "Install the systemd service file (headless only)" OFF
|
||||
"NOT GUI" OFF)
|
||||
cmake_dependent_option(DBUS "Enable use of QtDBus (GUI only)" ON "GUI" OFF)
|
||||
endif(WIN32)
|
||||
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(dist)
|
||||
@@ -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.<br/>
|
||||
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<ClassName> 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).<br/>
|
||||
<br/>
|
||||
Example:
|
||||
```c++
|
||||
// examplewidget.cpp
|
||||
|
||||
#include "examplewidget.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
#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,11 +250,51 @@ a += "b"
|
||||
+ "d";
|
||||
```
|
||||
|
||||
* **auto** keyword
|
||||
|
||||
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.<br/>
|
||||
<br/>
|
||||
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.<br/>
|
||||
<br/>
|
||||
Some valid use cases:
|
||||
```c++
|
||||
template <typename List>
|
||||
void doSomethingWithList(const List &list)
|
||||
{
|
||||
foreach (const auto &item, list) {
|
||||
// we don't know item type here so we use 'auto' keyword
|
||||
// do something with item
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = container.begin(), end = container.end(); it != end; ++it) {
|
||||
// we don't need to know the exact iterator type,
|
||||
// because all iterators have the same interface
|
||||
}
|
||||
|
||||
auto spinBox = static_cast<QSpinBox*>(sender());
|
||||
// we know the variable type based on the right-hand expression
|
||||
```
|
||||
|
||||
* 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.<br/>
|
||||
There should not be a space between increment/decrement and its operand.<br/>
|
||||
Some valid use cases:
|
||||
```c++
|
||||
for (int a=0; a<b; ++b) {
|
||||
a += 20;
|
||||
a = (b <= MAX_B ? b : MAX_B);
|
||||
++a;
|
||||
b--;
|
||||
|
||||
for (int a = 0; a < b; ++b) {
|
||||
// code
|
||||
}
|
||||
```
|
||||
|
||||
@@ -225,5 +304,5 @@ for (int a=0; a<b; ++b) {
|
||||
|
||||
* Method definitions aren't allowed in header files
|
||||
|
||||
###8. Not covered above###
|
||||
###10. Not covered above###
|
||||
If something isn't covered above, just follow the same style the file you are editing has. If that particular detail isn't present in the file you are editing, then use whatever the rest of the project uses.
|
||||
72
COPYING
72
COPYING
@@ -12,15 +12,15 @@ exception statement from your version.
|
||||
|
||||
----------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -70,7 +70,7 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -269,7 +269,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -291,64 +291,4 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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
|
||||
|
||||
137
Changelog
137
Changelog
@@ -1,3 +1,140 @@
|
||||
* Mon Jun 20 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.5
|
||||
- FEATURE: Implement Torrent Management Mode(TMM) (glassez)
|
||||
- FEATURE: New cookies management dialog and various related fixes (glassez)
|
||||
- FEATURE: Use unique temp directories (temp_path/<truncated_torrent_hash>). Closes #5154. (glassez)
|
||||
- FEATURE: Display notifications when a torrent is added. Closes #334 and #915. (sledgehammer999)
|
||||
- FEATURE: Sort labels with natural sort algorithm in the right-click menu. Closes #3919. (Chocobo1)
|
||||
- FEATURE: Add option to automatically remove .torrent files upon adding (Eugene Shalygin)
|
||||
- FEATURE: Add option to bind directly to an IP instead of using a network Interface (Sjoerd van der Berg, sledgehammer999)
|
||||
- FEATURE: Detailed tooltips on the progress and availability bars in the General button of each torrent. (Eugene Shalygin)
|
||||
- FEATURE: Let user able to specifiy a filter when choosing an IP filter file (Chocobo1)
|
||||
- FEATURE: Improve usability of "Run External Program". Users can write (platform dependent) shell scripts now. (Chocobo1)
|
||||
- PERFORMANCE: Optimize drawing in speed graph (Anton Lashkov, Chocobo1)
|
||||
- BUGFIX: Fix memory leak. (sledgehammer999)
|
||||
- BUGFIX: Fix resizing bug in "add torrent dialog". Closes #5036. (Chocobo1)
|
||||
- BUGFIX: Fix qBittorrent doesn't exit immediately when "all donwloads are done -> exit" option enabled. (glassez, Chocobo1)
|
||||
- BUGFIX: Display the filepath when a torrent fails to load. Closes #100 and #805. (sledgehammer999)
|
||||
- BUGFIX: Fix Add tracker dialog empty trackers (ngosang)
|
||||
- BUGFIX: Fix Add tracker dialog URL download (ngosang)
|
||||
- BUGFIX: Fix torrent adding with existing data (glassez)
|
||||
- BUGFIX: Try to find incomplete files for new torrent (glassez)
|
||||
- BUGFIX: Fix rechecking after torrent is finished (glassez)
|
||||
- BUGFIX: Fix duplicate network interfaces. Closes #5131 (ngosang)
|
||||
- BUGFIX: Fix .!qB extension is added when disabled (glassez)
|
||||
- BUGFIX: Fix "IP Filtering - Apply to trackers" wasn't being applied. Closes #5217. (Chocobo1)
|
||||
- BUGFIX: Don't resize the Country column needlessly (thalieht)
|
||||
- BUGFIX: Fix crashing when exiting the program while the Options window was showing. Closes #4871, #5049. (Chocobo1)
|
||||
- BUGFIX: Fix parsing of eMule .DAT filters. Closes #5281. (thalieht, sledgehammer999)
|
||||
- WEBUI: Implement in setting/removing/showing categories. (buinsky)
|
||||
- WEBUI: Add 'Added on' column in Webui. Closes #5145,#1092,#738 (Ibrahim Tachijian)
|
||||
- WEBUI: Add command to get the logs (pmzqla)
|
||||
- WEBUI: Expose Add trackers feature (pmzqla)
|
||||
- WEBUI: Bump API_VERSION and API_VERSION_MIN to 10. (sledgehammer999)
|
||||
- SEARCH: Implement search filters in the proxy model. (Eugene Shalygin)
|
||||
- SEARCH: Optimize widgets inside of the search tab (Eugene Shalygin)
|
||||
- SEARCH: Remove duplicated code from search tab and widget (Eugene Shalygin)
|
||||
- SEARCH: Use nova2dl.py script instead of DownloadManager. Closes #5026. (glassez)
|
||||
- SEARCH: Various UI cleanups and optimizations. (Chocobo1, ngosang)
|
||||
- SEARCH: Fix Torrentz, Mininova, LegitTorrents, PirateBay plugins (ngosang)
|
||||
- SEARCH: Fix toolbar resizing when m_searchFilter appears/disappears. Closes #5120. (Chocobo1)
|
||||
- SEARCH: Use QPalette::LinkVisited color for downloaded items in search results (Eugene Shalygin)
|
||||
- SEARCH: Update Python version requirements (min 2.7.9 / 3.3.0) (ngosang)
|
||||
- SEARCH: Remove filters from Torrentreactor, BTDigg and Demonoid. It is done in the GUI now. (ngosang)
|
||||
- RSS: default refresh interval to 30 mins (botmtl)
|
||||
- COSMETIC: Improve TransferListDelegate::sizeHint (Chocobo1)
|
||||
- COSMETIC: Fix long text clipping. Closes #5091. (Chocobo1)
|
||||
- COSMETIC: Minor change in Stats dialog (ngosang)
|
||||
- COSMETIC: Fix Update all button height in RSS tab (ngosang)
|
||||
- COSMETIC: Disable `comboHideZero` when `checkHideZero` is unchecked. (Chocobo1)
|
||||
- COSMETIC: Disable `checkLimituTPConnections` when `checkuTP` is unchecked. (Chocobo1)
|
||||
- COSMETIC: Enable word wrap for "Run external program" help text (Chocobo1)
|
||||
- COSMETIC: Fix mutually exclusive radio buttons can be unselected. (Chocobo1)
|
||||
- COSMETIC: Set About tab font-size to default (zywo)
|
||||
- COSMETIC: Improve wordings in "Auto download torrent" section (Chocobo1)
|
||||
- COSMETIC: Change "Auto download torrent" default save path to "default location". (Chocobo1)
|
||||
- COSMETIC: Change ambiguous text "Copy selected" to "Copy IP:port" (Chocobo1)
|
||||
- COSMETIC: Improve error messages for "Auto download torrents" (Chocobo1)
|
||||
- COSMETIC: Slim down 'Downloads' page of options window. (sledgehammer999)
|
||||
- COSMETIC: Cleanup Connection page in Option dialog. Closes #845. (Chocobo1)
|
||||
- COSMETIC: Fix reload button size is not the same size as "..." button in options dialog. (Chocobo1)
|
||||
- WINDOWS: Let Windows handle all widgets scaling. (Chocobo1)
|
||||
- WINDOWS: Improve stack trace for windows by including source filenames and line numbers (SeigneurSerpent)
|
||||
- OSX: Fix qt5 bundle on OSX. Closes #4014. (sledgehammer999)
|
||||
- OTHER: Make AddNewTorrentDialog behavior uniform (glassez)
|
||||
- OTHER: Optimize the shutdown dialog (Chocobo1)
|
||||
- OTHER: Enable access to shutdown functions when configured with `--disable-gui` option (Chocobo1)
|
||||
- OTHER: Delete Import Torrent Dialog. Just use the "add new torrent" dialog. (glassez)
|
||||
- OTHER: Optimize code for natural sorting (Chocobo1)
|
||||
- OTHER: Use new alert dispathing API for libtorrent 1.1.x (glassez)
|
||||
- OTHER: Fix gcc 6 compilation with qmake. See #5237. (sledgehammer999)
|
||||
|
||||
|
||||
* Tue Mar 29 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.4
|
||||
- FEATURE: Download more pieces in "Download first and last pieces first" feature (ngosang)
|
||||
- FEATURE: Unlock first column in peerlist too (thalieht)
|
||||
- FEATURE: Add "Hide zero values" option. Closes #3543. (Chocobo1)
|
||||
- FEATURE: Add a "remaining" column to the torrent content model (Ben Lau)
|
||||
- FEATURE: Allow to toggle columns in peerlist (thalieht)
|
||||
- FEATURE: Add ability to filter log messages by type. (sledgehammer999)
|
||||
- FEATURE: Add ability to write the log to file. (sledgehammer999)
|
||||
- FEATURE: Add 'never show again' checkbox/pref to auto-exit confirm dialog (d3fault, sledgehammer999, Chocobo1)
|
||||
- PERFORMANCE: Perform fastresume data saving in separate thread (glassez)
|
||||
- PERFORMANCE: Optimize session startup (glassez)
|
||||
- BUGFIX: Save resume data using QSaveFile (Qt5 only). This reduces chances of corrupt files. (glassez)
|
||||
- BUGFIX: Check torrent file permissions before opening (birdie-github)
|
||||
- BUGFIX: Always update native session's announce_ip setting (Jesse Connop)
|
||||
- BUGFIX: Fix loading *.magnet files from watched folders. Closes #4701. (sledgehammer999)
|
||||
- BUGFIX: Fix upgrade corrupted fastresume file (glassez)
|
||||
- BUGFIX: Fix total values for "Seeds" & "Peers" (Chocobo1)
|
||||
- BUGFIX: Fix potential race condition. Closes #4742. (Chocobo1)
|
||||
- BUGFIX: Don't merge trackers for private torrents. Closes #2928. (sledgehammer999)
|
||||
- BUGFIX: Fix double buttons in "Add New Torrent" dialog. (Chocobo1)
|
||||
- BUGFIX: Fix malformed date header in email. Closes #4828. (Chocobo1)
|
||||
- BUGFIX: Save "Run external program" input as is. Closes #4830. (Chocobo1)
|
||||
- BUGFIX: Enable "filename" column in peers list again. Crash is fixed now. (Eugene Shalygin)
|
||||
- BUGFIX: Don't display warning when folder name stayed the same after rename. (sledgehammer999)
|
||||
- BUGFIX: Fix selection of Portuguese translation files. (sledgehammer999)
|
||||
- BUGFIX: Fix selection of Esperanto locale. Closes #4999. (sledgehammer999)
|
||||
- BUGFIX: Fix "caja" file manager opens the file instead of opens the directory. Closes #5003. (Chocobo1)
|
||||
- BUGFIX: Fix periodic latency spikes on Windows with WiFi connections. Closes #4209. (sledgehammer999)
|
||||
- BUGFIX: Potentially fix a random crash coming from the sidepanel (sledgehammer999, ngosang)
|
||||
- WEBUI: Fix max_ratio precision. Closes #4707 (ngosang)
|
||||
- WEBUI: Fix JavaScript exception on WebUI load (buinsky)
|
||||
- WEBUI: Fix translation (buinsky)
|
||||
- WEBUI: Submit the label in the new label dialog on pressing enter key (buinsky)
|
||||
- WEBUI: Check WebUI username and password length. Closes #4191 (ngosang)
|
||||
- WEBUI: Minor changes in CSS styles (ngosang)
|
||||
- WEBUI: Add "Added on" and "Completion on" fields to query/torrents query response (buinsky)
|
||||
- WEBUI: Do not try to parse request message when content-length is 0 (Dan Seminara)
|
||||
- WEBUI: Support SSL certificate bundles. Issue #4896. (UnDifferential)
|
||||
- WEBUI: Change the order of the values of speed labels (buinsky)
|
||||
- WEBUI: Bump WebUI API_VERSION
|
||||
- SEARCH: Update PirateBay plugin. (ngosang)
|
||||
- SEARCH: Added TorLock search engine (ngosang)
|
||||
- COSMETIC: Fix splash screen staying on top of all windows. Closes #1391. (sledgehammer999)
|
||||
- COSMETIC: Rearrange advanced settings (Chocobo1)
|
||||
- COSMETIC: Cleanup "about" dialog (Chocobo1)
|
||||
- COSMETIC: Cleanup "Add New Torrent" dialog (Chocobo1)
|
||||
- COSMETIC: Use short date in addnewtorrentdialog (Chocobo1)
|
||||
- COSMETIC: Change "Free disk space" to "Free space on disk" (Chocobo1)
|
||||
- COSMETIC: Let OS handle DPI scaling for now. Should let Qt do the work when it's more mature. (Chocobo1)
|
||||
- COSMETIC: Put comment_lbl in QScrollArea. Closes #4881. (Chocobo1)
|
||||
- COSMETIC: Change the order of the values of speed labels (buinsky)
|
||||
- COSMETIC: Cleanup the Log tab (Chocobo1)
|
||||
- COSMETIC: Cleanup the RSS tab (Chocobo1)
|
||||
- OTHER: Set "Show splash screen on start up" option default to off (Chocobo1)
|
||||
- OTHER: Support for cross-compilation with MXE (Boris Nagaev)
|
||||
- OTHER: Add basic (and unofficial) cmake support (Eugene Shalygin)
|
||||
- OTHER: Move some URLs overs to https (funkydude)
|
||||
- OTHER: Add appveyor support (Chocobo1)
|
||||
- OTHER: TravisCI: switch to Trusty image. Closes #4953. (Chocobo1)
|
||||
- OTHER: Many other internal code restructuring, cleaning and fixing.
|
||||
|
||||
* Thu Jan 21 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.3
|
||||
- BUGFIX: Temporarily disable "filename" column of peers view. It has a bug that causes frequent crashes. See issue #4597.
|
||||
- WEBUI: Move style of dynamic table header to CSS (buinsky)
|
||||
- WEBUI: Fix unnecessary updates of torrent peers table (buinsky)
|
||||
|
||||
* Tue Jan 19 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.2
|
||||
- FEATURE: Add a new column to peers list that shows list of files which are downloaded right now from a peer. (evsh)
|
||||
- FEATURE: Improve the "Watch folders" UI. Closes #4300. You'll need to redo your watch folders settings. (sledgehammer999, glassez)
|
||||
|
||||
12
README.md
12
README.md
@@ -1,7 +1,8 @@
|
||||
qBittorrent - A BitTorrent client in Qt
|
||||
------------------------------------------
|
||||
|
||||
[](https://travis-ci.org/qbittorrent/qBittorrent)
|
||||
[](https://travis-ci.org/qbittorrent/qBittorrent)
|
||||
[](https://ci.appveyor.com/project/qbittorrent/qBittorrent)
|
||||
[](https://scan.coverity.com/projects/5494)
|
||||
********************************
|
||||
### Description:
|
||||
@@ -13,7 +14,7 @@ out there. qBittorrent is fast, stable and provides unicode
|
||||
support as well as many features.
|
||||
|
||||
This product includes GeoLite data created by MaxMind, available from
|
||||
http://maxmind.com/
|
||||
https://www.maxmind.com/
|
||||
|
||||
### Installation:
|
||||
For installation, follow the instructions from INSTALL file, but simple:
|
||||
@@ -26,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.<br />
|
||||
The key currently used is 4096R/[5B7CC9A2](https://pgp.mit.edu/pks/lookup?op=get&search=0x6E4A2D025B7CC9A2) with fingerprint `D8F3DA77AAC6741053599C136E4A2D025B7CC9A2`.<br />
|
||||
You can also download it from [here](https://github.com/qbittorrent/qBittorrent/raw/master/5B7CC9A2.asc).<br />
|
||||
**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`.<br />
|
||||
|
||||
### Misc:
|
||||
For more information please visit:
|
||||
http://www.qbittorrent.org
|
||||
|
||||
|
||||
52
appveyor.yml
Normal file
52
appveyor.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
version: '{branch}-{build}'
|
||||
|
||||
# Do not build on tags (GitHub only)
|
||||
skip_tags: true
|
||||
|
||||
os: Visual Studio 2015
|
||||
|
||||
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
|
||||
|
||||
# project directory
|
||||
clone_folder: *REPO_DIR
|
||||
|
||||
# cache size should < 100MB (after compressing with fastest option):
|
||||
# see: https://www.appveyor.com/docs/build-cache#save-update-cache-before-build-finishes
|
||||
cache:
|
||||
- *CACHE_DIR
|
||||
|
||||
install:
|
||||
# check if library needs update
|
||||
- appveyor DownloadFile "%QBT_VER_URL%" -FileName "c:\version_new" && SET /P newVersion=<"c:\version_new"
|
||||
- IF EXIST "%CACHE_DIR%\version" (SET /P oldVersion=<"%CACHE_DIR%\version")
|
||||
- IF NOT EXIST "%CACHE_DIR%\version" (SET updateCache=1)
|
||||
- IF NOT "%oldVersion%" == "%newVersion%" (SET updateCache=1)
|
||||
# update library
|
||||
- IF "%updateCache%" == "1" (ECHO "--- Will redownload libraries ---" &&
|
||||
RMDIR /S /Q "%CACHE_DIR%" & MKDIR "%CACHE_DIR%" &&
|
||||
appveyor DownloadFile "%QBT_LIB_URL%" -FileName "c:\qbt_lib.7z" && 7z x "c:\qbt_lib.7z" -o"%CACHE_DIR%" > nul &&
|
||||
COPY "c:\version_new" "%CACHE_DIR%\version")
|
||||
# Qt stay compressed in cache
|
||||
- 7z x "%CACHE_DIR%\qt5_32.7z" -o"c:\qbt" > nul
|
||||
|
||||
before_build:
|
||||
# setup env
|
||||
- CALL "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"
|
||||
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
|
||||
# setup project
|
||||
- COPY /Y "%CACHE_DIR%\winconf.pri" "%REPO_DIR%"
|
||||
- COPY /Y "%CACHE_DIR%\winconf-msvc.pri" "%REPO_DIR%"
|
||||
# workarounds
|
||||
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
||||
|
||||
build_script:
|
||||
- cd "%REPO_DIR%"
|
||||
- qmake qbittorrent.pro && cd src && qmake src.pro
|
||||
- jom -j2 -f Makefile.Release
|
||||
|
||||
test: off
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
aclocal -I m4
|
||||
autoconf
|
||||
|
||||
106
cmake/Modules/FindLibtorrentRasterbar.cmake
Normal file
106
cmake/Modules/FindLibtorrentRasterbar.cmake
Normal file
@@ -0,0 +1,106 @@
|
||||
# - Try to find libtorrent-rasterbar
|
||||
#
|
||||
# If not using pkg-config, you can pre-set LibtorrentRasterbar_CUSTOM_DEFINITIONS
|
||||
# for definitions unrelated to Boost's separate compilation (which are already
|
||||
# decided by the LibtorrentRasterbar_USE_STATIC_LIBS variable).
|
||||
#
|
||||
# Once done this will define
|
||||
# LibtorrentRasterbar_FOUND - System has libtorrent-rasterbar
|
||||
# LibtorrentRasterbar_INCLUDE_DIRS - The libtorrent-rasterbar include directories
|
||||
# LibtorrentRasterbar_LIBRARIES - The libraries needed to use libtorrent-rasterbar
|
||||
# LibtorrentRasterbar_DEFINITIONS - Compiler switches required for using libtorrent-rasterbar
|
||||
# LibtorrentRasterbar_OPENSSL_ENABLED - libtorrent-rasterbar uses and links against OpenSSL
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(PkgConfig QUIET)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_LIBTORRENT_RASTERBAR QUIET libtorrent-rasterbar)
|
||||
endif()
|
||||
|
||||
if(LibtorrentRasterbar_USE_STATIC_LIBS)
|
||||
set(LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
if(PC_LIBTORRENT_RASTERBAR_FOUND)
|
||||
set(LibtorrentRasterbar_DEFINITIONS ${PC_LIBTORRENT_RASTERBAR_CFLAGS})
|
||||
else()
|
||||
if(LibtorrentRasterbar_CUSTOM_DEFINITIONS)
|
||||
set(LibtorrentRasterbar_DEFINITIONS ${LibtorrentRasterbar_CUSTOM_DEFINITIONS})
|
||||
else()
|
||||
# Without pkg-config, we can't possibly figure out the correct build flags.
|
||||
# libtorrent is very picky about those. Let's take a set of defaults and
|
||||
# hope that they apply. If not, you the user are on your own.
|
||||
set(LibtorrentRasterbar_DEFINITIONS
|
||||
-DTORRENT_USE_OPENSSL
|
||||
-DTORRENT_DISABLE_GEO_IP
|
||||
-DBOOST_ASIO_ENABLE_CANCELIO
|
||||
-DUNICODE -D_UNICODE -D_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
if(NOT LibtorrentRasterbar_USE_STATIC_LIBS)
|
||||
list(APPEND LibtorrentRasterbar_DEFINITIONS
|
||||
-DTORRENT_LINKING_SHARED
|
||||
-DBOOST_SYSTEM_DYN_LINK -DBOOST_CHRONO_DYN_LINK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "libtorrent definitions: ${LibtorrentRasterbar_DEFINITIONS}")
|
||||
|
||||
find_path(LibtorrentRasterbar_INCLUDE_DIR libtorrent
|
||||
HINTS ${PC_LIBTORRENT_RASTERBAR_INCLUDEDIR} ${PC_LIBTORRENT_RASTERBAR_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES libtorrent-rasterbar)
|
||||
|
||||
find_library(LibtorrentRasterbar_LIBRARY NAMES torrent-rasterbar libtorrent
|
||||
HINTS ${PC_LIBTORRENT_RASTERBAR_LIBDIR} ${PC_LIBTORRENT_RASTERBAR_LIBRARY_DIRS})
|
||||
|
||||
if(LibtorrentRasterbar_USE_STATIC_LIBS)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif()
|
||||
|
||||
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||
set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIR})
|
||||
|
||||
if(NOT Boost_SYSTEM_FOUND OR NOT Boost_CHRONO_FOUND OR NOT Boost_RANDOM_FOUND)
|
||||
find_package(Boost REQUIRED COMPONENTS date_time system chrono random thread)
|
||||
set(LibtorrentRasterbar_LIBRARIES
|
||||
${LibtorrentRasterbar_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
set(LibtorrentRasterbar_INCLUDE_DIRS
|
||||
${LibtorrentRasterbar_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRasterbar_ENCRYPTION_INDEX)
|
||||
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} ${OPENSSL_LIBRARIES})
|
||||
set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS})
|
||||
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
# handle the QUIETLY and REQUIRED arguments and set LibtorrentRasterbar_FOUND to TRUE
|
||||
# if all listed variables are TRUE
|
||||
find_package_handle_standard_args(LibtorrentRasterbar DEFAULT_MSG
|
||||
LibtorrentRasterbar_LIBRARY
|
||||
LibtorrentRasterbar_INCLUDE_DIR
|
||||
Boost_SYSTEM_FOUND
|
||||
Boost_CHRONO_FOUND
|
||||
Boost_RANDOM_FOUND)
|
||||
|
||||
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()
|
||||
94
cmake/Modules/FindQtSingleApplication.cmake
Normal file
94
cmake/Modules/FindQtSingleApplication.cmake
Normal file
@@ -0,0 +1,94 @@
|
||||
# - Try to find the QtSingleApplication includes and library
|
||||
# which defines
|
||||
#
|
||||
# QTSINGLEAPPLICATION_FOUND - system has QtSingleApplication
|
||||
# QTSINGLEAPPLICATION_INCLUDE_DIR - where to find header QtSingleApplication
|
||||
# QTSINGLEAPPLICATION_LIBRARIES - the libraries to link against to use QtSingleApplication
|
||||
# QTSINGLEAPPLICATION_LIBRARY - where to find the QtSingleApplication library (not for general use)
|
||||
|
||||
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the FreeBSD license.
|
||||
|
||||
SET(QTSINGLEAPPLICATION_FOUND FALSE)
|
||||
|
||||
IF(QT4_FOUND)
|
||||
message(STATUS "Looking for Qt4 single application library")
|
||||
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication
|
||||
# standard locations
|
||||
/usr/include
|
||||
/usr/include/QtSolutions
|
||||
# qt4 location except mac's frameworks
|
||||
"${QT_INCLUDE_DIR}/QtSolutions"
|
||||
# mac's frameworks
|
||||
${FRAMEWORK_INCLUDE_DIR}/QtSolutions
|
||||
)
|
||||
|
||||
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
|
||||
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
||||
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
|
||||
NAMES ${QTSINGLEAPPLICATION_NAMES}
|
||||
PATHS ${QT_LIBRARY_DIR}
|
||||
)
|
||||
ELSEIF(Qt5Widgets_FOUND)
|
||||
message(STATUS "Looking for Qt5 single application library")
|
||||
FOREACH(TOP_INCLUDE_PATH in ${Qt5Widgets_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
|
||||
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication ${TOP_INCLUDE_PATH}/QtSolutions)
|
||||
|
||||
IF(QTSINGLEAPPLICATION_INCLUDE_DIR)
|
||||
BREAK()
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
|
||||
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
|
||||
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
|
||||
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
||||
GET_TARGET_PROPERTY(QT5_WIDGETSLIBRARY Qt5::Widgets LOCATION)
|
||||
GET_FILENAME_COMPONENT(QT5_WIDGETSLIBRARYPATH ${QT5_WIDGETSLIBRARY} PATH)
|
||||
|
||||
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
|
||||
NAMES ${QTSINGLEAPPLICATION_NAMES}
|
||||
PATHS ${QT5_WIDGETSLIBRARYPATH}
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
IF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
|
||||
|
||||
SET(QTSINGLEAPPLICATION_LIBRARIES ${QTSINGLEAPPLICATION_LIBRARY})
|
||||
SET(QTSINGLEAPPLICATION_FOUND TRUE)
|
||||
|
||||
IF (CYGWIN)
|
||||
IF(BUILD_SHARED_LIBS)
|
||||
# No need to define QTSINGLEAPPLICATION_USE_DLL here, because it's default for Cygwin.
|
||||
ELSE(BUILD_SHARED_LIBS)
|
||||
SET (QTSINGLEAPPLICATION_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
|
||||
ENDIF(BUILD_SHARED_LIBS)
|
||||
ENDIF (CYGWIN)
|
||||
|
||||
ENDIF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
|
||||
|
||||
IF (QTSINGLEAPPLICATION_FOUND)
|
||||
IF (NOT QtSingleApplication_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found QtSingleApplication: ${QTSINGLEAPPLICATION_LIBRARY}")
|
||||
MESSAGE(STATUS " includes: ${QTSINGLEAPPLICATION_INCLUDE_DIR}")
|
||||
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
|
||||
ELSE (QTSINGLEAPPLICATION_FOUND)
|
||||
IF (QtSingleApplication_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
|
||||
ENDIF (QtSingleApplication_FIND_REQUIRED)
|
||||
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)
|
||||
23
cmake/Modules/FindSystemd.cmake
Normal file
23
cmake/Modules/FindSystemd.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
#######
|
||||
# Find systemd service dir
|
||||
# sets variables
|
||||
# SYSTEMD_FOUND
|
||||
# SYSTEMD_SERVICES_INSTALL_DIR
|
||||
if (NOT SYSTEMD_FOUND)
|
||||
pkg_check_modules(SYSTEMD "systemd")
|
||||
endif(NOT SYSTEMD_FOUND)
|
||||
|
||||
if (SYSTEMD_FOUND AND "${SYSTEMD_SERVICES_INSTALL_DIR}" STREQUAL "")
|
||||
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
|
||||
--variable=systemdsystemunitdir systemd
|
||||
OUTPUT_VARIABLE SYSTEMD_SERVICES_INSTALL_DIR)
|
||||
string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SERVICES_INSTALL_DIR
|
||||
"${SYSTEMD_SERVICES_INSTALL_DIR}")
|
||||
elseif (NOT SYSTEMD_FOUND AND SYSTEMD_SERVICES_INSTALL_DIR)
|
||||
message (FATAL_ERROR "Variable SYSTEMD_SERVICES_INSTALL_DIR is\
|
||||
defined, but we can't find systemd using pkg-config")
|
||||
endif()
|
||||
|
||||
if (SYSTEMD_FOUND)
|
||||
message(STATUS "systemd services install dir: ${SYSTEMD_SERVICES_INSTALL_DIR}")
|
||||
endif(SYSTEMD_FOUND)
|
||||
38
cmake/Modules/MacroConfigureMSVCRuntime.cmake
Normal file
38
cmake/Modules/MacroConfigureMSVCRuntime.cmake
Normal file
@@ -0,0 +1,38 @@
|
||||
macro(configure_msvc_runtime)
|
||||
if(MSVC)
|
||||
# Default to statically-linked runtime.
|
||||
if("${MSVC_RUNTIME}" STREQUAL "")
|
||||
set(MSVC_RUNTIME "static")
|
||||
endif()
|
||||
# Set compiler options.
|
||||
set(variables
|
||||
CMAKE_C_FLAGS_DEBUG
|
||||
CMAKE_C_FLAGS_MINSIZEREL
|
||||
CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
)
|
||||
if(${MSVC_RUNTIME} STREQUAL "static")
|
||||
message(STATUS
|
||||
"MSVC -> forcing use of statically-linked runtime."
|
||||
)
|
||||
foreach(variable ${variables})
|
||||
if(${variable} MATCHES "/MD")
|
||||
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS
|
||||
"MSVC -> forcing use of dynamically-linked runtime."
|
||||
)
|
||||
foreach(variable ${variables})
|
||||
if(${variable} MATCHES "/MT")
|
||||
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
16
cmake/Modules/MacroLinkQtComponents.cmake
Normal file
16
cmake/Modules/MacroLinkQtComponents.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
# - macro similar to target_link_libraries, which links Qt components
|
||||
# names of the components are pased in Qt4/Qt5 agnostic way (Core, DBus, Xml...)
|
||||
# and the macro links Qt4 ones if QT4_FOUND is set or Qt5 ones if not
|
||||
|
||||
macro (target_link_qt_components target)
|
||||
if (QT4_FOUND)
|
||||
foreach(_cmp ${ARGN})
|
||||
list(APPEND _QT_CMPNTS "Qt4::Qt${_cmp}")
|
||||
endforeach()
|
||||
else (QT4_FOUND)
|
||||
foreach(_cmp ${ARGN})
|
||||
list(APPEND _QT_CMPNTS "Qt5::${_cmp}")
|
||||
endforeach()
|
||||
endif (QT4_FOUND)
|
||||
target_link_libraries(${target} ${_QT_CMPNTS})
|
||||
endmacro()
|
||||
17
cmake/Modules/QbtTargetSources.cmake
Normal file
17
cmake/Modules/QbtTargetSources.cmake
Normal file
@@ -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)
|
||||
14
cmake/Modules/winconf-mingw.cmake
Normal file
14
cmake/Modules/winconf-mingw.cmake
Normal file
@@ -0,0 +1,14 @@
|
||||
if (STACKTRACE_WIN)
|
||||
if ("${WINXXBITS}" NOT STREQUAL "Win64")
|
||||
add_compile_options(-fno-omit-frame-pointer)
|
||||
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
|
||||
link_libraries(libdbghelp -Wl,--export-all-symbols)
|
||||
endif (STACKTRACE_WIN)
|
||||
|
||||
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
|
||||
link_libraries(-Wl,--dynamicbase)
|
||||
endif()
|
||||
|
||||
# LIBS += libadvapi32 libshell32 libuser32
|
||||
# LIBS += libcrypto.dll libssl.dll libwsock32 libws2_32 libz libiconv.dll
|
||||
# LIBS += libpowrprof
|
||||
21
cmake/Modules/winconf-msvc.cmake
Normal file
21
cmake/Modules/winconf-msvc.cmake
Normal file
@@ -0,0 +1,21 @@
|
||||
if (STACKTRACE_WIN)
|
||||
if ("${WINXXBITS}" STREQUAL "Win64")
|
||||
add_compile_options(-Zi)
|
||||
else ("${WINXXBITS}" STREQUAL "Win64")
|
||||
# i686 arch requires frame pointer preservation
|
||||
add_compile_options(-Oy-)
|
||||
endif ("${WINXXBITS}" STREQUAL "Win64")
|
||||
link_libraries(dbghelp.lib)
|
||||
endif (STACKTRACE_WIN)
|
||||
|
||||
# Enable Wide characters
|
||||
add_definitions(-DTORRENT_USE_WPATH)
|
||||
|
||||
if (NOT QT5)
|
||||
# Qt4 does not detect it itself
|
||||
add_definitions(-DQ_COMPILER_INITIALIZER_LISTS)
|
||||
endif (NOT QT5)
|
||||
|
||||
include(MacroConfigureMSVCRuntime)
|
||||
set(MSVC_RUNTIME "dynamic")
|
||||
configure_msvc_runtime()
|
||||
86
cmake/Modules/winconf.cmake
Normal file
86
cmake/Modules/winconf.cmake
Normal file
@@ -0,0 +1,86 @@
|
||||
# Settings for compiling qBittorrent on Windows
|
||||
|
||||
# We want to link with static version of
|
||||
# libtorrent
|
||||
set(LibtorrentRasterbar_USE_STATIC_LIBS True)
|
||||
set(LibtorrentRasterbar_CUSTOM_DEFINITIONS
|
||||
-DBOOST_ALL_NO_LIB -DBOOST_ASIO_HASH_MAP_BUCKETS=1021
|
||||
-DBOOST_ASIO_SEPARATE_COMPILATION
|
||||
-DBOOST_EXCEPTION_DISABLE
|
||||
-DBOOST_SYSTEM_STATIC_LINK=1
|
||||
-DTORRENT_USE_OPENSSL
|
||||
-DUNICODE
|
||||
-D_UNICODE
|
||||
-DWIN32
|
||||
-D_WIN32
|
||||
-DWIN32_LEAN_AND_MEAN
|
||||
-D_WIN32_WINNT=0x0501
|
||||
-D_WIN32_IE=0x0500
|
||||
-D_CRT_SECURE_NO_DEPRECATE
|
||||
-D_SCL_SECURE_NO_DEPRECATE
|
||||
-D__USE_W32_SOCKETS
|
||||
-D_FILE_OFFSET_BITS=64)
|
||||
|
||||
# and boost
|
||||
set(Boost_USE_STATIC_LIBS True)
|
||||
# set(Boost_USE_STATIC_RUNTIME True)
|
||||
|
||||
# Here we assume that all required libraries are installed into the same prefix
|
||||
# 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")
|
||||
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")
|
||||
|
||||
# it is safe to set Qt dirs even if their files are directly in the prefix
|
||||
# Qt4
|
||||
if(NOT QT5)
|
||||
LIST(APPEND CMAKE_PROGRAM_PATH "${QT4_INSTALL_PREFIX}/bin/")
|
||||
endif(NOT QT5)
|
||||
|
||||
# Qt5
|
||||
set(Qt5_DIR "${QT5_INSTALL_PREFIX}/lib/cmake/Qt5")
|
||||
|
||||
# And now we can set specific values for the Boost and libtorrent libraries.
|
||||
# The following values are generated from the paths listed above just for an example
|
||||
# they have to be set to actual locations
|
||||
|
||||
# Boost
|
||||
# set(BOOST_ROOT "${COMMON_INSTALL_PREFIX}")
|
||||
# set(Boost_version_suffix "1_59")
|
||||
# if a link like boost-version/boost -> boost was created or the boost directory was renamed in the same way,
|
||||
# the following needs adjustment
|
||||
# set(BOOST_INCLUDEDIR "${COMMON_INSTALL_PREFIX}/include/boost-${Boost_version_suffix}")
|
||||
# set(BOOST_LIBRARYDIR "${COMMON_INSTALL_PREFIX}/lib/")
|
||||
|
||||
# libtorrent
|
||||
|
||||
# set(PC_LIBTORRENT_RASTERBAR_INCLUDEDIR "${COMMON_INSTALL_PREFIX}")
|
||||
# set(PC_LIBTORRENT_RASTERBAR_LIBDIR "${COMMON_INSTALL_PREFIX}/lib")
|
||||
|
||||
set(AUTOGEN_TARGETS_FOLDER "generated")
|
||||
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
|
||||
# Test 32/64 bits
|
||||
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message(STATUS "Target is 64 bits")
|
||||
if (WIN32)
|
||||
set(WINXXBITS Win64)
|
||||
endif(WIN32)
|
||||
else("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message(STATUS "Target is 32 bits")
|
||||
if (WIN32)
|
||||
set(WINXXBITS Win32)
|
||||
endif(WIN32)
|
||||
endif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
|
||||
if (MSVC)
|
||||
include(winconf-msvc)
|
||||
else (MSVC)
|
||||
include(winconf-mingw)
|
||||
endif (MSVC)
|
||||
10
conf.pri.in
10
conf.pri.in
@@ -6,8 +6,14 @@ 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
|
||||
INCLUDEPATH += $$EXTERNAL_INCLUDES
|
||||
|
||||
EXTERNAL_LIBS = @LDFLAGS@ @LIBS@
|
||||
EXTERNAL_LIBS -= $$QMAKE_DEFAULT_LIBDIRS
|
||||
LIBS += $$EXTERNAL_LIBS
|
||||
|
||||
CONFIG += @QBT_ADD_CONFIG@
|
||||
CONFIG -= @QBT_REMOVE_CONFIG@
|
||||
|
||||
2
configure
vendored
2
configure
vendored
@@ -8345,7 +8345,7 @@ fi
|
||||
$as_echo "$as_me: Running qmake to generate the makefile..." >&6;}
|
||||
CONFDIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
$QT_QMAKE -r $CONFDIR/qbittorrent.pro
|
||||
$QT_QMAKE -r $CONFDIR/qbittorrent.pro "QMAKE_LRELEASE=$QMAKE_LRELEASE"
|
||||
|
||||
ret="$?"
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ AS_IF([test "x$enable_systemd" = "xyes"],
|
||||
AC_MSG_NOTICE([Running qmake to generate the makefile...])
|
||||
CONFDIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||
|
||||
$QT_QMAKE -r [$CONFDIR]/qbittorrent.pro
|
||||
$QT_QMAKE -r [$CONFDIR]/qbittorrent.pro "QMAKE_LRELEASE=$QMAKE_LRELEASE"
|
||||
|
||||
ret="$?"
|
||||
|
||||
|
||||
10
dist/CMakeLists.txt
vendored
Normal file
10
dist/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
if (APPLE)
|
||||
add_subdirectory(mac)
|
||||
else (APPLE)
|
||||
if (UNIX)
|
||||
add_subdirectory(unix)
|
||||
endif (UNIX)
|
||||
if (WIN32)
|
||||
add_subdirectory(windows)
|
||||
endif (WIN32)
|
||||
endif (APPLE)
|
||||
0
dist/mac/CMakeLists.txt
vendored
Normal file
0
dist/mac/CMakeLists.txt
vendored
Normal file
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -45,7 +45,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.3.2</string>
|
||||
<string>3.3.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>qBit</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
||||
2
dist/mac/bundle.cmake
vendored
Normal file
2
dist/mac/bundle.cmake
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
include(BundleUtilities)
|
||||
fixup_bundle("$ENV{DESTDIR}/${CMAKE_INSTALL_PREFIX}/qbittorrent.app" "" "")
|
||||
37
dist/unix/CMakeLists.txt
vendored
Normal file
37
dist/unix/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
if (SYSTEMD)
|
||||
find_package(Systemd)
|
||||
if (SYSTEMD_FOUND)
|
||||
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
|
||||
configure_file(systemd/qbittorrent-nox.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox.service @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox.service
|
||||
DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR}
|
||||
COMPONENT data)
|
||||
endif(SYSTEMD_FOUND)
|
||||
endif(SYSTEMD)
|
||||
|
||||
|
||||
if (GUI)
|
||||
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
|
||||
else (GUI)
|
||||
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
|
||||
endif (GUI)
|
||||
|
||||
install(FILES ${MAN_FILES}
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
||||
COMPONENT doc)
|
||||
|
||||
install(DIRECTORY menuicons/
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
||||
FILES_MATCHING PATTERN "*.png")
|
||||
|
||||
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps/
|
||||
COMPONENT data)
|
||||
|
||||
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qBittorrent.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
||||
COMPONENT data)
|
||||
|
||||
install(FILES qBittorrent.appdata.xml
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/appdata/
|
||||
COMPONENT data)
|
||||
1
dist/windows/CMakeLists.txt
vendored
Normal file
1
dist/windows/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
install(FILES qt.conf DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -19,7 +19,7 @@ XPStyle on
|
||||
!define CSIDL_APPDATA '0x1A' ;Application Data path
|
||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||
|
||||
!define PROG_VERSION "3.3.2"
|
||||
!define PROG_VERSION "3.3.5"
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)
|
||||
|
||||
3
dist/windows/qt.conf
vendored
3
dist/windows/qt.conf
vendored
@@ -1,2 +1,5 @@
|
||||
[Paths]
|
||||
Translations = translations
|
||||
|
||||
[Platforms]
|
||||
WindowsArguments = dpiawareness=0
|
||||
|
||||
108
src/CMakeLists.txt
Normal file
108
src/CMakeLists.txt
Normal file
@@ -0,0 +1,108 @@
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_STANDARD "11")
|
||||
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
|
||||
include(MacroLinkQtComponents)
|
||||
include(QbtTargetSources)
|
||||
|
||||
find_package(LibtorrentRasterbar REQUIRED)
|
||||
|
||||
# Qt
|
||||
if (QT5)
|
||||
add_definitions(-DQBT_USES_QT5)
|
||||
list(APPEND QBT_QT_COMPONENTS Core Network Xml)
|
||||
if (GUI)
|
||||
list (APPEND QBT_QT_COMPONENTS Concurrent Gui Widgets)
|
||||
endif (GUI)
|
||||
if (DBUS)
|
||||
list (APPEND QBT_QT_COMPONENTS DBus)
|
||||
endif (DBUS)
|
||||
find_package(Qt5 5.2.0 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
|
||||
else (QT5)
|
||||
list(APPEND QBT_QT_COMPONENTS QtCore QtNetwork QtXml)
|
||||
if (GUI)
|
||||
list (APPEND QBT_QT_COMPONENTS QtGui)
|
||||
endif (GUI)
|
||||
if (DBUS)
|
||||
list (APPEND QBT_QT_COMPONENTS QtDBus)
|
||||
endif (DBUS)
|
||||
find_package(Qt4 4.8.0 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
|
||||
include(${QT_USE_FILE})
|
||||
endif (QT5)
|
||||
|
||||
set(CMAKE_AUTOMOC True)
|
||||
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# defines
|
||||
add_definitions(-DQT_NO_CAST_TO_ASCII)
|
||||
# Fast concatenation (Qt >= 4.6)
|
||||
add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS)
|
||||
if (WIN32)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif (WIN32)
|
||||
|
||||
if (NOT GUI)
|
||||
add_definitions(-DDISABLE_GUI -DDISABLE_COUNTRIES_RESOLUTION)
|
||||
endif (NOT GUI)
|
||||
|
||||
if (NOT WEBUI)
|
||||
add_definitions(-DDISABLE_WEBUI)
|
||||
endif (NOT WEBUI)
|
||||
|
||||
if (STACKTRACE_WIN)
|
||||
add_definitions(-DSTACKTRACE_WIN)
|
||||
endif(STACKTRACE_WIN)
|
||||
# nogui {
|
||||
# TARGET = qbittorrent-nox
|
||||
# } else {
|
||||
# CONFIG(static) {
|
||||
# DEFINES += QBT_STATIC_QT
|
||||
# QTPLUGIN += qico
|
||||
# }
|
||||
# TARGET = qbittorrent
|
||||
# }
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
add_compile_options(-Wformat -Wformat-security)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(STATUS "Project is built in DEBUG mode.")
|
||||
else (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(STATUS "Project is built in RELEASE mode.")
|
||||
message(STATUS "Disabling debug output.")
|
||||
add_definitions(-DQT_NO_DEBUG_OUTPUT)
|
||||
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
set(QBT_USE_GUI ${GUI})
|
||||
set(QBT_USE_WEBUI ${WEBUI})
|
||||
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)
|
||||
|
||||
|
||||
if (SYSTEM_QTSINGLEAPPLICATION)
|
||||
find_package(QtSingleApplication REQUIRED)
|
||||
else (SYSTEM_QTSINGLEAPPLICATION)
|
||||
add_subdirectory(app/qtsingleapplication)
|
||||
endif (SYSTEM_QTSINGLEAPPLICATION)
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(base)
|
||||
|
||||
if (GUI)
|
||||
add_subdirectory(gui)
|
||||
endif (GUI)
|
||||
|
||||
if (WEBUI)
|
||||
add_subdirectory(webui)
|
||||
endif (WEBUI)
|
||||
|
||||
179
src/app/CMakeLists.txt
Normal file
179
src/app/CMakeLists.txt
Normal file
@@ -0,0 +1,179 @@
|
||||
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
|
||||
)
|
||||
|
||||
# translations
|
||||
file(GLOB QBT_TS_FILES ../lang/*.ts)
|
||||
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
||||
set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${QBT_QM_FILES_BINARY_DIR}")
|
||||
|
||||
if (QT5)
|
||||
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
|
||||
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
||||
else (QT5)
|
||||
qt4_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
||||
endif (QT5)
|
||||
|
||||
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang.qrc" ABSOLUTE)
|
||||
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang.qrc" ABSOLUTE)
|
||||
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../" ABSOLUTE)
|
||||
|
||||
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
|
||||
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
|
||||
|
||||
set_source_files_properties("${_lang_qrc_dst}" PROPERTIES GENERATED True)
|
||||
foreach(qm_file ${QBT_QM_FILES})
|
||||
set_source_files_properties("${_lang_qrc_dst}" PROPERTIES OBJECT_DEPENDS ${qm_file})
|
||||
endforeach()
|
||||
|
||||
set(QBT_APP_RESOURCES
|
||||
../icons.qrc
|
||||
"${_lang_qrc_dst}"
|
||||
)
|
||||
|
||||
# With AUTORCC rcc is ran by cmake before language files are generated,
|
||||
# and thus we call rcc explicitly
|
||||
if (QT5)
|
||||
qt5_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
|
||||
else (QT5)
|
||||
qt4_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
|
||||
endif (QT5)
|
||||
|
||||
if (WIN32)
|
||||
if (MINGW)
|
||||
list (APPEND QBT_APP_SOURCES ../qbittorrent_mingw.rc)
|
||||
else (MINGW)
|
||||
list (APPEND QBT_APP_SOURCES ../qbittorrent.rc)
|
||||
endif (MINGW)
|
||||
endif (WIN32)
|
||||
|
||||
if (UNIX)
|
||||
list(APPEND QBT_APP_HEADERS stacktrace.h)
|
||||
endif (UNIX)
|
||||
|
||||
if (STACKTRACE_WIN)
|
||||
list(APPEND QBT_APP_HEADERS stacktrace_win.h)
|
||||
if (GUI)
|
||||
list(APPEND QBT_APP_HEADERS stacktrace_win_dlg.h)
|
||||
endif (GUI)
|
||||
endif (STACKTRACE_WIN)
|
||||
|
||||
# usesystemqtsingleapplication {
|
||||
# nogui {
|
||||
# CONFIG += qtsinglecoreapplication
|
||||
# } else {
|
||||
# CONFIG += qtsingleapplication
|
||||
# }
|
||||
# } else {
|
||||
# nogui {
|
||||
# include(qtsingleapplication/qtsinglecoreapplication.pri)
|
||||
# } else {
|
||||
# include(qtsingleapplication/qtsingleapplication.pri)
|
||||
# }
|
||||
# }
|
||||
|
||||
# upgrade code
|
||||
list(APPEND QBT_APP_HEADERS upgrade.h)
|
||||
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
|
||||
|
||||
if (GUI)
|
||||
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
|
||||
include_directories(../gui
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../gui
|
||||
)
|
||||
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
|
||||
AUTORCC True
|
||||
MACOSX_BUNDLE True
|
||||
)
|
||||
|
||||
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} QtSingleApplication::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}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
BUNDLE DESTINATION .
|
||||
COMPONENT runtime)
|
||||
|
||||
if (APPLE)
|
||||
install(SCRIPT ${OSX_RES_SRC_DIR}/bundle.cmake)
|
||||
endif (APPLE)
|
||||
@@ -14,8 +14,14 @@ usesystemqtsingleapplication {
|
||||
}
|
||||
}
|
||||
|
||||
HEADERS += $$PWD/application.h
|
||||
SOURCES += $$PWD/application.cpp
|
||||
HEADERS += \
|
||||
$$PWD/application.h \
|
||||
$$PWD/filelogger.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/application.cpp \
|
||||
$$PWD/filelogger.cpp \
|
||||
$$PWD/main.cpp
|
||||
|
||||
unix: HEADERS += $$PWD/stacktrace.h
|
||||
strace_win {
|
||||
@@ -26,7 +32,5 @@ strace_win {
|
||||
}
|
||||
}
|
||||
|
||||
SOURCES += $$PWD/main.cpp
|
||||
|
||||
# upgrade code
|
||||
HEADERS += $$PWD/upgrade.h
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <QLibraryInfo>
|
||||
#include <QSysInfo>
|
||||
#include <QProcess>
|
||||
#include <QAtomicInt>
|
||||
|
||||
#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 <iostream>
|
||||
#endif // DISABLE_GUI
|
||||
@@ -58,8 +59,10 @@
|
||||
#endif
|
||||
|
||||
#include "application.h"
|
||||
#include "filelogger.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/settingsstorage.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "base/iconprovider.h"
|
||||
@@ -70,16 +73,34 @@
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/torrenthandle.h"
|
||||
|
||||
static const char PARAMS_SEPARATOR[] = "|";
|
||||
namespace
|
||||
{
|
||||
#define SETTINGS_KEY(name) "Application/" name
|
||||
|
||||
// FileLogger properties keys
|
||||
#define FILELOGGER_SETTINGS_KEY(name) SETTINGS_KEY("FileLogger/") name
|
||||
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
|
||||
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
|
||||
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
|
||||
const QString KEY_FILELOGGER_DELETEOLD = FILELOGGER_SETTINGS_KEY("DeleteOld");
|
||||
const QString KEY_FILELOGGER_MAXSIZE = FILELOGGER_SETTINGS_KEY("MaxSize");
|
||||
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
|
||||
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
|
||||
|
||||
//just a shortcut
|
||||
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
|
||||
|
||||
const QString LOG_FOLDER("logs");
|
||||
const char PARAMS_SEPARATOR[] = "|";
|
||||
}
|
||||
|
||||
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();
|
||||
Preferences::initInstance();
|
||||
|
||||
#if defined(Q_OS_MACX) && !defined(DISABLE_GUI)
|
||||
@@ -92,7 +113,9 @@ Application::Application(const QString &id, int &argc, char **argv)
|
||||
setApplicationName("qBittorrent");
|
||||
initializeTranslation();
|
||||
#ifndef DISABLE_GUI
|
||||
setStyleSheet("QStatusBar::item { border-width: 0; }");
|
||||
#ifdef QBT_USES_QT5
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
|
||||
#endif // QBT_USES_QT5
|
||||
setQuitOnLastWindowClosed(false);
|
||||
#ifdef Q_OS_WIN
|
||||
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
|
||||
@@ -102,9 +125,112 @@ Application::Application(const QString &id, int &argc, char **argv)
|
||||
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
||||
|
||||
if (isFileLoggerEnabled())
|
||||
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
||||
|
||||
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
QPointer<MainWindow> Application::mainWindow()
|
||||
{
|
||||
return m_window;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Application::isFileLoggerEnabled() const
|
||||
{
|
||||
return settings()->loadValue(KEY_FILELOGGER_ENABLED, true).toBool();
|
||||
}
|
||||
|
||||
void Application::setFileLoggerEnabled(bool value)
|
||||
{
|
||||
if (value && !m_fileLogger)
|
||||
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
||||
else if (!value)
|
||||
delete m_fileLogger;
|
||||
settings()->storeValue(KEY_FILELOGGER_ENABLED, value);
|
||||
}
|
||||
|
||||
QString Application::fileLoggerPath() const
|
||||
{
|
||||
return settings()->loadValue(KEY_FILELOGGER_PATH, QVariant(Utils::Fs::QDesktopServicesDataLocation() + LOG_FOLDER)).toString();
|
||||
}
|
||||
|
||||
void Application::setFileLoggerPath(const QString &value)
|
||||
{
|
||||
if (m_fileLogger)
|
||||
m_fileLogger->changePath(value);
|
||||
settings()->storeValue(KEY_FILELOGGER_PATH, value);
|
||||
}
|
||||
|
||||
bool Application::isFileLoggerBackup() const
|
||||
{
|
||||
return settings()->loadValue(KEY_FILELOGGER_BACKUP, true).toBool();
|
||||
}
|
||||
|
||||
void Application::setFileLoggerBackup(bool value)
|
||||
{
|
||||
if (m_fileLogger)
|
||||
m_fileLogger->setBackup(value);
|
||||
settings()->storeValue(KEY_FILELOGGER_BACKUP, value);
|
||||
}
|
||||
|
||||
bool Application::isFileLoggerDeleteOld() const
|
||||
{
|
||||
return settings()->loadValue(KEY_FILELOGGER_DELETEOLD, true).toBool();
|
||||
}
|
||||
|
||||
void Application::setFileLoggerDeleteOld(bool value)
|
||||
{
|
||||
if (value && m_fileLogger)
|
||||
m_fileLogger->deleteOld(fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
||||
settings()->storeValue(KEY_FILELOGGER_DELETEOLD, value);
|
||||
}
|
||||
|
||||
int Application::fileLoggerMaxSize() const
|
||||
{
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZE, 10).toInt();
|
||||
if (val < 1)
|
||||
return 1;
|
||||
if (val > 1000)
|
||||
return 1000;
|
||||
return val;
|
||||
}
|
||||
|
||||
void Application::setFileLoggerMaxSize(const int value)
|
||||
{
|
||||
if (m_fileLogger)
|
||||
m_fileLogger->setMaxSize(value);
|
||||
settings()->storeValue(KEY_FILELOGGER_MAXSIZE, std::min(std::max(value, 1), 1000));
|
||||
}
|
||||
|
||||
int Application::fileLoggerAge() const
|
||||
{
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 6).toInt();
|
||||
if (val < 1)
|
||||
return 1;
|
||||
if (val > 365)
|
||||
return 365;
|
||||
return val;
|
||||
}
|
||||
|
||||
void Application::setFileLoggerAge(const int value)
|
||||
{
|
||||
settings()->storeValue(KEY_FILELOGGER_AGE, std::min(std::max(value, 1), 365));
|
||||
}
|
||||
|
||||
int Application::fileLoggerAgeType() const
|
||||
{
|
||||
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
|
||||
return (val < 0 || val > 2) ? 1 : val;
|
||||
}
|
||||
|
||||
void Application::setFileLoggerAgeType(const int value)
|
||||
{
|
||||
settings()->storeValue(KEY_FILELOGGER_AGETYPE, (value < 0 || value > 2) ? 1 : value);
|
||||
}
|
||||
|
||||
void Application::processMessage(const QString &message)
|
||||
{
|
||||
QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts);
|
||||
@@ -116,6 +242,48 @@ 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("cmd.exe /C "));
|
||||
if (program.size() >= MAX_PATH) {
|
||||
logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(MAX_PATH), 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
|
||||
@@ -140,68 +308,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->label());
|
||||
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();
|
||||
|
||||
// Confirm shutdown
|
||||
ShutdownAction action = ShutdownAction::None;
|
||||
if (suspend)
|
||||
action = ShutdownAction::Suspend;
|
||||
else if (hibernate)
|
||||
action = ShutdownAction::Hibernate;
|
||||
else if (shutdown)
|
||||
action = ShutdownAction::Shutdown;
|
||||
ShutdownDialogAction action = ShutdownDialogAction::Exit;
|
||||
if (isSuspend)
|
||||
action = ShutdownDialogAction::Suspend;
|
||||
else if (isHibernate)
|
||||
action = ShutdownDialogAction::Hibernate;
|
||||
else if (isShutdown)
|
||||
action = ShutdownDialogAction::Shutdown;
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
// ask confirm
|
||||
if ((action == ShutdownDialogAction::Exit) && (pref->dontConfirmAutoExit())) {
|
||||
// do nothing & skip confirm
|
||||
}
|
||||
else {
|
||||
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();
|
||||
}
|
||||
#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)
|
||||
@@ -225,7 +383,7 @@ void Application::processParams(const QStringList ¶ms)
|
||||
foreach (QString param, params) {
|
||||
param = param.trimmed();
|
||||
#ifndef DISABLE_GUI
|
||||
if (Preferences::instance()->useAdditionDialog())
|
||||
if (AddNewTorrentDialog::isEnabled())
|
||||
AddNewTorrentDialog::show(param, m_window);
|
||||
else
|
||||
#endif
|
||||
@@ -244,7 +402,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();
|
||||
@@ -421,11 +579,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
|
||||
@@ -464,8 +620,11 @@ void Application::cleanup()
|
||||
#endif
|
||||
Net::DownloadManager::freeInstance();
|
||||
Preferences::freeInstance();
|
||||
SettingsStorage::freeInstance();
|
||||
delete m_fileLogger;
|
||||
Logger::freeInstance();
|
||||
IconProvider::freeInstance();
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
#ifdef Q_OS_WIN
|
||||
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
||||
@@ -475,9 +634,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
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ typedef QtSingleCoreApplication BaseApplication;
|
||||
class WebUI;
|
||||
#endif
|
||||
|
||||
class FileLogger;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentHandle;
|
||||
@@ -74,6 +76,26 @@ public:
|
||||
int exec(const QStringList ¶ms);
|
||||
bool sendParams(const QStringList ¶ms);
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
QPointer<MainWindow> mainWindow();
|
||||
#endif
|
||||
|
||||
// FileLogger properties
|
||||
bool isFileLoggerEnabled() const;
|
||||
void setFileLoggerEnabled(bool value);
|
||||
QString fileLoggerPath() const;
|
||||
void setFileLoggerPath(const QString &path);
|
||||
bool isFileLoggerBackup() const;
|
||||
void setFileLoggerBackup(bool value);
|
||||
bool isFileLoggerDeleteOld() const;
|
||||
void setFileLoggerDeleteOld(bool value);
|
||||
int fileLoggerMaxSize() const;
|
||||
void setFileLoggerMaxSize(const int value);
|
||||
int fileLoggerAge() const;
|
||||
void setFileLoggerAge(const int value);
|
||||
int fileLoggerAgeType() const;
|
||||
void setFileLoggerAgeType(const int value);
|
||||
|
||||
protected:
|
||||
#ifndef DISABLE_GUI
|
||||
#ifdef Q_OS_MAC
|
||||
@@ -93,22 +115,26 @@ private slots:
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
ShutdownDialogAction m_shutdownAct;
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
QPointer<MainWindow> m_window;
|
||||
ShutdownAction m_shutdownAct;
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_WEBUI
|
||||
QPointer<WebUI> m_webui;
|
||||
#endif
|
||||
|
||||
// FileLog
|
||||
QPointer<FileLogger> m_fileLogger;
|
||||
|
||||
QTranslator m_qtTranslator;
|
||||
QTranslator m_translator;
|
||||
QStringList m_paramsQueue;
|
||||
|
||||
void initializeTranslation();
|
||||
void processParams(const QStringList ¶ms);
|
||||
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
|
||||
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
|
||||
};
|
||||
|
||||
|
||||
176
src/app/filelogger.cpp
Normal file
176
src/app/filelogger.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 sledgehammer999 <hammered999@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include "filelogger.h"
|
||||
#include "base/logger.h"
|
||||
#include "base/utils/fs.h"
|
||||
|
||||
FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize, const bool deleteOld, const int age, const FileLogAgeType ageType)
|
||||
: m_backup(backup)
|
||||
, m_maxSize(maxSize)
|
||||
, m_logFile(nullptr)
|
||||
{
|
||||
m_flusher.setInterval(0);
|
||||
m_flusher.setSingleShot(true);
|
||||
connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog()));
|
||||
|
||||
changePath(path);
|
||||
if (deleteOld)
|
||||
this->deleteOld(age, ageType);
|
||||
|
||||
const Logger* const logger = Logger::instance();
|
||||
foreach (const Log::Msg& msg, logger->getMessages())
|
||||
addLogMessage(msg);
|
||||
|
||||
connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &)));
|
||||
}
|
||||
|
||||
FileLogger::~FileLogger()
|
||||
{
|
||||
if (!m_logFile) return;
|
||||
closeLogFile();
|
||||
delete m_logFile;
|
||||
}
|
||||
|
||||
void FileLogger::changePath(const QString& newPath)
|
||||
{
|
||||
QString tmpPath = Utils::Fs::fromNativePath(newPath);
|
||||
QDir dir(tmpPath);
|
||||
dir.mkpath(tmpPath);
|
||||
tmpPath = dir.absoluteFilePath("qbittorrent.log");
|
||||
|
||||
if (tmpPath != m_path) {
|
||||
m_path = tmpPath;
|
||||
|
||||
if (m_logFile) {
|
||||
closeLogFile();
|
||||
delete m_logFile;
|
||||
}
|
||||
m_logFile = new QFile(m_path);
|
||||
openLogFile();
|
||||
}
|
||||
}
|
||||
|
||||
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
||||
{
|
||||
QDateTime date = QDateTime::currentDateTime();
|
||||
QDir dir(m_path);
|
||||
|
||||
switch (ageType) {
|
||||
case DAYS:
|
||||
date = date.addDays(age);
|
||||
break;
|
||||
case MONTHS:
|
||||
date = date.addMonths(age);
|
||||
break;
|
||||
default:
|
||||
date = date.addYears(age);
|
||||
}
|
||||
|
||||
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
|
||||
if (file.lastModified() < date)
|
||||
break;
|
||||
Utils::Fs::forceRemove(file.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
void FileLogger::setBackup(bool value)
|
||||
{
|
||||
m_backup = value;
|
||||
}
|
||||
|
||||
void FileLogger::setMaxSize(int value)
|
||||
{
|
||||
m_maxSize = value;
|
||||
}
|
||||
|
||||
void FileLogger::addLogMessage(const Log::Msg &msg)
|
||||
{
|
||||
if (!m_logFile) return;
|
||||
|
||||
QTextStream str(m_logFile);
|
||||
|
||||
switch (msg.type) {
|
||||
case Log::INFO:
|
||||
str << "(I) ";
|
||||
break;
|
||||
case Log::WARNING:
|
||||
str << "(W) ";
|
||||
break;
|
||||
case Log::CRITICAL:
|
||||
str << "(C) ";
|
||||
break;
|
||||
default:
|
||||
str << "(N) ";
|
||||
}
|
||||
|
||||
str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl;
|
||||
|
||||
if (m_backup && (m_logFile->size() >= (m_maxSize * 1024 * 1024))) {
|
||||
closeLogFile();
|
||||
int counter = 0;
|
||||
QString backupLogFilename = m_path + ".bak";
|
||||
|
||||
while (QFile::exists(backupLogFilename)) {
|
||||
++counter;
|
||||
backupLogFilename = m_path + ".bak" + QString::number(counter);
|
||||
}
|
||||
|
||||
QFile::rename(m_path, backupLogFilename);
|
||||
openLogFile();
|
||||
}
|
||||
else {
|
||||
m_flusher.start();
|
||||
}
|
||||
}
|
||||
|
||||
void FileLogger::flushLog()
|
||||
{
|
||||
if (m_logFile)
|
||||
m_logFile->flush();
|
||||
}
|
||||
|
||||
void FileLogger::openLogFile()
|
||||
{
|
||||
if (!m_logFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)
|
||||
|| !m_logFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner)) {
|
||||
delete m_logFile;
|
||||
m_logFile = nullptr;
|
||||
Logger::instance()->addMessage(tr("An error occured while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
void FileLogger::closeLogFile()
|
||||
{
|
||||
m_flusher.stop();
|
||||
m_logFile->close();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 sledgehammer999 <hammered999@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -24,63 +24,56 @@
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef TORRENTIMPORTDLG_H
|
||||
#define TORRENTIMPORTDLG_H
|
||||
#ifndef FILELOGGER_H
|
||||
#define FILELOGGER_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QStringList>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "base/bittorrent/torrentinfo.h"
|
||||
class QFile;
|
||||
|
||||
namespace Ui
|
||||
namespace Log
|
||||
{
|
||||
class TorrentImportDlg;
|
||||
struct Msg;
|
||||
}
|
||||
|
||||
class TorrentImportDlg: public QDialog
|
||||
class FileLogger : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(FileLogger)
|
||||
|
||||
public:
|
||||
explicit TorrentImportDlg(QWidget *parent = 0);
|
||||
~TorrentImportDlg();
|
||||
enum FileLogAgeType
|
||||
{
|
||||
DAYS,
|
||||
MONTHS,
|
||||
YEARS
|
||||
};
|
||||
|
||||
static void importTorrent();
|
||||
FileLogger(const QString &path, const bool backup, const int maxSize, const bool deleteOld, const int age, const FileLogAgeType ageType);
|
||||
~FileLogger();
|
||||
|
||||
QString getTorrentPath() const;
|
||||
QString getContentPath() const;
|
||||
bool fileRenamed() const;
|
||||
BitTorrent::TorrentInfo torrent() const;
|
||||
bool skipFileChecking() const;
|
||||
|
||||
protected slots:
|
||||
void loadTorrent(const QString &torrentPath);
|
||||
void initializeFilesPath();
|
||||
void changePath(const QString &newPath);
|
||||
void deleteOld(const int age, const FileLogAgeType ageType);
|
||||
void setBackup(bool value);
|
||||
void setMaxSize(int value);
|
||||
|
||||
private slots:
|
||||
void on_browseTorrentBtn_clicked();
|
||||
void on_browseContentBtn_clicked();
|
||||
void on_importBtn_clicked();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void addLogMessage(const Log::Msg &msg);
|
||||
void flushLog();
|
||||
|
||||
private:
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void openLogFile();
|
||||
void closeLogFile();
|
||||
|
||||
private:
|
||||
Ui::TorrentImportDlg *ui;
|
||||
BitTorrent::TorrentInfo m_torrentInfo;
|
||||
// NOTE: Where do we use it?
|
||||
QStringList m_filesPath;
|
||||
QString m_contentPath;
|
||||
QString m_torrentPath;
|
||||
bool m_fileRenamed;
|
||||
QString m_path;
|
||||
bool m_backup;
|
||||
int m_maxSize;
|
||||
QFile *m_logFile;
|
||||
QTimer m_flusher;
|
||||
};
|
||||
|
||||
#endif // TORRENTIMPORTDLG_H
|
||||
#endif // FILELOGGER_H
|
||||
|
||||
115
src/app/main.cpp
115
src/app/main.cpp
@@ -79,10 +79,15 @@ Q_IMPORT_PLUGIN(qico)
|
||||
|
||||
// Signal handlers
|
||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
||||
void sigintHandler(int);
|
||||
void sigtermHandler(int);
|
||||
void sigsegvHandler(int);
|
||||
void sigabrtHandler(int);
|
||||
void sigNormalHandler(int signum);
|
||||
void sigAbnormalHandler(int signum);
|
||||
// sys_signame[] is only defined in BSD
|
||||
const char *sysSigName[] = {
|
||||
"", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL",
|
||||
"SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP",
|
||||
"SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
|
||||
"SIGPWR", "SIGUNUSED"
|
||||
};
|
||||
#endif
|
||||
|
||||
struct QBtCommandLineParameters
|
||||
@@ -210,6 +215,21 @@ int main(int argc, char *argv[])
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(QBT_USES_QT5)
|
||||
// This affects only Windows apparently and Qt5.
|
||||
// When QNetworkAccessManager is instantiated it regularly starts polling
|
||||
// the network interfaces to see what's available and their status.
|
||||
// This polling creates jitter and high ping with wifi interfaces.
|
||||
// So here we disable it for lack of better measure.
|
||||
// It will also spew this message in the console: QObject::startTimer: Timers cannot have negative intervals
|
||||
// For more info see:
|
||||
// 1. https://github.com/qbittorrent/qBittorrent/issues/4209
|
||||
// 2. https://bugreports.qt.io/browse/QTBUG-40332
|
||||
// 3. https://bugreports.qt.io/browse/QTBUG-46015
|
||||
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
if (!upgrade()) return EXIT_FAILURE;
|
||||
#else
|
||||
@@ -240,10 +260,10 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
||||
signal(SIGABRT, sigabrtHandler);
|
||||
signal(SIGTERM, sigtermHandler);
|
||||
signal(SIGINT, sigintHandler);
|
||||
signal(SIGSEGV, sigsegvHandler);
|
||||
signal(SIGINT, sigNormalHandler);
|
||||
signal(SIGTERM, sigNormalHandler);
|
||||
signal(SIGABRT, sigAbnormalHandler);
|
||||
signal(SIGSEGV, sigAbnormalHandler);
|
||||
#endif
|
||||
|
||||
return app->exec(params.torrents);
|
||||
@@ -303,58 +323,41 @@ QBtCommandLineParameters parseCommandLine()
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
||||
void sigintHandler(int)
|
||||
void sigNormalHandler(int signum)
|
||||
{
|
||||
signal(SIGINT, 0);
|
||||
qDebug("Catching SIGINT, exiting cleanly");
|
||||
qApp->exit();
|
||||
}
|
||||
|
||||
void sigtermHandler(int)
|
||||
{
|
||||
signal(SIGTERM, 0);
|
||||
qDebug("Catching SIGTERM, exiting cleanly");
|
||||
qApp->exit();
|
||||
}
|
||||
|
||||
void sigsegvHandler(int)
|
||||
{
|
||||
signal(SIGABRT, 0);
|
||||
signal(SIGSEGV, 0);
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
std::cerr << "\n\n*************************************************************\n";
|
||||
std::cerr << "Catching SIGSEGV, please report a bug at http://bug.qbittorrent.org\nand provide the following backtrace:\n";
|
||||
std::cerr << "qBittorrent version: " << VERSION << std::endl;
|
||||
print_stacktrace();
|
||||
#else
|
||||
const char str1[] = "Catching signal: ";
|
||||
const char *sigName = sysSigName[signum];
|
||||
const char str2[] = "\nExiting cleanly\n";
|
||||
write(STDERR_FILENO, str1, strlen(str1));
|
||||
write(STDERR_FILENO, sigName, strlen(sigName));
|
||||
write(STDERR_FILENO, str2, strlen(str2));
|
||||
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
signal(signum, SIG_DFL);
|
||||
qApp->exit(); // unsafe, but exit anyway
|
||||
}
|
||||
|
||||
void sigAbnormalHandler(int signum)
|
||||
{
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
const char str1[] = "\n\n*************************************************************\nCatching signal: ";
|
||||
const char *sigName = sysSigName[signum];
|
||||
const char str2[] = "\nPlease file a bug report at http://bug.qbittorrent.org and provide the following information:\n\n"
|
||||
"qBittorrent version: " VERSION "\n";
|
||||
write(STDERR_FILENO, str1, strlen(str1));
|
||||
write(STDERR_FILENO, sigName, strlen(sigName));
|
||||
write(STDERR_FILENO, str2, strlen(str2));
|
||||
print_stacktrace(); // unsafe
|
||||
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
#ifdef STACKTRACE_WIN
|
||||
StraceDlg dlg;
|
||||
StraceDlg dlg; // unsafe
|
||||
dlg.setStacktraceString(straceWin::getBacktrace());
|
||||
dlg.exec();
|
||||
#endif
|
||||
#endif
|
||||
raise(SIGSEGV);
|
||||
#endif // STACKTRACE_WIN
|
||||
signal(signum, SIG_DFL);
|
||||
raise(signum);
|
||||
}
|
||||
|
||||
void sigabrtHandler(int)
|
||||
{
|
||||
signal(SIGABRT, 0);
|
||||
signal(SIGSEGV, 0);
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
std::cerr << "\n\n*************************************************************\n";
|
||||
std::cerr << "Catching SIGABRT, please report a bug at http://bug.qbittorrent.org\nand provide the following backtrace:\n";
|
||||
std::cerr << "qBittorrent version: " << VERSION << std::endl;
|
||||
print_stacktrace();
|
||||
#else
|
||||
#ifdef STACKTRACE_WIN
|
||||
StraceDlg dlg;
|
||||
dlg.setStacktraceString(straceWin::getBacktrace());
|
||||
dlg.exec();
|
||||
#endif
|
||||
#endif
|
||||
raise(SIGABRT);
|
||||
}
|
||||
#endif
|
||||
#endif // defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
void showSplashScreen()
|
||||
@@ -365,9 +368,9 @@ void showSplashScreen()
|
||||
painter.setPen(QPen(Qt::white));
|
||||
painter.setFont(QFont("Arial", 22, QFont::Black));
|
||||
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
|
||||
QSplashScreen *splash = new QSplashScreen(splash_img, Qt::WindowStaysOnTopHint);
|
||||
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
|
||||
QSplashScreen *splash = new QSplashScreen(splash_img);
|
||||
splash->show();
|
||||
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
|
||||
qApp->processEvents();
|
||||
}
|
||||
#endif
|
||||
|
||||
36
src/app/qtsingleapplication/CMakeLists.txt
Normal file
36
src/app/qtsingleapplication/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
project(qtsingleapplication)
|
||||
|
||||
set(QBT_QTSINGLEAPPLICATION_HEADERS
|
||||
qtlocalpeer.h
|
||||
)
|
||||
|
||||
set(QBT_QTSINGLEAPPLICATION_SOURCES
|
||||
qtlocalpeer.cpp
|
||||
)
|
||||
|
||||
if (GUI)
|
||||
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
|
||||
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
|
||||
else (GUI)
|
||||
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
|
||||
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
|
||||
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)
|
||||
else (QT4_FOUND)
|
||||
target_link_libraries(qtsingleapplication Qt5::Network)
|
||||
endif (QT4_FOUND)
|
||||
|
||||
if (GUI)
|
||||
if (QT4_FOUND)
|
||||
target_link_libraries(qtsingleapplication Qt4::QtGui)
|
||||
else (QT4_FOUND)
|
||||
target_link_libraries(qtsingleapplication Qt5::Widgets)
|
||||
endif(QT4_FOUND)
|
||||
endif (GUI)
|
||||
|
||||
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)
|
||||
@@ -18,10 +18,14 @@
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef STACKTRACE_WIN_H
|
||||
#define STACKTRACE_WIN_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QTextStream>
|
||||
#ifdef __MINGW32__
|
||||
#include <cxxabi.h>
|
||||
@@ -38,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__
|
||||
@@ -105,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
|
||||
@@ -218,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'));
|
||||
@@ -245,6 +316,9 @@ const QString straceWin::getBacktrace()
|
||||
.arg(funcName)
|
||||
#ifndef __MINGW32__
|
||||
.arg(params.join(", "));
|
||||
|
||||
if (!sourceFile.isEmpty())
|
||||
debugLine += QString("[ %1 ]").arg(sourceFile);
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
@@ -256,9 +330,11 @@ const QString straceWin::getBacktrace()
|
||||
}
|
||||
}
|
||||
|
||||
logStream << "\n\nList of linked Modules:\n";
|
||||
EnumModulesContext modulesContext(hProcess, logStream);
|
||||
SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
|
||||
//logStream << "\n\nList of linked Modules:\n";
|
||||
//EnumModulesContext modulesContext(hProcess, logStream);
|
||||
//SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
|
||||
SymCleanup(hProcess);
|
||||
|
||||
logStream << "```";
|
||||
return log;
|
||||
}
|
||||
@@ -266,3 +342,5 @@ const QString straceWin::getBacktrace()
|
||||
#pragma warning(pop)
|
||||
#pragma optimize("g", on)
|
||||
#endif
|
||||
|
||||
#endif // STACKTRACE_WIN_H
|
||||
|
||||
@@ -1,51 +1,77 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 The qBittorrent project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STACKTRACE_WIN_DLG_H
|
||||
#define STACKTRACE_WIN_DLG_H
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QClipboard>
|
||||
#include "boost/version.hpp"
|
||||
#include "libtorrent/version.hpp"
|
||||
#include <QString>
|
||||
#include <QDialog>
|
||||
#include "base/utils/misc.h"
|
||||
#include "ui_stacktrace_win_dlg.h"
|
||||
|
||||
class StraceDlg: public QDialog, private Ui::errorDialog
|
||||
class StraceDlg : public QDialog, private Ui::errorDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StraceDlg(QWidget* parent = 0): QDialog(parent)
|
||||
StraceDlg(QWidget* parent = 0)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
}
|
||||
|
||||
~StraceDlg()
|
||||
{
|
||||
}
|
||||
|
||||
void setStacktraceString(const QString& trace)
|
||||
{
|
||||
QString htmlStr;
|
||||
QTextStream outStream(&htmlStr);
|
||||
outStream << "<p align=center><b><font size=7 color=red>" <<
|
||||
"qBittorrent has crashed" <<
|
||||
"</font></b></p>" <<
|
||||
"<font size=4>" <<
|
||||
"<p>" <<
|
||||
"Please report a bug at <a href=\"http://bugs.qbittorrent.org\">" <<
|
||||
"http://bugs.qbittorrent.org</a>" <<
|
||||
" and provide the following backtrace." <<
|
||||
"</p>" <<
|
||||
"</font>" <<
|
||||
"<br/><hr><br/>" <<
|
||||
"<p align=center><font size=4>qBittorrent version: " << VERSION <<
|
||||
"<br/>Libtorrent version: " << LIBTORRENT_VERSION <<
|
||||
"<br/>Qt version: " << QT_VERSION_STR <<
|
||||
"<br/>Boost version: " << QString::number(BOOST_VERSION / 100000) << '.' <<
|
||||
QString::number((BOOST_VERSION / 100) % 1000) << '.' <<
|
||||
QString::number(BOOST_VERSION % 100) << "</font></p><br/>"
|
||||
"<pre><code>" <<
|
||||
trace <<
|
||||
"</code></pre>" <<
|
||||
"<br/><hr><br/><br/>";
|
||||
// try to call Qt function as less as possible
|
||||
QString htmlStr = QString(
|
||||
"<p align=center><b><font size=7 color=red>"
|
||||
"qBittorrent has crashed"
|
||||
"</font></b></p>"
|
||||
"<font size=4><p>"
|
||||
"Please file a bug report at "
|
||||
"<a href=\"http://bugs.qbittorrent.org\">http://bugs.qbittorrent.org</a> "
|
||||
"and provide the following information:"
|
||||
"</p></font>"
|
||||
"<br/><hr><br/>"
|
||||
"<p align=center><font size=4>"
|
||||
"qBittorrent version: " VERSION "<br/>"
|
||||
"Libtorrent version: %1<br/>"
|
||||
"Qt version: " QT_VERSION_STR "<br/>"
|
||||
"Boost version: %2<br/>"
|
||||
"OS version: %3"
|
||||
"</font></p><br/>"
|
||||
"<pre><code>%4</code></pre>"
|
||||
"<br/><hr><br/><br/>")
|
||||
.arg(Utils::Misc::libtorrentVersionString())
|
||||
.arg(Utils::Misc::boostVersionString())
|
||||
.arg(Utils::Misc::osName())
|
||||
.arg(trace);
|
||||
|
||||
errorText->setHtml(htmlStr);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QRegExp>
|
||||
#ifndef DISABLE_GUI
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
@@ -76,7 +77,7 @@ bool userAcceptsUpgrade()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent, int &maxPrio)
|
||||
bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent = QVariantHash())
|
||||
{
|
||||
QFile file1(filepath);
|
||||
if (!file1.open(QIODevice::ReadOnly))
|
||||
@@ -88,24 +89,37 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent,
|
||||
libtorrent::lazy_entry fastOld;
|
||||
libtorrent::error_code ec;
|
||||
libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
|
||||
if ((fastOld.type() != libtorrent::lazy_entry::dict_t) && !ec) return false;
|
||||
if (ec || (fastOld.type() != libtorrent::lazy_entry::dict_t)) return false;
|
||||
|
||||
libtorrent::entry fastNew;
|
||||
fastNew = fastOld;
|
||||
|
||||
int priority = fastOld.dict_find_int_value("qBt-queuePosition");
|
||||
if (priority > maxPrio)
|
||||
maxPrio = priority;
|
||||
bool v3_3 = false;
|
||||
int queuePosition = 0;
|
||||
QString outFilePath = filepath;
|
||||
QRegExp rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$"));
|
||||
if (rx.indexIn(filepath) != -1) {
|
||||
// old v3.3.x format
|
||||
queuePosition = rx.cap(2).toInt();
|
||||
v3_3 = true;
|
||||
outFilePath.replace(QRegExp("\\.\\d+$"), "");
|
||||
}
|
||||
else {
|
||||
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
|
||||
fastNew["qBt-name"] = Utils::String::toStdString(oldTorrent.value("name").toString());
|
||||
fastNew["qBt-tempPathDisabled"] = false;
|
||||
}
|
||||
|
||||
fastNew["qBt-name"] = Utils::String::toStdString(oldTorrent.value("name").toString());
|
||||
fastNew["qBt-tempPathDisabled"] = false;
|
||||
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
|
||||
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
|
||||
|
||||
QFile file2(QString("%1.%2").arg(filepath).arg(priority > 0 ? priority : 0));
|
||||
QFile file2(outFilePath);
|
||||
QVector<char> out;
|
||||
libtorrent::bencode(std::back_inserter(out), fastNew);
|
||||
if (file2.open(QIODevice::WriteOnly)) {
|
||||
if (file2.write(&out[0], out.size()) == out.size()) {
|
||||
Utils::Fs::forceRemove(filepath);
|
||||
if (v3_3)
|
||||
Utils::Fs::forceRemove(filepath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -115,29 +129,38 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent,
|
||||
|
||||
bool upgrade(bool ask = true)
|
||||
{
|
||||
// Move RSS cookies to common storage
|
||||
Preferences::instance()->moveRSSCookies();
|
||||
// Upgrade preferences
|
||||
Preferences::instance()->upgrade();
|
||||
|
||||
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup");
|
||||
QDir backupFolderDir(backupFolderPath);
|
||||
|
||||
// ****************************************************************************************
|
||||
// Silently converts old v3.3.x .fastresume files
|
||||
QStringList backupFiles_3_3 = backupFolderDir.entryList(
|
||||
QStringList(QLatin1String("*.fastresume.*")), QDir::Files, QDir::Unsorted);
|
||||
foreach (const QString &backupFile, backupFiles_3_3)
|
||||
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
|
||||
// ****************************************************************************************
|
||||
|
||||
QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume");
|
||||
QString oldResumeFilename = oldResumeSettings->fileName();
|
||||
QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash();
|
||||
delete oldResumeSettings;
|
||||
bool oldResumeWasEmpty = oldResumeData.isEmpty();
|
||||
if (oldResumeWasEmpty)
|
||||
|
||||
if (oldResumeData.isEmpty()) {
|
||||
Utils::Fs::forceRemove(oldResumeFilename);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup");
|
||||
QDir backupFolderDir(backupFolderPath);
|
||||
QStringList backupFiles = backupFolderDir.entryList(QStringList() << QLatin1String("*.fastresume"), QDir::Files, QDir::Unsorted);
|
||||
if (backupFiles.isEmpty() && oldResumeWasEmpty) return true;
|
||||
if (ask && !userAcceptsUpgrade()) return false;
|
||||
|
||||
int maxPrio = 0;
|
||||
QStringList backupFiles = backupFolderDir.entryList(
|
||||
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
||||
foreach (QString backupFile, backupFiles) {
|
||||
if (rx.indexIn(backupFile) != -1) {
|
||||
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash(), maxPrio))
|
||||
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash()))
|
||||
oldResumeData.remove(rx.cap(1));
|
||||
else
|
||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(rx.cap(1)), Log::WARNING);
|
||||
@@ -162,7 +185,10 @@ bool upgrade(bool ask = true)
|
||||
resumeData["qBt-seedStatus"] = oldTorrent.value("seed").toBool();
|
||||
resumeData["qBt-tempPathDisabled"] = false;
|
||||
|
||||
QString filename = QString("%1.fastresume.%2").arg(hash).arg(++maxPrio);
|
||||
int queuePosition = oldTorrent.value("priority", 0).toInt();
|
||||
resumeData["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
|
||||
|
||||
QString filename = QString("%1.fastresume").arg(hash);
|
||||
QString filepath = backupFolderDir.absoluteFilePath(filename);
|
||||
|
||||
QFile resumeFile(filepath);
|
||||
@@ -173,17 +199,13 @@ bool upgrade(bool ask = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldResumeWasEmpty) {
|
||||
int counter = 0;
|
||||
QString backupResumeFilename = oldResumeFilename + ".bak";
|
||||
|
||||
while (QFile::exists(backupResumeFilename)) {
|
||||
++counter;
|
||||
backupResumeFilename = oldResumeFilename + ".bak" + QString::number(counter);
|
||||
}
|
||||
|
||||
QFile::rename(oldResumeFilename, backupResumeFilename);
|
||||
int counter = 0;
|
||||
QString backupResumeFilename = oldResumeFilename + ".bak";
|
||||
while (QFile::exists(backupResumeFilename)) {
|
||||
++counter;
|
||||
backupResumeFilename = oldResumeFilename + ".bak" + QString::number(counter);
|
||||
}
|
||||
QFile::rename(oldResumeFilename, backupResumeFilename);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
138
src/base/CMakeLists.txt
Normal file
138
src/base/CMakeLists.txt
Normal file
@@ -0,0 +1,138 @@
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
set(QBT_BASE_HEADERS
|
||||
bittorrent/cachestatus.h
|
||||
bittorrent/infohash.h
|
||||
bittorrent/magneturi.h
|
||||
bittorrent/peerinfo.h
|
||||
bittorrent/private/bandwidthscheduler.h
|
||||
bittorrent/private/filterparserthread.h
|
||||
bittorrent/private/resumedatasavingmanager.h
|
||||
bittorrent/private/speedmonitor.h
|
||||
bittorrent/private/statistics.h
|
||||
bittorrent/session.h
|
||||
bittorrent/sessionstatus.h
|
||||
bittorrent/torrentcreatorthread.h
|
||||
bittorrent/torrenthandle.h
|
||||
bittorrent/torrentinfo.h
|
||||
bittorrent/tracker.h
|
||||
bittorrent/trackerentry.h
|
||||
http/connection.h
|
||||
http/irequesthandler.h
|
||||
http/requestparser.h
|
||||
http/responsebuilder.h
|
||||
http/responsegenerator.h
|
||||
http/server.h
|
||||
http/types.h
|
||||
net/dnsupdater.h
|
||||
net/downloadhandler.h
|
||||
net/downloadmanager.h
|
||||
net/geoipmanager.h
|
||||
net/portforwarder.h
|
||||
net/private/geoipdatabase.h
|
||||
net/reverseresolution.h
|
||||
net/smtp.h
|
||||
rss/private/rssparser.h
|
||||
rss/rssarticle.h
|
||||
rss/rssdownloadrule.h
|
||||
rss/rssdownloadrulelist.h
|
||||
rss/rssfeed.h
|
||||
rss/rssfile.h
|
||||
rss/rssfolder.h
|
||||
rss/rssmanager.h
|
||||
utils/fs.h
|
||||
utils/gzip.h
|
||||
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
|
||||
unicodestrings.h
|
||||
)
|
||||
|
||||
set(QBT_BASE_SOURCES
|
||||
bittorrent/cachestatus.cpp
|
||||
bittorrent/infohash.cpp
|
||||
bittorrent/magneturi.cpp
|
||||
bittorrent/peerinfo.cpp
|
||||
bittorrent/private/bandwidthscheduler.cpp
|
||||
bittorrent/private/filterparserthread.cpp
|
||||
bittorrent/private/resumedatasavingmanager.cpp
|
||||
bittorrent/private/speedmonitor.cpp
|
||||
bittorrent/private/statistics.cpp
|
||||
bittorrent/session.cpp
|
||||
bittorrent/sessionstatus.cpp
|
||||
bittorrent/torrentcreatorthread.cpp
|
||||
bittorrent/torrenthandle.cpp
|
||||
bittorrent/torrentinfo.cpp
|
||||
bittorrent/tracker.cpp
|
||||
bittorrent/trackerentry.cpp
|
||||
http/connection.cpp
|
||||
http/requestparser.cpp
|
||||
http/responsebuilder.cpp
|
||||
http/responsegenerator.cpp
|
||||
http/server.cpp
|
||||
net/dnsupdater.cpp
|
||||
net/downloadhandler.cpp
|
||||
net/downloadmanager.cpp
|
||||
net/geoipmanager.cpp
|
||||
net/portforwarder.cpp
|
||||
net/private/geoipdatabase.cpp
|
||||
net/reverseresolution.cpp
|
||||
net/smtp.cpp
|
||||
rss/private/rssparser.cpp
|
||||
rss/rssarticle.cpp
|
||||
rss/rssdownloadrule.cpp
|
||||
rss/rssdownloadrulelist.cpp
|
||||
rss/rssfeed.cpp
|
||||
rss/rssfile.cpp
|
||||
rss/rssfolder.cpp
|
||||
rss/rssmanager.cpp
|
||||
utils/fs.cpp
|
||||
utils/gzip.cpp
|
||||
utils/misc.cpp
|
||||
utils/string.cpp
|
||||
filesystemwatcher.cpp
|
||||
iconprovider.cpp
|
||||
logger.cpp
|
||||
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::ZLIB LibtorrentRasterbar::LibTorrent)
|
||||
target_link_qt_components(qbt_base Core Network Xml)
|
||||
if (QT4_FOUND)
|
||||
if (GUI)
|
||||
target_link_libraries(qbt_base Qt4::QtGui)
|
||||
endif (GUI)
|
||||
else (QT4_FOUND)
|
||||
if (GUI)
|
||||
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)
|
||||
@@ -4,7 +4,9 @@ HEADERS += \
|
||||
$$PWD/filesystemwatcher.h \
|
||||
$$PWD/qinisettings.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/settingsstorage.h \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/indexrange.h \
|
||||
$$PWD/iconprovider.h \
|
||||
$$PWD/http/irequesthandler.h \
|
||||
$$PWD/http/connection.h \
|
||||
@@ -36,6 +38,7 @@ HEADERS += \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.h \
|
||||
$$PWD/bittorrent/private/filterparserthread.h \
|
||||
$$PWD/bittorrent/private/statistics.h \
|
||||
$$PWD/bittorrent/private/resumedatasavingmanager.h \
|
||||
$$PWD/rss/rssmanager.h \
|
||||
$$PWD/rss/rssfeed.h \
|
||||
$$PWD/rss/rssfolder.h \
|
||||
@@ -49,6 +52,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
|
||||
@@ -57,6 +61,7 @@ SOURCES += \
|
||||
$$PWD/tristatebool.cpp \
|
||||
$$PWD/filesystemwatcher.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/settingsstorage.cpp \
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/iconprovider.cpp \
|
||||
$$PWD/http/connection.cpp \
|
||||
@@ -87,6 +92,7 @@ SOURCES += \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/private/filterparserthread.cpp \
|
||||
$$PWD/bittorrent/private/statistics.cpp \
|
||||
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
|
||||
$$PWD/rss/rssmanager.cpp \
|
||||
$$PWD/rss/rssfeed.cpp \
|
||||
$$PWD/rss/rssfolder.cpp \
|
||||
@@ -99,6 +105,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
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
#include "cachestatus.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
@@ -50,7 +51,11 @@ qreal CacheStatus::readRatio() const
|
||||
|
||||
int CacheStatus::jobQueueLength() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
return m_nativeStatus.job_queue_length;
|
||||
#else
|
||||
return m_nativeStatus.queued_jobs;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CacheStatus::averageJobTime() const
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
#ifndef BITTORRENT_CACHESTATUS_H
|
||||
#define BITTORRENT_CACHESTATUS_H
|
||||
|
||||
#include <libtorrent/disk_io_thread.hpp>
|
||||
#include <QtGlobal>
|
||||
#include <libtorrent/disk_io_thread.hpp>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
|
||||
@@ -68,7 +68,6 @@ bool InfoHash::isValid() const
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
|
||||
InfoHash::operator libtorrent::sha1_hash() const
|
||||
{
|
||||
return m_nativeHash;
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/error_code.hpp>
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
@@ -33,17 +37,45 @@
|
||||
#include "base/utils/string.h"
|
||||
#include "magneturi.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
QString bcLinkToMagnet(QString bcLink)
|
||||
{
|
||||
QByteArray rawBc = bcLink.toUtf8();
|
||||
rawBc = rawBc.mid(8); // skip bc://bt/
|
||||
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
|
||||
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
|
||||
QStringList parts = QString(rawBc).split("/");
|
||||
if (parts.size() != 5) return QString();
|
||||
|
||||
QString filename = parts.at(1);
|
||||
QString hash = parts.at(3);
|
||||
QString magnet = "magnet:?xt=urn:btih:" + hash;
|
||||
magnet += "&dn=" + filename;
|
||||
return magnet;
|
||||
}
|
||||
}
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
MagnetUri::MagnetUri(const QString &url)
|
||||
MagnetUri::MagnetUri(const QString &source)
|
||||
: m_valid(false)
|
||||
, m_url(url)
|
||||
, m_url(source)
|
||||
{
|
||||
if (url.isEmpty()) return;
|
||||
if (source.isEmpty()) return;
|
||||
|
||||
if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
||||
qDebug("Creating magnet link from bc link");
|
||||
m_url = bcLinkToMagnet(source);
|
||||
}
|
||||
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|
||||
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
|
||||
m_url = "magnet:?xt=urn:btih:" + source;
|
||||
}
|
||||
|
||||
libt::error_code ec;
|
||||
libt::parse_magnet_uri(url.toUtf8().constData(), m_addTorrentParams, ec);
|
||||
libt::parse_magnet_uri(m_url.toUtf8().constData(), m_addTorrentParams, ec);
|
||||
if (ec) return;
|
||||
|
||||
m_valid = true;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace BitTorrent
|
||||
class MagnetUri
|
||||
{
|
||||
public:
|
||||
explicit MagnetUri(const QString &url = QString());
|
||||
explicit MagnetUri(const QString &source = QString());
|
||||
|
||||
bool isValid() const;
|
||||
InfoHash hash() const;
|
||||
|
||||
@@ -401,7 +401,30 @@ void FilterParserThread::processFilterList(libt::session *s, const QStringList &
|
||||
|
||||
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();
|
||||
|
||||
65
src/base/bittorrent/private/resumedatasavingmanager.cpp
Normal file
65
src/base/bittorrent/private/resumedatasavingmanager.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#ifdef QBT_USES_QT5
|
||||
#include <QSaveFile>
|
||||
#else
|
||||
#include <QFile>
|
||||
#endif
|
||||
|
||||
#include "base/logger.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "resumedatasavingmanager.h"
|
||||
|
||||
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
|
||||
: m_resumeDataDir(resumeFolderPath)
|
||||
{
|
||||
}
|
||||
|
||||
void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data) const
|
||||
{
|
||||
QString filename = QString("%1.fastresume").arg(infoHash);
|
||||
QString filepath = m_resumeDataDir.absoluteFilePath(filename);
|
||||
|
||||
qDebug() << "Saving resume data in" << filepath;
|
||||
#ifdef QBT_USES_QT5
|
||||
QSaveFile resumeFile(filepath);
|
||||
#else
|
||||
QFile resumeFile(filepath);
|
||||
#endif
|
||||
if (resumeFile.open(QIODevice::WriteOnly)) {
|
||||
resumeFile.write(data);
|
||||
#ifdef QBT_USES_QT5
|
||||
if (!resumeFile.commit()) {
|
||||
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
|
||||
.arg(filepath).arg(resumeFile.errorString()), Log::WARNING);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
50
src/base/bittorrent/private/resumedatasavingmanager.h
Normal file
50
src/base/bittorrent/private/resumedatasavingmanager.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef RESUMEDATASAVINGMANAGER_H
|
||||
#define RESUMEDATASAVINGMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
|
||||
class ResumeDataSavingManager: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
|
||||
|
||||
public slots:
|
||||
void saveResumeData(QString infoHash, QByteArray data) const;
|
||||
|
||||
private:
|
||||
QDir m_resumeDataDir;
|
||||
};
|
||||
|
||||
#endif // RESUMEDATASAVINGMANAGER_H
|
||||
@@ -27,17 +27,21 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QList>
|
||||
#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
|
||||
|
||||
@@ -30,7 +30,11 @@
|
||||
#ifndef SPEEDMONITOR_H
|
||||
#define SPEEDMONITOR_H
|
||||
|
||||
template<typename T> class QList;
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#endif
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
template<typename T>
|
||||
struct Sample
|
||||
@@ -71,13 +75,15 @@ typedef Sample<qreal> SpeedSampleAvg;
|
||||
class SpeedMonitor
|
||||
{
|
||||
public:
|
||||
SpeedMonitor();
|
||||
|
||||
void addSample(const SpeedSample &sample);
|
||||
SpeedSampleAvg average() const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
static const int MAX_SAMPLES = 30;
|
||||
QList<SpeedSample> m_speedSamples;
|
||||
boost::circular_buffer<SpeedSample> m_speedSamples;
|
||||
SpeedSample m_sum;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <libtorrent/session.hpp>
|
||||
|
||||
#include "base/qinisettings.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/bittorrent/sessionstatus.h"
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "statistics.h"
|
||||
@@ -76,40 +75,9 @@ void Statistics::save() const
|
||||
|
||||
void Statistics::load()
|
||||
{
|
||||
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
|
||||
// This code reads the data from there, writes it to the new file, and removes the keys
|
||||
// from the old file. This code should be removed after some time has passed.
|
||||
// e.g. When we reach v3.3.0
|
||||
// Don't forget to remove:
|
||||
// 1. Preferences::getStats()
|
||||
// 2. Preferences::removeStats()
|
||||
// 3. #include "base/preferences.h"
|
||||
Preferences* const pref = Preferences::instance();
|
||||
QIniSettings s("qBittorrent", "qBittorrent-data");
|
||||
QVariantHash v = pref->getStats();
|
||||
|
||||
// Let's test if the qbittorrent.ini holds the key
|
||||
if (!v.isEmpty()) {
|
||||
m_dirty = true;
|
||||
|
||||
// If the user has used qbt > 3.1.5 and then reinstalled/used
|
||||
// qbt < 3.1.6, there will be stats in qbittorrent-data.ini too
|
||||
// so we need to merge those 2.
|
||||
if (s.contains("Stats/AllStats")) {
|
||||
QVariantHash tmp = s.value("Stats/AllStats").toHash();
|
||||
v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong();
|
||||
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
|
||||
}
|
||||
}
|
||||
else {
|
||||
v = s.value("Stats/AllStats").toHash();
|
||||
}
|
||||
QVariantHash v = s.value("Stats/AllStats").toHash();
|
||||
|
||||
m_alltimeDL = v["AlltimeDL"].toULongLong();
|
||||
m_alltimeUL = v["AlltimeUL"].toULongLong();
|
||||
|
||||
if (m_dirty) {
|
||||
save();
|
||||
pref->removeStats();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,15 @@
|
||||
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QPointer>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QNetworkConfigurationManager>
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
#include "base/tristatebool.h"
|
||||
#include "base/types.h"
|
||||
#include "torrentinfo.h"
|
||||
@@ -45,13 +48,24 @@
|
||||
namespace libtorrent
|
||||
{
|
||||
class session;
|
||||
struct torrent_handle;
|
||||
class entry;
|
||||
struct add_torrent_params;
|
||||
struct pe_settings;
|
||||
struct proxy_settings;
|
||||
struct session_settings;
|
||||
struct session_status;
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
struct proxy_settings;
|
||||
#else
|
||||
namespace aux
|
||||
{
|
||||
struct proxy_settings;
|
||||
}
|
||||
|
||||
typedef aux::proxy_settings proxy_settings;
|
||||
#endif
|
||||
|
||||
class alert;
|
||||
struct torrent_alert;
|
||||
struct state_update_alert;
|
||||
@@ -86,6 +100,7 @@ namespace libtorrent
|
||||
struct external_ip_alert;
|
||||
}
|
||||
|
||||
class QThread;
|
||||
class QTimer;
|
||||
class QStringList;
|
||||
class QString;
|
||||
@@ -95,8 +110,20 @@ template<typename T> class QList;
|
||||
class FilterParserThread;
|
||||
class BandwidthScheduler;
|
||||
class Statistics;
|
||||
class ResumeDataSavingManager;
|
||||
class SettingsStorage;
|
||||
|
||||
typedef QPair<QString, QString> QStringPair;
|
||||
enum MaxRatioAction
|
||||
{
|
||||
Pause,
|
||||
Remove
|
||||
};
|
||||
|
||||
enum TorrentExportFolder
|
||||
{
|
||||
Regular,
|
||||
Finished
|
||||
};
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
@@ -112,17 +139,15 @@ namespace BitTorrent
|
||||
struct AddTorrentParams
|
||||
{
|
||||
QString name;
|
||||
QString label;
|
||||
QString category;
|
||||
QString savePath;
|
||||
bool disableTempPath; // e.g. for imported torrents
|
||||
bool sequential;
|
||||
bool disableTempPath = false; // e.g. for imported torrents
|
||||
bool sequential = false;
|
||||
TriStateBool addForced;
|
||||
TriStateBool addPaused;
|
||||
QVector<int> filePriorities; // used if TorrentInfo is set
|
||||
bool ignoreShareRatio;
|
||||
bool skipChecking;
|
||||
|
||||
AddTorrentParams();
|
||||
bool ignoreShareRatio = false;
|
||||
bool skipChecking = false;
|
||||
};
|
||||
|
||||
struct TorrentStatusReport
|
||||
@@ -152,11 +177,50 @@ namespace BitTorrent
|
||||
bool isPexEnabled() const;
|
||||
bool isQueueingEnabled() const;
|
||||
qreal globalMaxRatio() const;
|
||||
bool isTempPathEnabled() const;
|
||||
bool isAppendExtensionEnabled() const;
|
||||
bool useAppendLabelToSavePath() 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
|
||||
static QStringList expandCategory(const QString &category);
|
||||
|
||||
QStringList categories() const;
|
||||
QString categorySavePath(const QString &categoryName) const;
|
||||
bool addCategory(const QString &name, const QString &savePath = "");
|
||||
bool editCategory(const QString &name, const QString &savePath);
|
||||
bool removeCategory(const QString &name);
|
||||
bool isSubcategoriesEnabled() const;
|
||||
void setSubcategoriesEnabled(bool value);
|
||||
|
||||
// Torrent Management Mode subsystem (TMM)
|
||||
//
|
||||
// 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 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);
|
||||
|
||||
bool isAddTorrentPaused() const;
|
||||
void setAddTorrentPaused(bool value);
|
||||
|
||||
TorrentHandle *findTorrent(const InfoHash &hash) const;
|
||||
QHash<InfoHash, TorrentHandle *> torrents() const;
|
||||
@@ -171,6 +235,9 @@ namespace BitTorrent
|
||||
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);
|
||||
@@ -183,7 +250,7 @@ namespace BitTorrent
|
||||
bool addTorrent(QString source, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||
bool deleteTorrent(const QString &hash, bool deleteLocalFiles = false);
|
||||
bool loadMetadata(const QString &magnetUri);
|
||||
bool loadMetadata(const MagnetUri &magnetUri);
|
||||
bool cancelLoadMetadata(const InfoHash &hash);
|
||||
|
||||
void recursiveTorrentDownload(const InfoHash &hash);
|
||||
@@ -195,7 +262,8 @@ namespace BitTorrent
|
||||
// TorrentHandle interface
|
||||
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel);
|
||||
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
|
||||
void handleTorrentSavingModeChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentMetadataReceived(TorrentHandle *const torrent);
|
||||
void handleTorrentPaused(TorrentHandle *const torrent);
|
||||
void handleTorrentResumed(TorrentHandle *const torrent);
|
||||
@@ -217,13 +285,15 @@ 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);
|
||||
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentLabelChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel);
|
||||
void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory);
|
||||
void torrentSavingModeChanged(BitTorrent::TorrentHandle *const torrent);
|
||||
void allTorrentsFinished();
|
||||
void metadataLoaded(const BitTorrent::TorrentInfo &info);
|
||||
void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent);
|
||||
@@ -241,6 +311,9 @@ namespace BitTorrent
|
||||
void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless);
|
||||
void downloadFromUrlFailed(const QString &url, const QString &reason);
|
||||
void downloadFromUrlFinished(const QString &url);
|
||||
void categoryAdded(const QString &categoryName);
|
||||
void categoryRemoved(const QString &categoryName);
|
||||
void subcategoriesSupportChanged();
|
||||
|
||||
private slots:
|
||||
void configure();
|
||||
@@ -274,8 +347,6 @@ namespace BitTorrent
|
||||
void adjustLimits(libtorrent::session_settings &sessionSettings);
|
||||
const QStringList getListeningIPs();
|
||||
void setListeningPort();
|
||||
void setDefaultSavePath(const QString &path);
|
||||
void setDefaultTempPath(const QString &path = QString());
|
||||
void preAllocateAllFiles(bool b);
|
||||
void setMaxConnectionsPerTorrent(int max);
|
||||
void setMaxUploadsPerTorrent(int max);
|
||||
@@ -283,13 +354,13 @@ namespace BitTorrent
|
||||
void enableDHT(bool enable);
|
||||
void changeSpeedLimitMode_impl(bool alternative);
|
||||
|
||||
void setAppendLabelToSavePath(bool append);
|
||||
void setAppendExtension(bool append);
|
||||
|
||||
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);
|
||||
@@ -313,13 +384,16 @@ namespace BitTorrent
|
||||
void handleListenFailedAlert(libtorrent::listen_failed_alert *p);
|
||||
void handleExternalIPAlert(libtorrent::external_ip_alert *p);
|
||||
|
||||
void createTorrentHandle(const libtorrent::torrent_handle &nativeHandle);
|
||||
|
||||
void saveResumeData();
|
||||
bool writeResumeDataFile(TorrentHandle *const torrent, const libtorrent::entry &data);
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
|
||||
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
|
||||
#endif
|
||||
void getPendingAlerts(std::vector<libtorrent::alert *> &out, ulong time = 0);
|
||||
|
||||
AddTorrentData addDataFromParams(const AddTorrentParams ¶ms);
|
||||
SettingsStorage *m_settings;
|
||||
|
||||
// BitTorrent
|
||||
libtorrent::session *m_nativeSession;
|
||||
@@ -334,10 +408,9 @@ namespace BitTorrent
|
||||
qreal m_globalMaxRatio;
|
||||
int m_numResumeData;
|
||||
int m_extraLimit;
|
||||
bool m_appendLabelToSavePath;
|
||||
bool m_appendExtension;
|
||||
uint m_refreshInterval;
|
||||
MaxRatioAction m_highRatioAction;
|
||||
MaxRatioAction m_maxRatioAction;
|
||||
QList<BitTorrent::TrackerEntry> m_additionalTrackers;
|
||||
QString m_defaultSavePath;
|
||||
QString m_tempPath;
|
||||
@@ -355,16 +428,22 @@ namespace BitTorrent
|
||||
QPointer<BandwidthScheduler> m_bwScheduler;
|
||||
// Tracker
|
||||
QPointer<Tracker> m_tracker;
|
||||
// fastresume data writing thread
|
||||
QThread *m_ioThread;
|
||||
ResumeDataSavingManager *m_resumeDataSavingManager;
|
||||
|
||||
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
||||
QHash<InfoHash, TorrentHandle *> m_torrents;
|
||||
QHash<InfoHash, AddTorrentData> m_addingTorrents;
|
||||
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||
TorrentStatusReport m_torrentStatusReport;
|
||||
QStringMap m_categories;
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
QMutex m_alertsMutex;
|
||||
QWaitCondition m_alertsWaitCondition;
|
||||
QVector<libtorrent::alert *> m_alerts;
|
||||
std::vector<libtorrent::alert *> m_alerts;
|
||||
#endif
|
||||
|
||||
QNetworkConfigurationManager m_networkManager;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ using namespace BitTorrent;
|
||||
// name starts with a .
|
||||
bool fileFilter(const std::string &f)
|
||||
{
|
||||
return (libt::filename(f)[0] != '.');
|
||||
return !Utils::Fs::fileName(Utils::String::fromStdString(f)).startsWith('.');
|
||||
}
|
||||
|
||||
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
#include <libtorrent/alert_types.hpp>
|
||||
#include <libtorrent/create_torrent.hpp>
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||
#include <libtorrent/time.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -56,15 +60,36 @@
|
||||
#include "trackerentry.h"
|
||||
#include "torrenthandle.h"
|
||||
|
||||
static const char QB_EXT[] = ".!qB";
|
||||
const QString QB_EXT {".!qB"};
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
// TrackerInfo
|
||||
// AddTorrentData
|
||||
|
||||
TrackerInfo::TrackerInfo()
|
||||
: numPeers(0)
|
||||
AddTorrentData::AddTorrentData()
|
||||
: resumed(false)
|
||||
, disableTempPath(false)
|
||||
, sequential(false)
|
||||
, hasSeedStatus(false)
|
||||
, skipChecking(false)
|
||||
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
|
||||
{
|
||||
}
|
||||
|
||||
AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
||||
: resumed(false)
|
||||
, name(params.name)
|
||||
, category(params.category)
|
||||
, savePath(params.savePath)
|
||||
, disableTempPath(params.disableTempPath)
|
||||
, sequential(params.sequential)
|
||||
, hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping
|
||||
, skipChecking(params.skipChecking)
|
||||
, addForced(params.addForced)
|
||||
, addPaused(params.addPaused)
|
||||
, filePriorities(params.filePriorities)
|
||||
, ratioLimit(params.ignoreShareRatio ? TorrentHandle::NO_RATIO_LIMIT : TorrentHandle::USE_GLOBAL_RATIO)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -174,9 +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_label(data.label)
|
||||
, m_category(data.category)
|
||||
, m_hasSeedStatus(data.hasSeedStatus)
|
||||
, m_ratioLimit(data.ratioLimit)
|
||||
, m_tempPathDisabled(data.disableTempPath)
|
||||
@@ -184,15 +210,14 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
||||
, m_pauseAfterRecheck(false)
|
||||
, m_needSaveResumeData(false)
|
||||
{
|
||||
initialize();
|
||||
if (m_useAutoTMM)
|
||||
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
||||
|
||||
if (!data.resumed) {
|
||||
updateStatus();
|
||||
m_hash = InfoHash(m_nativeStatus.info_hash);
|
||||
|
||||
if (!data.resumed)
|
||||
setSequentialDownload(data.sequential);
|
||||
if (hasMetadata()) {
|
||||
if (m_session->isAppendExtensionEnabled())
|
||||
appendExtensionsToIncompleteFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TorrentHandle::~TorrentHandle() {}
|
||||
@@ -301,6 +326,22 @@ QString TorrentHandle::contentPath(bool actual) const
|
||||
return rootPath(actual);
|
||||
}
|
||||
|
||||
bool TorrentHandle::isAutoTMMEnabled() const
|
||||
{
|
||||
return m_useAutoTMM;
|
||||
}
|
||||
|
||||
void TorrentHandle::setAutoTMMEnabled(bool enabled)
|
||||
{
|
||||
if (m_useAutoTMM == enabled) return;
|
||||
|
||||
m_useAutoTMM = enabled;
|
||||
m_session->handleTorrentSavingModeChanged(this);
|
||||
|
||||
if (m_useAutoTMM)
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
}
|
||||
|
||||
QString TorrentHandle::nativeActualSavePath() const
|
||||
{
|
||||
return Utils::String::fromStdString(m_nativeStatus.save_path);
|
||||
@@ -433,7 +474,7 @@ bool TorrentHandle::connectPeer(const PeerAddress &peerAddress)
|
||||
libt::address addr = libt::address::from_string(Utils::String::toStdString(peerAddress.ip.toString()), ec);
|
||||
if (ec) return false;
|
||||
|
||||
libt::asio::ip::tcp::endpoint ep(addr, peerAddress.port);
|
||||
boost::asio::ip::tcp::endpoint ep(addr, peerAddress.port);
|
||||
SAFE_CALL_BOOL(connect_peer, ep);
|
||||
}
|
||||
|
||||
@@ -478,9 +519,22 @@ qreal TorrentHandle::progress() const
|
||||
return progress;
|
||||
}
|
||||
|
||||
QString TorrentHandle::label() const
|
||||
QString TorrentHandle::category() const
|
||||
{
|
||||
return m_label;
|
||||
return m_category;
|
||||
}
|
||||
|
||||
bool TorrentHandle::belongsToCategory(const QString &category) const
|
||||
{
|
||||
if (m_category.isEmpty()) return category.isEmpty();
|
||||
if (!Session::isValidCategoryName(category)) return false;
|
||||
|
||||
if (m_category == category) return true;
|
||||
|
||||
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + "/"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QDateTime TorrentHandle::addedTime() const
|
||||
@@ -543,25 +597,6 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
|
||||
return res;
|
||||
}
|
||||
|
||||
QPair<int, int> 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<int> TorrentHandle::filePriorities() const
|
||||
{
|
||||
std::vector<int> fp;
|
||||
@@ -679,13 +714,13 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
|
||||
std::vector<int> fp;
|
||||
SAFE_GET(fp, file_priorities);
|
||||
|
||||
QPair<int, int> extremities;
|
||||
TorrentInfo::PieceRange extremities;
|
||||
bool found = false;
|
||||
int count = static_cast<int>(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;
|
||||
}
|
||||
@@ -695,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));
|
||||
}
|
||||
@@ -847,7 +882,7 @@ qulonglong TorrentHandle::eta() const
|
||||
|
||||
QVector<qreal> TorrentHandle::filesProgress() const
|
||||
{
|
||||
std::vector<libt::size_type> fp;
|
||||
std::vector<boost::int64_t> fp;
|
||||
QVector<qreal> result;
|
||||
SAFE_CALL(file_progress, fp, libt::torrent_handle::piece_granularity);
|
||||
|
||||
@@ -1022,9 +1057,9 @@ qreal TorrentHandle::maxRatio(bool *usesGlobalRatio) const
|
||||
|
||||
qreal TorrentHandle::realRatio() const
|
||||
{
|
||||
libt::size_type upload = m_nativeStatus.all_time_upload;
|
||||
boost::int64_t upload = m_nativeStatus.all_time_upload;
|
||||
// special case for a seeder who lost its stats, also assume nobody will import a 99% done torrent
|
||||
libt::size_type download = (m_nativeStatus.all_time_download < m_nativeStatus.total_done * 0.01) ? m_nativeStatus.total_done : m_nativeStatus.all_time_download;
|
||||
boost::int64_t download = (m_nativeStatus.all_time_download < m_nativeStatus.total_done * 0.01) ? m_nativeStatus.total_done : m_nativeStatus.all_time_download;
|
||||
|
||||
if (download == 0)
|
||||
return (upload == 0) ? 0.0 : MAX_RATIO;
|
||||
@@ -1066,7 +1101,11 @@ int TorrentHandle::connectionsLimit() const
|
||||
|
||||
qlonglong TorrentHandle::nextAnnounce() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
return m_nativeStatus.next_announce.total_seconds();
|
||||
#else
|
||||
return libt::duration_cast<libt::seconds>(m_nativeStatus.next_announce).count();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TorrentHandle::setName(const QString &name)
|
||||
@@ -1077,17 +1116,47 @@ void TorrentHandle::setName(const QString &name)
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentHandle::setLabel(const QString &label)
|
||||
bool TorrentHandle::setCategory(const QString &category)
|
||||
{
|
||||
if (m_label != label) {
|
||||
QString oldLabel = m_label;
|
||||
m_label = label;
|
||||
if (m_category != category) {
|
||||
if (!category.isEmpty()) {
|
||||
if (!Session::isValidCategoryName(category)) return false;
|
||||
if (!m_session->categories().contains(category))
|
||||
if (!m_session->addCategory(category))
|
||||
return false;
|
||||
}
|
||||
|
||||
QString oldCategory = m_category;
|
||||
m_category = category;
|
||||
m_needSaveResumeData = true;
|
||||
m_session->handleTorrentLabelChanged(this, oldLabel);
|
||||
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
||||
|
||||
if (m_useAutoTMM) {
|
||||
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
else
|
||||
setAutoTMMEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TorrentHandle::move(QString path)
|
||||
{
|
||||
m_useAutoTMM = false;
|
||||
m_session->handleTorrentSavingModeChanged(this);
|
||||
|
||||
path = Utils::Fs::fromNativePath(path.trimmed());
|
||||
if (path.isEmpty())
|
||||
path = m_session->defaultSavePath();
|
||||
if (!path.endsWith('/'))
|
||||
path += '/';
|
||||
|
||||
move_impl(path);
|
||||
}
|
||||
|
||||
void TorrentHandle::move_impl(QString path)
|
||||
{
|
||||
path = Utils::Fs::toNativePath(path);
|
||||
if (path == savePath()) return;
|
||||
@@ -1143,6 +1212,8 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
|
||||
|
||||
std::vector<int> fp;
|
||||
SAFE_GET(fp, file_priorities);
|
||||
std::vector<int> pp;
|
||||
SAFE_GET(pp, piece_priorities);
|
||||
|
||||
// Download first and last pieces first for all media files in the torrent
|
||||
int nbfiles = static_cast<int>(fp.size());
|
||||
@@ -1151,14 +1222,22 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
|
||||
const QString ext = Utils::Fs::fileExtension(path);
|
||||
if (Utils::Misc::isPreviewable(ext) && (fp[index] > 0)) {
|
||||
qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first";
|
||||
|
||||
// Determine the priority to set
|
||||
int prio = b ? 7 : fp[index];
|
||||
|
||||
QPair<int, int> extremities = fileExtremityPieces(index);
|
||||
SAFE_CALL(piece_priority, extremities.first, prio);
|
||||
SAFE_CALL(piece_priority, extremities.second, prio);
|
||||
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.last() - i] = prio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_CALL(prioritize_pieces, pp);
|
||||
}
|
||||
|
||||
void TorrentHandle::toggleFirstLastPiecePriority()
|
||||
@@ -1294,7 +1373,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);
|
||||
}
|
||||
@@ -1371,6 +1450,7 @@ void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert
|
||||
m_hasSeedStatus = true;
|
||||
|
||||
adjustActualSavePath();
|
||||
manageIncompleteFiles();
|
||||
|
||||
if (m_pauseAfterRecheck) {
|
||||
m_pauseAfterRecheck = false;
|
||||
@@ -1392,13 +1472,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)
|
||||
@@ -1426,12 +1512,13 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
|
||||
resumeData["qBt-paused"] = isPaused();
|
||||
resumeData["qBt-forced"] = isForced();
|
||||
}
|
||||
resumeData["qBt-savePath"] = 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-label"] = Utils::String::toStdString(m_label);
|
||||
resumeData["qBt-category"] = Utils::String::toStdString(m_category);
|
||||
resumeData["qBt-name"] = Utils::String::toStdString(m_name);
|
||||
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
|
||||
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
|
||||
resumeData["qBt-queuePosition"] = queuePosition();
|
||||
|
||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||
}
|
||||
@@ -1488,12 +1575,6 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
||||
|
||||
updateStatus();
|
||||
|
||||
if (filesCount() == 1) {
|
||||
// Single-file torrent
|
||||
// Renaming a file corresponds to changing the save path
|
||||
m_session->handleTorrentSavePathChanged(this);
|
||||
}
|
||||
|
||||
--m_renameCount;
|
||||
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
||||
m_moveFinishedTriggers.takeFirst()();
|
||||
@@ -1517,7 +1598,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);
|
||||
}
|
||||
@@ -1538,7 +1619,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()) {
|
||||
@@ -1554,14 +1635,17 @@ void TorrentHandle::handleTempPathChanged()
|
||||
adjustActualSavePath();
|
||||
}
|
||||
|
||||
void TorrentHandle::handleCategorySavePathChanged()
|
||||
{
|
||||
if (m_useAutoTMM)
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
}
|
||||
|
||||
void TorrentHandle::handleAppendExtensionToggled()
|
||||
{
|
||||
if (!hasMetadata()) return;
|
||||
|
||||
if (m_session->isAppendExtensionEnabled())
|
||||
appendExtensionsToIncompleteFiles();
|
||||
else
|
||||
removeExtensionsFromIncompleteFiles();
|
||||
manageIncompleteFiles();
|
||||
}
|
||||
|
||||
void TorrentHandle::handleAlert(libtorrent::alert *a)
|
||||
@@ -1618,30 +1702,26 @@ void TorrentHandle::handleAlert(libtorrent::alert *a)
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentHandle::appendExtensionsToIncompleteFiles()
|
||||
void TorrentHandle::manageIncompleteFiles()
|
||||
{
|
||||
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
||||
QVector<qreal> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1664,8 +1744,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));
|
||||
@@ -1679,15 +1759,11 @@ libtorrent::torrent_handle TorrentHandle::nativeHandle() const
|
||||
void TorrentHandle::updateTorrentInfo()
|
||||
{
|
||||
if (!hasMetadata()) return;
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
m_torrentInfo = TorrentInfo(m_nativeStatus.torrent_file);
|
||||
}
|
||||
|
||||
void TorrentHandle::initialize()
|
||||
{
|
||||
updateStatus();
|
||||
m_hash = InfoHash(m_nativeStatus.info_hash);
|
||||
adjustActualSavePath();
|
||||
#else
|
||||
m_torrentInfo = TorrentInfo(m_nativeStatus.torrent_file.lock());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TorrentHandle::isMoveInProgress() const
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
#include <QHash>
|
||||
|
||||
#include <libtorrent/torrent_handle.hpp>
|
||||
#include <libtorrent/version.hpp>
|
||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||
#include <libtorrent/torrent_status.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "base/tristatebool.h"
|
||||
@@ -49,6 +54,8 @@ class QBitArray;
|
||||
class QStringList;
|
||||
template<typename T, typename U> struct QPair;
|
||||
|
||||
extern const QString QB_EXT;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class alert;
|
||||
@@ -85,7 +92,7 @@ namespace BitTorrent
|
||||
bool resumed;
|
||||
// for both new and resumed torrents
|
||||
QString name;
|
||||
QString label;
|
||||
QString category;
|
||||
QString savePath;
|
||||
bool disableTempPath;
|
||||
bool sequential;
|
||||
@@ -97,14 +104,15 @@ namespace BitTorrent
|
||||
QVector<int> filePriorities;
|
||||
// for resumed torrents
|
||||
qreal ratioLimit;
|
||||
|
||||
AddTorrentData();
|
||||
AddTorrentData(const AddTorrentParams ¶ms);
|
||||
};
|
||||
|
||||
struct TrackerInfo
|
||||
{
|
||||
QString lastMessage;
|
||||
quint32 numPeers;
|
||||
|
||||
TrackerInfo();
|
||||
quint32 numPeers = 0;
|
||||
};
|
||||
|
||||
class TorrentState
|
||||
@@ -221,11 +229,16 @@ namespace BitTorrent
|
||||
QString rootPath(bool actual = false) const;
|
||||
QString contentPath(bool actual = false) const;
|
||||
|
||||
bool isAutoTMMEnabled() const;
|
||||
void setAutoTMMEnabled(bool enabled);
|
||||
QString category() const;
|
||||
bool belongsToCategory(const QString &category) const;
|
||||
bool setCategory(const QString &category);
|
||||
|
||||
int filesCount() const;
|
||||
int piecesCount() const;
|
||||
int piecesHave() const;
|
||||
qreal progress() const;
|
||||
QString label() const;
|
||||
QDateTime addedTime() const;
|
||||
qreal ratioLimit() const;
|
||||
|
||||
@@ -234,7 +247,6 @@ namespace BitTorrent
|
||||
qlonglong fileSize(int index) const;
|
||||
QStringList absoluteFilePaths() const;
|
||||
QStringList absoluteFilePathsUnwanted() const;
|
||||
QPair<int, int> fileExtremityPieces(int index) const;
|
||||
QVector<int> filePriorities() const;
|
||||
|
||||
TorrentInfo info() const;
|
||||
@@ -301,7 +313,6 @@ namespace BitTorrent
|
||||
qlonglong nextAnnounce() const;
|
||||
|
||||
void setName(const QString &name);
|
||||
void setLabel(const QString &label);
|
||||
void setSequentialDownload(bool b);
|
||||
void toggleSequentialDownload();
|
||||
void setFirstLastPiecePriority(bool b);
|
||||
@@ -338,13 +349,13 @@ namespace BitTorrent
|
||||
void handleAlert(libtorrent::alert *a);
|
||||
void handleStateUpdate(const libtorrent::torrent_status &nativeStatus);
|
||||
void handleTempPathChanged();
|
||||
void handleCategorySavePathChanged();
|
||||
void handleAppendExtensionToggled();
|
||||
void saveResumeData();
|
||||
|
||||
private:
|
||||
typedef boost::function<void ()> EventTrigger;
|
||||
|
||||
void initialize();
|
||||
void updateStatus();
|
||||
void updateStatus(const libtorrent::torrent_status &nativeStatus);
|
||||
void updateState();
|
||||
@@ -374,9 +385,9 @@ namespace BitTorrent
|
||||
|
||||
void adjustActualSavePath();
|
||||
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);
|
||||
@@ -400,10 +411,12 @@ namespace BitTorrent
|
||||
QQueue<EventTrigger> m_moveFinishedTriggers;
|
||||
int m_renameCount;
|
||||
|
||||
bool m_useAutoTMM;
|
||||
|
||||
// Persistent data
|
||||
QString m_name;
|
||||
QString m_savePath;
|
||||
QString m_label;
|
||||
QString m_category;
|
||||
bool m_hasSeedStatus;
|
||||
qreal m_ratioLimit;
|
||||
bool m_tempPathDisabled;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
@@ -43,9 +44,9 @@
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
TorrentInfo::TorrentInfo(boost::intrusive_ptr<const libt::torrent_info> nativeInfo)
|
||||
: m_nativeInfo(const_cast<libt::torrent_info *>(nativeInfo.get()))
|
||||
TorrentInfo::TorrentInfo(NativeConstPtr nativeInfo)
|
||||
{
|
||||
m_nativeInfo = boost::const_pointer_cast<libt::torrent_info>(nativeInfo);
|
||||
}
|
||||
|
||||
TorrentInfo::TorrentInfo(const TorrentInfo &other)
|
||||
@@ -63,7 +64,7 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error)
|
||||
{
|
||||
error.clear();
|
||||
libt::error_code ec;
|
||||
TorrentInfo info(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec));
|
||||
TorrentInfo info(NativePtr(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec)));
|
||||
if (ec) {
|
||||
error = QString::fromUtf8(ec.message().c_str());
|
||||
qDebug("Cannot load .torrent file: %s", qPrintable(error));
|
||||
@@ -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,25 +220,80 @@ 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<int> fileIndices = fileIndicesForPiece(pieceIndex);
|
||||
|
||||
std::vector<libtorrent::file_slice> files(
|
||||
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_length()));
|
||||
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<int> TorrentInfo::fileIndicesForPiece(int pieceIndex) const
|
||||
{
|
||||
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
|
||||
return QVector<int>();
|
||||
|
||||
std::vector<libt::file_slice> files(
|
||||
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
|
||||
QVector<int> res;
|
||||
res.reserve(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<int>(firstOffset / pieceLength()),
|
||||
static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
|
||||
}
|
||||
|
||||
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
||||
{
|
||||
if (!isValid()) return;
|
||||
m_nativeInfo->rename_file(index, Utils::String::toStdString(newPath));
|
||||
nativeInfo()->rename_file(index, Utils::String::toStdString(newPath));
|
||||
}
|
||||
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> TorrentInfo::nativeInfo() const
|
||||
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
||||
{
|
||||
// the check whether the object valid is not needed here
|
||||
// because filesCount() returns -1 in that case and the loop exits immediately
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
if (fileName == filePath(i))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
|
||||
{
|
||||
return m_nativeInfo;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,11 @@
|
||||
#define BITTORRENT_TORRENTINFO_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
#include "base/indexrange.h"
|
||||
|
||||
class QString;
|
||||
class QUrl;
|
||||
@@ -38,6 +42,7 @@ class QDateTime;
|
||||
class QStringList;
|
||||
class QByteArray;
|
||||
template<typename T> class QList;
|
||||
template<typename T> class QVector;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
@@ -47,7 +52,15 @@ namespace BitTorrent
|
||||
class TorrentInfo
|
||||
{
|
||||
public:
|
||||
explicit TorrentInfo(boost::intrusive_ptr<const libtorrent::torrent_info> nativeInfo = boost::intrusive_ptr<const libtorrent::torrent_info>());
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
typedef boost::intrusive_ptr<const libtorrent::torrent_info> NativeConstPtr;
|
||||
typedef boost::intrusive_ptr<libtorrent::torrent_info> NativePtr;
|
||||
#else
|
||||
typedef boost::shared_ptr<const libtorrent::torrent_info> NativeConstPtr;
|
||||
typedef boost::shared_ptr<libtorrent::torrent_info> NativePtr;
|
||||
#endif
|
||||
|
||||
explicit TorrentInfo(NativeConstPtr nativeInfo = NativeConstPtr());
|
||||
TorrentInfo(const TorrentInfo &other);
|
||||
|
||||
static TorrentInfo loadFromFile(const QString &path, QString &error);
|
||||
@@ -65,6 +78,7 @@ namespace BitTorrent
|
||||
qlonglong totalSize() const;
|
||||
int filesCount() const;
|
||||
int pieceLength() const;
|
||||
int pieceLength(int index) const;
|
||||
int piecesCount() const;
|
||||
QString filePath(int index) const;
|
||||
QStringList filePaths() const;
|
||||
@@ -76,12 +90,22 @@ namespace BitTorrent
|
||||
QList<QUrl> urlSeeds() const;
|
||||
QByteArray metadata() const;
|
||||
QStringList filesForPiece(int pieceIndex) const;
|
||||
QVector<int> fileIndicesForPiece(int pieceIndex) const;
|
||||
|
||||
using PieceRange = IndexRange<int>;
|
||||
// returns pair of the first and the last pieces into which
|
||||
// the given file extends (maybe partially).
|
||||
PieceRange filePieces(const QString &file) const;
|
||||
PieceRange filePieces(int fileIndex) const;
|
||||
|
||||
void renameFile(uint index, const QString &newPath);
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> nativeInfo() const;
|
||||
|
||||
NativePtr nativeInfo() const;
|
||||
|
||||
private:
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> m_nativeInfo;
|
||||
// returns file index or -1 if fileName is not found
|
||||
int fileIndex(const QString &fileName) const;
|
||||
NativePtr m_nativeInfo;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#define BITTORRENT_TRACKERENTRY_H
|
||||
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
#include <libtorrent/version.hpp>
|
||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||
#include <libtorrent/announce_entry.hpp>
|
||||
#endif
|
||||
|
||||
class QString;
|
||||
|
||||
|
||||
@@ -195,11 +195,7 @@ void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrent
|
||||
foreach (const QString &file, files) {
|
||||
const QString fileAbsPath = dir.absoluteFilePath(file);
|
||||
if (fileAbsPath.endsWith(".magnet")) {
|
||||
QFile f(fileAbsPath);
|
||||
if (f.open(QIODevice::ReadOnly)
|
||||
&& !BitTorrent::MagnetUri(QString::fromLocal8Bit(f.readAll())).isValid()) {
|
||||
torrents << fileAbsPath;
|
||||
}
|
||||
torrents << fileAbsPath;
|
||||
}
|
||||
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) {
|
||||
torrents << fileAbsPath;
|
||||
|
||||
@@ -81,6 +81,11 @@ RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data,
|
||||
// Parse HTTP request message
|
||||
if (m_request.headers.contains("content-length")) {
|
||||
int content_length = m_request.headers["content-length"].toInt();
|
||||
if (content_length < 0) {
|
||||
qWarning() << Q_FUNC_INFO << "bad request: content-length negative";
|
||||
return BadRequest;
|
||||
}
|
||||
|
||||
if (content_length > static_cast<int>(m_maxContentLength)) {
|
||||
qWarning() << Q_FUNC_INFO << "bad request: message too long";
|
||||
return BadRequest;
|
||||
@@ -92,7 +97,7 @@ RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data,
|
||||
return IncompleteRequest;
|
||||
}
|
||||
|
||||
if (!parseContent(content)) {
|
||||
if ((content_length > 0) && !parseContent(content)) {
|
||||
qWarning() << Q_FUNC_INFO << "message parsing error";
|
||||
return BadRequest;
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ Server::~Server()
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
|
||||
void Server::enableHttps(const QList<QSslCertificate> &certificates, const QSslKey &key)
|
||||
{
|
||||
m_certificate = certificate;
|
||||
m_certificates = certificates;
|
||||
m_key = key;
|
||||
m_https = true;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
|
||||
void Server::disableHttps()
|
||||
{
|
||||
m_https = false;
|
||||
m_certificate.clear();
|
||||
m_certificates.clear();
|
||||
m_key.clear();
|
||||
}
|
||||
#endif
|
||||
@@ -84,9 +84,13 @@ void Server::incomingConnection(int socketDescriptor)
|
||||
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
|
||||
#ifndef QT_NO_OPENSSL
|
||||
if (m_https) {
|
||||
static_cast<QSslSocket*>(serverSocket)->setProtocol(QSsl::AnyProtocol);
|
||||
static_cast<QSslSocket*>(serverSocket)->setProtocol(QSsl::SecureProtocols);
|
||||
static_cast<QSslSocket*>(serverSocket)->setPrivateKey(m_key);
|
||||
static_cast<QSslSocket*>(serverSocket)->setLocalCertificate(m_certificate);
|
||||
#ifdef QBT_USES_QT5
|
||||
static_cast<QSslSocket*>(serverSocket)->setLocalCertificateChain(m_certificates);
|
||||
#else
|
||||
static_cast<QSslSocket*>(serverSocket)->setLocalCertificate(m_certificates.first());
|
||||
#endif
|
||||
static_cast<QSslSocket*>(serverSocket)->startServerEncryption();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Http
|
||||
~Server();
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
||||
void enableHttps(const QList<QSslCertificate> &certificates, const QSslKey &key);
|
||||
void disableHttps();
|
||||
#endif
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Http
|
||||
IRequestHandler *m_requestHandler;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
bool m_https;
|
||||
QSslCertificate m_certificate;
|
||||
QList<QSslCertificate> m_certificates;
|
||||
QSslKey m_key;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <QHostAddress>
|
||||
#include <QVector>
|
||||
|
||||
typedef QMap<QString, QString> QStringMap;
|
||||
#include "base/types.h"
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
130
src/base/indexrange.h
Normal file
130
src/base/indexrange.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Eugene Shalygin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef QBT_INDEXRANGE_H
|
||||
#define QBT_INDEXRANGE_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
// Interval is defined via [first;last]
|
||||
template <typename Index>
|
||||
class IndexInterval
|
||||
{
|
||||
public:
|
||||
using IndexType = Index;
|
||||
|
||||
IndexInterval(IndexType first, IndexType last)
|
||||
: m_first {first}
|
||||
, m_last {last}
|
||||
{
|
||||
Q_ASSERT(first <= last);
|
||||
}
|
||||
|
||||
IndexType first() const
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
IndexType last() const
|
||||
{
|
||||
return m_last;
|
||||
}
|
||||
|
||||
private:
|
||||
IndexType m_first;
|
||||
IndexType m_last;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline IndexInterval<T> makeInterval(T first, T last)
|
||||
{
|
||||
return {first, last};
|
||||
}
|
||||
|
||||
// range is defined via first index and size
|
||||
template <typename Index, typename IndexDiff = Index>
|
||||
class IndexRange
|
||||
{
|
||||
public:
|
||||
using IndexType = Index;
|
||||
using IndexDiffType = IndexDiff;
|
||||
|
||||
constexpr IndexRange()
|
||||
: m_first {0}
|
||||
, m_size {0}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr IndexRange(IndexType first, IndexDiffType size)
|
||||
: m_first {first}
|
||||
, m_size {size}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr IndexRange(const IndexInterval<IndexType> &interval)
|
||||
: m_first {interval.first()}
|
||||
, m_size {interval.last() - interval.first() + 1}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr IndexType begin() const
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
constexpr IndexType end() const
|
||||
{
|
||||
return m_first + m_size;
|
||||
}
|
||||
|
||||
constexpr IndexDiffType size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
constexpr IndexType first() const
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
constexpr IndexType last() const
|
||||
{
|
||||
return m_first + m_size - 1;
|
||||
}
|
||||
|
||||
constexpr bool isEmpty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
IndexType m_first;
|
||||
IndexDiffType m_size;
|
||||
};
|
||||
|
||||
#endif // QBT_INDEXRANGE_H
|
||||
@@ -12,11 +12,13 @@ namespace Log
|
||||
{
|
||||
enum MsgType
|
||||
{
|
||||
NORMAL,
|
||||
INFO,
|
||||
WARNING,
|
||||
CRITICAL //ERROR is defined by libtorrent and results in compiler error
|
||||
ALL = -1,
|
||||
NORMAL = 0x1,
|
||||
INFO = 0x2,
|
||||
WARNING = 0x4,
|
||||
CRITICAL = 0x8 //ERROR is defined by libtorrent and results in compiler error
|
||||
};
|
||||
Q_DECLARE_FLAGS(MsgTypes, MsgType)
|
||||
|
||||
struct Msg
|
||||
{
|
||||
@@ -36,6 +38,8 @@ namespace Log
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Log::MsgTypes)
|
||||
|
||||
class Logger : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -300,7 +300,7 @@ QUrl DNSUpdater::getRegistrationUrl(int service)
|
||||
case DNS::DYNDNS:
|
||||
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
||||
case DNS::NOIP:
|
||||
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
||||
return QUrl("https://www.noip.com/remote-access");
|
||||
default:
|
||||
Q_ASSERT(0);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,9 @@ namespace
|
||||
Preferences::instance()->setNetworkCookies(cookies);
|
||||
}
|
||||
|
||||
using QNetworkCookieJar::allCookies;
|
||||
using QNetworkCookieJar::setAllCookies;
|
||||
|
||||
#ifndef QBT_USES_QT5
|
||||
virtual bool deleteCookie(const QNetworkCookie &cookie)
|
||||
{
|
||||
@@ -188,6 +191,16 @@ bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
|
||||
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
||||
}
|
||||
|
||||
QList<QNetworkCookie> DownloadManager::allCookies() const
|
||||
{
|
||||
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
|
||||
}
|
||||
|
||||
void DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
|
||||
{
|
||||
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
|
||||
}
|
||||
|
||||
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
|
||||
{
|
||||
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace Net
|
||||
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false, const QString &userAgent = "");
|
||||
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
|
||||
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
||||
QList<QNetworkCookie> allCookies() const;
|
||||
void setAllCookies(const QList<QNetworkCookie> &cookieList);
|
||||
bool deleteCookie(const QNetworkCookie &cookie);
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "private/geoipdatabase.h"
|
||||
#include "geoipmanager.h"
|
||||
|
||||
static const char DATABASE_URL[] = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
|
||||
static const char DATABASE_URL[] = "https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
|
||||
static const char GEOIP_FOLDER[] = "GeoIP";
|
||||
static const char GEOIP_FILENAME[] = "GeoLite2-Country.mmdb";
|
||||
static const int CACHE_SIZE = 1000;
|
||||
|
||||
@@ -58,9 +58,8 @@ namespace
|
||||
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
|
||||
{
|
||||
const int blockSize = 64; // HMAC-MD5 block size
|
||||
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
|
||||
if (key.length() > blockSize) // if key is longer than block size (64), reduce key length with MD5 compression
|
||||
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
|
||||
}
|
||||
|
||||
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
|
||||
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
|
||||
@@ -123,18 +122,17 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
||||
m_message = "";
|
||||
m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
|
||||
m_message += encodeMimeHeader("From", from, latin1);
|
||||
m_message += encodeMimeHeader("Subject", subject, latin1);
|
||||
m_message += encodeMimeHeader("To", to, latin1);
|
||||
m_message += "MIME-Version: 1.0\r\n";
|
||||
m_message += "Content-Type: text/plain; charset=UTF-8\r\n";
|
||||
m_message += "Content-Transfer-Encoding: base64\r\n";
|
||||
m_message += "\r\n";
|
||||
m_message = "Date: " + getCurrentDateTime().toLatin1() + "\r\n"
|
||||
+ encodeMimeHeader("From", from, latin1)
|
||||
+ encodeMimeHeader("Subject", subject, latin1)
|
||||
+ encodeMimeHeader("To", to, latin1)
|
||||
+ "MIME-Version: 1.0\r\n"
|
||||
+ "Content-Type: text/plain; charset=UTF-8\r\n"
|
||||
+ "Content-Transfer-Encoding: base64\r\n"
|
||||
+ "\r\n";
|
||||
// Encode the body in base64
|
||||
QString crlf_body = body;
|
||||
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
|
||||
QByteArray b = crlf_body.replace("\n", "\r\n").toUtf8().toBase64();
|
||||
int ct = b.length();
|
||||
for (int i = 0; i < ct; i += 78)
|
||||
m_message += b.mid(i, 78);
|
||||
@@ -154,10 +152,10 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
||||
m_useSsl = false;
|
||||
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
||||
m_useSsl = false;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -186,7 +184,7 @@ void Smtp::readyRead()
|
||||
ehlo();
|
||||
}
|
||||
else {
|
||||
logError("Connection failed, unrecognized reply: "+line);
|
||||
logError("Connection failed, unrecognized reply: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
@@ -223,7 +221,7 @@ void Smtp::readyRead()
|
||||
}
|
||||
else {
|
||||
// Authentication failed!
|
||||
logError("Authentication failed, msg: "+line);
|
||||
logError("Authentication failed, msg: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
@@ -234,7 +232,7 @@ void Smtp::readyRead()
|
||||
m_state = Data;
|
||||
}
|
||||
else {
|
||||
logError("<mail from> was rejected by server, msg: "+line);
|
||||
logError("<mail from> was rejected by server, msg: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
@@ -245,7 +243,7 @@ void Smtp::readyRead()
|
||||
m_state = Body;
|
||||
}
|
||||
else {
|
||||
logError("<Rcpt to> was rejected by server, msg: "+line);
|
||||
logError("<Rcpt to> was rejected by server, msg: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
@@ -256,7 +254,7 @@ void Smtp::readyRead()
|
||||
m_state = Quit;
|
||||
}
|
||||
else {
|
||||
logError("<data> was rejected by server, msg: "+line);
|
||||
logError("<data> was rejected by server, msg: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
@@ -268,7 +266,7 @@ void Smtp::readyRead()
|
||||
m_state = Close;
|
||||
}
|
||||
else {
|
||||
logError("Message was rejected by the server, error: "+line);
|
||||
logError("Message was rejected by the server, error: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
@@ -309,9 +307,9 @@ QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTex
|
||||
line += "=?utf-8?b?";
|
||||
for (int i = 0; i < ct; i += 4) {
|
||||
/*if (line.length() > 72) {
|
||||
rv += line + "?\n\r";
|
||||
line = " =?utf-8?b?";
|
||||
}*/
|
||||
rv += line + "?\n\r";
|
||||
line = " =?utf-8?b?";
|
||||
}*/
|
||||
line = line + base64.mid(i, 4);
|
||||
}
|
||||
line += "?="; // end encoded-word atom
|
||||
@@ -347,7 +345,7 @@ void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QStri
|
||||
else {
|
||||
// Both EHLO and HELO failed, chances are this is NOT
|
||||
// a SMTP server
|
||||
logError("Both EHLO and HELO failed, msg: "+line);
|
||||
logError("Both EHLO and HELO failed, msg: " + line);
|
||||
m_state = Close;
|
||||
}
|
||||
return;
|
||||
@@ -362,7 +360,7 @@ void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QStri
|
||||
else {
|
||||
// greeting followed by extensions
|
||||
m_state = EhloGreetReceived;
|
||||
qDebug () << "EHLO greet received";
|
||||
qDebug() << "EHLO greet received";
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -419,7 +417,7 @@ void Smtp::authenticate()
|
||||
// Skip authentication
|
||||
logError("The SMTP server does not seem to support any of the authentications modes "
|
||||
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
||||
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
|
||||
"knowing it is likely to fail... Server Auth Modes: " + auth.join("|"));
|
||||
m_state = Authenticated;
|
||||
// At this point the server will not send any response
|
||||
// So fill the buffer with a fake one to pass the tests
|
||||
@@ -450,7 +448,7 @@ void Smtp::authCramMD5(const QByteArray& challenge)
|
||||
}
|
||||
else {
|
||||
QByteArray response = m_username.toLatin1() + ' '
|
||||
+ hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
|
||||
+ hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
|
||||
m_socket->write(response.toBase64() + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = AuthSent;
|
||||
@@ -470,7 +468,7 @@ void Smtp::authPlain()
|
||||
auth += m_password.toLatin1();
|
||||
qDebug() << "password: " << m_password.toLatin1();
|
||||
// Send it
|
||||
m_socket->write("auth plain "+ auth.toBase64() + "\r\n");
|
||||
m_socket->write("auth plain " + auth.toBase64() + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = AuthSent;
|
||||
}
|
||||
@@ -501,3 +499,29 @@ void Smtp::logError(const QString &msg)
|
||||
qDebug() << "Email Notification Error:" << msg;
|
||||
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
||||
}
|
||||
|
||||
QString Smtp::getCurrentDateTime() const
|
||||
{
|
||||
// return date & time in the format specified in RFC 2822, section 3.3
|
||||
const QDateTime nowDateTime = QDateTime::currentDateTime();
|
||||
const QDate nowDate = nowDateTime.date();
|
||||
const QLocale eng(QLocale::English);
|
||||
|
||||
QString timeStr = nowDateTime.time().toString("HH:mm:ss");
|
||||
QString weekDayStr = eng.dayName(nowDate.dayOfWeek(), QLocale::ShortFormat);
|
||||
QString dayStr = QString::number(nowDate.day());
|
||||
QString monthStr = eng.monthName(nowDate.month(), QLocale::ShortFormat);
|
||||
QString yearStr = QString::number(nowDate.year());
|
||||
|
||||
QDateTime tmp = nowDateTime;
|
||||
tmp.setTimeSpec(Qt::UTC);
|
||||
int timeOffsetHour = nowDateTime.secsTo(tmp) / 3600;
|
||||
int timeOffsetMin = nowDateTime.secsTo(tmp) / 60 - (60 * timeOffsetHour);
|
||||
int timeOffset = timeOffsetHour * 100 + timeOffsetMin;
|
||||
char buf[6] = {0};
|
||||
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
|
||||
QString timeOffsetStr = buf;
|
||||
|
||||
QString ret = weekDayStr + ", " + dayStr + " " + monthStr + " " + yearStr + " " + timeStr + " " + timeOffsetStr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace Net
|
||||
void authPlain();
|
||||
void authLogin();
|
||||
void logError(const QString &msg);
|
||||
QString getCurrentDateTime() const;
|
||||
|
||||
QByteArray m_message;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
|
||||
@@ -30,15 +30,10 @@
|
||||
* Contact : hammered999@gmail.com
|
||||
*/
|
||||
|
||||
#include "preferences.h"
|
||||
#include "qinisettings.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QPair>
|
||||
#include <QDir>
|
||||
#include <QReadLocker>
|
||||
#include <QWriteLocker>
|
||||
#include <QSettings>
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
#include <QApplication>
|
||||
@@ -56,74 +51,18 @@
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/misc.h"
|
||||
|
||||
#include "utils/fs.h"
|
||||
#include "utils/misc.h"
|
||||
#include "settingsstorage.h"
|
||||
#include "logger.h"
|
||||
#include "preferences.h"
|
||||
|
||||
Preferences* Preferences::m_instance = 0;
|
||||
|
||||
Preferences::Preferences()
|
||||
: m_randomPort(rand() % 64512 + 1024)
|
||||
, dirty(false)
|
||||
, lock(QReadWriteLock::Recursive)
|
||||
{
|
||||
qRegisterMetaTypeStreamOperators<MaxRatioAction>("MaxRatioAction");
|
||||
|
||||
QIniSettings *settings = new QIniSettings;
|
||||
#ifndef Q_OS_MAC
|
||||
QIniSettings *settings_new = new QIniSettings("qBittorrent", "qBittorrent_new");
|
||||
QStringList keys = settings_new->allKeys();
|
||||
bool use_new = false;
|
||||
|
||||
// This means that the PC closed either due to power outage
|
||||
// or because the disk was full. In any case the settings weren't transfered
|
||||
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
|
||||
// contains the most recent settings.
|
||||
if (!keys.isEmpty()) {
|
||||
Logger::instance()->addMessage(tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
|
||||
use_new = true;
|
||||
dirty = true;
|
||||
}
|
||||
else {
|
||||
keys = settings->allKeys();
|
||||
}
|
||||
#else
|
||||
QStringList keys = settings->allKeys();
|
||||
#endif
|
||||
|
||||
// Copy everything into memory. This means even keys inserted in the file manually
|
||||
// or that we don't touch directly in this code(eg disabled by ifdef). This ensures
|
||||
// that they will be copied over when save our settings to disk.
|
||||
for (QStringList::const_iterator i = keys.begin(), e = keys.end(); i != e; ++i) {
|
||||
#ifndef Q_OS_MAC
|
||||
if (!use_new)
|
||||
m_data[*i] = settings->value(*i);
|
||||
else
|
||||
m_data[*i] = settings_new->value(*i);
|
||||
#else
|
||||
m_data[*i] = settings->value(*i);
|
||||
#endif
|
||||
}
|
||||
|
||||
//Ensures sync to disk before we attempt to manipulate the files from save().
|
||||
delete settings;
|
||||
#ifndef Q_OS_MAC
|
||||
QString new_path = settings_new->fileName();
|
||||
delete settings_new;
|
||||
Utils::Fs::forceRemove(new_path);
|
||||
|
||||
if (use_new)
|
||||
save();
|
||||
#endif
|
||||
|
||||
timer.setSingleShot(true);
|
||||
timer.setInterval(5 * 1000);
|
||||
connect(&timer, SIGNAL(timeout()), SLOT(save()));
|
||||
}
|
||||
|
||||
Preferences::~Preferences()
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
Preferences *Preferences::instance()
|
||||
@@ -145,71 +84,14 @@ void Preferences::freeInstance()
|
||||
}
|
||||
}
|
||||
|
||||
bool Preferences::save()
|
||||
{
|
||||
QWriteLocker locker(&lock);
|
||||
|
||||
if (!dirty) return false;
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// QSettings delete the file before writing it out. This can result in problems
|
||||
// if the disk is full or a power outage occurs. Those events might occur
|
||||
// between deleting the file and recreating it. This is a safety measure.
|
||||
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
|
||||
// replace qBittorrent_new.ini/qBittorrent.conf with it.
|
||||
QIniSettings *settings = new QIniSettings("qBittorrent", "qBittorrent_new");
|
||||
#else
|
||||
QIniSettings *settings = new QIniSettings;
|
||||
#endif
|
||||
|
||||
for (QHash<QString, QVariant>::const_iterator i = m_data.begin(), e = m_data.end(); i != e; ++i)
|
||||
settings->setValue(i.key(), i.value());
|
||||
|
||||
dirty = false;
|
||||
locker.unlock();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
settings->sync(); // Important to get error status
|
||||
QString new_path = settings->fileName();
|
||||
QSettings::Status status = settings->status();
|
||||
|
||||
if (status != QSettings::NoError) {
|
||||
if (status == QSettings::AccessError)
|
||||
Logger::instance()->addMessage(tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
else
|
||||
Logger::instance()->addMessage(tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
|
||||
delete settings;
|
||||
Utils::Fs::forceRemove(new_path);
|
||||
return false;
|
||||
}
|
||||
delete settings;
|
||||
QString final_path = new_path;
|
||||
int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
final_path.remove(index, 4);
|
||||
Utils::Fs::forceRemove(final_path);
|
||||
QFile::rename(new_path, final_path);
|
||||
#else
|
||||
delete settings;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const
|
||||
{
|
||||
QReadLocker locker(&lock);
|
||||
return m_data.value(key, defaultValue);
|
||||
return SettingsStorage::instance()->loadValue(key, defaultValue);
|
||||
}
|
||||
|
||||
void Preferences::setValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
QWriteLocker locker(&lock);
|
||||
if (m_data.value(key) == value)
|
||||
return;
|
||||
dirty = true;
|
||||
timer.start();
|
||||
m_data.insert(key, value);
|
||||
SettingsStorage::instance()->storeValue(key, value);
|
||||
}
|
||||
|
||||
// General options
|
||||
@@ -223,16 +105,6 @@ void Preferences::setLocale(const QString &locale)
|
||||
setValue("Preferences/General/Locale", locale);
|
||||
}
|
||||
|
||||
bool Preferences::useProgramNotification() const
|
||||
{
|
||||
return value("Preferences/General/ProgramNotification", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::useProgramNotification(bool use)
|
||||
{
|
||||
setValue("Preferences/General/ProgramNotification", use);
|
||||
}
|
||||
|
||||
bool Preferences::deleteTorrentFilesAsDefault() const
|
||||
{
|
||||
return value("Preferences/General/DeleteTorrentsFilesAsDefault", false).toBool();
|
||||
@@ -273,6 +145,26 @@ void Preferences::setAlternatingRowColors(bool b)
|
||||
setValue("Preferences/General/AlternatingRowColors", b);
|
||||
}
|
||||
|
||||
bool Preferences::getHideZeroValues() const
|
||||
{
|
||||
return value("Preferences/General/HideZeroValues", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setHideZeroValues(bool b)
|
||||
{
|
||||
setValue("Preferences/General/HideZeroValues", b);
|
||||
}
|
||||
|
||||
int Preferences::getHideZeroComboValues() const
|
||||
{
|
||||
return value("Preferences/General/HideZeroComboValues", 0).toInt();
|
||||
}
|
||||
|
||||
void Preferences::setHideZeroComboValues(int n)
|
||||
{
|
||||
setValue("Preferences/General/HideZeroComboValues", n);
|
||||
}
|
||||
|
||||
bool Preferences::useRandomPort() const
|
||||
{
|
||||
return value("Preferences/General/UseRandomPort", false).toBool();
|
||||
@@ -335,7 +227,7 @@ void Preferences::setStartMinimized(bool b)
|
||||
|
||||
bool Preferences::isSplashScreenDisabled() const
|
||||
{
|
||||
return value("Preferences/General/NoSplashScreen", false).toBool();
|
||||
return value("Preferences/General/NoSplashScreen", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setSplashScreenDisabled(bool b)
|
||||
@@ -375,40 +267,6 @@ void Preferences::setWinStartup(bool b)
|
||||
#endif
|
||||
|
||||
// Downloads
|
||||
QString Preferences::getSavePath() const
|
||||
{
|
||||
QString save_path = value("Preferences/Downloads/SavePath").toString();
|
||||
if (!save_path.isEmpty())
|
||||
return Utils::Fs::fromNativePath(save_path);
|
||||
return Utils::Fs::QDesktopServicesDownloadLocation();
|
||||
}
|
||||
|
||||
void Preferences::setSavePath(const QString &save_path)
|
||||
{
|
||||
setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path));
|
||||
}
|
||||
|
||||
bool Preferences::isTempPathEnabled() const
|
||||
{
|
||||
return value("Preferences/Downloads/TempPathEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setTempPathEnabled(bool enabled)
|
||||
{
|
||||
setValue("Preferences/Downloads/TempPathEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Preferences::getTempPath() const
|
||||
{
|
||||
const QString temp = QDir(getSavePath()).absoluteFilePath("temp");
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
|
||||
}
|
||||
|
||||
void Preferences::setTempPath(const QString &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path));
|
||||
}
|
||||
|
||||
bool Preferences::useIncompleteFilesExtension() const
|
||||
{
|
||||
return value("Preferences/Downloads/UseIncompleteExtension", false).toBool();
|
||||
@@ -419,16 +277,6 @@ void Preferences::useIncompleteFilesExtension(bool enabled)
|
||||
setValue("Preferences/Downloads/UseIncompleteExtension", enabled);
|
||||
}
|
||||
|
||||
bool Preferences::appendTorrentLabel() const
|
||||
{
|
||||
return value("Preferences/Downloads/AppendLabel", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setAppendTorrentLabel(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/AppendLabel", b);
|
||||
}
|
||||
|
||||
QString Preferences::lastLocationPath() const
|
||||
{
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
|
||||
@@ -449,36 +297,6 @@ void Preferences::preAllocateAllFiles(bool enabled)
|
||||
return setValue("Preferences/Downloads/PreAllocation", enabled);
|
||||
}
|
||||
|
||||
bool Preferences::useAdditionDialog() const
|
||||
{
|
||||
return value("Preferences/Downloads/NewAdditionDialog", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::useAdditionDialog(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/NewAdditionDialog", b);
|
||||
}
|
||||
|
||||
bool Preferences::additionDialogFront() const
|
||||
{
|
||||
return value("Preferences/Downloads/NewAdditionDialogFront", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::additionDialogFront(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/NewAdditionDialogFront", b);
|
||||
}
|
||||
|
||||
bool Preferences::addTorrentsInPause() const
|
||||
{
|
||||
return value("Preferences/Downloads/StartInPause", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::addTorrentsInPause(bool b)
|
||||
{
|
||||
setValue("Preferences/Downloads/StartInPause", b);
|
||||
}
|
||||
|
||||
QVariantHash Preferences::getScanDirs() const
|
||||
{
|
||||
return value("Preferences/Downloads/ScanDirsV2").toHash();
|
||||
@@ -623,7 +441,6 @@ void Preferences::setActionOnDblClOnTorrentFn(int act)
|
||||
// Connection options
|
||||
int Preferences::getSessionPort() const
|
||||
{
|
||||
QReadLocker locker(&lock);
|
||||
if (useRandomPort())
|
||||
return m_randomPort;
|
||||
return value("Preferences/Connection/PortRangeMin", 8999).toInt();
|
||||
@@ -969,7 +786,7 @@ void Preferences::setTrackersList(const QString &val)
|
||||
|
||||
qreal Preferences::getGlobalMaxRatio() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/MaxRatio", -1).toDouble();
|
||||
return value("Preferences/Bittorrent/MaxRatio", -1).toReal();
|
||||
}
|
||||
|
||||
void Preferences::setGlobalMaxRatio(qreal ratio)
|
||||
@@ -977,16 +794,6 @@ void Preferences::setGlobalMaxRatio(qreal ratio)
|
||||
setValue("Preferences/Bittorrent/MaxRatio", ratio);
|
||||
}
|
||||
|
||||
MaxRatioAction Preferences::getMaxRatioAction() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(MaxRatioAction::Pause)).value<MaxRatioAction>();
|
||||
}
|
||||
|
||||
void Preferences::setMaxRatioAction(MaxRatioAction act)
|
||||
{
|
||||
setValue("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(act));
|
||||
}
|
||||
|
||||
// IP Filter
|
||||
bool Preferences::isFilteringEnabled() const
|
||||
{
|
||||
@@ -1043,17 +850,6 @@ void Preferences::setSearchEnabled(bool enabled)
|
||||
setValue("Preferences/Search/SearchEnabled", enabled);
|
||||
}
|
||||
|
||||
// Execution Log
|
||||
bool Preferences::isExecutionLogEnabled() const
|
||||
{
|
||||
return value("Preferences/ExecutionLog/enabled", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setExecutionLogEnabled(bool b)
|
||||
{
|
||||
setValue("Preferences/ExecutionLog/enabled", b);
|
||||
}
|
||||
|
||||
// Queueing system
|
||||
bool Preferences::isQueueingSystemEnabled() const
|
||||
{
|
||||
@@ -1310,12 +1106,12 @@ void Preferences::setAutoRunEnabled(bool enabled)
|
||||
|
||||
QString Preferences::getAutoRunProgram() const
|
||||
{
|
||||
return Utils::Fs::fromNativePath(value("AutoRun/program").toString());
|
||||
return value("AutoRun/program").toString();
|
||||
}
|
||||
|
||||
void Preferences::setAutoRunProgram(const QString &program)
|
||||
{
|
||||
setValue("AutoRun/program", Utils::Fs::fromNativePath(program));
|
||||
setValue("AutoRun/program", program);
|
||||
}
|
||||
|
||||
bool Preferences::shutdownWhenDownloadsComplete() const
|
||||
@@ -1358,6 +1154,16 @@ void Preferences::setShutdownqBTWhenDownloadsComplete(bool shutdown)
|
||||
setValue("Preferences/Downloads/AutoShutDownqBTOnCompletion", shutdown);
|
||||
}
|
||||
|
||||
bool Preferences::dontConfirmAutoExit() const
|
||||
{
|
||||
return value("ShutdownConfirmDlg/DontConfirmAutoExit", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setDontConfirmAutoExit(bool dontConfirmAutoExit)
|
||||
{
|
||||
setValue("ShutdownConfirmDlg/DontConfirmAutoExit", dontConfirmAutoExit);
|
||||
}
|
||||
|
||||
uint Preferences::diskCacheSize() const
|
||||
{
|
||||
uint size = value("Preferences/Downloads/DiskWriteCacheSize", 0).toUInt();
|
||||
@@ -1538,6 +1344,16 @@ void Preferences::setNetworkInterfaceName(const QString& iface)
|
||||
setValue("Preferences/Connection/InterfaceName", iface);
|
||||
}
|
||||
|
||||
void Preferences::setNetworkInterfaceAddress(const QString& addr)
|
||||
{
|
||||
setValue("Preferences/Connection/InterfaceAddress", addr);
|
||||
}
|
||||
|
||||
QString Preferences::getNetworkInterfaceAddress() const
|
||||
{
|
||||
return value("Preferences/Connection/InterfaceAddress").toString();
|
||||
}
|
||||
|
||||
bool Preferences::getListenIPv6() const
|
||||
{
|
||||
return value("Preferences/Connection/InterfaceListenIPv6", false).toBool();
|
||||
@@ -1600,51 +1416,6 @@ void Preferences::useSystemIconTheme(bool enabled)
|
||||
}
|
||||
#endif
|
||||
|
||||
QStringList Preferences::getTorrentLabels() const
|
||||
{
|
||||
return value("TransferListFilters/customLabels").toStringList();
|
||||
}
|
||||
|
||||
void Preferences::setTorrentLabels(const QStringList& labels)
|
||||
{
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
void Preferences::addTorrentLabelExternal(const QString &label)
|
||||
{
|
||||
addTorrentLabel(label);
|
||||
QString toEmit = label;
|
||||
emit externalLabelAdded(toEmit);
|
||||
}
|
||||
|
||||
void Preferences::addTorrentLabel(const QString& label)
|
||||
{
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
if (labels.contains(label))
|
||||
return;
|
||||
labels << label;
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
void Preferences::removeTorrentLabel(const QString& label)
|
||||
{
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
if (!labels.contains(label))
|
||||
return;
|
||||
labels.removeOne(label);
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
QString Preferences::getDefaultLabel() const
|
||||
{
|
||||
return value("Preferences/Downloads/DefaultLabel").toString();
|
||||
}
|
||||
|
||||
void Preferences::setDefaultLabel(const QString &defaultLabel)
|
||||
{
|
||||
setValue("Preferences/Downloads/DefaultLabel", defaultLabel);
|
||||
}
|
||||
|
||||
bool Preferences::recursiveDownloadDisabled() const
|
||||
{
|
||||
return value("Preferences/Advanced/DisableRecursiveDownload", false).toBool();
|
||||
@@ -1900,7 +1671,7 @@ bool Preferences::isTorrentFileAssocSet()
|
||||
CFStringRef myBundleId = CFBundleGetIdentifier(CFBundleGetMainBundle());
|
||||
isSet = CFStringCompare(myBundleId, defaultHandlerId, 0) == kCFCompareEqualTo;
|
||||
CFRelease(defaultHandlerId);
|
||||
}
|
||||
}
|
||||
CFRelease(torrentId);
|
||||
}
|
||||
return isSet;
|
||||
@@ -2003,63 +1774,6 @@ void Preferences::setTrayIconStyle(TrayIcon::Style style)
|
||||
|
||||
// Stuff that don't appear in the Options GUI but are saved
|
||||
// in the same file.
|
||||
QByteArray Preferences::getAddNewTorrentDialogState() const
|
||||
{
|
||||
#ifdef QBT_USES_QT5
|
||||
return value("AddNewTorrentDialog/qt5/treeHeaderState").toByteArray();
|
||||
#else
|
||||
return value("AddNewTorrentDialog/treeHeaderState").toByteArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogState(const QByteArray &state)
|
||||
{
|
||||
#ifdef QBT_USES_QT5
|
||||
setValue("AddNewTorrentDialog/qt5/treeHeaderState", state);
|
||||
#else
|
||||
setValue("AddNewTorrentDialog/treeHeaderState", state);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Preferences::getAddNewTorrentDialogPos() const
|
||||
{
|
||||
return value("AddNewTorrentDialog/y", -1).toInt();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogPos(const int &pos)
|
||||
{
|
||||
setValue("AddNewTorrentDialog/y", pos);
|
||||
}
|
||||
|
||||
int Preferences::getAddNewTorrentDialogWidth() const
|
||||
{
|
||||
return value("AddNewTorrentDialog/width", -1).toInt();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogWidth(const int &width)
|
||||
{
|
||||
setValue("AddNewTorrentDialog/width", width);
|
||||
}
|
||||
|
||||
bool Preferences::getAddNewTorrentDialogExpanded() const
|
||||
{
|
||||
return value("AddNewTorrentDialog/expanded", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogExpanded(const bool expanded)
|
||||
{
|
||||
setValue("AddNewTorrentDialog/expanded", expanded);
|
||||
}
|
||||
|
||||
QStringList Preferences::getAddNewTorrentDialogPathHistory() const
|
||||
{
|
||||
return value("TorrentAdditionDlg/save_path_history").toStringList();
|
||||
}
|
||||
|
||||
void Preferences::setAddNewTorrentDialogPathHistory(const QStringList &history)
|
||||
{
|
||||
setValue("TorrentAdditionDlg/save_path_history", history);
|
||||
}
|
||||
|
||||
QDateTime Preferences::getDNSLastUpd() const
|
||||
{
|
||||
@@ -2419,14 +2133,14 @@ void Preferences::setStatusFilterState(const bool checked)
|
||||
setValue("TransferListFilters/statusFilterState", checked);
|
||||
}
|
||||
|
||||
bool Preferences::getLabelFilterState() const
|
||||
bool Preferences::getCategoryFilterState() const
|
||||
{
|
||||
return value("TransferListFilters/labelFilterState", true).toBool();
|
||||
return value("TransferListFilters/CategoryFilterState", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setLabelFilterState(const bool checked)
|
||||
void Preferences::setCategoryFilterState(const bool checked)
|
||||
{
|
||||
setValue("TransferListFilters/labelFilterState", checked);
|
||||
setValue("TransferListFilters/CategoryFilterState", checked);
|
||||
}
|
||||
|
||||
bool Preferences::getTrackerFilterState() const
|
||||
@@ -2467,22 +2181,6 @@ void Preferences::setTransHeaderState(const QByteArray &state)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Temp code.
|
||||
// See TorrentStatistics::loadStats() for details.
|
||||
QVariantHash Preferences::getStats() const
|
||||
{
|
||||
return value("Stats/AllStats").toHash();
|
||||
}
|
||||
|
||||
void Preferences::removeStats()
|
||||
{
|
||||
QWriteLocker locker(&lock);
|
||||
dirty = true;
|
||||
if (!timer.isActive())
|
||||
timer.start();
|
||||
m_data.remove("Stats/AllStats");
|
||||
}
|
||||
|
||||
//From old RssSettings class
|
||||
bool Preferences::isRSSEnabled() const
|
||||
{
|
||||
@@ -2496,7 +2194,7 @@ void Preferences::setRSSEnabled(const bool enabled)
|
||||
|
||||
uint Preferences::getRSSRefreshInterval() const
|
||||
{
|
||||
return value("Preferences/RSS/RSSRefresh", 5).toUInt();
|
||||
return value("Preferences/RSS/RSSRefresh", 30).toUInt();
|
||||
}
|
||||
|
||||
void Preferences::setRSSRefreshInterval(const uint &interval)
|
||||
@@ -2554,31 +2252,6 @@ void Preferences::setToolbarTextPosition(const int position)
|
||||
setValue("Toolbar/textPosition", position);
|
||||
}
|
||||
|
||||
void Preferences::moveRSSCookies()
|
||||
{
|
||||
QList<QNetworkCookie> cookies = getNetworkCookies();
|
||||
QVariantMap hostsTable = value("Rss/hosts_cookies").toMap();
|
||||
foreach (const QString &key, hostsTable.keys()) {
|
||||
QVariant value = hostsTable[key];
|
||||
QList<QByteArray> rawCookies = value.toByteArray().split(':');
|
||||
foreach (const QByteArray &rawCookie, rawCookies) {
|
||||
foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) {
|
||||
cookie.setDomain(key);
|
||||
cookie.setPath("/");
|
||||
cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10));
|
||||
cookies << cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNetworkCookies(cookies);
|
||||
|
||||
QWriteLocker locker(&lock);
|
||||
dirty = true;
|
||||
timer.start();
|
||||
m_data.remove("Rss/hosts_cookies");
|
||||
}
|
||||
|
||||
QList<QNetworkCookie> Preferences::getNetworkCookies() const
|
||||
{
|
||||
QList<QNetworkCookie> cookies;
|
||||
@@ -2619,8 +2292,43 @@ void Preferences::setSpeedWidgetGraphEnable(int id, const bool enable)
|
||||
setValue("SpeedWidget/graph_enable_" + QString::number(id), enable);
|
||||
}
|
||||
|
||||
void Preferences::upgrade()
|
||||
{
|
||||
// Move RSS cookies to global storage
|
||||
QList<QNetworkCookie> cookies = getNetworkCookies();
|
||||
QVariantMap hostsTable = value("Rss/hosts_cookies").toMap();
|
||||
foreach (const QString &key, hostsTable.keys()) {
|
||||
QVariant value = hostsTable[key];
|
||||
QList<QByteArray> rawCookies = value.toByteArray().split(':');
|
||||
foreach (const QByteArray &rawCookie, rawCookies) {
|
||||
foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) {
|
||||
cookie.setDomain(key);
|
||||
cookie.setPath("/");
|
||||
cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10));
|
||||
cookies << cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNetworkCookies(cookies);
|
||||
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
if (!labels.isEmpty()) {
|
||||
QVariantMap categories = value("BitTorrent/Session/Categories").toMap();
|
||||
foreach (const QString &label, labels) {
|
||||
if (!categories.contains(label))
|
||||
categories[label] = "";
|
||||
}
|
||||
setValue("BitTorrent/Session/Categories", categories);
|
||||
SettingsStorage::instance()->removeValue("TransferListFilters/customLabels");
|
||||
}
|
||||
|
||||
SettingsStorage::instance()->removeValue("Rss/hosts_cookies");
|
||||
SettingsStorage::instance()->removeValue("Preferences/Downloads/AppendLabel");
|
||||
}
|
||||
|
||||
void Preferences::apply()
|
||||
{
|
||||
if (save())
|
||||
if (SettingsStorage::instance()->save())
|
||||
emit changed();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include <QNetworkCookie>
|
||||
#include <QVariant>
|
||||
|
||||
#include "base/types.h"
|
||||
#include "types.h"
|
||||
|
||||
enum scheduler_days
|
||||
{
|
||||
@@ -89,30 +89,23 @@ namespace DNS
|
||||
};
|
||||
}
|
||||
|
||||
class SettingsStorage;
|
||||
|
||||
class Preferences: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Preferences)
|
||||
|
||||
private:
|
||||
Preferences();
|
||||
~Preferences();
|
||||
|
||||
static Preferences* m_instance;
|
||||
QHash<QString, QVariant> m_data;
|
||||
int m_randomPort;
|
||||
bool dirty;
|
||||
QTimer timer;
|
||||
mutable QReadWriteLock lock;
|
||||
const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
void setValue(const QString &key, const QVariant &value);
|
||||
|
||||
private slots:
|
||||
bool save();
|
||||
static Preferences* m_instance;
|
||||
int m_randomPort;
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
void externalLabelAdded(QString&);
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
@@ -122,8 +115,6 @@ public:
|
||||
// General options
|
||||
QString getLocale() const;
|
||||
void setLocale(const QString &locale);
|
||||
bool useProgramNotification() const;
|
||||
void useProgramNotification(bool use);
|
||||
bool deleteTorrentFilesAsDefault() const;
|
||||
void setDeleteTorrentFilesAsDefault(bool del);
|
||||
bool confirmOnExit() const;
|
||||
@@ -132,6 +123,10 @@ public:
|
||||
void showSpeedInTitleBar(bool show);
|
||||
bool useAlternatingRowColors() const;
|
||||
void setAlternatingRowColors(bool b);
|
||||
bool getHideZeroValues() const;
|
||||
void setHideZeroValues(bool b);
|
||||
int getHideZeroComboValues() const;
|
||||
void setHideZeroComboValues(int n);
|
||||
bool useRandomPort() const;
|
||||
void setRandomPort(bool b);
|
||||
bool systrayIntegration() const;
|
||||
@@ -154,28 +149,12 @@ public:
|
||||
#endif
|
||||
|
||||
// Downloads
|
||||
QString getSavePath() const;
|
||||
void setSavePath(const QString &save_path);
|
||||
bool isTempPathEnabled() const;
|
||||
void setTempPathEnabled(bool enabled);
|
||||
QString getTempPath() const;
|
||||
void setTempPath(const QString &path);
|
||||
QString getDefaultLabel() const;
|
||||
void setDefaultLabel(const QString &defaultLabel);
|
||||
bool useIncompleteFilesExtension() const;
|
||||
void useIncompleteFilesExtension(bool enabled);
|
||||
bool appendTorrentLabel() const;
|
||||
void setAppendTorrentLabel(bool b);
|
||||
QString lastLocationPath() const;
|
||||
void setLastLocationPath(const QString &path);
|
||||
bool preAllocateAllFiles() const;
|
||||
void preAllocateAllFiles(bool enabled);
|
||||
bool useAdditionDialog() const;
|
||||
void useAdditionDialog(bool b);
|
||||
bool additionDialogFront() const;
|
||||
void additionDialogFront(bool b);
|
||||
bool addTorrentsInPause() const;
|
||||
void addTorrentsInPause(bool b);
|
||||
QVariantHash getScanDirs() const;
|
||||
void setScanDirs(const QVariantHash &dirs);
|
||||
QString getScanDirsLastPath() const;
|
||||
@@ -277,8 +256,6 @@ public:
|
||||
void setTrackersList(const QString &val);
|
||||
qreal getGlobalMaxRatio() const;
|
||||
void setGlobalMaxRatio(qreal ratio);
|
||||
MaxRatioAction getMaxRatioAction() const;
|
||||
void setMaxRatioAction(MaxRatioAction act);
|
||||
|
||||
// IP Filter
|
||||
bool isFilteringEnabled() const;
|
||||
@@ -294,10 +271,6 @@ public:
|
||||
bool isSearchEnabled() const;
|
||||
void setSearchEnabled(bool enabled);
|
||||
|
||||
// Execution Log
|
||||
bool isExecutionLogEnabled() const;
|
||||
void setExecutionLogEnabled(bool b);
|
||||
|
||||
// Queueing system
|
||||
bool isQueueingSystemEnabled() const;
|
||||
void setQueueingSystemEnabled(bool enabled);
|
||||
@@ -356,6 +329,8 @@ public:
|
||||
void setHibernateWhenDownloadsComplete(bool hibernate);
|
||||
bool shutdownqBTWhenDownloadsComplete() const;
|
||||
void setShutdownqBTWhenDownloadsComplete(bool shutdown);
|
||||
bool dontConfirmAutoExit() const;
|
||||
void setDontConfirmAutoExit(bool dontConfirmAutoExit);
|
||||
uint diskCacheSize() const;
|
||||
void setDiskCacheSize(uint size);
|
||||
uint diskCacheTTL() const;
|
||||
@@ -388,6 +363,8 @@ public:
|
||||
void setNetworkInterface(const QString& iface);
|
||||
QString getNetworkInterfaceName() const;
|
||||
void setNetworkInterfaceName(const QString& iface);
|
||||
QString getNetworkInterfaceAddress() const;
|
||||
void setNetworkInterfaceAddress(const QString& addr);
|
||||
bool getListenIPv6() const;
|
||||
void setListenIPv6(bool enable);
|
||||
QString getNetworkAddress() const;
|
||||
@@ -402,11 +379,6 @@ public:
|
||||
bool useSystemIconTheme() const;
|
||||
void useSystemIconTheme(bool enabled);
|
||||
#endif
|
||||
QStringList getTorrentLabels() const;
|
||||
void setTorrentLabels(const QStringList& labels);
|
||||
void addTorrentLabelExternal(const QString &label);
|
||||
void addTorrentLabel(const QString& label);
|
||||
void removeTorrentLabel(const QString& label);
|
||||
bool recursiveDownloadDisabled() const;
|
||||
void disableRecursiveDownload(bool disable = true);
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -439,19 +411,8 @@ public:
|
||||
TrayIcon::Style trayIconStyle() const;
|
||||
void setTrayIconStyle(TrayIcon::Style style);
|
||||
|
||||
|
||||
// Stuff that don't appear in the Options GUI but are saved
|
||||
// in the same file.
|
||||
QByteArray getAddNewTorrentDialogState() const;
|
||||
void setAddNewTorrentDialogState(const QByteArray &state);
|
||||
int getAddNewTorrentDialogPos() const;
|
||||
void setAddNewTorrentDialogPos(const int &pos);
|
||||
int getAddNewTorrentDialogWidth() const;
|
||||
void setAddNewTorrentDialogWidth(const int &width);
|
||||
bool getAddNewTorrentDialogExpanded() const;
|
||||
void setAddNewTorrentDialogExpanded(const bool expanded);
|
||||
QStringList getAddNewTorrentDialogPathHistory() const;
|
||||
void setAddNewTorrentDialogPathHistory(const QStringList &history);
|
||||
QDateTime getDNSLastUpd() const;
|
||||
void setDNSLastUpd(const QDateTime &date);
|
||||
QString getDNSLastIP() const;
|
||||
@@ -513,7 +474,7 @@ public:
|
||||
QByteArray getTorImportGeometry() const;
|
||||
void setTorImportGeometry(const QByteArray &geometry);
|
||||
bool getStatusFilterState() const;
|
||||
bool getLabelFilterState() const;
|
||||
bool getCategoryFilterState() const;
|
||||
bool getTrackerFilterState() const;
|
||||
int getTransSelFilter() const;
|
||||
void setTransSelFilter(const int &index);
|
||||
@@ -522,11 +483,6 @@ public:
|
||||
int getToolbarTextPosition() const;
|
||||
void setToolbarTextPosition(const int position);
|
||||
|
||||
// Temp code.
|
||||
// See TorrentStatistics::loadStats() for details.
|
||||
QVariantHash getStats() const;
|
||||
void removeStats();
|
||||
|
||||
//From old RssSettings class
|
||||
bool isRSSEnabled() const;
|
||||
void setRSSEnabled(const bool enabled);
|
||||
@@ -544,8 +500,6 @@ public:
|
||||
// Network
|
||||
QList<QNetworkCookie> getNetworkCookies() const;
|
||||
void setNetworkCookies(const QList<QNetworkCookie> &cookies);
|
||||
// Temporary method for upgrade purposes
|
||||
void moveRSSCookies();
|
||||
|
||||
// SpeedWidget
|
||||
int getSpeedWidgetPeriod() const;
|
||||
@@ -553,9 +507,11 @@ public:
|
||||
bool getSpeedWidgetGraphEnable(int id) const;
|
||||
void setSpeedWidgetGraphEnable(int id, const bool enable);
|
||||
|
||||
void upgrade();
|
||||
|
||||
public slots:
|
||||
void setStatusFilterState(bool checked);
|
||||
void setLabelFilterState(bool checked);
|
||||
void setCategoryFilterState(bool checked);
|
||||
void setTrackerFilterState(bool checked);
|
||||
|
||||
void apply();
|
||||
|
||||
@@ -178,7 +178,7 @@ DownloadRulePtr DownloadRule::fromVariantHash(const QVariantHash &ruleHash)
|
||||
rule->setRssFeeds(ruleHash.value("affected_feeds").toStringList());
|
||||
rule->setEnabled(ruleHash.value("enabled", false).toBool());
|
||||
rule->setSavePath(ruleHash.value("save_path").toString());
|
||||
rule->setLabel(ruleHash.value("label_assigned").toString());
|
||||
rule->setCategory(ruleHash.value("category_assigned").toString());
|
||||
rule->setAddPaused(AddPausedState(ruleHash.value("add_paused").toUInt()));
|
||||
rule->setLastMatch(ruleHash.value("last_match").toDateTime());
|
||||
rule->setIgnoreDays(ruleHash.value("ignore_days").toInt());
|
||||
@@ -194,7 +194,7 @@ QVariantHash DownloadRule::toVariantHash() const
|
||||
hash["save_path"] = m_savePath;
|
||||
hash["affected_feeds"] = m_rssFeeds;
|
||||
hash["enabled"] = m_enabled;
|
||||
hash["label_assigned"] = m_label;
|
||||
hash["category_assigned"] = m_category;
|
||||
hash["use_regex"] = m_useRegex;
|
||||
hash["add_paused"] = m_apstate;
|
||||
hash["episode_filter"] = m_episodeFilter;
|
||||
@@ -210,10 +210,7 @@ bool DownloadRule::operator==(const DownloadRule &other) const
|
||||
|
||||
void DownloadRule::setSavePath(const QString &savePath)
|
||||
{
|
||||
if (!savePath.isEmpty() && (QDir(savePath) != QDir(Preferences::instance()->getSavePath())))
|
||||
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||
else
|
||||
m_savePath = QString();
|
||||
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||
}
|
||||
|
||||
DownloadRule::AddPausedState DownloadRule::addPaused() const
|
||||
@@ -226,14 +223,14 @@ void DownloadRule::setAddPaused(const DownloadRule::AddPausedState &aps)
|
||||
m_apstate = aps;
|
||||
}
|
||||
|
||||
QString DownloadRule::label() const
|
||||
QString DownloadRule::category() const
|
||||
{
|
||||
return m_label;
|
||||
return m_category;
|
||||
}
|
||||
|
||||
void DownloadRule::setLabel(const QString &label)
|
||||
void DownloadRule::setCategory(const QString &category)
|
||||
{
|
||||
m_label = label;
|
||||
m_category = category;
|
||||
}
|
||||
|
||||
bool DownloadRule::isEnabled() const
|
||||
|
||||
@@ -69,8 +69,8 @@ namespace Rss
|
||||
void setSavePath(const QString &savePath);
|
||||
AddPausedState addPaused() const;
|
||||
void setAddPaused(const AddPausedState &aps);
|
||||
QString label() const;
|
||||
void setLabel(const QString &label);
|
||||
QString category() const;
|
||||
void setCategory(const QString &category);
|
||||
bool isEnabled() const;
|
||||
void setEnabled(bool enable);
|
||||
void setLastMatch(const QDateTime &d);
|
||||
@@ -93,7 +93,7 @@ namespace Rss
|
||||
QStringList m_mustNotContain;
|
||||
QString m_episodeFilter;
|
||||
QString m_savePath;
|
||||
QString m_label;
|
||||
QString m_category;
|
||||
bool m_enabled;
|
||||
QStringList m_rssFeeds;
|
||||
bool m_useRegex;
|
||||
|
||||
@@ -391,7 +391,7 @@ void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article)
|
||||
|
||||
BitTorrent::AddTorrentParams params;
|
||||
params.savePath = matchingRule->savePath();
|
||||
params.label = matchingRule->label();
|
||||
params.category = matchingRule->category();
|
||||
if (matchingRule->addPaused() == DownloadRule::ALWAYS_PAUSED)
|
||||
params.addPaused = TriStateBool::True;
|
||||
else if (matchingRule->addPaused() == DownloadRule::NEVER_PAUSED)
|
||||
|
||||
@@ -28,20 +28,17 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "scanfoldersmodel.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "utils/misc.h"
|
||||
#include "utils/fs.h"
|
||||
#include "preferences.h"
|
||||
#include "logger.h"
|
||||
#include "filesystemwatcher.h"
|
||||
#include "bittorrent/session.h"
|
||||
#include "scanfoldersmodel.h"
|
||||
#include "filesystemwatcher.h"
|
||||
#include "preferences.h"
|
||||
#include "utils/fs.h"
|
||||
|
||||
struct ScanFoldersModel::PathData
|
||||
{
|
||||
@@ -128,10 +125,8 @@ QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
|
||||
else if (role == Qt::DisplayRole) {
|
||||
switch (pathData->downloadType) {
|
||||
case DOWNLOAD_IN_WATCH_FOLDER:
|
||||
value = tr("Watch Folder");
|
||||
break;
|
||||
case DEFAULT_LOCATION:
|
||||
value = tr("Default Folder");
|
||||
value = pathTypeDisplayName(pathData->downloadType);
|
||||
break;
|
||||
case CUSTOM_LOCATION:
|
||||
value = pathData->downloadPath;
|
||||
@@ -153,10 +148,10 @@ QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation,
|
||||
|
||||
switch (section) {
|
||||
case WATCH:
|
||||
title = tr("Watched Folder");
|
||||
title = tr("Monitored Folder");
|
||||
break;
|
||||
case DOWNLOAD:
|
||||
title = tr("Save Files to");
|
||||
title = tr("Override Save Location");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -392,3 +387,18 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ScanFoldersModel::pathTypeDisplayName(const PathType type)
|
||||
{
|
||||
switch(type) {
|
||||
case DOWNLOAD_IN_WATCH_FOLDER:
|
||||
return tr("Monitored folder");
|
||||
case DEFAULT_LOCATION:
|
||||
return tr("Default save location");
|
||||
case CUSTOM_LOCATION:
|
||||
return tr("Browse...");
|
||||
default:
|
||||
qDebug("Invalid PathType: %d", type);
|
||||
};
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -34,13 +34,10 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStringList;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class FileSystemWatcher;
|
||||
|
||||
class ScanFoldersModel : public QAbstractListModel
|
||||
class ScanFoldersModel: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ScanFoldersModel)
|
||||
@@ -71,7 +68,9 @@ public:
|
||||
|
||||
static bool initInstance(QObject *parent = 0);
|
||||
static void freeInstance();
|
||||
static ScanFoldersModel *instance();
|
||||
static ScanFoldersModel* instance();
|
||||
|
||||
static QString pathTypeDisplayName(const PathType type);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
@@ -81,8 +80,8 @@ public:
|
||||
|
||||
// TODO: removePaths(); singular version becomes private helper functions;
|
||||
// also: remove functions should take modelindexes
|
||||
PathStatus addPath(const QString &watchPath, const PathType& downloadType, const QString &downloadPath, bool addToFSWatcher = true);
|
||||
PathStatus updatePath(const QString &watchPath, const PathType& downloadType, const QString &downloadPath);
|
||||
PathStatus addPath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath, bool addToFSWatcher = true);
|
||||
PathStatus updatePath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath);
|
||||
// PRECONDITION: The paths must have been added with addPath() first.
|
||||
void addToFSWatcher(const QStringList &watchPaths);
|
||||
void removePath(int row, bool removeFromFSWatcher = true);
|
||||
|
||||
@@ -266,6 +266,21 @@ void SearchEngine::cancelSearch()
|
||||
}
|
||||
}
|
||||
|
||||
void SearchEngine::downloadTorrent(const QString &siteUrl, const QString &url)
|
||||
{
|
||||
QProcess *downloadProcess = new QProcess(this);
|
||||
downloadProcess->setEnvironment(QProcess::systemEnvironment());
|
||||
connect(downloadProcess, SIGNAL(finished(int)), this, SLOT(torrentFileDownloadFinished(int)));
|
||||
m_downloaders << downloadProcess;
|
||||
QStringList params {
|
||||
Utils::Fs::toNativePath(engineLocation() + "/nova2dl.py"),
|
||||
siteUrl,
|
||||
url
|
||||
};
|
||||
// Launch search
|
||||
downloadProcess->start(Utils::Misc::pythonExecutable(), params, QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
void SearchEngine::startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins)
|
||||
{
|
||||
// Search process already running or
|
||||
@@ -295,6 +310,11 @@ QString SearchEngine::categoryFullName(const QString &categoryName)
|
||||
return tr(m_categoryNames.value(categoryName).toUtf8().constData());
|
||||
}
|
||||
|
||||
QString SearchEngine::pluginFullName(const QString &pluginName)
|
||||
{
|
||||
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
|
||||
}
|
||||
|
||||
QString SearchEngine::pluginsLocation()
|
||||
{
|
||||
return QString("%1/engines").arg(engineLocation());
|
||||
@@ -357,6 +377,21 @@ void SearchEngine::pluginDownloadFailed(const QString &url, const QString &reaso
|
||||
emit pluginInstallationFailed(pluginName, tr("Failed to download the plugin file. %1").arg(reason));
|
||||
}
|
||||
|
||||
void SearchEngine::torrentFileDownloadFinished(int exitcode)
|
||||
{
|
||||
QProcess *downloadProcess = static_cast<QProcess*>(sender());
|
||||
if (exitcode == 0) {
|
||||
QString line = QString::fromUtf8(downloadProcess->readAllStandardOutput()).trimmed();
|
||||
QStringList parts = line.split(' ');
|
||||
if (parts.size() == 2)
|
||||
emit torrentFileDownloaded(parts[0]);
|
||||
}
|
||||
|
||||
qDebug() << "Deleting downloadProcess";
|
||||
m_downloaders.removeOne(downloadProcess);
|
||||
downloadProcess->deleteLater();
|
||||
}
|
||||
|
||||
// Update nova.py search plugin if necessary
|
||||
void SearchEngine::updateNova()
|
||||
{
|
||||
@@ -383,6 +418,12 @@ void SearchEngine::updateNova()
|
||||
QFile::copy(":/" + novaFolder + "/nova2.py", filePath);
|
||||
}
|
||||
|
||||
filePath = searchDir.absoluteFilePath("nova2dl.py");
|
||||
if (getPluginVersion(":/" + novaFolder + "/nova2dl.py") > getPluginVersion(filePath)) {
|
||||
removePythonScriptIfExists(filePath);
|
||||
QFile::copy(":/" + novaFolder + "/nova2dl.py", filePath);
|
||||
}
|
||||
|
||||
filePath = searchDir.absoluteFilePath("fix_encoding.py");
|
||||
QFile::copy(":/" + novaFolder + "/fix_encoding.py", filePath);
|
||||
|
||||
|
||||
@@ -84,8 +84,11 @@ public:
|
||||
void startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
|
||||
void cancelSearch();
|
||||
|
||||
void downloadTorrent(const QString &siteUrl, const QString &url);
|
||||
|
||||
static qreal getPluginVersion(QString filePath);
|
||||
static QString categoryFullName(const QString &categoryName);
|
||||
QString pluginFullName(const QString &pluginName);
|
||||
static QString pluginsLocation();
|
||||
|
||||
signals:
|
||||
@@ -102,6 +105,8 @@ signals:
|
||||
void checkForUpdatesFinished(const QHash<QString, qreal> &updateInfo);
|
||||
void checkForUpdatesFailed(const QString &reason);
|
||||
|
||||
void torrentFileDownloaded(const QString &path);
|
||||
|
||||
private slots:
|
||||
void onTimeout();
|
||||
void readSearchOutput();
|
||||
@@ -110,6 +115,7 @@ private slots:
|
||||
void versionInfoDownloadFailed(const QString &url, const QString &reason);
|
||||
void pluginDownloaded(const QString &url, QString filePath);
|
||||
void pluginDownloadFailed(const QString &url, const QString &reason);
|
||||
void torrentFileDownloadFinished(int exitcode);
|
||||
|
||||
private:
|
||||
void update();
|
||||
@@ -132,6 +138,7 @@ private:
|
||||
bool m_searchStopped;
|
||||
QTimer *m_searchTimeout;
|
||||
QByteArray m_searchResultLineTruncated;
|
||||
QList<QProcess*> m_downloaders;
|
||||
};
|
||||
|
||||
#endif // SEARCHENGINE_H
|
||||
|
||||
286
src/base/settingsstorage.cpp
Normal file
286
src/base/settingsstorage.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2014 sledgehammer999 <hammered999@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "settingsstorage.h"
|
||||
|
||||
#include <memory>
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <QStringList>
|
||||
#include <QSettings>
|
||||
|
||||
#include "logger.h"
|
||||
#include "utils/fs.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#define QSETTINGS_SYNC_IS_SAVE // whether QSettings::sync() is "atomic"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
// Encapsulates serialization of settings in "atomic" way.
|
||||
// write() does not leave half-written files,
|
||||
// read() has a workaround for a case of power loss during a previous serialization
|
||||
class TransactionalSettings
|
||||
{
|
||||
public:
|
||||
TransactionalSettings(const QString &name)
|
||||
: m_name(name)
|
||||
{
|
||||
}
|
||||
|
||||
QVariantHash read();
|
||||
bool write(const QVariantHash &data);
|
||||
|
||||
private:
|
||||
// we return actual file names used by QSettings because
|
||||
// there is no other way to get that name except
|
||||
// actually create a QSettings object.
|
||||
// if serialization operation was not successful we return empty string
|
||||
QString deserialize(const QString &name, QVariantHash &data);
|
||||
QString serialize(const QString &name, const QVariantHash &data);
|
||||
|
||||
using SettingsPtr = std::unique_ptr<QSettings>;
|
||||
SettingsPtr createSettings(const QString &name)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return SettingsPtr(new QSettings(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", name));
|
||||
#else
|
||||
return SettingsPtr(new QSettings("qBittorrent", name));
|
||||
#endif
|
||||
}
|
||||
|
||||
QString m_name;
|
||||
};
|
||||
|
||||
#ifdef QBT_USES_QT5
|
||||
typedef QHash<QString, QString> MappingTable;
|
||||
#else
|
||||
class MappingTable: public QHash<QString, QString>
|
||||
{
|
||||
public:
|
||||
MappingTable(std::initializer_list<std::pair<QString, QString>> list)
|
||||
{
|
||||
reserve(static_cast<int>(list.size()));
|
||||
for (const auto &i : list)
|
||||
insert(i.first, i.second);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
QString mapKey(const QString &key)
|
||||
{
|
||||
static const MappingTable keyMapping = {
|
||||
|
||||
{ "BitTorrent/Session/MaxRatioAction", "Preferences/Bittorrent/MaxRatioAction" },
|
||||
{ "BitTorrent/Session/DefaultSavePath", "Preferences/Downloads/SavePath" },
|
||||
{ "BitTorrent/Session/TempPath", "Preferences/Downloads/TempPath" },
|
||||
{ "BitTorrent/Session/TempPathEnabled", "Preferences/Downloads/TempPathEnabled" },
|
||||
{ "BitTorrent/Session/AddTorrentPaused", "Preferences/Downloads/StartInPause" },
|
||||
#ifdef QBT_USES_QT5
|
||||
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/qt5/treeHeaderState" },
|
||||
#else
|
||||
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/treeHeaderState" },
|
||||
#endif
|
||||
{ "AddNewTorrentDialog/Width", "AddNewTorrentDialog/width" },
|
||||
{ "AddNewTorrentDialog/Position", "AddNewTorrentDialog/y" },
|
||||
{ "AddNewTorrentDialog/Expanded", "AddNewTorrentDialog/expanded" },
|
||||
{ "AddNewTorrentDialog/SavePathHistory", "TorrentAdditionDlg/save_path_history" },
|
||||
{ "AddNewTorrentDialog/Enabled", "Preferences/Downloads/NewAdditionDialog" },
|
||||
{ "AddNewTorrentDialog/TopLevel", "Preferences/Downloads/NewAdditionDialogFront" }
|
||||
|
||||
};
|
||||
|
||||
return keyMapping.value(key, key);
|
||||
}
|
||||
}
|
||||
|
||||
SettingsStorage *SettingsStorage::m_instance = nullptr;
|
||||
|
||||
SettingsStorage::SettingsStorage()
|
||||
: m_data{TransactionalSettings(QLatin1String("qBittorrent")).read()}
|
||||
, m_dirty(false)
|
||||
, m_lock(QReadWriteLock::Recursive)
|
||||
{
|
||||
m_timer.setSingleShot(true);
|
||||
m_timer.setInterval(5 * 1000);
|
||||
connect(&m_timer, SIGNAL(timeout()), SLOT(save()));
|
||||
}
|
||||
|
||||
SettingsStorage::~SettingsStorage()
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
void SettingsStorage::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new SettingsStorage;
|
||||
}
|
||||
|
||||
void SettingsStorage::freeInstance()
|
||||
{
|
||||
delete m_instance;
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
SettingsStorage *SettingsStorage::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
bool SettingsStorage::save()
|
||||
{
|
||||
if (!m_dirty) return false; // Obtaining the lock is expensive, let's check early
|
||||
QWriteLocker locker(&m_lock);
|
||||
if (!m_dirty) return false; // something might have changed while we were getting the lock
|
||||
|
||||
TransactionalSettings settings(QLatin1String("qBittorrent"));
|
||||
if (settings.write(m_data)) {
|
||||
m_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant SettingsStorage::loadValue(const QString &key, const QVariant &defaultValue) const
|
||||
{
|
||||
QReadLocker locker(&m_lock);
|
||||
return m_data.value(mapKey(key), defaultValue);
|
||||
}
|
||||
|
||||
void SettingsStorage::storeValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
QString realKey = mapKey(key);
|
||||
QWriteLocker locker(&m_lock);
|
||||
if (m_data.value(realKey) != value) {
|
||||
m_dirty = true;
|
||||
m_data.insert(realKey, value);
|
||||
m_timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsStorage::removeValue(const QString &key)
|
||||
{
|
||||
QString realKey = mapKey(key);
|
||||
QWriteLocker locker(&m_lock);
|
||||
if (m_data.contains(realKey)) {
|
||||
m_dirty = true;
|
||||
m_data.remove(realKey);
|
||||
m_timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
QVariantHash TransactionalSettings::read()
|
||||
{
|
||||
QVariantHash res;
|
||||
#ifdef QSETTINGS_SYNC_IS_SAVE
|
||||
deserialize(m_name, res);
|
||||
#else
|
||||
bool writeBackNeeded = false;
|
||||
QString newPath = deserialize(m_name + QLatin1String("_new"), res);
|
||||
if (!newPath.isEmpty()) { // "_new" file is NOT empty
|
||||
// This means that the PC closed either due to power outage
|
||||
// or because the disk was full. In any case the settings weren't transfered
|
||||
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
|
||||
// contains the most recent settings.
|
||||
Logger::instance()->addMessage(QObject::tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
|
||||
writeBackNeeded = true;
|
||||
}
|
||||
else {
|
||||
deserialize(m_name, res);
|
||||
}
|
||||
|
||||
Utils::Fs::forceRemove(newPath);
|
||||
|
||||
if (writeBackNeeded)
|
||||
write(res);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
bool TransactionalSettings::write(const QVariantHash &data)
|
||||
{
|
||||
#ifdef QSETTINGS_SYNC_IS_SAVE
|
||||
serialize(m_name, data);
|
||||
#else
|
||||
// QSettings delete the file before writing it out. This can result in problems
|
||||
// if the disk is full or a power outage occurs. Those events might occur
|
||||
// between deleting the file and recreating it. This is a safety measure.
|
||||
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
|
||||
// replace qBittorrent.ini/qBittorrent.conf with it.
|
||||
QString newPath = serialize(m_name + QLatin1String("_new"), data);
|
||||
if (newPath.isEmpty()) {
|
||||
Utils::Fs::forceRemove(newPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString finalPath = newPath;
|
||||
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
finalPath.remove(index, 4);
|
||||
Utils::Fs::forceRemove(finalPath);
|
||||
QFile::rename(newPath, finalPath);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
QString TransactionalSettings::deserialize(const QString &name, QVariantHash &data)
|
||||
{
|
||||
SettingsPtr settings = createSettings(name);
|
||||
|
||||
if (settings->allKeys().isEmpty())
|
||||
return QString();
|
||||
|
||||
// Copy everything into memory. This means even keys inserted in the file manually
|
||||
// or that we don't touch directly in this code (eg disabled by ifdef). This ensures
|
||||
// that they will be copied over when save our settings to disk.
|
||||
foreach (const QString &key, settings->allKeys())
|
||||
data.insert(key, settings->value(key));
|
||||
|
||||
return settings->fileName();
|
||||
}
|
||||
|
||||
QString TransactionalSettings::serialize(const QString &name, const QVariantHash &data)
|
||||
{
|
||||
SettingsPtr settings = createSettings(name);
|
||||
for (auto i = data.begin(); i != data.end(); ++i)
|
||||
settings->setValue(i.key(), i.value());
|
||||
|
||||
settings->sync(); // Important to get error status
|
||||
QSettings::Status status = settings->status();
|
||||
if (status != QSettings::NoError) {
|
||||
if (status == QSettings::AccessError)
|
||||
Logger::instance()->addMessage(QObject::tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
else
|
||||
Logger::instance()->addMessage(QObject::tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
return QString();
|
||||
}
|
||||
return settings->fileName();
|
||||
}
|
||||
65
src/base/settingsstorage.h
Normal file
65
src/base/settingsstorage.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2014 sledgehammer999 <hammered999@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef SETTINGSSTORAGE_H
|
||||
#define SETTINGSSTORAGE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantHash>
|
||||
#include <QTimer>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
class SettingsStorage: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
SettingsStorage();
|
||||
~SettingsStorage();
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static SettingsStorage* instance();
|
||||
|
||||
QVariant loadValue(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
void storeValue(const QString &key, const QVariant &value);
|
||||
void removeValue(const QString &key);
|
||||
|
||||
public slots:
|
||||
bool save();
|
||||
|
||||
private:
|
||||
static SettingsStorage *m_instance;
|
||||
|
||||
QVariantHash m_data;
|
||||
bool m_dirty;
|
||||
QTimer m_timer;
|
||||
mutable QReadWriteLock m_lock;
|
||||
};
|
||||
|
||||
#endif // SETTINGSSTORAGE_H
|
||||
100
src/base/torrentfileguard.cpp
Normal file
100
src/base/torrentfileguard.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "torrentfileguard.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
#include "settingsstorage.h"
|
||||
#include "utils/fs.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const QLatin1String KEY_AUTO_DELETE_ENABLED ("Core/AutoDeleteAddedTorrentFile");
|
||||
}
|
||||
|
||||
FileGuard::FileGuard(const QString &path)
|
||||
: m_path {path}
|
||||
, m_remove {true}
|
||||
{
|
||||
}
|
||||
|
||||
void FileGuard::setAutoRemove(bool remove) noexcept
|
||||
{
|
||||
m_remove = remove;
|
||||
}
|
||||
|
||||
FileGuard::~FileGuard()
|
||||
{
|
||||
if (m_remove && !m_path.isEmpty())
|
||||
Utils::Fs::forceRemove(m_path); // forceRemove() checks for file existence
|
||||
}
|
||||
|
||||
TorrentFileGuard::TorrentFileGuard(const QString &path)
|
||||
: m_mode {autoDeleteMode()}
|
||||
, m_wasAdded {false}
|
||||
, m_guard {m_mode != Never ? path : QString()}
|
||||
{
|
||||
}
|
||||
|
||||
TorrentFileGuard::~TorrentFileGuard()
|
||||
{
|
||||
if (!m_wasAdded && (m_mode != Always))
|
||||
m_guard.setAutoRemove(false);
|
||||
}
|
||||
|
||||
void TorrentFileGuard::markAsAddedToSession()
|
||||
{
|
||||
m_wasAdded = true;
|
||||
}
|
||||
|
||||
void TorrentFileGuard::setAutoRemove(bool remove)
|
||||
{
|
||||
m_guard.setAutoRemove(remove);
|
||||
}
|
||||
|
||||
TorrentFileGuard::AutoDeleteMode TorrentFileGuard::autoDeleteMode()
|
||||
{
|
||||
QMetaEnum meta {modeMetaEnum()};
|
||||
return static_cast<AutoDeleteMode>(meta.keyToValue(SettingsStorage::instance()->loadValue(
|
||||
KEY_AUTO_DELETE_ENABLED, meta.valueToKey(Never)).toByteArray()));
|
||||
}
|
||||
|
||||
void TorrentFileGuard::setAutoDeleteMode(TorrentFileGuard::AutoDeleteMode mode)
|
||||
{
|
||||
QMetaEnum meta {modeMetaEnum()};
|
||||
SettingsStorage::instance()->storeValue(KEY_AUTO_DELETE_ENABLED, meta.valueToKey(mode));
|
||||
}
|
||||
|
||||
QMetaEnum TorrentFileGuard::modeMetaEnum()
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
||||
return QMetaEnum::fromType<AutoDeleteMode>();
|
||||
#else
|
||||
return staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AutoDeleteMode"));
|
||||
#endif
|
||||
}
|
||||
95
src/base/torrentfileguard.h
Normal file
95
src/base/torrentfileguard.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
#include <QMetaType>
|
||||
|
||||
class QMetaEnum;
|
||||
/// Utility class to defer file deletion
|
||||
class FileGuard
|
||||
{
|
||||
public:
|
||||
FileGuard(const QString &path = QString());
|
||||
~FileGuard();
|
||||
|
||||
/// Cancels or re-enables deferred file deletion
|
||||
void setAutoRemove(bool remove) noexcept;
|
||||
|
||||
private:
|
||||
QString m_path;
|
||||
bool m_remove;
|
||||
};
|
||||
|
||||
/// Reads settings for .torrent files from preferences
|
||||
/// and sets the file guard up accordingly
|
||||
class TorrentFileGuard
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
TorrentFileGuard(const QString &path = QString());
|
||||
~TorrentFileGuard();
|
||||
|
||||
/// marks the torrent file as loaded (added) into the BitTorrent::Session
|
||||
void markAsAddedToSession();
|
||||
void setAutoRemove(bool remove);
|
||||
|
||||
enum AutoDeleteMode // do not change these names: they are stored in config file
|
||||
{
|
||||
Never,
|
||||
IfAdded,
|
||||
Always
|
||||
};
|
||||
|
||||
// static interface to get/set preferences
|
||||
static AutoDeleteMode autoDeleteMode();
|
||||
static void setAutoDeleteMode(AutoDeleteMode mode);
|
||||
|
||||
private:
|
||||
static QMetaEnum modeMetaEnum();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
||||
Q_ENUMS(AutoDeleteMode)
|
||||
#else
|
||||
Q_ENUM(AutoDeleteMode)
|
||||
#endif
|
||||
AutoDeleteMode m_mode;
|
||||
bool m_wasAdded;
|
||||
// Qt 4 moc has troubles with Q_GADGET: if Q_GADGET is present in a class, moc unconditionally
|
||||
// references in the generated code the statiMetaObject from the class ancestor.
|
||||
// Moreover, if the ancestor class has Q_GADGET but does not have other
|
||||
// Q_ declarations, moc does not generate staticMetaObject for it. These results
|
||||
// in referencing the non existent staticMetaObject and such code fails to compile.
|
||||
// This problem is NOT present in Qt 5.7.0 and maybe in some older Qt 5 versions too
|
||||
// Qt 4.8.7 has it.
|
||||
// Therefore, we can't inherit FileGuard :(
|
||||
FileGuard m_guard;
|
||||
};
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
||||
Q_DECLARE_METATYPE(TorrentFileGuard::AutoDeleteMode)
|
||||
#endif
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "bittorrent/torrenthandle.h"
|
||||
#include "torrentfilter.h"
|
||||
|
||||
const QString TorrentFilter::AnyLabel;
|
||||
const QString TorrentFilter::AnyCategory;
|
||||
const QStringSet TorrentFilter::AnyHash = (QStringSet() << QString());
|
||||
|
||||
const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading);
|
||||
@@ -49,16 +49,16 @@ TorrentFilter::TorrentFilter()
|
||||
{
|
||||
}
|
||||
|
||||
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString label)
|
||||
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString category)
|
||||
: m_type(type)
|
||||
, m_label(label)
|
||||
, m_category(category)
|
||||
, m_hashSet(hashSet)
|
||||
{
|
||||
}
|
||||
|
||||
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString label)
|
||||
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString category)
|
||||
: m_type(All)
|
||||
, m_label(label)
|
||||
, m_category(category)
|
||||
, m_hashSet(hashSet)
|
||||
{
|
||||
setTypeByName(filter);
|
||||
@@ -108,13 +108,13 @@ bool TorrentFilter::setHashSet(const QStringSet &hashSet)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TorrentFilter::setLabel(const QString &label)
|
||||
bool TorrentFilter::setCategory(const QString &category)
|
||||
{
|
||||
// QString::operator==() doesn't distinguish between empty and null strings.
|
||||
if ((m_label != label)
|
||||
|| (m_label.isNull() && !label.isNull())
|
||||
|| (!m_label.isNull() && label.isNull())) {
|
||||
m_label = label;
|
||||
if ((m_category != category)
|
||||
|| (m_category.isNull() && !category.isNull())
|
||||
|| (!m_category.isNull() && category.isNull())) {
|
||||
m_category = category;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ bool TorrentFilter::match(TorrentHandle *const torrent) const
|
||||
{
|
||||
if (!torrent) return false;
|
||||
|
||||
return (matchState(torrent) && matchHash(torrent) && matchLabel(torrent));
|
||||
return (matchState(torrent) && matchHash(torrent) && matchCategory(torrent));
|
||||
}
|
||||
|
||||
bool TorrentFilter::matchState(BitTorrent::TorrentHandle *const torrent) const
|
||||
@@ -160,9 +160,8 @@ bool TorrentFilter::matchHash(BitTorrent::TorrentHandle *const torrent) const
|
||||
else return m_hashSet.contains(torrent->hash());
|
||||
}
|
||||
|
||||
bool TorrentFilter::matchLabel(BitTorrent::TorrentHandle *const torrent) const
|
||||
bool TorrentFilter::matchCategory(BitTorrent::TorrentHandle *const torrent) const
|
||||
{
|
||||
if (m_label.isNull()) return true;
|
||||
else if (m_label.isEmpty()) return torrent->label().isEmpty();
|
||||
else return (torrent->label() == m_label);
|
||||
if (m_category.isNull()) return true;
|
||||
else return (torrent->belongsToCategory(m_category));
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
Errored
|
||||
};
|
||||
|
||||
static const QString AnyLabel;
|
||||
static const QString AnyCategory;
|
||||
static const QStringSet AnyHash;
|
||||
|
||||
static const TorrentFilter DownloadingTorrent;
|
||||
@@ -71,24 +71,24 @@ public:
|
||||
static const TorrentFilter ErroredTorrent;
|
||||
|
||||
TorrentFilter();
|
||||
// label: pass empty string for "no label" or null string (QString()) for "any label"
|
||||
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString label = AnyLabel);
|
||||
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString label = AnyLabel);
|
||||
// category: pass empty string for "no category" or null string (QString()) for "any category"
|
||||
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString category = AnyCategory);
|
||||
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString category = AnyCategory);
|
||||
|
||||
bool setType(Type type);
|
||||
bool setTypeByName(const QString &filter);
|
||||
bool setHashSet(const QStringSet &hashSet);
|
||||
bool setLabel(const QString &label);
|
||||
bool setCategory(const QString &category);
|
||||
|
||||
bool match(BitTorrent::TorrentHandle *const torrent) const;
|
||||
|
||||
private:
|
||||
bool matchState(BitTorrent::TorrentHandle *const torrent) const;
|
||||
bool matchHash(BitTorrent::TorrentHandle *const torrent) const;
|
||||
bool matchLabel(BitTorrent::TorrentHandle *const torrent) const;
|
||||
bool matchCategory(BitTorrent::TorrentHandle *const torrent) const;
|
||||
|
||||
Type m_type;
|
||||
QString m_label;
|
||||
QString m_category;
|
||||
QStringSet m_hashSet;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,46 +29,18 @@
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDataStream>
|
||||
#include <QMap>
|
||||
|
||||
const qlonglong MAX_ETA = 8640000;
|
||||
|
||||
enum class MaxRatioAction
|
||||
enum class ShutdownDialogAction
|
||||
{
|
||||
Pause,
|
||||
Remove
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MaxRatioAction)
|
||||
|
||||
enum class TorrentExportFolder
|
||||
{
|
||||
Regular,
|
||||
Finished
|
||||
};
|
||||
|
||||
enum class ShutdownAction
|
||||
{
|
||||
None,
|
||||
Exit,
|
||||
Shutdown,
|
||||
Suspend,
|
||||
Hibernate
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline QDataStream &operator<<(QDataStream &out, const T &val)
|
||||
{
|
||||
return (out << static_cast<int>(val));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QDataStream &operator>>(QDataStream &in, T &val)
|
||||
{
|
||||
int tmp;
|
||||
in >> tmp;
|
||||
val = static_cast<T>(tmp);
|
||||
return in;
|
||||
}
|
||||
typedef QMap<QString, QString> QStringMap;
|
||||
|
||||
#endif // TYPES_H
|
||||
|
||||
@@ -250,22 +250,23 @@ bool Utils::Fs::sameFiles(const QString& path1, const QString& path2)
|
||||
return same;
|
||||
}
|
||||
|
||||
QString Utils::Fs::toValidFileSystemName(const QString &filename)
|
||||
QString Utils::Fs::toValidFileSystemName(const QString &name, bool allowSeparators)
|
||||
{
|
||||
static const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||
QRegExp regex(allowSeparators ? "[:?\"*<>|]+" : "[\\\\/:?\"*<>|]+");
|
||||
|
||||
QString validName = filename.trimmed();
|
||||
QString validName = name.trimmed();
|
||||
validName.replace(regex, " ");
|
||||
qDebug() << "toValidFileSystemName:" << filename << "=>" << validName;
|
||||
qDebug() << "toValidFileSystemName:" << name << "=>" << validName;
|
||||
|
||||
return validName;
|
||||
}
|
||||
|
||||
bool Utils::Fs::isValidFileSystemName(const QString& filename)
|
||||
bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators)
|
||||
{
|
||||
if (filename.isEmpty()) return false;
|
||||
const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||
return !filename.contains(regex);
|
||||
if (name.isEmpty()) return false;
|
||||
|
||||
QRegExp regex(allowSeparators ? "[:?\"*<>|]" : "[\\\\/:?\"*<>|]");
|
||||
return !name.contains(regex);
|
||||
}
|
||||
|
||||
qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path)
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace Utils
|
||||
QString folderName(const QString& file_path);
|
||||
qint64 computePathSize(const QString& path);
|
||||
bool sameFiles(const QString& path1, const QString& path2);
|
||||
QString toValidFileSystemName(const QString &filename);
|
||||
bool isValidFileSystemName(const QString& filename);
|
||||
QString toValidFileSystemName(const QString &name, bool allowSeparators = false);
|
||||
bool isValidFileSystemName(const QString& name, bool allowSeparators = false);
|
||||
qlonglong freeDiskSpaceOnPath(QString path);
|
||||
QString branchPath(const QString& file_path, QString* removed = 0);
|
||||
bool sameFileNames(const QString& first, const QString& second);
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QThread>
|
||||
#include <QSysInfo>
|
||||
#include <boost/version.hpp>
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
#ifdef DISABLE_GUI
|
||||
#include <QCoreApplication>
|
||||
@@ -83,20 +86,21 @@ static struct { const char *source; const char *comment; } units[] = {
|
||||
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
|
||||
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
|
||||
};
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||
void Utils::Misc::shutdownComputer(const ShutdownDialogAction &action)
|
||||
{
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||
// Use dbus to power off / suspend the system
|
||||
if (action != ShutdownAction::Shutdown) {
|
||||
if (action != ShutdownDialogAction::Shutdown) {
|
||||
// Some recent systems use systemd's logind
|
||||
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
|
||||
if (login1Iface.isValid()) {
|
||||
if (action == ShutdownAction::Suspend)
|
||||
if (action == ShutdownDialogAction::Suspend)
|
||||
login1Iface.call("Suspend", false);
|
||||
else
|
||||
login1Iface.call("Hibernate", false);
|
||||
@@ -106,7 +110,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||
QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower",
|
||||
"org.freedesktop.UPower", QDBusConnection::systemBus());
|
||||
if (upowerIface.isValid()) {
|
||||
if (action == ShutdownAction::Suspend)
|
||||
if (action == ShutdownDialogAction::Suspend)
|
||||
upowerIface.call("Suspend");
|
||||
else
|
||||
upowerIface.call("Hibernate");
|
||||
@@ -116,7 +120,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
|
||||
"org.freedesktop.Hal.Device.SystemPowerManagement",
|
||||
QDBusConnection::systemBus());
|
||||
if (action == ShutdownAction::Suspend)
|
||||
if (action == ShutdownDialogAction::Suspend)
|
||||
halIface.call("Suspend", 5);
|
||||
else
|
||||
halIface.call("Hibernate");
|
||||
@@ -145,7 +149,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
AEEventID EventToSend;
|
||||
if (action != ShutdownAction::Shutdown)
|
||||
if (action != ShutdownDialogAction::Shutdown)
|
||||
EventToSend = kAESleep;
|
||||
else
|
||||
EventToSend = kAEShutDown;
|
||||
@@ -198,9 +202,9 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||
if (GetLastError() != ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
if (action == ShutdownAction::Suspend)
|
||||
if (action == ShutdownDialogAction::Suspend)
|
||||
SetSuspendState(false, false, false);
|
||||
else if (action == ShutdownAction::Hibernate)
|
||||
else if (action == ShutdownDialogAction::Hibernate)
|
||||
SetSuspendState(true, false, false);
|
||||
else
|
||||
InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false);
|
||||
@@ -211,7 +215,6 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||
(PTOKEN_PRIVILEGES) NULL, 0);
|
||||
#endif
|
||||
}
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
// Get screen center
|
||||
@@ -315,30 +318,58 @@ QString Utils::Misc::pythonVersionComplete() {
|
||||
return version;
|
||||
}
|
||||
|
||||
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
|
||||
QString Utils::Misc::unitString(Utils::Misc::SizeUnit unit)
|
||||
{
|
||||
return QCoreApplication::translate("misc",
|
||||
units[static_cast<int>(unit)].source, units[static_cast<int>(unit)].comment);
|
||||
}
|
||||
|
||||
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB, ...)
|
||||
// use Binary prefix standards from IEC 60027-2
|
||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||
// value must be given in bytes
|
||||
// to send numbers instead of strings with suffixes
|
||||
QString Utils::Misc::friendlyUnit(qreal val, bool is_speed)
|
||||
bool Utils::Misc::friendlyUnit(qint64 sizeInBytes, qreal &val, Utils::Misc::SizeUnit &unit)
|
||||
{
|
||||
if (val < 0)
|
||||
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
|
||||
if (sizeInBytes < 0) return false;
|
||||
|
||||
int i = 0;
|
||||
while(val >= 1024. && i < 4) {
|
||||
val /= 1024.;
|
||||
qreal rawVal = static_cast<qreal>(sizeInBytes);
|
||||
|
||||
while ((rawVal >= 1024.) && (i <= static_cast<int>(SizeUnit::ExbiByte))) {
|
||||
rawVal /= 1024.;
|
||||
++i;
|
||||
}
|
||||
val = rawVal;
|
||||
unit = static_cast<SizeUnit>(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
QString Utils::Misc::friendlyUnit(qint64 bytesValue, bool isSpeed)
|
||||
{
|
||||
SizeUnit unit;
|
||||
qreal friendlyVal;
|
||||
if (!friendlyUnit(bytesValue, friendlyVal, unit)) {
|
||||
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
|
||||
}
|
||||
QString ret;
|
||||
if (i == 0)
|
||||
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
|
||||
if (unit == SizeUnit::Byte)
|
||||
ret = QString::number(bytesValue) + " " + unitString(unit);
|
||||
else
|
||||
ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
|
||||
if (is_speed)
|
||||
ret = Utils::String::fromDouble(friendlyVal, 1) + " " + unitString(unit);
|
||||
if (isSpeed)
|
||||
ret += QCoreApplication::translate("misc", "/s", "per second");
|
||||
return ret;
|
||||
}
|
||||
|
||||
qlonglong Utils::Misc::sizeInBytes(qreal size, Utils::Misc::SizeUnit unit)
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(unit); ++i) {
|
||||
size *= 1024;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool Utils::Misc::isPreviewable(const QString& extension)
|
||||
{
|
||||
static QSet<QString> multimedia_extensions;
|
||||
@@ -392,21 +423,6 @@ bool Utils::Misc::isPreviewable(const QString& extension)
|
||||
return multimedia_extensions.contains(extension.toUpper());
|
||||
}
|
||||
|
||||
QString Utils::Misc::bcLinkToMagnet(QString bc_link)
|
||||
{
|
||||
QByteArray raw_bc = bc_link.toUtf8();
|
||||
raw_bc = raw_bc.mid(8); // skip bc://bt/
|
||||
raw_bc = QByteArray::fromBase64(raw_bc); // Decode base64
|
||||
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
|
||||
QStringList parts = QString(raw_bc).split("/");
|
||||
if (parts.size() != 5) return QString::null;
|
||||
QString filename = parts.at(1);
|
||||
QString hash = parts.at(3);
|
||||
QString magnet = "magnet:?xt=urn:btih:" + hash;
|
||||
magnet += "&dn=" + filename;
|
||||
return magnet;
|
||||
}
|
||||
|
||||
// Take a number of seconds and return an user-friendly
|
||||
// time duration like "1d 2h 10m".
|
||||
QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
|
||||
@@ -595,23 +611,22 @@ void Utils::Misc::openFolderSelect(const QString& absolutePath)
|
||||
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
|
||||
if (QFileInfo(path).exists()) {
|
||||
QProcess proc;
|
||||
QString output;
|
||||
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
|
||||
proc.waitForFinished();
|
||||
output = proc.readLine().simplified();
|
||||
QString output = proc.readLine().simplified();
|
||||
if (output == "dolphin.desktop" || output == "org.kde.dolphin.desktop")
|
||||
proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(path));
|
||||
else if (output == "nautilus.desktop" || output == "org.gnome.Nautilus.desktop"
|
||||
|| output == "nautilus-folder-handler.desktop")
|
||||
proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
|
||||
else if (output == "caja-folder-handler.desktop")
|
||||
proc.startDetached("caja", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
|
||||
else if (output == "nemo.desktop")
|
||||
proc.startDetached("nemo", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
|
||||
else if (output == "konqueror.desktop" || output == "kfmclient_dir.desktop")
|
||||
proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(path));
|
||||
else
|
||||
else {
|
||||
// "caja" manager can't pinpoint the file, see: https://github.com/qbittorrent/qBittorrent/issues/5003
|
||||
openPath(path.left(path.lastIndexOf("/")));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the item to select doesn't exist, try to open its parent
|
||||
@@ -649,3 +664,35 @@ QSize Utils::Misc::smallIconSize()
|
||||
return QSize(s, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
QString Utils::Misc::osName()
|
||||
{
|
||||
// static initialization for usage in signal handler
|
||||
static const QString name =
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
|
||||
QString("%1 %2 %3")
|
||||
.arg(QSysInfo::prettyProductName())
|
||||
.arg(QSysInfo::kernelVersion())
|
||||
.arg(QSysInfo::currentCpuArchitecture());
|
||||
#else
|
||||
"<Input OS name here>";
|
||||
#endif
|
||||
return name;
|
||||
}
|
||||
|
||||
QString Utils::Misc::boostVersionString()
|
||||
{
|
||||
// static initialization for usage in signal handler
|
||||
static const QString ver = QString("%1.%2.%3")
|
||||
.arg(BOOST_VERSION / 100000)
|
||||
.arg((BOOST_VERSION / 100) % 1000)
|
||||
.arg(BOOST_VERSION % 100);
|
||||
return ver;
|
||||
}
|
||||
|
||||
QString Utils::Misc::libtorrentVersionString()
|
||||
{
|
||||
// static initialization for usage in signal handler
|
||||
static const QString ver = LIBTORRENT_VERSION;
|
||||
return ver;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include <QSize>
|
||||
#include "base/types.h"
|
||||
|
||||
/* Miscellaneous functions that can be useful */
|
||||
@@ -47,26 +48,50 @@ namespace Utils
|
||||
{
|
||||
namespace Misc
|
||||
{
|
||||
// use binary prefix standards from IEC 60027-2
|
||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||
enum class SizeUnit
|
||||
{
|
||||
Byte, // 1024^0,
|
||||
KibiByte, // 1024^1,
|
||||
MebiByte, // 1024^2,
|
||||
GibiByte, // 1024^3,
|
||||
TebiByte, // 1024^4,
|
||||
PebiByte, // 1024^5,
|
||||
ExbiByte // 1024^6,
|
||||
// int64 is used for sizes and thus the next units can not be handled
|
||||
// ZebiByte, // 1024^7,
|
||||
// YobiByte, // 1024^8
|
||||
};
|
||||
|
||||
QString parseHtmlLinks(const QString &raw_text);
|
||||
bool isUrl(const QString &s);
|
||||
|
||||
void shutdownComputer(const ShutdownDialogAction &action);
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
void shutdownComputer(ShutdownAction action);
|
||||
// Get screen center
|
||||
QPoint screenCenter(QWidget *win);
|
||||
QSize smallIconSize();
|
||||
#endif
|
||||
QString osName();
|
||||
QString boostVersionString();
|
||||
QString libtorrentVersionString();
|
||||
|
||||
int pythonVersion();
|
||||
QString pythonExecutable();
|
||||
QString pythonVersionComplete();
|
||||
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
|
||||
// use Binary prefix standards from IEC 60027-2
|
||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||
|
||||
QString unitString(SizeUnit unit);
|
||||
|
||||
// return best user friendly storage unit (B, KiB, MiB, GiB, TiB)
|
||||
// value must be given in bytes
|
||||
QString friendlyUnit(qreal val, bool is_speed = false);
|
||||
bool friendlyUnit(qint64 sizeInBytes, qreal& val, SizeUnit& unit);
|
||||
QString friendlyUnit(qint64 bytesValue, bool isSpeed = false);
|
||||
qint64 sizeInBytes(qreal size, SizeUnit unit);
|
||||
|
||||
bool isPreviewable(const QString& extension);
|
||||
|
||||
QString bcLinkToMagnet(QString bc_link);
|
||||
// Take a number of seconds and return an user-friendly
|
||||
// time duration like "1d 2h 10m".
|
||||
QString userFriendlyDuration(qlonglong seconds);
|
||||
|
||||
@@ -27,12 +27,149 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QLocale>
|
||||
#include <cmath>
|
||||
#include "string.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QtGlobal>
|
||||
#include <QLocale>
|
||||
#ifdef QBT_USES_QT5
|
||||
#include <QCollator>
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
#include <QThreadStorage>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
class NaturalCompare
|
||||
{
|
||||
public:
|
||||
explicit NaturalCompare(const bool caseSensitive = true)
|
||||
: m_caseSensitive(caseSensitive)
|
||||
{
|
||||
#ifdef QBT_USES_QT5
|
||||
#if defined(Q_OS_WIN)
|
||||
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
|
||||
// sorts older versions of μTorrent differently than the newer ones because the
|
||||
// 'μ' character is encoded differently and the native API can't cope with that.
|
||||
// So default to using our custom natural sorting algorithm instead.
|
||||
// See #5238 and #5240
|
||||
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
|
||||
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
|
||||
return;
|
||||
#endif
|
||||
m_collator.setNumericMode(true);
|
||||
m_collator.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool operator()(const QString &left, const QString &right) const
|
||||
{
|
||||
#ifdef QBT_USES_QT5
|
||||
#if defined(Q_OS_WIN)
|
||||
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
|
||||
// sorts older versions of μTorrent differently than the newer ones because the
|
||||
// 'μ' character is encoded differently and the native API can't cope with that.
|
||||
// So default to using our custom natural sorting algorithm instead.
|
||||
// See #5238 and #5240
|
||||
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
|
||||
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
|
||||
return lessThan(left, right);
|
||||
#endif
|
||||
return (m_collator.compare(left, right) < 0);
|
||||
#else
|
||||
return lessThan(left, right);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool lessThan(const QString &left, const QString &right) const
|
||||
{
|
||||
// Return value `false` indicates `right` should go before `left`, otherwise, after
|
||||
int posL = 0;
|
||||
int posR = 0;
|
||||
while (true) {
|
||||
while (true) {
|
||||
if ((posL == left.size()) || (posR == right.size()))
|
||||
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
|
||||
|
||||
QChar leftChar = m_caseSensitive ? left[posL] : left[posL].toLower();
|
||||
QChar rightChar = m_caseSensitive ? right[posR] : right[posR].toLower();
|
||||
if (leftChar == rightChar)
|
||||
; // compare next character
|
||||
else if (leftChar.isDigit() && rightChar.isDigit())
|
||||
break; // Both are digits, break this loop and compare numbers
|
||||
else
|
||||
return leftChar < rightChar;
|
||||
|
||||
++posL;
|
||||
++posR;
|
||||
}
|
||||
|
||||
int startL = posL;
|
||||
while ((posL < left.size()) && left[posL].isDigit())
|
||||
++posL;
|
||||
#ifdef QBT_USES_QT5
|
||||
int numL = left.midRef(startL, posL - startL).toInt();
|
||||
#else
|
||||
int numL = left.mid(startL, posL - startL).toInt();
|
||||
#endif
|
||||
|
||||
int startR = posR;
|
||||
while ((posR < right.size()) && right[posR].isDigit())
|
||||
++posR;
|
||||
#ifdef QBT_USES_QT5
|
||||
int numR = right.midRef(startR, posR - startR).toInt();
|
||||
#else
|
||||
int numR = right.mid(startR, posR - startR).toInt();
|
||||
#endif
|
||||
|
||||
if (numL != numR)
|
||||
return (numL < numR);
|
||||
|
||||
// Strings + digits do match and we haven't hit string end
|
||||
// Do another round
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef QBT_USES_QT5
|
||||
QCollator m_collator;
|
||||
#endif
|
||||
const bool m_caseSensitive;
|
||||
};
|
||||
}
|
||||
|
||||
bool Utils::String::naturalCompareCaseSensitive(const QString &left, const QString &right)
|
||||
{
|
||||
// provide a single `NaturalCompare` instance for easy use
|
||||
// https://doc.qt.io/qt-5/threads-reentrancy.html
|
||||
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
|
||||
static QThreadStorage<NaturalCompare> nCmp;
|
||||
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(true));
|
||||
return (nCmp.localData())(left, right);
|
||||
#else
|
||||
thread_local NaturalCompare nCmp(true);
|
||||
return nCmp(left, right);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utils::String::naturalCompareCaseInsensitive(const QString &left, const QString &right)
|
||||
{
|
||||
// provide a single `NaturalCompare` instance for easy use
|
||||
// https://doc.qt.io/qt-5/threads-reentrancy.html
|
||||
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
|
||||
static QThreadStorage<NaturalCompare> nCmp;
|
||||
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(false));
|
||||
return (nCmp.localData())(left, right);
|
||||
#else
|
||||
thread_local NaturalCompare nCmp(false);
|
||||
return nCmp(left, right);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString Utils::String::fromStdString(const std::string &str)
|
||||
{
|
||||
return QString::fromUtf8(str.c_str());
|
||||
@@ -44,145 +181,6 @@ std::string Utils::String::toStdString(const QString &str)
|
||||
return std::string(utf8.constData(), utf8.length());
|
||||
}
|
||||
|
||||
// uses lessThan comparison
|
||||
bool Utils::String::naturalSort(const QString &left, const QString &right, bool &result)
|
||||
{
|
||||
// Return value indicates if functions was successful
|
||||
// result argument will contain actual comparison result if function was successful
|
||||
int posL = 0;
|
||||
int posR = 0;
|
||||
do {
|
||||
forever {
|
||||
if (posL == left.size() || posR == right.size())
|
||||
return false; // No data
|
||||
|
||||
QChar leftChar = left.at(posL);
|
||||
QChar rightChar = right.at(posR);
|
||||
bool leftCharIsDigit = leftChar.isDigit();
|
||||
bool rightCharIsDigit = rightChar.isDigit();
|
||||
if (leftCharIsDigit != rightCharIsDigit)
|
||||
return false; // Digit positions mismatch
|
||||
|
||||
if (leftCharIsDigit)
|
||||
break; // Both are digit, break this loop and compare numbers
|
||||
|
||||
if (leftChar != rightChar)
|
||||
return false; // Strings' subsets before digit do not match
|
||||
|
||||
++posL;
|
||||
++posR;
|
||||
}
|
||||
|
||||
QString temp;
|
||||
while (posL < left.size()) {
|
||||
if (left.at(posL).isDigit())
|
||||
temp += left.at(posL);
|
||||
else
|
||||
break;
|
||||
posL++;
|
||||
}
|
||||
int numL = temp.toInt();
|
||||
temp.clear();
|
||||
|
||||
while (posR < right.size()) {
|
||||
if (right.at(posR).isDigit())
|
||||
temp += right.at(posR);
|
||||
else
|
||||
break;
|
||||
posR++;
|
||||
}
|
||||
int numR = temp.toInt();
|
||||
|
||||
if (numL != numR) {
|
||||
result = (numL < numR);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Strings + digits do match and we haven't hit string end
|
||||
// Do another round
|
||||
|
||||
} while (true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Utils::String::NaturalCompare::NaturalCompare()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
|
||||
#if defined(Q_OS_WIN)
|
||||
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
|
||||
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
|
||||
return;
|
||||
#endif
|
||||
m_collator.setNumericMode(true);
|
||||
m_collator.setCaseSensitivity(Qt::CaseInsensitive);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utils::String::NaturalCompare::operator()(const QString &l, const QString &r)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
|
||||
#if defined(Q_OS_WIN)
|
||||
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
|
||||
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
|
||||
return lessThan(l, r);
|
||||
#endif
|
||||
return (m_collator.compare(l, r) < 0);
|
||||
#else
|
||||
return lessThan(l, r);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utils::String::NaturalCompare::lessThan(const QString &left, const QString &right)
|
||||
{
|
||||
// Return value `false` indicates `right` should go before `left`, otherwise, after
|
||||
int posL = 0;
|
||||
int posR = 0;
|
||||
while (true) {
|
||||
while (true) {
|
||||
if (posL == left.size() || posR == right.size())
|
||||
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
|
||||
|
||||
QChar leftChar = left[posL].toLower();
|
||||
QChar rightChar = right[posR].toLower();
|
||||
if (leftChar == rightChar)
|
||||
; // compare next character
|
||||
else if (leftChar.isDigit() && rightChar.isDigit())
|
||||
break; // Both are digits, break this loop and compare numbers
|
||||
else
|
||||
return leftChar < rightChar;
|
||||
|
||||
++posL;
|
||||
++posR;
|
||||
}
|
||||
|
||||
int startL = posL;
|
||||
while ((posL < left.size()) && left[posL].isDigit())
|
||||
++posL;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
|
||||
int numL = left.midRef(startL, posL - startL).toInt();
|
||||
#else
|
||||
int numL = left.mid(startL, posL - startL).toInt();
|
||||
#endif
|
||||
|
||||
int startR = posR;
|
||||
while ((posR < right.size()) && right[posR].isDigit())
|
||||
++posR;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
|
||||
int numR = right.midRef(startR, posR - startR).toInt();
|
||||
#else
|
||||
int numR = right.mid(startR, posR - startR).toInt();
|
||||
#endif
|
||||
|
||||
if (numL != numR)
|
||||
return (numL < numR);
|
||||
|
||||
// Strings + digits do match and we haven't hit string end
|
||||
// Do another round
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// to send numbers instead of strings with suffixes
|
||||
QString Utils::String::fromDouble(double n, int precision)
|
||||
{
|
||||
|
||||
@@ -31,13 +31,9 @@
|
||||
#define UTILS_STRING_H
|
||||
|
||||
#include <string>
|
||||
#include <QtGlobal>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
|
||||
#include <QCollator>
|
||||
#endif
|
||||
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QString;
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
@@ -51,19 +47,8 @@ namespace Utils
|
||||
// Taken from https://crackstation.net/hashing-security.htm
|
||||
bool slowEquals(const QByteArray &a, const QByteArray &b);
|
||||
|
||||
bool naturalSort(const QString &left, const QString &right, bool &result);
|
||||
|
||||
class NaturalCompare
|
||||
{
|
||||
public:
|
||||
NaturalCompare();
|
||||
bool operator()(const QString &l, const QString &r);
|
||||
bool lessThan(const QString &left, const QString &right);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
|
||||
private:
|
||||
QCollator m_collator;
|
||||
#endif
|
||||
};
|
||||
bool naturalCompareCaseSensitive(const QString &left, const QString &right);
|
||||
bool naturalCompareCaseInsensitive(const QString &left, const QString &right);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
src/config.h.cmakein
Normal file
15
src/config.h.cmakein
Normal file
@@ -0,0 +1,15 @@
|
||||
#cmakedefine QBT_USES_QT5
|
||||
#cmakedefine QBT_USE_GUI
|
||||
|
||||
#ifndef QBT_USE_GUI
|
||||
#define DISABLE_GUI
|
||||
#define DISABLE_COUNTRIES_RESOLUTION
|
||||
#endif
|
||||
|
||||
#cmakedefine QBT_USE_WEBUI
|
||||
|
||||
#ifndef QBT_USE_WEBUI
|
||||
#define DISABLE_WEBUI
|
||||
#endif
|
||||
|
||||
#cmakedefine STACKTRACE_WIN
|
||||
135
src/gui/CMakeLists.txt
Normal file
135
src/gui/CMakeLists.txt
Normal file
@@ -0,0 +1,135 @@
|
||||
set(CMAKE_AUTORCC True)
|
||||
set(CMAKE_AUTOUIC True)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_subdirectory(lineedit)
|
||||
add_subdirectory(properties)
|
||||
add_subdirectory(powermanagement)
|
||||
add_subdirectory(rss)
|
||||
add_subdirectory(search)
|
||||
|
||||
if (UNIX AND NOT APPLE AND DBUS)
|
||||
add_subdirectory(qtnotify)
|
||||
include_directories(qtnotify)
|
||||
list(APPEND QBT_GUI_OPTIONAL_LINK_LIBRARIES qbt_qtnotify)
|
||||
endif (UNIX AND NOT APPLE AND DBUS)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/properties
|
||||
${CMAKE_CURRENT_BINARY_DIR}/rss
|
||||
${CMAKE_CURRENT_BINARY_DIR}/search
|
||||
lineedit/src
|
||||
powermanagement
|
||||
properties
|
||||
rss
|
||||
../app
|
||||
)
|
||||
|
||||
set(QBT_GUI_HEADERS
|
||||
about_imp.h
|
||||
addnewtorrentdialog.h
|
||||
advancedsettings.h
|
||||
advancedsettings.h
|
||||
autoexpandabledialog.h
|
||||
cookiesdialog.h
|
||||
cookiesmodel.h
|
||||
deletionconfirmationdlg.h
|
||||
downloadfromurldlg.h
|
||||
executionlog.h
|
||||
guiiconprovider.h
|
||||
hidabletabwidget.h
|
||||
ico.h
|
||||
loglistwidget.h
|
||||
mainwindow.h
|
||||
messageboxraised.h
|
||||
optionsdlg.h
|
||||
previewlistdelegate.h
|
||||
previewselect.h
|
||||
scanfoldersdelegate.h
|
||||
shutdownconfirmdlg.h
|
||||
speedlimitdlg.h
|
||||
statsdialog.h
|
||||
statusbar.h
|
||||
torrentcontentfiltermodel.h
|
||||
torrentcontentmodel.h
|
||||
torrentcontentmodelfile.h
|
||||
torrentcontentmodelfolder.h
|
||||
torrentcontentmodelitem.h
|
||||
torrentcontenttreeview.h
|
||||
torrentcreatordlg.h
|
||||
torrentmodel.h
|
||||
trackerlogin.h
|
||||
transferlistdelegate.h
|
||||
transferlistfilterswidget.h
|
||||
transferlistsortmodel.h
|
||||
transferlistwidget.h
|
||||
updownratiodlg.h
|
||||
)
|
||||
|
||||
set(QBT_GUI_SOURCES
|
||||
addnewtorrentdialog.cpp
|
||||
advancedsettings.cpp
|
||||
autoexpandabledialog.cpp
|
||||
cookiesdialog.cpp
|
||||
cookiesmodel.cpp
|
||||
executionlog.cpp
|
||||
guiiconprovider.cpp
|
||||
ico.cpp
|
||||
loglistwidget.cpp
|
||||
mainwindow.cpp
|
||||
messageboxraised.cpp
|
||||
optionsdlg.cpp
|
||||
previewselect.cpp
|
||||
scanfoldersdelegate.cpp
|
||||
shutdownconfirmdlg.cpp
|
||||
speedlimitdlg.cpp
|
||||
statsdialog.cpp
|
||||
statusbar.cpp
|
||||
torrentcontentfiltermodel.cpp
|
||||
torrentcontentmodel.cpp
|
||||
torrentcontentmodelfile.cpp
|
||||
torrentcontentmodelfolder.cpp
|
||||
torrentcontentmodelitem.cpp
|
||||
torrentcontenttreeview.cpp
|
||||
torrentcreatordlg.cpp
|
||||
torrentmodel.cpp
|
||||
trackerlogin.cpp
|
||||
transferlistdelegate.cpp
|
||||
transferlistfilterswidget.cpp
|
||||
transferlistsortmodel.cpp
|
||||
transferlistwidget.cpp
|
||||
updownratiodlg.cpp
|
||||
)
|
||||
|
||||
if (WIN32 OR APPLE)
|
||||
list(APPEND QBT_GUI_HEADERS programupdater.h)
|
||||
list(APPEND QBT_GUI_SOURCES programupdater.cpp)
|
||||
endif (WIN32 OR APPLE)
|
||||
|
||||
set(QBT_GUI_FORMS
|
||||
mainwindow.ui
|
||||
about.ui
|
||||
cookiesdialog.ui
|
||||
preview.ui
|
||||
login.ui
|
||||
downloadfromurldlg.ui
|
||||
bandwidth_limit.ui
|
||||
updownratiodlg.ui
|
||||
confirmdeletiondlg.ui
|
||||
executionlog.ui
|
||||
addnewtorrentdialog.ui
|
||||
autoexpandabledialog.ui
|
||||
statsdialog.ui
|
||||
optionsdlg.ui
|
||||
torrentcreatordlg.ui
|
||||
shutdownconfirmdlg.ui
|
||||
)
|
||||
|
||||
qbt_target_sources(about.qrc)
|
||||
|
||||
add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES})
|
||||
target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine
|
||||
${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base
|
||||
QtSingleApplication::QtSingleApplication
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user