mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-17 14:08:03 -06:00
Compare commits
186 Commits
master
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1ebbcb35d | ||
|
|
2743d998a8 | ||
|
|
dbbfbaff9f | ||
|
|
0be8439cf6 | ||
|
|
66982c5524 | ||
|
|
85af8547f7 | ||
|
|
e26977ab2c | ||
|
|
ec1cc783a6 | ||
|
|
03b00ec045 | ||
|
|
5e90156e9e | ||
|
|
052206efa1 | ||
|
|
305d73180b | ||
|
|
80000bf0fd | ||
|
|
06ebe756e8 | ||
|
|
5fa3d9f19c | ||
|
|
5b4c6d3665 | ||
|
|
77bd0f17d1 | ||
|
|
03a702cfbd | ||
|
|
a932cd2ec1 | ||
|
|
8e5743380a | ||
|
|
8001eb0368 | ||
|
|
f214dc88fc | ||
|
|
5cff5ab135 | ||
|
|
82ba154b64 | ||
|
|
4ea44bbd2b | ||
|
|
a5e68a8725 | ||
|
|
70291014d1 | ||
|
|
1aabcfc30c | ||
|
|
aba80e2b1c | ||
|
|
be683fbcd3 | ||
|
|
2bcf09cfa5 | ||
|
|
697325af63 | ||
|
|
c21bd77be5 | ||
|
|
d5430adaaa | ||
|
|
9e99a0d3f5 | ||
|
|
d088ab6f43 | ||
|
|
820d510c12 | ||
|
|
676847fcd0 | ||
|
|
0204630ee6 | ||
|
|
b515c7eda4 | ||
|
|
73fcecac76 | ||
|
|
a7b82ebcb5 | ||
|
|
f8598b010d | ||
|
|
93779bcc4b | ||
|
|
938f5b9dd9 | ||
|
|
3b4d9f49d5 | ||
|
|
171c93af50 | ||
|
|
6f81e40106 | ||
|
|
e19b5cb2ce | ||
|
|
2c69faca58 | ||
|
|
9272151d0a | ||
|
|
d45ebf5a43 | ||
|
|
8074be7644 | ||
|
|
c99ac99a99 | ||
|
|
976e2450ec | ||
|
|
7e4db8fafd | ||
|
|
115a409d92 | ||
|
|
c203ab3d16 | ||
|
|
5dff96496d | ||
|
|
f813935011 | ||
|
|
2be719449f | ||
|
|
2094c870d5 | ||
|
|
4fe93ae8b8 | ||
|
|
fff1103cf4 | ||
|
|
8cede43a45 | ||
|
|
9b1fa3a5af | ||
|
|
409e73c074 | ||
|
|
c893729d62 | ||
|
|
945466968c | ||
|
|
54f080b755 | ||
|
|
bfad14d552 | ||
|
|
2972e1596d | ||
|
|
987d2aae88 | ||
|
|
4707d34fad | ||
|
|
2ffc09d097 | ||
|
|
afa8d6bb8f | ||
|
|
a37ead98e8 | ||
|
|
c73cd8d618 | ||
|
|
800a3aa61e | ||
|
|
ebd815be75 | ||
|
|
ef669acf89 | ||
|
|
ac6426eab1 | ||
|
|
b107b745f2 | ||
|
|
3d851a448f | ||
|
|
ce133f01aa | ||
|
|
492d378537 | ||
|
|
7ece484423 | ||
|
|
be5ad63e21 | ||
|
|
bdac8f8db8 | ||
|
|
bb893e70c5 | ||
|
|
57ec9db532 | ||
|
|
0287481001 | ||
|
|
0167496ecb | ||
|
|
d86cf193a0 | ||
|
|
246cad1108 | ||
|
|
23bf86a8a8 | ||
|
|
6ce4c885b9 | ||
|
|
faf84e483a | ||
|
|
576004c840 | ||
|
|
c93b05c293 | ||
|
|
55c3813fac | ||
|
|
725c6857be | ||
|
|
86767c9ab4 | ||
|
|
46aa631d2b | ||
|
|
7c61a937c9 | ||
|
|
b8d65dcc45 | ||
|
|
b9ab83eaf2 | ||
|
|
8b7b563992 | ||
|
|
b813a878d7 | ||
|
|
54e486c389 | ||
|
|
12d0a3acc1 | ||
|
|
6ad2a13386 | ||
|
|
2a9c401db9 | ||
|
|
ea5a29018f | ||
|
|
c8d0a715e8 | ||
|
|
2cfc6514ab | ||
|
|
1d78bc7206 | ||
|
|
e5577e43f8 | ||
|
|
17c0463906 | ||
|
|
4168772904 | ||
|
|
44f2186749 | ||
|
|
0c918bcc3a | ||
|
|
0a8925dc75 | ||
|
|
a446597597 | ||
|
|
54354a2732 | ||
|
|
d94b8f08ab | ||
|
|
0d8189efeb | ||
|
|
00c886e426 | ||
|
|
551fc35439 | ||
|
|
9ff17c8d9d | ||
|
|
ec37732e99 | ||
|
|
8a414f32a8 | ||
|
|
bac06acb49 | ||
|
|
ae1e3c2a81 | ||
|
|
67940eb0f9 | ||
|
|
007aa8480e | ||
|
|
dedec10c58 | ||
|
|
75219e21be | ||
|
|
10f5964f8e | ||
|
|
a4a64d51c0 | ||
|
|
1014313d88 | ||
|
|
e486bb4c29 | ||
|
|
5c3d9ffb46 | ||
|
|
2e474fd8db | ||
|
|
b2b110ae1f | ||
|
|
68a34e0738 | ||
|
|
38fa575958 | ||
|
|
6cfeefe054 | ||
|
|
8007971a53 | ||
|
|
d66bd30fae | ||
|
|
3fa59b1b12 | ||
|
|
20e7aff393 | ||
|
|
4b7ce87f57 | ||
|
|
2075533468 | ||
|
|
a4ad5c8d11 | ||
|
|
35f2f56757 | ||
|
|
e6f4aa6a2f | ||
|
|
92fc62bb0d | ||
|
|
44b57a59f5 | ||
|
|
97b8e02bf5 | ||
|
|
5df42420cb | ||
|
|
0ede11a1b7 | ||
|
|
7d9c282db9 | ||
|
|
bc0e0813a4 | ||
|
|
f3aebb3001 | ||
|
|
800f966df9 | ||
|
|
e33df4dd8c | ||
|
|
96d9d810fd | ||
|
|
8707a1bc86 | ||
|
|
0c988a5fd4 | ||
|
|
b396ca771d | ||
|
|
a37dfcf961 | ||
|
|
31989740cd | ||
|
|
501191289b | ||
|
|
8971e92d78 | ||
|
|
0c96e79d0d | ||
|
|
0704c0f5e6 | ||
|
|
9cb190ebe7 | ||
|
|
667f84995c | ||
|
|
7a93fae6e4 | ||
|
|
0d6deca15c | ||
|
|
f54d7d46f2 | ||
|
|
8cf00ba5e1 | ||
|
|
ecc9c6bbd9 | ||
|
|
e11199f988 | ||
|
|
e9ed621178 |
@@ -45,8 +45,7 @@ before_build:
|
|||||||
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
|
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
|
||||||
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
|
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
|
||||||
# setup project
|
# setup project
|
||||||
- COPY /Y "%CACHE_DIR%\winconf.pri" "%REPO_DIR%"
|
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
|
||||||
- COPY /Y "%CACHE_DIR%\winconf-msvc.pri" "%REPO_DIR%"
|
|
||||||
# workarounds
|
# workarounds
|
||||||
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
||||||
|
|
||||||
|
|||||||
@@ -166,7 +166,10 @@ script:
|
|||||||
if [ "$build_system" = "cmake" ]; then
|
if [ "$build_system" = "cmake" ]; then
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DGUI=${gui} -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
|
if [ "$gui" = "false" ]; then
|
||||||
|
DISABLE_GUI_OPTION="-DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=ON"
|
||||||
|
fi
|
||||||
|
cmake $DISABLE_GUI_OPTION -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
|
||||||
-G "Ninja" -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
|
-G "Ninja" -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
|
||||||
BUILD_TOOL="ninja"
|
BUILD_TOOL="ninja"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ mode = developer
|
|||||||
|
|
||||||
|
|
||||||
[qbittorrent.qbittorrentdesktop_master]
|
[qbittorrent.qbittorrentdesktop_master]
|
||||||
source_file = src/icons/qBittorrent.desktop
|
source_file = dist/unix/qbittorrent.desktop
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = DESKTOP
|
type = DESKTOP
|
||||||
minimum_perc = 23
|
minimum_perc = 23
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||||
cmake_policy(VERSION 3.5)
|
|
||||||
|
message(WARNING "No official support for cmake build system. If it is broken, please submit patches!")
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
|
||||||
include(FunctionReadVersion)
|
include(FunctionReadVersion)
|
||||||
@@ -25,32 +26,29 @@ add_definitions(-DQBT_VERSION_BUILD=${VER_BUILD})
|
|||||||
add_definitions(-DQBT_VERSION="v${PROJECT_VERSION}")
|
add_definitions(-DQBT_VERSION="v${PROJECT_VERSION}")
|
||||||
add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}")
|
add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}")
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
include(GNUInstallDirs)
|
||||||
include(GNUInstallDirs)
|
include(FeatureSummary)
|
||||||
endif (UNIX AND NOT APPLE)
|
|
||||||
|
# version requirements
|
||||||
|
set(requiredBoostVersion 1.35)
|
||||||
|
set(requiredQtVersion 5.5.1)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
include(winconf)
|
include(winconf)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
# we need options here, because they are used not only in "src" subdir, but in the "dist" dir too
|
|
||||||
include(CMakeDependentOption)
|
|
||||||
|
|
||||||
option(SYSTEM_QTSINGLEAPPLICATION
|
# we need options here, at the top level, because they are used not only in "src" subdir, but in the "dist" dir too
|
||||||
"Use the system qtsingleapplication library or shipped one otherwise")
|
include(CompileFeature)
|
||||||
|
|
||||||
option(GUI "Allows to disable GUI for headless running. Disables QtDBus and the GeoIP Database" ON)
|
|
||||||
|
|
||||||
option(WEBUI "Allows to disable the WebUI." ON)
|
|
||||||
|
|
||||||
option(STACKTRACE "Enable stacktrace feature" ON)
|
|
||||||
|
|
||||||
if (UNIX)
|
|
||||||
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(UNIX)
|
|
||||||
|
|
||||||
|
optional_compile_definitions(COUNTRIES_RESOLUTION FEATURE DESCRIPTION "Enable resolving peers IP addresses to countries"
|
||||||
|
DEFAULT ON DISABLED DISABLE_COUNTRIES_RESOLUTION)
|
||||||
|
optional_compile_definitions(STACKTRACE FEATURE DESCRIPTION "Enable stacktraces"
|
||||||
|
DEFAULT ON ENABLED STACKTRACE)
|
||||||
|
optional_compile_definitions(WEBUI FEATURE DESCRIPTION "Enables built-in HTTP server for headless use"
|
||||||
|
DEFAULT ON DISABLED DISABLE_WEBUI)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(dist)
|
add_subdirectory(dist)
|
||||||
|
|
||||||
|
feature_summary(DESCRIPTION "\nConfiguration results:" WHAT ALL)
|
||||||
|
|||||||
@@ -6,6 +6,31 @@ For programming languages other than C++ (e.g. JavaScript) used in this reposito
|
|||||||
**Note 2:** You can use the `uncrustify` program/tool to clean up any source file. Use it with the `uncrustify.cfg` configuration file found in the root folder.
|
**Note 2:** You can use the `uncrustify` program/tool to clean up any source file. Use it with the `uncrustify.cfg` configuration file found in the root folder.
|
||||||
**Note 3:** There is also a style for QtCreator but it doesn't cover all cases. In QtCreator `Tools->Options...->C++->Code Style->Import...` and choose the `codingStyleQtCreator.xml` file found in the root folder.
|
**Note 3:** There is also a style for QtCreator but it doesn't cover all cases. In QtCreator `Tools->Options...->C++->Code Style->Import...` and choose the `codingStyleQtCreator.xml` file found in the root folder.
|
||||||
|
|
||||||
|
### Table Of Contents
|
||||||
|
|
||||||
|
* [1. New lines & curly braces](#1-new-lines--curly-braces)
|
||||||
|
* [a. Function blocks, class/struct definitions, namespaces](#a-function-blocks-classstruct-definitions-namespaces)
|
||||||
|
* [b. Other code blocks](#b-other-code-blocks)
|
||||||
|
* [c. Blocks in switch's case labels](#c-blocks-in-switchs-case-labels)
|
||||||
|
* [d. If-else statements](#d-if-else-statements)
|
||||||
|
* [e. Single statement if blocks](#e-single-statement-if-blocks)
|
||||||
|
* [f. Acceptable conditions to omit braces](#f-acceptable-conditions-to-omit-braces)
|
||||||
|
* [g. Brace enclosed initializers](#g-brace-enclosed-initializers)
|
||||||
|
* [2. Indentation](#2-indentation)
|
||||||
|
* [3. File encoding and line endings](#3-file-encoding-and-line-endings)
|
||||||
|
* [4. Initialization lists](#4-initialization-lists)
|
||||||
|
* [5. Enums](#5-enums)
|
||||||
|
* [6. Names](#6-names)
|
||||||
|
* [a. Type names and namespaces](#a-type-names-and-namespaces)
|
||||||
|
* [b. Variable names](#b-variable-names)
|
||||||
|
* [c. Private member variable names](#c-private-member-variable-names)
|
||||||
|
* [7. Header inclusion order](#7-header-inclusion-order)
|
||||||
|
* [8. Include guard](#8-include-guard)
|
||||||
|
* [9. Misc](#9-misc)
|
||||||
|
* [10. Git commit message](#10-git-commit-message)
|
||||||
|
* [11. Not covered above](#11-not-covered-above)
|
||||||
|
---
|
||||||
|
|
||||||
### 1. New lines & curly braces ###
|
### 1. New lines & curly braces ###
|
||||||
|
|
||||||
#### a. Function blocks, class/struct definitions, namespaces ####
|
#### a. Function blocks, class/struct definitions, namespaces ####
|
||||||
@@ -165,11 +190,11 @@ QVariantMap map {{"key1", 5}, {"key2", 10}};
|
|||||||
### 2. Indentation ###
|
### 2. Indentation ###
|
||||||
4 spaces.
|
4 spaces.
|
||||||
|
|
||||||
### 3. File encoding and line endings. ###
|
### 3. File encoding and line endings ###
|
||||||
|
|
||||||
UTF-8 and Unix-like line ending (LF). Unless some platform specific files need other encodings/line endings.
|
UTF-8 and Unix-like line ending (LF). Unless some platform specific files need other encodings/line endings.
|
||||||
|
|
||||||
### 4. Initialization lists. ###
|
### 4. Initialization lists ###
|
||||||
Initialization lists should be vertical. This will allow for more easily readable diffs. The initialization colon should be indented and in its own line along with first argument. The rest of the arguments should be indented too and have the comma prepended.
|
Initialization lists should be vertical. This will allow for more easily readable diffs. The initialization colon should be indented and in its own line along with first argument. The rest of the arguments should be indented too and have the comma prepended.
|
||||||
```c++
|
```c++
|
||||||
myClass::myClass(int a, int b, int c, int d)
|
myClass::myClass(int a, int b, int c, int d)
|
||||||
@@ -182,7 +207,7 @@ myClass::myClass(int a, int b, int c, int d)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Enums. ###
|
### 5. Enums ###
|
||||||
Enums should be vertical. This will allow for more easily readable diffs. The members should be indented.
|
Enums should be vertical. This will allow for more easily readable diffs. The members should be indented.
|
||||||
```c++
|
```c++
|
||||||
enum Days
|
enum Days
|
||||||
@@ -197,7 +222,7 @@ enum Days
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Names. ###
|
### 6. Names ###
|
||||||
All names should be camelCased.
|
All names should be camelCased.
|
||||||
|
|
||||||
#### a. Type names and namespaces ####
|
#### a. Type names and namespaces ####
|
||||||
@@ -231,7 +256,7 @@ class MyClass
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. Header inclusion order. ###
|
### 7. Header inclusion order ###
|
||||||
The headers should be placed in the following group order:
|
The headers should be placed in the following group order:
|
||||||
1. Module header (in .cpp)
|
1. Module header (in .cpp)
|
||||||
2. C++ Standard Library headers
|
2. C++ Standard Library headers
|
||||||
@@ -239,11 +264,11 @@ The headers should be placed in the following group order:
|
|||||||
4. Boost library headers
|
4. Boost library headers
|
||||||
5. Libtorrent headers
|
5. Libtorrent headers
|
||||||
6. Qt headers
|
6. Qt headers
|
||||||
7. qBittorrent own headers, starting from *base* headers.
|
7. qBittorrent's own headers, starting from the *base* headers.
|
||||||
|
|
||||||
The headers should be ordered alphabetically within each group.
|
The headers should be ordered alphabetically within each group.
|
||||||
If there are conditionals for the same header group, then put them at the bottom of the respective group.
|
If there are conditionals for the same header group, then put them at the bottom of the respective group.
|
||||||
If there are conditionals for the different header groups, then put them above of the "qBittorrent own headers" group.
|
If there are conditionals that contain headers from several different header groups, then put them above the "qBittorrent's own headers" group.
|
||||||
|
|
||||||
One exception is the header containing the library version (for example, QtGlobal), this particular header isn't constrained by the aforementioned order.
|
One exception is the header containing the library version (for example, QtGlobal), this particular header isn't constrained by the aforementioned order.
|
||||||
|
|
||||||
@@ -285,19 +310,19 @@ Example:
|
|||||||
#include <QFont>
|
#include <QFont>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// conditional for the different header groups
|
// conditional that contains headers from several different header groups
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// qBittorrent own headers
|
// qBittorrent's own headers
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
#include "anothermodule.h"
|
#include "anothermodule.h"
|
||||||
#include "ui_examplewidget.h"
|
#include "ui_examplewidget.h"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8. Include guard. ###
|
### 8. Include guard ###
|
||||||
`#pragma once` should be used instead of "include guard" in new code:
|
`#pragma once` should be used instead of "include guard" in new code:
|
||||||
```c++
|
```c++
|
||||||
// examplewidget.h
|
// examplewidget.h
|
||||||
@@ -313,7 +338,7 @@ class ExampleWidget : public QWidget
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9. Misc. ###
|
### 9. Misc ###
|
||||||
|
|
||||||
* Line breaks for long lines with operation:
|
* Line breaks for long lines with operation:
|
||||||
|
|
||||||
|
|||||||
83
Changelog
83
Changelog
@@ -1,3 +1,86 @@
|
|||||||
|
* Sun Aug 12 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.2
|
||||||
|
- FEATURE: New options for "inhibit sleep" (Lukas Greib)
|
||||||
|
- FEATURE: Add option for regexps in the transferlist search filter's context menu (thalieht)
|
||||||
|
- FEATURE: Add async io threads option to AdvancedSettings (tjjh89017)
|
||||||
|
- FEATURE: Allow save resume interval to be disabled (Chocobo1)
|
||||||
|
- FEATURE: Add checkbox for recursive download dialog (Chocobo1)
|
||||||
|
- FEATURE: Add changelog link in program updater (Chocobo1)
|
||||||
|
- BUGFIX: Avoid allocating large memory when loading a .torrent file (Couchy)
|
||||||
|
- BUGFIX: Notify users on 1st time close/minimize to tray (sledgehammer999)
|
||||||
|
- BUGFIX: Fix I/O error after fetching magnet metadata (Chocobo1)
|
||||||
|
- BUGFIX: Never save resume data for already paused torrents (glassez)
|
||||||
|
- BUGFIX: Make ProgramUpdater upgrade to 64-bit qbt when running on 64-bit Windows (Chocobo1)
|
||||||
|
- BUGFIX: Put temporary files in qbt own temp folder (Chocobo1)
|
||||||
|
- BUGFIX: Avoid potentially setting the wrong piece priorities (Chocobo1)
|
||||||
|
- BUGFIX: Various code refactorings/improvements (Chocobo1, thalieht, glassez)
|
||||||
|
- BUGFIX: Add options "Download in sequential order" and "Download first and last pieces first" in AddNewTorrentDialog (Chocobo1)
|
||||||
|
- BUGFIX: Download favicon using appropriate protocol (glassez)
|
||||||
|
- BUGFIX: Apply proxy settings on DownloadManager creation (glassez)
|
||||||
|
- BUGFIX: Improve torrent initialization (glassez)
|
||||||
|
- BUGFIX: Save resume data on torrent change events (glassez)
|
||||||
|
- BUGFIX: Increase default resume data save interval (Chocobo1)
|
||||||
|
- BUGFIX: Work around crash when procesing recursive download. Closes #9086 (Chocobo1)
|
||||||
|
- BUGFIX: Reduce queries to python version (Chocobo1)
|
||||||
|
- BUGFIX: Disable certain mouse wheel events in Options dialog (Chocobo1)
|
||||||
|
- WEBUI: Send all rechecks in one request (Thomas Piccirello)
|
||||||
|
- WEBUI: Add WebUI Force Reannounce option (Thomas Piccirello)
|
||||||
|
- WEBUI: Create non-existing path in setLocationAction() (Goshik)
|
||||||
|
- WEBUI: Add WebUI support for Mac ⌘ (Command) key (Thomas Piccirello)
|
||||||
|
- WEBUI: Show current save path in 'Set location' window (Goshik)
|
||||||
|
- WEBUI: Fix WebUI cache behavior for css files (Chocobo1)
|
||||||
|
- WEBUI: Send Cache-Control header in WebUI responses (Chocobo1)
|
||||||
|
- WEBUI: Add form-action to CSP (Thomas Piccirello)
|
||||||
|
- WEBUI: Add upgrade-insecure-requests to CSP when HTTPS is enabled (Thomas Piccirello)
|
||||||
|
- WEBUI: Reset WebUI ban counter on login success (Chocobo1)
|
||||||
|
- WEBUI: Add logging messages in WebUI login action (Chocobo1)
|
||||||
|
- WEBUI: Add option to control CSRF protection (Chocobo1)
|
||||||
|
- WEBUI: Add option to control WebUI clickjacking protection (Chocobo1)
|
||||||
|
- RSS: Implement "Sequential downloading" feature. Closes #6835 (glassez)
|
||||||
|
- RSS: Don't use RSS feed URLs as base for file names. Closes #8399 (glassez)
|
||||||
|
- SEARCH: Add a name filter for search results (thalieht)
|
||||||
|
- SEARCH: Fix python version detection (Chocobo1)
|
||||||
|
- SEARCH: Clear python cache conditionally (Chocobo1)
|
||||||
|
- SEARCH: Properly normalize version string before parsing it (hannsen)
|
||||||
|
- WINDOWS: Turn on Control Flow Guard for MSVC builds (Chocobo1)
|
||||||
|
- MACOS: Replace deprecated function IOPMAssertionCreate() on macOS (Chocobo1)
|
||||||
|
- OTHER: Fix CMake build with QtSingleApplication. Fixes #9196 (Eugene Shalygin)
|
||||||
|
|
||||||
|
* Sun May 27 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.1
|
||||||
|
- FEATURE: Add 'Moving' state for torrents being relocated/moved (sledgehammer999)
|
||||||
|
- FEATURE: Show rechecking progress (sledgehammer999)
|
||||||
|
- FEATURE: Add option to remember last used save path (glassez)
|
||||||
|
- FEATURE: Torrent name is also renamed if the content was renamed in the "Add New Torrent" dialog (glassez)
|
||||||
|
- FEATURE: Relax behavior of "Download first and last piece first". It applies to all files and not only to the previewable. (Chocobo1)
|
||||||
|
- BUGFIX: Fix issues with translatable strings (Chocobo1)
|
||||||
|
- BUGFIX: Fix displayed tracker messages (Chocobo1)
|
||||||
|
- BUGFIX: Make settings file recovery more robust (Chocobo1)
|
||||||
|
- BUGFIX: Retry saving settings when operation failed (Chocobo1)
|
||||||
|
- BUGFIX: Log successful torrent move (sledgehammer999)
|
||||||
|
- BUGFIX: Fix deletion of old logs (sledgehammer999)
|
||||||
|
- BUGFIX: Delete non-commited fastresume files (sledgehammer999)
|
||||||
|
- BUGFIX: Don't migrate torrents that have newer fastresumes (sledgehammer999)
|
||||||
|
- BUGFIX: Fix adding multiple torrents at once from WebUI (glassez)
|
||||||
|
- BUGFIX: Improve "Run External Program" behavior. On Windows, a backslash isn't appended to paths from path variables (Chocobo1)
|
||||||
|
- BUGFIX: Suppress multiple I/O errors for the same torrent (sledgehammer999)
|
||||||
|
- BUGFIX: Replace raster qbt logo with vector version (Chocobo1)
|
||||||
|
- WEBUI: Fix wrong API method names (glassez)
|
||||||
|
- WEBUI: Filter torrent info endpoint by hashes (Marcel Petersen)
|
||||||
|
- WEBUI: Fix invalid API calls in WebUI (glassez)
|
||||||
|
- WEBUI: Improve legacy API params handling (glassez)
|
||||||
|
- WEBUI: Fix params handling for some legacy API methods (glassez)
|
||||||
|
- WEBUI: Apply locale changes immediately in WebUI (Chocobo1)
|
||||||
|
- WEBUI: Use 32px icons for favicon (Chocobo1)
|
||||||
|
- WEBUI/RSS: Properly set RSS settings via API (glassez)
|
||||||
|
- RSS: Fix auto-downloading rule when Smart filter with regular Episode filter are used (glassez)
|
||||||
|
- RSS: Make "Ignoring days" to behave like other filters (glassez)
|
||||||
|
- RSS: Place "Use Smart Episode Filter" more correctly (glassez)
|
||||||
|
- RSS: Use RSS feed update time as a fallback (glassez)
|
||||||
|
- COSMETIC: Fix Stats dialog size (sledgehammer999)
|
||||||
|
- MACOS: Fix GUI scaling factor on macOS (Chocobo1)
|
||||||
|
- WINDOWS: Update icons (adem4ik)
|
||||||
|
- LINUX: Fix open destination folder with Nautilus > 3.28 (Evgeny Lensky)
|
||||||
|
- OTHER: Code improvements and refactoring (thalieht, Nick Korotysh, Chocobo1)
|
||||||
|
|
||||||
* Sat May 05 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.0
|
* Sat May 05 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.0
|
||||||
- FEATURE: Add "Coalesce reads & writes" checkbox in advanced options (Chocobo1)
|
- FEATURE: Add "Coalesce reads & writes" checkbox in advanced options (Chocobo1)
|
||||||
- FEATURE: Smart Filter for RSS (Stephen Dawkins)
|
- FEATURE: Smart Filter for RSS (Stephen Dawkins)
|
||||||
|
|||||||
22
cmake/Modules/CompileFeature.cmake
Normal file
22
cmake/Modules/CompileFeature.cmake
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Helper function for coupling add_feature_info(), option(), and add_definitions()
|
||||||
|
|
||||||
|
function(optional_compile_definitions _name)
|
||||||
|
set(options FEATURE)
|
||||||
|
set(oneValueArgs DESCRIPTION DEFAULT)
|
||||||
|
set(multiValueArgs ENABLED DISABLED)
|
||||||
|
cmake_parse_arguments(OCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
option(${_name} "${OCD_DESCRIPTION}" ${OCD_DEFAULT})
|
||||||
|
if (${${_name}})
|
||||||
|
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY COMPILE_DEFINITIONS ${OCD_ENABLED})
|
||||||
|
else()
|
||||||
|
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY COMPILE_DEFINITIONS ${OCD_DISABLED})
|
||||||
|
endif()
|
||||||
|
if(${OCD_FEATURE})
|
||||||
|
add_feature_info(${_name} ${_name} "${OCD_DESCRIPTION}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
macro(feature_option _name _description _default)
|
||||||
|
option(${_name} "${_description}" ${_default})
|
||||||
|
add_feature_info(${_name} ${_name} "${_description}")
|
||||||
|
endmacro()
|
||||||
@@ -99,6 +99,7 @@ list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRaster
|
|||||||
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
|
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
|
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
|
||||||
|
list(APPEND LibtorrentRasterbar_INCLUDE_DIRS "${OPENSSL_INCLUDE_DIR}")
|
||||||
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
|
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -113,10 +114,10 @@ mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY
|
|||||||
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
|
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
|
||||||
LibtorrentRasterbar_ENCRYPTION_INDEX)
|
LibtorrentRasterbar_ENCRYPTION_INDEX)
|
||||||
|
|
||||||
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::LibTorrent)
|
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::torrent-rasterbar)
|
||||||
add_library(LibtorrentRasterbar::LibTorrent UNKNOWN IMPORTED)
|
add_library(LibtorrentRasterbar::torrent-rasterbar UNKNOWN IMPORTED)
|
||||||
|
|
||||||
set_target_properties(LibtorrentRasterbar::LibTorrent PROPERTIES
|
set_target_properties(LibtorrentRasterbar::torrent-rasterbar PROPERTIES
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
|
IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
|
INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
|
||||||
|
|||||||
@@ -1,94 +1,79 @@
|
|||||||
# - Try to find the QtSingleApplication includes and library
|
# - Try to find the QtSingleApplication includes and library
|
||||||
# which defines
|
# which defines
|
||||||
#
|
#
|
||||||
# QTSINGLEAPPLICATION_FOUND - system has QtSingleApplication
|
# QtSingleApplication_FOUND - system has QtSingleApplication
|
||||||
# QTSINGLEAPPLICATION_INCLUDE_DIR - where to find header QtSingleApplication
|
# QtSingleApplication_INCLUDE_DIR - where to find header QtSingleApplication
|
||||||
# QTSINGLEAPPLICATION_LIBRARIES - the libraries to link against to use QtSingleApplication
|
# QtSingleApplication_LIBRARIES - the libraries to link against to use QtSingleApplication
|
||||||
# QTSINGLEAPPLICATION_LIBRARY - where to find the QtSingleApplication library (not for general use)
|
# QtSingleApplication_LIBRARY - where to find the QtSingleApplication library (not for general use)
|
||||||
|
|
||||||
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
|
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
|
||||||
#
|
#
|
||||||
# Redistribution and use is allowed according to the terms of the FreeBSD license.
|
# Redistribution and use is allowed according to the terms of the FreeBSD license.
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_FOUND FALSE)
|
SET(QtSingleApplication_FOUND FALSE)
|
||||||
|
|
||||||
IF(QT4_FOUND)
|
if (Qt5Widgets_FOUND)
|
||||||
message(STATUS "Looking for Qt4 single application library")
|
set(_includeFileName qtsingleapplication.h)
|
||||||
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication
|
else()
|
||||||
# standard locations
|
set(_includeFileName qtsinglecoreapplication.h)
|
||||||
/usr/include
|
endif()
|
||||||
/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}
|
FOREACH(TOP_INCLUDE_PATH in ${Qt5Core_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
|
||||||
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
FIND_PATH(QtSingleApplication_INCLUDE_DIR ${_includeFileName} ${TOP_INCLUDE_PATH}/QtSolutions)
|
||||||
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
|
|
||||||
NAMES ${QTSINGLEAPPLICATION_NAMES}
|
|
||||||
PATHS ${QT_LIBRARY_DIR}
|
|
||||||
)
|
|
||||||
ELSEIF(Qt5Core_FOUND)
|
|
||||||
message(STATUS "Looking for Qt5 single application library")
|
|
||||||
FOREACH(TOP_INCLUDE_PATH in ${Qt5Core_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
|
|
||||||
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication ${TOP_INCLUDE_PATH}/QtSolutions)
|
|
||||||
|
|
||||||
IF(QTSINGLEAPPLICATION_INCLUDE_DIR)
|
IF(QtSingleApplication_INCLUDE_DIR)
|
||||||
BREAK()
|
BREAK()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ENDFOREACH()
|
ENDFOREACH()
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
|
SET(QtSingleApplication_NAMES ${QtSingleApplication_NAMES}
|
||||||
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
|
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
|
||||||
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
||||||
GET_TARGET_PROPERTY(_QT5_CORELIBRARY Qt5::Core LOCATION)
|
GET_TARGET_PROPERTY(_QT5_CORELIBRARY Qt5::Core LOCATION)
|
||||||
GET_FILENAME_COMPONENT(_QT5_CORELIBRARYPATH ${_QT5_CORELIBRARY} PATH)
|
GET_FILENAME_COMPONENT(_QT5_CORELIBRARYPATH ${_QT5_CORELIBRARY} PATH)
|
||||||
|
|
||||||
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
|
FIND_LIBRARY(QtSingleApplication_LIBRARY
|
||||||
NAMES ${QTSINGLEAPPLICATION_NAMES}
|
NAMES ${QtSingleApplication_NAMES}
|
||||||
PATHS ${_QT5_CORELIBRARYPATH}
|
PATHS ${_QT5_CORELIBRARYPATH}
|
||||||
)
|
)
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
|
IF (QtSingleApplication_LIBRARY AND QtSingleApplication_INCLUDE_DIR)
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_LIBRARIES ${QTSINGLEAPPLICATION_LIBRARY})
|
SET(QtSingleApplication_LIBRARIES ${QtSingleApplication_LIBRARY})
|
||||||
SET(QTSINGLEAPPLICATION_FOUND TRUE)
|
SET(QtSingleApplication_FOUND TRUE)
|
||||||
|
|
||||||
IF (CYGWIN)
|
IF (CYGWIN)
|
||||||
IF(BUILD_SHARED_LIBS)
|
IF(BUILD_SHARED_LIBS)
|
||||||
# No need to define QTSINGLEAPPLICATION_USE_DLL here, because it's default for Cygwin.
|
# No need to define QtSingleApplication_USE_DLL here, because it's default for Cygwin.
|
||||||
ELSE(BUILD_SHARED_LIBS)
|
ELSE(BUILD_SHARED_LIBS)
|
||||||
SET (QTSINGLEAPPLICATION_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
|
SET (QtSingleApplication_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
|
||||||
ENDIF(BUILD_SHARED_LIBS)
|
ENDIF(BUILD_SHARED_LIBS)
|
||||||
ENDIF (CYGWIN)
|
ENDIF (CYGWIN)
|
||||||
|
|
||||||
ENDIF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
|
ENDIF (QtSingleApplication_LIBRARY AND QtSingleApplication_INCLUDE_DIR)
|
||||||
|
|
||||||
IF (QTSINGLEAPPLICATION_FOUND)
|
IF (QtSingleApplication_FOUND)
|
||||||
IF (NOT QtSingleApplication_FIND_QUIETLY)
|
IF (NOT QtSingleApplication_FIND_QUIETLY)
|
||||||
MESSAGE(STATUS "Found QtSingleApplication: ${QTSINGLEAPPLICATION_LIBRARY}")
|
MESSAGE(STATUS "Found QtSingleApplication: ${QtSingleApplication_LIBRARY}")
|
||||||
MESSAGE(STATUS " includes: ${QTSINGLEAPPLICATION_INCLUDE_DIR}")
|
MESSAGE(STATUS " includes: ${QtSingleApplication_INCLUDE_DIR}")
|
||||||
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
|
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
|
||||||
ELSE (QTSINGLEAPPLICATION_FOUND)
|
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)
|
||||||
|
|
||||||
|
ELSE (QtSingleApplication_FOUND)
|
||||||
IF (QtSingleApplication_FIND_REQUIRED)
|
IF (QtSingleApplication_FIND_REQUIRED)
|
||||||
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
|
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
|
||||||
ENDIF (QtSingleApplication_FIND_REQUIRED)
|
ENDIF (QtSingleApplication_FIND_REQUIRED)
|
||||||
ENDIF (QTSINGLEAPPLICATION_FOUND)
|
ENDIF (QtSingleApplication_FOUND)
|
||||||
|
|
||||||
MARK_AS_ADVANCED(QTSINGLEAPPLICATION_INCLUDE_DIR QTSINGLEAPPLICATION_LIBRARY)
|
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)
|
|
||||||
|
|||||||
@@ -11,18 +11,17 @@ macro(qbt_set_compiler_options)
|
|||||||
#-Wshadow -Wconversion ?
|
#-Wshadow -Wconversion ?
|
||||||
set(_GCC_COMMON_C_AND_CXX_FLAGS "-Wall -Wextra"
|
set(_GCC_COMMON_C_AND_CXX_FLAGS "-Wall -Wextra"
|
||||||
"-Wfloat-equal -Wcast-qual -Wcast-align"
|
"-Wfloat-equal -Wcast-qual -Wcast-align"
|
||||||
"-Wsign-conversion -Winvalid-pch -Werror=return-type -Wno-long-long"
|
"-Wsign-conversion -Winvalid-pch -Wno-long-long"
|
||||||
# -fstack-protector-all
|
#"-fstack-protector-all"
|
||||||
"-Werror -Wno-error=deprecated-declarations"
|
#"-Werror -Wno-error=deprecated-declarations"
|
||||||
)
|
)
|
||||||
set (_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
|
set(_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
|
||||||
"-Woverloaded-virtual -Wold-style-cast"
|
"-Woverloaded-virtual -Wold-style-cast"
|
||||||
"-Wnon-virtual-dtor -Wfloat-equal -Wcast-qual -Wcast-align"
|
"-Wnon-virtual-dtor -Wfloat-equal -Wcast-qual -Wcast-align"
|
||||||
"-Werror=overloaded-virtual"
|
#"-Weffc++"
|
||||||
# "-Weffc++"
|
#"-Werror -Wno-error=cpp"
|
||||||
"-Werror -Wno-error=cpp"
|
|
||||||
# we should modify code to make these ones obsolete
|
# we should modify code to make these ones obsolete
|
||||||
"-Wno-error=sign-conversion -Wno-error=float-equal"
|
#"-Wno-error=sign-conversion -Wno-error=float-equal"
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
# a helper function which appends source to the main qBt target
|
# a helper function which appends source to the target
|
||||||
# sources file names are relative to the the ${qBittorrent_SOURCE_DIR}
|
# sources file names are relative to the the target source dir
|
||||||
|
|
||||||
function (qbt_target_sources)
|
function (qbt_target_sources _target _scope)
|
||||||
set (_sources_rel "")
|
get_target_property(targetSourceDir ${_target} SOURCE_DIR)
|
||||||
foreach (_source IN ITEMS ${ARGN})
|
set(sourcesRelative "")
|
||||||
if (IS_ABSOLUTE "${_source}")
|
foreach(source IN ITEMS ${ARGN})
|
||||||
set(source_abs "${_source}")
|
if(IS_ABSOLUTE "${source}")
|
||||||
|
set(sourceAbsolutePath "${source}")
|
||||||
else()
|
else()
|
||||||
get_filename_component(_source_abs "${_source}" ABSOLUTE)
|
get_filename_component(sourceAbsolutePath "${source}" ABSOLUTE)
|
||||||
endif()
|
endif()
|
||||||
file (RELATIVE_PATH _source_rel "${qbt_executable_SOURCE_DIR}" "${_source_abs}")
|
file(RELATIVE_PATH sourceRelativePath "${targetSourceDir}" "${sourceAbsolutePath}")
|
||||||
list (APPEND _sources_rel "${_source_rel}")
|
list(APPEND sourcesRelative "${sourceRelativePath}")
|
||||||
endforeach()
|
endforeach()
|
||||||
target_sources (qBittorrent PRIVATE "${_sources_rel}")
|
target_sources(${_target} ${_scope} "${sourcesRelative}")
|
||||||
endfunction (qbt_target_sources)
|
endfunction(qbt_target_sources)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
if (STACKTRACE_WIN)
|
if (STACKTRACE)
|
||||||
if ("${WINXXBITS}" NOT STREQUAL "Win64")
|
if ("${WINXXBITS}" NOT STREQUAL "Win64")
|
||||||
add_compile_options(-fno-omit-frame-pointer)
|
add_compile_options(-fno-omit-frame-pointer)
|
||||||
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
|
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
|
||||||
link_libraries(libdbghelp -Wl,--export-all-symbols)
|
link_libraries(libdbghelp -Wl,--export-all-symbols)
|
||||||
endif (STACKTRACE_WIN)
|
endif (STACKTRACE)
|
||||||
|
|
||||||
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
|
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
|
||||||
link_libraries(-Wl,--dynamicbase)
|
link_libraries(-Wl,--dynamicbase)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
if (STACKTRACE_WIN)
|
if (STACKTRACE)
|
||||||
if ("${WINXXBITS}" STREQUAL "Win64")
|
if ("${WINXXBITS}" STREQUAL "Win64")
|
||||||
add_compile_options(-Zi)
|
add_compile_options(-Zi)
|
||||||
else ("${WINXXBITS}" STREQUAL "Win64")
|
else ("${WINXXBITS}" STREQUAL "Win64")
|
||||||
@@ -6,7 +6,7 @@ if (STACKTRACE_WIN)
|
|||||||
add_compile_options(-Oy-)
|
add_compile_options(-Oy-)
|
||||||
endif ("${WINXXBITS}" STREQUAL "Win64")
|
endif ("${WINXXBITS}" STREQUAL "Win64")
|
||||||
link_libraries(dbghelp.lib)
|
link_libraries(dbghelp.lib)
|
||||||
endif (STACKTRACE_WIN)
|
endif (STACKTRACE)
|
||||||
|
|
||||||
# Enable Wide characters
|
# Enable Wide characters
|
||||||
add_definitions(-DTORRENT_USE_WPATH)
|
add_definitions(-DTORRENT_USE_WPATH)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ DEFINES += BOOST_USE_WINAPI_VERSION=0x0501
|
|||||||
#DEFINES += BOOST_ASIO_SEPARATE_COMPILATION
|
#DEFINES += BOOST_ASIO_SEPARATE_COMPILATION
|
||||||
# Enable if building against libtorrent 1.0.x (RC_1_0) (dynamic linking)
|
# Enable if building against libtorrent 1.0.x (RC_1_0) (dynamic linking)
|
||||||
#DEFINES += BOOST_ASIO_DYN_LINK
|
#DEFINES += BOOST_ASIO_DYN_LINK
|
||||||
|
# Enable if encountered build error with boost version <= 1.59
|
||||||
|
#DEFINES += BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
|
||||||
# Enable if building against libtorrent 1.1.x (RC_1_1)
|
# Enable if building against libtorrent 1.1.x (RC_1_1)
|
||||||
# built with this flag defined
|
# built with this flag defined
|
||||||
@@ -58,4 +60,10 @@ DEFINES += BOOST_USE_WINAPI_VERSION=0x0501
|
|||||||
#DEFINES += TORRENT_LINKING_SHARED
|
#DEFINES += TORRENT_LINKING_SHARED
|
||||||
|
|
||||||
# Enable stack trace support
|
# Enable stack trace support
|
||||||
CONFIG += strace_win
|
CONFIG += stacktrace
|
||||||
|
|
||||||
|
win32-msvc* {
|
||||||
|
QMAKE_CXXFLAGS += "/guard:cf"
|
||||||
|
QMAKE_LFLAGS += "/guard:cf"
|
||||||
|
QMAKE_LFLAGS_RELEASE += "/OPT:REF /OPT:ICF"
|
||||||
|
}
|
||||||
|
|||||||
58
configure
vendored
58
configure
vendored
@@ -1,6 +1,6 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.0alpha.
|
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.2.
|
||||||
#
|
#
|
||||||
# Report bugs to <bugs.qbittorrent.org>.
|
# Report bugs to <bugs.qbittorrent.org>.
|
||||||
#
|
#
|
||||||
@@ -580,8 +580,8 @@ MAKEFLAGS=
|
|||||||
# Identity of this package.
|
# Identity of this package.
|
||||||
PACKAGE_NAME='qbittorrent'
|
PACKAGE_NAME='qbittorrent'
|
||||||
PACKAGE_TARNAME='qbittorrent'
|
PACKAGE_TARNAME='qbittorrent'
|
||||||
PACKAGE_VERSION='v4.1.0alpha'
|
PACKAGE_VERSION='v4.1.2'
|
||||||
PACKAGE_STRING='qbittorrent v4.1.0alpha'
|
PACKAGE_STRING='qbittorrent v4.1.2'
|
||||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||||
|
|
||||||
@@ -1297,7 +1297,7 @@ if test "$ac_init_help" = "long"; then
|
|||||||
# Omit some internal or obsolete options to make the list less imposing.
|
# Omit some internal or obsolete options to make the list less imposing.
|
||||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||||
cat <<_ACEOF
|
cat <<_ACEOF
|
||||||
\`configure' configures qbittorrent v4.1.0alpha to adapt to many kinds of systems.
|
\`configure' configures qbittorrent v4.1.2 to adapt to many kinds of systems.
|
||||||
|
|
||||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
|
|
||||||
@@ -1368,7 +1368,7 @@ fi
|
|||||||
|
|
||||||
if test -n "$ac_init_help"; then
|
if test -n "$ac_init_help"; then
|
||||||
case $ac_init_help in
|
case $ac_init_help in
|
||||||
short | recursive ) echo "Configuration of qbittorrent v4.1.0alpha:";;
|
short | recursive ) echo "Configuration of qbittorrent v4.1.2:";;
|
||||||
esac
|
esac
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
|
|
||||||
@@ -1503,7 +1503,7 @@ fi
|
|||||||
test -n "$ac_init_help" && exit $ac_status
|
test -n "$ac_init_help" && exit $ac_status
|
||||||
if $ac_init_version; then
|
if $ac_init_version; then
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
qbittorrent configure v4.1.0alpha
|
qbittorrent configure v4.1.2
|
||||||
generated by GNU Autoconf 2.69
|
generated by GNU Autoconf 2.69
|
||||||
|
|
||||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||||
@@ -1642,7 +1642,7 @@ cat >config.log <<_ACEOF
|
|||||||
This file contains any messages produced by compilers while
|
This file contains any messages produced by compilers while
|
||||||
running configure, to aid debugging if configure makes a mistake.
|
running configure, to aid debugging if configure makes a mistake.
|
||||||
|
|
||||||
It was created by qbittorrent $as_me v4.1.0alpha, which was
|
It was created by qbittorrent $as_me v4.1.2, which was
|
||||||
generated by GNU Autoconf 2.69. Invocation command line was
|
generated by GNU Autoconf 2.69. Invocation command line was
|
||||||
|
|
||||||
$ $0 $@
|
$ $0 $@
|
||||||
@@ -3820,7 +3820,7 @@ fi
|
|||||||
|
|
||||||
# Define the identity of the package.
|
# Define the identity of the package.
|
||||||
PACKAGE='qbittorrent'
|
PACKAGE='qbittorrent'
|
||||||
VERSION='v4.1.0alpha'
|
VERSION='v4.1.2'
|
||||||
|
|
||||||
|
|
||||||
cat >>confdefs.h <<_ACEOF
|
cat >>confdefs.h <<_ACEOF
|
||||||
@@ -5014,6 +5014,40 @@ fi
|
|||||||
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
||||||
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
||||||
|
|
||||||
|
# add workaround for problematic boost version
|
||||||
|
ac_ext=cpp
|
||||||
|
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||||
|
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||||
|
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||||
|
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||||
|
|
||||||
|
# taken from ax_boost_base.m4
|
||||||
|
|
||||||
|
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < (106000))]));
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||||
|
|
||||||
|
else
|
||||||
|
QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||||
|
ac_ext=cpp
|
||||||
|
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||||
|
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||||
|
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||||
|
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check whether --with-boost-system was given.
|
# Check whether --with-boost-system was given.
|
||||||
@@ -6140,7 +6174,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by qbittorrent $as_me v4.1.0alpha, which was
|
This file was extended by qbittorrent $as_me v4.1.2, which was
|
||||||
generated by GNU Autoconf 2.69. Invocation command line was
|
generated by GNU Autoconf 2.69. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
CONFIG_FILES = $CONFIG_FILES
|
||||||
@@ -6198,7 +6232,7 @@ _ACEOF
|
|||||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
qbittorrent config.status v4.1.0alpha
|
qbittorrent config.status v4.1.2
|
||||||
configured by $0, generated by GNU Autoconf 2.69,
|
configured by $0, generated by GNU Autoconf 2.69,
|
||||||
with options \\"\$ac_cs_config\\"
|
with options \\"\$ac_cs_config\\"
|
||||||
|
|
||||||
@@ -7455,7 +7489,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by qbittorrent $as_me v4.1.0alpha, which was
|
This file was extended by qbittorrent $as_me v4.1.2, which was
|
||||||
generated by GNU Autoconf 2.69. Invocation command line was
|
generated by GNU Autoconf 2.69. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
CONFIG_FILES = $CONFIG_FILES
|
||||||
@@ -7513,7 +7547,7 @@ _ACEOF
|
|||||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
qbittorrent config.status v4.1.0alpha
|
qbittorrent config.status v4.1.2
|
||||||
configured by $0, generated by GNU Autoconf 2.69,
|
configured by $0, generated by GNU Autoconf 2.69,
|
||||||
with options \\"\$ac_cs_config\\"
|
with options \\"\$ac_cs_config\\"
|
||||||
|
|
||||||
|
|||||||
13
configure.ac
13
configure.ac
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([qbittorrent], [v4.1.0alpha], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
AC_INIT([qbittorrent], [v4.1.2], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
@@ -168,6 +168,17 @@ AX_BOOST_BASE([1.35],
|
|||||||
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
||||||
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
||||||
|
|
||||||
|
# add workaround for problematic boost version
|
||||||
|
AC_LANG_PUSH(C++)
|
||||||
|
# taken from ax_boost_base.m4
|
||||||
|
m4_define([DETECT_BOOST_VERSION_PROGRAM],
|
||||||
|
[AC_LANG_PROGRAM([[#include <boost/version.hpp>]],
|
||||||
|
[[(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));]])])
|
||||||
|
|
||||||
|
AC_COMPILE_IFELSE([DETECT_BOOST_VERSION_PROGRAM(106000)], [],
|
||||||
|
[QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"])
|
||||||
|
AC_LANG_POP([C++])
|
||||||
|
|
||||||
AX_BOOST_SYSTEM()
|
AX_BOOST_SYSTEM()
|
||||||
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
|
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
|
||||||
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
||||||
|
|||||||
2
dist/CMakeLists.txt
vendored
2
dist/CMakeLists.txt
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
find_package(Qt5Widgets ${requiredQtVersion}) # to conditionally install desktop-related files
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
add_subdirectory(mac)
|
add_subdirectory(mac)
|
||||||
else (APPLE)
|
else (APPLE)
|
||||||
|
|||||||
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -45,7 +45,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.1.0</string>
|
<string>4.1.2</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>qBit</string>
|
<string>qBit</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
30
dist/unix/CMakeLists.txt
vendored
30
dist/unix/CMakeLists.txt
vendored
@@ -1,31 +1,37 @@
|
|||||||
if (SYSTEMD)
|
if (NOT Qt5Widgets_FOUND)
|
||||||
find_package(Systemd)
|
feature_option(SYSTEMD "Install systemd service file (headless only)" OFF)
|
||||||
if (SYSTEMD_FOUND)
|
if (SYSTEMD)
|
||||||
|
if (NOT Systemd_SERVICES_INSTALL_DIR)
|
||||||
|
find_package(Systemd)
|
||||||
|
if (NOT Systemd_FOUND)
|
||||||
|
message(FATAL_ERROR "Could not locate systemd services install dir."
|
||||||
|
" Either pass -DSystemd_SERVICES_INSTALL_DIR=/path/to/systemd/services option or install systemd pkg-config")
|
||||||
|
endif(NOT Systemd_FOUND)
|
||||||
|
endif(NOT Systemd_SERVICES_INSTALL_DIR)
|
||||||
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
|
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
|
||||||
configure_file(systemd/qbittorrent-nox@.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service @ONLY)
|
configure_file(systemd/qbittorrent-nox@.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service @ONLY)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service
|
||||||
DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR}
|
DESTINATION ${Systemd_SERVICES_INSTALL_DIR}
|
||||||
COMPONENT data)
|
COMPONENT data)
|
||||||
endif(SYSTEMD_FOUND)
|
endif(SYSTEMD)
|
||||||
endif(SYSTEMD)
|
endif()
|
||||||
|
|
||||||
|
if (Qt5Widgets_FOUND)
|
||||||
if (GUI)
|
|
||||||
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
|
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
|
||||||
else (GUI)
|
else (Qt5Widgets_FOUND)
|
||||||
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
|
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
install(FILES ${MAN_FILES}
|
install(FILES ${MAN_FILES}
|
||||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
||||||
COMPONENT doc)
|
COMPONENT doc)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
install(DIRECTORY menuicons/
|
install(DIRECTORY menuicons/
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
||||||
FILES_MATCHING PATTERN "*.png")
|
FILES_MATCHING PATTERN "*.png")
|
||||||
|
|
||||||
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.desktop
|
install(FILES qbittorrent.desktop
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
||||||
COMPONENT data)
|
COMPONENT data)
|
||||||
|
|
||||||
|
|||||||
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -27,7 +27,7 @@ XPStyle on
|
|||||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||||
|
|
||||||
; Program specific
|
; Program specific
|
||||||
!define PROG_VERSION "4.1.0"
|
!define PROG_VERSION "4.1.2"
|
||||||
|
|
||||||
!define MUI_FINISHPAGE_RUN
|
!define MUI_FINISHPAGE_RUN
|
||||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ TEMPLATE = subdirs
|
|||||||
SUBDIRS += src
|
SUBDIRS += src
|
||||||
|
|
||||||
include(version.pri)
|
include(version.pri)
|
||||||
include(qm_gen.pri)
|
|
||||||
|
|
||||||
# Make target to create release tarball. Use 'make tarball'
|
# Make target to create release tarball. Use 'make tarball'
|
||||||
tarball.commands += rm -fR ../$${PROJECT_NAME}-$${PROJECT_VERSION}/ &&
|
tarball.commands += rm -fR ../$${PROJECT_NAME}-$${PROJECT_VERSION}/ &&
|
||||||
@@ -18,6 +17,10 @@ tarball.commands += rm -fR $${PROJECT_NAME}-$${PROJECT_VERSION}
|
|||||||
|
|
||||||
QMAKE_EXTRA_TARGETS += tarball
|
QMAKE_EXTRA_TARGETS += tarball
|
||||||
|
|
||||||
|
# Translations included here (at top level) is to avoid regenerating the .qm files
|
||||||
|
# every time when src.pro is processed
|
||||||
|
include(src/lang/lang.pri)
|
||||||
|
|
||||||
# For Qt Creator beautifier
|
# For Qt Creator beautifier
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
uncrustify.cfg
|
uncrustify.cfg
|
||||||
|
|||||||
21
qm_gen.pri
21
qm_gen.pri
@@ -1,21 +0,0 @@
|
|||||||
TS_IN = $$fromfile(src/src.pro,TRANSLATIONS)
|
|
||||||
TS_IN_NOEXT = $$replace(TS_IN,".ts","")
|
|
||||||
|
|
||||||
isEmpty(QMAKE_LRELEASE) {
|
|
||||||
win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe
|
|
||||||
else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
|
|
||||||
unix {
|
|
||||||
equals(QT_MAJOR_VERSION, 5) {
|
|
||||||
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease-qt5 }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message("Building translations")
|
|
||||||
for(L,TS_IN_NOEXT) {
|
|
||||||
message("Processing $${L}")
|
|
||||||
system("$$QMAKE_LRELEASE -silent src/$${L}.ts -qm src/$${L}.qm")
|
|
||||||
!exists("src/$${L}.qm"):error("Building translations failed, cannot continue")
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,45 @@
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
set(CMAKE_CXX_STANDARD "11")
|
set(CMAKE_CXX_STANDARD "11")
|
||||||
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
|
|
||||||
|
|
||||||
include(MacroQbtCompilerSettings)
|
include(MacroQbtCompilerSettings)
|
||||||
qbt_set_compiler_options()
|
qbt_set_compiler_options()
|
||||||
|
|
||||||
include(MacroLinkQtComponents)
|
|
||||||
include(QbtTargetSources)
|
include(QbtTargetSources)
|
||||||
|
|
||||||
|
find_package(Boost ${requiredBoostVersion} REQUIRED)
|
||||||
find_package(LibtorrentRasterbar REQUIRED)
|
find_package(LibtorrentRasterbar REQUIRED)
|
||||||
|
|
||||||
# Qt
|
if (Boost_VERSION VERSION_LESS 106000)
|
||||||
list(APPEND QBT_QT_COMPONENTS Core Network Xml)
|
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||||
if (GUI)
|
endif()
|
||||||
list (APPEND QBT_QT_COMPONENTS Gui Svg Widgets)
|
|
||||||
if (WIN32)
|
|
||||||
list (APPEND QBT_QT_COMPONENTS WinExtras)
|
|
||||||
endif(WIN32)
|
|
||||||
if (APPLE)
|
|
||||||
list (APPEND QBT_GUI_OPTIONAL_LINK_LIBRARIES objc)
|
|
||||||
list (APPEND QBT_QT_COMPONENTS MacExtras)
|
|
||||||
endif (APPLE)
|
|
||||||
endif (GUI)
|
|
||||||
if (DBUS)
|
|
||||||
list (APPEND QBT_QT_COMPONENTS DBus)
|
|
||||||
endif (DBUS)
|
|
||||||
find_package(Qt5 5.5.1 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
|
|
||||||
|
|
||||||
if (GUI AND APPLE)
|
find_package(Qt5 ${requiredQtVersion} REQUIRED COMPONENTS Core Network Xml)
|
||||||
# Fix MOC inability to detect macOS. This seems to only affect cmake.
|
find_package(Qt5Widgets ${requiredQtVersion})
|
||||||
# Relevant issue: https://bugreports.qt.io/browse/QTBUG-58325
|
if (Qt5Widgets_FOUND)
|
||||||
set(CMAKE_AUTOMOC_MOC_OPTIONS ${CMAKE_AUTOMOC_MOC_OPTIONS} -DQ_OS_MAC)
|
find_package(Qt5DBus ${requiredQtVersion})
|
||||||
endif ()
|
else()
|
||||||
|
add_definitions(-DDISABLE_GUI)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_package_properties(Qt5Widgets PROPERTIES
|
||||||
|
DESCRIPTION "Set of components for creating classic desktop-style UIs for the Qt5 framework"
|
||||||
|
PURPOSE "Enables qBittorrent GUI. Unneeded for headless configuration."
|
||||||
|
TYPE OPTIONAL
|
||||||
|
)
|
||||||
|
|
||||||
|
set_package_properties(Qt5DBus PROPERTIES
|
||||||
|
DESCRIPTION "Qt5 module for inter-process communication over the D-Bus protocol"
|
||||||
|
PURPOSE "Enables communication with other system components (e.g. notification service) via D-Bus. "
|
||||||
|
TYPE RECOMMENDED
|
||||||
|
)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC True)
|
set(CMAKE_AUTOMOC True)
|
||||||
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
|
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
|
||||||
|
if (APPLE)
|
||||||
|
# Workaround CMake bug (autogen does not pass required parameters to moc)
|
||||||
|
# Relevant issue: https://gitlab.kitware.com/cmake/cmake/issues/18041
|
||||||
|
list(APPEND CMAKE_AUTOMOC_MOC_OPTIONS -DQ_OS_MAC -DQ_OS_DARWIN)
|
||||||
|
endif ()
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
@@ -43,56 +48,34 @@ add_definitions(-DQT_NO_CAST_TO_ASCII)
|
|||||||
# Efficient construction for QString & QByteArray (Qt >= 4.8)
|
# Efficient construction for QString & QByteArray (Qt >= 4.8)
|
||||||
add_definitions(-DQT_USE_QSTRINGBUILDER)
|
add_definitions(-DQT_USE_QSTRINGBUILDER)
|
||||||
|
|
||||||
if (NOT GUI)
|
if (CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||||
add_definitions(-DDISABLE_GUI -DDISABLE_COUNTRIES_RESOLUTION)
|
|
||||||
endif (NOT GUI)
|
|
||||||
|
|
||||||
if (NOT WEBUI)
|
|
||||||
add_definitions(-DDISABLE_WEBUI)
|
|
||||||
endif (NOT WEBUI)
|
|
||||||
|
|
||||||
if (STACKTRACE)
|
|
||||||
add_definitions(-DSTACKTRACE)
|
|
||||||
endif(STACKTRACE)
|
|
||||||
# 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.")
|
message(STATUS "Project is built in DEBUG mode.")
|
||||||
else (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
else()
|
||||||
message(STATUS "Project is built in RELEASE mode.")
|
message(STATUS "Project is built in RELEASE mode.")
|
||||||
message(STATUS "Disabling debug output.")
|
message(STATUS "Disabling debug output.")
|
||||||
add_definitions(-DQT_NO_DEBUG_OUTPUT)
|
add_definitions(-DQT_NO_DEBUG_OUTPUT)
|
||||||
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
endif()
|
||||||
|
|
||||||
set(QBT_USE_GUI ${GUI})
|
|
||||||
set(QBT_USE_WEBUI ${WEBUI})
|
|
||||||
|
|
||||||
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
if (SYSTEM_QTSINGLEAPPLICATION)
|
find_package(QtSingleApplication)
|
||||||
find_package(QtSingleApplication REQUIRED)
|
set_package_properties(QtSingleApplication PROPERTIES
|
||||||
else (SYSTEM_QTSINGLEAPPLICATION)
|
URL "https://code.qt.io/cgit/qt-solutions/qt-solutions.git/"
|
||||||
|
DESCRIPTION "Qt library to start applications only once per user"
|
||||||
|
TYPE RECOMMENDED
|
||||||
|
PURPOSE "Use the system qtsingleapplication library or shipped one otherwise"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT QtSingleApplication_FOUND)
|
||||||
add_subdirectory(app/qtsingleapplication)
|
add_subdirectory(app/qtsingleapplication)
|
||||||
endif (SYSTEM_QTSINGLEAPPLICATION)
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(app)
|
add_subdirectory(app)
|
||||||
add_subdirectory(base)
|
add_subdirectory(base)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
endif (GUI)
|
endif ()
|
||||||
|
|
||||||
if (WEBUI)
|
if (WEBUI)
|
||||||
add_subdirectory(webui)
|
add_subdirectory(webui)
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
project(qbt_executable)
|
add_executable(qBittorrent
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
|
||||||
set(QBT_APP_HEADERS
|
|
||||||
application.h
|
application.h
|
||||||
cmdoptions.h
|
cmdoptions.h
|
||||||
filelogger.h
|
filelogger.h
|
||||||
)
|
upgrade.h
|
||||||
|
|
||||||
set(QBT_APP_SOURCES
|
|
||||||
application.cpp
|
application.cpp
|
||||||
cmdoptions.cpp
|
cmdoptions.cpp
|
||||||
filelogger.cpp
|
filelogger.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_include_directories(qBittorrent PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_link_libraries(qBittorrent
|
||||||
|
PRIVATE
|
||||||
|
qbt_base
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(qBittorrent
|
||||||
|
PROPERTIES
|
||||||
|
AUTOUIC True
|
||||||
|
AUTORCC True
|
||||||
|
MACOSX_BUNDLE True
|
||||||
|
)
|
||||||
|
|
||||||
# translations
|
# translations
|
||||||
file(GLOB QBT_TS_FILES ../lang/*.ts)
|
file(GLOB QBT_TS_FILES ../lang/*.ts)
|
||||||
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
||||||
@@ -22,9 +30,9 @@ set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${QBT_QM
|
|||||||
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
|
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
|
||||||
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
||||||
|
|
||||||
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang.qrc" ABSOLUTE)
|
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang/lang.qrc" ABSOLUTE)
|
||||||
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang.qrc" ABSOLUTE)
|
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang/lang.qrc" ABSOLUTE)
|
||||||
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../" ABSOLUTE)
|
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
||||||
|
|
||||||
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
|
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
|
||||||
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
|
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
|
||||||
@@ -35,8 +43,8 @@ foreach(qm_file ${QBT_QM_FILES})
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
set(QBT_APP_RESOURCES
|
set(QBT_APP_RESOURCES
|
||||||
../icons.qrc
|
../icons/icons.qrc
|
||||||
../searchengine.qrc
|
../searchengine/searchengine.qrc
|
||||||
"${_lang_qrc_dst}"
|
"${_lang_qrc_dst}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,51 +54,41 @@ qt5_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
|
|||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
list (APPEND QBT_APP_SOURCES ../qbittorrent_mingw.rc)
|
target_sources(qBittorrent PRIVATE ../qbittorrent_mingw.rc)
|
||||||
else (MINGW)
|
else (MINGW)
|
||||||
list (APPEND QBT_APP_SOURCES ../qbittorrent.rc)
|
target_sources(qBittorrent PRIVATE ../qbittorrent.rc)
|
||||||
endif (MINGW)
|
endif (MINGW)
|
||||||
list(APPEND QBT_APP_SOURCES ../qbittorrent.exe.manifest)
|
target_sources(qBittorrent PRIVATE ../qbittorrent.exe.manifest)
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
if (STACKTRACE)
|
if (STACKTRACE)
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
list(APPEND QBT_APP_HEADERS stacktrace.h)
|
target_sources(qBittorrent PRIVATE stacktrace.h)
|
||||||
else (UNIX)
|
else (UNIX)
|
||||||
list(APPEND QBT_APP_HEADERS stacktrace_win.h)
|
target_sources(qBittorrent PRIVATE stacktrace_win.h)
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
list(APPEND QBT_APP_HEADERS stacktrace_win_dlg.h)
|
target_sources(qBittorrent PRIVATE stacktracedialog.h)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
endif (UNIX)
|
endif (UNIX)
|
||||||
endif (STACKTRACE)
|
endif (STACKTRACE)
|
||||||
|
|
||||||
# usesystemqtsingleapplication {
|
|
||||||
# nogui {
|
|
||||||
# CONFIG += qtsinglecoreapplication
|
|
||||||
# } else {
|
|
||||||
# CONFIG += qtsingleapplication
|
|
||||||
# }
|
|
||||||
# } else {
|
|
||||||
# nogui {
|
|
||||||
# include(qtsingleapplication/qtsinglecoreapplication.pri)
|
|
||||||
# } else {
|
|
||||||
# include(qtsingleapplication/qtsingleapplication.pri)
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# upgrade code
|
if (Qt5Widgets_FOUND)
|
||||||
list(APPEND QBT_APP_HEADERS upgrade.h)
|
target_link_libraries(qBittorrent PRIVATE qbt_searchengine qbt_gui)
|
||||||
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
|
set_target_properties(qBittorrent
|
||||||
|
PROPERTIES
|
||||||
if (GUI)
|
OUTPUT_NAME qbittorrent
|
||||||
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
|
WIN32_EXECUTABLE True
|
||||||
include_directories(../gui
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/../gui
|
|
||||||
)
|
)
|
||||||
endif (GUI)
|
else(Qt5Widgets_FOUND)
|
||||||
|
set_target_properties(qBittorrent
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME qbittorrent-nox
|
||||||
|
)
|
||||||
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
if (WEBUI)
|
if (WEBUI)
|
||||||
list(APPEND QBT_TARGET_LIBRARIES qbt_webui)
|
target_link_libraries(qBittorrent PRIVATE qbt_webui)
|
||||||
endif (WEBUI)
|
endif (WEBUI)
|
||||||
|
|
||||||
# we have to include resources into the bundle
|
# we have to include resources into the bundle
|
||||||
@@ -142,30 +140,11 @@ if (APPLE)
|
|||||||
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
|
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
add_executable(qBittorrent ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
|
target_sources(qBittorrent PRIVATE ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
|
||||||
if (GUI)
|
|
||||||
set_target_properties(qBittorrent
|
|
||||||
PROPERTIES
|
|
||||||
OUTPUT_NAME qbittorrent
|
|
||||||
WIN32_EXECUTABLE True
|
|
||||||
)
|
|
||||||
else (GUI)
|
|
||||||
set_target_properties(qBittorrent
|
|
||||||
PROPERTIES
|
|
||||||
OUTPUT_NAME qbittorrent-nox
|
|
||||||
)
|
|
||||||
endif (GUI)
|
|
||||||
|
|
||||||
set_target_properties(qBittorrent
|
|
||||||
PROPERTIES
|
|
||||||
AUTOUIC True
|
|
||||||
AUTORCC True
|
|
||||||
MACOSX_BUNDLE True
|
|
||||||
)
|
|
||||||
|
|
||||||
get_target_property(QBT_EXECUTABLE_NAME qBittorrent OUTPUT_NAME)
|
get_target_property(QBT_EXECUTABLE_NAME qBittorrent OUTPUT_NAME)
|
||||||
|
|
||||||
target_link_libraries(qBittorrent ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
|
target_link_libraries(qBittorrent PRIVATE ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(qbt_BUNDLE_NAME ${QBT_EXECUTABLE_NAME})
|
set(qbt_BUNDLE_NAME ${QBT_EXECUTABLE_NAME})
|
||||||
@@ -186,6 +165,7 @@ install(TARGETS qBittorrent
|
|||||||
BUNDLE DESTINATION .
|
BUNDLE DESTINATION .
|
||||||
COMPONENT runtime)
|
COMPONENT runtime)
|
||||||
|
|
||||||
if (GUI AND APPLE)
|
if (Qt5Widgets_FOUND AND APPLE)
|
||||||
|
find_package(Qt5Svg REQUIRED)
|
||||||
include(bundle)
|
include(bundle)
|
||||||
endif (GUI AND APPLE)
|
endif (Qt5Widgets_FOUND AND APPLE)
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ stacktrace {
|
|||||||
else {
|
else {
|
||||||
HEADERS += $$PWD/stacktrace_win.h
|
HEADERS += $$PWD/stacktrace_win.h
|
||||||
!nogui {
|
!nogui {
|
||||||
HEADERS += $$PWD/stacktrace_win_dlg.h
|
HEADERS += $$PWD/stacktracedialog.h
|
||||||
FORMS += $$PWD/stacktrace_win_dlg.ui
|
FORMS += $$PWD/stacktracedialog.ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,18 +31,34 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <memory>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInt>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <memory>
|
||||||
|
#include <Shellapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DISABLE_GUI
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QSessionManager>
|
||||||
|
#include <QSharedMemory>
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include <QFileOpenEvent>
|
||||||
|
#endif // Q_OS_MAC
|
||||||
|
#include "addnewtorrentdialog.h"
|
||||||
|
#include "gui/guiiconprovider.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "shutdownconfirmdialog.h"
|
||||||
|
#else // DISABLE_GUI
|
||||||
|
#include <cstdio>
|
||||||
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/torrenthandle.h"
|
#include "base/bittorrent/torrenthandle.h"
|
||||||
#include "base/iconprovider.h"
|
#include "base/iconprovider.h"
|
||||||
@@ -62,28 +78,6 @@
|
|||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "filelogger.h"
|
#include "filelogger.h"
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <QSessionManager>
|
|
||||||
#include <QSharedMemory>
|
|
||||||
#endif // Q_OS_WIN
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
#include <QFileOpenEvent>
|
|
||||||
#include <QFont>
|
|
||||||
#include <QUrl>
|
|
||||||
#endif // Q_OS_MAC
|
|
||||||
#include "addnewtorrentdialog.h"
|
|
||||||
#include "gui/guiiconprovider.h"
|
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "shutdownconfirmdlg.h"
|
|
||||||
#else // DISABLE_GUI
|
|
||||||
#include <cstdio>
|
|
||||||
#endif // DISABLE_GUI
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <Shellapi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
#include "webui/webui.h"
|
#include "webui/webui.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -93,7 +87,7 @@ namespace
|
|||||||
#define SETTINGS_KEY(name) "Application/" name
|
#define SETTINGS_KEY(name) "Application/" name
|
||||||
|
|
||||||
// FileLogger properties keys
|
// FileLogger properties keys
|
||||||
#define FILELOGGER_SETTINGS_KEY(name) SETTINGS_KEY("FileLogger/") name
|
#define FILELOGGER_SETTINGS_KEY(name) QStringLiteral(SETTINGS_KEY("FileLogger/") name)
|
||||||
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
|
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
|
||||||
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
|
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
|
||||||
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
|
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
|
||||||
@@ -102,13 +96,13 @@ namespace
|
|||||||
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
|
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
|
||||||
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
|
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
|
||||||
|
|
||||||
//just a shortcut
|
// just a shortcut
|
||||||
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
|
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
|
||||||
|
|
||||||
const QString LOG_FOLDER("logs");
|
const QString LOG_FOLDER = QStringLiteral("logs");
|
||||||
const char PARAMS_SEPARATOR[] = "|";
|
const QChar PARAMS_SEPARATOR = '|';
|
||||||
|
|
||||||
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
|
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QStringLiteral("profile");
|
||||||
|
|
||||||
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
||||||
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
||||||
@@ -151,11 +145,11 @@ Application::Application(const QString &id, int &argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
||||||
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
|
connect(this, &QGuiApplication::commitDataRequest, this, &Application::shutdownCleanup, Qt::DirectConnection);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
connect(this, &Application::messageReceived, this, &Application::processMessage);
|
||||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
connect(this, &QCoreApplication::aboutToQuit, this, &Application::cleanup);
|
||||||
|
|
||||||
if (isFileLoggerEnabled())
|
if (isFileLoggerEnabled())
|
||||||
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
||||||
@@ -261,17 +255,17 @@ void Application::setFileLoggerAge(const int value)
|
|||||||
int Application::fileLoggerAgeType() const
|
int Application::fileLoggerAgeType() const
|
||||||
{
|
{
|
||||||
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
|
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
|
||||||
return (val < 0 || val > 2) ? 1 : val;
|
return ((val < 0) || (val > 2)) ? 1 : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setFileLoggerAgeType(const int value)
|
void Application::setFileLoggerAgeType(const int value)
|
||||||
{
|
{
|
||||||
settings()->storeValue(KEY_FILELOGGER_AGETYPE, (value < 0 || value > 2) ? 1 : value);
|
settings()->storeValue(KEY_FILELOGGER_AGETYPE, ((value < 0) || (value > 2)) ? 1 : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processMessage(const QString &message)
|
void Application::processMessage(const QString &message)
|
||||||
{
|
{
|
||||||
QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts);
|
QStringList params = message.split(PARAMS_SEPARATOR, QString::SkipEmptyParts);
|
||||||
// If Application is not running (i.e., other
|
// If Application is not running (i.e., other
|
||||||
// components are not ready) store params
|
// components are not ready) store params
|
||||||
if (m_running)
|
if (m_running)
|
||||||
@@ -290,9 +284,21 @@ void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) c
|
|||||||
std::sort(tags.begin(), tags.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
std::sort(tags.begin(), tags.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||||
program.replace("%G", tags.join(','));
|
program.replace("%G", tags.join(','));
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
const auto chopPathSep = [](const QString &str) -> QString
|
||||||
|
{
|
||||||
|
if (str.endsWith('\\'))
|
||||||
|
return str.mid(0, (str.length() -1));
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
program.replace("%F", chopPathSep(Utils::Fs::toNativePath(torrent->contentPath())));
|
||||||
|
program.replace("%R", chopPathSep(Utils::Fs::toNativePath(torrent->rootPath())));
|
||||||
|
program.replace("%D", chopPathSep(Utils::Fs::toNativePath(torrent->savePath())));
|
||||||
|
#else
|
||||||
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
|
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
|
||||||
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
|
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
|
||||||
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
|
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
|
||||||
|
#endif
|
||||||
program.replace("%C", QString::number(torrent->filesCount()));
|
program.replace("%C", QString::number(torrent->filesCount()));
|
||||||
program.replace("%Z", QString::number(torrent->totalSize()));
|
program.replace("%Z", QString::number(torrent->totalSize()));
|
||||||
program.replace("%T", torrent->currentTracker());
|
program.replace("%T", torrent->currentTracker());
|
||||||
@@ -301,9 +307,7 @@ void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) c
|
|||||||
Logger *logger = Logger::instance();
|
Logger *logger = Logger::instance();
|
||||||
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name(), program));
|
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name(), program));
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_WIN)
|
||||||
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
|
|
||||||
#else
|
|
||||||
std::unique_ptr<wchar_t[]> programWchar(new wchar_t[program.length() + 1] {});
|
std::unique_ptr<wchar_t[]> programWchar(new wchar_t[program.length() + 1] {});
|
||||||
program.toWCharArray(programWchar.get());
|
program.toWCharArray(programWchar.get());
|
||||||
|
|
||||||
@@ -320,18 +324,20 @@ void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) c
|
|||||||
QProcess::startDetached(QString::fromWCharArray(args[0]), argList);
|
QProcess::startDetached(QString::fromWCharArray(args[0]), argList);
|
||||||
|
|
||||||
::LocalFree(args);
|
::LocalFree(args);
|
||||||
|
#else
|
||||||
|
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::sendNotificationEmail(const BitTorrent::TorrentHandle *torrent)
|
void Application::sendNotificationEmail(const BitTorrent::TorrentHandle *torrent)
|
||||||
{
|
{
|
||||||
// Prepare mail content
|
// Prepare mail content
|
||||||
const QString content = tr("Torrent name: %1").arg(torrent->name()) + "\n"
|
const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
|
||||||
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + "\n"
|
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + '\n'
|
||||||
+ tr("Save path: %1").arg(torrent->savePath()) + "\n\n"
|
+ tr("Save path: %1").arg(torrent->savePath()) + "\n\n"
|
||||||
+ tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds")
|
+ tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds")
|
||||||
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
|
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
|
||||||
+ tr("Thank you for using qBittorrent.") + "\n";
|
+ tr("Thank you for using qBittorrent.") + '\n';
|
||||||
|
|
||||||
// Send the notification email
|
// Send the notification email
|
||||||
const Preferences *pref = Preferences::instance();
|
const Preferences *pref = Preferences::instance();
|
||||||
@@ -382,7 +388,7 @@ void Application::allTorrentsFinished()
|
|||||||
// do nothing & skip confirm
|
// do nothing & skip confirm
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!ShutdownConfirmDlg::askForConfirmation(m_window, action)) return;
|
if (!ShutdownConfirmDialog::askForConfirmation(m_window, action)) return;
|
||||||
}
|
}
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
@@ -403,7 +409,7 @@ void Application::allTorrentsFinished()
|
|||||||
|
|
||||||
bool Application::sendParams(const QStringList ¶ms)
|
bool Application::sendParams(const QStringList ¶ms)
|
||||||
{
|
{
|
||||||
return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR)));
|
return sendMessage(params.join(PARAMS_SEPARATOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
// As program parameters, we can get paths or urls.
|
// As program parameters, we can get paths or urls.
|
||||||
@@ -489,8 +495,8 @@ int Application::exec(const QStringList ¶ms)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
BitTorrent::Session::initInstance();
|
BitTorrent::Session::initInstance();
|
||||||
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
|
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &Application::torrentFinished);
|
||||||
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()), Qt::QueuedConnection);
|
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
|
||||||
|
|
||||||
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
Net::GeoIPManager::initInstance();
|
Net::GeoIPManager::initInstance();
|
||||||
@@ -511,7 +517,7 @@ int Application::exec(const QStringList ¶ms)
|
|||||||
|
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
// Display some information to the user
|
// Display some information to the user
|
||||||
const QString mesg = QString("\n******** %1 ********\n").arg(tr("Information"))
|
const QString mesg = QString("\n******** %1 ********\n").arg(tr("Information"))
|
||||||
+ tr("To control qBittorrent, access the Web UI at %1")
|
+ tr("To control qBittorrent, access the Web UI at %1")
|
||||||
@@ -607,7 +613,7 @@ bool Application::notify(QObject *receiver, QEvent *event)
|
|||||||
|
|
||||||
void Application::initializeTranslation()
|
void Application::initializeTranslation()
|
||||||
{
|
{
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
// Load translation
|
// Load translation
|
||||||
QString localeStr = pref->getLocale();
|
QString localeStr = pref->getLocale();
|
||||||
|
|
||||||
@@ -660,7 +666,7 @@ void Application::shutdownCleanup(QSessionManager &manager)
|
|||||||
// According to the qt docs we shouldn't call quit() inside a slot.
|
// According to the qt docs we shouldn't call quit() inside a slot.
|
||||||
// aboutToQuit() is never emitted if the user hits "Cancel" in
|
// aboutToQuit() is never emitted if the user hits "Cancel" in
|
||||||
// the above dialog.
|
// the above dialog.
|
||||||
QTimer::singleShot(0, qApp, SLOT(quit()));
|
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -673,7 +679,7 @@ void Application::cleanup()
|
|||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
if (m_window) {
|
if (m_window) {
|
||||||
// Hide the window and not leave it on screen as
|
// Hide the window and don't leave it on screen as
|
||||||
// unresponsive. Also for Windows take the WinId
|
// unresponsive. Also for Windows take the WinId
|
||||||
// after it's hidden, because hide() may cause a
|
// after it's hidden, because hide() may cause a
|
||||||
// WinId change.
|
// WinId change.
|
||||||
|
|||||||
@@ -40,15 +40,13 @@ typedef QtSingleApplication BaseApplication;
|
|||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QSessionManager;
|
class QSessionManager;
|
||||||
QT_END_NAMESPACE
|
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include "qtsinglecoreapplication.h"
|
#include "qtsinglecoreapplication.h"
|
||||||
typedef QtSingleCoreApplication BaseApplication;
|
typedef QtSingleCoreApplication BaseApplication;
|
||||||
#endif
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
@@ -110,9 +108,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
bool event(QEvent *);
|
bool event(QEvent *) override;
|
||||||
#endif
|
#endif
|
||||||
bool notify(QObject* receiver, QEvent* event) override;
|
bool notify(QObject *receiver, QEvent *event) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -26,8 +26,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
@@ -100,7 +98,7 @@ namespace
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Boolean option.
|
// Boolean option.
|
||||||
class BoolOption: protected Option
|
class BoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr BoolOption(const char *name, char shortcut = 0)
|
constexpr BoolOption(const char *name, char shortcut = 0)
|
||||||
@@ -118,7 +116,7 @@ namespace
|
|||||||
{
|
{
|
||||||
QString val = env.value(envVarName());
|
QString val = env.value(envVarName());
|
||||||
// we accept "1" and "true" (upper or lower cased) as boolean 'true' values
|
// we accept "1" and "true" (upper or lower cased) as boolean 'true' values
|
||||||
return (val == QLatin1String("1") || val.toUpper() == QLatin1String("TRUE"));
|
return ((val == QLatin1String("1")) || (val.toUpper() == QLatin1String("TRUE")));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString usage() const
|
QString usage() const
|
||||||
@@ -137,7 +135,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option with string value. May not have a shortcut
|
// Option with string value. May not have a shortcut
|
||||||
struct StringOption: protected Option
|
struct StringOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr StringOption(const char *name)
|
constexpr StringOption(const char *name)
|
||||||
@@ -184,7 +182,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option with integer value. May not have a shortcut
|
// Option with integer value. May not have a shortcut
|
||||||
class IntOption: protected StringOption
|
class IntOption : protected StringOption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr IntOption(const char *name)
|
constexpr IntOption(const char *name)
|
||||||
@@ -230,7 +228,7 @@ namespace
|
|||||||
|
|
||||||
// Option that is explicitly set to true or false, and whose value is undefined when unspecified.
|
// Option that is explicitly set to true or false, and whose value is undefined when unspecified.
|
||||||
// May not have a shortcut.
|
// May not have a shortcut.
|
||||||
class TriStateBoolOption: protected Option
|
class TriStateBoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr TriStateBoolOption(const char *name, bool defaultValue)
|
constexpr TriStateBoolOption(const char *name, bool defaultValue)
|
||||||
@@ -260,10 +258,10 @@ namespace
|
|||||||
else if (parts.size() == 2) {
|
else if (parts.size() == 2) {
|
||||||
QString val = parts[1];
|
QString val = parts[1];
|
||||||
|
|
||||||
if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
|
if ((val.toUpper() == QLatin1String("TRUE")) || (val == QLatin1String("1"))) {
|
||||||
return TriStateBool::True;
|
return TriStateBool::True;
|
||||||
}
|
}
|
||||||
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
|
else if ((val.toUpper() == QLatin1String("FALSE")) || (val == QLatin1String("0"))) {
|
||||||
return TriStateBool::False;
|
return TriStateBool::False;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,10 +283,10 @@ namespace
|
|||||||
else if (val == QLatin1String("-1")) {
|
else if (val == QLatin1String("-1")) {
|
||||||
return TriStateBool::Undefined;
|
return TriStateBool::Undefined;
|
||||||
}
|
}
|
||||||
else if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
|
else if ((val.toUpper() == QLatin1String("TRUE")) || (val == QLatin1String("1"))) {
|
||||||
return TriStateBool::True;
|
return TriStateBool::True;
|
||||||
}
|
}
|
||||||
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
|
else if ((val.toUpper() == QLatin1String("FALSE")) || (val == QLatin1String("0"))) {
|
||||||
return TriStateBool::False;
|
return TriStateBool::False;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -360,7 +358,7 @@ QStringList QBtCommandLineParameters::paramList() const
|
|||||||
// the user has specified. Here we place special strings that are
|
// the user has specified. Here we place special strings that are
|
||||||
// almost certainly not going to collide with a file path or URL
|
// almost certainly not going to collide with a file path or URL
|
||||||
// specified by the user, and placing them at the beginning of the
|
// specified by the user, and placing them at the beginning of the
|
||||||
// string listr so that they will be processed before the list of
|
// string list so that they will be processed before the list of
|
||||||
// torrent paths or URLs.
|
// torrent paths or URLs.
|
||||||
|
|
||||||
if (!savePath.isEmpty())
|
if (!savePath.isEmpty())
|
||||||
@@ -404,9 +402,9 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||||||
const QString &arg = args[i];
|
const QString &arg = args[i];
|
||||||
|
|
||||||
if ((arg.startsWith("--") && !arg.endsWith(".torrent"))
|
if ((arg.startsWith("--") && !arg.endsWith(".torrent"))
|
||||||
|| (arg.startsWith("-") && (arg.size() == 2))) {
|
|| (arg.startsWith('-') && (arg.size() == 2))) {
|
||||||
// Parse known parameters
|
// Parse known parameters
|
||||||
if ((arg == SHOW_HELP_OPTION)) {
|
if (arg == SHOW_HELP_OPTION) {
|
||||||
result.showHelp = true;
|
result.showHelp = true;
|
||||||
}
|
}
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
@@ -501,7 +499,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
|
|||||||
|
|
||||||
foreach (const QString &word, words.mid(1)) {
|
foreach (const QString &word, words.mid(1)) {
|
||||||
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
|
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
|
||||||
lines.last().append(" " + word);
|
lines.last().append(' ' + word);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lines.append(QString(initialIndentation, ' ') + word);
|
lines.append(QString(initialIndentation, ' ') + word);
|
||||||
@@ -509,7 +507,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.join("\n");
|
return lines.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeUsage(const QString &prgName)
|
QString makeUsage(const QString &prgName)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -26,8 +26,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef APP_OPTIONS_H
|
#ifndef APP_OPTIONS_H
|
||||||
@@ -62,11 +60,11 @@ struct QBtCommandLineParameters
|
|||||||
QStringList paramList() const;
|
QStringList paramList() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandLineParameterError: public std::runtime_error
|
class CommandLineParameterError : public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CommandLineParameterError(const QString &messageForUser);
|
CommandLineParameterError(const QString &messageForUser);
|
||||||
const QString& messageForUser() const;
|
const QString &messageForUser() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString m_messageForUser;
|
const QString m_messageForUser;
|
||||||
|
|||||||
@@ -26,11 +26,13 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "filelogger.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include "filelogger.h"
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
@@ -41,17 +43,17 @@ FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize
|
|||||||
{
|
{
|
||||||
m_flusher.setInterval(0);
|
m_flusher.setInterval(0);
|
||||||
m_flusher.setSingleShot(true);
|
m_flusher.setSingleShot(true);
|
||||||
connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog()));
|
connect(&m_flusher, &QTimer::timeout, this, &FileLogger::flushLog);
|
||||||
|
|
||||||
changePath(path);
|
changePath(path);
|
||||||
if (deleteOld)
|
if (deleteOld)
|
||||||
this->deleteOld(age, ageType);
|
this->deleteOld(age, ageType);
|
||||||
|
|
||||||
const Logger* const logger = Logger::instance();
|
const Logger *const logger = Logger::instance();
|
||||||
foreach (const Log::Msg& msg, logger->getMessages())
|
foreach (const Log::Msg &msg, logger->getMessages())
|
||||||
addLogMessage(msg);
|
addLogMessage(msg);
|
||||||
|
|
||||||
connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &)));
|
connect(logger, &Logger::newLogMessage, this, &FileLogger::addLogMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileLogger::~FileLogger()
|
FileLogger::~FileLogger()
|
||||||
@@ -61,7 +63,7 @@ FileLogger::~FileLogger()
|
|||||||
delete m_logFile;
|
delete m_logFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLogger::changePath(const QString& newPath)
|
void FileLogger::changePath(const QString &newPath)
|
||||||
{
|
{
|
||||||
QString tmpPath = Utils::Fs::fromNativePath(newPath);
|
QString tmpPath = Utils::Fs::fromNativePath(newPath);
|
||||||
QDir dir(tmpPath);
|
QDir dir(tmpPath);
|
||||||
@@ -83,21 +85,21 @@ void FileLogger::changePath(const QString& newPath)
|
|||||||
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
||||||
{
|
{
|
||||||
QDateTime date = QDateTime::currentDateTime();
|
QDateTime date = QDateTime::currentDateTime();
|
||||||
QDir dir(m_path);
|
QDir dir(Utils::Fs::branchPath(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)) {
|
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
|
||||||
if (file.lastModified() < date)
|
QDateTime modificationDate = file.lastModified();
|
||||||
|
switch (ageType) {
|
||||||
|
case DAYS:
|
||||||
|
modificationDate = modificationDate.addDays(age);
|
||||||
|
break;
|
||||||
|
case MONTHS:
|
||||||
|
modificationDate = modificationDate.addMonths(age);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
modificationDate = modificationDate.addYears(age);
|
||||||
|
}
|
||||||
|
if (modificationDate > date)
|
||||||
break;
|
break;
|
||||||
Utils::Fs::forceRemove(file.absoluteFilePath());
|
Utils::Fs::forceRemove(file.absoluteFilePath());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,4 +76,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILELOGGER_H
|
#endif // FILELOGGER_H
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,8 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -63,14 +61,14 @@ Q_IMPORT_PLUGIN(QICOPlugin)
|
|||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
#else
|
#else
|
||||||
#include "stacktrace_win.h"
|
#include "stacktrace_win.h"
|
||||||
#include "stacktrace_win_dlg.h"
|
#include "stacktracedialog.h"
|
||||||
#endif // Q_OS_UNIX
|
#endif // Q_OS_UNIX
|
||||||
#endif //STACKTRACE
|
#endif //STACKTRACE
|
||||||
|
|
||||||
#include "application.h"
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/preferences.h"
|
#include "application.h"
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
#include "upgrade.h"
|
#include "upgrade.h"
|
||||||
|
|
||||||
@@ -95,7 +93,7 @@ const char *sysSigName[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
void reportToUser(const char* str);
|
void reportToUser(const char *str);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void displayVersion();
|
void displayVersion();
|
||||||
@@ -236,7 +234,7 @@ int main(int argc, char *argv[])
|
|||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
if (params.shouldDaemonize) {
|
if (params.shouldDaemonize) {
|
||||||
app.reset(); // Destroy current application
|
app.reset(); // Destroy current application
|
||||||
if ((daemon(1, 0) == 0)) {
|
if (daemon(1, 0) == 0) {
|
||||||
app.reset(new Application(appId, argc, argv));
|
app.reset(new Application(appId, argc, argv));
|
||||||
if (app->isRunning()) {
|
if (app->isRunning()) {
|
||||||
// Another instance had time to start.
|
// Another instance had time to start.
|
||||||
@@ -269,7 +267,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
void reportToUser(const char* str)
|
void reportToUser(const char *str)
|
||||||
{
|
{
|
||||||
const size_t strLen = strlen(str);
|
const size_t strLen = strlen(str);
|
||||||
if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
|
if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
|
||||||
@@ -308,7 +306,7 @@ void sigAbnormalHandler(int signum)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_WIN
|
#if defined Q_OS_WIN
|
||||||
StraceDlg dlg; // unsafe
|
StacktraceDialog dlg; // unsafe
|
||||||
dlg.setStacktraceString(QLatin1String(sigName), straceWin::getBacktrace());
|
dlg.setStacktraceString(QLatin1String(sigName), straceWin::getBacktrace());
|
||||||
dlg.exec();
|
dlg.exec();
|
||||||
#endif
|
#endif
|
||||||
@@ -321,15 +319,15 @@ void sigAbnormalHandler(int signum)
|
|||||||
#if !defined(DISABLE_GUI)
|
#if !defined(DISABLE_GUI)
|
||||||
void showSplashScreen()
|
void showSplashScreen()
|
||||||
{
|
{
|
||||||
QPixmap splash_img(":/icons/skin/splash.png");
|
QPixmap splashImg(":/icons/skin/splash.png");
|
||||||
QPainter painter(&splash_img);
|
QPainter painter(&splashImg);
|
||||||
QString version = QBT_VERSION;
|
QString version = QBT_VERSION;
|
||||||
painter.setPen(QPen(Qt::white));
|
painter.setPen(QPen(Qt::white));
|
||||||
painter.setFont(QFont("Arial", 22, QFont::Black));
|
painter.setFont(QFont("Arial", 22, QFont::Black));
|
||||||
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
|
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
|
||||||
QSplashScreen *splash = new QSplashScreen(splash_img);
|
QSplashScreen *splash = new QSplashScreen(splashImg);
|
||||||
splash->show();
|
splash->show();
|
||||||
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
|
QTimer::singleShot(1500, splash, &QObject::deleteLater);
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,7 +345,7 @@ void displayVersion()
|
|||||||
printf("%s %s\n", qUtf8Printable(qApp->applicationName()), QBT_VERSION);
|
printf("%s %s\n", qUtf8Printable(qApp->applicationName()), QBT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayBadArgMessage(const QString& message)
|
void displayBadArgMessage(const QString &message)
|
||||||
{
|
{
|
||||||
QString help = QObject::tr("Run application with -h option to read about command line parameters.");
|
QString help = QObject::tr("Run application with -h option to read about command line parameters.");
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@@ -366,7 +364,7 @@ void displayBadArgMessage(const QString& message)
|
|||||||
|
|
||||||
bool userAgreesWithLegalNotice()
|
bool userAgreesWithLegalNotice()
|
||||||
{
|
{
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
if (pref->getAcceptedLegal()) // Already accepted once
|
if (pref->getAcceptedLegal()) // Already accepted once
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -378,7 +376,7 @@ bool userAgreesWithLegalNotice()
|
|||||||
printf("%s", qUtf8Printable(eula));
|
printf("%s", qUtf8Printable(eula));
|
||||||
|
|
||||||
char ret = getchar(); // Read pressed key
|
char ret = getchar(); // Read pressed key
|
||||||
if (ret == 'y' || ret == 'Y') {
|
if ((ret == 'y') || (ret == 'Y')) {
|
||||||
// Save the answer
|
// Save the answer
|
||||||
pref->setAcceptedLegal(true);
|
pref->setAcceptedLegal(true);
|
||||||
return true;
|
return true;
|
||||||
@@ -388,16 +386,16 @@ bool userAgreesWithLegalNotice()
|
|||||||
msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
|
msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
|
||||||
msgBox.setWindowTitle(QObject::tr("Legal notice"));
|
msgBox.setWindowTitle(QObject::tr("Legal notice"));
|
||||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
||||||
QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
if (msgBox.clickedButton() == agree_button) {
|
if (msgBox.clickedButton() == agreeButton) {
|
||||||
// Save the answer
|
// Save the answer
|
||||||
pref->setAcceptedLegal(true);
|
pref->setAcceptedLegal(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,24 @@ set(QBT_QTSINGLEAPPLICATION_SOURCES
|
|||||||
qtlocalpeer.cpp
|
qtlocalpeer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
|
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
|
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
|
||||||
else (GUI)
|
else (Qt5Widgets_FOUND)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
|
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
|
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
|
add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
|
||||||
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
|
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
|
||||||
target_link_qt_components(qtsingleapplication Network)
|
target_link_libraries(qtsingleapplication PRIVATE Qt5::Network)
|
||||||
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
target_compile_options(qtsingleapplication PRIVATE "-w") # disable warning for 3rdparty code
|
target_compile_options(qtsingleapplication PRIVATE "-w") # disable warning for 3rdparty code
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
target_link_qt_components(qtsingleapplication Widgets)
|
target_link_libraries(qtsingleapplication PRIVATE Qt5::Widgets)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)
|
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ bool straceWin::makeRelativePath(const QString& dir, QString& file)
|
|||||||
|
|
||||||
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
|
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
|
||||||
{
|
{
|
||||||
IMAGEHLP_LINE64 line = {0};
|
IMAGEHLP_LINE64 line {};
|
||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
DWORD dwDisplacement = 0;
|
DWORD dwDisplacement = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -27,20 +27,21 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef STACKTRACE_WIN_DLG_H
|
#ifndef STACKTRACEDIALOG_H
|
||||||
#define STACKTRACE_WIN_DLG_H
|
#define STACKTRACEDIALOG_H
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "base/utils/misc.h"
|
|
||||||
#include "ui_stacktrace_win_dlg.h"
|
|
||||||
|
|
||||||
class StraceDlg : public QDialog, private Ui::errorDialog
|
#include "base/utils/misc.h"
|
||||||
|
#include "ui_stacktracedialog.h"
|
||||||
|
|
||||||
|
class StacktraceDialog : public QDialog, private Ui::StacktraceDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StraceDlg(QWidget *parent = nullptr)
|
StacktraceDialog(QWidget *parent = nullptr)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
@@ -83,4 +84,4 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // STACKTRACEDIALOG_H
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>errorDialog</class>
|
<class>StacktraceDialog</class>
|
||||||
<widget class="QDialog" name="errorDialog">
|
<widget class="QDialog" name="StacktraceDialog">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
@@ -29,34 +29,34 @@
|
|||||||
#ifndef UPGRADE_H
|
#ifndef UPGRADE_H
|
||||||
#define UPGRADE_H
|
#define UPGRADE_H
|
||||||
|
|
||||||
#include <libtorrent/version.hpp>
|
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
|
||||||
#include <libtorrent/bdecode.hpp>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/entry.hpp>
|
#include <libtorrent/entry.hpp>
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||||
|
#include <libtorrent/bdecode.hpp>
|
||||||
|
#else
|
||||||
#include <libtorrent/lazy_entry.hpp>
|
#include <libtorrent/lazy_entry.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#endif
|
#endif
|
||||||
#include <QRegExp>
|
|
||||||
#include <QString>
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "base/preferences.h"
|
|
||||||
|
|
||||||
bool userAcceptsUpgrade()
|
bool userAcceptsUpgrade()
|
||||||
{
|
{
|
||||||
@@ -114,12 +114,28 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
|
|||||||
bool v3_3 = false;
|
bool v3_3 = false;
|
||||||
int queuePosition = 0;
|
int queuePosition = 0;
|
||||||
QString outFilePath = filepath;
|
QString outFilePath = filepath;
|
||||||
QRegExp rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$"));
|
static const QRegularExpression rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(.+)$"));
|
||||||
if (rx.indexIn(filepath) != -1) {
|
const QRegularExpressionMatch rxMatch = rx.match(filepath);
|
||||||
// old v3.3.x format
|
if (rxMatch.hasMatch()) {
|
||||||
queuePosition = rx.cap(2).toInt();
|
// Old v3.3.x format had a number at the end indicating the queue position.
|
||||||
|
// The naming scheme was '<infohash>.fastresume.<queueposition>'.
|
||||||
|
// However, QSaveFile, which uses QTemporaryFile internally, might leave
|
||||||
|
// non-commited files behind eg after a crash. These files have the
|
||||||
|
// naming scheme '<infohash>.fastresume.XXXXXX' where each X is a random
|
||||||
|
// character. So we detect if the last part is present. Then check if it
|
||||||
|
// is 6 chars long. If all the 6 chars are digits we assume it is an old
|
||||||
|
// v3.3.x format. Otherwise it is considered a non-commited fastresume
|
||||||
|
// and is deleted, because it may be a corrupted/incomplete fastresume.
|
||||||
|
// NOTE: When the upgrade code is removed, we must continue to perform
|
||||||
|
// cleanup of non-commited QSaveFile/QTemporaryFile fastresumes
|
||||||
|
queuePosition = rxMatch.captured(2).toInt();
|
||||||
|
if ((rxMatch.captured(2).size() == 6) && (queuePosition <= 99999)) {
|
||||||
|
Utils::Fs::forceRemove(filepath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
v3_3 = true;
|
v3_3 = true;
|
||||||
outFilePath.replace(QRegExp("\\.\\d+$"), "");
|
outFilePath.replace(QRegularExpression("\\.fastresume\\..+$"), ".fastresume");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
|
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
|
||||||
@@ -130,6 +146,15 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
|
|||||||
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
|
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
|
||||||
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
|
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
|
||||||
|
|
||||||
|
if (v3_3) {
|
||||||
|
QFileInfo oldFile(filepath);
|
||||||
|
QFileInfo newFile(outFilePath);
|
||||||
|
if (newFile.exists()
|
||||||
|
&& (oldFile.lastModified() < newFile.lastModified())) {
|
||||||
|
Utils::Fs::forceRemove(filepath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
QFile file2(outFilePath);
|
QFile file2(outFilePath);
|
||||||
QVector<char> out;
|
QVector<char> out;
|
||||||
libtorrent::bencode(std::back_inserter(out), fastNew);
|
libtorrent::bencode(std::back_inserter(out), fastNew);
|
||||||
@@ -174,13 +199,15 @@ bool upgrade(bool ask = true)
|
|||||||
|
|
||||||
QStringList backupFiles = backupFolderDir.entryList(
|
QStringList backupFiles = backupFolderDir.entryList(
|
||||||
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||||
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
||||||
foreach (QString backupFile, backupFiles) {
|
foreach (QString backupFile, backupFiles) {
|
||||||
if (rx.indexIn(backupFile) != -1) {
|
const QRegularExpressionMatch rxMatch = rx.match(backupFile);
|
||||||
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash()))
|
if (rxMatch.hasMatch()) {
|
||||||
oldResumeData.remove(rx.cap(1));
|
const QString hashStr = rxMatch.captured(1);
|
||||||
|
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[hashStr].toHash()))
|
||||||
|
oldResumeData.remove(hashStr);
|
||||||
else
|
else
|
||||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(rx.cap(1)), Log::WARNING);
|
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(hashStr), Log::WARNING);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent. Invalid fastresume file name: %1").arg(backupFile), Log::WARNING);
|
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent. Invalid fastresume file name: %1").arg(backupFile), Log::WARNING);
|
||||||
@@ -272,6 +299,6 @@ void migrateRSS()
|
|||||||
qBTRSSLegacy->remove("old_items");
|
qBTRSSLegacy->remove("old_items");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#endif // UPGRADE_H
|
#endif // UPGRADE_H
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
find_package(ZLIB 1.2.5.2 REQUIRED)
|
find_package(ZLIB 1.2.5.2 REQUIRED)
|
||||||
|
|
||||||
set(QBT_BASE_HEADERS
|
add_library(qbt_base STATIC
|
||||||
|
# headers
|
||||||
bittorrent/addtorrentparams.h
|
bittorrent/addtorrentparams.h
|
||||||
bittorrent/cachestatus.h
|
bittorrent/cachestatus.h
|
||||||
bittorrent/infohash.h
|
bittorrent/infohash.h
|
||||||
@@ -48,6 +49,7 @@ search/searchdownloadhandler.h
|
|||||||
search/searchhandler.h
|
search/searchhandler.h
|
||||||
search/searchpluginmanager.h
|
search/searchpluginmanager.h
|
||||||
utils/bytearray.h
|
utils/bytearray.h
|
||||||
|
utils/foreignapps.h
|
||||||
utils/fs.h
|
utils/fs.h
|
||||||
utils/gzip.h
|
utils/gzip.h
|
||||||
utils/misc.h
|
utils/misc.h
|
||||||
@@ -72,9 +74,8 @@ torrentfilter.h
|
|||||||
tristatebool.h
|
tristatebool.h
|
||||||
types.h
|
types.h
|
||||||
unicodestrings.h
|
unicodestrings.h
|
||||||
)
|
|
||||||
|
|
||||||
set(QBT_BASE_SOURCES
|
# sources
|
||||||
bittorrent/infohash.cpp
|
bittorrent/infohash.cpp
|
||||||
bittorrent/magneturi.cpp
|
bittorrent/magneturi.cpp
|
||||||
bittorrent/peerinfo.cpp
|
bittorrent/peerinfo.cpp
|
||||||
@@ -117,6 +118,7 @@ search/searchdownloadhandler.cpp
|
|||||||
search/searchhandler.cpp
|
search/searchhandler.cpp
|
||||||
search/searchpluginmanager.cpp
|
search/searchpluginmanager.cpp
|
||||||
utils/bytearray.cpp
|
utils/bytearray.cpp
|
||||||
|
utils/foreignapps.cpp
|
||||||
utils/fs.cpp
|
utils/fs.cpp
|
||||||
utils/gzip.cpp
|
utils/gzip.cpp
|
||||||
utils/misc.cpp
|
utils/misc.cpp
|
||||||
@@ -137,16 +139,20 @@ torrentfilter.cpp
|
|||||||
tristatebool.cpp
|
tristatebool.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(qbt_base STATIC ${QBT_BASE_HEADERS} ${QBT_BASE_SOURCES})
|
target_link_libraries(qbt_base
|
||||||
target_link_libraries(qbt_base PRIVATE ZLIB::ZLIB PUBLIC LibtorrentRasterbar::LibTorrent)
|
PRIVATE
|
||||||
target_link_qt_components(qbt_base PUBLIC Core Network Xml)
|
ZLIB::ZLIB
|
||||||
|
PUBLIC
|
||||||
|
LibtorrentRasterbar::torrent-rasterbar
|
||||||
|
Qt5::Core Qt5::Network Qt5::Xml
|
||||||
|
)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
target_link_libraries(qbt_base PUBLIC Qt5::Gui Qt5::Widgets)
|
target_link_libraries(qbt_base PUBLIC Qt5::Gui Qt5::Widgets)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
if (DBUS)
|
if (Qt5DBus_FOUND)
|
||||||
target_link_qt_components(qbt_base PRIVATE DBus)
|
target_link_libraries(qbt_base PRIVATE Qt5::DBus)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
|||||||
@@ -34,14 +34,14 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class AsyncFileStorageError: public std::runtime_error
|
class AsyncFileStorageError : public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AsyncFileStorageError(const QString &message);
|
explicit AsyncFileStorageError(const QString &message);
|
||||||
QString message() const;
|
QString message() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncFileStorage: public QObject
|
class AsyncFileStorage : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ HEADERS += \
|
|||||||
$$PWD/types.h \
|
$$PWD/types.h \
|
||||||
$$PWD/unicodestrings.h \
|
$$PWD/unicodestrings.h \
|
||||||
$$PWD/utils/bytearray.h \
|
$$PWD/utils/bytearray.h \
|
||||||
|
$$PWD/utils/foreignapps.h \
|
||||||
$$PWD/utils/fs.h \
|
$$PWD/utils/fs.h \
|
||||||
$$PWD/utils/gzip.h \
|
$$PWD/utils/gzip.h \
|
||||||
$$PWD/utils/misc.h \
|
$$PWD/utils/misc.h \
|
||||||
@@ -127,6 +128,7 @@ SOURCES += \
|
|||||||
$$PWD/torrentfilter.cpp \
|
$$PWD/torrentfilter.cpp \
|
||||||
$$PWD/tristatebool.cpp \
|
$$PWD/tristatebool.cpp \
|
||||||
$$PWD/utils/bytearray.cpp \
|
$$PWD/utils/bytearray.cpp \
|
||||||
|
$$PWD/utils/foreignapps.cpp \
|
||||||
$$PWD/utils/fs.cpp \
|
$$PWD/utils/fs.cpp \
|
||||||
$$PWD/utils/gzip.cpp \
|
$$PWD/utils/gzip.cpp \
|
||||||
$$PWD/utils/misc.cpp \
|
$$PWD/utils/misc.cpp \
|
||||||
|
|||||||
@@ -26,9 +26,10 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
InfoHash::InfoHash()
|
InfoHash::InfoHash()
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
#ifndef BITTORRENT_INFOHASH_H
|
#ifndef BITTORRENT_INFOHASH_H
|
||||||
#define BITTORRENT_INFOHASH_H
|
#define BITTORRENT_INFOHASH_H
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <libtorrent/sha1_hash.hpp>
|
#include <libtorrent/sha1_hash.hpp>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,16 +26,17 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QByteArray>
|
#include "magneturi.h"
|
||||||
#include <QRegExp>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/error_code.hpp>
|
#include <libtorrent/error_code.hpp>
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "magneturi.h"
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -45,7 +46,7 @@ namespace
|
|||||||
rawBc = rawBc.mid(8); // skip bc://bt/
|
rawBc = rawBc.mid(8); // skip bc://bt/
|
||||||
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
|
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
|
||||||
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
|
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
|
||||||
QStringList parts = QString(rawBc).split("/");
|
QStringList parts = QString(rawBc).split('/');
|
||||||
if (parts.size() != 5) return QString();
|
if (parts.size() != 5) return QString();
|
||||||
|
|
||||||
QString filename = parts.at(1);
|
QString filename = parts.at(1);
|
||||||
@@ -69,8 +70,8 @@ MagnetUri::MagnetUri(const QString &source)
|
|||||||
qDebug("Creating magnet link from bc link");
|
qDebug("Creating magnet link from bc link");
|
||||||
m_url = bcLinkToMagnet(source);
|
m_url = bcLinkToMagnet(source);
|
||||||
}
|
}
|
||||||
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|
else if (((source.size() == 40) && !source.contains(QRegularExpression("[^0-9A-Fa-f]")))
|
||||||
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
|
|| ((source.size() == 32) && !source.contains(QRegularExpression("[^2-7A-Za-z]")))) {
|
||||||
m_url = "magnet:?xt=urn:btih:" + source;
|
m_url = "magnet:?xt=urn:btih:" + source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,8 +246,8 @@ QString PeerInfo::connectionType() const
|
|||||||
|
|
||||||
void PeerInfo::calcRelevance(const TorrentHandle *torrent)
|
void PeerInfo::calcRelevance(const TorrentHandle *torrent)
|
||||||
{
|
{
|
||||||
const QBitArray &allPieces = torrent->pieces();
|
const QBitArray allPieces = torrent->pieces();
|
||||||
const QBitArray &peerPieces = pieces();
|
const QBitArray peerPieces = pieces();
|
||||||
|
|
||||||
int localMissing = 0;
|
int localMissing = 0;
|
||||||
int remoteHaves = 0;
|
int remoteHaves = 0;
|
||||||
@@ -377,7 +377,7 @@ void PeerInfo::determineFlags()
|
|||||||
|
|
||||||
// L = Peer is local
|
// L = Peer is local
|
||||||
if (fromLSD()) {
|
if (fromLSD()) {
|
||||||
m_flags += "L";
|
m_flags += 'L';
|
||||||
flagsDescriptionList += "L = "
|
flagsDescriptionList += "L = "
|
||||||
+ tr("peer from LSD");
|
+ tr("peer from LSD");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ void BandwidthScheduler::start()
|
|||||||
|
|
||||||
bool BandwidthScheduler::isTimeForAlternative() const
|
bool BandwidthScheduler::isTimeForAlternative() const
|
||||||
{
|
{
|
||||||
const Preferences* const pref = Preferences::instance();
|
const Preferences *const pref = Preferences::instance();
|
||||||
|
|
||||||
QTime start = pref->getSchedulerStartTime();
|
QTime start = pref->getSchedulerStartTime();
|
||||||
QTime end = pref->getSchedulerEndTime();
|
QTime end = pref->getSchedulerEndTime();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
class BandwidthScheduler: public QObject
|
class BandwidthScheduler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(BandwidthScheduler)
|
Q_DISABLE_COPY(BandwidthScheduler)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libt.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -147,13 +147,13 @@ int FilterParserThread::parseDATFilterFile()
|
|||||||
if (bytesRead < 0)
|
if (bytesRead < 0)
|
||||||
break;
|
break;
|
||||||
int dataSize = bytesRead + offset;
|
int dataSize = bytesRead + offset;
|
||||||
if (bytesRead == 0 && dataSize == 0)
|
if ((bytesRead == 0) && (dataSize == 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (start = 0; start < dataSize; ++start) {
|
for (start = 0; start < dataSize; ++start) {
|
||||||
endOfLine = -1;
|
endOfLine = -1;
|
||||||
// The file might have ended without the last line having a newline
|
// The file might have ended without the last line having a newline
|
||||||
if (!(bytesRead == 0 && dataSize > 0)) {
|
if (!((bytesRead == 0) && (dataSize > 0))) {
|
||||||
for (int i = start; i < dataSize; ++i) {
|
for (int i = start; i < dataSize; ++i) {
|
||||||
if (buffer[i] == '\n') {
|
if (buffer[i] == '\n') {
|
||||||
endOfLine = i;
|
endOfLine = i;
|
||||||
@@ -295,13 +295,13 @@ int FilterParserThread::parseP2PFilterFile()
|
|||||||
if (bytesRead < 0)
|
if (bytesRead < 0)
|
||||||
break;
|
break;
|
||||||
int dataSize = bytesRead + offset;
|
int dataSize = bytesRead + offset;
|
||||||
if (bytesRead == 0 && dataSize == 0)
|
if ((bytesRead == 0) && (dataSize == 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (start = 0; start < dataSize; ++start) {
|
for (start = 0; start < dataSize; ++start) {
|
||||||
endOfLine = -1;
|
endOfLine = -1;
|
||||||
// The file might have ended without the last line having a newline
|
// The file might have ended without the last line having a newline
|
||||||
if (!(bytesRead == 0 && dataSize > 0)) {
|
if (!((bytesRead == 0) && (dataSize > 0))) {
|
||||||
for (int i = start; i < dataSize; ++i) {
|
for (int i = start; i < dataSize; ++i) {
|
||||||
if (buffer[i] == '\n') {
|
if (buffer[i] == '\n') {
|
||||||
endOfLine = i;
|
endOfLine = i;
|
||||||
@@ -407,24 +407,24 @@ int FilterParserThread::parseP2PFilterFile()
|
|||||||
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
|
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
int total_read = 0;
|
int totalRead = 0;
|
||||||
int read;
|
int read;
|
||||||
do {
|
do {
|
||||||
read = stream.readRawData(&c, 1);
|
read = stream.readRawData(&c, 1);
|
||||||
total_read += read;
|
totalRead += read;
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
if (c != delim) {
|
if (c != delim) {
|
||||||
name += c;
|
name += c;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Delim found
|
// Delim found
|
||||||
return total_read;
|
return totalRead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(read > 0);
|
while (read > 0);
|
||||||
|
|
||||||
return total_read;
|
return totalRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser for PeerGuardian ip filter in p2p format
|
// Parser for PeerGuardian ip filter in p2p format
|
||||||
@@ -455,7 +455,7 @@ int FilterParserThread::parseP2BFilterFile()
|
|||||||
unsigned int start, end;
|
unsigned int start, end;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
while(getlineInStream(stream, name, '\0') && !m_abort) {
|
while (getlineInStream(stream, name, '\0') && !m_abort) {
|
||||||
if (!stream.readRawData(reinterpret_cast<char*>(&start), sizeof(start))
|
if (!stream.readRawData(reinterpret_cast<char*>(&start), sizeof(start))
|
||||||
|| !stream.readRawData(reinterpret_cast<char*>(&end), sizeof(end))) {
|
|| !stream.readRawData(reinterpret_cast<char*>(&end), sizeof(end))) {
|
||||||
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
@@ -610,7 +610,7 @@ int FilterParserThread::findAndNullDelimiter(char *const data, char delimiter, i
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FilterParserThread::trim(char* const data, int start, int end)
|
int FilterParserThread::trim(char *const data, int start, int end)
|
||||||
{
|
{
|
||||||
if (start >= end) return start;
|
if (start >= end) return start;
|
||||||
int newStart = start;
|
int newStart = start;
|
||||||
|
|||||||
@@ -26,12 +26,13 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "resumedatasavingmanager.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "resumedatasavingmanager.h"
|
|
||||||
|
|
||||||
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
|
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
|
||||||
: m_resumeDataDir(resumeFolderPath)
|
: m_resumeDataDir(resumeFolderPath)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Statistics::Statistics(Session *session)
|
|||||||
, m_dirty(false)
|
, m_dirty(false)
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather()));
|
connect(&m_timer, &QTimer::timeout, this, &Statistics::gather);
|
||||||
m_timer.start(60 * 1000);
|
m_timer.start(60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace BitTorrent
|
|||||||
class Session;
|
class Session;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Statistics : QObject
|
class Statistics : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(Statistics)
|
Q_DISABLE_COPY(Statistics)
|
||||||
@@ -28,7 +28,6 @@ private:
|
|||||||
void save() const;
|
void save() const;
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
private:
|
|
||||||
BitTorrent::Session *m_session;
|
BitTorrent::Session *m_session;
|
||||||
// Will overflow at 15.9 EiB
|
// Will overflow at 15.9 EiB
|
||||||
quint64 m_alltimeUL;
|
quint64 m_alltimeUL;
|
||||||
|
|||||||
@@ -35,23 +35,19 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QNetworkAddressEntry>
|
#include <QNetworkAddressEntry>
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QRegExp>
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include <libtorrent/alert_types.hpp>
|
#include <libtorrent/alert_types.hpp>
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
|
||||||
#include <libtorrent/bdecode.hpp>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/disk_io_thread.hpp>
|
#include <libtorrent/disk_io_thread.hpp>
|
||||||
#include <libtorrent/error_code.hpp>
|
#include <libtorrent/error_code.hpp>
|
||||||
@@ -60,17 +56,18 @@
|
|||||||
#include <libtorrent/extensions/smart_ban.hpp>
|
#include <libtorrent/extensions/smart_ban.hpp>
|
||||||
#include <libtorrent/identify_client.hpp>
|
#include <libtorrent/identify_client.hpp>
|
||||||
#include <libtorrent/ip_filter.hpp>
|
#include <libtorrent/ip_filter.hpp>
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
|
||||||
#include <libtorrent/lazy_entry.hpp>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
|
||||||
#include <libtorrent/session_stats.hpp>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/session_status.hpp>
|
#include <libtorrent/session_status.hpp>
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
|
#include <libtorrent/lazy_entry.hpp>
|
||||||
|
#else
|
||||||
|
#include <libtorrent/bdecode.hpp>
|
||||||
|
#include <libtorrent/session_stats.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "base/algorithm.h"
|
#include "base/algorithm.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/net/downloadhandler.h"
|
#include "base/net/downloadhandler.h"
|
||||||
@@ -96,6 +93,7 @@
|
|||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#include <wincrypt.h>
|
||||||
#include <iphlpapi.h>
|
#include <iphlpapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -113,7 +111,7 @@ using namespace BitTorrent;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
bool readFile(const QString &path, QByteArray &buf);
|
bool readFile(const QString &path, QByteArray &buf);
|
||||||
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri);
|
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &prio, MagnetUri &magnetUri);
|
||||||
|
|
||||||
void torrentQueuePositionUp(const libt::torrent_handle &handle);
|
void torrentQueuePositionUp(const libt::torrent_handle &handle);
|
||||||
void torrentQueuePositionDown(const libt::torrent_handle &handle);
|
void torrentQueuePositionDown(const libt::torrent_handle &handle);
|
||||||
@@ -276,6 +274,7 @@ Session::Session(QObject *parent)
|
|||||||
, m_IPFilterFile(BITTORRENT_SESSION_KEY("IPFilter"))
|
, m_IPFilterFile(BITTORRENT_SESSION_KEY("IPFilter"))
|
||||||
, m_announceToAllTrackers(BITTORRENT_SESSION_KEY("AnnounceToAllTrackers"), false)
|
, m_announceToAllTrackers(BITTORRENT_SESSION_KEY("AnnounceToAllTrackers"), false)
|
||||||
, m_announceToAllTiers(BITTORRENT_SESSION_KEY("AnnounceToAllTiers"), true)
|
, m_announceToAllTiers(BITTORRENT_SESSION_KEY("AnnounceToAllTiers"), true)
|
||||||
|
, m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 4)
|
||||||
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), 64)
|
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), 64)
|
||||||
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
|
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
|
||||||
, m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
|
, m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
|
||||||
@@ -332,7 +331,7 @@ Session::Session(QObject *parent)
|
|||||||
, m_altGlobalUploadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalUPSpeedLimit"), 10, lowerLimited(0))
|
, m_altGlobalUploadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalUPSpeedLimit"), 10, lowerLimited(0))
|
||||||
, m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
|
, m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
|
||||||
, m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
|
, m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
|
||||||
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 3)
|
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 60)
|
||||||
, m_port(BITTORRENT_SESSION_KEY("Port"), 8999)
|
, m_port(BITTORRENT_SESSION_KEY("Port"), 8999)
|
||||||
, m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
|
, m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
|
||||||
, m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
|
, m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
|
||||||
@@ -371,11 +370,16 @@ Session::Session(QObject *parent)
|
|||||||
, m_numResumeData(0)
|
, m_numResumeData(0)
|
||||||
, m_extraLimit(0)
|
, m_extraLimit(0)
|
||||||
, m_useProxy(false)
|
, m_useProxy(false)
|
||||||
|
, m_recentErroredTorrentsTimer(new QTimer(this))
|
||||||
{
|
{
|
||||||
Logger* const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
|
|
||||||
initResumeFolder();
|
initResumeFolder();
|
||||||
|
|
||||||
|
m_recentErroredTorrentsTimer->setSingleShot(true);
|
||||||
|
m_recentErroredTorrentsTimer->setInterval(1000);
|
||||||
|
connect(m_recentErroredTorrentsTimer, &QTimer::timeout, this, [this]() { m_recentErroredTorrents.clear(); });
|
||||||
|
|
||||||
m_seedingLimitTimer = new QTimer(this);
|
m_seedingLimitTimer = new QTimer(this);
|
||||||
m_seedingLimitTimer->setInterval(10000);
|
m_seedingLimitTimer->setInterval(10000);
|
||||||
connect(m_seedingLimitTimer, &QTimer::timeout, this, &Session::processShareLimits);
|
connect(m_seedingLimitTimer, &QTimer::timeout, this, &Session::processShareLimits);
|
||||||
@@ -507,11 +511,6 @@ Session::Session(QObject *parent)
|
|||||||
connect(m_refreshTimer, &QTimer::timeout, this, &Session::refresh);
|
connect(m_refreshTimer, &QTimer::timeout, this, &Session::refresh);
|
||||||
m_refreshTimer->start();
|
m_refreshTimer->start();
|
||||||
|
|
||||||
// Regular saving of fastresume data
|
|
||||||
m_resumeDataTimer = new QTimer(this);
|
|
||||||
m_resumeDataTimer->setInterval(saveResumeDataInterval() * 60 * 1000);
|
|
||||||
connect(m_resumeDataTimer, &QTimer::timeout, this, [this]() { generateResumeData(); });
|
|
||||||
|
|
||||||
m_statistics = new Statistics(this);
|
m_statistics = new Statistics(this);
|
||||||
|
|
||||||
updateSeedingLimitTimer();
|
updateSeedingLimitTimer();
|
||||||
@@ -519,20 +518,29 @@ Session::Session(QObject *parent)
|
|||||||
|
|
||||||
enableTracker(isTrackerEnabled());
|
enableTracker(isTrackerEnabled());
|
||||||
|
|
||||||
connect(Net::ProxyConfigurationManager::instance(), SIGNAL(proxyConfigurationChanged()), SLOT(configureDeferred()));
|
connect(Net::ProxyConfigurationManager::instance(), &Net::ProxyConfigurationManager::proxyConfigurationChanged
|
||||||
|
, this, &Session::configureDeferred);
|
||||||
|
|
||||||
// Network configuration monitor
|
// Network configuration monitor
|
||||||
connect(&m_networkManager, SIGNAL(onlineStateChanged(bool)), SLOT(networkOnlineStateChanged(bool)));
|
connect(&m_networkManager, &QNetworkConfigurationManager::onlineStateChanged, this, &Session::networkOnlineStateChanged);
|
||||||
connect(&m_networkManager, SIGNAL(configurationAdded(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
|
connect(&m_networkManager, &QNetworkConfigurationManager::configurationAdded, this, &Session::networkConfigurationChange);
|
||||||
connect(&m_networkManager, SIGNAL(configurationRemoved(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
|
connect(&m_networkManager, &QNetworkConfigurationManager::configurationRemoved, this, &Session::networkConfigurationChange);
|
||||||
connect(&m_networkManager, SIGNAL(configurationChanged(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
|
connect(&m_networkManager, &QNetworkConfigurationManager::configurationChanged, this, &Session::networkConfigurationChange);
|
||||||
|
|
||||||
m_ioThread = new QThread(this);
|
m_ioThread = new QThread(this);
|
||||||
m_resumeDataSavingManager = new ResumeDataSavingManager(m_resumeFolderPath);
|
m_resumeDataSavingManager = new ResumeDataSavingManager(m_resumeFolderPath);
|
||||||
m_resumeDataSavingManager->moveToThread(m_ioThread);
|
m_resumeDataSavingManager->moveToThread(m_ioThread);
|
||||||
connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
|
connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
m_resumeDataTimer->start();
|
|
||||||
|
// Regular saving of fastresume data
|
||||||
|
m_resumeDataTimer = new QTimer(this);
|
||||||
|
connect(m_resumeDataTimer, &QTimer::timeout, this, [this]() { generateResumeData(); });
|
||||||
|
const uint saveInterval = saveResumeDataInterval();
|
||||||
|
if (saveInterval > 0) {
|
||||||
|
m_resumeDataTimer->setInterval(saveInterval * 60 * 1000);
|
||||||
|
m_resumeDataTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
// initialize PortForwarder instance
|
// initialize PortForwarder instance
|
||||||
Net::PortForwarder::initInstance(m_nativeSession);
|
Net::PortForwarder::initInstance(m_nativeSession);
|
||||||
@@ -680,15 +688,15 @@ QString Session::torrentTempPath(const TorrentInfo &torrentInfo) const
|
|||||||
if ((torrentInfo.filesCount() > 1) && !torrentInfo.hasRootFolder())
|
if ((torrentInfo.filesCount() > 1) && !torrentInfo.hasRootFolder())
|
||||||
return tempPath()
|
return tempPath()
|
||||||
+ QString::fromStdString(torrentInfo.nativeInfo()->orig_files().name())
|
+ QString::fromStdString(torrentInfo.nativeInfo()->orig_files().name())
|
||||||
+ "/";
|
+ '/';
|
||||||
|
|
||||||
return tempPath();
|
return tempPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::isValidCategoryName(const QString &name)
|
bool Session::isValidCategoryName(const QString &name)
|
||||||
{
|
{
|
||||||
QRegExp re(R"(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)");
|
static const QRegularExpression re(R"(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)");
|
||||||
if (!name.isEmpty() && (re.indexIn(name) != 0)) {
|
if (!name.isEmpty() && (name.indexOf(re) != 0)) {
|
||||||
qDebug() << "Incorrect category name:" << name;
|
qDebug() << "Incorrect category name:" << name;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -785,7 +793,7 @@ bool Session::removeCategory(const QString &name)
|
|||||||
bool result = false;
|
bool result = false;
|
||||||
if (isSubcategoriesEnabled()) {
|
if (isSubcategoriesEnabled()) {
|
||||||
// remove subcategories
|
// remove subcategories
|
||||||
const QString test = name + "/";
|
const QString test = name + '/';
|
||||||
Dict::removeIf(m_categories, [this, &test, &result](const QString &category, const QString &)
|
Dict::removeIf(m_categories, [this, &test, &result](const QString &category, const QString &)
|
||||||
{
|
{
|
||||||
if (category.startsWith(test)) {
|
if (category.startsWith(test)) {
|
||||||
@@ -941,7 +949,7 @@ qreal Session::globalMaxRatio() const
|
|||||||
return m_globalMaxRatio;
|
return m_globalMaxRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Torrents will a ratio superior to the given value will
|
// Torrents with a ratio superior to the given value will
|
||||||
// be automatically deleted
|
// be automatically deleted
|
||||||
void Session::setGlobalMaxRatio(qreal ratio)
|
void Session::setGlobalMaxRatio(qreal ratio)
|
||||||
{
|
{
|
||||||
@@ -1005,7 +1013,7 @@ void Session::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1084,7 +1092,7 @@ void Session::processBannedIPs(libt::ip_filter &filter)
|
|||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||||
void Session::adjustLimits(libt::settings_pack &settingsPack)
|
void Session::adjustLimits(libt::settings_pack &settingsPack)
|
||||||
{
|
{
|
||||||
//Internally increase the queue limits to ensure that the magnet is started
|
// Internally increase the queue limits to ensure that the magnet is started
|
||||||
int maxDownloads = maxActiveDownloads();
|
int maxDownloads = maxActiveDownloads();
|
||||||
int maxActive = maxActiveTorrents();
|
int maxActive = maxActiveTorrents();
|
||||||
|
|
||||||
@@ -1181,7 +1189,7 @@ void Session::initMetrics()
|
|||||||
|
|
||||||
void Session::configure(libtorrent::settings_pack &settingsPack)
|
void Session::configure(libtorrent::settings_pack &settingsPack)
|
||||||
{
|
{
|
||||||
Logger* const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QString chosenIP;
|
QString chosenIP;
|
||||||
@@ -1250,7 +1258,7 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
|
|||||||
settingsPack.set_int(libt::settings_pack::allowed_enc_level, libt::settings_pack::pe_rc4);
|
settingsPack.set_int(libt::settings_pack::allowed_enc_level, libt::settings_pack::pe_rc4);
|
||||||
settingsPack.set_bool(libt::settings_pack::prefer_rc4, true);
|
settingsPack.set_bool(libt::settings_pack::prefer_rc4, true);
|
||||||
switch (encryption()) {
|
switch (encryption()) {
|
||||||
case 0: //Enabled
|
case 0: // Enabled
|
||||||
settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_enabled);
|
settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_enabled);
|
||||||
settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_enabled);
|
settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_enabled);
|
||||||
break;
|
break;
|
||||||
@@ -1303,7 +1311,9 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
|
|||||||
settingsPack.set_bool(libt::settings_pack::announce_to_all_trackers, announceToAllTrackers());
|
settingsPack.set_bool(libt::settings_pack::announce_to_all_trackers, announceToAllTrackers());
|
||||||
settingsPack.set_bool(libt::settings_pack::announce_to_all_tiers, announceToAllTiers());
|
settingsPack.set_bool(libt::settings_pack::announce_to_all_tiers, announceToAllTiers());
|
||||||
|
|
||||||
const int cacheSize = (diskCacheSize() > -1) ? diskCacheSize() * 64 : -1;
|
settingsPack.set_int(libt::settings_pack::aio_threads, asyncIOThreads());
|
||||||
|
|
||||||
|
const int cacheSize = (diskCacheSize() > -1) ? (diskCacheSize() * 64) : -1;
|
||||||
settingsPack.set_int(libt::settings_pack::cache_size, cacheSize);
|
settingsPack.set_int(libt::settings_pack::cache_size, cacheSize);
|
||||||
settingsPack.set_int(libt::settings_pack::cache_expiry, diskCacheTTL());
|
settingsPack.set_int(libt::settings_pack::cache_expiry, diskCacheTTL());
|
||||||
qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
|
qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
|
||||||
@@ -1511,12 +1521,12 @@ void Session::configurePeerClasses()
|
|||||||
|
|
||||||
void Session::adjustLimits(libt::session_settings &sessionSettings)
|
void Session::adjustLimits(libt::session_settings &sessionSettings)
|
||||||
{
|
{
|
||||||
//Internally increase the queue limits to ensure that the magnet is started
|
// Internally increase the queue limits to ensure that the magnet is started
|
||||||
int maxDownloads = maxActiveDownloads();
|
int maxDownloads = maxActiveDownloads();
|
||||||
int maxActive = maxActiveTorrents();
|
int maxActive = maxActiveTorrents();
|
||||||
|
|
||||||
sessionSettings.active_downloads = maxDownloads > -1 ? maxDownloads + m_extraLimit : maxDownloads;
|
sessionSettings.active_downloads = (maxDownloads > -1) ? (maxDownloads + m_extraLimit) : maxDownloads;
|
||||||
sessionSettings.active_limit = maxActive > -1 ? maxActive + m_extraLimit : maxActive;
|
sessionSettings.active_limit = (maxActive > -1) ? (maxActive + m_extraLimit) : maxActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::applyBandwidthLimits(libt::session_settings &sessionSettings)
|
void Session::applyBandwidthLimits(libt::session_settings &sessionSettings)
|
||||||
@@ -1535,7 +1545,7 @@ void Session::configure(libtorrent::session_settings &sessionSettings)
|
|||||||
encryptionSettings.allowed_enc_level = libt::pe_settings::rc4;
|
encryptionSettings.allowed_enc_level = libt::pe_settings::rc4;
|
||||||
encryptionSettings.prefer_rc4 = true;
|
encryptionSettings.prefer_rc4 = true;
|
||||||
switch (encryption()) {
|
switch (encryption()) {
|
||||||
case 0: //Enabled
|
case 0: // Enabled
|
||||||
encryptionSettings.out_enc_policy = libt::pe_settings::enabled;
|
encryptionSettings.out_enc_policy = libt::pe_settings::enabled;
|
||||||
encryptionSettings.in_enc_policy = libt::pe_settings::enabled;
|
encryptionSettings.in_enc_policy = libt::pe_settings::enabled;
|
||||||
break;
|
break;
|
||||||
@@ -1590,7 +1600,7 @@ void Session::configure(libtorrent::session_settings &sessionSettings)
|
|||||||
|
|
||||||
sessionSettings.announce_to_all_trackers = announceToAllTrackers();
|
sessionSettings.announce_to_all_trackers = announceToAllTrackers();
|
||||||
sessionSettings.announce_to_all_tiers = announceToAllTiers();
|
sessionSettings.announce_to_all_tiers = announceToAllTiers();
|
||||||
const int cacheSize = (diskCacheSize() > -1) ? diskCacheSize() * 64 : -1;
|
const int cacheSize = (diskCacheSize() > -1) ? (diskCacheSize() * 64) : -1;
|
||||||
sessionSettings.cache_size = cacheSize;
|
sessionSettings.cache_size = cacheSize;
|
||||||
sessionSettings.cache_expiry = diskCacheTTL();
|
sessionSettings.cache_expiry = diskCacheTTL();
|
||||||
qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
|
qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
|
||||||
@@ -1763,7 +1773,7 @@ void Session::enableBandwidthScheduler()
|
|||||||
void Session::populateAdditionalTrackers()
|
void Session::populateAdditionalTrackers()
|
||||||
{
|
{
|
||||||
m_additionalTrackerList.clear();
|
m_additionalTrackerList.clear();
|
||||||
foreach (QString tracker, additionalTrackers().split("\n")) {
|
foreach (QString tracker, additionalTrackers().split('\n')) {
|
||||||
tracker = tracker.trimmed();
|
tracker = tracker.trimmed();
|
||||||
if (!tracker.isEmpty())
|
if (!tracker.isEmpty())
|
||||||
m_additionalTrackerList << tracker;
|
m_additionalTrackerList << tracker;
|
||||||
@@ -1787,7 +1797,7 @@ void Session::processShareLimits()
|
|||||||
qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit);
|
qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit);
|
||||||
|
|
||||||
if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) {
|
if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) {
|
||||||
Logger* const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
if (m_maxRatioAction == Remove) {
|
if (m_maxRatioAction == Remove) {
|
||||||
logger->addMessage(tr("'%1' reached the maximum ratio you set. Removed.").arg(torrent->name()));
|
logger->addMessage(tr("'%1' reached the maximum ratio you set. Removed.").arg(torrent->name()));
|
||||||
deleteTorrent(torrent->hash());
|
deleteTorrent(torrent->hash());
|
||||||
@@ -1812,7 +1822,7 @@ void Session::processShareLimits()
|
|||||||
qDebug("Seeding Time: %d (limit: %d)", seedingTimeInMinutes, seedingTimeLimit);
|
qDebug("Seeding Time: %d (limit: %d)", seedingTimeInMinutes, seedingTimeLimit);
|
||||||
|
|
||||||
if ((seedingTimeInMinutes <= TorrentHandle::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit)) {
|
if ((seedingTimeInMinutes <= TorrentHandle::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit)) {
|
||||||
Logger* const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
if (m_maxRatioAction == Remove) {
|
if (m_maxRatioAction == Remove) {
|
||||||
logger->addMessage(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent->name()));
|
logger->addMessage(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent->name()));
|
||||||
deleteTorrent(torrent->hash());
|
deleteTorrent(torrent->hash());
|
||||||
@@ -1839,11 +1849,10 @@ void Session::handleRedirectedToMagnet(const QString &url, const QString &magnet
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to BitTorrent session the downloaded torrent file
|
// Add to BitTorrent session the downloaded torrent file
|
||||||
void Session::handleDownloadFinished(const QString &url, const QString &filePath)
|
void Session::handleDownloadFinished(const QString &url, const QByteArray &data)
|
||||||
{
|
{
|
||||||
emit downloadFromUrlFinished(url);
|
emit downloadFromUrlFinished(url);
|
||||||
addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(), TorrentInfo::loadFromFile(filePath));
|
addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(), TorrentInfo::load(data));
|
||||||
Utils::Fs::forceRemove(filePath); // remove temporary file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the torrent handle, given its hash
|
// Return the torrent handle, given its hash
|
||||||
@@ -1854,20 +1863,26 @@ TorrentHandle *Session::findTorrent(const InfoHash &hash) const
|
|||||||
|
|
||||||
bool Session::hasActiveTorrents() const
|
bool Session::hasActiveTorrents() const
|
||||||
{
|
{
|
||||||
foreach (TorrentHandle *const torrent, m_torrents)
|
return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentHandle *torrent)
|
||||||
if (TorrentFilter::ActiveTorrent.match(torrent))
|
{
|
||||||
return true;
|
return TorrentFilter::ActiveTorrent.match(torrent);
|
||||||
|
});
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Session::hasUnfinishedTorrents() const
|
bool Session::hasUnfinishedTorrents() const
|
||||||
{
|
{
|
||||||
foreach (TorrentHandle *const torrent, m_torrents)
|
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandle *torrent)
|
||||||
if (!torrent->isSeed() && !torrent->isPaused())
|
{
|
||||||
return true;
|
return (!torrent->isSeed() && !torrent->isPaused());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
bool Session::hasRunningSeed() const
|
||||||
|
{
|
||||||
|
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandle *torrent)
|
||||||
|
{
|
||||||
|
return (torrent->isSeed() && !torrent->isPaused());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::banIP(const QString &ip)
|
void Session::banIP(const QString &ip)
|
||||||
@@ -2072,12 +2087,14 @@ bool Session::addTorrent(QString source, const AddTorrentParams ¶ms)
|
|||||||
return addTorrent_impl(params, magnetUri);
|
return addTorrent_impl(params, magnetUri);
|
||||||
}
|
}
|
||||||
else if (Utils::Misc::isUrl(source)) {
|
else if (Utils::Misc::isUrl(source)) {
|
||||||
Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
|
LogMsg(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
|
||||||
// Launch downloader
|
// Launch downloader
|
||||||
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true);
|
Net::DownloadHandler *handler =
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleDownloadFinished(QString, QString)));
|
Net::DownloadManager::instance()->download(Net::DownloadRequest(source).limit(10485760 /* 10MB */).handleRedirectToMagnet(true));
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailed(QString, QString)));
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString)));
|
, this, &Session::handleDownloadFinished);
|
||||||
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &Session::handleDownloadFailed);
|
||||||
|
connect(handler, &Net::DownloadHandler::redirectedToMagnet, this, &Session::handleRedirectedToMagnet);
|
||||||
m_downloadedTorrents[handler->url()] = params;
|
m_downloadedTorrents[handler->url()] = params;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2099,29 +2116,24 @@ bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a torrent to the BitTorrent session
|
// Add a torrent to the BitTorrent session
|
||||||
bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
|
bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
|
||||||
TorrentInfo torrentInfo, const QByteArray &fastresumeData)
|
TorrentInfo torrentInfo, const QByteArray &fastresumeData)
|
||||||
{
|
{
|
||||||
addData.savePath = normalizeSavePath(addData.savePath, "");
|
params.savePath = normalizeSavePath(params.savePath, "");
|
||||||
|
|
||||||
if (!addData.category.isEmpty()) {
|
if (!params.category.isEmpty()) {
|
||||||
if (!m_categories.contains(addData.category) && !addCategory(addData.category)) {
|
if (!m_categories.contains(params.category) && !addCategory(params.category)) {
|
||||||
qWarning() << "Couldn't create category" << addData.category;
|
qWarning() << "Couldn't create category" << params.category;
|
||||||
addData.category = "";
|
params.category = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If empty then Automatic mode, otherwise Manual mode
|
||||||
|
QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath;
|
||||||
libt::add_torrent_params p;
|
libt::add_torrent_params p;
|
||||||
InfoHash hash;
|
InfoHash hash;
|
||||||
std::vector<boost::uint8_t> filePriorities;
|
const bool fromMagnetUri = magnetUri.isValid();
|
||||||
|
|
||||||
QString savePath;
|
|
||||||
if (addData.savePath.isEmpty()) // using Automatic mode
|
|
||||||
savePath = categorySavePath(addData.category);
|
|
||||||
else // using Manual mode
|
|
||||||
savePath = addData.savePath;
|
|
||||||
|
|
||||||
bool fromMagnetUri = magnetUri.isValid();
|
|
||||||
if (fromMagnetUri) {
|
if (fromMagnetUri) {
|
||||||
hash = magnetUri.hash();
|
hash = magnetUri.hash();
|
||||||
|
|
||||||
@@ -2140,7 +2152,7 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
|||||||
adjustLimits();
|
adjustLimits();
|
||||||
|
|
||||||
// use common 2nd step of torrent addition
|
// use common 2nd step of torrent addition
|
||||||
m_addingTorrents.insert(hash, addData);
|
m_addingTorrents.insert(hash, params);
|
||||||
createTorrentHandle(handle);
|
createTorrentHandle(handle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2148,12 +2160,26 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
|||||||
p = magnetUri.addTorrentParams();
|
p = magnetUri.addTorrentParams();
|
||||||
}
|
}
|
||||||
else if (torrentInfo.isValid()) {
|
else if (torrentInfo.isValid()) {
|
||||||
if (!addData.resumed && !addData.hasRootFolder)
|
if (!params.restored) {
|
||||||
torrentInfo.stripRootFolder();
|
if (!params.hasRootFolder)
|
||||||
|
torrentInfo.stripRootFolder();
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
if (!params.hasSeedStatus)
|
||||||
|
findIncompleteFiles(torrentInfo, savePath);
|
||||||
|
|
||||||
|
// if torrent name wasn't explicitly set we handle the case of
|
||||||
|
// initial renaming of torrent content and rename torrent accordingly
|
||||||
|
if (params.name.isEmpty()) {
|
||||||
|
QString contentName = torrentInfo.rootFolder();
|
||||||
|
if (contentName.isEmpty() && (torrentInfo.filesCount() == 1))
|
||||||
|
contentName = torrentInfo.fileName(0);
|
||||||
|
|
||||||
|
if (!contentName.isEmpty() && (contentName != torrentInfo.name()))
|
||||||
|
params.name = contentName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Metadata
|
|
||||||
if (!addData.resumed && !addData.hasSeedStatus)
|
|
||||||
findIncompleteFiles(torrentInfo, savePath);
|
|
||||||
p.ti = torrentInfo.nativeInfo();
|
p.ti = torrentInfo.nativeInfo();
|
||||||
hash = torrentInfo.hash();
|
hash = torrentInfo.hash();
|
||||||
}
|
}
|
||||||
@@ -2164,23 +2190,21 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addData.resumed && !fromMagnetUri) {
|
if (params.restored && !fromMagnetUri) {
|
||||||
// Set torrent fast resume data
|
// Set torrent fast resume data
|
||||||
p.resume_data = {fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()};
|
p.resume_data = {fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()};
|
||||||
p.flags |= libt::add_torrent_params::flag_use_resume_save_path;
|
p.flags |= libt::add_torrent_params::flag_use_resume_save_path;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
foreach (int prio, addData.filePriorities)
|
p.file_priorities = {params.filePriorities.begin(), params.filePriorities.end()};
|
||||||
filePriorities.push_back(prio);
|
|
||||||
p.file_priorities = filePriorities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should not add torrent if it already
|
// We should not add torrent if it already
|
||||||
// processed or adding to session
|
// processed or adding to session
|
||||||
if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) return false;
|
if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) return false;
|
||||||
|
|
||||||
if (m_torrents.contains(hash)) {
|
TorrentHandle *const torrent = m_torrents.value(hash);
|
||||||
TorrentHandle *const torrent = m_torrents.value(hash);
|
if (torrent) {
|
||||||
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
|
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
|
||||||
return false;
|
return false;
|
||||||
torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers());
|
torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers());
|
||||||
@@ -2203,7 +2227,7 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
|||||||
|
|
||||||
// Seeding mode
|
// Seeding mode
|
||||||
// Skip checking and directly start seeding (new in libtorrent v0.15)
|
// Skip checking and directly start seeding (new in libtorrent v0.15)
|
||||||
if (addData.skipChecking)
|
if (params.skipChecking)
|
||||||
p.flags |= libt::add_torrent_params::flag_seed_mode;
|
p.flags |= libt::add_torrent_params::flag_seed_mode;
|
||||||
else
|
else
|
||||||
p.flags &= ~libt::add_torrent_params::flag_seed_mode;
|
p.flags &= ~libt::add_torrent_params::flag_seed_mode;
|
||||||
@@ -2212,10 +2236,10 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
|||||||
p.max_connections = maxConnectionsPerTorrent();
|
p.max_connections = maxConnectionsPerTorrent();
|
||||||
p.max_uploads = maxUploadsPerTorrent();
|
p.max_uploads = maxUploadsPerTorrent();
|
||||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||||
p.upload_limit = addData.uploadLimit;
|
p.upload_limit = params.uploadLimit;
|
||||||
p.download_limit = addData.downloadLimit;
|
p.download_limit = params.downloadLimit;
|
||||||
|
|
||||||
m_addingTorrents.insert(hash, addData);
|
m_addingTorrents.insert(hash, params);
|
||||||
// Adding torrent to BitTorrent session
|
// Adding torrent to BitTorrent session
|
||||||
m_nativeSession->async_add_torrent(p);
|
m_nativeSession->async_add_torrent(p);
|
||||||
return true;
|
return true;
|
||||||
@@ -2259,7 +2283,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
|
|||||||
InfoHash hash = magnetUri.hash();
|
InfoHash hash = magnetUri.hash();
|
||||||
QString name = magnetUri.name();
|
QString name = magnetUri.name();
|
||||||
|
|
||||||
// We should not add torrent if it already
|
// We should not add torrent if it's already
|
||||||
// processed or adding to session
|
// processed or adding to session
|
||||||
if (m_torrents.contains(hash)) return false;
|
if (m_torrents.contains(hash)) return false;
|
||||||
if (m_addingTorrents.contains(hash)) return false;
|
if (m_addingTorrents.contains(hash)) return false;
|
||||||
@@ -2282,7 +2306,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
|
|||||||
p.max_connections = maxConnectionsPerTorrent();
|
p.max_connections = maxConnectionsPerTorrent();
|
||||||
p.max_uploads = maxUploadsPerTorrent();
|
p.max_uploads = maxUploadsPerTorrent();
|
||||||
|
|
||||||
QString savePath = QString("%1/%2").arg(QDir::tempPath(), hash);
|
const QString savePath = Utils::Fs::tempPath() + static_cast<QString>(hash);
|
||||||
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
|
||||||
|
|
||||||
// Forced start
|
// Forced start
|
||||||
@@ -2332,12 +2356,11 @@ void Session::generateResumeData(bool final)
|
|||||||
{
|
{
|
||||||
foreach (TorrentHandle *const torrent, m_torrents) {
|
foreach (TorrentHandle *const torrent, m_torrents) {
|
||||||
if (!torrent->isValid()) continue;
|
if (!torrent->isValid()) continue;
|
||||||
if (torrent->hasMissingFiles()) continue;
|
if (torrent->isChecking() || torrent->isPaused()) continue;
|
||||||
if (torrent->isChecking() || torrent->hasError()) continue;
|
|
||||||
if (!final && !torrent->needSaveResumeData()) continue;
|
if (!final && !torrent->needSaveResumeData()) continue;
|
||||||
|
if (torrent->hasMissingFiles() || torrent->hasError()) continue;
|
||||||
|
|
||||||
saveTorrentResumeData(torrent, final);
|
saveTorrentResumeData(torrent, final);
|
||||||
qDebug("Saving fastresume data for %s", qUtf8Printable(torrent->name()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2404,7 +2427,7 @@ void Session::networkOnlineStateChanged(const bool online)
|
|||||||
Logger::instance()->addMessage(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
|
Logger::instance()->addMessage(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::networkConfigurationChange(const QNetworkConfiguration& cfg)
|
void Session::networkConfigurationChange(const QNetworkConfiguration &cfg)
|
||||||
{
|
{
|
||||||
const QString configuredInterfaceName = networkInterface();
|
const QString configuredInterfaceName = networkInterface();
|
||||||
// Empty means "Any Interface". In this case libtorrent has binded to 0.0.0.0 so any change to any interface will
|
// Empty means "Any Interface". In this case libtorrent has binded to 0.0.0.0 so any change to any interface will
|
||||||
@@ -2430,7 +2453,7 @@ void Session::networkConfigurationChange(const QNetworkConfiguration& cfg)
|
|||||||
|
|
||||||
const QStringList Session::getListeningIPs()
|
const QStringList Session::getListeningIPs()
|
||||||
{
|
{
|
||||||
Logger* const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
QStringList IPs;
|
QStringList IPs;
|
||||||
|
|
||||||
const QString ifaceName = networkInterface();
|
const QString ifaceName = networkInterface();
|
||||||
@@ -2473,12 +2496,12 @@ const QStringList Session::getListeningIPs()
|
|||||||
ip = entry.ip();
|
ip = entry.ip();
|
||||||
ipString = ip.toString();
|
ipString = ip.toString();
|
||||||
protocol = ip.protocol();
|
protocol = ip.protocol();
|
||||||
Q_ASSERT(protocol == QAbstractSocket::IPv4Protocol || protocol == QAbstractSocket::IPv6Protocol);
|
Q_ASSERT((protocol == QAbstractSocket::IPv4Protocol) || (protocol == QAbstractSocket::IPv6Protocol));
|
||||||
if ((!listenIPv6 && (protocol == QAbstractSocket::IPv6Protocol))
|
if ((!listenIPv6 && (protocol == QAbstractSocket::IPv6Protocol))
|
||||||
|| (listenIPv6 && (protocol == QAbstractSocket::IPv4Protocol)))
|
|| (listenIPv6 && (protocol == QAbstractSocket::IPv4Protocol)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//If an iface address has been defined only allow ip's that match it to go through
|
// If an iface address has been defined to only allow ip's that match it to go through
|
||||||
if (!ifaceAddr.isEmpty()) {
|
if (!ifaceAddr.isEmpty()) {
|
||||||
if (ifaceAddr == ipString) {
|
if (ifaceAddr == ipString) {
|
||||||
IPs.append(ipString);
|
IPs.append(ipString);
|
||||||
@@ -2509,7 +2532,7 @@ void Session::configureListeningInterface()
|
|||||||
const ushort port = this->port();
|
const ushort port = this->port();
|
||||||
qDebug() << Q_FUNC_INFO << port;
|
qDebug() << Q_FUNC_INFO << port;
|
||||||
|
|
||||||
Logger* const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
|
|
||||||
std::pair<int, int> ports(port, port);
|
std::pair<int, int> ports(port, port);
|
||||||
libt::error_code ec;
|
libt::error_code ec;
|
||||||
@@ -2685,11 +2708,19 @@ uint Session::saveResumeDataInterval() const
|
|||||||
return m_saveResumeDataInterval;
|
return m_saveResumeDataInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setSaveResumeDataInterval(uint value)
|
void Session::setSaveResumeDataInterval(const uint value)
|
||||||
{
|
{
|
||||||
if (value != saveResumeDataInterval()) {
|
if (value == m_saveResumeDataInterval)
|
||||||
m_saveResumeDataInterval = value;
|
return;
|
||||||
|
|
||||||
|
m_saveResumeDataInterval = value;
|
||||||
|
|
||||||
|
if (value > 0) {
|
||||||
m_resumeDataTimer->setInterval(value * 60 * 1000);
|
m_resumeDataTimer->setInterval(value * 60 * 1000);
|
||||||
|
m_resumeDataTimer->start();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_resumeDataTimer->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2945,7 +2976,7 @@ void Session::setMaxConnectionsPerTorrent(int max)
|
|||||||
try {
|
try {
|
||||||
handle.set_max_connections(max);
|
handle.set_max_connections(max);
|
||||||
}
|
}
|
||||||
catch (std::exception) {}
|
catch (const std::exception &) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2967,7 +2998,7 @@ void Session::setMaxUploadsPerTorrent(int max)
|
|||||||
try {
|
try {
|
||||||
handle.set_max_uploads(max);
|
handle.set_max_uploads(max);
|
||||||
}
|
}
|
||||||
catch (std::exception) {}
|
catch (const std::exception &) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2998,6 +3029,20 @@ void Session::setAnnounceToAllTiers(bool val)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Session::asyncIOThreads() const
|
||||||
|
{
|
||||||
|
return qBound(1, m_asyncIOThreads.value(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::setAsyncIOThreads(const int num)
|
||||||
|
{
|
||||||
|
if (num == m_asyncIOThreads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_asyncIOThreads = num;
|
||||||
|
configureDeferred();
|
||||||
|
}
|
||||||
|
|
||||||
int Session::diskCacheSize() const
|
int Session::diskCacheSize() const
|
||||||
{
|
{
|
||||||
int size = m_diskCacheSize;
|
int size = m_diskCacheSize;
|
||||||
@@ -3460,7 +3505,7 @@ void Session::setMaxRatioAction(MaxRatioAction act)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If this functions returns true, we cannot add torrent to session,
|
// If this functions returns true, we cannot add torrent to session,
|
||||||
// but it is still possible to merge trackers in some case
|
// but it is still possible to merge trackers in some cases
|
||||||
bool Session::isKnownTorrent(const InfoHash &hash) const
|
bool Session::isKnownTorrent(const InfoHash &hash) const
|
||||||
{
|
{
|
||||||
return (m_torrents.contains(hash)
|
return (m_torrents.contains(hash)
|
||||||
@@ -3482,45 +3527,58 @@ void Session::updateSeedingLimitTimer()
|
|||||||
|
|
||||||
void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent)
|
void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
Q_UNUSED(torrent);
|
saveTorrentResumeData(torrent);
|
||||||
updateSeedingLimitTimer();
|
updateSeedingLimitTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave)
|
void Session::saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave)
|
||||||
{
|
{
|
||||||
|
qDebug("Saving fastresume data for %s", qUtf8Printable(torrent->name()));
|
||||||
torrent->saveResumeData(finalSave);
|
torrent->saveResumeData(finalSave);
|
||||||
++m_numResumeData;
|
++m_numResumeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::handleTorrentNameChanged(TorrentHandle *const torrent)
|
||||||
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
|
}
|
||||||
|
|
||||||
void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent)
|
void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit torrentSavePathChanged(torrent);
|
emit torrentSavePathChanged(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory)
|
void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit torrentCategoryChanged(torrent, oldCategory);
|
emit torrentCategoryChanged(torrent, oldCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag)
|
void Session::handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit torrentTagAdded(torrent, tag);
|
emit torrentTagAdded(torrent, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentTagRemoved(TorrentHandle *const torrent, const QString &tag)
|
void Session::handleTorrentTagRemoved(TorrentHandle *const torrent, const QString &tag)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit torrentTagRemoved(torrent, tag);
|
emit torrentTagRemoved(torrent, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentSavingModeChanged(TorrentHandle * const torrent)
|
void Session::handleTorrentSavingModeChanged(TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit torrentSavingModeChanged(torrent);
|
emit torrentSavingModeChanged(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList<TrackerEntry> &newTrackers)
|
void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList<TrackerEntry> &newTrackers)
|
||||||
{
|
{
|
||||||
foreach (const TrackerEntry &newTracker, newTrackers)
|
saveTorrentResumeData(torrent);
|
||||||
Logger::instance()->addMessage(tr("Tracker '%1' was added to torrent '%2'").arg(newTracker.url(), torrent->name()));
|
|
||||||
|
for (const TrackerEntry &newTracker : newTrackers)
|
||||||
|
LogMsg(tr("Tracker '%1' was added to torrent '%2'").arg(newTracker.url(), torrent->name()));
|
||||||
emit trackersAdded(torrent, newTrackers);
|
emit trackersAdded(torrent, newTrackers);
|
||||||
if (torrent->trackers().size() == newTrackers.size())
|
if (torrent->trackers().size() == newTrackers.size())
|
||||||
emit trackerlessStateChanged(torrent, false);
|
emit trackerlessStateChanged(torrent, false);
|
||||||
@@ -3529,8 +3587,10 @@ void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QLi
|
|||||||
|
|
||||||
void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList<TrackerEntry> &deletedTrackers)
|
void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList<TrackerEntry> &deletedTrackers)
|
||||||
{
|
{
|
||||||
foreach (const TrackerEntry &deletedTracker, deletedTrackers)
|
saveTorrentResumeData(torrent);
|
||||||
Logger::instance()->addMessage(tr("Tracker '%1' was deleted from torrent '%2'").arg(deletedTracker.url(), torrent->name()));
|
|
||||||
|
for (const TrackerEntry &deletedTracker : deletedTrackers)
|
||||||
|
LogMsg(tr("Tracker '%1' was deleted from torrent '%2'").arg(deletedTracker.url(), torrent->name()));
|
||||||
emit trackersRemoved(torrent, deletedTrackers);
|
emit trackersRemoved(torrent, deletedTrackers);
|
||||||
if (torrent->trackers().size() == 0)
|
if (torrent->trackers().size() == 0)
|
||||||
emit trackerlessStateChanged(torrent, true);
|
emit trackerlessStateChanged(torrent, true);
|
||||||
@@ -3539,19 +3599,22 @@ void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const Q
|
|||||||
|
|
||||||
void Session::handleTorrentTrackersChanged(TorrentHandle *const torrent)
|
void Session::handleTorrentTrackersChanged(TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit trackersChanged(torrent);
|
emit trackersChanged(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList<QUrl> &newUrlSeeds)
|
void Session::handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList<QUrl> &newUrlSeeds)
|
||||||
{
|
{
|
||||||
foreach (const QUrl &newUrlSeed, newUrlSeeds)
|
saveTorrentResumeData(torrent);
|
||||||
Logger::instance()->addMessage(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString(), torrent->name()));
|
for (const QUrl &newUrlSeed : newUrlSeeds)
|
||||||
|
LogMsg(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString(), torrent->name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList<QUrl> &urlSeeds)
|
void Session::handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList<QUrl> &urlSeeds)
|
||||||
{
|
{
|
||||||
foreach (const QUrl &urlSeed, urlSeeds)
|
saveTorrentResumeData(torrent);
|
||||||
Logger::instance()->addMessage(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString(), torrent->name()));
|
for (const QUrl &urlSeed : urlSeeds)
|
||||||
|
LogMsg(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString(), torrent->name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
|
void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
|
||||||
@@ -3579,6 +3642,7 @@ void Session::handleTorrentPaused(TorrentHandle *const torrent)
|
|||||||
|
|
||||||
void Session::handleTorrentResumed(TorrentHandle *const torrent)
|
void Session::handleTorrentResumed(TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
|
saveTorrentResumeData(torrent);
|
||||||
emit torrentResumed(torrent);
|
emit torrentResumed(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3599,7 +3663,7 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent)
|
|||||||
const QString torrentRelpath = torrent->filePath(i);
|
const QString torrentRelpath = torrent->filePath(i);
|
||||||
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive)) {
|
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive)) {
|
||||||
qDebug("Found possible recursive torrent download.");
|
qDebug("Found possible recursive torrent download.");
|
||||||
const QString torrentFullpath = torrent->savePath(true) + "/" + torrentRelpath;
|
const QString torrentFullpath = torrent->savePath(true) + '/' + torrentRelpath;
|
||||||
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
|
||||||
TorrentInfo torrentInfo = TorrentInfo::loadFromFile(torrentFullpath);
|
TorrentInfo torrentInfo = TorrentInfo::loadFromFile(torrentFullpath);
|
||||||
if (torrentInfo.isValid()) {
|
if (torrentInfo.isValid()) {
|
||||||
@@ -3714,8 +3778,8 @@ void Session::enableIPFilter()
|
|||||||
// set between clearing the old one and setting the new one.
|
// set between clearing the old one and setting the new one.
|
||||||
if (!m_filterParser) {
|
if (!m_filterParser) {
|
||||||
m_filterParser = new FilterParserThread(this);
|
m_filterParser = new FilterParserThread(this);
|
||||||
connect(m_filterParser.data(), SIGNAL(IPFilterParsed(int)), SLOT(handleIPFilterParsed(int)));
|
connect(m_filterParser.data(), &FilterParserThread::IPFilterParsed, this, &Session::handleIPFilterParsed);
|
||||||
connect(m_filterParser.data(), SIGNAL(IPFilterError()), SLOT(handleIPFilterError()));
|
connect(m_filterParser.data(), &FilterParserThread::IPFilterError, this, &Session::handleIPFilterError);
|
||||||
}
|
}
|
||||||
m_filterParser->processFilterFile(IPFilterFile());
|
m_filterParser->processFilterFile(IPFilterFile());
|
||||||
}
|
}
|
||||||
@@ -3749,7 +3813,7 @@ void Session::recursiveTorrentDownload(const InfoHash &hash)
|
|||||||
tr("Recursive download of file '%1' embedded in torrent '%2'"
|
tr("Recursive download of file '%1' embedded in torrent '%2'"
|
||||||
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
|
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
|
||||||
.arg(Utils::Fs::toNativePath(torrentRelpath), torrent->name()));
|
.arg(Utils::Fs::toNativePath(torrentRelpath), torrent->name()));
|
||||||
const QString torrentFullpath = torrent->savePath() + "/" + torrentRelpath;
|
const QString torrentFullpath = torrent->savePath() + '/' + torrentRelpath;
|
||||||
|
|
||||||
AddTorrentParams params;
|
AddTorrentParams params;
|
||||||
// Passing the save path along to the sub torrent file
|
// Passing the save path along to the sub torrent file
|
||||||
@@ -3784,7 +3848,7 @@ void Session::startUpTorrents()
|
|||||||
{
|
{
|
||||||
QString hash;
|
QString hash;
|
||||||
MagnetUri magnetUri;
|
MagnetUri magnetUri;
|
||||||
AddTorrentData addTorrentData;
|
CreateTorrentParams addTorrentData;
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
} TorrentResumeData;
|
} TorrentResumeData;
|
||||||
|
|
||||||
@@ -3798,7 +3862,7 @@ void Session::startUpTorrents()
|
|||||||
.arg(params.hash), Log::CRITICAL);
|
.arg(params.hash), Log::CRITICAL);
|
||||||
|
|
||||||
// process add torrent messages before message queue overflow
|
// process add torrent messages before message queue overflow
|
||||||
if (resumedTorrentsCount % 100 == 0) readAlerts();
|
if ((resumedTorrentsCount % 100) == 0) readAlerts();
|
||||||
|
|
||||||
++resumedTorrentsCount;
|
++resumedTorrentsCount;
|
||||||
};
|
};
|
||||||
@@ -3809,19 +3873,20 @@ void Session::startUpTorrents()
|
|||||||
QMap<int, TorrentResumeData> queuedResumeData;
|
QMap<int, TorrentResumeData> queuedResumeData;
|
||||||
int nextQueuePosition = 1;
|
int nextQueuePosition = 1;
|
||||||
int numOfRemappedFiles = 0;
|
int numOfRemappedFiles = 0;
|
||||||
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
||||||
foreach (const QString &fastresumeName, fastresumes) {
|
foreach (const QString &fastresumeName, fastresumes) {
|
||||||
if (rx.indexIn(fastresumeName) == -1) continue;
|
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
|
||||||
|
if (!rxMatch.hasMatch()) continue;
|
||||||
|
|
||||||
QString hash = rx.cap(1);
|
QString hash = rxMatch.captured(1);
|
||||||
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
|
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
AddTorrentData resumeData;
|
CreateTorrentParams torrentParams;
|
||||||
MagnetUri magnetUri;
|
MagnetUri magnetUri;
|
||||||
int queuePosition;
|
int queuePosition;
|
||||||
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, resumeData, queuePosition, magnetUri)) {
|
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) {
|
||||||
if (queuePosition <= nextQueuePosition) {
|
if (queuePosition <= nextQueuePosition) {
|
||||||
startupTorrent({ hash, magnetUri, resumeData, data });
|
startupTorrent({ hash, magnetUri, torrentParams, data });
|
||||||
|
|
||||||
if (queuePosition == nextQueuePosition) {
|
if (queuePosition == nextQueuePosition) {
|
||||||
++nextQueuePosition;
|
++nextQueuePosition;
|
||||||
@@ -3833,12 +3898,12 @@ void Session::startUpTorrents()
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int q = queuePosition;
|
int q = queuePosition;
|
||||||
for(; queuedResumeData.contains(q); ++q) {
|
for (; queuedResumeData.contains(q); ++q) {
|
||||||
}
|
}
|
||||||
if (q != queuePosition) {
|
if (q != queuePosition) {
|
||||||
++numOfRemappedFiles;
|
++numOfRemappedFiles;
|
||||||
}
|
}
|
||||||
queuedResumeData[q] = { hash, magnetUri, resumeData, data };
|
queuedResumeData[q] = {hash, magnetUri, torrentParams, data};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3964,6 +4029,7 @@ void Session::handleAlert(libt::alert *a)
|
|||||||
case libt::storage_moved_alert::alert_type:
|
case libt::storage_moved_alert::alert_type:
|
||||||
case libt::storage_moved_failed_alert::alert_type:
|
case libt::storage_moved_failed_alert::alert_type:
|
||||||
case libt::torrent_paused_alert::alert_type:
|
case libt::torrent_paused_alert::alert_type:
|
||||||
|
case libt::torrent_resumed_alert::alert_type:
|
||||||
case libt::tracker_error_alert::alert_type:
|
case libt::tracker_error_alert::alert_type:
|
||||||
case libt::tracker_reply_alert::alert_type:
|
case libt::tracker_reply_alert::alert_type:
|
||||||
case libt::tracker_warning_alert::alert_type:
|
case libt::tracker_warning_alert::alert_type:
|
||||||
@@ -4041,25 +4107,19 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
|
|||||||
// Magnet added for preload its metadata
|
// Magnet added for preload its metadata
|
||||||
if (!m_addingTorrents.contains(nativeHandle.info_hash())) return;
|
if (!m_addingTorrents.contains(nativeHandle.info_hash())) return;
|
||||||
|
|
||||||
AddTorrentData data = m_addingTorrents.take(nativeHandle.info_hash());
|
CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash());
|
||||||
|
|
||||||
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, data);
|
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, params);
|
||||||
m_torrents.insert(torrent->hash(), torrent);
|
m_torrents.insert(torrent->hash(), torrent);
|
||||||
|
|
||||||
Logger *const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
|
|
||||||
bool fromMagnetUri = !torrent->hasMetadata();
|
bool fromMagnetUri = !torrent->hasMetadata();
|
||||||
|
|
||||||
if (data.resumed) {
|
if (params.restored) {
|
||||||
if (fromMagnetUri && !data.addPaused)
|
logger->addMessage(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name()));
|
||||||
torrent->resume(data.addForced);
|
|
||||||
|
|
||||||
logger->addMessage(tr("'%1' resumed. (fast resume)", "'torrent name' was resumed. (fast resume)")
|
|
||||||
.arg(torrent->name()));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qDebug("This is a NEW torrent (first time)...");
|
|
||||||
|
|
||||||
// The following is useless for newly added magnet
|
// The following is useless for newly added magnet
|
||||||
if (!fromMagnetUri) {
|
if (!fromMagnetUri) {
|
||||||
// Backup torrent file
|
// Backup torrent file
|
||||||
@@ -4078,9 +4138,6 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
|
|||||||
if (isAddTrackersEnabled() && !torrent->isPrivate())
|
if (isAddTrackersEnabled() && !torrent->isPrivate())
|
||||||
torrent->addTrackers(m_additionalTrackerList);
|
torrent->addTrackers(m_additionalTrackerList);
|
||||||
|
|
||||||
// Start torrent because it was added in paused state
|
|
||||||
if (!data.addPaused)
|
|
||||||
torrent->resume();
|
|
||||||
logger->addMessage(tr("'%1' added to download list.", "'torrent name' was added to download list.")
|
logger->addMessage(tr("'%1' added to download list.", "'torrent name' was added to download list.")
|
||||||
.arg(torrent->name()));
|
.arg(torrent->name()));
|
||||||
|
|
||||||
@@ -4096,7 +4153,7 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
|
|||||||
// Send torrent addition signal
|
// Send torrent addition signal
|
||||||
emit torrentAdded(torrent);
|
emit torrentAdded(torrent);
|
||||||
// Send new torrent signal
|
// Send new torrent signal
|
||||||
if (!data.resumed)
|
if (!params.restored)
|
||||||
emit torrentNew(torrent);
|
emit torrentNew(torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4169,10 +4226,15 @@ void Session::handleFileErrorAlert(libt::file_error_alert *p)
|
|||||||
// NOTE: Check this function!
|
// NOTE: Check this function!
|
||||||
TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash());
|
TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash());
|
||||||
if (torrent) {
|
if (torrent) {
|
||||||
QString msg = QString::fromStdString(p->message());
|
const InfoHash hash = torrent->hash();
|
||||||
Logger::instance()->addMessage(tr("An I/O error occurred, '%1' paused. %2")
|
if (!m_recentErroredTorrents.contains(hash)) {
|
||||||
.arg(torrent->name(), msg));
|
m_recentErroredTorrents.insert(hash);
|
||||||
emit fullDiskError(torrent, msg);
|
const QString msg = QString::fromStdString(p->message());
|
||||||
|
LogMsg(tr("An I/O error occurred, '%1' paused. %2").arg(torrent->name(), msg));
|
||||||
|
emit fullDiskError(torrent, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_recentErroredTorrentsTimer->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4246,7 +4308,7 @@ void Session::handleListenSucceededAlert(libt::listen_succeeded_alert *p)
|
|||||||
proto = "TCP";
|
proto = "TCP";
|
||||||
else if (p->sock_type == libt::listen_succeeded_alert::tcp_ssl)
|
else if (p->sock_type == libt::listen_succeeded_alert::tcp_ssl)
|
||||||
proto = "TCP_SSL";
|
proto = "TCP_SSL";
|
||||||
qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
|
qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << '/' << p->endpoint.port();
|
||||||
Logger::instance()->addMessage(
|
Logger::instance()->addMessage(
|
||||||
tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881")
|
tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881")
|
||||||
.arg(p->endpoint.address().to_string(ec).c_str(), proto, QString::number(p->endpoint.port())), Log::INFO);
|
.arg(p->endpoint.address().to_string(ec).c_str(), proto, QString::number(p->endpoint.port())), Log::INFO);
|
||||||
@@ -4273,7 +4335,7 @@ void Session::handleListenFailedAlert(libt::listen_failed_alert *p)
|
|||||||
proto = "I2P";
|
proto = "I2P";
|
||||||
else if (p->sock_type == libt::listen_failed_alert::socks5)
|
else if (p->sock_type == libt::listen_failed_alert::socks5)
|
||||||
proto = "SOCKS5";
|
proto = "SOCKS5";
|
||||||
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
|
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << '/' << p->endpoint.port();
|
||||||
Logger::instance()->addMessage(
|
Logger::instance()->addMessage(
|
||||||
tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4.",
|
tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4.",
|
||||||
"e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use.")
|
"e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use.")
|
||||||
@@ -4441,11 +4503,11 @@ namespace
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri)
|
bool loadTorrentResumeData(const QByteArray &data, CreateTorrentParams &torrentParams, int &prio, MagnetUri &magnetUri)
|
||||||
{
|
{
|
||||||
torrentData = AddTorrentData();
|
torrentParams = CreateTorrentParams();
|
||||||
torrentData.resumed = true;
|
torrentParams.restored = true;
|
||||||
torrentData.skipChecking = false;
|
torrentParams.skipChecking = false;
|
||||||
|
|
||||||
libt::error_code ec;
|
libt::error_code ec;
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
@@ -4458,36 +4520,36 @@ namespace
|
|||||||
if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false;
|
if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
torrentData.savePath = Profile::instance().fromPortablePath(
|
torrentParams.savePath = Profile::instance().fromPortablePath(
|
||||||
Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath"))));
|
Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath"))));
|
||||||
|
|
||||||
std::string ratioLimitString = fast.dict_find_string_value("qBt-ratioLimit");
|
std::string ratioLimitString = fast.dict_find_string_value("qBt-ratioLimit");
|
||||||
if (ratioLimitString.empty())
|
if (ratioLimitString.empty())
|
||||||
torrentData.ratioLimit = fast.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0;
|
torrentParams.ratioLimit = fast.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0;
|
||||||
else
|
else
|
||||||
torrentData.ratioLimit = QString::fromStdString(ratioLimitString).toDouble();
|
torrentParams.ratioLimit = QString::fromStdString(ratioLimitString).toDouble();
|
||||||
torrentData.seedingTimeLimit = fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME);
|
torrentParams.seedingTimeLimit = fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME);
|
||||||
// **************************************************************************************
|
// **************************************************************************************
|
||||||
// Workaround to convert legacy label to category
|
// Workaround to convert legacy label to category
|
||||||
// TODO: Should be removed in future
|
// TODO: Should be removed in future
|
||||||
torrentData.category = QString::fromStdString(fast.dict_find_string_value("qBt-label"));
|
torrentParams.category = QString::fromStdString(fast.dict_find_string_value("qBt-label"));
|
||||||
if (torrentData.category.isEmpty())
|
if (torrentParams.category.isEmpty())
|
||||||
// **************************************************************************************
|
// **************************************************************************************
|
||||||
torrentData.category = QString::fromStdString(fast.dict_find_string_value("qBt-category"));
|
torrentParams.category = QString::fromStdString(fast.dict_find_string_value("qBt-category"));
|
||||||
// auto because the return type depends on the #if above.
|
// auto because the return type depends on the #if above.
|
||||||
const auto tagsEntry = fast.dict_find_list("qBt-tags");
|
const auto tagsEntry = fast.dict_find_list("qBt-tags");
|
||||||
if (isList(tagsEntry))
|
if (isList(tagsEntry))
|
||||||
torrentData.tags = entryListToSet(tagsEntry);
|
torrentParams.tags = entryListToSet(tagsEntry);
|
||||||
torrentData.name = QString::fromStdString(fast.dict_find_string_value("qBt-name"));
|
torrentParams.name = QString::fromStdString(fast.dict_find_string_value("qBt-name"));
|
||||||
torrentData.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
|
torrentParams.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
|
||||||
torrentData.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
|
torrentParams.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
|
||||||
torrentData.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder");
|
torrentParams.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder");
|
||||||
|
|
||||||
magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
|
magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
|
||||||
torrentData.addPaused = fast.dict_find_int_value("qBt-paused");
|
torrentParams.paused = fast.dict_find_int_value("qBt-paused");
|
||||||
torrentData.addForced = fast.dict_find_int_value("qBt-forced");
|
torrentParams.forced = fast.dict_find_int_value("qBt-forced");
|
||||||
torrentData.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority");
|
torrentParams.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority");
|
||||||
torrentData.sequential = fast.dict_find_int_value("qBt-sequential");
|
torrentParams.sequential = fast.dict_find_int_value("qBt-sequential");
|
||||||
|
|
||||||
prio = fast.dict_find_int_value("qBt-queuePosition");
|
prio = fast.dict_find_int_value("qBt-queuePosition");
|
||||||
|
|
||||||
|
|||||||
@@ -30,18 +30,14 @@
|
|||||||
#ifndef BITTORRENT_SESSION_H
|
#ifndef BITTORRENT_SESSION_H
|
||||||
#define BITTORRENT_SESSION_H
|
#define BITTORRENT_SESSION_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
#include <vector>
|
||||||
#include <QElapsedTimer>
|
|
||||||
#endif
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
|
||||||
#include <QMutex>
|
|
||||||
#endif
|
|
||||||
#include <QNetworkConfigurationManager>
|
#include <QNetworkConfigurationManager>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@@ -49,6 +45,12 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
|
#include <QMutex>
|
||||||
|
#else
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
#include "base/tristatebool.h"
|
#include "base/tristatebool.h"
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
@@ -111,7 +113,6 @@ class QTimer;
|
|||||||
class QStringList;
|
class QStringList;
|
||||||
class QString;
|
class QString;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
template<typename T> class QList;
|
|
||||||
|
|
||||||
class FilterParserThread;
|
class FilterParserThread;
|
||||||
class BandwidthScheduler;
|
class BandwidthScheduler;
|
||||||
@@ -137,7 +138,7 @@ namespace BitTorrent
|
|||||||
class Tracker;
|
class Tracker;
|
||||||
class MagnetUri;
|
class MagnetUri;
|
||||||
class TrackerEntry;
|
class TrackerEntry;
|
||||||
struct AddTorrentData;
|
struct CreateTorrentParams;
|
||||||
|
|
||||||
struct TorrentStatusReport
|
struct TorrentStatusReport
|
||||||
{
|
{
|
||||||
@@ -374,6 +375,8 @@ namespace BitTorrent
|
|||||||
void setAnnounceToAllTrackers(bool val);
|
void setAnnounceToAllTrackers(bool val);
|
||||||
bool announceToAllTiers() const;
|
bool announceToAllTiers() const;
|
||||||
void setAnnounceToAllTiers(bool val);
|
void setAnnounceToAllTiers(bool val);
|
||||||
|
int asyncIOThreads() const;
|
||||||
|
void setAsyncIOThreads(int num);
|
||||||
int diskCacheSize() const;
|
int diskCacheSize() const;
|
||||||
void setDiskCacheSize(int size);
|
void setDiskCacheSize(int size);
|
||||||
int diskCacheTTL() const;
|
int diskCacheTTL() const;
|
||||||
@@ -451,6 +454,7 @@ namespace BitTorrent
|
|||||||
TorrentStatusReport torrentStatusReport() const;
|
TorrentStatusReport torrentStatusReport() const;
|
||||||
bool hasActiveTorrents() const;
|
bool hasActiveTorrents() const;
|
||||||
bool hasUnfinishedTorrents() const;
|
bool hasUnfinishedTorrents() const;
|
||||||
|
bool hasRunningSeed() const;
|
||||||
const SessionStatus &status() const;
|
const SessionStatus &status() const;
|
||||||
const CacheStatus &cacheStatus() const;
|
const CacheStatus &cacheStatus() const;
|
||||||
quint64 getAlltimeDL() const;
|
quint64 getAlltimeDL() const;
|
||||||
@@ -477,6 +481,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
// TorrentHandle interface
|
// TorrentHandle interface
|
||||||
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
|
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentNameChanged(TorrentHandle *const torrent);
|
||||||
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||||
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
|
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
|
||||||
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag);
|
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag);
|
||||||
@@ -546,7 +551,7 @@ namespace BitTorrent
|
|||||||
void generateResumeData(bool final = false);
|
void generateResumeData(bool final = false);
|
||||||
void handleIPFilterParsed(int ruleCount);
|
void handleIPFilterParsed(int ruleCount);
|
||||||
void handleIPFilterError();
|
void handleIPFilterError();
|
||||||
void handleDownloadFinished(const QString &url, const QString &filePath);
|
void handleDownloadFinished(const QString &url, const QByteArray &data);
|
||||||
void handleDownloadFailed(const QString &url, const QString &reason);
|
void handleDownloadFailed(const QString &url, const QString &reason);
|
||||||
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||||
|
|
||||||
@@ -562,7 +567,7 @@ namespace BitTorrent
|
|||||||
bool requestedFileDeletion;
|
bool requestedFileDeletion;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Session(QObject *parent = 0);
|
explicit Session(QObject *parent = nullptr);
|
||||||
~Session();
|
~Session();
|
||||||
|
|
||||||
bool hasPerTorrentRatioLimit() const;
|
bool hasPerTorrentRatioLimit() const;
|
||||||
@@ -594,7 +599,7 @@ namespace BitTorrent
|
|||||||
void enableIPFilter();
|
void enableIPFilter();
|
||||||
void disableIPFilter();
|
void disableIPFilter();
|
||||||
|
|
||||||
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
|
bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
|
||||||
TorrentInfo torrentInfo = TorrentInfo(),
|
TorrentInfo torrentInfo = TorrentInfo(),
|
||||||
const QByteArray &fastresumeData = QByteArray());
|
const QByteArray &fastresumeData = QByteArray());
|
||||||
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
|
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
|
||||||
@@ -650,6 +655,7 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<QString> m_IPFilterFile;
|
CachedSettingValue<QString> m_IPFilterFile;
|
||||||
CachedSettingValue<bool> m_announceToAllTrackers;
|
CachedSettingValue<bool> m_announceToAllTrackers;
|
||||||
CachedSettingValue<bool> m_announceToAllTiers;
|
CachedSettingValue<bool> m_announceToAllTiers;
|
||||||
|
CachedSettingValue<int> m_asyncIOThreads;
|
||||||
CachedSettingValue<int> m_diskCacheSize;
|
CachedSettingValue<int> m_diskCacheSize;
|
||||||
CachedSettingValue<int> m_diskCacheTTL;
|
CachedSettingValue<int> m_diskCacheTTL;
|
||||||
CachedSettingValue<bool> m_useOSCache;
|
CachedSettingValue<bool> m_useOSCache;
|
||||||
@@ -753,13 +759,17 @@ namespace BitTorrent
|
|||||||
|
|
||||||
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
||||||
QHash<InfoHash, TorrentHandle *> m_torrents;
|
QHash<InfoHash, TorrentHandle *> m_torrents;
|
||||||
QHash<InfoHash, AddTorrentData> m_addingTorrents;
|
QHash<InfoHash, CreateTorrentParams> m_addingTorrents;
|
||||||
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||||
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
|
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
|
||||||
TorrentStatusReport m_torrentStatusReport;
|
TorrentStatusReport m_torrentStatusReport;
|
||||||
QStringMap m_categories;
|
QStringMap m_categories;
|
||||||
QSet<QString> m_tags;
|
QSet<QString> m_tags;
|
||||||
|
|
||||||
|
// I/O errored torrents
|
||||||
|
QSet<InfoHash> m_recentErroredTorrents;
|
||||||
|
QTimer *m_recentErroredTorrentsTimer;
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QMutex m_alertsMutex;
|
QMutex m_alertsMutex;
|
||||||
QWaitCondition m_alertsWaitCondition;
|
QWaitCondition m_alertsWaitCondition;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "torrentcreatorthread.h"
|
#include "torrentcreatorthread.h"
|
||||||
@@ -91,7 +89,7 @@ void TorrentCreatorThread::run()
|
|||||||
emit updateProgress(0);
|
emit updateProgress(0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
|
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
|
||||||
|
|
||||||
// Adding files to the torrent
|
// Adding files to the torrent
|
||||||
libt::file_storage fs;
|
libt::file_storage fs;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
|
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
|
|
||||||
const QString QB_EXT {".!qB"};
|
const QString QB_EXT {QStringLiteral(".!qB")};
|
||||||
|
|
||||||
namespace libt = libtorrent;
|
namespace libt = libtorrent;
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
@@ -85,16 +85,16 @@ namespace
|
|||||||
|
|
||||||
// AddTorrentData
|
// AddTorrentData
|
||||||
|
|
||||||
AddTorrentData::AddTorrentData()
|
CreateTorrentParams::CreateTorrentParams()
|
||||||
: resumed(false)
|
: restored(false)
|
||||||
, disableTempPath(false)
|
, disableTempPath(false)
|
||||||
, sequential(false)
|
, sequential(false)
|
||||||
, firstLastPiecePriority(false)
|
, firstLastPiecePriority(false)
|
||||||
, hasSeedStatus(false)
|
, hasSeedStatus(false)
|
||||||
, skipChecking(false)
|
, skipChecking(false)
|
||||||
, hasRootFolder(true)
|
, hasRootFolder(true)
|
||||||
, addForced(false)
|
, forced(false)
|
||||||
, addPaused(false)
|
, paused(false)
|
||||||
, uploadLimit(-1)
|
, uploadLimit(-1)
|
||||||
, downloadLimit(-1)
|
, downloadLimit(-1)
|
||||||
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
|
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
|
||||||
@@ -102,8 +102,8 @@ AddTorrentData::AddTorrentData()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
CreateTorrentParams::CreateTorrentParams(const AddTorrentParams ¶ms)
|
||||||
: resumed(false)
|
: restored(false)
|
||||||
, name(params.name)
|
, name(params.name)
|
||||||
, category(params.category)
|
, category(params.category)
|
||||||
, tags(params.tags)
|
, tags(params.tags)
|
||||||
@@ -116,8 +116,8 @@ AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
|||||||
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
|
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
|
||||||
? Session::instance()->isCreateTorrentSubfolder()
|
? Session::instance()->isCreateTorrentSubfolder()
|
||||||
: params.createSubfolder == TriStateBool::True)
|
: params.createSubfolder == TriStateBool::True)
|
||||||
, addForced(params.addForced == TriStateBool::True)
|
, forced(params.addForced == TriStateBool::True)
|
||||||
, addPaused(params.addPaused == TriStateBool::Undefined
|
, paused(params.addPaused == TriStateBool::Undefined
|
||||||
? Session::instance()->isAddTorrentPaused()
|
? Session::instance()->isAddTorrentPaused()
|
||||||
: params.addPaused == TriStateBool::True)
|
: params.addPaused == TriStateBool::True)
|
||||||
, uploadLimit(params.uploadLimit)
|
, uploadLimit(params.uploadLimit)
|
||||||
@@ -156,8 +156,6 @@ const int TorrentHandle::MAX_SEEDING_TIME = 525600;
|
|||||||
// The following can be removed after one or two libtorrent releases on each branch.
|
// The following can be removed after one or two libtorrent releases on each branch.
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const char i18nContext[] = "TorrentHandle";
|
|
||||||
|
|
||||||
// new constructor is available
|
// new constructor is available
|
||||||
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
|
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
|
||||||
T makeTorrentCreator(const libtorrent::torrent_info & ti)
|
T makeTorrentCreator(const libtorrent::torrent_info & ti)
|
||||||
@@ -174,26 +172,25 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
||||||
const AddTorrentData &data)
|
const CreateTorrentParams ¶ms)
|
||||||
: QObject(session)
|
: QObject(session)
|
||||||
, m_session(session)
|
, m_session(session)
|
||||||
, m_nativeHandle(nativeHandle)
|
, m_nativeHandle(nativeHandle)
|
||||||
, m_state(TorrentState::Unknown)
|
, m_state(TorrentState::Unknown)
|
||||||
, m_renameCount(0)
|
, m_renameCount(0)
|
||||||
, m_useAutoTMM(data.savePath.isEmpty())
|
, m_useAutoTMM(params.savePath.isEmpty())
|
||||||
, m_name(data.name)
|
, m_name(params.name)
|
||||||
, m_savePath(Utils::Fs::toNativePath(data.savePath))
|
, m_savePath(Utils::Fs::toNativePath(params.savePath))
|
||||||
, m_category(data.category)
|
, m_category(params.category)
|
||||||
, m_tags(data.tags)
|
, m_tags(params.tags)
|
||||||
, m_hasSeedStatus(data.hasSeedStatus)
|
, m_hasSeedStatus(params.hasSeedStatus)
|
||||||
, m_ratioLimit(data.ratioLimit)
|
, m_ratioLimit(params.ratioLimit)
|
||||||
, m_seedingTimeLimit(data.seedingTimeLimit)
|
, m_seedingTimeLimit(params.seedingTimeLimit)
|
||||||
, m_tempPathDisabled(data.disableTempPath)
|
, m_tempPathDisabled(params.disableTempPath)
|
||||||
, m_hasMissingFiles(false)
|
, m_hasMissingFiles(false)
|
||||||
, m_hasRootFolder(data.hasRootFolder)
|
, m_hasRootFolder(params.hasRootFolder)
|
||||||
, m_needsToSetFirstLastPiecePriority(false)
|
, m_needsToSetFirstLastPiecePriority(false)
|
||||||
, m_pauseAfterRecheck(false)
|
, m_pauseAfterRecheck(false)
|
||||||
, m_needSaveResumeData(false)
|
|
||||||
{
|
{
|
||||||
if (m_useAutoTMM)
|
if (m_useAutoTMM)
|
||||||
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
||||||
@@ -209,15 +206,29 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
|||||||
// download sequentially or have first/last piece priority enabled when
|
// download sequentially or have first/last piece priority enabled when
|
||||||
// its resume data was saved. These two settings are restored later. But
|
// its resume data was saved. These two settings are restored later. But
|
||||||
// if we set them to false now, both will erroneously not be restored.
|
// if we set them to false now, both will erroneously not be restored.
|
||||||
if (!data.resumed || data.sequential)
|
if (!params.restored || params.sequential)
|
||||||
setSequentialDownload(data.sequential);
|
setSequentialDownload(params.sequential);
|
||||||
if (!data.resumed || data.firstLastPiecePriority)
|
if (!params.restored || params.firstLastPiecePriority)
|
||||||
setFirstLastPiecePriority(data.firstLastPiecePriority);
|
setFirstLastPiecePriority(params.firstLastPiecePriority);
|
||||||
|
|
||||||
if (!data.resumed && hasMetadata()) {
|
if (!params.restored && hasMetadata()) {
|
||||||
if (filesCount() == 1)
|
if (filesCount() == 1)
|
||||||
m_hasRootFolder = false;
|
m_hasRootFolder = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "started" means "all initialization has completed and torrent has started regular processing".
|
||||||
|
// When torrent added/restored in "paused" state it become "started" immediately after construction.
|
||||||
|
// When it is added/restored in "resumed" state, it become "started" after it is really resumed
|
||||||
|
// (i.e. after receiving "torrent resumed" alert).
|
||||||
|
m_started = (params.restored && hasMetadata() ? isPaused() : params.paused);
|
||||||
|
|
||||||
|
if (!m_started) {
|
||||||
|
if (!params.restored || !hasMetadata()) {
|
||||||
|
// Resume torrent because it was added in "resumed" state
|
||||||
|
// but it's actually paused during initialization
|
||||||
|
resume(params.forced);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentHandle::~TorrentHandle() {}
|
TorrentHandle::~TorrentHandle() {}
|
||||||
@@ -317,7 +328,7 @@ QString TorrentHandle::rootPath(bool actual) const
|
|||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
QString firstFilePath = filePath(0);
|
QString firstFilePath = filePath(0);
|
||||||
const int slashIndex = firstFilePath.indexOf("/");
|
const int slashIndex = firstFilePath.indexOf('/');
|
||||||
if (slashIndex >= 0)
|
if (slashIndex >= 0)
|
||||||
return QDir(savePath(actual)).absoluteFilePath(firstFilePath.left(slashIndex));
|
return QDir(savePath(actual)).absoluteFilePath(firstFilePath.left(slashIndex));
|
||||||
else
|
else
|
||||||
@@ -490,8 +501,6 @@ bool TorrentHandle::connectPeer(const PeerAddress &peerAddress)
|
|||||||
|
|
||||||
bool TorrentHandle::needSaveResumeData() const
|
bool TorrentHandle::needSaveResumeData() const
|
||||||
{
|
{
|
||||||
if (m_needSaveResumeData) return true;
|
|
||||||
|
|
||||||
return m_nativeHandle.need_save_resume_data();
|
return m_nativeHandle.need_save_resume_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +510,6 @@ void TorrentHandle::saveResumeData(bool updateStatus)
|
|||||||
this->updateStatus();
|
this->updateStatus();
|
||||||
|
|
||||||
m_nativeHandle.save_resume_data();
|
m_nativeHandle.save_resume_data();
|
||||||
m_needSaveResumeData = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TorrentHandle::filesCount() const
|
int TorrentHandle::filesCount() const
|
||||||
@@ -521,15 +529,19 @@ int TorrentHandle::piecesHave() const
|
|||||||
|
|
||||||
qreal TorrentHandle::progress() const
|
qreal TorrentHandle::progress() const
|
||||||
{
|
{
|
||||||
if (!m_nativeStatus.total_wanted)
|
if (!isChecking()) {
|
||||||
return 0.;
|
if (!m_nativeStatus.total_wanted)
|
||||||
|
return 0.;
|
||||||
|
|
||||||
if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted)
|
if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted)
|
||||||
return 1.;
|
return 1.;
|
||||||
|
|
||||||
float progress = static_cast<float>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
qreal progress = static_cast<qreal>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
||||||
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
||||||
return progress;
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_nativeStatus.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentHandle::category() const
|
QString TorrentHandle::category() const
|
||||||
@@ -544,7 +556,7 @@ bool TorrentHandle::belongsToCategory(const QString &category) const
|
|||||||
|
|
||||||
if (m_category == category) return true;
|
if (m_category == category) return true;
|
||||||
|
|
||||||
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + "/"))
|
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + '/'))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -571,7 +583,6 @@ bool TorrentHandle::addTag(const QString &tag)
|
|||||||
return false;
|
return false;
|
||||||
m_tags.insert(tag);
|
m_tags.insert(tag);
|
||||||
m_session->handleTorrentTagAdded(this, tag);
|
m_session->handleTorrentTagAdded(this, tag);
|
||||||
m_needSaveResumeData = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -581,7 +592,6 @@ bool TorrentHandle::removeTag(const QString &tag)
|
|||||||
{
|
{
|
||||||
if (m_tags.remove(tag)) {
|
if (m_tags.remove(tag)) {
|
||||||
m_session->handleTorrentTagRemoved(this, tag);
|
m_session->handleTorrentTagRemoved(this, tag);
|
||||||
m_needSaveResumeData = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -736,7 +746,8 @@ bool TorrentHandle::isActive() const
|
|||||||
|| m_state == TorrentState::Downloading
|
|| m_state == TorrentState::Downloading
|
||||||
|| m_state == TorrentState::ForcedDownloading
|
|| m_state == TorrentState::ForcedDownloading
|
||||||
|| m_state == TorrentState::Uploading
|
|| m_state == TorrentState::Uploading
|
||||||
|| m_state == TorrentState::ForcedUploading;
|
|| m_state == TorrentState::ForcedUploading
|
||||||
|
|| m_state == TorrentState::Moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentHandle::isInactive() const
|
bool TorrentHandle::isInactive() const
|
||||||
@@ -778,28 +789,18 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
|
|||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return m_needsToSetFirstLastPiecePriority;
|
return m_needsToSetFirstLastPiecePriority;
|
||||||
|
|
||||||
// Get int first media file
|
const std::vector<int> filePriorities = nativeHandle().file_priorities();
|
||||||
std::vector<int> fp;
|
for (int i = 0; i < static_cast<int>(filePriorities.size()); ++i) {
|
||||||
fp = m_nativeHandle.file_priorities();
|
if (filePriorities[i] <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
TorrentInfo::PieceRange extremities;
|
const TorrentInfo::PieceRange extremities = info().filePieces(i);
|
||||||
bool found = false;
|
const int firstPiecePrio = nativeHandle().piece_priority(extremities.first());
|
||||||
int count = static_cast<int>(fp.size());
|
const int lastPiecePrio = nativeHandle().piece_priority(extremities.last());
|
||||||
for (int i = 0; i < count; ++i) {
|
return ((firstPiecePrio == 7) && (lastPiecePrio == 7));
|
||||||
const QString ext = Utils::Fs::fileExtension(filePath(i));
|
|
||||||
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
|
|
||||||
extremities = info().filePieces(i);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) return false; // No media file
|
return false;
|
||||||
|
|
||||||
int first = m_nativeHandle.piece_priority(extremities.first());
|
|
||||||
int last = m_nativeHandle.piece_priority(extremities.last());
|
|
||||||
|
|
||||||
return ((first == 7) && (last == 7));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentState TorrentHandle::state() const
|
TorrentState TorrentHandle::state() const
|
||||||
@@ -809,7 +810,10 @@ TorrentState TorrentHandle::state() const
|
|||||||
|
|
||||||
void TorrentHandle::updateState()
|
void TorrentHandle::updateState()
|
||||||
{
|
{
|
||||||
if (isPaused()) {
|
if (isMoveInProgress()) {
|
||||||
|
m_state = TorrentState::Moving;
|
||||||
|
}
|
||||||
|
else if (isPaused()) {
|
||||||
if (hasMissingFiles())
|
if (hasMissingFiles())
|
||||||
m_state = TorrentState::MissingFiles;
|
m_state = TorrentState::MissingFiles;
|
||||||
else if (hasError())
|
else if (hasError())
|
||||||
@@ -1202,7 +1206,7 @@ void TorrentHandle::setName(const QString &name)
|
|||||||
{
|
{
|
||||||
if (m_name != name) {
|
if (m_name != name) {
|
||||||
m_name = name;
|
m_name = name;
|
||||||
m_needSaveResumeData = true;
|
m_session->handleTorrentNameChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1218,7 +1222,6 @@ bool TorrentHandle::setCategory(const QString &category)
|
|||||||
|
|
||||||
QString oldCategory = m_category;
|
QString oldCategory = m_category;
|
||||||
m_category = category;
|
m_category = category;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
||||||
|
|
||||||
if (m_useAutoTMM) {
|
if (m_useAutoTMM) {
|
||||||
@@ -1256,7 +1259,6 @@ void TorrentHandle::move_impl(QString path, bool overwrite)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_savePath = path;
|
m_savePath = path;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentSavePathChanged(this);
|
m_session->handleTorrentSavePathChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1296,39 +1298,45 @@ void TorrentHandle::toggleSequentialDownload()
|
|||||||
setSequentialDownload(!isSequentialDownload());
|
setSequentialDownload(!isSequentialDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::setFirstLastPiecePriority(bool b)
|
void TorrentHandle::setFirstLastPiecePriority(const bool enabled)
|
||||||
{
|
{
|
||||||
|
setFirstLastPiecePriorityImpl(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentHandle::setFirstLastPiecePriorityImpl(const bool enabled, const QVector<int> &updatedFilePrio)
|
||||||
|
{
|
||||||
|
// Download first and last pieces first for every file in the torrent
|
||||||
|
|
||||||
if (!hasMetadata()) {
|
if (!hasMetadata()) {
|
||||||
m_needsToSetFirstLastPiecePriority = b;
|
m_needsToSetFirstLastPiecePriority = enabled;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> fp = m_nativeHandle.file_priorities();
|
// Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it
|
||||||
std::vector<int> pp = m_nativeHandle.piece_priorities();
|
// we might get the old/wrong values, so we rely on `updatedFilePrio` in this case.
|
||||||
|
const std::vector<int> filePriorities = !updatedFilePrio.isEmpty() ? updatedFilePrio.toStdVector() : nativeHandle().file_priorities();
|
||||||
|
std::vector<int> piecePriorities = nativeHandle().piece_priorities();
|
||||||
|
for (int index = 0; index < static_cast<int>(filePriorities.size()); ++index) {
|
||||||
|
const int filePrio = filePriorities[index];
|
||||||
|
if (filePrio <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Download first and last pieces first for all media files in the torrent
|
// Determine the priority to set
|
||||||
int nbfiles = static_cast<int>(fp.size());
|
const int newPrio = enabled ? 7 : filePrio;
|
||||||
for (int index = 0; index < nbfiles; ++index) {
|
const TorrentInfo::PieceRange extremities = info().filePieces(index);
|
||||||
const QString path = filePath(index);
|
|
||||||
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
|
// worst case: AVI index = 1% of total file size (at the end of the file)
|
||||||
int prio = b ? 7 : fp[index];
|
const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength());
|
||||||
|
for (int i = 0; i < nNumPieces; ++i) {
|
||||||
TorrentInfo::PieceRange extremities = info().filePieces(index);
|
piecePriorities[extremities.first() + i] = newPrio;
|
||||||
|
piecePriorities[extremities.last() - i] = newPrio;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nativeHandle.prioritize_pieces(pp);
|
m_nativeHandle.prioritize_pieces(piecePriorities);
|
||||||
|
|
||||||
|
LogMsg(tr("Download first and last piece first: %1, torrent: '%2'")
|
||||||
|
.arg((enabled ? tr("On") : tr("Off")), name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::toggleFirstLastPiecePriority()
|
void TorrentHandle::toggleFirstLastPiecePriority()
|
||||||
@@ -1376,6 +1384,7 @@ void TorrentHandle::moveStorage(const QString &newPath, bool overwrite)
|
|||||||
, (overwrite ? libt::always_replace_files : libt::dont_replace));
|
, (overwrite ? libt::always_replace_files : libt::dont_replace));
|
||||||
m_moveStorageInfo.oldPath = oldPath;
|
m_moveStorageInfo.oldPath = oldPath;
|
||||||
m_moveStorageInfo.newPath = newPath;
|
m_moveStorageInfo.newPath = newPath;
|
||||||
|
updateState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1425,7 +1434,7 @@ void TorrentHandle::handleStateUpdate(const libt::torrent_status &nativeStatus)
|
|||||||
updateStatus(nativeStatus);
|
updateStatus(nativeStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
void TorrentHandle::handleStorageMovedAlert(const libtorrent::storage_moved_alert *p)
|
||||||
{
|
{
|
||||||
if (!isMoveInProgress()) {
|
if (!isMoveInProgress()) {
|
||||||
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
||||||
@@ -1442,8 +1451,8 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug("Torrent is successfully moved from %s to %s"
|
LogMsg(tr("Successfully moved torrent: %1. New path: %2").arg(name(), m_moveStorageInfo.newPath));
|
||||||
, qUtf8Printable(m_moveStorageInfo.oldPath), qUtf8Printable(m_moveStorageInfo.newPath));
|
|
||||||
const QDir oldDir {m_moveStorageInfo.oldPath};
|
const QDir oldDir {m_moveStorageInfo.oldPath};
|
||||||
if ((oldDir == QDir(m_session->torrentTempPath(info())))
|
if ((oldDir == QDir(m_session->torrentTempPath(info())))
|
||||||
&& (oldDir != QDir(m_session->tempPath()))) {
|
&& (oldDir != QDir(m_session->tempPath()))) {
|
||||||
@@ -1452,9 +1461,10 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
|||||||
qDebug() << "Removing torrent temp folder:" << m_moveStorageInfo.oldPath;
|
qDebug() << "Removing torrent temp folder:" << m_moveStorageInfo.oldPath;
|
||||||
Utils::Fs::smartRemoveEmptyFolderTree(m_moveStorageInfo.oldPath);
|
Utils::Fs::smartRemoveEmptyFolderTree(m_moveStorageInfo.oldPath);
|
||||||
}
|
}
|
||||||
updateStatus();
|
|
||||||
|
|
||||||
m_moveStorageInfo.newPath.clear();
|
m_moveStorageInfo.newPath.clear();
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
||||||
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
||||||
m_moveStorageInfo.queuedPath.clear();
|
m_moveStorageInfo.queuedPath.clear();
|
||||||
@@ -1469,17 +1479,19 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p)
|
void TorrentHandle::handleStorageMovedFailedAlert(const libtorrent::storage_moved_failed_alert *p)
|
||||||
{
|
{
|
||||||
if (!isMoveInProgress()) {
|
if (!isMoveInProgress()) {
|
||||||
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogMsg(QCoreApplication::translate(i18nContext, "Could not move torrent: '%1'. Reason: %2")
|
LogMsg(tr("Could not move torrent: '%1'. Reason: %2")
|
||||||
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
||||||
|
|
||||||
m_moveStorageInfo.newPath.clear();
|
m_moveStorageInfo.newPath.clear();
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
||||||
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
||||||
m_moveStorageInfo.queuedPath.clear();
|
m_moveStorageInfo.queuedPath.clear();
|
||||||
@@ -1489,7 +1501,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p)
|
void TorrentHandle::handleTrackerReplyAlert(const libtorrent::tracker_reply_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString trackerUrl = QString::fromStdString(p->url);
|
QString trackerUrl = QString::fromStdString(p->url);
|
||||||
@@ -1504,32 +1516,32 @@ void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p)
|
|||||||
m_session->handleTorrentTrackerReply(this, trackerUrl);
|
m_session->handleTorrentTrackerReply(this, trackerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p)
|
void TorrentHandle::handleTrackerWarningAlert(const libtorrent::tracker_warning_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString trackerUrl = QString::fromStdString(p->url);
|
const QString trackerUrl = QString::fromStdString(p->url);
|
||||||
QString message = QString::fromStdString(p->msg);
|
const QString message = QString::fromStdString(p->msg);
|
||||||
#else
|
#else
|
||||||
QString trackerUrl(p->tracker_url());
|
const QString trackerUrl = p->tracker_url();
|
||||||
QString message = QString::fromStdString(p->message());
|
const QString message = p->warning_message();
|
||||||
#endif
|
#endif
|
||||||
qDebug("Received a tracker warning for %s: %s", qUtf8Printable(trackerUrl), qUtf8Printable(message));
|
|
||||||
// Connection was successful now but there is a warning message
|
// Connection was successful now but there is a warning message
|
||||||
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
|
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
|
||||||
|
|
||||||
m_session->handleTorrentTrackerWarning(this, trackerUrl);
|
m_session->handleTorrentTrackerWarning(this, trackerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p)
|
void TorrentHandle::handleTrackerErrorAlert(const libtorrent::tracker_error_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString trackerUrl = QString::fromStdString(p->url);
|
const QString trackerUrl = QString::fromStdString(p->url);
|
||||||
QString message = QString::fromStdString(p->msg);
|
const QString message = QString::fromStdString(p->msg);
|
||||||
#else
|
#else
|
||||||
QString trackerUrl(p->tracker_url());
|
const QString trackerUrl = p->tracker_url();
|
||||||
QString message = QString::fromStdString(p->message());
|
const QString message = p->error_message();
|
||||||
#endif
|
#endif
|
||||||
qDebug("Received a tracker error for %s: %s", qUtf8Printable(trackerUrl), qUtf8Printable(message));
|
|
||||||
m_trackerInfos[trackerUrl].lastMessage = message;
|
m_trackerInfos[trackerUrl].lastMessage = message;
|
||||||
|
|
||||||
if (p->status_code == 401)
|
if (p->status_code == 401)
|
||||||
@@ -1538,7 +1550,7 @@ void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p)
|
|||||||
m_session->handleTorrentTrackerError(this, trackerUrl);
|
m_session->handleTorrentTrackerError(this, trackerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p)
|
void TorrentHandle::handleTorrentCheckedAlert(const libtorrent::torrent_checked_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
qDebug("%s have just finished checking", qUtf8Printable(hash()));
|
qDebug("%s have just finished checking", qUtf8Printable(hash()));
|
||||||
@@ -1561,7 +1573,7 @@ void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert
|
|||||||
m_session->handleTorrentChecked(this);
|
m_session->handleTorrentChecked(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p)
|
void TorrentHandle::handleTorrentFinishedAlert(const libtorrent::torrent_finished_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
qDebug("Got a torrent finished alert for %s", qUtf8Printable(name()));
|
qDebug("Got a torrent finished alert for %s", qUtf8Printable(name()));
|
||||||
@@ -1588,7 +1600,7 @@ void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_aler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p)
|
void TorrentHandle::handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
updateStatus();
|
updateStatus();
|
||||||
@@ -1596,13 +1608,17 @@ void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p
|
|||||||
m_session->handleTorrentPaused(this);
|
m_session->handleTorrentPaused(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p)
|
void TorrentHandle::handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
m_session->handleTorrentResumed(this);
|
|
||||||
|
if (m_started)
|
||||||
|
m_session->handleTorrentResumed(this);
|
||||||
|
else
|
||||||
|
m_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p)
|
void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p)
|
||||||
{
|
{
|
||||||
const bool useDummyResumeData = !(p && p->resume_data);
|
const bool useDummyResumeData = !(p && p->resume_data);
|
||||||
libtorrent::entry dummyEntry;
|
libtorrent::entry dummyEntry;
|
||||||
@@ -1636,36 +1652,35 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
|
|||||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p)
|
void TorrentHandle::handleSaveResumeDataFailedAlert(const libtorrent::save_resume_data_failed_alert *p)
|
||||||
{
|
{
|
||||||
// if torrent has no metadata we should save dummy fastresume data
|
// if torrent has no metadata we should save dummy fastresume data
|
||||||
// containing Magnet URI and qBittorrent own resume data only
|
// containing Magnet URI and qBittorrent own resume data only
|
||||||
if (p->error.value() == libt::errors::no_metadata)
|
if (p->error.value() == libt::errors::no_metadata)
|
||||||
handleSaveResumeDataAlert(0);
|
handleSaveResumeDataAlert(nullptr);
|
||||||
else
|
else
|
||||||
m_session->handleTorrentResumeDataFailed(this);
|
m_session->handleTorrentResumeDataFailed(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p)
|
void TorrentHandle::handleFastResumeRejectedAlert(const libtorrent::fastresume_rejected_alert *p)
|
||||||
{
|
{
|
||||||
qDebug("/!\\ Fast resume failed for %s, reason: %s", qUtf8Printable(name()), p->message().c_str());
|
qDebug("/!\\ Fast resume failed for %s, reason: %s", qUtf8Printable(name()), p->message().c_str());
|
||||||
Logger *const logger = Logger::instance();
|
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
if (p->error.value() == libt::errors::mismatching_file_size) {
|
if (p->error.value() == libt::errors::mismatching_file_size) {
|
||||||
// Mismatching file size (files were probably moved)
|
// Mismatching file size (files were probably moved)
|
||||||
logger->addMessage(QCoreApplication::translate(i18nContext, "File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
LogMsg(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
||||||
m_hasMissingFiles = true;
|
m_hasMissingFiles = true;
|
||||||
if (!isPaused())
|
if (!isPaused())
|
||||||
pause();
|
pause();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger->addMessage(QCoreApplication::translate(i18nContext, "Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
LogMsg(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
||||||
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
void TorrentHandle::handleFileRenamedAlert(const libtorrent::file_renamed_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString newName = Utils::Fs::fromNativePath(QString::fromStdString(p->name));
|
QString newName = Utils::Fs::fromNativePath(QString::fromStdString(p->name));
|
||||||
@@ -1676,12 +1691,12 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
|||||||
// TODO: Check this!
|
// TODO: Check this!
|
||||||
if (filesCount() > 1) {
|
if (filesCount() > 1) {
|
||||||
// Check if folders were renamed
|
// Check if folders were renamed
|
||||||
QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split("/");
|
QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split('/');
|
||||||
oldPathParts.removeLast();
|
oldPathParts.removeLast();
|
||||||
QString oldPath = oldPathParts.join("/");
|
QString oldPath = oldPathParts.join('/');
|
||||||
QStringList newPathParts = newName.split("/");
|
QStringList newPathParts = newName.split('/');
|
||||||
newPathParts.removeLast();
|
newPathParts.removeLast();
|
||||||
QString newPath = newPathParts.join("/");
|
QString newPath = newPathParts.join('/');
|
||||||
if (!newPathParts.isEmpty() && (oldPath != newPath)) {
|
if (!newPathParts.isEmpty() && (oldPath != newPath)) {
|
||||||
qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath));
|
qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath));
|
||||||
oldPath = QString("%1/%2").arg(savePath(true), oldPath);
|
oldPath = QString("%1/%2").arg(savePath(true), oldPath);
|
||||||
@@ -1697,7 +1712,7 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFileRenameFailedAlert(libtorrent::file_rename_failed_alert *p)
|
void TorrentHandle::handleFileRenameFailedAlert(const libtorrent::file_rename_failed_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
|
|
||||||
@@ -1706,7 +1721,7 @@ void TorrentHandle::handleFileRenameFailedAlert(libtorrent::file_rename_failed_a
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p)
|
void TorrentHandle::handleFileCompletedAlert(const libtorrent::file_completed_alert *p)
|
||||||
{
|
{
|
||||||
updateStatus();
|
updateStatus();
|
||||||
|
|
||||||
@@ -1722,7 +1737,7 @@ void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleStatsAlert(libtorrent::stats_alert *p)
|
void TorrentHandle::handleStatsAlert(const libtorrent::stats_alert *p)
|
||||||
{
|
{
|
||||||
Q_ASSERT(p->interval >= 1000);
|
Q_ASSERT(p->interval >= 1000);
|
||||||
SpeedSample transferred(p->transferred[libt::stats_alert::download_payload] * 1000LL / p->interval,
|
SpeedSample transferred(p->transferred[libt::stats_alert::download_payload] * 1000LL / p->interval,
|
||||||
@@ -1730,7 +1745,7 @@ void TorrentHandle::handleStatsAlert(libtorrent::stats_alert *p)
|
|||||||
m_speedMonitor.addSample(transferred);
|
m_speedMonitor.addSample(transferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p)
|
void TorrentHandle::handleMetadataReceivedAlert(const libt::metadata_received_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
|
qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
|
||||||
@@ -1809,6 +1824,9 @@ void TorrentHandle::handleAlert(libtorrent::alert *a)
|
|||||||
case libt::torrent_paused_alert::alert_type:
|
case libt::torrent_paused_alert::alert_type:
|
||||||
handleTorrentPausedAlert(static_cast<libt::torrent_paused_alert*>(a));
|
handleTorrentPausedAlert(static_cast<libt::torrent_paused_alert*>(a));
|
||||||
break;
|
break;
|
||||||
|
case libt::torrent_resumed_alert::alert_type:
|
||||||
|
handleTorrentResumedAlert(static_cast<libt::torrent_resumed_alert*>(a));
|
||||||
|
break;
|
||||||
case libt::tracker_error_alert::alert_type:
|
case libt::tracker_error_alert::alert_type:
|
||||||
handleTrackerErrorAlert(static_cast<libt::tracker_error_alert*>(a));
|
handleTrackerErrorAlert(static_cast<libt::tracker_error_alert*>(a));
|
||||||
break;
|
break;
|
||||||
@@ -1834,7 +1852,7 @@ void TorrentHandle::manageIncompleteFiles()
|
|||||||
{
|
{
|
||||||
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
||||||
QVector<qreal> fp = filesProgress();
|
QVector<qreal> fp = filesProgress();
|
||||||
if( fp.size() != filesCount() ) {
|
if (fp.size() != filesCount()) {
|
||||||
qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress";
|
qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1931,7 +1949,6 @@ void TorrentHandle::setRatioLimit(qreal limit)
|
|||||||
|
|
||||||
if (m_ratioLimit != limit) {
|
if (m_ratioLimit != limit) {
|
||||||
m_ratioLimit = limit;
|
m_ratioLimit = limit;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentShareLimitChanged(this);
|
m_session->handleTorrentShareLimitChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1945,7 +1962,6 @@ void TorrentHandle::setSeedingTimeLimit(int limit)
|
|||||||
|
|
||||||
if (m_seedingTimeLimit != limit) {
|
if (m_seedingTimeLimit != limit) {
|
||||||
m_seedingTimeLimit = limit;
|
m_seedingTimeLimit = limit;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentShareLimitChanged(this);
|
m_session->handleTorrentShareLimitChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1983,7 +1999,7 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
if (priorities.size() != filesCount()) return;
|
if (priorities.size() != filesCount()) return;
|
||||||
|
|
||||||
// Save first/last piece first option state
|
// Save first/last piece first option state
|
||||||
bool firstLastPieceFirst = hasFirstLastPiecePriority();
|
const bool firstLastPieceFirst = hasFirstLastPiecePriority();
|
||||||
|
|
||||||
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
||||||
// 'torrent_finished_alert' and eg show tray notifications
|
// 'torrent_finished_alert' and eg show tray notifications
|
||||||
@@ -2010,7 +2026,7 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
// Make sure the file does not already exists
|
// Make sure the file does not already exists
|
||||||
if (QDir(parentAbsPath).dirName() != ".unwanted") {
|
if (QDir(parentAbsPath).dirName() != ".unwanted") {
|
||||||
QString unwantedAbsPath = parentAbsPath + "/.unwanted";
|
QString unwantedAbsPath = parentAbsPath + "/.unwanted";
|
||||||
QString newAbsPath = unwantedAbsPath + "/" + Utils::Fs::fileName(filepath);
|
QString newAbsPath = unwantedAbsPath + '/' + Utils::Fs::fileName(filepath);
|
||||||
qDebug() << "Unwanted path is" << unwantedAbsPath;
|
qDebug() << "Unwanted path is" << unwantedAbsPath;
|
||||||
if (QFile::exists(newAbsPath)) {
|
if (QFile::exists(newAbsPath)) {
|
||||||
qWarning() << "File" << newAbsPath << "already exists at destination.";
|
qWarning() << "File" << newAbsPath << "already exists at destination.";
|
||||||
@@ -2030,8 +2046,8 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
QString parentPath = Utils::Fs::branchPath(filepath);
|
QString parentPath = Utils::Fs::branchPath(filepath);
|
||||||
if (!parentPath.isEmpty() && !parentPath.endsWith("/"))
|
if (!parentPath.isEmpty() && !parentPath.endsWith('/'))
|
||||||
parentPath += "/";
|
parentPath += '/';
|
||||||
renameFile(i, parentPath + ".unwanted/" + Utils::Fs::fileName(filepath));
|
renameFile(i, parentPath + ".unwanted/" + Utils::Fs::fileName(filepath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2048,15 +2064,15 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
renameFile(i, QDir(newRelPath).filePath(oldName));
|
renameFile(i, QDir(newRelPath).filePath(oldName));
|
||||||
|
|
||||||
// Remove .unwanted directory if empty
|
// Remove .unwanted directory if empty
|
||||||
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + newRelPath).absoluteFilePath(".unwanted");
|
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
|
||||||
QDir(spath + "/" + newRelPath).rmdir(".unwanted");
|
QDir(spath + '/' + newRelPath).rmdir(".unwanted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore first/last piece first option if necessary
|
// Restore first/last piece first option if necessary
|
||||||
if (firstLastPieceFirst)
|
if (firstLastPieceFirst)
|
||||||
setFirstLastPiecePriority(true);
|
setFirstLastPiecePriorityImpl(true, priorities);
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ namespace BitTorrent
|
|||||||
class TrackerEntry;
|
class TrackerEntry;
|
||||||
struct AddTorrentParams;
|
struct AddTorrentParams;
|
||||||
|
|
||||||
struct AddTorrentData
|
struct CreateTorrentParams
|
||||||
{
|
{
|
||||||
bool resumed;
|
bool restored; // is existing torrent job?
|
||||||
// for both new and resumed torrents
|
// for both new and restored torrents
|
||||||
QString name;
|
QString name;
|
||||||
QString category;
|
QString category;
|
||||||
QSet<QString> tags;
|
QSet<QString> tags;
|
||||||
@@ -102,18 +102,18 @@ namespace BitTorrent
|
|||||||
bool hasSeedStatus;
|
bool hasSeedStatus;
|
||||||
bool skipChecking;
|
bool skipChecking;
|
||||||
bool hasRootFolder;
|
bool hasRootFolder;
|
||||||
bool addForced;
|
bool forced;
|
||||||
bool addPaused;
|
bool paused;
|
||||||
int uploadLimit;
|
int uploadLimit;
|
||||||
int downloadLimit;
|
int downloadLimit;
|
||||||
// for new torrents
|
// for new torrents
|
||||||
QVector<int> filePriorities;
|
QVector<int> filePriorities;
|
||||||
// for resumed torrents
|
// for restored torrents
|
||||||
qreal ratioLimit;
|
qreal ratioLimit;
|
||||||
int seedingTimeLimit;
|
int seedingTimeLimit;
|
||||||
|
|
||||||
AddTorrentData();
|
CreateTorrentParams();
|
||||||
AddTorrentData(const AddTorrentParams ¶ms);
|
CreateTorrentParams(const AddTorrentParams ¶ms);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TrackerInfo
|
struct TrackerInfo
|
||||||
@@ -149,6 +149,8 @@ namespace BitTorrent
|
|||||||
PausedDownloading,
|
PausedDownloading,
|
||||||
PausedUploading,
|
PausedUploading,
|
||||||
|
|
||||||
|
Moving,
|
||||||
|
|
||||||
MissingFiles,
|
MissingFiles,
|
||||||
Error
|
Error
|
||||||
};
|
};
|
||||||
@@ -168,7 +170,7 @@ namespace BitTorrent
|
|||||||
static const int MAX_SEEDING_TIME;
|
static const int MAX_SEEDING_TIME;
|
||||||
|
|
||||||
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
||||||
const AddTorrentData &data);
|
const CreateTorrentParams ¶ms);
|
||||||
~TorrentHandle();
|
~TorrentHandle();
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
@@ -331,7 +333,7 @@ namespace BitTorrent
|
|||||||
void setName(const QString &name);
|
void setName(const QString &name);
|
||||||
void setSequentialDownload(bool b);
|
void setSequentialDownload(bool b);
|
||||||
void toggleSequentialDownload();
|
void toggleSequentialDownload();
|
||||||
void setFirstLastPiecePriority(bool b);
|
void setFirstLastPiecePriority(bool enabled);
|
||||||
void toggleFirstLastPiecePriority();
|
void toggleFirstLastPiecePriority();
|
||||||
void pause();
|
void pause();
|
||||||
void resume(bool forced = false);
|
void resume(bool forced = false);
|
||||||
@@ -388,23 +390,23 @@ namespace BitTorrent
|
|||||||
void updateState();
|
void updateState();
|
||||||
void updateTorrentInfo();
|
void updateTorrentInfo();
|
||||||
|
|
||||||
void handleStorageMovedAlert(libtorrent::storage_moved_alert *p);
|
void handleStorageMovedAlert(const libtorrent::storage_moved_alert *p);
|
||||||
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p);
|
void handleStorageMovedFailedAlert(const libtorrent::storage_moved_failed_alert *p);
|
||||||
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p);
|
void handleTrackerReplyAlert(const libtorrent::tracker_reply_alert *p);
|
||||||
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p);
|
void handleTrackerWarningAlert(const libtorrent::tracker_warning_alert *p);
|
||||||
void handleTrackerErrorAlert(libtorrent::tracker_error_alert *p);
|
void handleTrackerErrorAlert(const libtorrent::tracker_error_alert *p);
|
||||||
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p);
|
void handleTorrentCheckedAlert(const libtorrent::torrent_checked_alert *p);
|
||||||
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p);
|
void handleTorrentFinishedAlert(const libtorrent::torrent_finished_alert *p);
|
||||||
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p);
|
void handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p);
|
||||||
void handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p);
|
void handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p);
|
||||||
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p);
|
void handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p);
|
||||||
void handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p);
|
void handleSaveResumeDataFailedAlert(const libtorrent::save_resume_data_failed_alert *p);
|
||||||
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p);
|
void handleFastResumeRejectedAlert(const libtorrent::fastresume_rejected_alert *p);
|
||||||
void handleFileRenamedAlert(libtorrent::file_renamed_alert *p);
|
void handleFileRenamedAlert(const libtorrent::file_renamed_alert *p);
|
||||||
void handleFileRenameFailedAlert(libtorrent::file_rename_failed_alert *p);
|
void handleFileRenameFailedAlert(const libtorrent::file_rename_failed_alert *p);
|
||||||
void handleFileCompletedAlert(libtorrent::file_completed_alert *p);
|
void handleFileCompletedAlert(const libtorrent::file_completed_alert *p);
|
||||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
|
||||||
void handleStatsAlert(libtorrent::stats_alert *p);
|
void handleStatsAlert(const libtorrent::stats_alert *p);
|
||||||
|
|
||||||
void resume_impl(bool forced, bool uploadMode);
|
void resume_impl(bool forced, bool uploadMode);
|
||||||
bool isMoveInProgress() const;
|
bool isMoveInProgress() const;
|
||||||
@@ -418,6 +420,7 @@ namespace BitTorrent
|
|||||||
bool addTracker(const TrackerEntry &tracker);
|
bool addTracker(const TrackerEntry &tracker);
|
||||||
bool addUrlSeed(const QUrl &urlSeed);
|
bool addUrlSeed(const QUrl &urlSeed);
|
||||||
bool removeUrlSeed(const QUrl &urlSeed);
|
bool removeUrlSeed(const QUrl &urlSeed);
|
||||||
|
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<int> &updatedFilePrio = {});
|
||||||
|
|
||||||
Session *const m_session;
|
Session *const m_session;
|
||||||
libtorrent::torrent_handle m_nativeHandle;
|
libtorrent::torrent_handle m_nativeHandle;
|
||||||
@@ -459,8 +462,9 @@ namespace BitTorrent
|
|||||||
bool m_needsToSetFirstLastPiecePriority;
|
bool m_needsToSetFirstLastPiecePriority;
|
||||||
|
|
||||||
bool m_pauseAfterRecheck;
|
bool m_pauseAfterRecheck;
|
||||||
bool m_needSaveResumeData;
|
|
||||||
QHash<QString, TrackerInfo> m_trackerInfos;
|
QHash<QString, TrackerInfo> m_trackerInfos;
|
||||||
|
|
||||||
|
bool m_started = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,20 +26,20 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDebug>
|
#include "torrentinfo.h"
|
||||||
#include <QString>
|
|
||||||
#include <QList>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QDateTime>
|
|
||||||
|
|
||||||
#include <libtorrent/error_code.hpp>
|
#include <libtorrent/error_code.hpp>
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
#include "torrentinfo.h"
|
|
||||||
|
|
||||||
namespace libt = libtorrent;
|
namespace libt = libtorrent;
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
@@ -62,46 +62,6 @@ TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
|
|||||||
|
|
||||||
TorrentInfo TorrentInfo::load(const QByteArray &data, QString *error) noexcept
|
TorrentInfo TorrentInfo::load(const QByteArray &data, QString *error) noexcept
|
||||||
{
|
{
|
||||||
libt::error_code ec;
|
|
||||||
TorrentInfo info(NativePtr(new libt::torrent_info(data.constData(), data.size(), ec)));
|
|
||||||
if (error) {
|
|
||||||
if (ec)
|
|
||||||
*error = QString::fromStdString(ec.message());
|
|
||||||
else
|
|
||||||
error->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexcept
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
error->clear();
|
|
||||||
|
|
||||||
QFile file {path};
|
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
|
||||||
if (error)
|
|
||||||
*error = file.errorString();
|
|
||||||
return TorrentInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
const qint64 fileSizeLimit = 100 * 1024 * 1024; // 100 MB
|
|
||||||
if (file.size() > fileSizeLimit) {
|
|
||||||
if (error)
|
|
||||||
*error = tr("File size exceeds max limit %1").arg(fileSizeLimit);
|
|
||||||
return TorrentInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray data = file.read(fileSizeLimit);
|
|
||||||
if (data.size() != file.size()) {
|
|
||||||
if (error)
|
|
||||||
*error = tr("Torrent file read error");
|
|
||||||
return TorrentInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
|
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
|
||||||
// used in `torrent_info()` constructor
|
// used in `torrent_info()` constructor
|
||||||
const int depthLimit = 100;
|
const int depthLimit = 100;
|
||||||
@@ -133,6 +93,45 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexcept
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
error->clear();
|
||||||
|
|
||||||
|
QFile file {path};
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
if (error)
|
||||||
|
*error = file.errorString();
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
const qint64 fileSizeLimit = 100 * 1024 * 1024; // 100 MB
|
||||||
|
if (file.size() > fileSizeLimit) {
|
||||||
|
if (error)
|
||||||
|
*error = tr("File size exceeds max limit %1").arg(fileSizeLimit);
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
try {
|
||||||
|
data = file.readAll();
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc &e) {
|
||||||
|
if (error)
|
||||||
|
*error = tr("Torrent file read error: %1").arg(e.what());
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
if (data.size() != file.size()) {
|
||||||
|
if (error)
|
||||||
|
*error = tr("Torrent file read error: size mismatch");
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return load(data, error);
|
||||||
|
}
|
||||||
|
|
||||||
bool TorrentInfo::isValid() const
|
bool TorrentInfo::isValid() const
|
||||||
{
|
{
|
||||||
return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0));
|
return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0));
|
||||||
@@ -315,7 +314,7 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
|
|||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
|
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
|
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||||
return {};
|
return {};
|
||||||
@@ -353,8 +352,8 @@ void TorrentInfo::renameFile(uint index, const QString &newPath)
|
|||||||
|
|
||||||
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
||||||
{
|
{
|
||||||
// the check whether the object valid is not needed here
|
// the check whether the object is valid is not needed here
|
||||||
// because filesCount() returns -1 in that case and the loop exits immediately
|
// because if filesCount() returns -1 the loop exits immediately
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
if (fileName == filePath(i))
|
if (fileName == filePath(i))
|
||||||
return i;
|
return i;
|
||||||
@@ -362,24 +361,29 @@ int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentInfo::hasRootFolder() const
|
QString TorrentInfo::rootFolder() const
|
||||||
{
|
{
|
||||||
QString testRootFolder;
|
QString rootFolder;
|
||||||
for (int i = 0; i < filesCount(); ++i) {
|
for (int i = 0; i < filesCount(); ++i) {
|
||||||
const QString filePath = this->filePath(i);
|
const QString filePath = this->filePath(i);
|
||||||
if (QDir::isAbsolutePath(filePath)) continue;
|
if (QDir::isAbsolutePath(filePath)) continue;
|
||||||
|
|
||||||
const auto filePathElements = filePath.splitRef('/');
|
const auto filePathElements = filePath.splitRef('/');
|
||||||
// if at least one file has no root folder, no common root folder exists
|
// if at least one file has no root folder, no common root folder exists
|
||||||
if (filePathElements.count() <= 1) return false;
|
if (filePathElements.count() <= 1) return "";
|
||||||
|
|
||||||
if (testRootFolder.isEmpty())
|
if (rootFolder.isEmpty())
|
||||||
testRootFolder = filePathElements.at(0).toString();
|
rootFolder = filePathElements.at(0).toString();
|
||||||
else if (testRootFolder != filePathElements.at(0))
|
else if (rootFolder != filePathElements.at(0))
|
||||||
return false;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return rootFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentInfo::hasRootFolder() const
|
||||||
|
{
|
||||||
|
return !rootFolder().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentInfo::stripRootFolder()
|
void TorrentInfo::stripRootFolder()
|
||||||
|
|||||||
@@ -29,21 +29,21 @@
|
|||||||
#ifndef BITTORRENT_TORRENTINFO_H
|
#ifndef BITTORRENT_TORRENTINFO_H
|
||||||
#define BITTORRENT_TORRENTINFO_H
|
#define BITTORRENT_TORRENTINFO_H
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QList>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
|
|
||||||
class QString;
|
|
||||||
class QUrl;
|
|
||||||
class QDateTime;
|
|
||||||
class QStringList;
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
template<typename T> class QList;
|
class QDateTime;
|
||||||
template<typename T> class QVector;
|
class QString;
|
||||||
|
class QStringList;
|
||||||
|
class QUrl;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
class TorrentInfo
|
class TorrentInfo
|
||||||
{
|
{
|
||||||
Q_DECLARE_TR_FUNCTIONS("TorrentInfo")
|
Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
@@ -104,6 +104,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
void renameFile(uint index, const QString &newPath);
|
void renameFile(uint index, const QString &newPath);
|
||||||
|
|
||||||
|
QString rootFolder() const;
|
||||||
bool hasRootFolder() const;
|
bool hasRootFolder() const;
|
||||||
void stripRootFolder();
|
void stripRootFolder();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "tracker.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
@@ -37,7 +39,6 @@
|
|||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/utils/bytearray.h"
|
#include "base/utils/bytearray.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "tracker.h"
|
|
||||||
|
|
||||||
// static limits
|
// static limits
|
||||||
static const int MAX_TORRENTS = 100;
|
static const int MAX_TORRENTS = 100;
|
||||||
@@ -59,7 +60,7 @@ bool Peer::operator==(const Peer &other) const
|
|||||||
|
|
||||||
QString Peer::uid() const
|
QString Peer::uid() const
|
||||||
{
|
{
|
||||||
return ip + ":" + QString::number(port);
|
return ip + ':' + QString::number(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
libtorrent::entry Peer::toEntry(bool noPeerId) const
|
libtorrent::entry Peer::toEntry(bool noPeerId) const
|
||||||
@@ -277,5 +278,3 @@ void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
|
|||||||
// HTTP reply
|
// HTTP reply
|
||||||
print(reply, Http::CONTENT_TYPE_TXT);
|
print(reply, Http::CONTENT_TYPE_TXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,11 +26,12 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "trackerentry.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "trackerentry.h"
|
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
|||||||
@@ -28,14 +28,14 @@
|
|||||||
|
|
||||||
#include "filesystemwatcher.h"
|
#include "filesystemwatcher.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#include "base/algorithm.h"
|
#include "base/algorithm.h"
|
||||||
#include "base/bittorrent/magneturi.h"
|
#include "base/bittorrent/magneturi.h"
|
||||||
#include "base/bittorrent/torrentinfo.h"
|
#include "base/bittorrent/torrentinfo.h"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
|
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -26,13 +26,10 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
|
||||||
#include <QRegExp>
|
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
|
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,8 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +48,7 @@ namespace Http
|
|||||||
Q_DISABLE_COPY(Connection)
|
Q_DISABLE_COPY(Connection)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = nullptr);
|
||||||
~Connection();
|
~Connection();
|
||||||
|
|
||||||
bool hasExpired(qint64 timeout) const;
|
bool hasExpired(qint64 timeout) const;
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ bool RequestParser::parseFormData(const QByteArray &data)
|
|||||||
const QLatin1String name("name");
|
const QLatin1String name("name");
|
||||||
|
|
||||||
if (headersMap.contains(filename)) {
|
if (headersMap.contains(filename)) {
|
||||||
m_request.files.append({filename, headersMap[HEADER_CONTENT_TYPE], payload});
|
m_request.files.append({headersMap[filename], headersMap[HEADER_CONTENT_TYPE], payload});
|
||||||
}
|
}
|
||||||
else if (headersMap.contains(name)) {
|
else if (headersMap.contains(name)) {
|
||||||
m_request.posts[headersMap[name]] = payload;
|
m_request.posts[headersMap[name]] = payload;
|
||||||
|
|||||||
@@ -63,10 +63,12 @@ namespace Http
|
|||||||
const char HEADER_REQUEST_METHOD_POST[] = "POST";
|
const char HEADER_REQUEST_METHOD_POST[] = "POST";
|
||||||
|
|
||||||
const char CONTENT_TYPE_HTML[] = "text/html";
|
const char CONTENT_TYPE_HTML[] = "text/html";
|
||||||
|
const char CONTENT_TYPE_CSS[] = "text/css";
|
||||||
|
const char CONTENT_TYPE_TXT[] = "text/plain";
|
||||||
|
const char CONTENT_TYPE_JS[] = "application/javascript";
|
||||||
const char CONTENT_TYPE_JSON[] = "application/json";
|
const char CONTENT_TYPE_JSON[] = "application/json";
|
||||||
const char CONTENT_TYPE_GIF[] = "image/gif";
|
const char CONTENT_TYPE_GIF[] = "image/gif";
|
||||||
const char CONTENT_TYPE_PNG[] = "image/png";
|
const char CONTENT_TYPE_PNG[] = "image/png";
|
||||||
const char CONTENT_TYPE_TXT[] = "text/plain";
|
|
||||||
const char CONTENT_TYPE_FORM_ENCODED[] = "application/x-www-form-urlencoded";
|
const char CONTENT_TYPE_FORM_ENCODED[] = "application/x-www-form-urlencoded";
|
||||||
const char CONTENT_TYPE_FORM_DATA[] = "multipart/form-data";
|
const char CONTENT_TYPE_FORM_DATA[] = "multipart/form-data";
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include "iconprovider.h"
|
#include "iconprovider.h"
|
||||||
|
|
||||||
IconProvider::IconProvider(QObject *parent)
|
IconProvider::IconProvider(QObject *parent)
|
||||||
@@ -47,7 +46,7 @@ void IconProvider::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,9 +55,9 @@ IconProvider *IconProvider::instance()
|
|||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString IconProvider::getIconPath(const QString &iconId)
|
QString IconProvider::getIconPath(const QString &iconId) const
|
||||||
{
|
{
|
||||||
return ":/icons/qbt-theme/" + iconId + ".png";
|
return ":/icons/qbt-theme/" + iconId + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
IconProvider *IconProvider::m_instance = 0;
|
IconProvider *IconProvider::m_instance = nullptr;
|
||||||
|
|||||||
@@ -31,8 +31,7 @@
|
|||||||
#define ICONPROVIDER_H
|
#define ICONPROVIDER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
class QString;
|
|
||||||
|
|
||||||
class IconProvider : public QObject
|
class IconProvider : public QObject
|
||||||
{
|
{
|
||||||
@@ -43,10 +42,10 @@ public:
|
|||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static IconProvider *instance();
|
static IconProvider *instance();
|
||||||
|
|
||||||
virtual QString getIconPath(const QString &iconId);
|
virtual QString getIconPath(const QString &iconId) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit IconProvider(QObject *parent = 0);
|
explicit IconProvider(QObject *parent = nullptr);
|
||||||
~IconProvider();
|
~IconProvider();
|
||||||
|
|
||||||
static IconProvider *m_instance;
|
static IconProvider *m_instance;
|
||||||
|
|||||||
@@ -38,30 +38,30 @@ class IndexInterval
|
|||||||
public:
|
public:
|
||||||
using IndexType = Index;
|
using IndexType = Index;
|
||||||
|
|
||||||
IndexInterval(IndexType first, IndexType last)
|
IndexInterval(IndexType first, IndexType last) // add constexpr when using C++14
|
||||||
: m_first {first}
|
: m_first {first}
|
||||||
, m_last {last}
|
, m_last {last}
|
||||||
{
|
{
|
||||||
Q_ASSERT(first <= last);
|
Q_ASSERT(first <= last);
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexType first() const
|
constexpr IndexType first() const
|
||||||
{
|
{
|
||||||
return m_first;
|
return m_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexType last() const
|
constexpr IndexType last() const
|
||||||
{
|
{
|
||||||
return m_last;
|
return m_last;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IndexType m_first;
|
const IndexType m_first;
|
||||||
IndexType m_last;
|
const IndexType m_last;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline IndexInterval<T> makeInterval(T first, T last)
|
constexpr IndexInterval<T> makeInterval(T first, T last)
|
||||||
{
|
{
|
||||||
return {first, last};
|
return {first, last};
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ public:
|
|||||||
|
|
||||||
constexpr IndexType end() const
|
constexpr IndexType end() const
|
||||||
{
|
{
|
||||||
return m_first + m_size;
|
return (m_first + m_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr IndexDiffType size() const
|
constexpr IndexDiffType size() const
|
||||||
@@ -114,12 +114,12 @@ public:
|
|||||||
|
|
||||||
constexpr IndexType last() const
|
constexpr IndexType last() const
|
||||||
{
|
{
|
||||||
return m_first + m_size - 1;
|
return (m_first + m_size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool isEmpty() const
|
constexpr bool isEmpty() const
|
||||||
{
|
{
|
||||||
return m_size == 0;
|
return (m_size == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
|
|
||||||
Logger* Logger::m_instance = 0;
|
Logger *Logger::m_instance = nullptr;
|
||||||
|
|
||||||
Logger::Logger()
|
Logger::Logger()
|
||||||
: lock(QReadWriteLock::Recursive)
|
: m_lock(QReadWriteLock::Recursive)
|
||||||
, msgCounter(0)
|
, m_msgCounter(0)
|
||||||
, peerCounter(0)
|
, m_peerCounter(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,15 +29,15 @@ void Logger::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
||||||
{
|
{
|
||||||
QWriteLocker locker(&lock);
|
QWriteLocker locker(&m_lock);
|
||||||
|
|
||||||
Log::Msg temp = { msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped() };
|
Log::Msg temp = {m_msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped()};
|
||||||
m_messages.push_back(temp);
|
m_messages.push_back(temp);
|
||||||
|
|
||||||
if (m_messages.size() >= MAX_LOG_MESSAGES)
|
if (m_messages.size() >= MAX_LOG_MESSAGES)
|
||||||
@@ -48,9 +48,9 @@ void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
|||||||
|
|
||||||
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
||||||
{
|
{
|
||||||
QWriteLocker locker(&lock);
|
QWriteLocker locker(&m_lock);
|
||||||
|
|
||||||
Log::Peer temp = { peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped() };
|
Log::Peer temp = {m_peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped()};
|
||||||
m_peers.push_back(temp);
|
m_peers.push_back(temp);
|
||||||
|
|
||||||
if (m_peers.size() >= MAX_LOG_MESSAGES)
|
if (m_peers.size() >= MAX_LOG_MESSAGES)
|
||||||
@@ -61,9 +61,9 @@ void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
|||||||
|
|
||||||
QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
|
QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
|
||||||
{
|
{
|
||||||
QReadLocker locker(&lock);
|
QReadLocker locker(&m_lock);
|
||||||
|
|
||||||
int diff = msgCounter - lastKnownId - 1;
|
int diff = m_msgCounter - lastKnownId - 1;
|
||||||
int size = m_messages.size();
|
int size = m_messages.size();
|
||||||
|
|
||||||
if ((lastKnownId == -1) || (diff >= size))
|
if ((lastKnownId == -1) || (diff >= size))
|
||||||
@@ -77,9 +77,9 @@ QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
|
|||||||
|
|
||||||
QVector<Log::Peer> Logger::getPeers(int lastKnownId) const
|
QVector<Log::Peer> Logger::getPeers(int lastKnownId) const
|
||||||
{
|
{
|
||||||
QReadLocker locker(&lock);
|
QReadLocker locker(&m_lock);
|
||||||
|
|
||||||
int diff = peerCounter - lastKnownId - 1;
|
int diff = m_peerCounter - lastKnownId - 1;
|
||||||
int size = m_peers.size();
|
int size = m_peers.size();
|
||||||
|
|
||||||
if ((lastKnownId == -1) || (diff >= size))
|
if ((lastKnownId == -1) || (diff >= size))
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#ifndef LOGGER_H
|
#ifndef LOGGER_H
|
||||||
#define LOGGER_H
|
#define LOGGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QReadWriteLock>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QReadWriteLock>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
const int MAX_LOG_MESSAGES = 20000;
|
const int MAX_LOG_MESSAGES = 20000;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ namespace Log
|
|||||||
NORMAL = 0x1,
|
NORMAL = 0x1,
|
||||||
INFO = 0x2,
|
INFO = 0x2,
|
||||||
WARNING = 0x4,
|
WARNING = 0x4,
|
||||||
CRITICAL = 0x8 //ERROR is defined by libtorrent and results in compiler error
|
CRITICAL = 0x8 // ERROR is defined by libtorrent and results in compiler error
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(MsgTypes, MsgType)
|
Q_DECLARE_FLAGS(MsgTypes, MsgType)
|
||||||
|
|
||||||
@@ -63,12 +63,12 @@ private:
|
|||||||
Logger();
|
Logger();
|
||||||
~Logger();
|
~Logger();
|
||||||
|
|
||||||
static Logger* m_instance;
|
static Logger *m_instance;
|
||||||
QVector<Log::Msg> m_messages;
|
QVector<Log::Msg> m_messages;
|
||||||
QVector<Log::Peer> m_peers;
|
QVector<Log::Peer> m_peers;
|
||||||
mutable QReadWriteLock lock;
|
mutable QReadWriteLock m_lock;
|
||||||
int msgCounter;
|
int m_msgCounter;
|
||||||
int peerCounter;
|
int m_peerCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function
|
// Helper function
|
||||||
|
|||||||
@@ -26,15 +26,15 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "dnsupdater.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QRegExp>
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/net/downloadhandler.h"
|
#include "base/net/downloadhandler.h"
|
||||||
#include "base/net/downloadmanager.h"
|
#include "base/net/downloadmanager.h"
|
||||||
#include "dnsupdater.h"
|
|
||||||
|
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ DNSUpdater::DNSUpdater(QObject *parent)
|
|||||||
|
|
||||||
// Start IP checking timer
|
// Start IP checking timer
|
||||||
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
||||||
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
connect(&m_ipCheckTimer, &QTimer::timeout, this, &DNSUpdater::checkPublicIP);
|
||||||
m_ipCheckTimer.start();
|
m_ipCheckTimer.start();
|
||||||
|
|
||||||
// Check lastUpdate to avoid flooding
|
// Check lastUpdate to avoid flooding
|
||||||
@@ -74,11 +74,11 @@ void DNSUpdater::checkPublicIP()
|
|||||||
{
|
{
|
||||||
Q_ASSERT(m_state == OK);
|
Q_ASSERT(m_state == OK);
|
||||||
|
|
||||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
|
DownloadHandler *handler = DownloadManager::instance()->download(
|
||||||
"http://checkip.dyndns.org", false, 0, false,
|
DownloadRequest("http://checkip.dyndns.org").userAgent("qBittorrent/" QBT_VERSION_2));
|
||||||
"qBittorrent/" QBT_VERSION_2);
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipRequestFinished(QString, QByteArray)));
|
, this, &DNSUpdater::ipRequestFinished);
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipRequestFailed(QString, QString)));
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &DNSUpdater::ipRequestFailed);
|
||||||
|
|
||||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
@@ -88,9 +88,9 @@ void DNSUpdater::ipRequestFinished(const QString &url, const QByteArray &data)
|
|||||||
Q_UNUSED(url);
|
Q_UNUSED(url);
|
||||||
|
|
||||||
// Parse response
|
// Parse response
|
||||||
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
const QRegularExpressionMatch ipRegexMatch = QRegularExpression("Current IP Address:\\s+([^<]+)</body>").match(data);
|
||||||
if (ipregex.indexIn(data) >= 0) {
|
if (ipRegexMatch.hasMatch()) {
|
||||||
QString ipStr = ipregex.cap(1);
|
QString ipStr = ipRegexMatch.captured(1);
|
||||||
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ipStr;
|
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ipStr;
|
||||||
QHostAddress newIp(ipStr);
|
QHostAddress newIp(ipStr);
|
||||||
if (!newIp.isNull()) {
|
if (!newIp.isNull()) {
|
||||||
@@ -121,11 +121,11 @@ void DNSUpdater::updateDNSService()
|
|||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
|
DownloadHandler *handler = DownloadManager::instance()->download(
|
||||||
getUpdateUrl(), false, 0, false,
|
DownloadRequest(getUpdateUrl()).userAgent("qBittorrent/" QBT_VERSION_2));
|
||||||
"qBittorrent/" QBT_VERSION_2);
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipUpdateFinished(QString, QByteArray)));
|
, this, &DNSUpdater::ipUpdateFinished);
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipUpdateFailed(QString, QString)));
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &DNSUpdater::ipUpdateFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DNSUpdater::getUpdateUrl() const
|
QString DNSUpdater::getUpdateUrl() const
|
||||||
@@ -181,7 +181,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
|
|||||||
{
|
{
|
||||||
Logger *const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
qDebug() << Q_FUNC_INFO << reply;
|
qDebug() << Q_FUNC_INFO << reply;
|
||||||
QString code = reply.split(" ").first();
|
QString code = reply.split(' ').first();
|
||||||
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
||||||
|
|
||||||
if ((code == "good") || (code == "nochg")) {
|
if ((code == "good") || (code == "nochg")) {
|
||||||
@@ -244,8 +244,8 @@ void DNSUpdater::updateCredentials()
|
|||||||
}
|
}
|
||||||
if (m_domain != pref->getDynDomainName()) {
|
if (m_domain != pref->getDynDomainName()) {
|
||||||
m_domain = pref->getDynDomainName();
|
m_domain = pref->getDynDomainName();
|
||||||
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
const QRegularExpressionMatch domainRegexMatch = QRegularExpression("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$").match(m_domain);
|
||||||
if (domain_regex.indexIn(m_domain) < 0) {
|
if (!domainRegexMatch.hasMatch()) {
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
||||||
m_lastIP.clear();
|
m_lastIP.clear();
|
||||||
m_ipCheckTimer.stop();
|
m_ipCheckTimer.stop();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
|
|
||||||
#include "downloadhandler.h"
|
#include "downloadhandler.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkCookie>
|
#include <QNetworkCookie>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
@@ -43,35 +43,57 @@
|
|||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "downloadmanager.h"
|
#include "downloadmanager.h"
|
||||||
|
|
||||||
static QString errorCodeToString(QNetworkReply::NetworkError status);
|
namespace
|
||||||
|
{
|
||||||
|
bool saveToFile(const QByteArray &replyData, QString &filePath)
|
||||||
|
{
|
||||||
|
QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"};
|
||||||
|
tmpfile.setAutoRemove(false);
|
||||||
|
|
||||||
using namespace Net;
|
if (!tmpfile.open())
|
||||||
|
return false;
|
||||||
|
|
||||||
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
|
filePath = tmpfile.fileName();
|
||||||
|
|
||||||
|
tmpfile.write(replyData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest)
|
||||||
: QObject(manager)
|
: QObject(manager)
|
||||||
, m_reply(reply)
|
, m_reply(reply)
|
||||||
, m_manager(manager)
|
, m_manager(manager)
|
||||||
, m_saveToFile(saveToFile)
|
, m_downloadRequest(downloadRequest)
|
||||||
, m_sizeLimit(limit)
|
|
||||||
, m_handleRedirectToMagnet(handleRedirectToMagnet)
|
|
||||||
, m_url(reply->url().toString())
|
|
||||||
{
|
{
|
||||||
init();
|
if (reply)
|
||||||
|
assignNetworkReply(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadHandler::~DownloadHandler()
|
Net::DownloadHandler::~DownloadHandler()
|
||||||
{
|
{
|
||||||
if (m_reply)
|
if (m_reply)
|
||||||
delete m_reply;
|
delete m_reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns original url
|
void Net::DownloadHandler::assignNetworkReply(QNetworkReply *reply)
|
||||||
QString DownloadHandler::url() const
|
|
||||||
{
|
{
|
||||||
return m_url;
|
Q_ASSERT(reply);
|
||||||
|
|
||||||
|
m_reply = reply;
|
||||||
|
m_reply->setParent(this);
|
||||||
|
if (m_downloadRequest.limit() > 0)
|
||||||
|
connect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize);
|
||||||
|
connect(m_reply, &QNetworkReply::finished, this, &Net::DownloadHandler::processFinishedDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadHandler::processFinishedDownload()
|
// Returns original url
|
||||||
|
QString Net::DownloadHandler::url() const
|
||||||
|
{
|
||||||
|
return m_downloadRequest.url();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Net::DownloadHandler::processFinishedDownload()
|
||||||
{
|
{
|
||||||
QString url = m_reply->url().toString();
|
QString url = m_reply->url().toString();
|
||||||
qDebug("Download finished: %s", qUtf8Printable(url));
|
qDebug("Download finished: %s", qUtf8Printable(url));
|
||||||
@@ -79,7 +101,7 @@ void DownloadHandler::processFinishedDownload()
|
|||||||
if (m_reply->error() != QNetworkReply::NoError) {
|
if (m_reply->error() != QNetworkReply::NoError) {
|
||||||
// Failure
|
// Failure
|
||||||
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error())));
|
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error())));
|
||||||
emit downloadFailed(m_url, errorCodeToString(m_reply->error()));
|
emit downloadFailed(m_downloadRequest.url(), errorCodeToString(m_reply->error()));
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -97,15 +119,15 @@ void DownloadHandler::processFinishedDownload()
|
|||||||
replyData = Utils::Gzip::decompress(replyData);
|
replyData = Utils::Gzip::decompress(replyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_saveToFile) {
|
if (m_downloadRequest.saveToFile()) {
|
||||||
QString filePath;
|
QString filePath;
|
||||||
if (saveToFile(replyData, filePath))
|
if (saveToFile(replyData, filePath))
|
||||||
emit downloadFinished(m_url, filePath);
|
emit downloadFinished(m_downloadRequest.url(), filePath);
|
||||||
else
|
else
|
||||||
emit downloadFailed(m_url, tr("I/O Error"));
|
emit downloadFailed(m_downloadRequest.url(), tr("I/O Error"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
emit downloadFinished(m_url, replyData);
|
emit downloadFinished(m_downloadRequest.url(), replyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
@@ -113,137 +135,116 @@ void DownloadHandler::processFinishedDownload()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
|
void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
|
||||||
{
|
{
|
||||||
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
|
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
|
||||||
|
|
||||||
if (bytesTotal > 0) {
|
if (bytesTotal > 0) {
|
||||||
// Total number of bytes is available
|
// Total number of bytes is available
|
||||||
if (bytesTotal > m_sizeLimit) {
|
if (bytesTotal > m_downloadRequest.limit()) {
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal), Utils::Misc::friendlyUnit(m_sizeLimit)));
|
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesTotal), Utils::Misc::friendlyUnit(m_downloadRequest.limit())));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
disconnect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bytesReceived > m_sizeLimit) {
|
else if (bytesReceived > m_downloadRequest.limit()) {
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived), Utils::Misc::friendlyUnit(m_sizeLimit)));
|
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesReceived), Utils::Misc::friendlyUnit(m_downloadRequest.limit())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadHandler::init()
|
void Net::DownloadHandler::handleRedirection(QUrl newUrl)
|
||||||
{
|
|
||||||
m_reply->setParent(this);
|
|
||||||
if (m_sizeLimit > 0)
|
|
||||||
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
|
||||||
connect(m_reply, SIGNAL(finished()), this, SLOT(processFinishedDownload()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath)
|
|
||||||
{
|
|
||||||
QTemporaryFile *tmpfile = new QTemporaryFile(Utils::Fs::tempPath() + "XXXXXX");
|
|
||||||
if (!tmpfile->open()) {
|
|
||||||
delete tmpfile;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpfile->setAutoRemove(false);
|
|
||||||
filePath = tmpfile->fileName();
|
|
||||||
qDebug("Temporary filename is: %s", qUtf8Printable(filePath));
|
|
||||||
if (m_reply->isOpen() || m_reply->open(QIODevice::ReadOnly)) {
|
|
||||||
tmpfile->write(replyData);
|
|
||||||
tmpfile->close();
|
|
||||||
// XXX: tmpfile needs to be deleted on Windows before using the file
|
|
||||||
// or it will complain that the file is used by another process.
|
|
||||||
delete tmpfile;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
delete tmpfile;
|
|
||||||
Utils::Fs::forceRemove(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadHandler::handleRedirection(QUrl newUrl)
|
|
||||||
{
|
{
|
||||||
// Resolve relative urls
|
// Resolve relative urls
|
||||||
if (newUrl.isRelative())
|
if (newUrl.isRelative())
|
||||||
newUrl = m_reply->url().resolved(newUrl);
|
newUrl = m_reply->url().resolved(newUrl);
|
||||||
|
|
||||||
const QString newUrlString = newUrl.toString();
|
const QString newUrlString = newUrl.toString();
|
||||||
qDebug("Redirecting from %s to %s", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
|
qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
|
||||||
|
|
||||||
// Redirect to magnet workaround
|
// Redirect to magnet workaround
|
||||||
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||||
qDebug("Magnet redirect detected.");
|
qDebug("Magnet redirect detected.");
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
if (m_handleRedirectToMagnet)
|
if (m_downloadRequest.handleRedirectToMagnet())
|
||||||
emit redirectedToMagnet(m_url, newUrlString);
|
emit redirectedToMagnet(m_downloadRequest.url(), newUrlString);
|
||||||
else
|
else
|
||||||
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI."));
|
emit downloadFailed(m_downloadRequest.url(), tr("Unexpected redirect to magnet URI."));
|
||||||
|
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_saveToFile, m_sizeLimit, m_handleRedirectToMagnet);
|
DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString));
|
||||||
m_reply->deleteLater();
|
connect(redirected, &DownloadHandler::destroyed, this, &DownloadHandler::deleteLater);
|
||||||
m_reply = tmp->m_reply;
|
connect(redirected, &DownloadHandler::downloadFailed, this, [this](const QString &, const QString &reason)
|
||||||
init();
|
{
|
||||||
tmp->m_reply = nullptr;
|
emit downloadFailed(url(), reason);
|
||||||
delete tmp;
|
});
|
||||||
|
connect(redirected, &DownloadHandler::redirectedToMagnet, this, [this](const QString &, const QString &magnetUri)
|
||||||
|
{
|
||||||
|
emit redirectedToMagnet(url(), magnetUri);
|
||||||
|
});
|
||||||
|
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished)
|
||||||
|
, this, [this](const QString &, const QString &fileName)
|
||||||
|
{
|
||||||
|
emit downloadFinished(url(), fileName);
|
||||||
|
});
|
||||||
|
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished)
|
||||||
|
, this, [this](const QString &, const QByteArray &data)
|
||||||
|
{
|
||||||
|
emit downloadFinished(url(), data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString errorCodeToString(QNetworkReply::NetworkError status)
|
QString Net::DownloadHandler::errorCodeToString(const QNetworkReply::NetworkError status)
|
||||||
{
|
{
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case QNetworkReply::HostNotFoundError:
|
case QNetworkReply::HostNotFoundError:
|
||||||
return QObject::tr("The remote host name was not found (invalid hostname)");
|
return tr("The remote host name was not found (invalid hostname)");
|
||||||
case QNetworkReply::OperationCanceledError:
|
case QNetworkReply::OperationCanceledError:
|
||||||
return QObject::tr("The operation was canceled");
|
return tr("The operation was canceled");
|
||||||
case QNetworkReply::RemoteHostClosedError:
|
case QNetworkReply::RemoteHostClosedError:
|
||||||
return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
return tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
||||||
case QNetworkReply::TimeoutError:
|
case QNetworkReply::TimeoutError:
|
||||||
return QObject::tr("The connection to the remote server timed out");
|
return tr("The connection to the remote server timed out");
|
||||||
case QNetworkReply::SslHandshakeFailedError:
|
case QNetworkReply::SslHandshakeFailedError:
|
||||||
return QObject::tr("SSL/TLS handshake failed");
|
return tr("SSL/TLS handshake failed");
|
||||||
case QNetworkReply::ConnectionRefusedError:
|
case QNetworkReply::ConnectionRefusedError:
|
||||||
return QObject::tr("The remote server refused the connection");
|
return tr("The remote server refused the connection");
|
||||||
case QNetworkReply::ProxyConnectionRefusedError:
|
case QNetworkReply::ProxyConnectionRefusedError:
|
||||||
return QObject::tr("The connection to the proxy server was refused");
|
return tr("The connection to the proxy server was refused");
|
||||||
case QNetworkReply::ProxyConnectionClosedError:
|
case QNetworkReply::ProxyConnectionClosedError:
|
||||||
return QObject::tr("The proxy server closed the connection prematurely");
|
return tr("The proxy server closed the connection prematurely");
|
||||||
case QNetworkReply::ProxyNotFoundError:
|
case QNetworkReply::ProxyNotFoundError:
|
||||||
return QObject::tr("The proxy host name was not found");
|
return tr("The proxy host name was not found");
|
||||||
case QNetworkReply::ProxyTimeoutError:
|
case QNetworkReply::ProxyTimeoutError:
|
||||||
return QObject::tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
||||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||||
return QObject::tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
|
return tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
|
||||||
case QNetworkReply::ContentAccessDenied:
|
case QNetworkReply::ContentAccessDenied:
|
||||||
return QObject::tr("The access to the remote content was denied (401)");
|
return tr("The access to the remote content was denied (401)");
|
||||||
case QNetworkReply::ContentOperationNotPermittedError:
|
case QNetworkReply::ContentOperationNotPermittedError:
|
||||||
return QObject::tr("The operation requested on the remote content is not permitted");
|
return tr("The operation requested on the remote content is not permitted");
|
||||||
case QNetworkReply::ContentNotFoundError:
|
case QNetworkReply::ContentNotFoundError:
|
||||||
return QObject::tr("The remote content was not found at the server (404)");
|
return tr("The remote content was not found at the server (404)");
|
||||||
case QNetworkReply::AuthenticationRequiredError:
|
case QNetworkReply::AuthenticationRequiredError:
|
||||||
return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
||||||
case QNetworkReply::ProtocolUnknownError:
|
case QNetworkReply::ProtocolUnknownError:
|
||||||
return QObject::tr("The Network Access API cannot honor the request because the protocol is not known");
|
return tr("The Network Access API cannot honor the request because the protocol is not known");
|
||||||
case QNetworkReply::ProtocolInvalidOperationError:
|
case QNetworkReply::ProtocolInvalidOperationError:
|
||||||
return QObject::tr("The requested operation is invalid for this protocol");
|
return tr("The requested operation is invalid for this protocol");
|
||||||
case QNetworkReply::UnknownNetworkError:
|
case QNetworkReply::UnknownNetworkError:
|
||||||
return QObject::tr("An unknown network-related error was detected");
|
return tr("An unknown network-related error was detected");
|
||||||
case QNetworkReply::UnknownProxyError:
|
case QNetworkReply::UnknownProxyError:
|
||||||
return QObject::tr("An unknown proxy-related error was detected");
|
return tr("An unknown proxy-related error was detected");
|
||||||
case QNetworkReply::UnknownContentError:
|
case QNetworkReply::UnknownContentError:
|
||||||
return QObject::tr("An unknown error related to the remote content was detected");
|
return tr("An unknown error related to the remote content was detected");
|
||||||
case QNetworkReply::ProtocolFailure:
|
case QNetworkReply::ProtocolFailure:
|
||||||
return QObject::tr("A breakdown in protocol was detected");
|
return tr("A breakdown in protocol was detected");
|
||||||
default:
|
default:
|
||||||
return QObject::tr("Unknown error");
|
return tr("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -30,10 +30,11 @@
|
|||||||
#ifndef NET_DOWNLOADHANDLER_H
|
#ifndef NET_DOWNLOADHANDLER_H
|
||||||
#define NET_DOWNLOADHANDLER_H
|
#define NET_DOWNLOADHANDLER_H
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
#include "downloadmanager.h"
|
||||||
class QNetworkReply;
|
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
@@ -43,10 +44,14 @@ namespace Net
|
|||||||
class DownloadHandler : public QObject
|
class DownloadHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(DownloadHandler)
|
||||||
|
|
||||||
|
friend class DownloadManager;
|
||||||
|
|
||||||
|
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
|
~DownloadHandler() override;
|
||||||
~DownloadHandler();
|
|
||||||
|
|
||||||
QString url() const;
|
QString url() const;
|
||||||
|
|
||||||
@@ -61,16 +66,14 @@ namespace Net
|
|||||||
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void assignNetworkReply(QNetworkReply *reply);
|
||||||
bool saveToFile(const QByteArray &replyData, QString &filePath);
|
|
||||||
void handleRedirection(QUrl newUrl);
|
void handleRedirection(QUrl newUrl);
|
||||||
|
|
||||||
|
static QString errorCodeToString(QNetworkReply::NetworkError status);
|
||||||
|
|
||||||
QNetworkReply *m_reply;
|
QNetworkReply *m_reply;
|
||||||
DownloadManager *m_manager;
|
DownloadManager *m_manager;
|
||||||
bool m_saveToFile;
|
const DownloadRequest m_downloadRequest;
|
||||||
qint64 m_sizeLimit;
|
|
||||||
bool m_handleRedirectToMagnet;
|
|
||||||
QString m_url;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -48,10 +48,10 @@ const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class NetworkCookieJar: public QNetworkCookieJar
|
class NetworkCookieJar : public QNetworkCookieJar
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NetworkCookieJar(QObject *parent = 0)
|
explicit NetworkCookieJar(QObject *parent = nullptr)
|
||||||
: QNetworkCookieJar(parent)
|
: QNetworkCookieJar(parent)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
@@ -103,91 +103,113 @@ namespace
|
|||||||
return QNetworkCookieJar::setCookiesFromUrl(cookies, url);
|
return QNetworkCookieJar::setCookiesFromUrl(cookies, url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QNetworkRequest createNetworkRequest(const Net::DownloadRequest &downloadRequest)
|
||||||
|
{
|
||||||
|
QNetworkRequest request {downloadRequest.url()};
|
||||||
|
|
||||||
|
if (downloadRequest.userAgent().isEmpty())
|
||||||
|
request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
|
||||||
|
else
|
||||||
|
request.setRawHeader("User-Agent", downloadRequest.userAgent().toUtf8());
|
||||||
|
|
||||||
|
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
||||||
|
request.setRawHeader("Referer", request.url().toEncoded().data());
|
||||||
|
// Accept gzip
|
||||||
|
request.setRawHeader("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Net;
|
Net::DownloadManager *Net::DownloadManager::m_instance = nullptr;
|
||||||
|
|
||||||
DownloadManager *DownloadManager::m_instance = 0;
|
Net::DownloadManager::DownloadManager(QObject *parent)
|
||||||
|
|
||||||
DownloadManager::DownloadManager(QObject *parent)
|
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
|
connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this, &Net::DownloadManager::ignoreSslErrors);
|
||||||
#endif
|
#endif
|
||||||
|
connect(&m_networkManager, &QNetworkAccessManager::finished, this, &DownloadManager::handleReplyFinished);
|
||||||
|
connect(ProxyConfigurationManager::instance(), &ProxyConfigurationManager::proxyConfigurationChanged
|
||||||
|
, this, &DownloadManager::applyProxySettings);
|
||||||
m_networkManager.setCookieJar(new NetworkCookieJar(this));
|
m_networkManager.setCookieJar(new NetworkCookieJar(this));
|
||||||
|
applyProxySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::initInstance()
|
void Net::DownloadManager::initInstance()
|
||||||
{
|
{
|
||||||
if (!m_instance)
|
if (!m_instance)
|
||||||
m_instance = new DownloadManager;
|
m_instance = new DownloadManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::freeInstance()
|
void Net::DownloadManager::freeInstance()
|
||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadManager *DownloadManager::instance()
|
Net::DownloadManager *Net::DownloadManager::instance()
|
||||||
{
|
{
|
||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet, const QString &userAgent)
|
Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &downloadRequest)
|
||||||
{
|
{
|
||||||
// Update proxy settings
|
|
||||||
applyProxySettings();
|
|
||||||
|
|
||||||
// Process download request
|
// Process download request
|
||||||
qDebug("url is %s", qUtf8Printable(url));
|
const QNetworkRequest request = createNetworkRequest(downloadRequest);
|
||||||
const QUrl qurl = QUrl(url);
|
const ServiceID id = ServiceID::fromURL(request.url());
|
||||||
QNetworkRequest request(qurl);
|
const bool isSequentialService = m_sequentialServices.contains(id);
|
||||||
|
if (!isSequentialService || !m_busyServices.contains(id)) {
|
||||||
|
qDebug("Downloading %s...", qUtf8Printable(downloadRequest.url()));
|
||||||
|
if (isSequentialService)
|
||||||
|
m_busyServices.insert(id);
|
||||||
|
return new DownloadHandler {
|
||||||
|
m_networkManager.get(request), this, downloadRequest};
|
||||||
|
}
|
||||||
|
|
||||||
if (userAgent.isEmpty())
|
auto *downloadHandler = new DownloadHandler {nullptr, this, downloadRequest};
|
||||||
request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
|
connect(downloadHandler, &DownloadHandler::destroyed, this, [this, id, downloadHandler]()
|
||||||
else
|
{
|
||||||
request.setRawHeader("User-Agent", userAgent.toUtf8());
|
m_waitingJobs[id].removeOne(downloadHandler);
|
||||||
|
});
|
||||||
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
m_waitingJobs[id].enqueue(downloadHandler);
|
||||||
request.setRawHeader("Referer", request.url().toEncoded().data());
|
return downloadHandler;
|
||||||
|
|
||||||
qDebug("Downloading %s...", request.url().toEncoded().data());
|
|
||||||
qDebug() << "Cookies:" << m_networkManager.cookieJar()->cookiesForUrl(request.url());
|
|
||||||
// accept gzip
|
|
||||||
request.setRawHeader("Accept-Encoding", "gzip");
|
|
||||||
return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QUrl &url) const
|
void Net::DownloadManager::registerSequentialService(const Net::ServiceID &serviceID)
|
||||||
|
{
|
||||||
|
m_sequentialServices.insert(serviceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QNetworkCookie> Net::DownloadManager::cookiesForUrl(const QUrl &url) const
|
||||||
{
|
{
|
||||||
return m_networkManager.cookieJar()->cookiesForUrl(url);
|
return m_networkManager.cookieJar()->cookiesForUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
bool Net::DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
||||||
{
|
{
|
||||||
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QNetworkCookie> DownloadManager::allCookies() const
|
QList<QNetworkCookie> Net::DownloadManager::allCookies() const
|
||||||
{
|
{
|
||||||
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
|
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
|
void Net::DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
|
||||||
{
|
{
|
||||||
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
|
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
|
bool Net::DownloadManager::deleteCookie(const QNetworkCookie &cookie)
|
||||||
{
|
{
|
||||||
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
|
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::applyProxySettings()
|
void Net::DownloadManager::applyProxySettings()
|
||||||
{
|
{
|
||||||
auto proxyManager = ProxyConfigurationManager::instance();
|
auto proxyManager = ProxyConfigurationManager::instance();
|
||||||
ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
|
ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
|
||||||
@@ -208,7 +230,7 @@ void DownloadManager::applyProxySettings()
|
|||||||
}
|
}
|
||||||
// Authentication?
|
// Authentication?
|
||||||
if (proxyManager->isAuthenticationRequired()) {
|
if (proxyManager->isAuthenticationRequired()) {
|
||||||
qDebug("Proxy requires authentication, authenticating");
|
qDebug("Proxy requires authentication, authenticating...");
|
||||||
proxy.setUser(proxyConfig.username);
|
proxy.setUser(proxyConfig.username);
|
||||||
proxy.setPassword(proxyConfig.password);
|
proxy.setPassword(proxyConfig.password);
|
||||||
}
|
}
|
||||||
@@ -220,11 +242,101 @@ void DownloadManager::applyProxySettings()
|
|||||||
m_networkManager.setProxy(proxy);
|
m_networkManager.setProxy(proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Net::DownloadManager::handleReplyFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
const ServiceID id = ServiceID::fromURL(reply->url());
|
||||||
|
auto waitingJobsIter = m_waitingJobs.find(id);
|
||||||
|
if ((waitingJobsIter == m_waitingJobs.end()) || waitingJobsIter.value().isEmpty()) {
|
||||||
|
m_busyServices.remove(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadHandler *handler = waitingJobsIter.value().dequeue();
|
||||||
|
qDebug("Downloading %s...", qUtf8Printable(handler->m_downloadRequest.url()));
|
||||||
|
handler->assignNetworkReply(m_networkManager.get(createNetworkRequest(handler->m_downloadRequest)));
|
||||||
|
handler->disconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
void DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
void Net::DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||||
{
|
{
|
||||||
Q_UNUSED(errors)
|
Q_UNUSED(errors)
|
||||||
// Ignore all SSL errors
|
// Ignore all SSL errors
|
||||||
reply->ignoreSslErrors();
|
reply->ignoreSslErrors();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Net::DownloadRequest::DownloadRequest(const QString &url)
|
||||||
|
: m_url {url}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Net::DownloadRequest::url() const
|
||||||
|
{
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::url(const QString &value)
|
||||||
|
{
|
||||||
|
m_url = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Net::DownloadRequest::userAgent() const
|
||||||
|
{
|
||||||
|
return m_userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::userAgent(const QString &value)
|
||||||
|
{
|
||||||
|
m_userAgent = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Net::DownloadRequest::limit() const
|
||||||
|
{
|
||||||
|
return m_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::limit(qint64 value)
|
||||||
|
{
|
||||||
|
m_limit = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Net::DownloadRequest::saveToFile() const
|
||||||
|
{
|
||||||
|
return m_saveToFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::saveToFile(bool value)
|
||||||
|
{
|
||||||
|
m_saveToFile = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Net::DownloadRequest::handleRedirectToMagnet() const
|
||||||
|
{
|
||||||
|
return m_handleRedirectToMagnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::handleRedirectToMagnet(bool value)
|
||||||
|
{
|
||||||
|
m_handleRedirectToMagnet = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::ServiceID Net::ServiceID::fromURL(const QUrl &url)
|
||||||
|
{
|
||||||
|
return {url.host(), url.port(80)};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint Net::qHash(const ServiceID &serviceID, uint seed)
|
||||||
|
{
|
||||||
|
return ::qHash(serviceID.hostName, seed) ^ serviceID.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Net::operator==(const ServiceID &lhs, const ServiceID &rhs)
|
||||||
|
{
|
||||||
|
return ((lhs.hostName == rhs.hostName) && (lhs.port == rhs.port));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -30,8 +30,12 @@
|
|||||||
#ifndef NET_DOWNLOADMANAGER_H
|
#ifndef NET_DOWNLOADMANAGER_H
|
||||||
#define NET_DOWNLOADMANAGER_H
|
#define NET_DOWNLOADMANAGER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QHash>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QNetworkCookie;
|
class QNetworkCookie;
|
||||||
@@ -42,16 +46,57 @@ namespace Net
|
|||||||
{
|
{
|
||||||
class DownloadHandler;
|
class DownloadHandler;
|
||||||
|
|
||||||
|
class DownloadRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DownloadRequest(const QString &url);
|
||||||
|
DownloadRequest(const DownloadRequest &other) = default;
|
||||||
|
|
||||||
|
QString url() const;
|
||||||
|
DownloadRequest &url(const QString &value);
|
||||||
|
|
||||||
|
QString userAgent() const;
|
||||||
|
DownloadRequest &userAgent(const QString &value);
|
||||||
|
|
||||||
|
qint64 limit() const;
|
||||||
|
DownloadRequest &limit(qint64 value);
|
||||||
|
|
||||||
|
bool saveToFile() const;
|
||||||
|
DownloadRequest &saveToFile(bool value);
|
||||||
|
|
||||||
|
bool handleRedirectToMagnet() const;
|
||||||
|
DownloadRequest &handleRedirectToMagnet(bool value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_url;
|
||||||
|
QString m_userAgent;
|
||||||
|
qint64 m_limit = 0;
|
||||||
|
bool m_saveToFile = false;
|
||||||
|
bool m_handleRedirectToMagnet = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ServiceID
|
||||||
|
{
|
||||||
|
QString hostName;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
static ServiceID fromURL(const QUrl &url);
|
||||||
|
};
|
||||||
|
|
||||||
class DownloadManager : public QObject
|
class DownloadManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(DownloadManager)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void initInstance();
|
static void initInstance();
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static DownloadManager *instance();
|
static DownloadManager *instance();
|
||||||
|
|
||||||
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false, const QString &userAgent = "");
|
DownloadHandler *download(const DownloadRequest &downloadRequest);
|
||||||
|
|
||||||
|
void registerSequentialService(const ServiceID &serviceID);
|
||||||
|
|
||||||
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
|
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
|
||||||
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
||||||
QList<QNetworkCookie> allCookies() const;
|
QList<QNetworkCookie> allCookies() const;
|
||||||
@@ -60,17 +105,25 @@ namespace Net
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
void ignoreSslErrors(QNetworkReply *,const QList<QSslError> &);
|
void ignoreSslErrors(QNetworkReply *, const QList<QSslError> &);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit DownloadManager(QObject *parent = 0);
|
explicit DownloadManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
void applyProxySettings();
|
void applyProxySettings();
|
||||||
|
void handleReplyFinished(QNetworkReply *reply);
|
||||||
|
|
||||||
static DownloadManager *m_instance;
|
static DownloadManager *m_instance;
|
||||||
QNetworkAccessManager m_networkManager;
|
QNetworkAccessManager m_networkManager;
|
||||||
|
|
||||||
|
QSet<ServiceID> m_sequentialServices;
|
||||||
|
QSet<ServiceID> m_busyServices;
|
||||||
|
QHash<ServiceID, QQueue<DownloadHandler *>> m_waitingJobs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint qHash(const ServiceID &serviceID, uint seed);
|
||||||
|
bool operator==(const ServiceID &lhs, const ServiceID &rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // NET_DOWNLOADMANAGER_H
|
#endif // NET_DOWNLOADMANAGER_H
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ GeoIPManager::GeoIPManager()
|
|||||||
, m_geoIPDatabase(nullptr)
|
, m_geoIPDatabase(nullptr)
|
||||||
{
|
{
|
||||||
configure();
|
configure();
|
||||||
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
|
connect(Preferences::instance(), &Preferences::changed, this, &GeoIPManager::configure);
|
||||||
}
|
}
|
||||||
|
|
||||||
GeoIPManager::~GeoIPManager()
|
GeoIPManager::~GeoIPManager()
|
||||||
@@ -118,9 +118,10 @@ void GeoIPManager::manageDatabaseUpdate()
|
|||||||
|
|
||||||
void GeoIPManager::downloadDatabaseFile()
|
void GeoIPManager::downloadDatabaseFile()
|
||||||
{
|
{
|
||||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(DATABASE_URL);
|
DownloadHandler *handler = DownloadManager::instance()->download({DATABASE_URL});
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(downloadFinished(QString, QByteArray)));
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(downloadFailed(QString, QString)));
|
, this, &GeoIPManager::downloadFinished);
|
||||||
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &GeoIPManager::downloadFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GeoIPManager::lookup(const QHostAddress &hostAddr) const
|
QString GeoIPManager::lookup(const QHostAddress &hostAddr) const
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/settingsstorage.h"
|
#include "base/settingsstorage.h"
|
||||||
|
|
||||||
static const QString KEY_ENABLED = QLatin1String("Network/PortForwardingEnabled");
|
static const QString KEY_ENABLED = QStringLiteral("Network/PortForwardingEnabled");
|
||||||
|
|
||||||
namespace libt = libtorrent;
|
namespace libt = libtorrent;
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
|
|||||||
@@ -26,25 +26,22 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "geoipdatabase.h"
|
#include "geoipdatabase.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const quint32 __ENDIAN_TEST__ = 0x00000001;
|
|
||||||
const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast<const uchar *>(&__ENDIAN_TEST__)[0] == 0x01);
|
|
||||||
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
|
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
|
||||||
const char DB_TYPE[] = "GeoLite2-Country";
|
const char DB_TYPE[] = "GeoLite2-Country";
|
||||||
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
||||||
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
|
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
|
||||||
const char DATA_SECTION_SEPARATOR[16] = { 0 };
|
const char DATA_SECTION_SEPARATOR[16] = {0};
|
||||||
|
|
||||||
enum class DataType
|
enum class DataType
|
||||||
{
|
{
|
||||||
@@ -91,7 +88,7 @@ GeoIPDatabase::GeoIPDatabase(quint32 size)
|
|||||||
|
|
||||||
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
||||||
{
|
{
|
||||||
GeoIPDatabase *db = 0;
|
GeoIPDatabase *db = nullptr;
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
if (file.size() > MAX_FILE_SIZE) {
|
if (file.size() > MAX_FILE_SIZE) {
|
||||||
error = tr("Unsupported database file size.");
|
error = tr("Unsupported database file size.");
|
||||||
@@ -122,7 +119,7 @@ GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
|||||||
|
|
||||||
GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
|
GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
|
||||||
{
|
{
|
||||||
GeoIPDatabase *db = 0;
|
GeoIPDatabase *db = nullptr;
|
||||||
if (data.size() > MAX_FILE_SIZE) {
|
if (data.size() > MAX_FILE_SIZE) {
|
||||||
error = tr("Unsupported database file size.");
|
error = tr("Unsupported database file size.");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -448,8 +445,12 @@ bool GeoIPDatabase::readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor
|
|||||||
|
|
||||||
void GeoIPDatabase::fromBigEndian(uchar *buf, quint32 len) const
|
void GeoIPDatabase::fromBigEndian(uchar *buf, quint32 len) const
|
||||||
{
|
{
|
||||||
if (__IS_LITTLE_ENDIAN__)
|
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
|
||||||
std::reverse(buf, buf + len);
|
std::reverse(buf, buf + len);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(buf);
|
||||||
|
Q_UNUSED(len);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant GeoIPDatabase::readMapValue(quint32 &offset, quint32 count) const
|
QVariant GeoIPDatabase::readMapValue(quint32 &offset, quint32 count) const
|
||||||
|
|||||||
@@ -29,13 +29,13 @@
|
|||||||
#ifndef GEOIPDATABASE_H
|
#ifndef GEOIPDATABASE_H
|
||||||
#define GEOIPDATABASE_H
|
#define GEOIPDATABASE_H
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
class QHostAddress;
|
|
||||||
class QString;
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
|
class QHostAddress;
|
||||||
|
class QString;
|
||||||
|
|
||||||
struct DataFieldDescriptor;
|
struct DataFieldDescriptor;
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "proxyconfigurationmanager.h"
|
#include "proxyconfigurationmanager.h"
|
||||||
|
|
||||||
#include "base/settingsstorage.h"
|
#include "base/settingsstorage.h"
|
||||||
|
|
||||||
#define SETTINGS_KEY(name) "Network/Proxy/" name
|
#define SETTINGS_KEY(name) QStringLiteral("Network/Proxy/" name)
|
||||||
const QString KEY_ONLY_FOR_TORRENTS = SETTINGS_KEY("OnlyForTorrents");
|
const QString KEY_ONLY_FOR_TORRENTS = SETTINGS_KEY("OnlyForTorrents");
|
||||||
const QString KEY_TYPE = SETTINGS_KEY("Type");
|
const QString KEY_TYPE = SETTINGS_KEY("Type");
|
||||||
const QString KEY_IP = SETTINGS_KEY("IP");
|
const QString KEY_IP = SETTINGS_KEY("IP");
|
||||||
@@ -80,7 +81,7 @@ void ProxyConfigurationManager::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace Net
|
|||||||
QString password;
|
QString password;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProxyConfigurationManager: public QObject
|
class ProxyConfigurationManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(ProxyConfigurationManager)
|
Q_DISABLE_COPY(ProxyConfigurationManager)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,26 +24,24 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "reverseresolution.h"
|
||||||
|
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include <boost/version.hpp>
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
|
||||||
|
|
||||||
#include "reverseresolution.h"
|
|
||||||
|
|
||||||
const int CACHE_SIZE = 500;
|
const int CACHE_SIZE = 500;
|
||||||
|
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
|
|
||||||
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
|
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
|
||||||
{
|
{
|
||||||
return (!hostname.isEmpty() && hostname != ip);
|
return (!hostname.isEmpty() && (hostname != ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReverseResolution::ReverseResolution(QObject *parent)
|
ReverseResolution::ReverseResolution(QObject *parent)
|
||||||
@@ -67,13 +65,17 @@ void ReverseResolution::resolve(const QString &ip)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Actually resolve the ip
|
// Actually resolve the ip
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||||
|
m_lookups.insert(QHostInfo::lookupHost(ip, this, &ReverseResolution::hostResolved), ip);
|
||||||
|
#else
|
||||||
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverseResolution::hostResolved(const QHostInfo &host)
|
void ReverseResolution::hostResolved(const QHostInfo &host)
|
||||||
{
|
{
|
||||||
const QString &ip = m_lookups.take(host.lookupId());
|
const QString ip = m_lookups.take(host.lookupId());
|
||||||
Q_ASSERT(!ip.isNull());
|
Q_ASSERT(!ip.isNull());
|
||||||
|
|
||||||
if (host.error() != QHostInfo::NoError) {
|
if (host.error() != QHostInfo::NoError) {
|
||||||
@@ -81,7 +83,7 @@ void ReverseResolution::hostResolved(const QHostInfo &host)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &hostname = host.hostName();
|
const QString hostname = host.hostName();
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
||||||
m_cache.insert(ip, new QString(hostname));
|
m_cache.insert(ip, new QString(hostname));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef NET_REVERSERESOLUTION_H
|
#ifndef NET_REVERSERESOLUTION_H
|
||||||
@@ -34,10 +32,8 @@
|
|||||||
#include <QCache>
|
#include <QCache>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QHostInfo;
|
class QHostInfo;
|
||||||
class QString;
|
class QString;
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
@@ -47,7 +43,7 @@ namespace Net
|
|||||||
Q_DISABLE_COPY(ReverseResolution)
|
Q_DISABLE_COPY(ReverseResolution)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ReverseResolution(QObject *parent = 0);
|
explicit ReverseResolution(QObject *parent = nullptr);
|
||||||
~ReverseResolution();
|
~ReverseResolution();
|
||||||
|
|
||||||
void resolve(const QString &ip);
|
void resolve(const QString &ip);
|
||||||
|
|||||||
@@ -34,12 +34,9 @@
|
|||||||
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
#include <QNetworkInterface>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QTextStream>
|
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
#else
|
#else
|
||||||
@@ -67,7 +64,7 @@ namespace
|
|||||||
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
||||||
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
||||||
|
|
||||||
for (int i = 0; i < key.length(); i++) {
|
for (int i = 0; i < key.length(); ++i) {
|
||||||
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
||||||
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
||||||
}
|
}
|
||||||
@@ -111,9 +108,10 @@ Smtp::Smtp(QObject *parent)
|
|||||||
m_socket = new QTcpSocket(this);
|
m_socket = new QTcpSocket(this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
|
connect(m_socket, &QIODevice::readyRead, this, &Smtp::readyRead);
|
||||||
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
connect(m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||||
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(error(QAbstractSocket::SocketError)));
|
connect(m_socket, static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error)
|
||||||
|
, this, &Smtp::error);
|
||||||
|
|
||||||
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
||||||
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
||||||
@@ -129,8 +127,8 @@ Smtp::~Smtp()
|
|||||||
|
|
||||||
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
|
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
|
||||||
{
|
{
|
||||||
const Preferences* const pref = Preferences::instance();
|
const Preferences *const pref = Preferences::instance();
|
||||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
QTextCodec *latin1 = QTextCodec::codecForName("latin1");
|
||||||
m_message = "Date: " + getCurrentDateTime().toLatin1() + "\r\n"
|
m_message = "Date: " + getCurrentDateTime().toLatin1() + "\r\n"
|
||||||
+ encodeMimeHeader("From", from, latin1)
|
+ encodeMimeHeader("From", from, latin1)
|
||||||
+ encodeMimeHeader("Subject", subject, latin1)
|
+ encodeMimeHeader("Subject", subject, latin1)
|
||||||
@@ -140,8 +138,8 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
|||||||
+ "Content-Transfer-Encoding: base64\r\n"
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
+ "\r\n";
|
+ "\r\n";
|
||||||
// Encode the body in base64
|
// Encode the body in base64
|
||||||
QString crlf_body = body;
|
QString crlfBody = body;
|
||||||
QByteArray b = crlf_body.replace("\n", "\r\n").toUtf8().toBase64();
|
QByteArray b = crlfBody.replace("\n", "\r\n").toUtf8().toBase64();
|
||||||
int ct = b.length();
|
int ct = b.length();
|
||||||
for (int i = 0; i < ct; i += 78)
|
for (int i = 0; i < ct; i += 78)
|
||||||
m_message += b.mid(i, 78);
|
m_message += b.mid(i, 78);
|
||||||
@@ -164,7 +162,7 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
|||||||
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
||||||
m_useSsl = false;
|
m_useSsl = false;
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +181,7 @@ void Smtp::readyRead()
|
|||||||
QByteArray code = line.left(3);
|
QByteArray code = line.left(3);
|
||||||
|
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
case Init: {
|
case Init:
|
||||||
if (code[0] == '2') {
|
if (code[0] == '2') {
|
||||||
// The server may send a multiline greeting/INIT/220 response.
|
// The server may send a multiline greeting/INIT/220 response.
|
||||||
// We wait until it finishes.
|
// We wait until it finishes.
|
||||||
@@ -197,7 +195,6 @@ void Smtp::readyRead()
|
|||||||
m_state = Close;
|
m_state = Close;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case EhloSent:
|
case EhloSent:
|
||||||
case HeloSent:
|
case HeloSent:
|
||||||
case EhloGreetReceived:
|
case EhloGreetReceived:
|
||||||
@@ -302,7 +299,7 @@ QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTex
|
|||||||
if (firstWord)
|
if (firstWord)
|
||||||
line += word;
|
line += word;
|
||||||
else
|
else
|
||||||
line += " " + word;
|
line += ' ' + word;
|
||||||
firstWord = false;
|
firstWord = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,7 +423,7 @@ void Smtp::authenticate()
|
|||||||
// Skip authentication
|
// Skip authentication
|
||||||
logError("The SMTP server does not seem to support any of the authentications modes "
|
logError("The SMTP server does not seem to support any of the authentications modes "
|
||||||
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
"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;
|
m_state = Authenticated;
|
||||||
// At this point the server will not send any response
|
// At this point the server will not send any response
|
||||||
// So fill the buffer with a fake one to pass the tests
|
// So fill the buffer with a fake one to pass the tests
|
||||||
@@ -447,7 +444,7 @@ void Smtp::startTLS()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smtp::authCramMD5(const QByteArray& challenge)
|
void Smtp::authCramMD5(const QByteArray &challenge)
|
||||||
{
|
{
|
||||||
if (m_state != AuthRequestSent) {
|
if (m_state != AuthRequestSent) {
|
||||||
m_socket->write("auth cram-md5\r\n");
|
m_socket->write("auth cram-md5\r\n");
|
||||||
@@ -506,7 +503,7 @@ void Smtp::authLogin()
|
|||||||
void Smtp::logError(const QString &msg)
|
void Smtp::logError(const QString &msg)
|
||||||
{
|
{
|
||||||
qDebug() << "Email Notification Error:" << msg;
|
qDebug() << "Email Notification Error:" << msg;
|
||||||
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
Logger::instance()->addMessage(tr("Email Notification Error:") + ' ' + msg, Log::CRITICAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Smtp::getCurrentDateTime() const
|
QString Smtp::getCurrentDateTime() const
|
||||||
@@ -532,7 +529,7 @@ QString Smtp::getCurrentDateTime() const
|
|||||||
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
|
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
|
||||||
QString timeOffsetStr = buf;
|
QString timeOffsetStr = buf;
|
||||||
|
|
||||||
QString ret = weekDayStr + ", " + dayStr + " " + monthStr + " " + yearStr + " " + timeStr + " " + timeOffsetStr;
|
QString ret = weekDayStr + ", " + dayStr + ' ' + monthStr + ' ' + yearStr + ' ' + timeStr + ' ' + timeOffsetStr;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class QSslSocket;
|
|||||||
class QTcpSocket;
|
class QTcpSocket;
|
||||||
#endif
|
#endif
|
||||||
class QTextCodec;
|
class QTextCodec;
|
||||||
class QTextStream;
|
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2014 sledgehammer999 <hammered999@gmail.com>
|
||||||
* Copyright (C) 2014 sledgehammer999
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,9 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
* Contact : hammered999@gmail.com
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
@@ -36,7 +33,6 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QMutableListIterator>
|
#include <QMutableListIterator>
|
||||||
#include <QPair>
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
@@ -48,6 +44,7 @@
|
|||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <winreg.h>
|
#include <winreg.h>
|
||||||
|
#include <QRegularExpression>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
@@ -59,7 +56,7 @@
|
|||||||
#include "utils/fs.h"
|
#include "utils/fs.h"
|
||||||
#include "utils/misc.h"
|
#include "utils/misc.h"
|
||||||
|
|
||||||
Preferences *Preferences::m_instance = 0;
|
Preferences *Preferences::m_instance = nullptr;
|
||||||
|
|
||||||
Preferences::Preferences() = default;
|
Preferences::Preferences() = default;
|
||||||
|
|
||||||
@@ -78,7 +75,7 @@ void Preferences::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +183,16 @@ void Preferences::setMinimizeToTray(bool b)
|
|||||||
setValue("Preferences/General/MinimizeToTray", b);
|
setValue("Preferences/General/MinimizeToTray", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Preferences::minimizeToTrayNotified() const
|
||||||
|
{
|
||||||
|
return value("Preferences/General/MinimizeToTrayNotified", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setMinimizeToTrayNotified(bool b)
|
||||||
|
{
|
||||||
|
setValue("Preferences/General/MinimizeToTrayNotified", b);
|
||||||
|
}
|
||||||
|
|
||||||
bool Preferences::closeToTray() const
|
bool Preferences::closeToTray() const
|
||||||
{
|
{
|
||||||
return value("Preferences/General/CloseToTray", true).toBool();
|
return value("Preferences/General/CloseToTray", true).toBool();
|
||||||
@@ -195,6 +202,16 @@ void Preferences::setCloseToTray(bool b)
|
|||||||
{
|
{
|
||||||
setValue("Preferences/General/CloseToTray", b);
|
setValue("Preferences/General/CloseToTray", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Preferences::closeToTrayNotified() const
|
||||||
|
{
|
||||||
|
return value("Preferences/General/CloseToTrayNotified", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setCloseToTrayNotified(bool b)
|
||||||
|
{
|
||||||
|
setValue("Preferences/General/CloseToTrayNotified", b);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool Preferences::isToolbarDisplayed() const
|
bool Preferences::isToolbarDisplayed() const
|
||||||
@@ -238,14 +255,24 @@ void Preferences::setSplashScreenDisabled(bool b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Preventing from system suspend while active torrents are presented.
|
// Preventing from system suspend while active torrents are presented.
|
||||||
bool Preferences::preventFromSuspend() const
|
bool Preferences::preventFromSuspendWhenDownloading() const
|
||||||
{
|
{
|
||||||
return value("Preferences/General/PreventFromSuspend", false).toBool();
|
return value("Preferences/General/PreventFromSuspendWhenDownloading", false).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setPreventFromSuspend(bool b)
|
void Preferences::setPreventFromSuspendWhenDownloading(bool b)
|
||||||
{
|
{
|
||||||
setValue("Preferences/General/PreventFromSuspend", b);
|
setValue("Preferences/General/PreventFromSuspendWhenDownloading", b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Preferences::preventFromSuspendWhenSeeding() const
|
||||||
|
{
|
||||||
|
return value("Preferences/General/PreventFromSuspendWhenSeeding", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setPreventFromSuspendWhenSeeding(bool b)
|
||||||
|
{
|
||||||
|
setValue("Preferences/General/PreventFromSuspendWhenSeeding", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@@ -259,8 +286,8 @@ void Preferences::setWinStartup(bool b)
|
|||||||
{
|
{
|
||||||
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||||
if (b) {
|
if (b) {
|
||||||
const QString bin_path = "\"" + Utils::Fs::toNativePath(qApp->applicationFilePath()) + "\"";
|
const QString binPath = '"' + Utils::Fs::toNativePath(qApp->applicationFilePath()) + '"';
|
||||||
settings.setValue("qBittorrent", bin_path);
|
settings.setValue("qBittorrent", binPath);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
settings.remove("qBittorrent");
|
settings.remove("qBittorrent");
|
||||||
@@ -420,12 +447,12 @@ void Preferences::setSchedulerEndTime(const QTime &time)
|
|||||||
setValue("Preferences/Scheduler/end_time", time);
|
setValue("Preferences/Scheduler/end_time", time);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler_days Preferences::getSchedulerDays() const
|
SchedulerDays Preferences::getSchedulerDays() const
|
||||||
{
|
{
|
||||||
return static_cast<scheduler_days>(value("Preferences/Scheduler/days", EVERY_DAY).toInt());
|
return static_cast<SchedulerDays>(value("Preferences/Scheduler/days", EVERY_DAY).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setSchedulerDays(scheduler_days days)
|
void Preferences::setSchedulerDays(SchedulerDays days)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Scheduler/days", static_cast<int>(days));
|
setValue("Preferences/Scheduler/days", static_cast<int>(days));
|
||||||
}
|
}
|
||||||
@@ -503,7 +530,7 @@ void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets)
|
|||||||
|
|
||||||
QString Preferences::getServerDomains() const
|
QString Preferences::getServerDomains() const
|
||||||
{
|
{
|
||||||
return value("Preferences/WebUI/ServerDomains", "*").toString();
|
return value("Preferences/WebUI/ServerDomains", '*').toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setServerDomains(const QString &str)
|
void Preferences::setServerDomains(const QString &str)
|
||||||
@@ -513,7 +540,7 @@ void Preferences::setServerDomains(const QString &str)
|
|||||||
|
|
||||||
QString Preferences::getWebUiAddress() const
|
QString Preferences::getWebUiAddress() const
|
||||||
{
|
{
|
||||||
return value("Preferences/WebUI/Address", "*").toString().trimmed();
|
return value("Preferences/WebUI/Address", '*').toString().trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setWebUiAddress(const QString &addr)
|
void Preferences::setWebUiAddress(const QString &addr)
|
||||||
@@ -557,28 +584,48 @@ void Preferences::setWebUiUsername(const QString &username)
|
|||||||
|
|
||||||
QString Preferences::getWebUiPassword() const
|
QString Preferences::getWebUiPassword() const
|
||||||
{
|
{
|
||||||
QString pass_ha1 = value("Preferences/WebUI/Password_ha1").toString();
|
QString passHa1 = value("Preferences/WebUI/Password_ha1").toString();
|
||||||
if (pass_ha1.isEmpty()) {
|
if (passHa1.isEmpty()) {
|
||||||
QCryptographicHash md5(QCryptographicHash::Md5);
|
QCryptographicHash md5(QCryptographicHash::Md5);
|
||||||
md5.addData("adminadmin");
|
md5.addData("adminadmin");
|
||||||
pass_ha1 = md5.result().toHex();
|
passHa1 = md5.result().toHex();
|
||||||
}
|
}
|
||||||
return pass_ha1;
|
return passHa1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setWebUiPassword(const QString &new_password)
|
void Preferences::setWebUiPassword(const QString &newPassword)
|
||||||
{
|
{
|
||||||
// Do not overwrite current password with its hash
|
// Do not overwrite current password with its hash
|
||||||
if (new_password == getWebUiPassword())
|
if (newPassword == getWebUiPassword())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Encode to md5 and save
|
// Encode to md5 and save
|
||||||
QCryptographicHash md5(QCryptographicHash::Md5);
|
QCryptographicHash md5(QCryptographicHash::Md5);
|
||||||
md5.addData(new_password.toLocal8Bit());
|
md5.addData(newPassword.toLocal8Bit());
|
||||||
|
|
||||||
setValue("Preferences/WebUI/Password_ha1", md5.result().toHex());
|
setValue("Preferences/WebUI/Password_ha1", md5.result().toHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Preferences::isWebUiClickjackingProtectionEnabled() const
|
||||||
|
{
|
||||||
|
return value("Preferences/WebUI/ClickjackingProtection", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setWebUiClickjackingProtectionEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
setValue("Preferences/WebUI/ClickjackingProtection", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Preferences::isWebUiCSRFProtectionEnabled() const
|
||||||
|
{
|
||||||
|
return value("Preferences/WebUI/CSRFProtection", true).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setWebUiCSRFProtectionEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
setValue("Preferences/WebUI/CSRFProtection", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
bool Preferences::isWebUiHttpsEnabled() const
|
bool Preferences::isWebUiHttpsEnabled() const
|
||||||
{
|
{
|
||||||
return value("Preferences/WebUI/HTTPS/Enabled", false).toBool();
|
return value("Preferences/WebUI/HTTPS/Enabled", false).toBool();
|
||||||
@@ -690,12 +737,12 @@ QString Preferences::getUILockPasswordMD5() const
|
|||||||
return value("Locking/password").toString();
|
return value("Locking/password").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setUILockPassword(const QString &clear_password)
|
void Preferences::setUILockPassword(const QString &clearPassword)
|
||||||
{
|
{
|
||||||
QCryptographicHash md5(QCryptographicHash::Md5);
|
QCryptographicHash md5(QCryptographicHash::Md5);
|
||||||
md5.addData(clear_password.toLocal8Bit());
|
md5.addData(clearPassword.toLocal8Bit());
|
||||||
QString md5_password = md5.result().toHex();
|
QString md5Password = md5.result().toHex();
|
||||||
setValue("Locking/password", md5_password);
|
setValue("Locking/password", md5Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isUILocked() const
|
bool Preferences::isUILocked() const
|
||||||
@@ -849,7 +896,7 @@ namespace
|
|||||||
LONG res = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
|
LONG res = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
if (res == ERROR_SUCCESS) {
|
if (res == ERROR_SUCCESS) {
|
||||||
cMaxSubKeyLen++; // For null character
|
++cMaxSubKeyLen; // For null character
|
||||||
LPWSTR lpName = new WCHAR[cMaxSubKeyLen];
|
LPWSTR lpName = new WCHAR[cMaxSubKeyLen];
|
||||||
DWORD cName;
|
DWORD cName;
|
||||||
|
|
||||||
@@ -970,7 +1017,7 @@ QString Preferences::getPythonPath()
|
|||||||
// Fallback: Detect python from default locations
|
// Fallback: Detect python from default locations
|
||||||
const QStringList dirs = QDir("C:/").entryList(QStringList("Python*"), QDir::Dirs, QDir::Name | QDir::Reversed);
|
const QStringList dirs = QDir("C:/").entryList(QStringList("Python*"), QDir::Dirs, QDir::Name | QDir::Reversed);
|
||||||
foreach (const QString &dir, dirs) {
|
foreach (const QString &dir, dirs) {
|
||||||
const QString path("C:/" + dir + "/");
|
const QString path("C:/" + dir + '/');
|
||||||
if (QFile::exists(path + "python.exe"))
|
if (QFile::exists(path + "python.exe"))
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -1004,13 +1051,14 @@ bool Preferences::isMagnetLinkAssocSet()
|
|||||||
QSettings settings("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
|
QSettings settings("HKEY_CURRENT_USER\\Software\\Classes", QSettings::NativeFormat);
|
||||||
|
|
||||||
// Check magnet link assoc
|
// Check magnet link assoc
|
||||||
QRegExp exe_reg("\"([^\"]+)\".*");
|
const QString shellCommand = Utils::Fs::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
||||||
QString shell_command = Utils::Fs::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
|
||||||
if (exe_reg.indexIn(shell_command) < 0)
|
const QRegularExpressionMatch exeRegMatch = QRegularExpression("\"([^\"]+)\".*").match(shellCommand);
|
||||||
|
if (!exeRegMatch.hasMatch())
|
||||||
return false;
|
return false;
|
||||||
QString assoc_exe = exe_reg.cap(1);
|
|
||||||
qDebug("exe: %s", qUtf8Printable(assoc_exe));
|
const QString assocExe = exeRegMatch.captured(1);
|
||||||
if (assoc_exe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
if (assocExe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1022,9 +1070,9 @@ void Preferences::setTorrentFileAssoc(bool set)
|
|||||||
|
|
||||||
// .Torrent association
|
// .Torrent association
|
||||||
if (set) {
|
if (set) {
|
||||||
QString old_progid = settings.value(".torrent/Default").toString();
|
QString oldProgId = settings.value(".torrent/Default").toString();
|
||||||
if (!old_progid.isEmpty() && (old_progid != "qBittorrent"))
|
if (!oldProgId.isEmpty() && (oldProgId != "qBittorrent"))
|
||||||
settings.setValue(".torrent/OpenWithProgids/" + old_progid, "");
|
settings.setValue(".torrent/OpenWithProgids/" + oldProgId, "");
|
||||||
settings.setValue(".torrent/Default", "qBittorrent");
|
settings.setValue(".torrent/Default", "qBittorrent");
|
||||||
}
|
}
|
||||||
else if (isTorrentFileAssocSet()) {
|
else if (isTorrentFileAssocSet()) {
|
||||||
@@ -1040,15 +1088,15 @@ void Preferences::setMagnetLinkAssoc(bool set)
|
|||||||
|
|
||||||
// Magnet association
|
// Magnet association
|
||||||
if (set) {
|
if (set) {
|
||||||
const QString command_str = "\"" + qApp->applicationFilePath() + "\" \"%1\"";
|
const QString commandStr = '"' + qApp->applicationFilePath() + "\" \"%1\"";
|
||||||
const QString icon_str = "\"" + qApp->applicationFilePath() + "\",1";
|
const QString iconStr = '"' + qApp->applicationFilePath() + "\",1";
|
||||||
|
|
||||||
settings.setValue("magnet/Default", "URL:Magnet link");
|
settings.setValue("magnet/Default", "URL:Magnet link");
|
||||||
settings.setValue("magnet/Content Type", "application/x-magnet");
|
settings.setValue("magnet/Content Type", "application/x-magnet");
|
||||||
settings.setValue("magnet/URL Protocol", "");
|
settings.setValue("magnet/URL Protocol", "");
|
||||||
settings.setValue("magnet/DefaultIcon/Default", Utils::Fs::toNativePath(icon_str));
|
settings.setValue("magnet/DefaultIcon/Default", Utils::Fs::toNativePath(iconStr));
|
||||||
settings.setValue("magnet/shell/Default", "open");
|
settings.setValue("magnet/shell/Default", "open");
|
||||||
settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(command_str));
|
settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(commandStr));
|
||||||
}
|
}
|
||||||
else if (isMagnetLinkAssocSet()) {
|
else if (isMagnetLinkAssocSet()) {
|
||||||
settings.remove("magnet");
|
settings.remove("magnet");
|
||||||
@@ -1471,6 +1519,16 @@ void Preferences::setTransHeaderState(const QByteArray &state)
|
|||||||
setValue("TransferList/qt5/HeaderState", state);
|
setValue("TransferList/qt5/HeaderState", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Preferences::getRegexAsFilteringPattern() const
|
||||||
|
{
|
||||||
|
return value("TransferList/UseRegexAsFilteringPattern", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setRegexAsFilteringPattern(const bool checked)
|
||||||
|
{
|
||||||
|
setValue("TransferList/UseRegexAsFilteringPattern", checked);
|
||||||
|
}
|
||||||
|
|
||||||
// From old RssSettings class
|
// From old RssSettings class
|
||||||
bool Preferences::isRSSWidgetEnabled() const
|
bool Preferences::isRSSWidgetEnabled() const
|
||||||
{
|
{
|
||||||
@@ -1534,6 +1592,8 @@ void Preferences::setSpeedWidgetGraphEnable(int id, const bool enable)
|
|||||||
|
|
||||||
void Preferences::upgrade()
|
void Preferences::upgrade()
|
||||||
{
|
{
|
||||||
|
SettingsStorage *settingsStorage = SettingsStorage::instance();
|
||||||
|
|
||||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||||
if (!labels.isEmpty()) {
|
if (!labels.isEmpty()) {
|
||||||
QVariantMap categories = value("BitTorrent/Session/Categories").toMap();
|
QVariantMap categories = value("BitTorrent/Session/Categories").toMap();
|
||||||
@@ -1542,10 +1602,17 @@ void Preferences::upgrade()
|
|||||||
categories[label] = "";
|
categories[label] = "";
|
||||||
}
|
}
|
||||||
setValue("BitTorrent/Session/Categories", categories);
|
setValue("BitTorrent/Session/Categories", categories);
|
||||||
SettingsStorage::instance()->removeValue("TransferListFilters/customLabels");
|
settingsStorage->removeValue("TransferListFilters/customLabels");
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsStorage::instance()->removeValue("Preferences/Downloads/AppendLabel");
|
settingsStorage->removeValue("Preferences/Downloads/AppendLabel");
|
||||||
|
|
||||||
|
// Inhibit sleep based on running downloads/available seeds rather than network activity.
|
||||||
|
if (value("Preferences/General/PreventFromSuspend", false).toBool()) {
|
||||||
|
setPreventFromSuspendWhenDownloading(true);
|
||||||
|
setPreventFromSuspendWhenSeeding(true);
|
||||||
|
}
|
||||||
|
settingsStorage->removeValue("Preferences/General/PreventFromSuspend");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::apply()
|
void Preferences::apply()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2014 sledgehammer999 <hammered999@gmail.com>
|
||||||
* Copyright (C) 2014 sledgehammer999
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,9 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
* Contact : hammered999@gmail.com
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PREFERENCES_H
|
#ifndef PREFERENCES_H
|
||||||
@@ -47,7 +44,7 @@
|
|||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
enum scheduler_days
|
enum SchedulerDays
|
||||||
{
|
{
|
||||||
EVERY_DAY,
|
EVERY_DAY,
|
||||||
WEEK_DAYS,
|
WEEK_DAYS,
|
||||||
@@ -83,7 +80,7 @@ namespace DNS
|
|||||||
|
|
||||||
class SettingsStorage;
|
class SettingsStorage;
|
||||||
|
|
||||||
class Preferences: public QObject
|
class Preferences : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(Preferences)
|
Q_DISABLE_COPY(Preferences)
|
||||||
@@ -126,8 +123,10 @@ public:
|
|||||||
void setStartMinimized(bool b);
|
void setStartMinimized(bool b);
|
||||||
bool isSplashScreenDisabled() const;
|
bool isSplashScreenDisabled() const;
|
||||||
void setSplashScreenDisabled(bool b);
|
void setSplashScreenDisabled(bool b);
|
||||||
bool preventFromSuspend() const;
|
bool preventFromSuspendWhenDownloading() const;
|
||||||
void setPreventFromSuspend(bool b);
|
void setPreventFromSuspendWhenDownloading(bool b);
|
||||||
|
bool preventFromSuspendWhenSeeding() const;
|
||||||
|
void setPreventFromSuspendWhenSeeding(bool b);
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
bool WinStartup() const;
|
bool WinStartup() const;
|
||||||
void setWinStartup(bool b);
|
void setWinStartup(bool b);
|
||||||
@@ -166,8 +165,8 @@ public:
|
|||||||
void setSchedulerStartTime(const QTime &time);
|
void setSchedulerStartTime(const QTime &time);
|
||||||
QTime getSchedulerEndTime() const;
|
QTime getSchedulerEndTime() const;
|
||||||
void setSchedulerEndTime(const QTime &time);
|
void setSchedulerEndTime(const QTime &time);
|
||||||
scheduler_days getSchedulerDays() const;
|
SchedulerDays getSchedulerDays() const;
|
||||||
void setSchedulerDays(scheduler_days days);
|
void setSchedulerDays(SchedulerDays days);
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
bool isSearchEnabled() const;
|
bool isSearchEnabled() const;
|
||||||
@@ -195,7 +194,13 @@ public:
|
|||||||
QString getWebUiUsername() const;
|
QString getWebUiUsername() const;
|
||||||
void setWebUiUsername(const QString &username);
|
void setWebUiUsername(const QString &username);
|
||||||
QString getWebUiPassword() const;
|
QString getWebUiPassword() const;
|
||||||
void setWebUiPassword(const QString &new_password);
|
void setWebUiPassword(const QString &newPassword);
|
||||||
|
|
||||||
|
// WebUI security
|
||||||
|
bool isWebUiClickjackingProtectionEnabled() const;
|
||||||
|
void setWebUiClickjackingProtectionEnabled(bool enabled);
|
||||||
|
bool isWebUiCSRFProtectionEnabled() const;
|
||||||
|
void setWebUiCSRFProtectionEnabled(bool enabled);
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
bool isWebUiHttpsEnabled() const;
|
bool isWebUiHttpsEnabled() const;
|
||||||
@@ -222,7 +227,7 @@ public:
|
|||||||
void setDynDNSPassword(const QString &password);
|
void setDynDNSPassword(const QString &password);
|
||||||
|
|
||||||
// Advanced settings
|
// Advanced settings
|
||||||
void setUILockPassword(const QString &clear_password);
|
void setUILockPassword(const QString &clearPassword);
|
||||||
void clearUILockPassword();
|
void clearUILockPassword();
|
||||||
QString getUILockPasswordMD5() const;
|
QString getUILockPasswordMD5() const;
|
||||||
bool isUILocked() const;
|
bool isUILocked() const;
|
||||||
@@ -283,10 +288,14 @@ public:
|
|||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
bool systrayIntegration() const;
|
bool systrayIntegration() const;
|
||||||
void setSystrayIntegration(bool enabled);
|
void setSystrayIntegration(bool enabled);
|
||||||
|
bool minimizeToTrayNotified() const;
|
||||||
|
void setMinimizeToTrayNotified(bool b);
|
||||||
bool minimizeToTray() const;
|
bool minimizeToTray() const;
|
||||||
void setMinimizeToTray(bool b);
|
void setMinimizeToTray(bool b);
|
||||||
bool closeToTray() const;
|
bool closeToTray() const;
|
||||||
void setCloseToTray(bool b);
|
void setCloseToTray(bool b);
|
||||||
|
bool closeToTrayNotified() const;
|
||||||
|
void setCloseToTrayNotified(bool b);
|
||||||
TrayIcon::Style trayIconStyle() const;
|
TrayIcon::Style trayIconStyle() const;
|
||||||
void setTrayIconStyle(TrayIcon::Style style);
|
void setTrayIconStyle(TrayIcon::Style style);
|
||||||
#endif
|
#endif
|
||||||
@@ -347,6 +356,8 @@ public:
|
|||||||
void setTransSelFilter(const int &index);
|
void setTransSelFilter(const int &index);
|
||||||
QByteArray getTransHeaderState() const;
|
QByteArray getTransHeaderState() const;
|
||||||
void setTransHeaderState(const QByteArray &state);
|
void setTransHeaderState(const QByteArray &state);
|
||||||
|
bool getRegexAsFilteringPattern() const;
|
||||||
|
void setRegexAsFilteringPattern(bool checked);
|
||||||
int getToolbarTextPosition() const;
|
int getToolbarTextPosition() const;
|
||||||
void setToolbarTextPosition(const int position);
|
void setToolbarTextPosition(const int position);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "profile_p.h"
|
#include "profile_p.h"
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef QBT_PROFILE_P_H
|
#ifndef QBT_PROFILE_P_H
|
||||||
@@ -33,6 +32,7 @@
|
|||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
|
|
||||||
namespace Private
|
namespace Private
|
||||||
@@ -63,7 +63,7 @@ namespace Private
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Default implementation. Takes paths from system
|
/// Default implementation. Takes paths from system
|
||||||
class DefaultProfile: public Profile
|
class DefaultProfile : public Profile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DefaultProfile(const QString &configurationName);
|
DefaultProfile(const QString &configurationName);
|
||||||
@@ -86,7 +86,7 @@ namespace Private
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Custom tree: creates directories under the specified root directory
|
/// Custom tree: creates directories under the specified root directory
|
||||||
class CustomProfile: public Profile
|
class CustomProfile : public Profile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CustomProfile(const QString &rootPath, const QString &configurationName);
|
CustomProfile(const QString &rootPath, const QString &configurationName);
|
||||||
@@ -114,14 +114,14 @@ namespace Private
|
|||||||
virtual ~PathConverter() = default;
|
virtual ~PathConverter() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NoConvertConverter: public PathConverter
|
class NoConvertConverter : public PathConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString toPortablePath(const QString &path) const override;
|
QString toPortablePath(const QString &path) const override;
|
||||||
QString fromPortablePath(const QString &portablePath) const override;
|
QString fromPortablePath(const QString &portablePath) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Converter: public PathConverter
|
class Converter : public PathConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Converter(const QString &basePath);
|
Converter(const QString &basePath);
|
||||||
@@ -132,4 +132,5 @@ namespace Private
|
|||||||
QDir m_baseDir;
|
QDir m_baseDir;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // QBT_PROFILE_P_H
|
#endif // QBT_PROFILE_P_H
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef QBT_PROFILE_H
|
#ifndef QBT_PROFILE_H
|
||||||
@@ -33,9 +32,9 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
|
|
||||||
#include "rss_parser.h"
|
#include "rss_parser.h"
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
#include <QGlobalStatic>
|
#include <QGlobalStatic>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace RSS
|
|||||||
QList<QVariantHash> articles;
|
QList<QVariantHash> articles;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Parser: public QObject
|
class Parser : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,19 @@
|
|||||||
|
|
||||||
using namespace RSS;
|
using namespace RSS;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
QVariantHash articleDataFromJSON(const QJsonObject &jsonObj)
|
||||||
|
{
|
||||||
|
auto varHash = jsonObj.toVariantHash();
|
||||||
|
// JSON object store DateTime as string so we need to convert it
|
||||||
|
varHash[Article::KeyDate] =
|
||||||
|
QDateTime::fromString(jsonObj.value(Article::KeyDate).toString(), Qt::RFC2822Date);
|
||||||
|
|
||||||
|
return varHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QString Article::KeyId(QStringLiteral("id"));
|
const QString Article::KeyId(QStringLiteral("id"));
|
||||||
const QString Article::KeyDate(QStringLiteral("date"));
|
const QString Article::KeyDate(QStringLiteral("date"));
|
||||||
const QString Article::KeyTitle(QStringLiteral("title"));
|
const QString Article::KeyTitle(QStringLiteral("title"));
|
||||||
@@ -60,6 +73,9 @@ Article::Article(Feed *feed, const QVariantHash &varHash)
|
|||||||
, m_isRead(varHash.value(KeyIsRead, false).toBool())
|
, m_isRead(varHash.value(KeyIsRead, false).toBool())
|
||||||
, m_data(varHash)
|
, m_data(varHash)
|
||||||
{
|
{
|
||||||
|
if (!m_date.isValid())
|
||||||
|
throw std::runtime_error("Bad RSS Article data");
|
||||||
|
|
||||||
// If item does not have a guid, fall back to some other identifier
|
// If item does not have a guid, fall back to some other identifier
|
||||||
if (m_guid.isEmpty())
|
if (m_guid.isEmpty())
|
||||||
m_guid = varHash.value(KeyTorrentURL).toString();
|
m_guid = varHash.value(KeyTorrentURL).toString();
|
||||||
@@ -77,11 +93,8 @@ Article::Article(Feed *feed, const QVariantHash &varHash)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Article::Article(Feed *feed, const QJsonObject &jsonObj)
|
Article::Article(Feed *feed, const QJsonObject &jsonObj)
|
||||||
: Article(feed, jsonObj.toVariantHash())
|
: Article(feed, articleDataFromJSON(jsonObj))
|
||||||
{
|
{
|
||||||
// JSON object store DateTime as string so we need to convert it
|
|
||||||
m_date = QDateTime::fromString(jsonObj.value(KeyDate).toString(), Qt::RFC2822Date);
|
|
||||||
m_data[KeyDate] = m_date;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Article::guid() const
|
QString Article::guid() const
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace RSS
|
|||||||
{
|
{
|
||||||
class Feed;
|
class Feed;
|
||||||
|
|
||||||
class Article: public QObject
|
class Article : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(Article)
|
Q_DISABLE_COPY(Article)
|
||||||
|
|||||||
@@ -365,19 +365,7 @@ void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
|
|||||||
for (AutoDownloadRule &rule: m_rules) {
|
for (AutoDownloadRule &rule: m_rules) {
|
||||||
if (!rule.isEnabled()) continue;
|
if (!rule.isEnabled()) continue;
|
||||||
if (!rule.feedURLs().contains(job->feedURL)) continue;
|
if (!rule.feedURLs().contains(job->feedURL)) continue;
|
||||||
if (!rule.matches(job->articleData.value(Article::KeyTitle).toString())) continue;
|
if (!rule.accepts(job->articleData)) continue;
|
||||||
|
|
||||||
auto articleDate = job->articleData.value(Article::KeyDate).toDateTime();
|
|
||||||
// if rule is in ignoring state do nothing with matched torrent
|
|
||||||
if (rule.ignoreDays() > 0) {
|
|
||||||
if (rule.lastMatch().isValid()) {
|
|
||||||
if (articleDate < rule.lastMatch().addDays(rule.ignoreDays()))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rule.setLastMatch(articleDate);
|
|
||||||
rule.appendLastComputedEpisode();
|
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
storeDeferred();
|
storeDeferred();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace RSS
|
|||||||
QString message() const;
|
QString message() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AutoDownloader final: public QObject
|
class AutoDownloader final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(AutoDownloader)
|
Q_DISABLE_COPY(AutoDownloader)
|
||||||
|
|||||||
@@ -39,13 +39,14 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "../global.h"
|
||||||
#include "../preferences.h"
|
#include "../preferences.h"
|
||||||
#include "../tristatebool.h"
|
#include "../tristatebool.h"
|
||||||
#include "../utils/fs.h"
|
#include "../utils/fs.h"
|
||||||
#include "../utils/string.h"
|
#include "../utils/string.h"
|
||||||
#include "rss_feed.h"
|
|
||||||
#include "rss_article.h"
|
#include "rss_article.h"
|
||||||
#include "rss_autodownloader.h"
|
#include "rss_autodownloader.h"
|
||||||
|
#include "rss_feed.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -105,7 +106,7 @@ const QString Str_PreviouslyMatched(QStringLiteral("previouslyMatchedEpisodes"))
|
|||||||
|
|
||||||
namespace RSS
|
namespace RSS
|
||||||
{
|
{
|
||||||
struct AutoDownloadRuleData: public QSharedData
|
struct AutoDownloadRuleData : public QSharedData
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
@@ -192,197 +193,198 @@ QRegularExpression AutoDownloadRule::cachedRegex(const QString &expression, bool
|
|||||||
// The cache is cleared whenever the regex/wildcard, must or must not contain fields or
|
// The cache is cleared whenever the regex/wildcard, must or must not contain fields or
|
||||||
// episode filter are modified.
|
// episode filter are modified.
|
||||||
Q_ASSERT(!expression.isEmpty());
|
Q_ASSERT(!expression.isEmpty());
|
||||||
QRegularExpression regex(m_dataPtr->cachedRegexes[expression]);
|
|
||||||
|
|
||||||
if (!regex.pattern().isEmpty())
|
QRegularExpression ®ex = m_dataPtr->cachedRegexes[expression];
|
||||||
return regex;
|
if (regex.pattern().isEmpty()) {
|
||||||
|
regex = QRegularExpression {
|
||||||
|
(isRegex ? expression : Utils::String::wildcardToRegex(expression))
|
||||||
|
, QRegularExpression::CaseInsensitiveOption};
|
||||||
|
}
|
||||||
|
|
||||||
return m_dataPtr->cachedRegexes[expression] = QRegularExpression(isRegex ? expression : Utils::String::wildcardToRegex(expression), QRegularExpression::CaseInsensitiveOption);
|
return regex;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoDownloadRule::matches(const QString &articleTitle, const QString &expression) const
|
bool AutoDownloadRule::matchesExpression(const QString &articleTitle, const QString &expression) const
|
||||||
{
|
{
|
||||||
static QRegularExpression whitespace("\\s+");
|
const QRegularExpression whitespace {"\\s+"};
|
||||||
|
|
||||||
if (expression.isEmpty()) {
|
if (expression.isEmpty()) {
|
||||||
// A regex of the form "expr|" will always match, so do the same for wildcards
|
// A regex of the form "expr|" will always match, so do the same for wildcards
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (m_dataPtr->useRegex) {
|
|
||||||
|
if (m_dataPtr->useRegex) {
|
||||||
QRegularExpression reg(cachedRegex(expression));
|
QRegularExpression reg(cachedRegex(expression));
|
||||||
return reg.match(articleTitle).hasMatch();
|
return reg.match(articleTitle).hasMatch();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Only match if every wildcard token (separated by spaces) is present in the article name.
|
|
||||||
// Order of wildcard tokens is unimportant (if order is important, they should have used *).
|
|
||||||
foreach (const QString &wildcard, expression.split(whitespace, QString::SplitBehavior::SkipEmptyParts)) {
|
|
||||||
QRegularExpression reg(cachedRegex(wildcard, false));
|
|
||||||
|
|
||||||
if (!reg.match(articleTitle).hasMatch())
|
// Only match if every wildcard token (separated by spaces) is present in the article name.
|
||||||
return false;
|
// Order of wildcard tokens is unimportant (if order is important, they should have used *).
|
||||||
}
|
const QStringList wildcards {expression.split(whitespace, QString::SplitBehavior::SkipEmptyParts)};
|
||||||
|
for (const QString &wildcard : wildcards) {
|
||||||
|
const QRegularExpression reg {cachedRegex(wildcard, false)};
|
||||||
|
if (!reg.match(articleTitle).hasMatch())
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoDownloadRule::matches(const QString &articleTitle) const
|
bool AutoDownloadRule::matchesMustContainExpression(const QString &articleTitle) const
|
||||||
|
{
|
||||||
|
if (m_dataPtr->mustContain.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Each expression is either a regex, or a set of wildcards separated by whitespace.
|
||||||
|
// Accept if any complete expression matches.
|
||||||
|
for (const QString &expression : qAsConst(m_dataPtr->mustContain)) {
|
||||||
|
// A regex of the form "expr|" will always match, so do the same for wildcards
|
||||||
|
if (matchesExpression(articleTitle, expression))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoDownloadRule::matchesMustNotContainExpression(const QString& articleTitle) const
|
||||||
|
{
|
||||||
|
if (m_dataPtr->mustNotContain.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Each expression is either a regex, or a set of wildcards separated by whitespace.
|
||||||
|
// Reject if any complete expression matches.
|
||||||
|
for (const QString &expression : qAsConst(m_dataPtr->mustNotContain)) {
|
||||||
|
// A regex of the form "expr|" will always match, so do the same for wildcards
|
||||||
|
if (matchesExpression(articleTitle, expression))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoDownloadRule::matchesEpisodeFilterExpression(const QString& articleTitle) const
|
||||||
{
|
{
|
||||||
// Reset the lastComputedEpisode, we don't want to leak it between matches
|
// Reset the lastComputedEpisode, we don't want to leak it between matches
|
||||||
m_dataPtr->lastComputedEpisode.clear();
|
m_dataPtr->lastComputedEpisode.clear();
|
||||||
|
|
||||||
if (!m_dataPtr->mustContain.empty()) {
|
if (m_dataPtr->episodeFilter.isEmpty())
|
||||||
bool logged = false;
|
return true;
|
||||||
bool foundMustContain = false;
|
|
||||||
|
|
||||||
// Each expression is either a regex, or a set of wildcards separated by whitespace.
|
const QRegularExpression filterRegex {cachedRegex("(^\\d{1,4})x(.*;$)")};
|
||||||
// Accept if any complete expression matches.
|
const QRegularExpressionMatch matcher {filterRegex.match(m_dataPtr->episodeFilter)};
|
||||||
foreach (const QString &expression, m_dataPtr->mustContain) {
|
if (!matcher.hasMatch())
|
||||||
if (!logged) {
|
return false;
|
||||||
// qDebug() << "Checking matching" << (m_dataPtr->useRegex ? "regex:" : "wildcard expressions:") << m_dataPtr->mustContain.join("|");
|
|
||||||
logged = true;
|
const QString season {matcher.captured(1)};
|
||||||
|
const QStringList episodes {matcher.captured(2).split(';')};
|
||||||
|
const int seasonOurs {season.toInt()};
|
||||||
|
|
||||||
|
for (QString episode : episodes) {
|
||||||
|
if (episode.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We need to trim leading zeroes, but if it's all zeros then we want episode zero.
|
||||||
|
while ((episode.size() > 1) && episode.startsWith('0'))
|
||||||
|
episode = episode.right(episode.size() - 1);
|
||||||
|
|
||||||
|
if (episode.indexOf('-') != -1) { // Range detected
|
||||||
|
const QString partialPattern1 {"\\bs0?(\\d{1,4})[ -_\\.]?e(0?\\d{1,4})(?:\\D|\\b)"};
|
||||||
|
const QString partialPattern2 {"\\b(\\d{1,4})x(0?\\d{1,4})(?:\\D|\\b)"};
|
||||||
|
|
||||||
|
// Extract partial match from article and compare as digits
|
||||||
|
QRegularExpressionMatch matcher = cachedRegex(partialPattern1).match(articleTitle);
|
||||||
|
bool matched = matcher.hasMatch();
|
||||||
|
|
||||||
|
if (!matched) {
|
||||||
|
matcher = cachedRegex(partialPattern2).match(articleTitle);
|
||||||
|
matched = matcher.hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
// A regex of the form "expr|" will always match, so do the same for wildcards
|
if (matched) {
|
||||||
foundMustContain = matches(articleTitle, expression);
|
const int seasonTheirs {matcher.captured(1).toInt()};
|
||||||
|
const int episodeTheirs {matcher.captured(2).toInt()};
|
||||||
|
|
||||||
if (foundMustContain) {
|
if (episode.endsWith('-')) { // Infinite range
|
||||||
// qDebug() << "Found matching" << (m_dataPtr->useRegex ? "regex:" : "wildcard expression:") << expression;
|
const int episodeOurs {episode.leftRef(episode.size() - 1).toInt()};
|
||||||
break;
|
if (((seasonTheirs == seasonOurs) && (episodeTheirs >= episodeOurs)) || (seasonTheirs > seasonOurs))
|
||||||
}
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundMustContain)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_dataPtr->mustNotContain.empty()) {
|
|
||||||
bool logged = false;
|
|
||||||
|
|
||||||
// Each expression is either a regex, or a set of wildcards separated by whitespace.
|
|
||||||
// Reject if any complete expression matches.
|
|
||||||
foreach (const QString &expression, m_dataPtr->mustNotContain) {
|
|
||||||
if (!logged) {
|
|
||||||
// qDebug() << "Checking not matching" << (m_dataPtr->useRegex ? "regex:" : "wildcard expressions:") << m_dataPtr->mustNotContain.join("|");
|
|
||||||
logged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A regex of the form "expr|" will always match, so do the same for wildcards
|
|
||||||
if (matches(articleTitle, expression)) {
|
|
||||||
// qDebug() << "Found not matching" << (m_dataPtr->useRegex ? "regex:" : "wildcard expression:") << expression;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_dataPtr->episodeFilter.isEmpty()) {
|
|
||||||
// qDebug() << "Checking episode filter:" << m_dataPtr->episodeFilter;
|
|
||||||
QRegularExpression f(cachedRegex("(^\\d{1,4})x(.*;$)"));
|
|
||||||
QRegularExpressionMatch matcher = f.match(m_dataPtr->episodeFilter);
|
|
||||||
bool matched = matcher.hasMatch();
|
|
||||||
|
|
||||||
if (!matched)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QString s = matcher.captured(1);
|
|
||||||
QStringList eps = matcher.captured(2).split(";");
|
|
||||||
int sOurs = s.toInt();
|
|
||||||
|
|
||||||
foreach (QString ep, eps) {
|
|
||||||
if (ep.isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// We need to trim leading zeroes, but if it's all zeros then we want episode zero.
|
|
||||||
while (ep.size() > 1 && ep.startsWith("0"))
|
|
||||||
ep = ep.right(ep.size() - 1);
|
|
||||||
|
|
||||||
if (ep.indexOf('-') != -1) { // Range detected
|
|
||||||
QString partialPattern1 = "\\bs0?(\\d{1,4})[ -_\\.]?e(0?\\d{1,4})(?:\\D|\\b)";
|
|
||||||
QString partialPattern2 = "\\b(\\d{1,4})x(0?\\d{1,4})(?:\\D|\\b)";
|
|
||||||
QRegularExpression reg(cachedRegex(partialPattern1));
|
|
||||||
|
|
||||||
if (ep.endsWith('-')) { // Infinite range
|
|
||||||
int epOurs = ep.leftRef(ep.size() - 1).toInt();
|
|
||||||
|
|
||||||
// Extract partial match from article and compare as digits
|
|
||||||
matcher = reg.match(articleTitle);
|
|
||||||
matched = matcher.hasMatch();
|
|
||||||
|
|
||||||
if (!matched) {
|
|
||||||
reg = QRegularExpression(cachedRegex(partialPattern2));
|
|
||||||
matcher = reg.match(articleTitle);
|
|
||||||
matched = matcher.hasMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matched) {
|
|
||||||
int sTheirs = matcher.captured(1).toInt();
|
|
||||||
int epTheirs = matcher.captured(2).toInt();
|
|
||||||
if (((sTheirs == sOurs) && (epTheirs >= epOurs)) || (sTheirs > sOurs)) {
|
|
||||||
// qDebug() << "Matched episode:" << ep;
|
|
||||||
// qDebug() << "Matched article:" << articleTitle;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else { // Normal range
|
else { // Normal range
|
||||||
QStringList range = ep.split('-');
|
const QStringList range {episode.split('-')};
|
||||||
Q_ASSERT(range.size() == 2);
|
Q_ASSERT(range.size() == 2);
|
||||||
if (range.first().toInt() > range.last().toInt())
|
if (range.first().toInt() > range.last().toInt())
|
||||||
continue; // Ignore this subrule completely
|
continue; // Ignore this subrule completely
|
||||||
|
|
||||||
int epOursFirst = range.first().toInt();
|
const int episodeOursFirst {range.first().toInt()};
|
||||||
int epOursLast = range.last().toInt();
|
const int episodeOursLast {range.last().toInt()};
|
||||||
|
if ((seasonTheirs == seasonOurs) && ((episodeOursFirst <= episodeTheirs) && (episodeOursLast >= episodeTheirs)))
|
||||||
// Extract partial match from article and compare as digits
|
return true;
|
||||||
matcher = reg.match(articleTitle);
|
|
||||||
matched = matcher.hasMatch();
|
|
||||||
|
|
||||||
if (!matched) {
|
|
||||||
reg = QRegularExpression(cachedRegex(partialPattern2));
|
|
||||||
matcher = reg.match(articleTitle);
|
|
||||||
matched = matcher.hasMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matched) {
|
|
||||||
int sTheirs = matcher.captured(1).toInt();
|
|
||||||
int epTheirs = matcher.captured(2).toInt();
|
|
||||||
if ((sTheirs == sOurs) && ((epOursFirst <= epTheirs) && (epOursLast >= epTheirs))) {
|
|
||||||
// qDebug() << "Matched episode:" << ep;
|
|
||||||
// qDebug() << "Matched article:" << articleTitle;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // Single number
|
|
||||||
QString expStr("\\b(?:s0?" + s + "[ -_\\.]?" + "e0?" + ep + "|" + s + "x" + "0?" + ep + ")(?:\\D|\\b)");
|
|
||||||
QRegularExpression reg(cachedRegex(expStr));
|
|
||||||
if (reg.match(articleTitle).hasMatch()) {
|
|
||||||
// qDebug() << "Matched episode:" << ep;
|
|
||||||
// qDebug() << "Matched article:" << articleTitle;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else { // Single number
|
||||||
|
const QString expStr {QString("\\b(?:s0?%1[ -_\\.]?e0?%2|%1x0?%2)(?:\\D|\\b)").arg(season, episode)};
|
||||||
|
if (cachedRegex(expStr).match(articleTitle).hasMatch())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoDownloadRule::matchesSmartEpisodeFilter(const QString& articleTitle) const
|
||||||
|
{
|
||||||
|
if (!useSmartFilter())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const QString episodeStr = computeEpisodeName(articleTitle);
|
||||||
|
if (episodeStr.isEmpty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// See if this episode has been downloaded before
|
||||||
|
const bool previouslyMatched = m_dataPtr->previouslyMatchedEpisodes.contains(episodeStr);
|
||||||
|
const bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive) || articleTitle.contains("PROPER", Qt::CaseInsensitive);
|
||||||
|
if (previouslyMatched && !isRepack)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
m_dataPtr->lastComputedEpisode = episodeStr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoDownloadRule::matches(const QVariantHash &articleData) const
|
||||||
|
{
|
||||||
|
const QDateTime articleDate {articleData[Article::KeyDate].toDateTime()};
|
||||||
|
if (ignoreDays() > 0) {
|
||||||
|
if (lastMatch().isValid() && (articleDate < lastMatch().addDays(ignoreDays())))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useSmartFilter()) {
|
const QString articleTitle {articleData[Article::KeyTitle].toString()};
|
||||||
// now see if this episode has been downloaded before
|
if (!matchesMustContainExpression(articleTitle))
|
||||||
const QString episodeStr = computeEpisodeName(articleTitle);
|
return false;
|
||||||
|
if (!matchesMustNotContainExpression(articleTitle))
|
||||||
|
return false;
|
||||||
|
if (!matchesEpisodeFilterExpression(articleTitle))
|
||||||
|
return false;
|
||||||
|
if (!matchesSmartEpisodeFilter(articleTitle))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!episodeStr.isEmpty()) {
|
return true;
|
||||||
bool previouslyMatched = m_dataPtr->previouslyMatchedEpisodes.contains(episodeStr);
|
}
|
||||||
bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive) || articleTitle.contains("PROPER", Qt::CaseInsensitive);
|
|
||||||
if (previouslyMatched && !isRepack)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_dataPtr->lastComputedEpisode = episodeStr;
|
bool AutoDownloadRule::accepts(const QVariantHash &articleData)
|
||||||
}
|
{
|
||||||
|
if (!matches(articleData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setLastMatch(articleData[Article::KeyDate].toDateTime());
|
||||||
|
|
||||||
|
if (!m_dataPtr->lastComputedEpisode.isEmpty()) {
|
||||||
|
// TODO: probably need to add a marker for PROPER/REPACK to avoid duplicate downloads
|
||||||
|
m_dataPtr->previouslyMatchedEpisodes.append(m_dataPtr->lastComputedEpisode);
|
||||||
|
m_dataPtr->lastComputedEpisode.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// qDebug() << "Matched article:" << articleTitle;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +502,7 @@ void AutoDownloadRule::setMustContain(const QString &tokens)
|
|||||||
if (m_dataPtr->useRegex)
|
if (m_dataPtr->useRegex)
|
||||||
m_dataPtr->mustContain = QStringList() << tokens;
|
m_dataPtr->mustContain = QStringList() << tokens;
|
||||||
else
|
else
|
||||||
m_dataPtr->mustContain = tokens.split("|");
|
m_dataPtr->mustContain = tokens.split('|');
|
||||||
|
|
||||||
// Check for single empty string - if so, no condition
|
// Check for single empty string - if so, no condition
|
||||||
if ((m_dataPtr->mustContain.size() == 1) && m_dataPtr->mustContain[0].isEmpty())
|
if ((m_dataPtr->mustContain.size() == 1) && m_dataPtr->mustContain[0].isEmpty())
|
||||||
@@ -514,7 +516,7 @@ void AutoDownloadRule::setMustNotContain(const QString &tokens)
|
|||||||
if (m_dataPtr->useRegex)
|
if (m_dataPtr->useRegex)
|
||||||
m_dataPtr->mustNotContain = QStringList() << tokens;
|
m_dataPtr->mustNotContain = QStringList() << tokens;
|
||||||
else
|
else
|
||||||
m_dataPtr->mustNotContain = tokens.split("|");
|
m_dataPtr->mustNotContain = tokens.split('|');
|
||||||
|
|
||||||
// Check for single empty string - if so, no condition
|
// Check for single empty string - if so, no condition
|
||||||
if ((m_dataPtr->mustNotContain.size() == 1) && m_dataPtr->mustNotContain[0].isEmpty())
|
if ((m_dataPtr->mustNotContain.size() == 1) && m_dataPtr->mustNotContain[0].isEmpty())
|
||||||
@@ -603,12 +605,12 @@ int AutoDownloadRule::ignoreDays() const
|
|||||||
|
|
||||||
QString AutoDownloadRule::mustContain() const
|
QString AutoDownloadRule::mustContain() const
|
||||||
{
|
{
|
||||||
return m_dataPtr->mustContain.join("|");
|
return m_dataPtr->mustContain.join('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AutoDownloadRule::mustNotContain() const
|
QString AutoDownloadRule::mustNotContain() const
|
||||||
{
|
{
|
||||||
return m_dataPtr->mustNotContain.join("|");
|
return m_dataPtr->mustNotContain.join('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoDownloadRule::useSmartFilter() const
|
bool AutoDownloadRule::useSmartFilter() const
|
||||||
@@ -642,15 +644,6 @@ void AutoDownloadRule::setPreviouslyMatchedEpisodes(const QStringList &previousl
|
|||||||
m_dataPtr->previouslyMatchedEpisodes = previouslyMatchedEpisodes;
|
m_dataPtr->previouslyMatchedEpisodes = previouslyMatchedEpisodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoDownloadRule::appendLastComputedEpisode()
|
|
||||||
{
|
|
||||||
if (!m_dataPtr->lastComputedEpisode.isEmpty()) {
|
|
||||||
// TODO: probably need to add a marker for PROPER/REPACK to avoid duplicate downloads
|
|
||||||
m_dataPtr->previouslyMatchedEpisodes.append(m_dataPtr->lastComputedEpisode);
|
|
||||||
m_dataPtr->lastComputedEpisode.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AutoDownloadRule::episodeFilter() const
|
QString AutoDownloadRule::episodeFilter() const
|
||||||
{
|
{
|
||||||
return m_dataPtr->episodeFilter;
|
return m_dataPtr->episodeFilter;
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ namespace RSS
|
|||||||
QString episodeFilter() const;
|
QString episodeFilter() const;
|
||||||
void setEpisodeFilter(const QString &e);
|
void setEpisodeFilter(const QString &e);
|
||||||
|
|
||||||
void appendLastComputedEpisode();
|
|
||||||
QStringList previouslyMatchedEpisodes() const;
|
QStringList previouslyMatchedEpisodes() const;
|
||||||
void setPreviouslyMatchedEpisodes(const QStringList &previouslyMatchedEpisodes);
|
void setPreviouslyMatchedEpisodes(const QStringList &previouslyMatchedEpisodes);
|
||||||
|
|
||||||
@@ -82,7 +81,8 @@ namespace RSS
|
|||||||
QString assignedCategory() const;
|
QString assignedCategory() const;
|
||||||
void setCategory(const QString &category);
|
void setCategory(const QString &category);
|
||||||
|
|
||||||
bool matches(const QString &articleTitle) const;
|
bool matches(const QVariantHash &articleData) const;
|
||||||
|
bool accepts(const QVariantHash &articleData);
|
||||||
|
|
||||||
AutoDownloadRule &operator=(const AutoDownloadRule &other);
|
AutoDownloadRule &operator=(const AutoDownloadRule &other);
|
||||||
bool operator==(const AutoDownloadRule &other) const;
|
bool operator==(const AutoDownloadRule &other) const;
|
||||||
@@ -95,7 +95,11 @@ namespace RSS
|
|||||||
static AutoDownloadRule fromLegacyDict(const QVariantHash &dict);
|
static AutoDownloadRule fromLegacyDict(const QVariantHash &dict);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool matches(const QString &articleTitle, const QString &expression) const;
|
bool matchesMustContainExpression(const QString &articleTitle) const;
|
||||||
|
bool matchesMustNotContainExpression(const QString &articleTitle) const;
|
||||||
|
bool matchesEpisodeFilterExpression(const QString &articleTitle) const;
|
||||||
|
bool matchesSmartEpisodeFilter(const QString &articleTitle) const;
|
||||||
|
bool matchesExpression(const QString &articleTitle, const QString &expression) const;
|
||||||
QRegularExpression cachedRegex(const QString &expression, bool isRegex = true) const;
|
QRegularExpression cachedRegex(const QString &expression, bool isRegex = true) const;
|
||||||
|
|
||||||
QSharedDataPointer<AutoDownloadRuleData> m_dataPtr;
|
QSharedDataPointer<AutoDownloadRuleData> m_dataPtr;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user