mirror of
https://github.com/transmission/transmission
synced 2024-07-01 06:54:22 +00:00
Compare commits
53 Commits
3676a8cfc0
...
c1ee2b4f12
Author | SHA1 | Date | |
---|---|---|---|
|
c1ee2b4f12 | ||
|
ded1b96137 | ||
|
2c51f53b49 | ||
|
4151b0eed9 | ||
|
3cc0cc42d7 | ||
|
358139bc13 | ||
|
77ca06029d | ||
|
5b22db3fd2 | ||
|
ec5296c8dc | ||
|
0f1aaf11e0 | ||
|
1f10c50979 | ||
|
febfe49ca3 | ||
|
ec6112e0b1 | ||
|
d42d0f3f3f | ||
|
b565e076a9 | ||
|
489de60222 | ||
|
2c2011d40f | ||
|
acee39e15c | ||
|
78027a8e5b | ||
|
efec65050e | ||
|
f3f887c93e | ||
|
2b75869c80 | ||
|
a18fca5950 | ||
|
d467fa1f7f | ||
|
63e74f4df8 | ||
|
6132706565 | ||
|
88d280be8f | ||
|
96de1706af | ||
|
4657d210ba | ||
|
3677e7a591 | ||
|
d6f5e60a35 | ||
|
5f091fac18 | ||
|
92478ec849 | ||
|
bf0119dd3f | ||
|
17c6ec755c | ||
|
edddf9d80e | ||
|
c465575dab | ||
|
acd0c22a3d | ||
|
c2e12cbf52 | ||
|
381c17e0bb | ||
|
8bb49e3fdf | ||
|
be67b33f42 | ||
|
9748f42c5a | ||
|
adc405e5be | ||
|
e8a72d9c5d | ||
|
09b67c84b1 | ||
|
740ce3b904 | ||
|
0a299bbbe8 | ||
|
78c82b4526 | ||
|
f24582ea2b | ||
|
1be53ac139 | ||
|
b6363fcf47 | ||
|
459c8d3791 |
|
@ -32,7 +32,7 @@ PenaltyBreakBeforeFirstCallParameter: 0
|
|||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
PointerAlignment: Left
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SortIncludes: Never
|
||||
SpaceAfterCStyleCast: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
|
|
96
.github/workflows/actions.yml
vendored
96
.github/workflows/actions.yml
vendored
|
@ -147,11 +147,6 @@ jobs:
|
|||
libssl-dev \
|
||||
ninja-build \
|
||||
npm
|
||||
- name: Temporary workaround for sanitizer crashes
|
||||
# https://bugs.launchpad.net/ubuntu/+source/llvm-toolchain-14/+bug/2048768
|
||||
# https://github.com/actions/runner-images/issues/9491
|
||||
# https://github.com/actions/runner-images/pull/9513
|
||||
run: sudo sysctl vm.mmap_rnd_bits=28
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
@ -226,7 +221,7 @@ jobs:
|
|||
run: cmake -E chdir obj ctest -j $(nproc) --build-config Debug --output-on-failure
|
||||
|
||||
clang-tidy-libtransmission:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [ what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.test-style == 'true' }}
|
||||
steps:
|
||||
|
@ -335,9 +330,9 @@ jobs:
|
|||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
||||
# Only verify build support on old macOS
|
||||
macos-11:
|
||||
runs-on: macos-11
|
||||
# Only verify build support on older macOS and SDK
|
||||
macos-12:
|
||||
runs-on: macos-12
|
||||
needs: [ what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-mac == 'true' }}
|
||||
steps:
|
||||
|
@ -354,6 +349,9 @@ jobs:
|
|||
with:
|
||||
path: src
|
||||
submodules: recursive
|
||||
- name: Set Xcode to 13.2.1
|
||||
run: |
|
||||
sudo xcode-select --switch /Applications/Xcode_13.2.1.app
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
|
@ -363,8 +361,9 @@ jobs:
|
|||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=pfx \
|
||||
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
|
||||
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt \
|
||||
-DENABLE_GTK=OFF \
|
||||
-DENABLE_MAC=${{ (needs.what-to-make.outputs.make-mac == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_QT=OFF \
|
||||
-DENABLE_TESTS=OFF \
|
||||
-DENABLE_WERROR=ON \
|
||||
-DRUN_CLANG_TIDY=OFF
|
||||
|
@ -613,7 +612,6 @@ jobs:
|
|||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=pfx \
|
||||
-DCMAKE_OSX_ARCHITECTURES='x86_64;arm64' \
|
||||
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt \
|
||||
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_GTK=OFF \
|
||||
|
@ -990,3 +988,79 @@ jobs:
|
|||
working-directory: ./android
|
||||
run: |
|
||||
gradle build
|
||||
|
||||
ubuntu-24-04-cxx-23:
|
||||
needs: [ make-source-tarball, what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-cli == 'true' || needs.what-to-make.outputs.make-daemon == 'true' || needs.what-to-make.outputs.make-gtk == 'true' || needs.what-to-make.outputs.make-qt == 'true' || needs.what-to-make.outputs.make-tests == 'true' || needs.what-to-make.outputs.make-utils == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Show Configuration
|
||||
run: |
|
||||
echo '${{ toJSON(needs) }}'
|
||||
echo '${{ toJSON(runner) }}'
|
||||
cat /etc/os-release
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
set -ex
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
appstream \
|
||||
ca-certificates \
|
||||
clang \
|
||||
cmake \
|
||||
gettext \
|
||||
libcurl4-openssl-dev \
|
||||
libdeflate-dev \
|
||||
libevent-dev \
|
||||
libfmt-dev \
|
||||
libminiupnpc-dev \
|
||||
libnatpmp-dev \
|
||||
libpsl-dev \
|
||||
libssl-dev \
|
||||
ninja-build \
|
||||
npm
|
||||
- name: Get Dependencies (GTK)
|
||||
if: ${{ needs.what-to-make.outputs.make-gtk == 'true' }}
|
||||
run: sudo apt-get install -y --no-install-recommends libglibmm-2.4-dev libgtkmm-3.0-dev
|
||||
- name: Get Dependencies (Qt)
|
||||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: sudo apt-get install -y --no-install-recommends qtbase5-dev libqt5svg5-dev qttools5-dev
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
path: src
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-S src \
|
||||
-B obj \
|
||||
-G Ninja \
|
||||
-DCMAKE_CXX_STANDARD=23 \
|
||||
-DCMAKE_CXX_COMPILER='clang++' \
|
||||
-DCMAKE_C_COMPILER='clang' \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=pfx \
|
||||
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_GTK=${{ (needs.what-to-make.outputs.make-gtk == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_MAC=OFF \
|
||||
-DENABLE_QT=${{ (needs.what-to-make.outputs.make-qt == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_TESTS=OFF \
|
||||
-DENABLE_UTILS=${{ (needs.what-to-make.outputs.make-utils == 'true') && 'ON' || 'OFF' }} \
|
||||
-DREBUILD_WEB=${{ (needs.what-to-make.outputs.make-web == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_WERROR=ON \
|
||||
-DRUN_CLANG_TIDY=OFF
|
||||
- name: Make
|
||||
run: cmake --build obj --config RelWithDebInfo -- "-k 0"
|
||||
- name: Test
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
env:
|
||||
TMPDIR: /private/tmp
|
||||
run: cmake -E chdir obj ctest -j $(nproc) --build-config RelWithDebInfo --output-on-failure
|
||||
- name: Install
|
||||
run: cmake --build obj --config RelWithDebInfo --target install/strip
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
|
|
@ -169,9 +169,13 @@ set(TR_VCS_REVISION_FILE "${PROJECT_SOURCE_DIR}/REVISION")
|
|||
|
||||
## Compiler standard version
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
if(NOT CMAKE_C_STANDARD)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
endif()
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
||||
|
@ -526,17 +530,9 @@ if(NOT USE_SYSTEM_MINIUPNPC)
|
|||
target_compile_definitions(miniupnpc::libminiupnpc
|
||||
INTERFACE
|
||||
MINIUPNP_STATICLIB)
|
||||
|
||||
set(MINIUPNPC_VERSION 2.2)
|
||||
set(MINIUPNPC_API_VERSION 17)
|
||||
endif()
|
||||
unset(TR_MINIUPNPC_LIBNAME)
|
||||
|
||||
target_compile_definitions(miniupnpc::libminiupnpc
|
||||
INTERFACE
|
||||
SYSTEM_MINIUPNP
|
||||
$<$<VERSION_LESS:${MINIUPNPC_VERSION},1.7>:MINIUPNPC_API_VERSION=${MINIUPNPC_API_VERSION}>) # API version macro was only added in 1.7
|
||||
|
||||
add_subdirectory(${TR_THIRD_PARTY_SOURCE_DIR}/wildmat)
|
||||
|
||||
tr_add_external_auto_library(DHT dht dht
|
||||
|
|
|
@ -75,8 +75,8 @@
|
|||
A20162C913DE48BF00E15488 /* receivedata.c in Sources */ = {isa = PBXBuildFile; fileRef = A20162C713DE48BF00E15488 /* receivedata.c */; };
|
||||
A20162CA13DE48BF00E15488 /* receivedata.h in Headers */ = {isa = PBXBuildFile; fileRef = A20162C813DE48BF00E15488 /* receivedata.h */; };
|
||||
A20162CD13DE497000E15488 /* portlistingparse.c in Sources */ = {isa = PBXBuildFile; fileRef = A20162CB13DE497000E15488 /* portlistingparse.c */; };
|
||||
A20162CE13DE497000E15488 /* portlistingparse.h in Headers */ = {isa = PBXBuildFile; fileRef = A20162CC13DE497000E15488 /* portlistingparse.h */; };
|
||||
A20162D013DE49E500E15488 /* miniupnpctypes.h in Headers */ = {isa = PBXBuildFile; fileRef = A20162CF13DE49E500E15488 /* miniupnpctypes.h */; };
|
||||
A20162CE13DE497000E15488 /* portlistingparse.h in Headers */ = {isa = PBXBuildFile; fileRef = A20162CC13DE497000E15488 /* portlistingparse.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
A20162D013DE49E500E15488 /* miniupnpctypes.h in Headers */ = {isa = PBXBuildFile; fileRef = A20162CF13DE49E500E15488 /* miniupnpctypes.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
A2074F4C12BEA8CE00F70985 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = A2074F4B12BEA8CE00F70985 /* buffer.c */; };
|
||||
A2074F5912BEA8E000F70985 /* bufferevent_filter.c in Sources */ = {isa = PBXBuildFile; fileRef = A2074F5012BEA8E000F70985 /* bufferevent_filter.c */; };
|
||||
A2074F5B12BEA8E000F70985 /* bufferevent_pair.c in Sources */ = {isa = PBXBuildFile; fileRef = A2074F5212BEA8E000F70985 /* bufferevent_pair.c */; };
|
||||
|
@ -264,14 +264,14 @@
|
|||
A2FB701C0D95CAEA0001F331 /* GroupsController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2FB701B0D95CAEA0001F331 /* GroupsController.mm */; };
|
||||
A47A7C87B8B57BE50DF0D410 /* torrent-files.cc in Sources */ = {isa = PBXBuildFile; fileRef = A47A7C87B8B57BE50DF0D411 /* torrent-files.cc */; };
|
||||
A47A7C87B8B57BE50DF0D412 /* torrent-files.h in Headers */ = {isa = PBXBuildFile; fileRef = A47A7C87B8B57BE50DF0D413 /* torrent-files.h */; };
|
||||
BE1183580CE160C50002D0F3 /* miniupnpc_declspec.h in Headers */ = {isa = PBXBuildFile; fileRef = BE11834E0CE160C50002D0F3 /* miniupnpc_declspec.h */; };
|
||||
BE1183590CE160C50002D0F3 /* igd_desc_parse.h in Headers */ = {isa = PBXBuildFile; fileRef = BE11834F0CE160C50002D0F3 /* igd_desc_parse.h */; };
|
||||
BE1183580CE160C50002D0F3 /* miniupnpc_declspec.h in Headers */ = {isa = PBXBuildFile; fileRef = BE11834E0CE160C50002D0F3 /* miniupnpc_declspec.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BE1183590CE160C50002D0F3 /* igd_desc_parse.h in Headers */ = {isa = PBXBuildFile; fileRef = BE11834F0CE160C50002D0F3 /* igd_desc_parse.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BE11835A0CE160C50002D0F3 /* minixml.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183500CE160C50002D0F3 /* minixml.h */; };
|
||||
BE11835B0CE160C50002D0F3 /* miniwget.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183510CE160C50002D0F3 /* miniwget.h */; };
|
||||
BE11835B0CE160C50002D0F3 /* miniwget.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183510CE160C50002D0F3 /* miniwget.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BE11835C0CE160C50002D0F3 /* minisoap.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183520CE160C50002D0F3 /* minisoap.h */; };
|
||||
BE11835D0CE160C50002D0F3 /* upnpreplyparse.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183530CE160C50002D0F3 /* upnpreplyparse.h */; };
|
||||
BE11835E0CE160C50002D0F3 /* upnpcommands.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183540CE160C50002D0F3 /* upnpcommands.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BE11835F0CE160C50002D0F3 /* miniupnpc.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183550CE160C50002D0F3 /* miniupnpc.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BE11835D0CE160C50002D0F3 /* upnpreplyparse.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183530CE160C50002D0F3 /* upnpreplyparse.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BE11835E0CE160C50002D0F3 /* upnpcommands.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183540CE160C50002D0F3 /* upnpcommands.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BE11835F0CE160C50002D0F3 /* miniupnpc.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183550CE160C50002D0F3 /* miniupnpc.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
BE1183600CE160C50002D0F3 /* minissdpc.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1183560CE160C50002D0F3 /* minissdpc.h */; };
|
||||
BE1183690CE160D50002D0F3 /* igd_desc_parse.c in Sources */ = {isa = PBXBuildFile; fileRef = BE1183610CE160D50002D0F3 /* igd_desc_parse.c */; };
|
||||
BE11836A0CE160D50002D0F3 /* minixml.c in Sources */ = {isa = PBXBuildFile; fileRef = BE1183620CE160D50002D0F3 /* minixml.c */; };
|
||||
|
@ -325,7 +325,7 @@
|
|||
C11DEA161FCD31C0009E22B9 /* subprocess-posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = C11DEA141FCD31C0009E22B9 /* subprocess-posix.cc */; };
|
||||
C11DEA171FCD31C0009E22B9 /* subprocess.h in Headers */ = {isa = PBXBuildFile; fileRef = C11DEA151FCD31C0009E22B9 /* subprocess.h */; };
|
||||
C12F19791E1AE3C30005E93F /* upnperrors.c in Sources */ = {isa = PBXBuildFile; fileRef = C12F19771E1AE3C30005E93F /* upnperrors.c */; };
|
||||
C12F197B1E1AE4460005E93F /* upnperrors.h in Headers */ = {isa = PBXBuildFile; fileRef = C12F197A1E1AE4460005E93F /* upnperrors.h */; };
|
||||
C12F197B1E1AE4460005E93F /* upnperrors.h in Headers */ = {isa = PBXBuildFile; fileRef = C12F197A1E1AE4460005E93F /* upnperrors.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
C1305EBE186A13B100F03351 /* file.cc in Sources */ = {isa = PBXBuildFile; fileRef = C1305EB8186A134000F03351 /* file.cc */; };
|
||||
C1425B361EE9C605001DB85F /* tr-assert.h in Headers */ = {isa = PBXBuildFile; fileRef = C1425B331EE9C5EA001DB85F /* tr-assert.h */; };
|
||||
C1425B371EE9C705001DB85F /* tr-macros.h in Headers */ = {isa = PBXBuildFile; fileRef = C1425B341EE9C5EA001DB85F /* tr-macros.h */; };
|
||||
|
@ -353,7 +353,7 @@
|
|||
C1846BA3294F7A6800A98F30 /* wildmat.h in Headers */ = {isa = PBXBuildFile; fileRef = C1846B87294F781800A98F30 /* wildmat.h */; };
|
||||
C1846BA9294F7B5A00A98F30 /* libwildmat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C1846B9E294F7A3400A98F30 /* libwildmat.a */; };
|
||||
C1BF7BA81F2A3CB7008E88A7 /* upnpdev.c in Sources */ = {isa = PBXBuildFile; fileRef = C1BF7BA71F2A3CB7008E88A7 /* upnpdev.c */; };
|
||||
C1BF7BAA1F2A3CCE008E88A7 /* upnpdev.h in Headers */ = {isa = PBXBuildFile; fileRef = C1BF7BA91F2A3CCE008E88A7 /* upnpdev.h */; };
|
||||
C1BF7BAA1F2A3CCE008E88A7 /* upnpdev.h in Headers */ = {isa = PBXBuildFile; fileRef = C1BF7BA91F2A3CCE008E88A7 /* upnpdev.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
C1F690FD1AD0627500D95CF0 /* daemon-posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = C1F690FC1AD0627500D95CF0 /* daemon-posix.cc */; };
|
||||
C1FEE5781C3223CC00D62832 /* watchdir-generic.cc in Sources */ = {isa = PBXBuildFile; fileRef = C1FEE5731C3223CC00D62832 /* watchdir-generic.cc */; };
|
||||
C1FEE5791C3223CC00D62832 /* watchdir-kqueue.cc in Sources */ = {isa = PBXBuildFile; fileRef = C1FEE5741C3223CC00D62832 /* watchdir-kqueue.cc */; };
|
||||
|
@ -1367,7 +1367,7 @@
|
|||
ED20B87D285892C5005FA6BE /* crc32_multipliers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crc32_multipliers.h; path = lib/crc32_multipliers.h; sourceTree = "<group>"; };
|
||||
ED20B87E285892C5005FA6BE /* crc32_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crc32_tables.h; path = lib/crc32_tables.h; sourceTree = "<group>"; };
|
||||
ED67FB402B70FCE400D8A037 /* settings.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = settings.cc; sourceTree = "<group>"; };
|
||||
ED67FB412B70FCE400D8A037 /* settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = "<group>"; };
|
||||
ED67FB412B70FCE400D8A037 /* settings.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = settings.h; sourceTree = "<group>"; };
|
||||
ED86936D2ADAE34D00342B1A /* DefaultAppHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultAppHelper.h; sourceTree = "<group>"; };
|
||||
ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DefaultAppHelper.mm; sourceTree = "<group>"; };
|
||||
ED8A163B2735A8AA000D61F9 /* peer-mgr-active-requests.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "peer-mgr-active-requests.h"; sourceTree = "<group>"; };
|
||||
|
@ -2716,8 +2716,8 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = BE11834C0CE160A80002D0F3 /* Build configuration list for PBXNativeTarget "miniupnp" */;
|
||||
buildPhases = (
|
||||
A2305097100C0293003FDB0C /* ShellScript */,
|
||||
C12F197C1E1AE55A0005E93F /* ShellScript */,
|
||||
A2305097100C0293003FDB0C /* updateminiupnpcstrings */,
|
||||
C12F197C1E1AE55A0005E93F /* symlinks */,
|
||||
BE1183440CE160960002D0F3 /* Headers */,
|
||||
BE1183450CE160960002D0F3 /* Sources */,
|
||||
BE1183460CE160960002D0F3 /* Frameworks */,
|
||||
|
@ -2909,7 +2909,8 @@
|
|||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1420;
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1530;
|
||||
ORGANIZATIONNAME = "The Transmission Project";
|
||||
TargetAttributes = {
|
||||
8D1107260486CEB800E47090 = {
|
||||
|
@ -3040,16 +3041,19 @@
|
|||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"update-version-h.sh",
|
||||
CMakeLists.txt,
|
||||
);
|
||||
name = "Generate version file";
|
||||
outputPaths = (
|
||||
"$(SRCROOT)/libtransmission/version.h",
|
||||
"$(SRCROOT)/libtransmission/version.h.new",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "sh update-version-h.sh\n";
|
||||
};
|
||||
A2305097100C0293003FDB0C /* ShellScript */ = {
|
||||
A2305097100C0293003FDB0C /* updateminiupnpcstrings */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
|
@ -3059,6 +3063,7 @@
|
|||
"third-party/miniupnp/miniupnpc/miniupnpcstrings.h.in",
|
||||
"third-party/miniupnp/miniupnpc/updateminiupnpcstrings.sh",
|
||||
);
|
||||
name = updateminiupnpcstrings;
|
||||
outputPaths = (
|
||||
"third-party/miniupnp/miniupnpc/miniupnpcstrings.h",
|
||||
);
|
||||
|
@ -3077,26 +3082,27 @@
|
|||
);
|
||||
name = "Copy libevent headers";
|
||||
outputPaths = (
|
||||
"$(SRCROOT)/third-party/libevent/evconfig-private.h",
|
||||
"$(SRCROOT)/third-party/libevent/include/evconfig-private.h",
|
||||
"$(SRCROOT)/third-party/libevent/include/event2/event-config.h",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/bash;
|
||||
shellScript = "cd third-party/libevent/include/event2\n\nif [ ! -e event-config.h -a ! ../../../macosx-libevent-event-config.h -ef event-config.h ]; then\n ln -s ../../../macosx-libevent-event-config.h event-config.h;\nfi\n\nif [ ! -e ../evconfig-private.h -a ! ../../macosx-libevent-evconfig-private.h -ef ../evconfig-private.h ]; then\n ln -s ../../macosx-libevent-evconfig-private.h ../evconfig-private.h;\nfi\n";
|
||||
};
|
||||
C12F197C1E1AE55A0005E93F /* ShellScript */ = {
|
||||
C12F197C1E1AE55A0005E93F /* symlinks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = symlinks;
|
||||
outputPaths = (
|
||||
"third-party/miniupnpc/miniupnp",
|
||||
"third-party/miniupnp/miniupnpc/include/miniupnpc",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "cd third-party/miniupnp && rm -f miniupnp && ln -s . miniupnp\n";
|
||||
shellScript = "cd third-party/miniupnp/miniupnpc\nln -snf . include/miniupnpc\n";
|
||||
};
|
||||
C12F197E1E1AE6D50005E93F /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
@ -3952,7 +3958,6 @@
|
|||
0053D3D30C86774200545606 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_UNREACHABLE_CODE = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_UNUSED_FUNCTION = NO;
|
||||
|
@ -3967,6 +3972,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -4005,7 +4011,7 @@
|
|||
"third-party/libnatpmp/*.h",
|
||||
"third-party/libpsl/include",
|
||||
"third-party/libutp/include",
|
||||
"third-party/miniupnpc/*.h",
|
||||
"third-party/miniupnp/miniupnpc/include",
|
||||
"third-party/utfcpp/source",
|
||||
"third-party/wide-integer",
|
||||
"third-party/wildmat",
|
||||
|
@ -4022,9 +4028,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = macosx/Transmission.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = macosx;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
|
@ -4051,8 +4055,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4071,8 +4073,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4095,8 +4095,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4143,6 +4141,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_DYNAMIC_NO_PIC = YES;
|
||||
GCC_ENABLE_PASCAL_STRINGS = NO;
|
||||
|
@ -4181,14 +4180,12 @@
|
|||
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.m0k.transmission;
|
||||
SDKROOT = macosx;
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
3C7A118E0D0B2EB800B5701F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
OTHER_CFLAGS = "-DENABLE_STRNATPMPERR";
|
||||
|
@ -4199,7 +4196,6 @@
|
|||
3C7A118F0D0B2EB800B5701F /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
OTHER_CFLAGS = "-DENABLE_STRNATPMPERR";
|
||||
|
@ -4210,7 +4206,6 @@
|
|||
3C7A11900D0B2EB800B5701F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
OTHER_CFLAGS = "-DENABLE_STRNATPMPERR";
|
||||
|
@ -4222,6 +4217,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -4260,7 +4256,7 @@
|
|||
"third-party/libnatpmp/*.h",
|
||||
"third-party/libpsl/include",
|
||||
"third-party/libutp/include",
|
||||
"third-party/miniupnpc/*.h",
|
||||
"third-party/miniupnp/miniupnpc/include",
|
||||
"third-party/utfcpp/source",
|
||||
"third-party/wide-integer",
|
||||
"third-party/wildmat",
|
||||
|
@ -4276,8 +4272,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4297,9 +4291,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = macosx/Transmission.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = macosx;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
|
@ -4353,6 +4345,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEPLOYMENT_POSTPROCESSING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_DYNAMIC_NO_PIC = YES;
|
||||
GCC_ENABLE_PASCAL_STRINGS = NO;
|
||||
|
@ -4397,7 +4390,6 @@
|
|||
A22CFCBB0FC24F720009BD3E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4408,7 +4400,6 @@
|
|||
A22CFCBC0FC24F720009BD3E /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4419,7 +4410,6 @@
|
|||
A22CFCBD0FC24F720009BD3E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4460,6 +4450,7 @@
|
|||
DEPLOYMENT_POSTPROCESSING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_DYNAMIC_NO_PIC = YES;
|
||||
GCC_ENABLE_PASCAL_STRINGS = NO;
|
||||
|
@ -4497,7 +4488,6 @@
|
|||
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.m0k.transmission;
|
||||
SDKROOT = macosx;
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
};
|
||||
name = "Release - Debug";
|
||||
};
|
||||
|
@ -4506,9 +4496,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = macosx/Transmission.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = macosx;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
|
@ -4535,8 +4523,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4555,6 +4541,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -4593,7 +4580,7 @@
|
|||
"third-party/libnatpmp/*.h",
|
||||
"third-party/libpsl/include",
|
||||
"third-party/libutp/include",
|
||||
"third-party/miniupnpc/*.h",
|
||||
"third-party/miniupnp/miniupnpc/include",
|
||||
"third-party/utfcpp/source",
|
||||
"third-party/wide-integer",
|
||||
"third-party/wildmat",
|
||||
|
@ -4609,8 +4596,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4633,8 +4618,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4652,7 +4635,6 @@
|
|||
A250CFF20CDA19680068B4B6 /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_UNREACHABLE_CODE = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_UNUSED_FUNCTION = NO;
|
||||
|
@ -4666,7 +4648,6 @@
|
|||
A2E384CF130DFB1D001F501B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = POSIX;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4677,7 +4658,6 @@
|
|||
A2E384D0130DFB1D001F501B /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = POSIX;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4688,7 +4668,6 @@
|
|||
A2E384D1130DFB1D001F501B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
POSIX,
|
||||
NS_BLOCK_ASSERTIONS,
|
||||
|
@ -4703,7 +4682,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
GCC_PREFIX_HEADER = "macosx/QuickLookPlugin/QuickLookPlugin-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -4731,7 +4709,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
GCC_PREFIX_HEADER = "macosx/QuickLookPlugin/QuickLookPlugin-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -4759,7 +4736,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
GCC_PREFIX_HEADER = "macosx/QuickLookPlugin/QuickLookPlugin-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -4786,7 +4762,6 @@
|
|||
BE1183490CE160960002D0F3 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = miniupnp;
|
||||
|
@ -4796,7 +4771,6 @@
|
|||
BE11834A0CE160960002D0F3 /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = miniupnp;
|
||||
|
@ -4806,7 +4780,6 @@
|
|||
BE11834B0CE160960002D0F3 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = miniupnp;
|
||||
|
@ -4816,7 +4789,6 @@
|
|||
BE75C34B0C729EB600DBEFE0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_UNREACHABLE_CODE = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GCC_WARN_UNUSED_FUNCTION = NO;
|
||||
|
@ -4831,8 +4803,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4855,8 +4825,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -4874,7 +4842,6 @@
|
|||
C1639A701A55F4D600E42033 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -4884,7 +4851,6 @@
|
|||
C1639A711A55F4D600E42033 /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -4894,7 +4860,6 @@
|
|||
C1639A721A55F4D600E42033 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -4904,7 +4869,6 @@
|
|||
C1846B9B294F7A3400A98F30 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = wildmat;
|
||||
|
@ -4914,7 +4878,6 @@
|
|||
C1846B9C294F7A3400A98F30 /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = wildmat;
|
||||
|
@ -4924,7 +4887,6 @@
|
|||
C1846B9D294F7A3400A98F30 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
PRODUCT_NAME = wildmat;
|
||||
|
@ -4934,7 +4896,6 @@
|
|||
C3CEBBA627949CA000683BE0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_UNREACHABLE_CODE = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4946,7 +4907,6 @@
|
|||
C3CEBBA727949CA000683BE0 /* Release - Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_UNREACHABLE_CODE = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -4958,7 +4918,6 @@
|
|||
C3CEBBA827949CA000683BE0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_UNREACHABLE_CODE = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
GENERATE_MASTER_OBJECT_FILE = YES;
|
||||
|
@ -5016,8 +4975,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5036,8 +4993,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5056,8 +5011,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5076,8 +5029,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5096,8 +5047,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5116,8 +5065,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5136,8 +5083,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5156,8 +5101,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
@ -5176,8 +5119,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
.,
|
||||
|
|
|
@ -167,8 +167,8 @@ void onTorrentFileDownloaded(tr_web::FetchResponse const& response)
|
|||
{
|
||||
if (c == 'g')
|
||||
{
|
||||
tr_optind = ind;
|
||||
return my_optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
43
cmake/CheckAtomic.cmake
Normal file
43
cmake/CheckAtomic.cmake
Normal file
|
@ -0,0 +1,43 @@
|
|||
# - Try to find if 64-bits atomics need -latomic linking
|
||||
# Once done this will define
|
||||
# HAVE_CXX_ATOMICS_WITHOUT_LIB - Whether atomic types work without -latomic
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckLibraryExists)
|
||||
|
||||
# Sometimes linking against libatomic is required for atomic ops, if
|
||||
# the platform doesn't support lock-free atomics.
|
||||
|
||||
function(check_working_cxx_atomics VARNAME)
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<long long> x;
|
||||
return std::atomic_is_lock_free(&x);
|
||||
}
|
||||
" ${VARNAME})
|
||||
endfunction()
|
||||
|
||||
# Check for atomic operations.
|
||||
if(MSVC)
|
||||
# This isn't necessary on MSVC.
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB TRUE)
|
||||
else()
|
||||
# First check if atomics work without the library.
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
endif()
|
||||
|
||||
# If not, check if the library exists, and atomics work with it.
|
||||
if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
|
||||
check_library_exists(atomic __atomic_load_8 "" HAVE_LIBATOMIC)
|
||||
if(NOT HAVE_LIBATOMIC)
|
||||
message(STATUS "Host compiler appears to require libatomic, but cannot locate it.")
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "atomic")
|
||||
if(NOT HAVE_CXX_ATOMICS_WITH_LIB)
|
||||
message(FATAL_ERROR "Host compiler must support std::atomic!")
|
||||
endif()
|
||||
endif()
|
|
@ -21,100 +21,6 @@ find_library(MINIUPNPC_LIBRARY
|
|||
libminiupnpc
|
||||
HINTS ${_MINIUPNPC_LIBDIR})
|
||||
|
||||
if(MINIUPNPC_INCLUDE_DIR)
|
||||
if(_MINIUPNPC_VERSION)
|
||||
set(MINIUPNPC_VERSION ${_MINIUPNPC_VERSION})
|
||||
else()
|
||||
file(STRINGS "${MINIUPNPC_INCLUDE_DIR}/miniupnpc/miniupnpc.h" MINIUPNPC_VERSION_STR
|
||||
REGEX "^#define[\t ]+MINIUPNPC_VERSION[\t ]+\"[^\"]+\"")
|
||||
if(MINIUPNPC_VERSION_STR MATCHES "\"([^\"]+)\"")
|
||||
set(MINIUPNPC_VERSION "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
|
||||
# Let's hope it's 1.7 or higher, since it provides
|
||||
# MINIUPNPC_API_VERSION and we won't have to figure
|
||||
# it out on our own
|
||||
file(STRINGS "${MINIUPNPC_INCLUDE_DIR}/miniupnpc/miniupnpc.h" MINIUPNPC_API_VERSION_STR
|
||||
REGEX "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+[0-9]+")
|
||||
if(MINIUPNPC_API_VERSION_STR MATCHES "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+([0-9]+)")
|
||||
set(MINIUPNPC_API_VERSION "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MINIUPNPC_LIBRARY)
|
||||
# Or maybe it's miniupnp 1.6
|
||||
if(NOT DEFINED MINIUPNPC_API_VERSION)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckMiniUPnPC_1.6.c
|
||||
"#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
int main()
|
||||
{
|
||||
struct UPNPDev * devlist;
|
||||
struct UPNPUrls urls;
|
||||
struct IGDdatas data;
|
||||
char lanaddr[16];
|
||||
char portStr[8];
|
||||
char intPort[8];
|
||||
char intClient[16];
|
||||
upnpDiscover( 2000, NULL, NULL, 0, 0, &errno );
|
||||
UPNP_GetValidIGD( devlist, &urls, &data, lanaddr, sizeof( lanaddr ) );
|
||||
UPNP_GetSpecificPortMappingEntry( urls.controlURL, data.first.servicetype,
|
||||
portStr, \"TCP\", intClient, intPort, NULL, NULL, NULL );
|
||||
return 0;
|
||||
}")
|
||||
try_compile(_MINIUPNPC_HAVE_VERSION_1_6
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckMiniUPnPC_1.6.c
|
||||
COMPILE_DEFINITIONS -DINCLUDE_DIRECTORIES=${MINIUPNPC_INCLUDE_DIR}
|
||||
LINK_LIBRARIES ${MINIUPNPC_LIBRARY}
|
||||
OUTPUT_VARIABLE OUTPUT)
|
||||
if(_MINIUPNPC_HAVE_VERSION_1_6)
|
||||
if(NOT DEFINED MINIUPNPC_VERSION)
|
||||
set(MINIUPNPC_VERSION 1.6)
|
||||
endif()
|
||||
set(MINIUPNPC_API_VERSION 8)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Or maybe it's miniupnp 1.5
|
||||
if(NOT DEFINED MINIUPNPC_API_VERSION)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckMiniUPnPC_1.5.c
|
||||
"#include <stdlib.h>
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
int main()
|
||||
{
|
||||
struct UPNPDev * devlist;
|
||||
struct UPNPUrls urls;
|
||||
struct IGDdatas data;
|
||||
char lanaddr[16];
|
||||
char portStr[8];
|
||||
char intPort[8];
|
||||
char intClient[16];
|
||||
upnpDiscover( 2000, NULL, NULL, 0 );
|
||||
UPNP_GetValidIGD( devlist, &urls, &data, lanaddr, sizeof( lanaddr ) );
|
||||
UPNP_GetSpecificPortMappingEntry( urls.controlURL, data.first.servicetype,
|
||||
portStr, \"TCP\", intClient, intPort );
|
||||
return 0;
|
||||
}")
|
||||
try_compile(_MINIUPNPC_HAVE_VERSION_1_5
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckMiniUPnPC_1.5.c
|
||||
COMPILE_DEFINITIONS -DINCLUDE_DIRECTORIES=${MlINIUPNPC_INCLUDE_DIR}
|
||||
LINK_LIBRARIES ${MINIUPNPC_LIBRARY}
|
||||
OUTPUT_VARIABLE OUTPUT)
|
||||
if(_MINIUPNPC_HAVE_VERSION_1_5)
|
||||
if(NOT DEFINED MINIUPNPC_VERSION)
|
||||
set(MINIUPNPC_VERSION 1.5)
|
||||
endif()
|
||||
set(MINIUPNPC_API_VERSION 5)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(MINIUPNPC_INCLUDE_DIRS ${MINIUPNPC_INCLUDE_DIR})
|
||||
set(MINIUPNPC_LIBRARIES ${MINIUPNPC_LIBRARY})
|
||||
|
||||
|
@ -123,9 +29,7 @@ include(FindPackageHandleStandardArgs)
|
|||
find_package_handle_standard_args(MINIUPNPC
|
||||
REQUIRED_VARS
|
||||
MINIUPNPC_LIBRARY
|
||||
MINIUPNPC_INCLUDE_DIR
|
||||
MINIUPNPC_API_VERSION
|
||||
VERSION_VAR MINIUPNPC_VERSION)
|
||||
MINIUPNPC_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(MINIUPNPC_INCLUDE_DIR MINIUPNPC_LIBRARY)
|
||||
|
||||
|
|
|
@ -65,6 +65,16 @@ if ! find_cfiles -exec "${clang_format_exe}" $clang_format_args '{}' '+'; then
|
|||
exitcode=1
|
||||
fi
|
||||
|
||||
# format Xcodeproj
|
||||
if ! grep -q 'objectVersion = 51' Transmission.xcodeproj/project.pbxproj; then
|
||||
echo 'project.pbxproj needs objectVersion = 51 for compatibility with Xcode 11'
|
||||
exitcode=1
|
||||
fi
|
||||
if ! grep -q 'BuildIndependentTargetsInParallel = YES' Transmission.xcodeproj/project.pbxproj; then
|
||||
echo 'please keep BuildIndependentTargetsInParallel in project.pbxproj'
|
||||
exitcode=1
|
||||
fi
|
||||
|
||||
# format JS
|
||||
# but only if js has changed
|
||||
git diff --cached --quiet -- "web/**" && exit $exitcode
|
||||
|
|
|
@ -193,7 +193,7 @@ auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view
|
|||
if (!tr_file_read(filename, content, &error))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", basename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -218,7 +218,7 @@ auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view
|
|||
|
||||
if (tr_torrentNew(ctor, nullptr) == nullptr)
|
||||
{
|
||||
tr_logAddError(fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", basename)));
|
||||
tr_logAddError(fmt::format(fmt::runtime(_("Couldn't add torrent file '{path}'")), fmt::arg("path", basename)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -227,12 +227,12 @@ auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view
|
|||
|
||||
if (test && trash)
|
||||
{
|
||||
tr_logAddInfo(fmt::format(_("Removing torrent file '{path}'"), fmt::arg("path", basename)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Removing torrent file '{path}'")), fmt::arg("path", basename)));
|
||||
|
||||
if (auto error = tr_error{}; !tr_sys_path_remove(filename, &error))
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't remove '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't remove '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", basename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -620,7 +620,8 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
}
|
||||
else
|
||||
{
|
||||
std::cerr << fmt::format(_("Couldn't parse log level '{level}'"), fmt::arg("level", optstr)) << std::endl;
|
||||
std::cerr << fmt::format(fmt::runtime(_("Couldn't parse log level '{level}'")), fmt::arg("level", optstr))
|
||||
<< std::endl;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -681,7 +682,7 @@ void tr_daemon::reconfigure()
|
|||
}
|
||||
|
||||
configDir = tr_sessionGetConfigDir(my_session_);
|
||||
tr_logAddInfo(fmt::format(_("Reloading settings from '{path}'"), fmt::arg("path", configDir)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Reloading settings from '{path}'")), fmt::arg("path", configDir)));
|
||||
|
||||
tr_sessionSet(my_session_, load_settings(configDir));
|
||||
tr_sessionReloadBlocklists(my_session_);
|
||||
|
@ -705,7 +706,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
auto const errmsg = fmt::format(
|
||||
_("Couldn't initialize daemon: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't initialize daemon: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code));
|
||||
printMessage(log_stream_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
|
||||
|
@ -717,7 +718,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
auto const* const cdir = this->config_dir_.c_str();
|
||||
auto* session = tr_sessionInit(cdir, true, settings_);
|
||||
tr_sessionSetRPCCallback(session, on_rpc_callback, this);
|
||||
tr_logAddInfo(fmt::format(_("Loading settings from '{path}'"), fmt::arg("path", cdir)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Loading settings from '{path}'")), fmt::arg("path", cdir)));
|
||||
tr_sessionSaveSettings(session, cdir, settings_);
|
||||
|
||||
auto sv = std::string_view{};
|
||||
|
@ -738,13 +739,13 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
auto const out = std::to_string(getpid());
|
||||
tr_sys_file_write(fp, std::data(out), std::size(out), nullptr);
|
||||
tr_sys_file_close(fp);
|
||||
tr_logAddInfo(fmt::format(_("Saved pidfile '{path}'"), fmt::arg("path", sz_pid_filename)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Saved pidfile '{path}'")), fmt::arg("path", sz_pid_filename)));
|
||||
pidfile_created = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", sz_pid_filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -775,7 +776,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
(void)tr_variantDictFindStrView(&settings_, TR_KEY_watch_dir, &dir);
|
||||
if (!std::empty(dir))
|
||||
{
|
||||
tr_logAddInfo(fmt::format(_("Watching '{path}' for new torrent files"), fmt::arg("path", dir)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Watching '{path}' for new torrent files")), fmt::arg("path", dir)));
|
||||
|
||||
auto handler = [session](std::string_view dirname, std::string_view basename)
|
||||
{
|
||||
|
@ -820,7 +821,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't create event: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't create event: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
goto CLEANUP;
|
||||
|
@ -830,7 +831,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't add event: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't add event: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
goto CLEANUP;
|
||||
|
@ -844,7 +845,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't launch daemon event loop: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't launch daemon event loop: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
goto CLEANUP;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
Description=Transmission BitTorrent Daemon
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Documentation=man:transmission-daemon(1)
|
||||
|
||||
[Service]
|
||||
User=transmission
|
||||
|
|
|
@ -36,6 +36,16 @@ cmake --build build -t transmission-gtk
|
|||
./build/gtk/transmission-gtk
|
||||
```
|
||||
|
||||
### Building the QT app with CMake ###
|
||||
Install QT and build the app:
|
||||
```bash
|
||||
brew install qt
|
||||
brew services start dbus
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_QT=ON -DENABLE_MAC=OFF
|
||||
cmake --build build -t transmission-qt
|
||||
./build/qt/transmission-qt
|
||||
```
|
||||
|
||||
## On Unix ##
|
||||
### Prerequisites ###
|
||||
|
||||
|
|
|
@ -98,8 +98,8 @@ Here is a sample of the three basic types: respectively Boolean, Number and Stri
|
|||
* **bind-address-ipv4:** String (default = "") Where to listen for peer connections. When no valid IPv4 address is provided, Transmission will bind to "0.0.0.0".
|
||||
* **bind-address-ipv6:** String (default = "") Where to listen for peer connections. When no valid IPv6 address is provided, Transmission will try to bind to your default global IPv6 address. If that didn't work, then Transmission will bind to "::".
|
||||
* **peer-congestion-algorithm:** String. This is documented on https://www.pps.jussieu.fr/~jch/software/bittorrent/tcp-congestion-control.html.
|
||||
* **peer-limit-global:** Number (default = 240)
|
||||
* **peer-limit-per-torrent:** Number (default = 60)
|
||||
* **peer-limit-global:** Number (default = 200)
|
||||
* **peer-limit-per-torrent:** Number (default = 50)
|
||||
* **peer-socket-tos:** String (default = "default") Set the [Type-Of-Service (TOS)](https://en.wikipedia.org/wiki/Type_of_Service) parameter for outgoing TCP packets. Possible values are "default", "lowcost", "throughput", "lowdelay" and "reliability". The value "lowcost" is recommended if you're using a smart router, and shouldn't harm in any case.
|
||||
|
||||
#### Peer Port
|
||||
|
|
|
@ -46,4 +46,17 @@ Scripts which have not yet been ported and may not work with the latest version:
|
|||
* https://github.com/jaboto/Transmission-script - (cron)script set network limits according to the number of clients in the network
|
||||
|
||||
## Security with systemd
|
||||
`transmission-daemon`'s packaging has many permissions disabled as a standard safety measure. If your script needs more permissions than are provided by the default, users have [reported](https://github.com/transmission/transmission/issues/1951) that it can be resolved by changing to `NoNewPrivileges=false` in `/lib/systemd/system/transmission-daemon.service`.
|
||||
`transmission-daemon`'s packaging has many permissions disabled as a standard safety measure. If your script needs more permissions than are provided by the default, users have [reported](https://github.com/transmission/transmission/issues/1951) that it can be resolved by changing to `NoNewPrivileges=false` using a systemd unit override.
|
||||
|
||||
```
|
||||
$ sudo systemctl edit transmission-daemon.service
|
||||
```
|
||||
|
||||
and add the following content to the override:
|
||||
|
||||
```
|
||||
[Service]
|
||||
NoNewPrivileges=false
|
||||
```
|
||||
|
||||
and that override will be kept untouched by package upgrades.
|
||||
|
|
|
@ -395,7 +395,7 @@ void register_magnet_link_handler()
|
|||
catch (Gio::Error const& e)
|
||||
{
|
||||
gtr_warning(fmt::format(
|
||||
_("Couldn't register Transmission as a {content_type} handler: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't register Transmission as a {content_type} handler: {error} ({error_code})")),
|
||||
fmt::arg("content_type", content_type),
|
||||
fmt::arg("error", e.what()),
|
||||
fmt::arg("error_code", static_cast<int>(e.code()))));
|
||||
|
|
|
@ -715,15 +715,18 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
}
|
||||
else if (!empty_creator && !empty_date)
|
||||
{
|
||||
str = fmt::format(_("Created by {creator} on {date}"), fmt::arg("creator", creator), fmt::arg("date", datestr));
|
||||
str = fmt::format(
|
||||
fmt::runtime(_("Created by {creator} on {date}")),
|
||||
fmt::arg("creator", creator),
|
||||
fmt::arg("date", datestr));
|
||||
}
|
||||
else if (!empty_creator)
|
||||
{
|
||||
str = fmt::format(_("Created by {creator}"), fmt::arg("creator", creator));
|
||||
str = fmt::format(fmt::runtime(_("Created by {creator}")), fmt::arg("creator", creator));
|
||||
}
|
||||
else if (!empty_date)
|
||||
{
|
||||
str = fmt::format(_("Created on {date}"), fmt::arg("date", datestr));
|
||||
str = fmt::format(fmt::runtime(_("Created on {date}")), fmt::arg("date", datestr));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -874,7 +877,8 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
[](auto sum, auto const* tor) { return sum + tr_torrentFileCount(tor); });
|
||||
|
||||
str = fmt::format(
|
||||
ngettext("{total_size} in {file_count:L} file", "{total_size} in {file_count:L} files", file_count),
|
||||
fmt::runtime(
|
||||
ngettext("{total_size} in {file_count:L} file", "{total_size} in {file_count:L} files", file_count)),
|
||||
fmt::arg("total_size", tr_strlsize(total_size)),
|
||||
fmt::arg("file_count", file_count));
|
||||
|
||||
|
@ -888,10 +892,10 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
{
|
||||
str += ' ';
|
||||
str += fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"({piece_count} BitTorrent piece @ {piece_size})",
|
||||
"({piece_count} BitTorrent pieces @ {piece_size})",
|
||||
piece_count),
|
||||
piece_count)),
|
||||
fmt::arg("piece_count", piece_count),
|
||||
fmt::arg("piece_size", Memory{ piece_size, Memory::Units::Bytes }.to_string()));
|
||||
}
|
||||
|
@ -933,7 +937,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
if (haveUnchecked == 0 && leftUntilDone == 0)
|
||||
{
|
||||
str = fmt::format(
|
||||
_("{current_size} ({percent_done}%)"),
|
||||
fmt::runtime(_("{current_size} ({percent_done}%)")),
|
||||
fmt::arg("current_size", total),
|
||||
fmt::arg("percent_done", buf2));
|
||||
}
|
||||
|
@ -941,7 +945,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
{
|
||||
str = fmt::format(
|
||||
// xgettext:no-c-format
|
||||
_("{current_size} ({percent_done}% of {percent_available}% available)"),
|
||||
fmt::runtime(_("{current_size} ({percent_done}% of {percent_available}% available)")),
|
||||
fmt::arg("current_size", total),
|
||||
fmt::arg("percent_done", buf2),
|
||||
fmt::arg("percent_available", avail));
|
||||
|
@ -950,7 +954,8 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
{
|
||||
str = fmt::format(
|
||||
// xgettext:no-c-format
|
||||
_("{current_size} ({percent_done}% of {percent_available}% available; {unverified_size} unverified)"),
|
||||
fmt::runtime(
|
||||
_("{current_size} ({percent_done}% of {percent_available}% available; {unverified_size} unverified)")),
|
||||
fmt::arg("current_size", total),
|
||||
fmt::arg("percent_done", buf2),
|
||||
fmt::arg("percent_available", avail),
|
||||
|
@ -983,7 +988,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
if (failed != 0)
|
||||
{
|
||||
str = fmt::format(
|
||||
_("{downloaded_size} (+{discarded_size} discarded after failed checksum)"),
|
||||
fmt::runtime(_("{downloaded_size} (+{discarded_size} discarded after failed checksum)")),
|
||||
fmt::arg("downloaded_size", downloaded_str),
|
||||
fmt::arg("discarded_size", tr_strlsize(failed)));
|
||||
}
|
||||
|
@ -1013,7 +1018,7 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
|
|||
uint64_t{},
|
||||
[](auto sum, auto const* st) { return sum + st->sizeWhenDone; });
|
||||
str = fmt::format(
|
||||
_("{uploaded_size} (Ratio: {ratio})"),
|
||||
fmt::runtime(_("{uploaded_size} (Ratio: {ratio})")),
|
||||
fmt::arg("uploaded_size", tr_strlsize(uploaded)),
|
||||
fmt::arg("ratio", tr_strlratio(tr_getRatio(uploaded, denominator))));
|
||||
}
|
||||
|
@ -1809,10 +1814,10 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
|||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the peer text
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"Got a list of {markup_begin}{peer_count} peer{markup_end} {time_span_ago}",
|
||||
"Got a list of {markup_begin}{peer_count} peers{markup_end} {time_span_ago}",
|
||||
tracker.lastAnnouncePeerCount),
|
||||
tracker.lastAnnouncePeerCount)),
|
||||
fmt::arg("markup_begin", SuccessMarkupBegin),
|
||||
fmt::arg("peer_count", tracker.lastAnnouncePeerCount),
|
||||
fmt::arg("markup_end", SuccessMarkupEnd),
|
||||
|
@ -1822,7 +1827,7 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
|||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the time_span
|
||||
_("Peer list request {markup_begin}timed out {time_span_ago}{markup_end}; will retry"),
|
||||
fmt::runtime(_("Peer list request {markup_begin}timed out {time_span_ago}{markup_end}; will retry")),
|
||||
fmt::arg("markup_begin", TimeoutMarkupBegin),
|
||||
fmt::arg("time_span_ago", time_span_ago),
|
||||
fmt::arg("markup_end", TimeoutMarkupEnd));
|
||||
|
@ -1831,7 +1836,7 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
|||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the error
|
||||
_("Got an error '{markup_begin}{error}{markup_end}' {time_span_ago}"),
|
||||
fmt::runtime(_("Got an error '{markup_begin}{error}{markup_end}' {time_span_ago}")),
|
||||
fmt::arg("markup_begin", ErrMarkupBegin),
|
||||
fmt::arg("error", Glib::Markup::escape_text(std::data(tracker.lastAnnounceResult))),
|
||||
fmt::arg("markup_end", ErrMarkupEnd),
|
||||
|
@ -1851,7 +1856,7 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
|||
gstr << '\n';
|
||||
gstr << dir_mark;
|
||||
gstr << fmt::format(
|
||||
_("Asking for more peers {time_span_from_now}"),
|
||||
fmt::runtime(_("Asking for more peers {time_span_from_now}")),
|
||||
fmt::arg("time_span_from_now", tr_format_time_relative(now, tracker.nextAnnounceTime)));
|
||||
break;
|
||||
|
||||
|
@ -1866,7 +1871,7 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
|
|||
gstr << dir_mark;
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround time_span_ago
|
||||
_("Asked for more peers {markup_begin}{time_span_ago}{markup_end}"),
|
||||
fmt::runtime(_("Asked for more peers {markup_begin}{time_span_ago}{markup_end}")),
|
||||
fmt::arg("markup_begin", "<small>"),
|
||||
fmt::arg("time_span_ago", tr_format_time_relative(now, tracker.lastAnnounceStartTime)),
|
||||
fmt::arg("markup_end", "</small>"));
|
||||
|
@ -1891,7 +1896,8 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
|||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the seeder/leecher text
|
||||
_("Tracker had {markup_begin}{seeder_count} {seeder_or_seeders} and {leecher_count} {leecher_or_leechers}{markup_end} {time_span_ago}"),
|
||||
fmt::runtime(_(
|
||||
"Tracker had {markup_begin}{seeder_count} {seeder_or_seeders} and {leecher_count} {leecher_or_leechers}{markup_end} {time_span_ago}")),
|
||||
fmt::arg("seeder_count", tracker.seederCount),
|
||||
fmt::arg("seeder_or_seeders", ngettext("seeder", "seeders", tracker.seederCount)),
|
||||
fmt::arg("leecher_count", tracker.leecherCount),
|
||||
|
@ -1904,7 +1910,7 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
|||
{
|
||||
gstr << fmt::format(
|
||||
// {markup_begin} and {markup_end} should surround the error text
|
||||
_("Got a scrape error '{markup_begin}{error}{markup_end}' {time_span_ago}"),
|
||||
fmt::runtime(_("Got a scrape error '{markup_begin}{error}{markup_end}' {time_span_ago}")),
|
||||
fmt::arg("error", Glib::Markup::escape_text(std::data(tracker.lastScrapeResult))),
|
||||
fmt::arg("time_span_ago", time_span_ago),
|
||||
fmt::arg("markup_begin", ErrMarkupBegin),
|
||||
|
@ -1921,7 +1927,7 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
|||
gstr << '\n';
|
||||
gstr << dir_mark;
|
||||
gstr << fmt::format(
|
||||
_("Asking for peer counts in {time_span_from_now}"),
|
||||
fmt::runtime(_("Asking for peer counts in {time_span_from_now}")),
|
||||
fmt::arg("time_span_from_now", tr_format_time_relative(now, tracker.nextScrapeTime)));
|
||||
break;
|
||||
|
||||
|
@ -1935,7 +1941,7 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
|
|||
gstr << '\n';
|
||||
gstr << dir_mark;
|
||||
gstr << fmt::format(
|
||||
_("Asked for peer counts {markup_begin}{time_span_ago}{markup_end}"),
|
||||
fmt::runtime(_("Asked for peer counts {markup_begin}{time_span_ago}{markup_end}")),
|
||||
fmt::arg("markup_begin", "<small>"),
|
||||
fmt::arg("time_span_ago", tr_format_time_relative(now, tracker.lastScrapeStartTime)),
|
||||
fmt::arg("markup_end", "</small>"));
|
||||
|
@ -2218,7 +2224,8 @@ EditTrackersDialog::EditTrackersDialog(
|
|||
, torrent_id_(tr_torrentId(torrent))
|
||||
, urls_view_(gtr_get_widget<Gtk::TextView>(builder, "urls_view"))
|
||||
{
|
||||
set_title(fmt::format(_("{torrent_name} - Edit Trackers"), fmt::arg("torrent_name", tr_torrentName(torrent))));
|
||||
set_title(
|
||||
fmt::format(fmt::runtime(_("{torrent_name} - Edit Trackers")), fmt::arg("torrent_name", tr_torrentName(torrent))));
|
||||
set_transient_for(parent);
|
||||
|
||||
urls_view_->get_buffer()->set_text(tr_torrentGetTrackerList(torrent));
|
||||
|
@ -2337,7 +2344,7 @@ AddTrackerDialog::AddTrackerDialog(
|
|||
, torrent_id_(tr_torrentId(torrent))
|
||||
, url_entry_(gtr_get_widget<Gtk::Entry>(builder, "url_entry"))
|
||||
{
|
||||
set_title(fmt::format(_("{torrent_name} - Add Tracker"), fmt::arg("torrent_name", tr_torrentName(torrent))));
|
||||
set_title(fmt::format(fmt::runtime(_("{torrent_name} - Add Tracker")), fmt::arg("torrent_name", tr_torrentName(torrent))));
|
||||
set_transient_for(parent);
|
||||
|
||||
gtr_paste_clipboard_url_into_entry(*url_entry_);
|
||||
|
@ -2625,12 +2632,12 @@ void DetailsDialog::Impl::set_torrents(std::vector<tr_torrent_id_t> const& ids)
|
|||
{
|
||||
int const id = ids.front();
|
||||
auto const* tor = core_->find_torrent(id);
|
||||
title = fmt::format(_("{torrent_name} Properties"), fmt::arg("torrent_name", tr_torrentName(tor)));
|
||||
title = fmt::format(fmt::runtime(_("{torrent_name} Properties")), fmt::arg("torrent_name", tr_torrentName(tor)));
|
||||
}
|
||||
else
|
||||
{
|
||||
title = fmt::format(
|
||||
ngettext("Properties - {torrent_count:L} Torrent", "Properties - {torrent_count:L} Torrents", len),
|
||||
fmt::runtime(ngettext("Properties - {torrent_count:L} Torrent", "Properties - {torrent_count:L} Torrents", len)),
|
||||
fmt::arg("torrent_count", len));
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,12 @@ void gtr_confirm_remove(
|
|||
}
|
||||
|
||||
auto const primary_text = fmt::format(
|
||||
!delete_files ?
|
||||
ngettext("Remove torrent?", "Remove {count:L} torrents?", count) :
|
||||
ngettext("Delete this torrent's downloaded files?", "Delete these {count:L} torrents' downloaded files?", count),
|
||||
fmt::runtime(
|
||||
!delete_files ? ngettext("Remove torrent?", "Remove {count:L} torrents?", count) :
|
||||
ngettext(
|
||||
"Delete this torrent's downloaded files?",
|
||||
"Delete these {count:L} torrents' downloaded files?",
|
||||
count)),
|
||||
fmt::arg("count", count));
|
||||
|
||||
Glib::ustring secondary_text;
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
@ -853,7 +852,7 @@ void FileList::Impl::on_rename_done_idle(Glib::ustring const& path_string, Glib:
|
|||
auto w = std::make_shared<Gtk::MessageDialog>(
|
||||
gtr_widget_get_window(widget_),
|
||||
fmt::format(
|
||||
_("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})")),
|
||||
fmt::arg("old_path", path_string),
|
||||
fmt::arg("path", newname),
|
||||
fmt::arg("error", tr_strerror(error)),
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
|
||||
#include <algorithm> // std::transform()
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -583,7 +583,7 @@ bool FilterBar::Impl::update_count_label()
|
|||
/* set the text */
|
||||
if (auto const new_markup = visibleCount == std::min(activityCount, trackerCount) ?
|
||||
_("_Show:") :
|
||||
fmt::format(_("_Show {count:L} of:"), fmt::arg("count", visibleCount));
|
||||
fmt::format(fmt::runtime(_("_Show {count:L} of:")), fmt::arg("count", visibleCount));
|
||||
new_markup != show_lb_->get_label().raw())
|
||||
{
|
||||
show_lb_->set_markup_with_mnemonic(new_markup);
|
||||
|
|
|
@ -51,8 +51,9 @@ bool FreeSpaceLabel::Impl::on_freespace_timer()
|
|||
}
|
||||
|
||||
auto const capacity = tr_sys_path_get_capacity(dir_);
|
||||
auto const text = capacity ? fmt::format(_("{disk_space} free"), fmt::arg("disk_space", tr_strlsize(capacity->free))) :
|
||||
_("Error");
|
||||
auto const text = capacity ?
|
||||
fmt::format(fmt::runtime(_("{disk_space} free")), fmt::arg("disk_space", tr_strlsize(capacity->free))) :
|
||||
_("Error");
|
||||
label_.set_markup(fmt::format("<i>{:s}</i>", text));
|
||||
|
||||
return true;
|
||||
|
|
|
@ -378,8 +378,9 @@ void MainWindow::Impl::syncAltSpeedButton()
|
|||
bool const b = gtr_pref_flag_get(TR_KEY_alt_speed_enabled);
|
||||
alt_speed_button_->set_active(b);
|
||||
alt_speed_button_->set_tooltip_text(fmt::format(
|
||||
b ? _("Click to disable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)") :
|
||||
_("Click to enable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)"),
|
||||
fmt::runtime(
|
||||
b ? _("Click to disable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)") :
|
||||
_("Click to enable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)")),
|
||||
fmt::arg("download_speed", Speed{ gtr_pref_int_get(TR_KEY_alt_speed_down), Speed::Units::KByps }.to_string()),
|
||||
fmt::arg("upload_speed", Speed{ gtr_pref_int_get(TR_KEY_alt_speed_up), Speed::Units::KByps }.to_string())));
|
||||
}
|
||||
|
@ -577,7 +578,9 @@ void MainWindow::Impl::onOptionsClicked()
|
|||
|
||||
update_menu(
|
||||
ratio_menu_info_,
|
||||
fmt::format(_("Stop at Ratio ({ratio})"), fmt::arg("ratio", tr_strlratio(gtr_pref_double_get(TR_KEY_ratio_limit)))),
|
||||
fmt::format(
|
||||
fmt::runtime(_("Stop at Ratio ({ratio})")),
|
||||
fmt::arg("ratio", tr_strlratio(gtr_pref_double_get(TR_KEY_ratio_limit)))),
|
||||
TR_KEY_ratio_limit_enabled);
|
||||
}
|
||||
|
||||
|
@ -758,13 +761,13 @@ void MainWindow::Impl::updateStats()
|
|||
if (auto const pch = gtr_pref_string_get(TR_KEY_statusbar_stats); pch == "session-ratio")
|
||||
{
|
||||
auto const stats = tr_sessionGetStats(session);
|
||||
buf = fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(stats.ratio)));
|
||||
buf = fmt::format(fmt::runtime(_("Ratio: {ratio}")), fmt::arg("ratio", tr_strlratio(stats.ratio)));
|
||||
}
|
||||
else if (pch == "session-transfer")
|
||||
{
|
||||
auto const stats = tr_sessionGetStats(session);
|
||||
buf = fmt::format(
|
||||
C_("current session totals", "Down: {downloaded_size}, Up: {uploaded_size}"),
|
||||
fmt::runtime(C_("current session totals", "Down: {downloaded_size}, Up: {uploaded_size}")),
|
||||
fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
|
||||
fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes)));
|
||||
}
|
||||
|
@ -772,14 +775,14 @@ void MainWindow::Impl::updateStats()
|
|||
{
|
||||
auto const stats = tr_sessionGetCumulativeStats(session);
|
||||
buf = fmt::format(
|
||||
C_("all-time totals", "Down: {downloaded_size}, Up: {uploaded_size}"),
|
||||
fmt::runtime(C_("all-time totals", "Down: {downloaded_size}, Up: {uploaded_size}")),
|
||||
fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
|
||||
fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes)));
|
||||
}
|
||||
else /* default is total-ratio */
|
||||
{
|
||||
auto const stats = tr_sessionGetCumulativeStats(session);
|
||||
buf = fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(stats.ratio)));
|
||||
buf = fmt::format(fmt::runtime(_("Ratio: {ratio}")), fmt::arg("ratio", tr_strlratio(stats.ratio)));
|
||||
}
|
||||
|
||||
stats_lb_->set_text(buf);
|
||||
|
|
|
@ -187,7 +187,7 @@ bool MakeProgressDialog::onProgressDialogRefresh()
|
|||
auto const base = Glib::path_get_basename(builder_.top());
|
||||
if (!is_done)
|
||||
{
|
||||
str = fmt::format(_("Creating '{path}'"), fmt::arg("path", base));
|
||||
str = fmt::format(fmt::runtime(_("Creating '{path}'")), fmt::arg("path", base));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -200,13 +200,13 @@ bool MakeProgressDialog::onProgressDialogRefresh()
|
|||
|
||||
if (!error)
|
||||
{
|
||||
str = fmt::format(_("Created '{path}'"), fmt::arg("path", base));
|
||||
str = fmt::format(fmt::runtime(_("Created '{path}'")), fmt::arg("path", base));
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
str = fmt::format(
|
||||
_("Couldn't create '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't create '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", base),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code()));
|
||||
|
@ -224,7 +224,7 @@ bool MakeProgressDialog::onProgressDialogRefresh()
|
|||
{
|
||||
/* how much data we've scanned through to generate checksums */
|
||||
str = fmt::format(
|
||||
_("Scanned {file_size}"),
|
||||
fmt::runtime(_("Scanned {file_size}")),
|
||||
fmt::arg("file_size", tr_strlsize(static_cast<uint64_t>(piece_index) * builder_.piece_size())));
|
||||
}
|
||||
|
||||
|
@ -385,15 +385,18 @@ void MakeDialog::Impl::updatePiecesLabel()
|
|||
else
|
||||
{
|
||||
gstr += fmt::format(
|
||||
ngettext("{total_size} in {file_count:L} file", "{total_size} in {file_count:L} files", builder_->file_count()),
|
||||
fmt::runtime(ngettext(
|
||||
"{total_size} in {file_count:L} file",
|
||||
"{total_size} in {file_count:L} files",
|
||||
builder_->file_count())),
|
||||
fmt::arg("total_size", tr_strlsize(builder_->total_size())),
|
||||
fmt::arg("file_count", builder_->file_count()));
|
||||
gstr += ' ';
|
||||
gstr += fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"({piece_count} BitTorrent piece @ {piece_size})",
|
||||
"({piece_count} BitTorrent pieces @ {piece_size})",
|
||||
builder_->piece_count()),
|
||||
builder_->piece_count())),
|
||||
fmt::arg("piece_count", builder_->piece_count()),
|
||||
fmt::arg("piece_size", Memory{ builder_->piece_size(), Memory::Units::Bytes }.to_string()));
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@ void MessageLogWindow::Impl::doSave(std::string const& filename)
|
|||
auto w = std::make_shared<Gtk::MessageDialog>(
|
||||
window_,
|
||||
fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", Glib::filename_to_utf8(filename)),
|
||||
fmt::arg("error", e.code().message()),
|
||||
fmt::arg("error_code", e.code().value())),
|
||||
|
|
|
@ -145,7 +145,7 @@ void dbus_proxy_ready_callback(Glib::RefPtr<Gio::AsyncResult>& res)
|
|||
catch (Glib::Error const& e)
|
||||
{
|
||||
gtr_warning(fmt::format(
|
||||
_("Couldn't create proxy for '{bus}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't create proxy for '{bus}': {error} ({error_code})")),
|
||||
fmt::arg("bus", NotificationsDbusName),
|
||||
fmt::arg("error", TR_GLIB_EXCEPTION_WHAT(e)),
|
||||
fmt::arg("error_code", e.code())));
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
template<typename T, typename... ArgTs>
|
||||
static void localize_label(T& widget, ArgTs&&... args)
|
||||
{
|
||||
widget.set_label(fmt::format(widget.get_label().raw(), std::forward<ArgTs>(args)...));
|
||||
widget.set_label(fmt::format(fmt::runtime(widget.get_label().raw()), std::forward<ArgTs>(args)...));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -552,7 +552,7 @@ void PrivacyPage::updateBlocklistText()
|
|||
{
|
||||
int const n = tr_blocklistGetRuleCount(core_->get_session());
|
||||
auto const msg = fmt::format(
|
||||
ngettext("Blocklist has {count:L} entry", "Blocklist has {count:L} entries", n),
|
||||
fmt::runtime(ngettext("Blocklist has {count:L} entry", "Blocklist has {count:L} entries", n)),
|
||||
fmt::arg("count", n));
|
||||
label_->set_text(msg);
|
||||
}
|
||||
|
@ -954,9 +954,9 @@ void NetworkPage::updatePortStatusText()
|
|||
|
||||
portLabel_->set_markup(
|
||||
portTestStatus_[Session::PORT_TEST_IPV4] == portTestStatus_[Session::PORT_TEST_IPV6] ?
|
||||
fmt::format(_("Status: <b>{status}</b>"), fmt::arg("status", status_ipv4)) :
|
||||
fmt::format(fmt::runtime(_("Status: <b>{status}</b>")), fmt::arg("status", status_ipv4)) :
|
||||
fmt::format(
|
||||
_("Status: <b>{status_ipv4}</b> (IPv4), <b>{status_ipv6}</b> (IPv6)"),
|
||||
fmt::runtime(_("Status: <b>{status_ipv4}</b> (IPv4), <b>{status_ipv6}</b> (IPv6)")),
|
||||
fmt::arg("status_ipv4", status_ipv4),
|
||||
fmt::arg("status_ipv6", status_ipv6)));
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ void RelocateDialog::Impl::startMovingNextTorrent()
|
|||
torrent_ids_.pop_back();
|
||||
|
||||
message_dialog_->set_message(
|
||||
fmt::format(_("Moving '{torrent_name}'"), fmt::arg("torrent_name", tr_torrentName(tor))),
|
||||
fmt::format(fmt::runtime(_("Moving '{torrent_name}'")), fmt::arg("torrent_name", tr_torrentName(tor))),
|
||||
true);
|
||||
}
|
||||
|
||||
|
|
|
@ -313,7 +313,7 @@ void rename_torrent(Glib::RefPtr<Gio::File> const& file)
|
|||
catch (Glib::Error const& e)
|
||||
{
|
||||
gtr_message(fmt::format(
|
||||
_("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})")),
|
||||
fmt::arg("old_path", old_name),
|
||||
fmt::arg("path", new_name),
|
||||
fmt::arg("error", e.what()),
|
||||
|
@ -781,7 +781,7 @@ void Session::Impl::add_file_async_callback(
|
|||
|
||||
if (!file->load_contents_finish(result, contents, length))
|
||||
{
|
||||
gtr_message(fmt::format(_("Couldn't read '{path}'"), fmt::arg("path", file->get_parse_name())));
|
||||
gtr_message(fmt::format(fmt::runtime(_("Couldn't read '{path}'")), fmt::arg("path", file->get_parse_name())));
|
||||
}
|
||||
else if (tr_ctorSetMetainfo(ctor, contents, length, nullptr))
|
||||
{
|
||||
|
@ -795,7 +795,7 @@ void Session::Impl::add_file_async_callback(
|
|||
catch (Glib::Error const& e)
|
||||
{
|
||||
gtr_message(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", file->get_parse_name()),
|
||||
fmt::arg("error", e.what()),
|
||||
fmt::arg("error_code", e.code())));
|
||||
|
@ -846,7 +846,10 @@ bool Session::Impl::add_file(Glib::RefPtr<Gio::File> const& file, bool do_start,
|
|||
else
|
||||
{
|
||||
tr_ctorFree(ctor);
|
||||
std::cerr << fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", file->get_parse_name())) << '\n';
|
||||
std::cerr << fmt::format(
|
||||
fmt::runtime(_("Couldn't add torrent file '{path}'")),
|
||||
fmt::arg("path", file->get_parse_name()))
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
return handled;
|
||||
|
@ -1044,7 +1047,8 @@ bool gtr_inhibit_hibernation(guint32& cookie)
|
|||
}
|
||||
catch (Glib::Error const& e)
|
||||
{
|
||||
tr_logAddError(fmt::format(_("Couldn't inhibit desktop hibernation: {error}"), fmt::arg("error", e.what())));
|
||||
tr_logAddError(
|
||||
fmt::format(fmt::runtime(_("Couldn't inhibit desktop hibernation: {error}")), fmt::arg("error", e.what())));
|
||||
}
|
||||
|
||||
return success;
|
||||
|
@ -1069,7 +1073,8 @@ void gtr_uninhibit_hibernation(guint inhibit_cookie)
|
|||
}
|
||||
catch (Glib::Error const& e)
|
||||
{
|
||||
tr_logAddError(fmt::format(_("Couldn't inhibit desktop hibernation: {error}"), fmt::arg("error", e.what())));
|
||||
tr_logAddError(
|
||||
fmt::format(fmt::runtime(_("Couldn't inhibit desktop hibernation: {error}")), fmt::arg("error", e.what())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1183,7 +1188,7 @@ bool core_read_rpc_response_idle(tr_variant& response)
|
|||
}
|
||||
else
|
||||
{
|
||||
gtr_warning(fmt::format(_("Couldn't find pending RPC request for tag {tag}"), fmt::arg("tag", tag)));
|
||||
gtr_warning(fmt::format(fmt::runtime(_("Couldn't find pending RPC request for tag {tag}")), fmt::arg("tag", tag)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ void setLabelFromRatio(Gtk::Label* l, double d)
|
|||
|
||||
auto startedTimesText(uint64_t n)
|
||||
{
|
||||
return fmt::format(ngettext("Started {count:L} time", "Started {count:L} times", n), fmt::arg("count", n));
|
||||
return fmt::format(fmt::runtime(ngettext("Started {count:L} time", "Started {count:L} times", n)), fmt::arg("count", n));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -55,12 +55,12 @@ namespace
|
|||
{
|
||||
|
||||
#if !defined(TR_SYS_TRAY_IMPL_NONE)
|
||||
auto const TrayIconName = Glib::ustring("transmission-tray-icon"s);
|
||||
auto const AppIconName = Glib::ustring("transmission"s);
|
||||
char const* const TrayIconName = "transmission-tray-icon";
|
||||
char const* const AppIconName = "transmission";
|
||||
#endif
|
||||
|
||||
#if defined(TR_SYS_TRAY_IMPL_APPINDICATOR)
|
||||
auto const AppName = Glib::ustring("transmission-gtk"s);
|
||||
char const* const AppName = "transmission-gtk";
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
@ -138,23 +138,11 @@ namespace
|
|||
|
||||
Glib::ustring getIconName()
|
||||
{
|
||||
Glib::ustring icon_name;
|
||||
|
||||
// if the tray's icon is a 48x48 file, use it.
|
||||
// otherwise, use the fallback builtin icon.
|
||||
if (auto theme = Gtk::IconTheme::get_default(); !theme->has_icon(TrayIconName))
|
||||
{
|
||||
icon_name = AppIconName;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const icon_info = theme->lookup_icon(TrayIconName, 48, Gtk::ICON_LOOKUP_USE_BUILTIN);
|
||||
bool const icon_is_builtin = icon_info.get_filename().empty();
|
||||
|
||||
icon_name = icon_is_builtin ? AppIconName : TrayIconName;
|
||||
}
|
||||
|
||||
return icon_name;
|
||||
auto const icon = Gtk::IconTheme::get_default()->lookup_icon(TrayIconName, 48, Gtk::ICON_LOOKUP_USE_BUILTIN);
|
||||
return icon && !icon.get_filename().empty() ? TrayIconName : AppIconName;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -199,7 +187,7 @@ SystemTrayIcon::Impl::Impl([[maybe_unused]] Gtk::Window& main_window, Glib::RefP
|
|||
#endif
|
||||
|
||||
#if defined(TR_SYS_TRAY_IMPL_APPINDICATOR)
|
||||
indicator_ = app_indicator_new(AppName.c_str(), icon_name.c_str(), APP_INDICATOR_CATEGORY_SYSTEM_SERVICES);
|
||||
indicator_ = app_indicator_new(AppName, icon_name.c_str(), APP_INDICATOR_CATEGORY_SYSTEM_SERVICES);
|
||||
app_indicator_set_status(indicator_, APP_INDICATOR_STATUS_ACTIVE);
|
||||
app_indicator_set_menu(indicator_, Glib::unwrap(menu_));
|
||||
app_indicator_set_title(indicator_, Glib::get_application_name().c_str());
|
||||
|
@ -214,7 +202,7 @@ std::string SystemTrayIcon::Impl::make_tooltip_text() const
|
|||
{
|
||||
auto const* const session = core_->get_session();
|
||||
return fmt::format(
|
||||
_("{upload_speed} ▲ {download_speed} ▼"),
|
||||
fmt::runtime(_("{upload_speed} ▲ {download_speed} ▼")),
|
||||
fmt::arg("upload_speed", Speed{ tr_sessionGetRawSpeed_KBps(session, TR_UP), Speed::Units::KByps }.to_string()),
|
||||
fmt::arg("download_speed", Speed{ tr_sessionGetRawSpeed_KBps(session, TR_DOWN), Speed::Units::KByps }.to_string()));
|
||||
}
|
||||
|
|
|
@ -436,7 +436,7 @@ Glib::ustring Torrent::Impl::get_short_status_text() const
|
|||
case TR_STATUS_CHECK:
|
||||
return fmt::format(
|
||||
// xgettext:no-c-format
|
||||
_("Verifying local data ({percent_done}% tested)"),
|
||||
fmt::runtime(_("Verifying local data ({percent_done}% tested)")),
|
||||
fmt::arg("percent_done", cache_.recheck_progress.to_string()));
|
||||
|
||||
case TR_STATUS_DOWNLOAD:
|
||||
|
@ -444,7 +444,7 @@ Glib::ustring Torrent::Impl::get_short_status_text() const
|
|||
return fmt::format(
|
||||
"{:s} {:s}",
|
||||
get_short_transfer_text(),
|
||||
fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(cache_.ratio))));
|
||||
fmt::format(fmt::runtime(_("Ratio: {ratio}")), fmt::arg("ratio", tr_strlratio(cache_.ratio))));
|
||||
|
||||
default:
|
||||
return {};
|
||||
|
@ -463,7 +463,7 @@ Glib::ustring Torrent::Impl::get_long_progress_text() const
|
|||
{
|
||||
// 50 MB of 200 MB (25%)
|
||||
gstr += fmt::format(
|
||||
_("{current_size} of {complete_size} ({percent_done}%)"),
|
||||
fmt::runtime(_("{current_size} of {complete_size} ({percent_done}%)")),
|
||||
fmt::arg("current_size", tr_strlsize(haveTotal)),
|
||||
fmt::arg("complete_size", tr_strlsize(cache_.size_when_done)),
|
||||
fmt::arg("percent_done", cache_.percent_done.to_string()));
|
||||
|
@ -473,7 +473,8 @@ Glib::ustring Torrent::Impl::get_long_progress_text() const
|
|||
// 50 MB of 200 MB (25%), uploaded 30 MB (Ratio: X%, Goal: Y%)
|
||||
gstr += fmt::format(
|
||||
// xgettext:no-c-format
|
||||
_("{current_size} of {complete_size} ({percent_complete}%), uploaded {uploaded_size} (Ratio: {ratio}, Goal: {seed_ratio})"),
|
||||
fmt::runtime(_(
|
||||
"{current_size} of {complete_size} ({percent_complete}%), uploaded {uploaded_size} (Ratio: {ratio}, Goal: {seed_ratio})")),
|
||||
fmt::arg("current_size", tr_strlsize(haveTotal)),
|
||||
fmt::arg("complete_size", tr_strlsize(cache_.total_size)),
|
||||
fmt::arg("percent_complete", cache_.percent_complete.to_string()),
|
||||
|
@ -485,7 +486,8 @@ Glib::ustring Torrent::Impl::get_long_progress_text() const
|
|||
{
|
||||
gstr += fmt::format(
|
||||
// xgettext:no-c-format
|
||||
_("{current_size} of {complete_size} ({percent_complete}%), uploaded {uploaded_size} (Ratio: {ratio})"),
|
||||
fmt::runtime(
|
||||
_("{current_size} of {complete_size} ({percent_complete}%), uploaded {uploaded_size} (Ratio: {ratio})")),
|
||||
fmt::arg("current_size", tr_strlsize(haveTotal)),
|
||||
fmt::arg("complete_size", tr_strlsize(cache_.total_size)),
|
||||
fmt::arg("percent_complete", cache_.percent_complete.to_string()),
|
||||
|
@ -495,7 +497,7 @@ Glib::ustring Torrent::Impl::get_long_progress_text() const
|
|||
else if (cache_.has_seed_ratio) // seed, seed ratio
|
||||
{
|
||||
gstr += fmt::format(
|
||||
_("{complete_size}, uploaded {uploaded_size} (Ratio: {ratio}, Goal: {seed_ratio})"),
|
||||
fmt::runtime(_("{complete_size}, uploaded {uploaded_size} (Ratio: {ratio}, Goal: {seed_ratio})")),
|
||||
fmt::arg("complete_size", tr_strlsize(cache_.total_size)),
|
||||
fmt::arg("uploaded_size", tr_strlsize(cache_.uploaded_ever)),
|
||||
fmt::arg("ratio", tr_strlratio(cache_.ratio)),
|
||||
|
@ -504,7 +506,7 @@ Glib::ustring Torrent::Impl::get_long_progress_text() const
|
|||
else // seed, no seed ratio
|
||||
{
|
||||
gstr += fmt::format(
|
||||
_("{complete_size}, uploaded {uploaded_size} (Ratio: {ratio})"),
|
||||
fmt::runtime(_("{complete_size}, uploaded {uploaded_size} (Ratio: {ratio})")),
|
||||
fmt::arg("complete_size", tr_strlsize(cache_.total_size)),
|
||||
fmt::arg("uploaded_size", tr_strlsize(cache_.uploaded_ever)),
|
||||
fmt::arg("ratio", tr_strlratio(cache_.ratio)));
|
||||
|
@ -637,13 +639,13 @@ Glib::ustring Torrent::Impl::get_error_text() const
|
|||
switch (cache_.error_code)
|
||||
{
|
||||
case TR_STAT_TRACKER_WARNING:
|
||||
return fmt::format(_("Tracker warning: '{warning}'"), fmt::arg("warning", cache_.error_message));
|
||||
return fmt::format(fmt::runtime(_("Tracker warning: '{warning}'")), fmt::arg("warning", cache_.error_message));
|
||||
|
||||
case TR_STAT_TRACKER_ERROR:
|
||||
return fmt::format(_("Tracker Error: '{error}'"), fmt::arg("error", cache_.error_message));
|
||||
return fmt::format(fmt::runtime(_("Tracker Error: '{error}'")), fmt::arg("error", cache_.error_message));
|
||||
|
||||
case TR_STAT_LOCAL_ERROR:
|
||||
return fmt::format(_("Local error: '{error}'"), fmt::arg("error", cache_.error_message));
|
||||
return fmt::format(fmt::runtime(_("Local error: '{error}'")), fmt::arg("error", cache_.error_message));
|
||||
|
||||
default:
|
||||
return {};
|
||||
|
@ -665,11 +667,11 @@ Glib::ustring Torrent::Impl::get_activity_text() const
|
|||
if (!cache_.has_metadata)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
// xgettext:no-c-format
|
||||
"Downloading metadata from {active_count} connected peer ({percent_done}% done)",
|
||||
"Downloading metadata from {active_count} connected peers ({percent_done}% done)",
|
||||
cache_.peers_connected),
|
||||
cache_.peers_connected)),
|
||||
fmt::arg("active_count", cache_.peers_connected),
|
||||
fmt::arg("percent_done", cache_.metadata_percent_complete.to_string()));
|
||||
}
|
||||
|
@ -677,10 +679,10 @@ Glib::ustring Torrent::Impl::get_activity_text() const
|
|||
if (cache_.peers_sending_to_us != 0 && cache_.webseeds_sending_to_us != 0)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"Downloading from {active_count} of {connected_count} connected peer and webseed",
|
||||
"Downloading from {active_count} of {connected_count} connected peers and webseeds",
|
||||
cache_.peers_connected + cache_.webseeds_sending_to_us),
|
||||
cache_.peers_connected + cache_.webseeds_sending_to_us)),
|
||||
fmt::arg("active_count", cache_.peers_sending_to_us + cache_.webseeds_sending_to_us),
|
||||
fmt::arg("connected_count", cache_.peers_connected + cache_.webseeds_sending_to_us));
|
||||
}
|
||||
|
@ -688,27 +690,27 @@ Glib::ustring Torrent::Impl::get_activity_text() const
|
|||
if (cache_.webseeds_sending_to_us != 0)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"Downloading from {active_count} webseed",
|
||||
"Downloading from {active_count} webseeds",
|
||||
cache_.webseeds_sending_to_us),
|
||||
cache_.webseeds_sending_to_us)),
|
||||
fmt::arg("active_count", cache_.webseeds_sending_to_us));
|
||||
}
|
||||
|
||||
return fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"Downloading from {active_count} of {connected_count} connected peer",
|
||||
"Downloading from {active_count} of {connected_count} connected peers",
|
||||
cache_.peers_connected),
|
||||
cache_.peers_connected)),
|
||||
fmt::arg("active_count", cache_.peers_sending_to_us),
|
||||
fmt::arg("connected_count", cache_.peers_connected));
|
||||
|
||||
case TR_STATUS_SEED:
|
||||
return fmt::format(
|
||||
ngettext(
|
||||
fmt::runtime(ngettext(
|
||||
"Seeding to {active_count} of {connected_count} connected peer",
|
||||
"Seeding to {active_count} of {connected_count} connected peers",
|
||||
cache_.peers_connected),
|
||||
cache_.peers_connected)),
|
||||
fmt::arg("active_count", cache_.peers_getting_from_us),
|
||||
fmt::arg("connected_count", cache_.peers_connected));
|
||||
|
||||
|
|
54
gtk/Utils.cc
54
gtk/Utils.cc
|
@ -132,28 +132,30 @@ std::string tr_format_future_time(time_t seconds)
|
|||
if (auto const days_from_now = seconds / 86400U; days_from_now > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{days_from_now:L} day from now", "{days_from_now:L} days from now", days_from_now),
|
||||
fmt::runtime(ngettext("{days_from_now:L} day from now", "{days_from_now:L} days from now", days_from_now)),
|
||||
fmt::arg("days_from_now", days_from_now));
|
||||
}
|
||||
|
||||
if (auto const hours_from_now = (seconds % 86400U) / 3600U; hours_from_now > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{hours_from_now:L} hour from now", "{hours_from_now:L} hours from now", hours_from_now),
|
||||
fmt::runtime(ngettext("{hours_from_now:L} hour from now", "{hours_from_now:L} hours from now", hours_from_now)),
|
||||
fmt::arg("hours_from_now", hours_from_now));
|
||||
}
|
||||
|
||||
if (auto const minutes_from_now = (seconds % 3600U) / 60U; minutes_from_now > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{minutes_from_now:L} minute from now", "{minutes_from_now:L} minutes from now", minutes_from_now),
|
||||
fmt::runtime(
|
||||
ngettext("{minutes_from_now:L} minute from now", "{minutes_from_now:L} minutes from now", minutes_from_now)),
|
||||
fmt::arg("minutes_from_now", minutes_from_now));
|
||||
}
|
||||
|
||||
if (auto const seconds_from_now = seconds % 60U; seconds_from_now > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{seconds_from_now:L} second from now", "{seconds_from_now:L} seconds from now", seconds_from_now),
|
||||
fmt::runtime(
|
||||
ngettext("{seconds_from_now:L} second from now", "{seconds_from_now:L} seconds from now", seconds_from_now)),
|
||||
fmt::arg("seconds_from_now", seconds_from_now));
|
||||
}
|
||||
|
||||
|
@ -164,27 +166,29 @@ std::string tr_format_past_time(time_t seconds)
|
|||
{
|
||||
if (auto const days_ago = seconds / 86400U; days_ago > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{days_ago:L} day ago", "{days_ago:L} days ago", days_ago), fmt::arg("days_ago", days_ago));
|
||||
return fmt::format(
|
||||
fmt::runtime(ngettext("{days_ago:L} day ago", "{days_ago:L} days ago", days_ago)),
|
||||
fmt::arg("days_ago", days_ago));
|
||||
}
|
||||
|
||||
if (auto const hours_ago = (seconds % 86400U) / 3600U; hours_ago > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{hours_ago:L} hour ago", "{hours_ago:L} hours ago", hours_ago),
|
||||
fmt::runtime(ngettext("{hours_ago:L} hour ago", "{hours_ago:L} hours ago", hours_ago)),
|
||||
fmt::arg("hours_ago", hours_ago));
|
||||
}
|
||||
|
||||
if (auto const minutes_ago = (seconds % 3600U) / 60U; minutes_ago > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{minutes_ago:L} minute ago", "{minutes_ago:L} minutes ago", minutes_ago),
|
||||
fmt::runtime(ngettext("{minutes_ago:L} minute ago", "{minutes_ago:L} minutes ago", minutes_ago)),
|
||||
fmt::arg("minutes_ago", minutes_ago));
|
||||
}
|
||||
|
||||
if (auto const seconds_ago = seconds % 60U; seconds_ago > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{seconds_ago:L} second ago", "{seconds_ago:L} seconds ago", seconds_ago),
|
||||
fmt::runtime(ngettext("{seconds_ago:L} second ago", "{seconds_ago:L} seconds ago", seconds_ago)),
|
||||
fmt::arg("seconds_ago", seconds_ago));
|
||||
}
|
||||
|
||||
|
@ -197,22 +201,26 @@ std::string tr_format_time(time_t timestamp)
|
|||
{
|
||||
if (auto const days = timestamp / 86400U; days > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{days:L} day", "{days:L} days", days), fmt::arg("days", days));
|
||||
return fmt::format(fmt::runtime(ngettext("{days:L} day", "{days:L} days", days)), fmt::arg("days", days));
|
||||
}
|
||||
|
||||
if (auto const hours = (timestamp % 86400U) / 3600U; hours > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{hours:L} hour", "{hours:L} hours", hours), fmt::arg("hours", hours));
|
||||
return fmt::format(fmt::runtime(ngettext("{hours:L} hour", "{hours:L} hours", hours)), fmt::arg("hours", hours));
|
||||
}
|
||||
|
||||
if (auto const minutes = (timestamp % 3600U) / 60U; minutes > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{minutes:L} minute", "{minutes:L} minutes", minutes), fmt::arg("minutes", minutes));
|
||||
return fmt::format(
|
||||
fmt::runtime(ngettext("{minutes:L} minute", "{minutes:L} minutes", minutes)),
|
||||
fmt::arg("minutes", minutes));
|
||||
}
|
||||
|
||||
if (auto const seconds = timestamp % 60U; seconds > 0U)
|
||||
{
|
||||
return fmt::format(ngettext("{seconds:L} second", "{seconds:L} seconds", seconds), fmt::arg("seconds", seconds));
|
||||
return fmt::format(
|
||||
fmt::runtime(ngettext("{seconds:L} second", "{seconds:L} seconds", seconds)),
|
||||
fmt::arg("seconds", seconds));
|
||||
}
|
||||
|
||||
return _("now");
|
||||
|
@ -223,28 +231,28 @@ std::string tr_format_time_left(time_t timestamp)
|
|||
if (auto const days_left = timestamp / 86400U; days_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{days_left:L} day left", "{days_left:L} days left", days_left),
|
||||
fmt::runtime(ngettext("{days_left:L} day left", "{days_left:L} days left", days_left)),
|
||||
fmt::arg("days_left", days_left));
|
||||
}
|
||||
|
||||
if (auto const hours_left = (timestamp % 86400U) / 3600U; hours_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{hours_left:L} hour left", "{hours_left:L} hours left", hours_left),
|
||||
fmt::runtime(ngettext("{hours_left:L} hour left", "{hours_left:L} hours left", hours_left)),
|
||||
fmt::arg("hours_left", hours_left));
|
||||
}
|
||||
|
||||
if (auto const minutes_left = (timestamp % 3600U) / 60U; minutes_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{minutes_left:L} minute left", "{minutes_left:L} minutes left", minutes_left),
|
||||
fmt::runtime(ngettext("{minutes_left:L} minute left", "{minutes_left:L} minutes left", minutes_left)),
|
||||
fmt::arg("minutes_left", minutes_left));
|
||||
}
|
||||
|
||||
if (auto const seconds_left = timestamp % 60U; seconds_left > 0U)
|
||||
{
|
||||
return fmt::format(
|
||||
ngettext("{seconds_left:L} second left", "{seconds_left:L} seconds left", seconds_left),
|
||||
fmt::runtime(ngettext("{seconds_left:L} second left", "{seconds_left:L} seconds left", seconds_left)),
|
||||
fmt::arg("seconds_left", seconds_left));
|
||||
}
|
||||
|
||||
|
@ -263,13 +271,13 @@ void gtr_add_torrent_error_dialog(Gtk::Widget& child, tr_torrent* duplicate_torr
|
|||
if (duplicate_torrent != nullptr)
|
||||
{
|
||||
secondary = fmt::format(
|
||||
_("The torrent file '{path}' is already in use by '{torrent_name}'."),
|
||||
fmt::runtime(_("The torrent file '{path}' is already in use by '{torrent_name}'.")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("torrent_name", tr_torrentName(duplicate_torrent)));
|
||||
}
|
||||
else
|
||||
{
|
||||
secondary = fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", filename));
|
||||
secondary = fmt::format(fmt::runtime(_("Couldn't add torrent file '{path}'")), fmt::arg("path", filename));
|
||||
}
|
||||
|
||||
auto w = std::make_shared<Gtk::MessageDialog>(
|
||||
|
@ -534,7 +542,7 @@ bool gtr_file_trash_or_remove(std::string const& filename, tr_error* error)
|
|||
{
|
||||
error->set(e.code(), TR_GLIB_EXCEPTION_WHAT(e));
|
||||
gtr_message(fmt::format(
|
||||
_("Couldn't move '{path}' to trash: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't move '{path}' to trash: {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message()),
|
||||
fmt::arg("error_code", error->code())));
|
||||
|
@ -552,7 +560,7 @@ bool gtr_file_trash_or_remove(std::string const& filename, tr_error* error)
|
|||
{
|
||||
error->set(e.code(), TR_GLIB_EXCEPTION_WHAT(e));
|
||||
gtr_message(fmt::format(
|
||||
_("Couldn't remove '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't remove '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message()),
|
||||
fmt::arg("error_code", error->code())));
|
||||
|
@ -641,7 +649,7 @@ void gtr_open_uri(Glib::ustring const& uri)
|
|||
|
||||
if (!opened)
|
||||
{
|
||||
gtr_message(fmt::format(_("Couldn't open '{url}'"), fmt::arg("url", uri)));
|
||||
gtr_message(fmt::format(fmt::runtime(_("Couldn't open '{url}'")), fmt::arg("url", uri)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -852,13 +860,13 @@ void gtr_unrecognized_url_dialog(Gtk::Widget& parent, Glib::ustring const& url)
|
|||
|
||||
auto w = std::make_shared<Gtk::MessageDialog>(
|
||||
gtr_widget_get_window(parent),
|
||||
fmt::format(_("Unsupported URL: '{url}'"), fmt::arg("url", url)),
|
||||
fmt::format(fmt::runtime(_("Unsupported URL: '{url}'")), fmt::arg("url", url)),
|
||||
false /*use markup*/,
|
||||
TR_GTK_MESSAGE_TYPE(ERROR),
|
||||
TR_GTK_BUTTONS_TYPE(CLOSE),
|
||||
true /*modal*/);
|
||||
|
||||
gstr += fmt::format(_("Transmission doesn't know how to use '{url}'"), fmt::arg("url", url));
|
||||
gstr += fmt::format(fmt::runtime(_("Transmission doesn't know how to use '{url}'")), fmt::arg("url", url));
|
||||
|
||||
if (tr_magnet_metainfo{}.parseMagnet(url.raw()))
|
||||
{
|
||||
|
|
|
@ -103,7 +103,7 @@ int main(int argc, char** argv)
|
|||
fmt::print(stderr, "{}\n", TR_GLIB_EXCEPTION_WHAT(e));
|
||||
fmt::print(
|
||||
stderr,
|
||||
_("Run '{program} --help' to see a full list of available command line options.\n"),
|
||||
fmt::runtime(_("Run '{program} --help' to see a full list of available command line options.\n")),
|
||||
fmt::arg("program", *argv));
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include(CheckAtomic)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckSymbolExists)
|
||||
|
||||
|
@ -222,7 +223,6 @@ target_compile_definitions(${TR_NAME}
|
|||
$<$<BOOL:${WITH_INOTIFY}>:WITH_INOTIFY>
|
||||
$<$<BOOL:${WITH_KQUEUE}>:WITH_KQUEUE>
|
||||
$<$<BOOL:${ENABLE_UTP}>:WITH_UTP>
|
||||
$<$<VERSION_LESS:${MINIUPNPC_VERSION},1.7>:MINIUPNPC_API_VERSION=${MINIUPNPC_API_VERSION}> # API version macro was only added in 1.7
|
||||
$<$<BOOL:${USE_SYSTEM_B64}>:USE_SYSTEM_B64>
|
||||
$<$<BOOL:${HAVE_SO_REUSEPORT}>:HAVE_SO_REUSEPORT=1>
|
||||
PUBLIC
|
||||
|
@ -299,6 +299,7 @@ target_link_libraries(${TR_NAME}
|
|||
$<$<BOOL:${WIN32}>:shlwapi>
|
||||
"$<$<BOOL:${APPLE}>:-framework Foundation>"
|
||||
"$<$<BOOL:${ANDROID}>:${log-lib}>"
|
||||
$<$<BOOL:${HAVE_LIBATOMIC}>:atomic>
|
||||
PUBLIC
|
||||
transmission::crypto_impl
|
||||
fmt::fmt-header-only
|
||||
|
|
|
@ -157,10 +157,10 @@ struct tr_announce_response
|
|||
* This is only an upper bound: if the tracker complains about
|
||||
* length, announcer will incrementally lower the batch size.
|
||||
*/
|
||||
auto inline constexpr TR_MULTISCRAPE_MAX = 60;
|
||||
auto inline constexpr TrMultiscrapeMax = 60;
|
||||
|
||||
auto inline constexpr TR_ANNOUNCE_TIMEOUT_SEC = std::chrono::seconds{ 45 };
|
||||
auto inline constexpr TR_SCRAPE_TIMEOUT_SEC = std::chrono::seconds{ 30 };
|
||||
auto inline constexpr TrAnnounceTimeoutSec = std::chrono::seconds{ 45 };
|
||||
auto inline constexpr TrScrapeTimeoutSec = std::chrono::seconds{ 30 };
|
||||
|
||||
struct tr_scrape_request
|
||||
{
|
||||
|
@ -171,7 +171,7 @@ struct tr_scrape_request
|
|||
std::string log_name;
|
||||
|
||||
/* info hashes of the torrents to scrape */
|
||||
std::array<tr_sha1_digest_t, TR_MULTISCRAPE_MAX> info_hash;
|
||||
std::array<tr_sha1_digest_t, TrMultiscrapeMax> info_hash;
|
||||
|
||||
/* how many hashes to use in the info_hash field */
|
||||
int info_hash_count = 0;
|
||||
|
@ -209,7 +209,7 @@ struct tr_scrape_response
|
|||
int row_count;
|
||||
|
||||
/* the individual torrents' scrape results */
|
||||
std::array<tr_scrape_response_row, TR_MULTISCRAPE_MAX> rows;
|
||||
std::array<tr_scrape_response_row, TrMultiscrapeMax> rows;
|
||||
|
||||
/* the raw scrape url */
|
||||
tr_interned_string scrape_url;
|
||||
|
|
|
@ -265,7 +265,7 @@ void tr_tracker_http_announce(
|
|||
auto url = tr_urlbuf{};
|
||||
announce_url_new(url, session, request);
|
||||
auto options = tr_web::FetchOptions{ url.sv(), onAnnounceDone, d };
|
||||
options.timeout_secs = TR_ANNOUNCE_TIMEOUT_SEC;
|
||||
options.timeout_secs = TrAnnounceTimeoutSec;
|
||||
options.sndbuf = 4096;
|
||||
options.rcvbuf = 4096;
|
||||
|
||||
|
@ -437,7 +437,7 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
{
|
||||
tr_logAddWarn(
|
||||
fmt::format(
|
||||
_("Couldn't parse announce response: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't parse announce response: {error} ({error_code})")),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())),
|
||||
log_name);
|
||||
|
@ -542,7 +542,7 @@ void tr_tracker_http_scrape(tr_session const* session, tr_scrape_request const&
|
|||
scrape_url_new(scrape_url, request);
|
||||
tr_logAddTrace(fmt::format("Sending scrape to libcurl: '{}'", scrape_url), request.log_name);
|
||||
auto options = tr_web::FetchOptions{ scrape_url, onScrapeDone, d };
|
||||
options.timeout_secs = TR_SCRAPE_TIMEOUT_SEC;
|
||||
options.timeout_secs = TrScrapeTimeoutSec;
|
||||
options.sndbuf = 4096;
|
||||
options.rcvbuf = 4096;
|
||||
session->fetch(std::move(options));
|
||||
|
@ -644,7 +644,7 @@ void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::stri
|
|||
{
|
||||
tr_logAddWarn(
|
||||
fmt::format(
|
||||
_("Couldn't parse scrape response: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't parse scrape response: {error} ({error_code})")),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())),
|
||||
log_name);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <algorithm> // for std::find_if()
|
||||
#include <array>
|
||||
#include <chrono> // operator""ms, literals
|
||||
#include <climits> // CHAR_BIT
|
||||
#include <cstddef> // std::byte
|
||||
#include <cstdint> // uint32_t, uint64_t
|
||||
#include <cstring> // memcpy()
|
||||
|
@ -17,8 +16,8 @@
|
|||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
|
@ -55,13 +54,15 @@ namespace
|
|||
{
|
||||
using namespace std::literals;
|
||||
|
||||
// size defined by bep15
|
||||
// size defined by https://www.bittorrent.org/beps/bep_0015.html
|
||||
using tau_connection_t = uint64_t;
|
||||
using tau_transaction_t = uint32_t;
|
||||
|
||||
using InBuf = libtransmission::BufferReader<std::byte>;
|
||||
using PayloadBuffer = libtransmission::StackBuffer<4096, std::byte>;
|
||||
|
||||
using ipp_t = std::underlying_type_t<tr_address_type>;
|
||||
|
||||
constexpr auto TauConnectionTtlSecs = time_t{ 45 };
|
||||
|
||||
auto tau_transaction_new()
|
||||
|
@ -69,7 +70,8 @@ auto tau_transaction_new()
|
|||
return tr_rand_obj<tau_transaction_t>();
|
||||
}
|
||||
|
||||
// used in the "action" field of a request. Values defined in bep 15.
|
||||
// used in the "action" field of a request.
|
||||
// Values defined in https://www.bittorrent.org/beps/bep_0015.html
|
||||
enum tau_action_t : uint8_t
|
||||
{
|
||||
TAU_ACTION_CONNECT = 0,
|
||||
|
@ -85,22 +87,20 @@ struct tau_scrape_request
|
|||
tau_scrape_request(tr_scrape_request const& in, tr_scrape_response_func on_response)
|
||||
: on_response_{ std::move(on_response) }
|
||||
{
|
||||
this->response.scrape_url = in.scrape_url;
|
||||
this->response.row_count = in.info_hash_count;
|
||||
for (int i = 0; i < this->response.row_count; ++i)
|
||||
response.scrape_url = in.scrape_url;
|
||||
response.row_count = in.info_hash_count;
|
||||
for (int i = 0; i < response.row_count; ++i)
|
||||
{
|
||||
this->response.rows[i].info_hash = in.info_hash[i];
|
||||
response.rows[i].info_hash = in.info_hash[i];
|
||||
}
|
||||
|
||||
// build the payload
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint32(TAU_ACTION_SCRAPE);
|
||||
buf.add_uint32(transaction_id);
|
||||
payload.add_uint32(TAU_ACTION_SCRAPE);
|
||||
payload.add_uint32(transaction_id);
|
||||
for (int i = 0; i < in.info_hash_count; ++i)
|
||||
{
|
||||
buf.add(in.info_hash[i]);
|
||||
payload.add(in.info_hash[i]);
|
||||
}
|
||||
this->payload.insert(std::end(this->payload), std::begin(buf), std::end(buf));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_callback() const noexcept
|
||||
|
@ -108,7 +108,7 @@ struct tau_scrape_request
|
|||
return !!on_response_;
|
||||
}
|
||||
|
||||
void requestFinished() const
|
||||
void request_finished() const
|
||||
{
|
||||
if (on_response_)
|
||||
{
|
||||
|
@ -121,10 +121,10 @@ struct tau_scrape_request
|
|||
response.did_connect = did_connect;
|
||||
response.did_timeout = did_timeout;
|
||||
response.errmsg = errmsg;
|
||||
requestFinished();
|
||||
request_finished();
|
||||
}
|
||||
|
||||
void onResponse(tau_action_t action, InBuf& buf)
|
||||
void on_response(tau_action_t action, InBuf& buf)
|
||||
{
|
||||
response.did_connect = true;
|
||||
response.did_timeout = false;
|
||||
|
@ -139,7 +139,7 @@ struct tau_scrape_request
|
|||
row.leechers = buf.to_uint32();
|
||||
}
|
||||
|
||||
requestFinished();
|
||||
request_finished();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -148,18 +148,20 @@ struct tau_scrape_request
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto expiresAt() const noexcept
|
||||
[[nodiscard]] constexpr auto expires_at() const noexcept
|
||||
{
|
||||
return created_at_ + TR_SCRAPE_TIMEOUT_SEC.count();
|
||||
return created_at_ + TrScrapeTimeoutSec.count();
|
||||
}
|
||||
|
||||
std::vector<std::byte> payload;
|
||||
PayloadBuffer payload;
|
||||
|
||||
time_t sent_at = 0;
|
||||
tau_transaction_t const transaction_id = tau_transaction_new();
|
||||
|
||||
tr_scrape_response response = {};
|
||||
|
||||
static auto constexpr ip_protocol = TR_AF_UNSPEC; // NOLINT(readability-identifier-naming)
|
||||
|
||||
private:
|
||||
time_t const created_at_ = tr_time();
|
||||
|
||||
|
@ -171,38 +173,39 @@ private:
|
|||
struct tau_announce_request
|
||||
{
|
||||
tau_announce_request(
|
||||
tr_address_type ip_protocol_in,
|
||||
std::optional<tr_address> announce_ip,
|
||||
tr_announce_request const& in,
|
||||
tr_announce_response_func on_response)
|
||||
: on_response_{ std::move(on_response) }
|
||||
: ip_protocol{ ip_protocol_in }
|
||||
, on_response_{ std::move(on_response) }
|
||||
{
|
||||
// https://www.bittorrent.org/beps/bep_0015.html sets key size at 32 bits
|
||||
static_assert(sizeof(tr_announce_request::key) * CHAR_BIT == 32);
|
||||
static_assert(sizeof(tr_announce_request::key) == sizeof(uint32_t));
|
||||
|
||||
response.info_hash = in.info_hash;
|
||||
|
||||
// build the payload
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint32(TAU_ACTION_ANNOUNCE);
|
||||
buf.add_uint32(transaction_id);
|
||||
buf.add(in.info_hash);
|
||||
buf.add(in.peer_id);
|
||||
buf.add_uint64(in.down);
|
||||
buf.add_uint64(in.leftUntilComplete);
|
||||
buf.add_uint64(in.up);
|
||||
buf.add_uint32(get_tau_announce_event(in.event));
|
||||
payload.add_uint32(TAU_ACTION_ANNOUNCE);
|
||||
payload.add_uint32(transaction_id);
|
||||
payload.add(in.info_hash);
|
||||
payload.add(in.peer_id);
|
||||
payload.add_uint64(in.down);
|
||||
payload.add_uint64(in.leftUntilComplete);
|
||||
payload.add_uint64(in.up);
|
||||
payload.add_uint32(get_tau_announce_event(in.event));
|
||||
if (announce_ip && announce_ip->is_ipv4())
|
||||
{
|
||||
buf.add_address(*announce_ip);
|
||||
// Since size of IP field is only 4 bytes long, we can only announce IPv4 addresses
|
||||
payload.add_address(*announce_ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.add_uint32(0U);
|
||||
payload.add_uint32(0U);
|
||||
}
|
||||
buf.add_uint32(in.key);
|
||||
buf.add_uint32(in.numwant);
|
||||
buf.add_port(in.port);
|
||||
payload.insert(std::end(payload), std::begin(buf), std::end(buf));
|
||||
payload.add_uint32(in.key);
|
||||
payload.add_uint32(in.numwant);
|
||||
payload.add_port(in.port);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_callback() const noexcept
|
||||
|
@ -210,28 +213,28 @@ struct tau_announce_request
|
|||
return !!on_response_;
|
||||
}
|
||||
|
||||
void requestFinished() const
|
||||
void request_finished() const
|
||||
{
|
||||
if (on_response_)
|
||||
{
|
||||
on_response_(this->response);
|
||||
on_response_(response);
|
||||
}
|
||||
}
|
||||
|
||||
void fail(bool did_connect, bool did_timeout, std::string_view errmsg)
|
||||
{
|
||||
this->response.did_connect = did_connect;
|
||||
this->response.did_timeout = did_timeout;
|
||||
this->response.errmsg = errmsg;
|
||||
this->requestFinished();
|
||||
response.did_connect = did_connect;
|
||||
response.did_timeout = did_timeout;
|
||||
response.errmsg = errmsg;
|
||||
request_finished();
|
||||
}
|
||||
|
||||
void onResponse(tau_action_t action, InBuf& buf)
|
||||
void on_response(tr_address_type ip_protocol_resp, tau_action_t action, InBuf& buf)
|
||||
{
|
||||
auto const buflen = std::size(buf);
|
||||
|
||||
this->response.did_connect = true;
|
||||
this->response.did_timeout = false;
|
||||
response.did_connect = true;
|
||||
response.did_timeout = false;
|
||||
|
||||
if (action == TAU_ACTION_ANNOUNCE && buflen >= 3 * sizeof(uint32_t))
|
||||
{
|
||||
|
@ -239,8 +242,18 @@ struct tau_announce_request
|
|||
response.leechers = buf.to_uint32();
|
||||
response.seeders = buf.to_uint32();
|
||||
|
||||
response.pex = tr_pex::from_compact_ipv4(std::data(buf), std::size(buf), nullptr, 0);
|
||||
requestFinished();
|
||||
switch (ip_protocol_resp)
|
||||
{
|
||||
case TR_AF_INET:
|
||||
response.pex = tr_pex::from_compact_ipv4(std::data(buf), std::size(buf), nullptr, 0);
|
||||
break;
|
||||
case TR_AF_INET6:
|
||||
response.pex6 = tr_pex::from_compact_ipv6(std::data(buf), std::size(buf), nullptr, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
request_finished();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -249,23 +262,24 @@ struct tau_announce_request
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto expiresAt() const noexcept
|
||||
[[nodiscard]] constexpr auto expires_at() const noexcept
|
||||
{
|
||||
return created_at_ + TR_ANNOUNCE_TIMEOUT_SEC.count();
|
||||
return created_at_ + TrAnnounceTimeoutSec.count();
|
||||
}
|
||||
|
||||
enum tau_announce_event : uint8_t
|
||||
{
|
||||
// https://www.bittorrent.org/beps/bep_0015.html
|
||||
// Used in the "event" field of an announce request.
|
||||
// These values come from BEP 15
|
||||
TAU_ANNOUNCE_EVENT_NONE = 0,
|
||||
TAU_ANNOUNCE_EVENT_COMPLETED = 1,
|
||||
TAU_ANNOUNCE_EVENT_STARTED = 2,
|
||||
TAU_ANNOUNCE_EVENT_STOPPED = 3
|
||||
};
|
||||
|
||||
std::vector<std::byte> payload;
|
||||
PayloadBuffer payload;
|
||||
|
||||
tr_address_type const ip_protocol;
|
||||
time_t sent_at = 0;
|
||||
tau_transaction_t const transaction_id = tau_transaction_new();
|
||||
|
||||
|
@ -303,97 +317,145 @@ struct tau_tracker
|
|||
|
||||
tau_tracker(
|
||||
Mediator& mediator,
|
||||
std::string_view const interned_authority,
|
||||
std::string_view const interned_host,
|
||||
std::string_view const authority_in,
|
||||
std::string_view const host_in,
|
||||
std::string_view const host_lookup_in,
|
||||
tr_port const port_in)
|
||||
: authority{ interned_authority }
|
||||
, host{ interned_host }
|
||||
: authority{ authority_in }
|
||||
, host{ host_in }
|
||||
, host_lookup{ host_lookup_in }
|
||||
, port{ port_in }
|
||||
, mediator_{ mediator }
|
||||
{
|
||||
}
|
||||
|
||||
void sendto(std::byte const* buf, size_t buflen)
|
||||
void sendto(tr_address_type ip_protocol, std::byte const* buf, size_t buflen)
|
||||
{
|
||||
TR_ASSERT(addr_);
|
||||
if (!addr_)
|
||||
TR_ASSERT(tr_address::is_valid(ip_protocol));
|
||||
if (!tr_address::is_valid(ip_protocol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const& [ss, sslen] = *addr_;
|
||||
auto const& addr = addr_[ip_protocol];
|
||||
TR_ASSERT(addr);
|
||||
if (!addr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const& [ss, sslen] = *addr;
|
||||
mediator_.sendto(buf, buflen, reinterpret_cast<sockaddr const*>(&ss), sslen);
|
||||
}
|
||||
|
||||
void on_connection_response(tau_action_t action, InBuf& buf)
|
||||
void on_connection_response(tr_address_type ip_protocol, tau_action_t action, InBuf& buf)
|
||||
{
|
||||
this->connecting_at = 0;
|
||||
this->connection_transaction_id = 0;
|
||||
TR_ASSERT(tr_address::is_valid(ip_protocol));
|
||||
if (!tr_address::is_valid(ip_protocol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
connecting_at[ip_protocol] = 0;
|
||||
connection_transaction_id[ip_protocol] = 0;
|
||||
|
||||
if (action == TAU_ACTION_CONNECT)
|
||||
{
|
||||
this->connection_id = buf.to_uint64();
|
||||
this->connection_expiration_time = tr_time() + TauConnectionTtlSecs;
|
||||
logdbg(log_name(), fmt::format("Got a new connection ID from tracker: {}", this->connection_id));
|
||||
connection_id[ip_protocol] = buf.to_uint64();
|
||||
connection_expiration_time[ip_protocol] = tr_time() + TauConnectionTtlSecs;
|
||||
logdbg(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"Got a new {} connection ID from tracker: {}",
|
||||
tr_ip_protocol_to_sv(ip_protocol),
|
||||
connection_id[ip_protocol]));
|
||||
}
|
||||
else if (action == TAU_ACTION_ERROR)
|
||||
{
|
||||
std::string errmsg = !std::empty(buf) ? buf.to_string() : _("Connection failed");
|
||||
this->failAll(true, false, errmsg);
|
||||
std::string errmsg = !std::empty(buf) ?
|
||||
buf.to_string() :
|
||||
fmt::format(_("{ip_protocol} connection failed"), fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol)));
|
||||
fail_all(true, false, errmsg);
|
||||
logdbg(log_name(), std::move(errmsg));
|
||||
}
|
||||
|
||||
this->upkeep();
|
||||
upkeep();
|
||||
}
|
||||
|
||||
void upkeep(bool timeout_reqs = true)
|
||||
{
|
||||
time_t const now = tr_time();
|
||||
|
||||
// do we have a DNS request that's ready?
|
||||
if (addr_pending_dns_ && addr_pending_dns_->wait_for(0ms) == std::future_status::ready)
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
addr_ = addr_pending_dns_->get();
|
||||
addr_pending_dns_.reset();
|
||||
addr_expires_at_ = now + DnsRetryIntervalSecs;
|
||||
// do we have a DNS request that's ready?
|
||||
if (auto& dns = addr_pending_dns_[ipp]; dns && dns->wait_for(0ms) == std::future_status::ready)
|
||||
{
|
||||
addr_[ipp] = dns->get();
|
||||
dns.reset();
|
||||
addr_expires_at_[ipp] = now + DnsRetryIntervalSecs;
|
||||
}
|
||||
}
|
||||
|
||||
// are there any requests pending?
|
||||
if (this->is_idle())
|
||||
// are there any tracker requests pending?
|
||||
if (is_idle())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// update the addr if our lookup is past its shelf date
|
||||
if (!addr_pending_dns_ && addr_expires_at_ <= now)
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
// update the addr if our lookup is past its shelf date
|
||||
if (!addr_pending_dns_[ipp] && addr_expires_at_[ipp] <= now)
|
||||
{
|
||||
addr_[ipp].reset();
|
||||
addr_pending_dns_[ipp] = std::async(
|
||||
std::launch::async,
|
||||
[this](tr_address_type ip_protocol) { return lookup(ip_protocol); },
|
||||
static_cast<tr_address_type>(ipp));
|
||||
}
|
||||
}
|
||||
|
||||
// are there any dns requests pending?
|
||||
if (is_dns_pending())
|
||||
{
|
||||
addr_.reset();
|
||||
addr_pending_dns_ = std::async(std::launch::async, lookup, this->log_name(), this->host, this->port);
|
||||
return;
|
||||
}
|
||||
|
||||
logtrace(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"connected {} ({} {}) -- connecting_at {}",
|
||||
is_connected(now),
|
||||
this->connection_expiration_time,
|
||||
now,
|
||||
this->connecting_at));
|
||||
|
||||
/* also need a valid connection ID... */
|
||||
if (addr_ && !is_connected(now) && this->connecting_at == 0)
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
this->connecting_at = now;
|
||||
this->connection_transaction_id = tau_transaction_new();
|
||||
logtrace(log_name(), fmt::format("Trying to connect. Transaction ID is {}", this->connection_transaction_id));
|
||||
auto const ipp_enum = static_cast<tr_address_type>(ipp);
|
||||
logtrace(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"{} connected {} ({} {}) -- connecting_at {}",
|
||||
tr_ip_protocol_to_sv(ipp_enum),
|
||||
is_connected(ipp_enum, now),
|
||||
connection_expiration_time[ipp],
|
||||
now,
|
||||
connecting_at[ipp]));
|
||||
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint64(0x41727101980LL);
|
||||
buf.add_uint32(TAU_ACTION_CONNECT);
|
||||
buf.add_uint32(this->connection_transaction_id);
|
||||
// also need a valid connection ID...
|
||||
if (auto const& addr = addr_[ipp]; addr && !is_connected(ipp_enum, now) && connecting_at[ipp] == 0)
|
||||
{
|
||||
TR_ASSERT(addr->first.ss_family == tr_ip_protocol_to_af(ipp_enum));
|
||||
|
||||
this->sendto(std::data(buf), std::size(buf));
|
||||
connecting_at[ipp] = now;
|
||||
connection_transaction_id[ipp] = tau_transaction_new();
|
||||
logtrace(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"Trying to connect {}. Transaction ID is {}",
|
||||
tr_ip_protocol_to_sv(ipp_enum),
|
||||
connection_transaction_id[ipp]));
|
||||
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint64(0x41727101980LL);
|
||||
buf.add_uint32(TAU_ACTION_CONNECT);
|
||||
buf.add_uint32(connection_transaction_id[ipp]);
|
||||
|
||||
sendto(ipp_enum, std::data(buf), std::size(buf));
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout_reqs)
|
||||
|
@ -401,91 +463,113 @@ struct tau_tracker
|
|||
timeout_requests(now);
|
||||
}
|
||||
|
||||
if (addr_ && is_connected(now))
|
||||
{
|
||||
send_requests();
|
||||
}
|
||||
maybe_send_requests(now);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_idle() const noexcept
|
||||
[[nodiscard]] constexpr bool is_idle() const noexcept
|
||||
{
|
||||
return std::empty(announces) && std::empty(scrapes) && !addr_pending_dns_;
|
||||
return std::empty(announces) && std::empty(scrapes);
|
||||
}
|
||||
|
||||
private:
|
||||
using Sockaddr = std::pair<sockaddr_storage, socklen_t>;
|
||||
using MaybeSockaddr = std::optional<Sockaddr>;
|
||||
|
||||
[[nodiscard]] constexpr bool is_connected(time_t now) const noexcept
|
||||
[[nodiscard]] constexpr bool is_connected(tr_address_type ip_protocol, time_t now) const noexcept
|
||||
{
|
||||
return connection_id != tau_connection_t{} && now < connection_expiration_time;
|
||||
return connection_id[ip_protocol] != tau_connection_t{} && now < connection_expiration_time[ip_protocol];
|
||||
}
|
||||
|
||||
[[nodiscard]] static MaybeSockaddr lookup(
|
||||
std::string_view const interned_log_name,
|
||||
std::string_view const interned_host,
|
||||
tr_port const port)
|
||||
[[nodiscard]] TR_CONSTEXPR20 bool is_dns_pending() const noexcept
|
||||
{
|
||||
return std::any_of(std::begin(addr_pending_dns_), std::end(addr_pending_dns_), [](auto const& o) { return !!o; });
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 bool has_addr() const noexcept
|
||||
{
|
||||
return std::any_of(std::begin(addr_), std::end(addr_), [](auto const& o) { return !!o; });
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeSockaddr lookup(tr_address_type ip_protocol)
|
||||
{
|
||||
auto szport = std::array<char, 16>{};
|
||||
*fmt::format_to(std::data(szport), "{:d}", port.host()) = '\0';
|
||||
|
||||
auto hints = addrinfo{};
|
||||
hints.ai_family = AF_INET; // https://github.com/transmission/transmission/issues/4719
|
||||
hints.ai_family = tr_ip_protocol_to_af(ip_protocol);
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
addrinfo* info = nullptr;
|
||||
auto const szhost = tr_pathbuf{ interned_host };
|
||||
auto const szhost = tr_urlbuf{ host_lookup };
|
||||
if (int const rc = getaddrinfo(szhost.c_str(), std::data(szport), &hints, &info); rc != 0)
|
||||
{
|
||||
logwarn(
|
||||
interned_log_name,
|
||||
log_name(),
|
||||
fmt::format(
|
||||
_("Couldn't look up '{address}:{port}': {error} ({error_code})"),
|
||||
fmt::arg("address", interned_host),
|
||||
fmt::runtime(_("Couldn't look up '{address}:{port}' in {ip_protocol}: {error} ({error_code})")),
|
||||
fmt::arg("address", host),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol)),
|
||||
fmt::arg("error", gai_strerror(rc)),
|
||||
fmt::arg("error_code", static_cast<int>(rc))));
|
||||
return {};
|
||||
}
|
||||
auto const info_uniq = std::unique_ptr<addrinfo, void (*)(addrinfo*)>{ info,
|
||||
[](addrinfo* p) // MSVC forced my hands
|
||||
{
|
||||
freeaddrinfo(p);
|
||||
} };
|
||||
|
||||
auto ss = sockaddr_storage{};
|
||||
auto const len = info->ai_addrlen;
|
||||
memcpy(&ss, info->ai_addr, len);
|
||||
freeaddrinfo(info);
|
||||
// N.B. getaddrinfo() will return IPv4-mapped addresses by default on macOS
|
||||
auto socket_address = tr_socket_address::from_sockaddr(info->ai_addr);
|
||||
if (!socket_address || socket_address->address().is_ipv4_mapped_address())
|
||||
{
|
||||
logdbg(
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"Couldn't look up '{address}:{port}' in {ip_protocol}: got invalid address",
|
||||
fmt::arg("address", host),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(ip_protocol))));
|
||||
return {};
|
||||
}
|
||||
|
||||
logdbg(interned_log_name, "DNS lookup succeeded");
|
||||
return std::make_pair(ss, len);
|
||||
logdbg(log_name(), fmt::format("{} DNS lookup succeeded", tr_ip_protocol_to_sv(ip_protocol)));
|
||||
return socket_address->to_sockaddr();
|
||||
}
|
||||
|
||||
void failAll(bool did_connect, bool did_timeout, std::string_view errmsg)
|
||||
void fail_all(bool did_connect, bool did_timeout, std::string_view errmsg)
|
||||
{
|
||||
for (auto& req : this->scrapes)
|
||||
for (auto& req : scrapes)
|
||||
{
|
||||
req.fail(did_connect, did_timeout, errmsg);
|
||||
}
|
||||
|
||||
for (auto& req : this->announces)
|
||||
for (auto& req : announces)
|
||||
{
|
||||
req.fail(did_connect, did_timeout, errmsg);
|
||||
}
|
||||
|
||||
this->scrapes.clear();
|
||||
this->announces.clear();
|
||||
scrapes.clear();
|
||||
announces.clear();
|
||||
}
|
||||
|
||||
///
|
||||
// ---
|
||||
|
||||
void timeout_requests(time_t now)
|
||||
{
|
||||
if (this->connecting_at != 0 && this->connecting_at + ConnectionRequestTtl < now)
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
auto empty_buf = PayloadBuffer{};
|
||||
on_connection_response(TAU_ACTION_ERROR, empty_buf);
|
||||
if (connecting_at[ipp] != 0 && connecting_at[ipp] + ConnectionRequestTtl < now)
|
||||
{
|
||||
auto empty_buf = PayloadBuffer{};
|
||||
on_connection_response(static_cast<tr_address_type>(ipp), TAU_ACTION_ERROR, empty_buf);
|
||||
}
|
||||
}
|
||||
|
||||
timeout_requests(this->announces, now, "announce");
|
||||
timeout_requests(this->scrapes, now, "scrape");
|
||||
timeout_requests(announces, now, "announce"sv);
|
||||
timeout_requests(scrapes, now, "scrape"sv);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -493,7 +577,7 @@ private:
|
|||
{
|
||||
for (auto it = std::begin(requests); it != std::end(requests);)
|
||||
{
|
||||
if (auto& req = *it; req.expiresAt() <= now)
|
||||
if (auto& req = *it; req.expires_at() <= now)
|
||||
{
|
||||
logtrace(log_name(), fmt::format("timeout {} req {}", name, fmt::ptr(&req)));
|
||||
req.fail(false, true, "");
|
||||
|
@ -506,37 +590,35 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
///
|
||||
// ---
|
||||
|
||||
void send_requests()
|
||||
void maybe_send_requests(time_t now)
|
||||
{
|
||||
TR_ASSERT(!addr_pending_dns_);
|
||||
TR_ASSERT(addr_);
|
||||
TR_ASSERT(this->connecting_at == 0);
|
||||
TR_ASSERT(this->connection_expiration_time > tr_time());
|
||||
TR_ASSERT(!is_dns_pending());
|
||||
if (is_dns_pending() || !has_addr())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
send_requests(this->announces);
|
||||
send_requests(this->scrapes);
|
||||
maybe_send_requests(announces, now);
|
||||
maybe_send_requests(scrapes, now);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void send_requests(std::list<T>& reqs)
|
||||
void maybe_send_requests(std::list<T>& reqs, time_t now)
|
||||
{
|
||||
auto const now = tr_time();
|
||||
|
||||
for (auto it = std::begin(reqs); it != std::end(reqs);)
|
||||
{
|
||||
auto& req = *it;
|
||||
|
||||
if (req.sent_at != 0) // it's already been sent; we're awaiting a response
|
||||
if (req.sent_at != 0 || // it's already been sent; we're awaiting a response
|
||||
!maybe_send_request(req.ip_protocol, std::data(req.payload), std::size(req.payload), now))
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
logdbg(log_name(), fmt::format("sending req {}", fmt::ptr(&req)));
|
||||
logdbg(log_name(), fmt::format("sent req {}", fmt::ptr(&req)));
|
||||
req.sent_at = now;
|
||||
send_request(std::data(req.payload), std::size(req.payload));
|
||||
|
||||
if (req.has_callback())
|
||||
{
|
||||
|
@ -549,15 +631,24 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void send_request(std::byte const* payload, size_t payload_len)
|
||||
bool maybe_send_request(tr_address_type ip_protocol, std::byte const* payload, size_t payload_len, time_t now)
|
||||
{
|
||||
logdbg(log_name(), fmt::format("sending request w/connection id {}", this->connection_id));
|
||||
for (uint8_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
auto const ipp_enum = static_cast<tr_address_type>(ipp);
|
||||
if (addr_[ipp] && (ip_protocol == TR_AF_UNSPEC || ipp == ip_protocol) && is_connected(ipp_enum, now))
|
||||
{
|
||||
logdbg(log_name(), fmt::format("sending request w/connection id {}", connection_id[ipp]));
|
||||
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint64(this->connection_id);
|
||||
buf.add(payload, payload_len);
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint64(connection_id[ipp]);
|
||||
buf.add(payload, payload_len);
|
||||
|
||||
this->sendto(std::data(buf), std::size(buf));
|
||||
sendto(ipp_enum, std::data(buf), std::size(buf));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -566,14 +657,15 @@ public:
|
|||
return authority;
|
||||
}
|
||||
|
||||
std::string_view const authority; // interned
|
||||
std::string_view const host; // interned
|
||||
std::string_view const authority;
|
||||
std::string_view const host;
|
||||
std::string_view const host_lookup;
|
||||
tr_port const port;
|
||||
|
||||
time_t connecting_at = 0;
|
||||
time_t connection_expiration_time = 0;
|
||||
tau_connection_t connection_id = {};
|
||||
tau_transaction_t connection_transaction_id = {};
|
||||
std::array<time_t, NUM_TR_AF_INET_TYPES> connecting_at = {};
|
||||
std::array<time_t, NUM_TR_AF_INET_TYPES> connection_expiration_time = {};
|
||||
std::array<tau_connection_t, NUM_TR_AF_INET_TYPES> connection_id = {};
|
||||
std::array<tau_transaction_t, NUM_TR_AF_INET_TYPES> connection_transaction_id = {};
|
||||
|
||||
std::list<tau_announce_request> announces;
|
||||
std::list<tau_scrape_request> scrapes;
|
||||
|
@ -581,13 +673,13 @@ public:
|
|||
private:
|
||||
Mediator& mediator_;
|
||||
|
||||
std::optional<std::future<MaybeSockaddr>> addr_pending_dns_;
|
||||
std::array<std::optional<std::future<MaybeSockaddr>>, NUM_TR_AF_INET_TYPES> addr_pending_dns_;
|
||||
|
||||
MaybeSockaddr addr_;
|
||||
time_t addr_expires_at_ = 0;
|
||||
std::array<MaybeSockaddr, NUM_TR_AF_INET_TYPES> addr_ = {};
|
||||
std::array<time_t, NUM_TR_AF_INET_TYPES> addr_expires_at_ = {};
|
||||
|
||||
static constexpr auto DnsRetryIntervalSecs = time_t{ 3600 };
|
||||
static constexpr auto ConnectionRequestTtl = 30;
|
||||
static constexpr auto ConnectionRequestTtl = time_t{ 30 };
|
||||
};
|
||||
|
||||
// --- SESSION
|
||||
|
@ -602,20 +694,22 @@ public:
|
|||
|
||||
void announce(tr_announce_request const& request, tr_announce_response_func on_response) override
|
||||
{
|
||||
auto* const tracker = getTrackerFromUrl(request.announce_url);
|
||||
auto* const tracker = get_tracker_from_url(request.announce_url);
|
||||
if (tracker == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Since size of IP field is only 4 bytes long, we can only announce IPv4 addresses
|
||||
tracker->announces.emplace_back(mediator_.announce_ip(), request, std::move(on_response));
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
tracker->announces.emplace_back(static_cast<tr_address_type>(ipp), mediator_.announce_ip(), request, on_response);
|
||||
}
|
||||
tracker->upkeep(false);
|
||||
}
|
||||
|
||||
void scrape(tr_scrape_request const& request, tr_scrape_response_func on_response) override
|
||||
{
|
||||
auto* const tracker = getTrackerFromUrl(request.scrape_url);
|
||||
auto* const tracker = get_tracker_from_url(request.scrape_url);
|
||||
if (tracker == nullptr)
|
||||
{
|
||||
return;
|
||||
|
@ -635,7 +729,7 @@ public:
|
|||
|
||||
// @brief process an incoming udp message if it's a tracker response.
|
||||
// @return true if msg was a tracker response; false otherwise
|
||||
bool handle_message(uint8_t const* msg, size_t msglen) override
|
||||
bool handle_message(uint8_t const* msg, size_t msglen, struct sockaddr const* from, socklen_t /*fromlen*/) override
|
||||
{
|
||||
if (msglen < sizeof(uint32_t) * 2)
|
||||
{
|
||||
|
@ -647,21 +741,24 @@ public:
|
|||
buf.add(msg, msglen);
|
||||
auto const action_id = static_cast<tau_action_t>(buf.to_uint32());
|
||||
|
||||
if (!isResponseMessage(action_id, msglen))
|
||||
if (!is_response_message(action_id, msglen))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* extract the transaction_id and look for a match */
|
||||
// extract the transaction_id and look for a match
|
||||
tau_transaction_t const transaction_id = buf.to_uint32();
|
||||
|
||||
auto const socket_address = tr_socket_address::from_sockaddr(from);
|
||||
auto const ip_protocol = socket_address ? socket_address->address().type : NUM_TR_AF_INET_TYPES;
|
||||
for (auto& tracker : trackers_)
|
||||
{
|
||||
// is it a connection response?
|
||||
if (tracker.connecting_at != 0 && transaction_id == tracker.connection_transaction_id)
|
||||
if (tr_address::is_valid(ip_protocol) && tracker.connecting_at[ip_protocol] != 0 &&
|
||||
transaction_id == tracker.connection_transaction_id[ip_protocol])
|
||||
{
|
||||
logtrace(tracker.log_name(), fmt::format("{} is my connection request!", transaction_id));
|
||||
tracker.on_connection_response(action_id, buf);
|
||||
tracker.on_connection_response(ip_protocol, action_id, buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -675,9 +772,8 @@ public:
|
|||
it != std::end(reqs))
|
||||
{
|
||||
logtrace(tracker.log_name(), fmt::format("{} is an announce request!", transaction_id));
|
||||
auto req = *it;
|
||||
it = reqs.erase(it);
|
||||
req.onResponse(action_id, buf);
|
||||
it->on_response(ip_protocol, action_id, buf);
|
||||
reqs.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -692,15 +788,14 @@ public:
|
|||
it != std::end(reqs))
|
||||
{
|
||||
logtrace(tracker.log_name(), fmt::format("{} is a scrape request!", transaction_id));
|
||||
auto req = *it;
|
||||
it = reqs.erase(it);
|
||||
req.onResponse(action_id, buf);
|
||||
it->on_response(action_id, buf);
|
||||
reqs.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no match... */
|
||||
// no match...
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -712,7 +807,7 @@ public:
|
|||
private:
|
||||
// Finds the tau_tracker struct that corresponds to this url.
|
||||
// If it doesn't exist yet, create one.
|
||||
tau_tracker* getTrackerFromUrl(tr_interned_string const announce_url)
|
||||
tau_tracker* get_tracker_from_url(tr_interned_string const announce_url)
|
||||
{
|
||||
// build a lookup key for this tracker
|
||||
auto const parsed = tr_urlParseTracker(announce_url);
|
||||
|
@ -733,12 +828,17 @@ private:
|
|||
}
|
||||
|
||||
// we don't have it -- build a new one
|
||||
auto& tracker = trackers_.emplace_back(mediator_, authority, parsed->host, tr_port::from_host(parsed->port));
|
||||
auto& tracker = trackers_.emplace_back(
|
||||
mediator_,
|
||||
authority,
|
||||
parsed->host,
|
||||
parsed->host_wo_brackets,
|
||||
tr_port::from_host(parsed->port));
|
||||
logtrace(tracker.log_name(), "New tau_tracker created");
|
||||
return &tracker;
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr bool isResponseMessage(tau_action_t action, size_t msglen) noexcept
|
||||
[[nodiscard]] static constexpr bool is_response_message(tau_action_t action, size_t msglen) noexcept
|
||||
{
|
||||
if (action == TAU_ACTION_CONNECT)
|
||||
{
|
||||
|
|
|
@ -160,7 +160,7 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
auto const [it, is_new] = scrape_info_.try_emplace(url, url, TR_MULTISCRAPE_MAX);
|
||||
auto const [it, is_new] = scrape_info_.try_emplace(url, url, TrMultiscrapeMax);
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
tr_logAddError(fmt::format(_("Unsupported URL: '{url}'"), fmt::arg("url", scrape_sv)));
|
||||
tr_logAddError(fmt::format(fmt::runtime(_("Unsupported URL: '{url}'")), fmt::arg("url", scrape_sv)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
tr_logAddWarn(fmt::format(_("Unsupported URL: '{url}'"), fmt::arg("url", announce_sv)));
|
||||
tr_logAddWarn(fmt::format(fmt::runtime(_("Unsupported URL: '{url}'")), fmt::arg("url", announce_sv)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -881,7 +881,10 @@ void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
|
|||
{
|
||||
tr_logAddErrorTier(
|
||||
tier,
|
||||
fmt::format(_("Announce error: {error} ({url})"), fmt::arg("error", err), fmt::arg("url", announce_url)));
|
||||
fmt::format(
|
||||
fmt::runtime(_("Announce error: {error} ({url})")),
|
||||
fmt::arg("error", err),
|
||||
fmt::arg("url", announce_url)));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -890,10 +893,10 @@ void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
|
|||
tr_logAddWarnTier(
|
||||
tier,
|
||||
fmt::format(
|
||||
tr_ngettext(
|
||||
fmt::runtime(tr_ngettext(
|
||||
"Announce error: {error} (Retrying in {count} second) ({url})",
|
||||
"Announce error: {error} (Retrying in {count} seconds) ({url})",
|
||||
interval),
|
||||
interval)),
|
||||
fmt::arg("error", err),
|
||||
fmt::arg("count", interval),
|
||||
fmt::arg("url", announce_url)));
|
||||
|
|
|
@ -156,7 +156,7 @@ public:
|
|||
|
||||
// @brief process an incoming udp message if it's a tracker response.
|
||||
// @return true if msg was a tracker response; false otherwise
|
||||
virtual bool handle_message(uint8_t const* msg, size_t msglen) = 0;
|
||||
virtual bool handle_message(uint8_t const* msg, size_t msglen, struct sockaddr const* from, socklen_t fromlen) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool is_idle() const noexcept = 0;
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ void save(std::string_view filename, address_range_t const* ranges, size_t n_ran
|
|||
if (!out.is_open())
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
|
@ -70,7 +70,7 @@ void save(std::string_view filename, address_range_t const* ranges, size_t n_ran
|
|||
!out.write(reinterpret_cast<char const*>(ranges), n_ranges * sizeof(*ranges)))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
|
@ -78,7 +78,8 @@ void save(std::string_view filename, address_range_t const* ranges, size_t n_ran
|
|||
else
|
||||
{
|
||||
tr_logAddInfo(fmt::format(
|
||||
tr_ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", n_ranges),
|
||||
fmt::runtime(
|
||||
tr_ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", n_ranges)),
|
||||
fmt::arg("path", tr_sys_path_basename(filename)),
|
||||
fmt::arg("count", n_ranges)));
|
||||
}
|
||||
|
@ -237,7 +238,7 @@ auto parseFile(std::string_view filename)
|
|||
if (!in.is_open())
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
|
@ -256,7 +257,7 @@ auto parseFile(std::string_view filename)
|
|||
else
|
||||
{
|
||||
// don't try to display the actual lines - it causes issues
|
||||
tr_logAddWarn(fmt::format(_("Couldn't parse line: '{line}'"), fmt::arg("line", line_number)));
|
||||
tr_logAddWarn(fmt::format(fmt::runtime(_("Couldn't parse line: '{line}'")), fmt::arg("line", line_number)));
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
|
@ -335,7 +336,7 @@ void Blocklists::Blocklist::ensureLoaded() const
|
|||
if (error)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", bin_file_),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -350,7 +351,7 @@ void Blocklists::Blocklist::ensureLoaded() const
|
|||
if (!in)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", bin_file_),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
|
@ -400,7 +401,8 @@ void Blocklists::Blocklist::ensureLoaded() const
|
|||
}
|
||||
|
||||
tr_logAddInfo(fmt::format(
|
||||
tr_ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", std::size(rules_)),
|
||||
fmt::runtime(
|
||||
tr_ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", std::size(rules_))),
|
||||
fmt::arg("path", tr_sys_path_basename(bin_file_)),
|
||||
fmt::arg("count", std::size(rules_))));
|
||||
}
|
||||
|
@ -470,7 +472,7 @@ std::optional<Blocklists::Blocklist> Blocklists::Blocklist::saveNew(
|
|||
if (error)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", src_file),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
|
|
@ -78,7 +78,7 @@ private:
|
|||
|
||||
[[nodiscard]] bool contains(tr_address const& addr) const;
|
||||
|
||||
[[nodiscard]] auto size() const
|
||||
[[nodiscard]] size_t size() const
|
||||
{
|
||||
ensureLoaded();
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ void log_ccrypto_error(CCCryptorStatus error_code, char const* file, long line)
|
|||
line,
|
||||
TR_LOG_ERROR,
|
||||
fmt::format(
|
||||
_("{crypto_library} error: {error} ({error_code})"),
|
||||
fmt::runtime(_("{crypto_library} error: {error} ({error_code})")),
|
||||
fmt::arg("crypto_library", "CCrypto"),
|
||||
fmt::arg("error", ccrypto_error_to_str(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <mbedtls/base64.h>
|
||||
|
@ -43,7 +42,7 @@ void log_mbedtls_error(int error_code, char const* file, int line)
|
|||
line,
|
||||
TR_LOG_ERROR,
|
||||
fmt::format(
|
||||
_("{crypto_library} error: {error} ({error_code})"),
|
||||
fmt::runtime(_("{crypto_library} error: {error} ({error_code})")),
|
||||
fmt::arg("crypto_library", "MbedTLS"),
|
||||
fmt::arg("error", error_message),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -118,7 +117,7 @@ void tr_sha1::clear()
|
|||
{
|
||||
mbedtls_sha1_init(&handle_);
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
#if MBEDTLS_VERSION_NUMBER < 0x03000000 && MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha1_starts_ret(&handle_);
|
||||
#else
|
||||
mbedtls_sha1_starts(&handle_);
|
||||
|
@ -132,7 +131,7 @@ void tr_sha1::add(void const* data, size_t data_length)
|
|||
return;
|
||||
}
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
#if MBEDTLS_VERSION_NUMBER < 0x03000000 && MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha1_update_ret(&handle_, static_cast<unsigned char const*>(data), data_length);
|
||||
#else
|
||||
mbedtls_sha1_update(&handle_, static_cast<unsigned char const*>(data), data_length);
|
||||
|
@ -143,7 +142,7 @@ tr_sha1_digest_t tr_sha1::finish()
|
|||
{
|
||||
auto digest = tr_sha1_digest_t{};
|
||||
auto* const digest_as_uchar = reinterpret_cast<unsigned char*>(std::data(digest));
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
#if MBEDTLS_VERSION_NUMBER < 0x03000000 && MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha1_finish_ret(&handle_, digest_as_uchar);
|
||||
#else
|
||||
mbedtls_sha1_finish(&handle_, digest_as_uchar);
|
||||
|
@ -165,10 +164,10 @@ void tr_sha256::clear()
|
|||
{
|
||||
mbedtls_sha256_init(&handle_);
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
#if MBEDTLS_VERSION_NUMBER < 0x03000000 && MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha256_starts_ret(&handle_, 0);
|
||||
#else
|
||||
mbedtls_sha256_starts(&handle_);
|
||||
mbedtls_sha256_starts(&handle_, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -179,7 +178,7 @@ void tr_sha256::add(void const* data, size_t data_length)
|
|||
return;
|
||||
}
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
#if MBEDTLS_VERSION_NUMBER < 0x03000000 && MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha256_update_ret(&handle_, static_cast<unsigned char const*>(data), data_length);
|
||||
#else
|
||||
mbedtls_sha256_update(&handle_, static_cast<unsigned char const*>(data), data_length);
|
||||
|
@ -190,7 +189,7 @@ tr_sha256_digest_t tr_sha256::finish()
|
|||
{
|
||||
auto digest = tr_sha256_digest_t{};
|
||||
auto* const digest_as_uchar = reinterpret_cast<unsigned char*>(std::data(digest));
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
#if MBEDTLS_VERSION_NUMBER < 0x03000000 && MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha256_finish_ret(&handle_, digest_as_uchar);
|
||||
#else
|
||||
mbedtls_sha256_finish(&handle_, digest_as_uchar);
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <array>
|
||||
#include <cstddef> // size_t
|
||||
#include <memory>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/err.h>
|
||||
|
@ -62,7 +61,7 @@ void log_openssl_error(char const* file, int line)
|
|||
line,
|
||||
TR_LOG_ERROR,
|
||||
fmt::format(
|
||||
_("{crypto_library} error: {error} ({error_code})"),
|
||||
fmt::runtime(_("{crypto_library} error: {error} ({error_code})")),
|
||||
fmt::arg("crypto_library", "OpenSSL"),
|
||||
fmt::arg("error", std::data(buf)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
|
@ -46,7 +45,7 @@ void log_wolfssl_error(int error_code, char const* file, int line)
|
|||
line,
|
||||
TR_LOG_ERROR,
|
||||
fmt::format(
|
||||
_("{crypto_library} error: {error} ({error_code})"),
|
||||
fmt::runtime(_("{crypto_library} error: {error} ({error_code})")),
|
||||
fmt::arg("crypto_library", "WolfSSL"),
|
||||
fmt::arg("error", wc_GetErrorString(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <cstddef> // size_t
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random> // for std::uniform_int_distribution<T>
|
||||
#include <string>
|
||||
|
|
|
@ -23,8 +23,11 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h> /* lseek(), write(), ftruncate(), pread(), pwrite(), pathconf(), etc */
|
||||
|
||||
#ifdef HAVE_XFS_XFS_H
|
||||
#ifdef HAVE_FLOCK
|
||||
#include <sys/file.h> /* flock() */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XFS_XFS_H
|
||||
#include <xfs/xfs.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -274,7 +274,7 @@ void tr_global_ip_cache::update_source_addr(tr_address_type type) noexcept
|
|||
{
|
||||
set_source_addr(*source_addr);
|
||||
tr_logAddDebug(fmt::format(
|
||||
_("Successfully updated source {protocol} address to {ip}"),
|
||||
fmt::runtime(_("Successfully updated source {protocol} address to {ip}")),
|
||||
fmt::arg("protocol", protocol),
|
||||
fmt::arg("ip", source_addr->display_name())));
|
||||
}
|
||||
|
@ -288,7 +288,8 @@ void tr_global_ip_cache::update_source_addr(tr_address_type type) noexcept
|
|||
{
|
||||
stop_timer(type); // No point in retrying
|
||||
has_ip_protocol_[type] = false;
|
||||
tr_logAddInfo(fmt::format(_("Your machine does not support {protocol}"), fmt::arg("protocol", protocol)));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Your machine does not support {protocol}")), fmt::arg("protocol", protocol)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +313,7 @@ void tr_global_ip_cache::on_response_ip_query(tr_address_type type, tr_web::Fetc
|
|||
upkeep_timers_[type]->set_interval(UpkeepInterval);
|
||||
|
||||
tr_logAddDebug(fmt::format(
|
||||
_("Successfully updated global {type} address to {ip} using {url}"),
|
||||
fmt::runtime(_("Successfully updated global {type} address to {ip} using {url}")),
|
||||
fmt::arg("type", protocol),
|
||||
fmt::arg("ip", addr->display_name()),
|
||||
fmt::arg("url", IPQueryServices[type][ix_service_[type]])));
|
||||
|
|
|
@ -26,7 +26,7 @@ class tr_recentHistory
|
|||
public:
|
||||
/**
|
||||
* @brief add a counter to the recent history object.
|
||||
* @param when the current time in sec, such as from tr_time()
|
||||
* @param now the current time in seconds, such as from tr_time()
|
||||
* @param n how many items to add to the history's counter
|
||||
*/
|
||||
constexpr void add(time_t now, SizeType n)
|
||||
|
@ -43,8 +43,8 @@ public:
|
|||
|
||||
/**
|
||||
* @brief count how many events have occurred in the last N seconds.
|
||||
* @param when the current time in sec, such as from tr_time()
|
||||
* @param seconds how many seconds to count back through.
|
||||
* @param now the current time in seconds, such as from tr_time()
|
||||
* @param age_sec how many seconds to count back through.
|
||||
*/
|
||||
[[nodiscard]] constexpr SizeType count(time_t now, unsigned int age_sec) const
|
||||
{
|
||||
|
|
|
@ -115,7 +115,7 @@ bool write_entire_buf(tr_sys_file_t const fd, uint64_t file_offset, uint8_t cons
|
|||
error.set(
|
||||
err,
|
||||
fmt::format(
|
||||
_("Couldn't get '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't get '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", tor.file_subpath(file_index)),
|
||||
fmt::arg("error", tr_strerror(err)),
|
||||
fmt::arg("error_code", err)));
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <chrono>
|
||||
#include <cstddef> // size_t
|
||||
#include <iterator> // back_insert_iterator, empty
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
|
|
@ -77,7 +77,7 @@ void walkTree(std::string_view const top, std::string_view const subpath, std::s
|
|||
if (error)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Skipping '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Skipping '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", path),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -134,7 +134,7 @@ tr_metainfo_builder::tr_metainfo_builder(std::string_view single_file_or_parent_
|
|||
: top_{ single_file_or_parent_directory }
|
||||
{
|
||||
files_ = findFiles(tr_sys_path_dirname(top_), tr_sys_path_basename(top_));
|
||||
block_info_ = tr_block_info{ files_.totalSize(), default_piece_size(files_.totalSize()) };
|
||||
block_info_ = tr_block_info{ files_.total_size(), default_piece_size(files_.total_size()) };
|
||||
}
|
||||
|
||||
bool tr_metainfo_builder::set_piece_size(uint32_t piece_size) noexcept
|
||||
|
@ -144,7 +144,7 @@ bool tr_metainfo_builder::set_piece_size(uint32_t piece_size) noexcept
|
|||
return false;
|
||||
}
|
||||
|
||||
block_info_ = tr_block_info{ files_.totalSize(), piece_size };
|
||||
block_info_ = tr_block_info{ files_.total_size(), piece_size };
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,12 +122,12 @@ public:
|
|||
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto file_count() const noexcept
|
||||
{
|
||||
return files_.fileCount();
|
||||
return files_.file_count();
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto file_size(tr_file_index_t i) const noexcept
|
||||
{
|
||||
return files_.fileSize(i);
|
||||
return files_.file_size(i);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_private() const noexcept
|
||||
|
@ -167,7 +167,7 @@ public:
|
|||
|
||||
[[nodiscard]] constexpr auto total_size() const noexcept
|
||||
{
|
||||
return files_.totalSize();
|
||||
return files_.total_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto const& webseeds() const noexcept
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// This file was generated with libtransmission/mime-types.js
|
||||
// DO NOT EDIT MANUALLY
|
||||
|
||||
// This file Copyright © Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const copyright =
|
||||
`// This file Copyright © 2021-${new Date().getFullYear()} Mnemosyne LLC.
|
||||
`// This file was generated with libtransmission/mime-types.js
|
||||
// DO NOT EDIT MANUALLY
|
||||
|
||||
// This file Copyright © Mnemosyne LLC.
|
||||
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
||||
// or any future license endorsed by Mnemosyne LLC.
|
||||
// License text can be found in the licenses/ folder.`;
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
#include <utility> // std::pair
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h> // must come before iphlpapi.h
|
||||
#include <iphlpapi.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/tcp.h> /* TCP_CONGESTION */
|
||||
#endif
|
||||
|
||||
|
@ -185,7 +189,7 @@ tr_socket_t createSocket(int domain, int type)
|
|||
if (sockerrno != EAFNOSUPPORT)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't create socket: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't create socket: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_net_strerror(sockerrno)),
|
||||
fmt::arg("error_code", sockerrno)));
|
||||
}
|
||||
|
@ -262,7 +266,7 @@ tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_socket_address const
|
|||
if (bind(s, reinterpret_cast<sockaddr const*>(&source_sock), sourcelen) == -1)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't set source address {address} on {socket}: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't set source address {address} on {socket}: {error} ({error_code})")),
|
||||
fmt::arg("address", source_addr.display_name()),
|
||||
fmt::arg("socket", s),
|
||||
fmt::arg("error", tr_net_strerror(sockerrno)),
|
||||
|
@ -281,7 +285,7 @@ tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_socket_address const
|
|||
(tmperrno != ECONNREFUSED && tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr.is_ipv4())
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't connect socket {socket} to {address}:{port}: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't connect socket {socket} to {address}:{port}: {error} ({error_code})")),
|
||||
fmt::arg("socket", s),
|
||||
fmt::arg("address", addr.display_name()),
|
||||
fmt::arg("port", port.host()),
|
||||
|
@ -339,9 +343,10 @@ tr_socket_t tr_netBindTCPImpl(tr_address const& addr, tr_port port, bool suppres
|
|||
if (!suppress_msgs)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
err == EADDRINUSE ?
|
||||
_("Couldn't bind port {port} on {address}: {error} ({error_code}) -- Is another copy of Transmission already running?") :
|
||||
_("Couldn't bind port {port} on {address}: {error} ({error_code})"),
|
||||
fmt::runtime(
|
||||
err == EADDRINUSE ?
|
||||
_("Couldn't bind port {port} on {address}: {error} ({error_code}) -- Is another copy of Transmission already running?") :
|
||||
_("Couldn't bind port {port} on {address}: {error} ({error_code})")),
|
||||
fmt::arg("address", addr.display_name()),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("error", tr_net_strerror(err)),
|
||||
|
@ -429,16 +434,6 @@ namespace
|
|||
namespace is_valid_for_peers_helpers
|
||||
{
|
||||
|
||||
[[nodiscard]] constexpr auto is_ipv4_mapped_address(tr_address const& addr)
|
||||
{
|
||||
return addr.is_ipv6() && IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_ipv6_link_local_address(tr_address const& addr)
|
||||
{
|
||||
return addr.is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr.addr.addr6);
|
||||
}
|
||||
|
||||
/* isMartianAddr was written by Juliusz Chroboczek,
|
||||
and is covered under the same license as third-party/dht/dht.c. */
|
||||
[[nodiscard]] auto is_martian_addr(tr_address const& addr, tr_peer_from from)
|
||||
|
@ -522,12 +517,16 @@ std::optional<tr_address> tr_address::from_string(std::string_view address_sv)
|
|||
std::string_view tr_address::display_name(char* out, size_t outlen) const
|
||||
{
|
||||
TR_ASSERT(is_valid());
|
||||
return evutil_inet_ntop(tr_ip_protocol_to_af(type), &addr, out, outlen);
|
||||
if (auto* name = evutil_inet_ntop(tr_ip_protocol_to_af(type), &addr, out, outlen))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
return "Invalid address"sv;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string tr_address::display_name() const
|
||||
{
|
||||
auto buf = std::array<char, INET6_ADDRSTRLEN>{};
|
||||
auto buf = std::array<char, std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)>{};
|
||||
return std::string{ display_name(std::data(buf), std::size(buf)) };
|
||||
}
|
||||
|
||||
|
@ -556,6 +555,99 @@ std::pair<tr_address, std::byte const*> tr_address::from_compact_ipv6(std::byte
|
|||
return { address, compact };
|
||||
}
|
||||
|
||||
std::optional<unsigned> tr_address::to_interface_index() const noexcept
|
||||
{
|
||||
if (!is_valid())
|
||||
{
|
||||
tr_logAddDebug("Invalid target address to find interface index");
|
||||
return {};
|
||||
}
|
||||
|
||||
tr_logAddDebug(fmt::format("Find interface index for {}", display_name()));
|
||||
|
||||
#ifdef _WIN32
|
||||
auto p_addresses = std::unique_ptr<void, void (*)(void*)>{ nullptr, operator delete };
|
||||
|
||||
// The recommended method of calling the GetAdaptersAddresses function is to
|
||||
// pre-allocate a 15KB working buffer pointed to by the AdapterAddresses parameter.
|
||||
// On typical computers, this dramatically reduces the chances that the
|
||||
// GetAdaptersAddresses function returns ERROR_BUFFER_OVERFLOW, which would require
|
||||
// calling GetAdaptersAddresses function multiple times.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
|
||||
for (auto p_addresses_size = ULONG{ 15000 } /* 15KB */;;)
|
||||
{
|
||||
p_addresses.reset(operator new(p_addresses_size, std::nothrow));
|
||||
if (!p_addresses)
|
||||
{
|
||||
tr_logAddDebug("Could not allocate memory for interface list");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (auto ret = GetAdaptersAddresses(
|
||||
AF_UNSPEC,
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
nullptr,
|
||||
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(p_addresses.get()),
|
||||
&p_addresses_size);
|
||||
ret != ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
if (ret != ERROR_SUCCESS)
|
||||
{
|
||||
tr_logAddDebug(fmt::format("Failed to retrieve interface list: {} ({})", ret, tr_win32_format_message(ret)));
|
||||
return {};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const* cur = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(p_addresses.get()); cur != nullptr; cur = cur->Next)
|
||||
{
|
||||
if (cur->OperStatus != IfOperStatusUp)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto const* sa_p = cur->FirstUnicastAddress; sa_p != nullptr; sa_p = sa_p->Next)
|
||||
{
|
||||
if (auto if_addr = tr_socket_address::from_sockaddr(sa_p->Address.lpSockaddr);
|
||||
if_addr && if_addr->address() == *this)
|
||||
{
|
||||
auto const ret = type == TR_AF_INET ? cur->IfIndex : cur->Ipv6IfIndex;
|
||||
tr_logAddDebug(fmt::format("Found interface index for {}: {}", display_name(), ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
struct ifaddrs* ifa = nullptr;
|
||||
if (getifaddrs(&ifa) != 0)
|
||||
{
|
||||
auto err = errno;
|
||||
tr_logAddDebug(fmt::format("Failed to retrieve interface list: {} ({})", err, tr_strerror(err)));
|
||||
return {};
|
||||
}
|
||||
auto const ifa_uniq = std::unique_ptr<ifaddrs, void (*)(struct ifaddrs*)>{ ifa, freeifaddrs };
|
||||
|
||||
for (; ifa != nullptr; ifa = ifa->ifa_next)
|
||||
{
|
||||
if (ifa->ifa_addr == nullptr || (ifa->ifa_flags & IFF_UP) == 0U)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto if_addr = tr_socket_address::from_sockaddr(ifa->ifa_addr); if_addr && if_addr->address() == *this)
|
||||
{
|
||||
auto const ret = if_nametoindex(ifa->ifa_name);
|
||||
tr_logAddDebug(fmt::format("Found interface index for {}: {}", display_name(), ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
tr_logAddDebug(fmt::format("Could not find interface index for {}", display_name()));
|
||||
return {};
|
||||
}
|
||||
|
||||
int tr_address::compare(tr_address const& that) const noexcept // <=>
|
||||
{
|
||||
// IPv6 addresses are always "greater than" IPv4
|
||||
|
@ -707,17 +799,30 @@ int tr_address::compare(tr_address const& that) const noexcept // <=>
|
|||
|
||||
std::string tr_socket_address::display_name(tr_address const& address, tr_port port) noexcept
|
||||
{
|
||||
return fmt::format(address.is_ipv6() ? "[{:s}]:{:d}" : "{:s}:{:d}", address.display_name(), port.host());
|
||||
return fmt::format(fmt::runtime(address.is_ipv6() ? "[{:s}]:{:d}" : "{:s}:{:d}"), address.display_name(), port.host());
|
||||
}
|
||||
|
||||
bool tr_socket_address::is_valid_for_peers(tr_peer_from from) const noexcept
|
||||
{
|
||||
using namespace is_valid_for_peers_helpers;
|
||||
|
||||
return is_valid() && !std::empty(port_) && !is_ipv6_link_local_address(address_) && !is_ipv4_mapped_address(address_) &&
|
||||
return is_valid() && !std::empty(port_) && !address_.is_ipv6_link_local_address() && !address_.is_ipv4_mapped_address() &&
|
||||
!is_martian_addr(address_, from);
|
||||
}
|
||||
|
||||
std::optional<tr_socket_address> tr_socket_address::from_string(std::string_view sockaddr_sv)
|
||||
{
|
||||
auto ss = sockaddr_storage{};
|
||||
auto sslen = int{ sizeof(ss) };
|
||||
if (evutil_parse_sockaddr_port(tr_strbuf<char, TR_ADDRSTRLEN>{ sockaddr_sv }, reinterpret_cast<sockaddr*>(&ss), &sslen) !=
|
||||
0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return from_sockaddr(reinterpret_cast<struct sockaddr const*>(&ss));
|
||||
}
|
||||
|
||||
std::optional<tr_socket_address> tr_socket_address::from_sockaddr(struct sockaddr const* from)
|
||||
{
|
||||
if (from == nullptr)
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <array>
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint16_t, uint32_t, uint8_t
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -146,9 +145,10 @@ private:
|
|||
|
||||
enum tr_address_type : uint8_t
|
||||
{
|
||||
TR_AF_INET,
|
||||
TR_AF_INET = 0,
|
||||
TR_AF_INET6,
|
||||
NUM_TR_AF_INET_TYPES
|
||||
NUM_TR_AF_INET_TYPES,
|
||||
TR_AF_UNSPEC = NUM_TR_AF_INET_TYPES
|
||||
};
|
||||
|
||||
std::string_view tr_ip_protocol_to_sv(tr_address_type type);
|
||||
|
@ -161,11 +161,11 @@ struct tr_address
|
|||
[[nodiscard]] static std::pair<tr_address, std::byte const*> from_compact_ipv4(std::byte const* compact) noexcept;
|
||||
[[nodiscard]] static std::pair<tr_address, std::byte const*> from_compact_ipv6(std::byte const* compact) noexcept;
|
||||
|
||||
// write the text form of the address, e.g. inet_ntop()
|
||||
// --- write the text form of the address, e.g. inet_ntop()
|
||||
std::string_view display_name(char* out, size_t outlen) const;
|
||||
[[nodiscard]] std::string display_name() const;
|
||||
|
||||
///
|
||||
// ---
|
||||
|
||||
[[nodiscard]] constexpr auto is_ipv4() const noexcept
|
||||
{
|
||||
|
@ -177,7 +177,7 @@ struct tr_address
|
|||
return type == TR_AF_INET6;
|
||||
}
|
||||
|
||||
/// bt protocol compact form
|
||||
// --- bt protocol compact form
|
||||
|
||||
// compact addr only -- used e.g. as `yourip` value in extension protocol handshake
|
||||
|
||||
|
@ -208,7 +208,11 @@ struct tr_address
|
|||
}
|
||||
}
|
||||
|
||||
// comparisons
|
||||
// ---
|
||||
|
||||
[[nodiscard]] std::optional<unsigned> to_interface_index() const noexcept;
|
||||
|
||||
// --- comparisons
|
||||
|
||||
[[nodiscard]] int compare(tr_address const& that) const noexcept;
|
||||
|
||||
|
@ -232,10 +236,20 @@ struct tr_address
|
|||
return this->compare(that) > 0;
|
||||
}
|
||||
|
||||
//
|
||||
// ---
|
||||
|
||||
[[nodiscard]] bool is_global_unicast_address() const noexcept;
|
||||
|
||||
[[nodiscard]] constexpr bool is_ipv4_mapped_address() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_V4MAPPED(&addr.addr6);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool is_ipv6_link_local_address() const noexcept
|
||||
{
|
||||
return is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr.addr6);
|
||||
}
|
||||
|
||||
tr_address_type type = NUM_TR_AF_INET_TYPES;
|
||||
union
|
||||
{
|
||||
|
@ -374,6 +388,7 @@ struct tr_socket_address
|
|||
|
||||
// --- sockaddr helpers
|
||||
|
||||
[[nodiscard]] static std::optional<tr_socket_address> from_string(std::string_view sockaddr_sv);
|
||||
[[nodiscard]] static std::optional<tr_socket_address> from_sockaddr(sockaddr const*);
|
||||
[[nodiscard]] static std::pair<sockaddr_storage, socklen_t> to_sockaddr(tr_address const& addr, tr_port port) noexcept;
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ std::optional<tr_sys_file_t> tr_open_files::get(
|
|||
if (!tr_sys_dir_create(dir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error))
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't create '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't create '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", dir),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -189,7 +189,7 @@ std::optional<tr_sys_file_t> tr_open_files::get(
|
|||
if (!is_open(fd))
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't open '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't open '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -217,7 +217,7 @@ std::optional<tr_sys_file_t> tr_open_files::get(
|
|||
if (!success)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't preallocate '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't preallocate '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -236,7 +236,7 @@ std::optional<tr_sys_file_t> tr_open_files::get(
|
|||
if (resize_needed && !tr_sys_file_truncate(fd, file_size, &error))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't truncate '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't truncate '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
|
|
@ -70,6 +70,13 @@ size_t get_desired_output_buffer_size(tr_peerIo const* io, uint64_t now)
|
|||
auto const current_speed = io->get_piece_speed(now, TR_UP);
|
||||
return std::max(Floor, current_speed.base_quantity() * PeriodSecs);
|
||||
}
|
||||
|
||||
void log_peer_io_bandwidth(tr_peerIo const& peer_io, tr_bandwidth* const parent)
|
||||
{
|
||||
tr_logAddTraceIo(
|
||||
&peer_io,
|
||||
fmt::format("bandwidth is {}; its parent is {}", fmt::ptr(&peer_io.bandwidth()), fmt::ptr(parent)));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// ---
|
||||
|
@ -100,7 +107,6 @@ std::shared_ptr<tr_peerIo> tr_peerIo::create(
|
|||
|
||||
auto io = std::make_shared<tr_peerIo>(session, info_hash, is_incoming, is_seed, parent);
|
||||
io->bandwidth().set_peer(io);
|
||||
tr_logAddTraceIo(io, fmt::format("bandwidth is {}; its parent is {}", fmt::ptr(&io->bandwidth()), fmt::ptr(parent)));
|
||||
return io;
|
||||
}
|
||||
|
||||
|
@ -110,6 +116,7 @@ std::shared_ptr<tr_peerIo> tr_peerIo::new_incoming(tr_session* session, tr_bandw
|
|||
|
||||
auto peer_io = tr_peerIo::create(session, parent, nullptr, true, false);
|
||||
peer_io->set_socket(std::move(socket));
|
||||
log_peer_io_bandwidth(*peer_io, parent);
|
||||
return peer_io;
|
||||
}
|
||||
|
||||
|
@ -167,12 +174,14 @@ std::shared_ptr<tr_peerIo> tr_peerIo::new_outgoing(
|
|||
|
||||
if (func.at(preferred)())
|
||||
{
|
||||
log_peer_io_bandwidth(*peer_io, parent);
|
||||
return peer_io;
|
||||
}
|
||||
for (preferred_key_t i = 0U; i < TR_NUM_PREFERRED_TRANSPORT; ++i)
|
||||
{
|
||||
if (i != preferred && func.at(i)())
|
||||
{
|
||||
log_peer_io_bandwidth(*peer_io, parent);
|
||||
return peer_io;
|
||||
}
|
||||
}
|
||||
|
@ -670,7 +679,7 @@ void tr_peerIo::on_utp_state_change(int state)
|
|||
}
|
||||
else
|
||||
{
|
||||
tr_logAddErrorIo(this, fmt::format(_("Unknown state: {state}"), fmt::arg("state", state)));
|
||||
tr_logAddErrorIo(this, fmt::format(fmt::runtime(_("Unknown state: {state}")), fmt::arg("state", state)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,15 +11,16 @@
|
|||
#include <cstddef> // std::byte
|
||||
#include <cstdint>
|
||||
#include <ctime> // time_t
|
||||
#include <functional>
|
||||
#include <iterator> // std::back_inserter
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <tuple> // std::tie
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <small/map.hpp>
|
||||
#include <small/vector.hpp>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
@ -270,7 +271,9 @@ constexpr struct
|
|||
return compare(a, b) < 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(tr_peer_info const* a, tr_peer_info const* b) const noexcept
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr std::enable_if_t<std::is_same_v<std::decay_t<decltype(*std::declval<T>())>, tr_peer_info>, bool>
|
||||
operator()(T const& a, T const& b) const noexcept
|
||||
{
|
||||
return compare(*a, *b) < 0;
|
||||
}
|
||||
|
@ -283,7 +286,7 @@ class tr_swarm
|
|||
{
|
||||
public:
|
||||
using Peers = std::vector<tr_peerMsgs*>;
|
||||
using Pool = std::unordered_map<tr_socket_address, tr_peer_info>;
|
||||
using Pool = small::map<tr_socket_address, std::shared_ptr<tr_peer_info>>;
|
||||
|
||||
class WishlistMediator final : public Wishlist::Mediator
|
||||
{
|
||||
|
@ -374,24 +377,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void remove_inactive_peer_info() noexcept
|
||||
{
|
||||
auto const now = tr_time();
|
||||
for (auto iter = std::begin(connectable_pool), end = std::end(connectable_pool); iter != end;)
|
||||
{
|
||||
auto& [socket_address, peer_info] = *iter;
|
||||
if (peer_info.is_inactive(now))
|
||||
{
|
||||
--stats.known_peer_from_count[peer_info.from_first()];
|
||||
iter = connectable_pool.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint16_t count_active_webseeds(uint64_t now) const noexcept
|
||||
{
|
||||
if (!tor->is_running() || tor->is_done())
|
||||
|
@ -416,10 +401,8 @@ public:
|
|||
|
||||
peer_disconnect.emit(tor, peer->has());
|
||||
|
||||
auto* const peer_info = peer->peer_info;
|
||||
auto const socket_address = peer->socket_address();
|
||||
[[maybe_unused]] auto const is_incoming = peer->is_incoming_connection();
|
||||
TR_ASSERT(peer_info != nullptr);
|
||||
auto const& peer_info = peer->peer_info;
|
||||
TR_ASSERT(peer_info);
|
||||
|
||||
--stats.peer_count;
|
||||
--stats.peer_from_count[peer_info->from_first()];
|
||||
|
@ -431,17 +414,6 @@ public:
|
|||
}
|
||||
|
||||
delete peer;
|
||||
|
||||
if (std::empty(peer_info->listen_port())) // is not connectable
|
||||
{
|
||||
TR_ASSERT(is_incoming);
|
||||
[[maybe_unused]] auto const count = incoming_pool.erase(socket_address);
|
||||
TR_ASSERT(count != 0U);
|
||||
}
|
||||
else
|
||||
{
|
||||
graveyard_pool.erase(peer_info->listen_socket_address());
|
||||
}
|
||||
}
|
||||
|
||||
void remove_all_peers()
|
||||
|
@ -475,37 +447,41 @@ public:
|
|||
pool_is_all_seeds_ = std::all_of(
|
||||
std::begin(connectable_pool),
|
||||
std::end(connectable_pool),
|
||||
[](auto const& key_val) { return key_val.second.is_seed(); });
|
||||
[](auto const& key_val) { return key_val.second->is_seed(); });
|
||||
}
|
||||
|
||||
return *pool_is_all_seeds_;
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_peer_info* get_existing_peer_info(tr_socket_address const& socket_address) noexcept
|
||||
[[nodiscard]] std::shared_ptr<tr_peer_info> get_existing_peer_info(tr_socket_address const& socket_address) const noexcept
|
||||
{
|
||||
auto&& it = connectable_pool.find(socket_address);
|
||||
return it != connectable_pool.end() ? &it->second : nullptr;
|
||||
if (auto it = connectable_pool.find(socket_address); it != std::end(connectable_pool))
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
tr_peer_info& ensure_info_exists(
|
||||
std::shared_ptr<tr_peer_info> ensure_info_exists(
|
||||
tr_socket_address const& socket_address,
|
||||
uint8_t const flags,
|
||||
tr_peer_from const from,
|
||||
bool is_connectable)
|
||||
tr_peer_from const from)
|
||||
{
|
||||
TR_ASSERT(socket_address.is_valid());
|
||||
TR_ASSERT(from < TR_PEER_FROM__MAX);
|
||||
|
||||
auto&& [it, is_new] = is_connectable ? connectable_pool.try_emplace(socket_address, socket_address, flags, from) :
|
||||
incoming_pool.try_emplace(socket_address, socket_address.address(), flags, from);
|
||||
auto& peer_info = it->second;
|
||||
if (!is_new)
|
||||
auto peer_info = get_existing_peer_info(socket_address);
|
||||
if (peer_info)
|
||||
{
|
||||
peer_info.found_at(from);
|
||||
peer_info.set_pex_flags(flags);
|
||||
peer_info->found_at(from);
|
||||
peer_info->set_pex_flags(flags);
|
||||
}
|
||||
else if (is_connectable)
|
||||
else
|
||||
{
|
||||
peer_info = connectable_pool
|
||||
.try_emplace(socket_address, std::make_shared<tr_peer_info>(socket_address, flags, from))
|
||||
.first->second;
|
||||
++stats.known_peer_from_count[from];
|
||||
}
|
||||
|
||||
|
@ -568,19 +544,13 @@ public:
|
|||
break;
|
||||
|
||||
case tr_peer_event::Type::ClientGotPort:
|
||||
if (std::empty(event.port))
|
||||
// We have 2 cases:
|
||||
// 1. We don't know the listening port of this peer (i.e. incoming connection and first time ClientGotPort)
|
||||
// 2. We got a new listening port from a known peer
|
||||
if (auto const& info = msgs->peer_info;
|
||||
!std::empty(event.port) && info && (std::empty(info->listen_port()) || info->listen_port() != event.port))
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
// If we don't know the listening port of this peer (i.e. incoming connection and first time ClientGotPort)
|
||||
else if (auto const& info = *msgs->peer_info; std::empty(info.listen_port()))
|
||||
{
|
||||
s->on_got_port(msgs, event, false);
|
||||
}
|
||||
// If we got a new listening port from a known connectable peer
|
||||
else if (info.listen_port() != event.port)
|
||||
{
|
||||
s->on_got_port(msgs, event, true);
|
||||
s->on_got_port(msgs, event);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -622,9 +592,6 @@ public:
|
|||
|
||||
std::unique_ptr<Wishlist> wishlist;
|
||||
|
||||
// tr_peerMsgs hold pointers to the items in these containers,
|
||||
// therefore references to elements within cannot invalidate
|
||||
Pool incoming_pool;
|
||||
Pool connectable_pool;
|
||||
|
||||
tr_peerMsgs* optimistic = nullptr; /* the optimistic peer, or nullptr if none */
|
||||
|
@ -672,9 +639,10 @@ private:
|
|||
|
||||
is_running = false;
|
||||
remove_all_peers();
|
||||
wishlist.reset();
|
||||
for (auto& [sockaddr, peer_info] : connectable_pool)
|
||||
{
|
||||
peer_info.destroy_handshake();
|
||||
peer_info->destroy_handshake();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -717,9 +685,9 @@ private:
|
|||
{
|
||||
auto const lock = unique_lock();
|
||||
|
||||
for (auto& [socket_address, atom] : connectable_pool)
|
||||
for (auto const& [socket_address, peer_info] : connectable_pool)
|
||||
{
|
||||
mark_peer_as_seed(atom);
|
||||
mark_peer_as_seed(*peer_info);
|
||||
}
|
||||
|
||||
mark_all_seeds_flag_dirty();
|
||||
|
@ -862,89 +830,75 @@ private:
|
|||
tor->session->add_downloaded(sent_length);
|
||||
}
|
||||
|
||||
void on_got_port(tr_peerMsgs* const msgs, tr_peer_event const& event, bool was_connectable)
|
||||
void on_got_port(tr_peerMsgs* const msgs, tr_peer_event const& event)
|
||||
{
|
||||
auto& info_this = *msgs->peer_info;
|
||||
TR_ASSERT(info_this.is_connected());
|
||||
TR_ASSERT(was_connectable != std::empty(info_this.listen_port()));
|
||||
auto info_this = msgs->peer_info;
|
||||
TR_ASSERT(info_this->is_connected());
|
||||
TR_ASSERT(info_this->listen_port() != event.port);
|
||||
|
||||
// If we already know about this peer, merge the info objects without invalidating references
|
||||
if (auto it_that = connectable_pool.find({ info_this.listen_address(), event.port });
|
||||
// we already know about this peer
|
||||
if (auto it_that = connectable_pool.find({ info_this->listen_address(), event.port });
|
||||
it_that != std::end(connectable_pool))
|
||||
{
|
||||
auto& info_that = it_that->second;
|
||||
TR_ASSERT(it_that->first == info_that.listen_socket_address());
|
||||
TR_ASSERT(it_that->first.address() == info_this.listen_address());
|
||||
TR_ASSERT(it_that->first.port() != info_this.listen_port());
|
||||
auto const info_that = it_that->second;
|
||||
TR_ASSERT(it_that->first == info_that->listen_socket_address());
|
||||
TR_ASSERT(it_that->first.address() == info_this->listen_address());
|
||||
TR_ASSERT(it_that->first.port() != info_this->listen_port());
|
||||
|
||||
// If there is an existing connection to this peer, keep the better one
|
||||
if (info_that.is_connected() && on_got_port_duplicate_connection(msgs, it_that, was_connectable))
|
||||
// if there is an existing connection to this peer, keep the better one
|
||||
if (info_that->is_connected() && on_got_port_duplicate_connection(msgs, info_that))
|
||||
{
|
||||
return;
|
||||
goto EXIT; // NOLINT cppcoreguidelines-avoid-goto
|
||||
}
|
||||
|
||||
info_this.merge(info_that);
|
||||
auto from = info_that.from_first();
|
||||
stats.known_peer_from_count[from] -= connectable_pool.erase(info_that.listen_socket_address());
|
||||
// merge the peer info objects
|
||||
info_this->merge(*info_that);
|
||||
|
||||
// info_that will be replaced by info_this later, so decrement stat
|
||||
--stats.known_peer_from_count[info_that->from_first()];
|
||||
}
|
||||
else if (!was_connectable)
|
||||
// we are going to insert a brand-new peer info object to the pool
|
||||
else if (std::empty(info_this->listen_port()))
|
||||
{
|
||||
info_this.set_connectable();
|
||||
info_this->set_connectable();
|
||||
}
|
||||
|
||||
auto nh = was_connectable ? connectable_pool.extract(info_this.listen_socket_address()) :
|
||||
incoming_pool.extract(msgs->socket_address());
|
||||
TR_ASSERT(!std::empty(nh));
|
||||
if (was_connectable)
|
||||
{
|
||||
TR_ASSERT(nh.key() == nh.mapped().listen_socket_address());
|
||||
}
|
||||
else
|
||||
{
|
||||
++stats.known_peer_from_count[nh.mapped().from_first()];
|
||||
TR_ASSERT(nh.key().address() == nh.mapped().listen_address());
|
||||
}
|
||||
nh.key().port_ = event.port;
|
||||
[[maybe_unused]] auto const inserted = connectable_pool.insert(std::move(nh)).inserted;
|
||||
TR_ASSERT(inserted);
|
||||
info_this.set_listen_port(event.port);
|
||||
// erase the old peer info entry
|
||||
stats.known_peer_from_count[info_this->from_first()] -= connectable_pool.erase(info_this->listen_socket_address());
|
||||
|
||||
// set new listen port
|
||||
info_this->set_listen_port(event.port);
|
||||
|
||||
// insert or replace the peer info ptr at the target location
|
||||
++stats.known_peer_from_count[info_this->from_first()];
|
||||
connectable_pool.insert_or_assign(info_this->listen_socket_address(), std::move(info_this));
|
||||
|
||||
EXIT:
|
||||
mark_all_seeds_flag_dirty();
|
||||
}
|
||||
|
||||
bool on_got_port_duplicate_connection(tr_peerMsgs* const msgs, Pool::iterator& it_that, bool was_connectable)
|
||||
bool on_got_port_duplicate_connection(tr_peerMsgs* const msgs, std::shared_ptr<tr_peer_info> info_that)
|
||||
{
|
||||
auto& info_this = *msgs->peer_info;
|
||||
auto& info_that = it_that->second;
|
||||
auto const info_this = msgs->peer_info;
|
||||
|
||||
TR_ASSERT(info_that.is_connected());
|
||||
TR_ASSERT(info_that->is_connected());
|
||||
|
||||
if (CompareAtomsByUsefulness(info_this, info_that))
|
||||
if (CompareAtomsByUsefulness(*info_this, *info_that))
|
||||
{
|
||||
auto it = std::find_if(
|
||||
auto const it = std::find_if(
|
||||
std::begin(peers),
|
||||
std::end(peers),
|
||||
[&info_that](tr_peerMsgs const* const peer) { return peer->peer_info == &info_that; });
|
||||
[&info_that](tr_peerMsgs const* const peer) { return peer->peer_info == info_that; });
|
||||
TR_ASSERT(it != std::end(peers));
|
||||
(*it)->do_purge = true;
|
||||
|
||||
--stats.known_peer_from_count[info_that.from_first()];
|
||||
// Note that it_that is invalid after this point
|
||||
graveyard_pool.insert(connectable_pool.extract(it_that));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
info_that.merge(info_this);
|
||||
info_that->merge(*info_this);
|
||||
msgs->do_purge = true;
|
||||
stats.known_peer_from_count[info_this->from_first()] -= connectable_pool.erase(info_this->listen_socket_address());
|
||||
|
||||
if (was_connectable)
|
||||
{
|
||||
--stats.known_peer_from_count[info_this.from_first()];
|
||||
graveyard_pool.insert(connectable_pool.extract(info_this.listen_socket_address()));
|
||||
}
|
||||
|
||||
mark_all_seeds_flag_dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -958,10 +912,6 @@ private:
|
|||
|
||||
std::array<libtransmission::ObserverTag, 8> const tags_;
|
||||
|
||||
// tr_peerMsgs hold pointers to the items in these containers,
|
||||
// therefore references to elements within cannot invalidate
|
||||
Pool graveyard_pool;
|
||||
|
||||
mutable std::optional<bool> pool_is_all_seeds_;
|
||||
|
||||
bool is_endgame_ = false;
|
||||
|
@ -1075,6 +1025,7 @@ struct tr_peerMgr
|
|||
{
|
||||
private:
|
||||
static auto constexpr BandwidthTimerPeriod = 500ms;
|
||||
static auto constexpr PeerInfoPeriod = 1min;
|
||||
static auto constexpr RechokePeriod = 10s;
|
||||
static auto constexpr RefillUpkeepPeriod = 10s;
|
||||
|
||||
|
@ -1109,11 +1060,13 @@ public:
|
|||
, blocklists_{ blocklist }
|
||||
, handshake_mediator_{ *session, timer_maker, torrents }
|
||||
, bandwidth_timer_{ timer_maker.create([this]() { bandwidth_pulse(); }) }
|
||||
, peer_info_timer_{ timer_maker.create([this]() { peer_info_pulse(); }) }
|
||||
, rechoke_timer_{ timer_maker.create([this]() { rechoke_pulse_marshall(); }) }
|
||||
, refill_upkeep_timer_{ timer_maker.create([this]() { refill_upkeep(); }) }
|
||||
, blocklists_tag_{ blocklist.observe_changes([this]() { on_blocklists_changed(); }) }
|
||||
{
|
||||
bandwidth_timer_->start_repeating(BandwidthTimerPeriod);
|
||||
peer_info_timer_->start_repeating(PeerInfoPeriod);
|
||||
rechoke_timer_->start_repeating(RechokePeriod);
|
||||
refill_upkeep_timer_->start_repeating(RefillUpkeepPeriod);
|
||||
}
|
||||
|
@ -1155,6 +1108,7 @@ public:
|
|||
private:
|
||||
void bandwidth_pulse();
|
||||
void make_new_peer_connections();
|
||||
void peer_info_pulse();
|
||||
void rechoke_pulse() const;
|
||||
void reconnect_pulse();
|
||||
void refill_upkeep() const;
|
||||
|
@ -1171,12 +1125,14 @@ private:
|
|||
since the blocklist has changed, erase that cached value */
|
||||
for (auto* const tor : torrents_)
|
||||
{
|
||||
for (auto& pool : { std::ref(tor->swarm->connectable_pool), std::ref(tor->swarm->incoming_pool) })
|
||||
for (auto const& [socket_address, peer_info] : tor->swarm->connectable_pool)
|
||||
{
|
||||
for (auto& [socket_address, atom] : pool.get())
|
||||
{
|
||||
atom.set_blocklisted_dirty();
|
||||
}
|
||||
peer_info->set_blocklisted_dirty();
|
||||
}
|
||||
|
||||
for (auto* const peer : tor->swarm->peers)
|
||||
{
|
||||
peer->peer_info->set_blocklisted_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1184,6 +1140,7 @@ private:
|
|||
OutboundCandidates outbound_candidates_;
|
||||
|
||||
std::unique_ptr<libtransmission::Timer> const bandwidth_timer_;
|
||||
std::unique_ptr<libtransmission::Timer> const peer_info_timer_;
|
||||
std::unique_ptr<libtransmission::Timer> const rechoke_timer_;
|
||||
std::unique_ptr<libtransmission::Timer> const refill_upkeep_timer_;
|
||||
|
||||
|
@ -1283,7 +1240,6 @@ void tr_peerMgr::refill_upkeep() const
|
|||
for (auto* const tor : torrents_)
|
||||
{
|
||||
tor->swarm->cancel_old_requests();
|
||||
tor->swarm->remove_inactive_peer_info();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1291,22 +1247,26 @@ namespace
|
|||
{
|
||||
namespace handshake_helpers
|
||||
{
|
||||
void create_bit_torrent_peer(tr_torrent& tor, std::shared_ptr<tr_peerIo> io, tr_peer_info* peer_info, tr_interned_string client)
|
||||
void create_bit_torrent_peer(
|
||||
tr_torrent& tor,
|
||||
std::shared_ptr<tr_peerIo> io,
|
||||
std::shared_ptr<tr_peer_info> peer_info,
|
||||
tr_interned_string client)
|
||||
{
|
||||
TR_ASSERT(peer_info != nullptr);
|
||||
TR_ASSERT(peer_info);
|
||||
TR_ASSERT(tor.swarm != nullptr);
|
||||
|
||||
tr_swarm* swarm = tor.swarm;
|
||||
|
||||
auto* peer = tr_peerMsgs::create(tor, peer_info, std::move(io), client, &tr_swarm::peer_callback_bt, swarm);
|
||||
|
||||
swarm->peers.push_back(peer);
|
||||
auto* const
|
||||
msgs = tr_peerMsgs::create(tor, std::move(peer_info), std::move(io), client, &tr_swarm::peer_callback_bt, swarm);
|
||||
swarm->peers.push_back(msgs);
|
||||
|
||||
++swarm->stats.peer_count;
|
||||
++swarm->stats.peer_from_count[peer_info->from_first()];
|
||||
++swarm->stats.peer_from_count[msgs->peer_info->from_first()];
|
||||
|
||||
TR_ASSERT(swarm->stats.peer_count == swarm->peerCount());
|
||||
TR_ASSERT(swarm->stats.peer_from_count[peer_info->from_first()] <= swarm->stats.peer_count);
|
||||
TR_ASSERT(swarm->stats.peer_from_count[msgs->peer_info->from_first()] <= swarm->stats.peer_count);
|
||||
}
|
||||
|
||||
/* FIXME: this is kind of a mess. */
|
||||
|
@ -1317,20 +1277,20 @@ void create_bit_torrent_peer(tr_torrent& tor, std::shared_ptr<tr_peerIo> io, tr_
|
|||
TR_ASSERT(result.io != nullptr);
|
||||
auto const& socket_address = result.io->socket_address();
|
||||
auto* const swarm = manager->get_existing_swarm(result.io->torrent_hash());
|
||||
auto* info = swarm != nullptr ? swarm->get_existing_peer_info(socket_address) : nullptr;
|
||||
auto info = swarm != nullptr ? swarm->get_existing_peer_info(socket_address) : std::shared_ptr<tr_peer_info>{};
|
||||
|
||||
if (result.io->is_incoming())
|
||||
{
|
||||
manager->incoming_handshakes.erase(socket_address);
|
||||
}
|
||||
else if (info != nullptr)
|
||||
else if (info)
|
||||
{
|
||||
info->destroy_handshake();
|
||||
}
|
||||
|
||||
if (!result.is_connected || swarm == nullptr || !swarm->is_running)
|
||||
{
|
||||
if (info != nullptr && !info->is_connected())
|
||||
if (info && !info->is_connected())
|
||||
{
|
||||
info->on_connection_failed();
|
||||
|
||||
|
@ -1351,10 +1311,10 @@ void create_bit_torrent_peer(tr_torrent& tor, std::shared_ptr<tr_peerIo> io, tr_
|
|||
|
||||
if (result.io->is_incoming())
|
||||
{
|
||||
info = &swarm->ensure_info_exists(socket_address, 0U, TR_PEER_FROM_INCOMING, false);
|
||||
info = std::make_shared<tr_peer_info>(socket_address.address(), 0U, TR_PEER_FROM_INCOMING);
|
||||
}
|
||||
|
||||
if (info == nullptr)
|
||||
if (!info)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1395,7 +1355,7 @@ void create_bit_torrent_peer(tr_torrent& tor, std::shared_ptr<tr_peerIo> io, tr_
|
|||
}
|
||||
|
||||
result.io->set_bandwidth(&swarm->tor->bandwidth());
|
||||
create_bit_torrent_peer(*swarm->tor, result.io, info, client);
|
||||
create_bit_torrent_peer(*swarm->tor, result.io, std::move(info), client);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1444,7 +1404,7 @@ size_t tr_peerMgrAddPex(tr_torrent* tor, tr_peer_from from, tr_pex const* pex, s
|
|||
{
|
||||
// we store this peer since it is supposedly connectable (socket address should be the peer's listening address)
|
||||
// don't care about non-connectable peers that we are not connected to
|
||||
s->ensure_info_exists(pex->socket_address, pex->flags, from, true);
|
||||
s->ensure_info_exists(pex->socket_address, pex->flags, from);
|
||||
++n_used;
|
||||
}
|
||||
}
|
||||
|
@ -1556,7 +1516,7 @@ std::vector<tr_pex> tr_peerMgrGetPeers(tr_torrent const* tor, uint8_t address_ty
|
|||
{
|
||||
if (peer->socket_address().address().type == address_type)
|
||||
{
|
||||
infos.emplace_back(peer->peer_info);
|
||||
infos.emplace_back(peer->peer_info.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1566,10 +1526,10 @@ std::vector<tr_pex> tr_peerMgrGetPeers(tr_torrent const* tor, uint8_t address_ty
|
|||
infos.reserve(std::size(pool));
|
||||
for (auto const& [socket_address, peer_info] : pool)
|
||||
{
|
||||
TR_ASSERT(socket_address == peer_info.listen_socket_address());
|
||||
if (socket_address.address().type == address_type && is_peer_interesting(tor, peer_info))
|
||||
TR_ASSERT(socket_address == peer_info->listen_socket_address());
|
||||
if (socket_address.address().type == address_type && is_peer_interesting(tor, *peer_info))
|
||||
{
|
||||
infos.emplace_back(&peer_info);
|
||||
infos.emplace_back(peer_info.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2151,7 +2111,7 @@ auto constexpr MaxUploadIdleSecs = time_t{ 60 * 5 };
|
|||
}
|
||||
|
||||
auto const* tor = s->tor;
|
||||
auto const* const info = peer->peer_info;
|
||||
auto const& info = peer->peer_info;
|
||||
|
||||
/* disconnect if we're both seeds and enough time has passed for PEX */
|
||||
if (tor->is_done() && peer->is_seed())
|
||||
|
@ -2195,7 +2155,7 @@ void close_peer(tr_peerMsgs* peer)
|
|||
|
||||
constexpr struct
|
||||
{
|
||||
[[nodiscard]] constexpr static int compare(tr_peerMsgs const* a, tr_peerMsgs const* b) // <=>
|
||||
[[nodiscard]] static int compare(tr_peerMsgs const* a, tr_peerMsgs const* b) // <=>
|
||||
{
|
||||
if (a->do_purge != b->do_purge)
|
||||
{
|
||||
|
@ -2205,7 +2165,7 @@ constexpr struct
|
|||
return -a->peer_info->compare_by_piece_data_time(*b->peer_info);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(tr_peerMsgs const* a, tr_peerMsgs const* b) const // less than
|
||||
[[nodiscard]] bool operator()(tr_peerMsgs const* a, tr_peerMsgs const* b) const // less than
|
||||
{
|
||||
return compare(a, b) < 0;
|
||||
}
|
||||
|
@ -2331,6 +2291,93 @@ void tr_peerMgr::reconnect_pulse()
|
|||
make_new_peer_connections();
|
||||
}
|
||||
|
||||
// --- Peer Pool Size
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace peer_info_pulse_helpers
|
||||
{
|
||||
auto get_max_peer_info_count(tr_torrent const& tor)
|
||||
{
|
||||
return tor.is_done() ? tor.peer_limit() : tor.peer_limit() * 3U;
|
||||
}
|
||||
|
||||
struct ComparePeerInfo
|
||||
{
|
||||
[[nodiscard]] int compare(tr_peer_info const& a, tr_peer_info const& b) const noexcept
|
||||
{
|
||||
auto const is_a_inactive = a.is_inactive(now_);
|
||||
auto const is_b_inactive = b.is_inactive(now_);
|
||||
if (is_a_inactive != is_b_inactive)
|
||||
{
|
||||
return is_a_inactive ? 1 : -1;
|
||||
}
|
||||
|
||||
return CompareAtomsByUsefulness.compare(a, b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] std::enable_if_t<std::is_same_v<std::decay_t<decltype(*std::declval<T>())>, tr_peer_info>, bool> operator()(
|
||||
T const& a,
|
||||
T const& b) const noexcept
|
||||
{
|
||||
return compare(*a, *b) < 0;
|
||||
}
|
||||
|
||||
time_t const now_ = tr_time();
|
||||
};
|
||||
} // namespace peer_info_pulse_helpers
|
||||
} // namespace
|
||||
|
||||
void tr_peerMgr::peer_info_pulse()
|
||||
{
|
||||
using namespace peer_info_pulse_helpers;
|
||||
|
||||
auto const lock = unique_lock();
|
||||
for (auto const* tor : torrents_)
|
||||
{
|
||||
auto& pool = tor->swarm->connectable_pool;
|
||||
auto const max = get_max_peer_info_count(*tor);
|
||||
auto const pool_size = std::size(pool);
|
||||
if (pool_size <= max)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto infos = std::vector<std::shared_ptr<tr_peer_info>>{};
|
||||
infos.reserve(pool_size);
|
||||
std::transform(
|
||||
std::begin(pool),
|
||||
std::end(pool),
|
||||
std::back_inserter(infos),
|
||||
[](auto const& keyval) { return keyval.second; });
|
||||
pool.clear();
|
||||
|
||||
// Keep all peer info objects before test_begin unconditionally
|
||||
auto const test_begin = std::partition(
|
||||
std::begin(infos),
|
||||
std::end(infos),
|
||||
[](auto const& info) { return info->is_in_use(); });
|
||||
|
||||
auto const iter_max = std::begin(infos) + max;
|
||||
if (iter_max > test_begin)
|
||||
{
|
||||
std::partial_sort(test_begin, iter_max, std::end(infos), ComparePeerInfo{});
|
||||
}
|
||||
infos.erase(std::max(test_begin, iter_max), std::end(infos));
|
||||
|
||||
pool.reserve(std::size(infos));
|
||||
for (auto& info : infos)
|
||||
{
|
||||
pool.try_emplace(info->listen_socket_address(), std::move(info));
|
||||
}
|
||||
|
||||
tr_logAddTraceSwarm(
|
||||
tor->swarm,
|
||||
fmt::format("max peer info count is {}... pruned from {} to {}", max, pool_size, std::size(pool)));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Bandwidth Allocation
|
||||
|
||||
namespace
|
||||
|
@ -2419,22 +2466,6 @@ namespace connect_helpers
|
|||
return true;
|
||||
}
|
||||
|
||||
struct peer_candidate
|
||||
{
|
||||
peer_candidate() = default;
|
||||
|
||||
peer_candidate(uint64_t score_in, tr_torrent const* const tor_in, tr_peer_info const* const peer_info_in)
|
||||
: score{ score_in }
|
||||
, tor{ tor_in }
|
||||
, peer_info{ peer_info_in }
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t score;
|
||||
tr_torrent const* tor;
|
||||
tr_peer_info const* peer_info;
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr uint64_t addValToKey(uint64_t value, unsigned int width, uint64_t addme)
|
||||
{
|
||||
value <<= width;
|
||||
|
@ -2506,6 +2537,22 @@ struct peer_candidate
|
|||
|
||||
void get_peer_candidates(size_t global_peer_limit, tr_torrents& torrents, tr_peerMgr::OutboundCandidates& setme)
|
||||
{
|
||||
struct peer_candidate
|
||||
{
|
||||
peer_candidate() = default;
|
||||
|
||||
peer_candidate(uint64_t score_in, tr_torrent const* const tor_in, tr_peer_info const* const peer_info_in)
|
||||
: score{ score_in }
|
||||
, tor{ tor_in }
|
||||
, peer_info{ peer_info_in }
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t score;
|
||||
tr_torrent const* tor;
|
||||
tr_peer_info const* peer_info;
|
||||
};
|
||||
|
||||
setme.clear();
|
||||
|
||||
auto const now = tr_time();
|
||||
|
@ -2551,24 +2598,24 @@ void get_peer_candidates(size_t global_peer_limit, tr_torrents& torrents, tr_pee
|
|||
continue;
|
||||
}
|
||||
|
||||
for (auto const& [socket_address, atom] : swarm->connectable_pool)
|
||||
for (auto const& [socket_address, peer_info] : swarm->connectable_pool)
|
||||
{
|
||||
if (is_peer_candidate(tor, atom, now))
|
||||
if (is_peer_candidate(tor, *peer_info, now))
|
||||
{
|
||||
candidates.emplace_back(getPeerCandidateScore(tor, atom, salter()), tor, &atom);
|
||||
candidates.emplace_back(getPeerCandidateScore(tor, *peer_info, salter()), tor, peer_info.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only keep the best `max` candidates
|
||||
if (auto const max = tr_peerMgr::OutboundCandidates::requested_inline_size; max < std::size(candidates))
|
||||
if (static auto constexpr Max = tr_peerMgr::OutboundCandidates::requested_inline_size; Max < std::size(candidates))
|
||||
{
|
||||
std::partial_sort(
|
||||
std::begin(candidates),
|
||||
std::begin(candidates) + max,
|
||||
std::begin(candidates) + Max,
|
||||
std::end(candidates),
|
||||
[](auto const& a, auto const& b) { return a.score < b.score; });
|
||||
candidates.resize(max);
|
||||
candidates.resize(Max);
|
||||
}
|
||||
|
||||
// put the best candidates at the end of the list
|
||||
|
@ -2638,14 +2685,13 @@ void tr_peerMgr::make_new_peer_connections()
|
|||
|
||||
// initiate connections to the last N candidates
|
||||
auto const n_this_pass = std::min(std::size(candidates), MaxConnectionsPerPulse);
|
||||
auto const it_end = std::crbegin(candidates) + n_this_pass;
|
||||
for (auto it = std::crbegin(candidates); it != it_end; ++it)
|
||||
for (auto it = std::crbegin(candidates), end = std::crbegin(candidates) + n_this_pass; it != end; ++it)
|
||||
{
|
||||
auto const& [tor_id, sock_addr] = *it;
|
||||
|
||||
if (auto* const tor = torrents_.get(tor_id); tor != nullptr)
|
||||
{
|
||||
if (auto* const peer_info = tor->swarm->get_existing_peer_info(sock_addr); peer_info != nullptr)
|
||||
if (auto const& peer_info = tor->swarm->get_existing_peer_info(sock_addr))
|
||||
{
|
||||
initiate_connection(this, tor->swarm, *peer_info);
|
||||
}
|
||||
|
@ -2660,7 +2706,7 @@ void HandshakeMediator::set_utp_failed(tr_sha1_digest_t const& info_hash, tr_soc
|
|||
{
|
||||
if (auto* const tor = torrents_.get(info_hash); tor != nullptr)
|
||||
{
|
||||
if (auto* const peer_info = tor->swarm->get_existing_peer_info(socket_address); peer_info != nullptr)
|
||||
if (auto const& peer_info = tor->swarm->get_existing_peer_info(socket_address))
|
||||
{
|
||||
peer_info->set_utp_supported(false);
|
||||
}
|
||||
|
|
|
@ -431,7 +431,7 @@ private:
|
|||
|
||||
// the minimum we'll wait before attempting to reconnect to a peer
|
||||
static auto constexpr MinimumReconnectIntervalSecs = time_t{ 5U };
|
||||
static auto constexpr InactiveThresSecs = time_t{ 24 * 60 * 60 };
|
||||
static auto constexpr InactiveThresSecs = time_t{ 60 * 60 };
|
||||
|
||||
static auto inline n_known_connectable = size_t{};
|
||||
|
||||
|
|
|
@ -305,12 +305,12 @@ class tr_peerMsgsImpl final : public tr_peerMsgs
|
|||
public:
|
||||
tr_peerMsgsImpl(
|
||||
tr_torrent& torrent_in,
|
||||
tr_peer_info* const peer_info_in,
|
||||
std::shared_ptr<tr_peer_info> peer_info_in,
|
||||
std::shared_ptr<tr_peerIo> io_in,
|
||||
tr_interned_string client,
|
||||
tr_peer_callback_bt callback,
|
||||
void* callback_data)
|
||||
: tr_peerMsgs{ torrent_in, peer_info_in, client, io_in->is_encrypted(), io_in->is_incoming(), io_in->is_utp() }
|
||||
: tr_peerMsgs{ torrent_in, std::move(peer_info_in), client, io_in->is_encrypted(), io_in->is_incoming(), io_in->is_utp() }
|
||||
, tor_{ torrent_in }
|
||||
, io_{ std::move(io_in) }
|
||||
, have_{ torrent_in.piece_count() }
|
||||
|
@ -2054,13 +2054,13 @@ size_t tr_peerMsgsImpl::max_available_reqs() const
|
|||
|
||||
tr_peerMsgs::tr_peerMsgs(
|
||||
tr_torrent const& tor,
|
||||
tr_peer_info* peer_info_in,
|
||||
std::shared_ptr<tr_peer_info> peer_info_in,
|
||||
tr_interned_string user_agent,
|
||||
bool connection_is_encrypted,
|
||||
bool connection_is_incoming,
|
||||
bool connection_is_utp)
|
||||
: tr_peer{ tor }
|
||||
, peer_info{ peer_info_in }
|
||||
, peer_info{ std::move(peer_info_in) }
|
||||
, user_agent_{ user_agent }
|
||||
, connection_is_encrypted_{ connection_is_encrypted }
|
||||
, connection_is_incoming_{ connection_is_incoming }
|
||||
|
@ -2079,11 +2079,11 @@ tr_peerMsgs::~tr_peerMsgs()
|
|||
|
||||
tr_peerMsgs* tr_peerMsgs::create(
|
||||
tr_torrent& torrent,
|
||||
tr_peer_info* const peer_info,
|
||||
std::shared_ptr<tr_peer_info> peer_info,
|
||||
std::shared_ptr<tr_peerIo> io,
|
||||
tr_interned_string user_agent,
|
||||
tr_peer_callback_bt callback,
|
||||
void* callback_data)
|
||||
{
|
||||
return new tr_peerMsgsImpl{ torrent, peer_info, std::move(io), user_agent, callback, callback_data };
|
||||
return new tr_peerMsgsImpl{ torrent, std::move(peer_info), std::move(io), user_agent, callback, callback_data };
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class tr_peerMsgs : public tr_peer
|
|||
public:
|
||||
tr_peerMsgs(
|
||||
tr_torrent const& tor,
|
||||
tr_peer_info* peer_info_in,
|
||||
std::shared_ptr<tr_peer_info> peer_info_in,
|
||||
tr_interned_string user_agent,
|
||||
bool connection_is_encrypted,
|
||||
bool connection_is_incoming,
|
||||
|
@ -108,7 +108,7 @@ public:
|
|||
|
||||
static tr_peerMsgs* create(
|
||||
tr_torrent& torrent,
|
||||
tr_peer_info* peer_info,
|
||||
std::shared_ptr<tr_peer_info> peer_info,
|
||||
std::shared_ptr<tr_peerIo> io,
|
||||
tr_interned_string user_agent,
|
||||
tr_peer_callback_bt callback,
|
||||
|
@ -146,8 +146,7 @@ protected:
|
|||
}
|
||||
|
||||
public:
|
||||
// TODO(tearfur): change this to reference
|
||||
tr_peer_info* const peer_info;
|
||||
std::shared_ptr<tr_peer_info> const peer_info;
|
||||
|
||||
private:
|
||||
static inline auto n_peers = std::atomic<size_t>{};
|
||||
|
|
|
@ -87,7 +87,8 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port local_port, bool is_enabled)
|
|||
{
|
||||
auto str = std::array<char, 128>{};
|
||||
evutil_inet_ntop(AF_INET, &response.pnu.publicaddress.addr, std::data(str), std::size(str));
|
||||
tr_logAddInfo(fmt::format(_("Found public address '{address}'"), fmt::arg("address", std::data(str))));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Found public address '{address}'")), fmt::arg("address", std::data(str))));
|
||||
state_ = State::Idle;
|
||||
}
|
||||
else if (val != NATPMP_TRYAGAIN)
|
||||
|
@ -124,7 +125,8 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port local_port, bool is_enabled)
|
|||
{
|
||||
auto const unmapped_port = tr_port::from_host(resp.pnu.newportmapping.privateport);
|
||||
|
||||
tr_logAddInfo(fmt::format(_("Port {port} is no longer forwarded"), fmt::arg("port", unmapped_port.host())));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Port {port} is no longer forwarded")), fmt::arg("port", unmapped_port.host())));
|
||||
|
||||
if (local_port_ == unmapped_port)
|
||||
{
|
||||
|
@ -140,16 +142,10 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port local_port, bool is_enabled)
|
|||
}
|
||||
}
|
||||
|
||||
if (state_ == State::Idle)
|
||||
if ((state_ == State::Idle || state_ == State::Err) &&
|
||||
(is_mapped_ ? tr_time() >= renew_time_ : is_enabled && has_discovered_))
|
||||
{
|
||||
if (is_enabled && !is_mapped_ && has_discovered_)
|
||||
{
|
||||
state_ = State::SendMap;
|
||||
}
|
||||
else if (is_mapped_ && tr_time() >= renew_time_)
|
||||
{
|
||||
state_ = State::SendMap;
|
||||
}
|
||||
state_ = State::SendMap;
|
||||
}
|
||||
|
||||
if (state_ == State::SendMap && canSendCommand())
|
||||
|
@ -178,7 +174,8 @@ tr_natpmp::PulseResult tr_natpmp::pulse(tr_port local_port, bool is_enabled)
|
|||
renew_time_ = tr_time() + (resp.pnu.newportmapping.lifetime / 2);
|
||||
local_port_ = tr_port::from_host(resp.pnu.newportmapping.privateport);
|
||||
advertised_port_ = tr_port::from_host(resp.pnu.newportmapping.mappedpublicport);
|
||||
tr_logAddInfo(fmt::format(_("Port {port} forwarded successfully"), fmt::arg("port", local_port_.host())));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Port {port} forwarded successfully")), fmt::arg("port", local_port_.host())));
|
||||
}
|
||||
else if (val != NATPMP_TRYAGAIN)
|
||||
{
|
||||
|
|
|
@ -15,13 +15,8 @@
|
|||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#ifdef SYSTEM_MINIUPNP
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
#else
|
||||
#include <miniupnp/miniupnpc.h>
|
||||
#include <miniupnp/upnpcommands.h>
|
||||
#endif
|
||||
|
||||
#define LIBTRANSMISSION_PORT_FORWARDING_MODULE
|
||||
|
||||
|
@ -34,12 +29,15 @@
|
|||
#include "libtransmission/tr-macros.h" // TR_ADDRSTRLEN
|
||||
#include "libtransmission/utils.h" // for _(), tr_strerror()
|
||||
|
||||
#ifndef MINIUPNPC_API_VERSION
|
||||
#error miniupnpc >= 1.7 is required
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class UpnpState : uint8_t
|
||||
{
|
||||
Idle,
|
||||
Failed,
|
||||
WillDiscover, // next action is upnpDiscover()
|
||||
Discovering, // currently making blocking upnpDiscover() call in a worker thread
|
||||
WillMap, // next action is UPNP_AddPortMapping()
|
||||
|
@ -58,9 +56,7 @@ struct tr_upnp
|
|||
~tr_upnp()
|
||||
{
|
||||
TR_ASSERT(!isMapped);
|
||||
TR_ASSERT(
|
||||
state == UpnpState::Idle || state == UpnpState::Failed || state == UpnpState::WillDiscover ||
|
||||
state == UpnpState::Discovering);
|
||||
TR_ASSERT(state == UpnpState::Idle || state == UpnpState::WillDiscover || state == UpnpState::Discovering);
|
||||
|
||||
FreeUPNPUrls(&urls);
|
||||
}
|
||||
|
@ -108,20 +104,16 @@ constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
|||
UPNPDev* ret = nullptr;
|
||||
auto have_err = bool{};
|
||||
|
||||
#if (MINIUPNPC_API_VERSION >= 8) /* adds ipv6 and error args */
|
||||
// MINIUPNPC_API_VERSION >= 8 (adds ipv6 and error args)
|
||||
int err = UPNPDISCOVER_SUCCESS;
|
||||
|
||||
#if (MINIUPNPC_API_VERSION >= 14) /* adds ttl */
|
||||
#if (MINIUPNPC_API_VERSION >= 14) // adds ttl
|
||||
ret = upnpDiscover(msec, bindaddr, nullptr, 0, 0, 2, &err);
|
||||
#else
|
||||
ret = upnpDiscover(msec, bindaddr, nullptr, 0, 0, &err);
|
||||
#endif
|
||||
|
||||
have_err = err != UPNPDISCOVER_SUCCESS;
|
||||
#else
|
||||
ret = upnpDiscover(msec, bindaddr, nullptr, 0);
|
||||
have_err = ret == nullptr;
|
||||
#endif
|
||||
|
||||
if (have_err)
|
||||
{
|
||||
|
@ -150,7 +142,7 @@ constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
|||
nullptr /*desc*/,
|
||||
nullptr /*enabled*/,
|
||||
nullptr /*duration*/);
|
||||
#elif (MINIUPNPC_API_VERSION >= 8) /* adds desc, enabled and leaseDuration args */
|
||||
#else // MINIUPNPC_API_VERSION >= 8 (adds desc, enabled and leaseDuration args)
|
||||
int const err = UPNP_GetSpecificPortMappingEntry(
|
||||
handle->urls.controlURL,
|
||||
handle->data.first.servicetype,
|
||||
|
@ -161,14 +153,6 @@ constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
|||
nullptr /*desc*/,
|
||||
nullptr /*enabled*/,
|
||||
nullptr /*duration*/);
|
||||
#else
|
||||
int const err = UPNP_GetSpecificPortMappingEntry(
|
||||
handle->urls.controlURL,
|
||||
handle->data.first.servicetype,
|
||||
port_str.c_str(),
|
||||
proto,
|
||||
std::data(int_client),
|
||||
std::data(int_port));
|
||||
#endif
|
||||
|
||||
return err;
|
||||
|
@ -187,7 +171,7 @@ constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
|||
auto const advertised_port_str = std::to_string(advertised_port.host());
|
||||
auto const local_port_str = std::to_string(local_port.host());
|
||||
|
||||
#if (MINIUPNPC_API_VERSION >= 8)
|
||||
// MINIUPNPC_API_VERSION >= 8
|
||||
int const err = UPNP_AddPortMapping(
|
||||
handle->urls.controlURL,
|
||||
handle->data.first.servicetype,
|
||||
|
@ -198,17 +182,6 @@ constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
|||
proto,
|
||||
nullptr,
|
||||
nullptr);
|
||||
#else
|
||||
int const err = UPNP_AddPortMapping(
|
||||
handle->urls.controlURL,
|
||||
handle->data.first.servicetype,
|
||||
advertised_port_str.c_str(),
|
||||
local_port_str.c_str(),
|
||||
handle->lanaddr.c_str(),
|
||||
desc,
|
||||
proto,
|
||||
nullptr);
|
||||
#endif
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
|
@ -288,17 +261,24 @@ tr_port_forwarding_state tr_upnpPulse(
|
|||
|
||||
FreeUPNPUrls(&handle->urls);
|
||||
auto lanaddr = std::array<char, TR_ADDRSTRLEN>{};
|
||||
if (UPNP_GetValidIGD(devlist, &handle->urls, &handle->data, std::data(lanaddr), std::size(lanaddr) - 1) ==
|
||||
UPNP_IGD_VALID_CONNECTED)
|
||||
if (
|
||||
#if (MINIUPNPC_API_VERSION >= 18)
|
||||
UPNP_GetValidIGD(devlist, &handle->urls, &handle->data, std::data(lanaddr), std::size(lanaddr) - 1, nullptr, 0)
|
||||
#else
|
||||
UPNP_GetValidIGD(devlist, &handle->urls, &handle->data, std::data(lanaddr), std::size(lanaddr) - 1)
|
||||
#endif
|
||||
== UPNP_IGD_VALID_CONNECTED)
|
||||
{
|
||||
tr_logAddInfo(fmt::format(_("Found Internet Gateway Device '{url}'"), fmt::arg("url", handle->urls.controlURL)));
|
||||
tr_logAddInfo(fmt::format(_("Local Address is '{address}'"), fmt::arg("address", lanaddr.data())));
|
||||
tr_logAddInfo(fmt::format(
|
||||
fmt::runtime(_("Found Internet Gateway Device '{url}'")),
|
||||
fmt::arg("url", handle->urls.controlURL)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Local Address is '{address}'")), fmt::arg("address", lanaddr.data())));
|
||||
handle->state = UpnpState::Idle;
|
||||
handle->lanaddr = std::data(lanaddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle->state = UpnpState::Failed;
|
||||
handle->state = UpnpState::WillDiscover;
|
||||
tr_logAddDebug(fmt::format("UPNP_GetValidIGD failed: {} ({})", tr_strerror(errno), errno));
|
||||
tr_logAddDebug("If your router supports UPnP, please make sure UPnP is enabled!");
|
||||
}
|
||||
|
@ -306,18 +286,18 @@ tr_port_forwarding_state tr_upnpPulse(
|
|||
freeUPNPDevlist(devlist);
|
||||
}
|
||||
|
||||
if ((handle->state == UpnpState::Idle) && (handle->isMapped) &&
|
||||
if (handle->state == UpnpState::Idle && handle->isMapped &&
|
||||
(!is_enabled || handle->advertised_port != advertised_port || handle->local_port != local_port))
|
||||
{
|
||||
handle->state = UpnpState::WillUnmap;
|
||||
}
|
||||
|
||||
if (is_enabled && handle->isMapped && do_port_check &&
|
||||
((get_specific_port_mapping_entry(handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
|
||||
(get_specific_port_mapping_entry(handle, "UDP") != UPNPCOMMAND_SUCCESS)))
|
||||
(get_specific_port_mapping_entry(handle, "TCP") != UPNPCOMMAND_SUCCESS ||
|
||||
get_specific_port_mapping_entry(handle, "UDP") != UPNPCOMMAND_SUCCESS))
|
||||
{
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("Local port {local_port} is not forwarded to {advertised_port}"),
|
||||
fmt::runtime(_("Local port {local_port} is not forwarded to {advertised_port}")),
|
||||
fmt::arg("local_port", handle->local_port.host()),
|
||||
fmt::arg("advertised_port", handle->advertised_port.host())));
|
||||
handle->isMapped = false;
|
||||
|
@ -329,7 +309,7 @@ tr_port_forwarding_state tr_upnpPulse(
|
|||
tr_upnpDeletePortMapping(handle, "UDP", handle->advertised_port);
|
||||
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("Stopping port forwarding through '{url}', service '{type}'"),
|
||||
fmt::runtime(_("Stopping port forwarding through '{url}', service '{type}'")),
|
||||
fmt::arg("url", handle->urls.controlURL),
|
||||
fmt::arg("type", handle->data.first.servicetype)));
|
||||
|
||||
|
@ -339,7 +319,7 @@ tr_port_forwarding_state tr_upnpPulse(
|
|||
handle->local_port = {};
|
||||
}
|
||||
|
||||
if ((handle->state == UpnpState::Idle) && is_enabled && !handle->isMapped)
|
||||
if (handle->state == UpnpState::Idle && is_enabled && !handle->isMapped)
|
||||
{
|
||||
handle->state = UpnpState::WillMap;
|
||||
}
|
||||
|
@ -362,7 +342,7 @@ tr_port_forwarding_state tr_upnpPulse(
|
|||
}
|
||||
|
||||
tr_logAddDebug(fmt::format(
|
||||
_("Port forwarding through '{url}', service '{type}'. (local address: {address}:{port})"),
|
||||
fmt::runtime(_("Port forwarding through '{url}', service '{type}'. (local address: {address}:{port})")),
|
||||
fmt::arg("url", handle->urls.controlURL),
|
||||
fmt::arg("type", handle->data.first.servicetype),
|
||||
fmt::arg("address", handle->lanaddr),
|
||||
|
@ -371,20 +351,19 @@ tr_port_forwarding_state tr_upnpPulse(
|
|||
if (handle->isMapped)
|
||||
{
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("Forwarded local port {local_port} to {advertised_port}"),
|
||||
fmt::runtime(_("Forwarded local port {local_port} to {advertised_port}")),
|
||||
fmt::arg("local_port", local_port.host()),
|
||||
fmt::arg("advertised_port", advertised_port.host())));
|
||||
handle->advertised_port = advertised_port;
|
||||
handle->local_port = local_port;
|
||||
handle->state = UpnpState::Idle;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddInfo(_("If your router supports UPnP, please make sure UPnP is enabled!"));
|
||||
handle->advertised_port = {};
|
||||
handle->local_port = {};
|
||||
handle->state = UpnpState::Failed;
|
||||
}
|
||||
handle->state = UpnpState::Idle;
|
||||
}
|
||||
|
||||
return port_fwd_state(handle->state, handle->isMapped);
|
||||
|
|
|
@ -198,7 +198,7 @@ private:
|
|||
{
|
||||
mediator_.on_port_forwarded(result.advertised_port);
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("Mapped private port {private_port} to public port {public_port}"),
|
||||
fmt::runtime(_("Mapped private port {private_port} to public port {public_port}")),
|
||||
fmt::arg("private_port", result.local_port.host()),
|
||||
fmt::arg("public_port", result.advertised_port.host())));
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ private:
|
|||
if (auto const new_state = state(); new_state != old_state)
|
||||
{
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("State changed from '{old_state}' to '{state}'"),
|
||||
fmt::runtime(_("State changed from '{old_state}' to '{state}'")),
|
||||
fmt::arg("old_state", getNatStateStr(old_state)),
|
||||
fmt::arg("state", getNatStateStr(new_state))));
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <cstddef>
|
||||
#include <iterator> // for std::distance()
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
|
|
@ -100,12 +100,12 @@ void saveLabels(tr_variant* dict, tr_torrent const* tor)
|
|||
}
|
||||
}
|
||||
|
||||
auto loadLabels(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadLabels(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
tr_variant* list = nullptr;
|
||||
if (!tr_variantDictFindList(dict, TR_KEY_labels, &list))
|
||||
{
|
||||
return tr_resume::fields_t{};
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const n = tr_variantListSize(list);
|
||||
|
@ -131,7 +131,7 @@ void saveGroup(tr_variant* dict, tr_torrent const* tor)
|
|||
tr_variantDictAddStrView(dict, TR_KEY_group, tor->bandwidth_group());
|
||||
}
|
||||
|
||||
auto loadGroup(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadGroup(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
if (std::string_view group_name; tr_variantDictFindStrView(dict, TR_KEY_group, &group_name) && !std::empty(group_name))
|
||||
{
|
||||
|
@ -139,7 +139,7 @@ auto loadGroup(tr_variant* dict, tr_torrent* tor)
|
|||
return tr_resume::Group;
|
||||
}
|
||||
|
||||
return tr_resume::fields_t{};
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -155,12 +155,12 @@ void saveDND(tr_variant* dict, tr_torrent const* tor)
|
|||
}
|
||||
}
|
||||
|
||||
auto loadDND(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadDND(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
auto ret = tr_resume::fields_t{};
|
||||
tr_variant* list = nullptr;
|
||||
auto const n = tor->file_count();
|
||||
|
||||
if (auto const n = tor->file_count(); tr_variantDictFindList(dict, TR_KEY_dnd, &list) && tr_variantListSize(list) == n)
|
||||
if (tr_variantDictFindList(dict, TR_KEY_dnd, &list) && tr_variantListSize(list) == n)
|
||||
{
|
||||
auto wanted = std::vector<tr_file_index_t>{};
|
||||
auto unwanted = std::vector<tr_file_index_t>{};
|
||||
|
@ -183,20 +183,17 @@ auto loadDND(tr_variant* dict, tr_torrent* tor)
|
|||
tor->init_files_wanted(std::data(unwanted), std::size(unwanted), false);
|
||||
tor->init_files_wanted(std::data(wanted), std::size(wanted), true);
|
||||
|
||||
ret = tr_resume::Dnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddDebugTor(
|
||||
tor,
|
||||
fmt::format(
|
||||
"Couldn't load DND flags. DND list {} has {} children; torrent has {} files",
|
||||
fmt::ptr(list),
|
||||
tr_variantListSize(list),
|
||||
n));
|
||||
return tr_resume::Dnd;
|
||||
}
|
||||
|
||||
return ret;
|
||||
tr_logAddDebugTor(
|
||||
tor,
|
||||
fmt::format(
|
||||
"Couldn't load DND flags. DND list {} has {} children; torrent has {} files",
|
||||
fmt::ptr(list),
|
||||
tr_variantListSize(list),
|
||||
n));
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -212,10 +209,8 @@ void saveFilePriorities(tr_variant* dict, tr_torrent const* tor)
|
|||
}
|
||||
}
|
||||
|
||||
auto loadFilePriorities(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadFilePriorities(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
auto ret = tr_resume::fields_t{};
|
||||
|
||||
auto const n = tor->file_count();
|
||||
tr_variant* list = nullptr;
|
||||
if (tr_variantDictFindList(dict, TR_KEY_priority, &list) && tr_variantListSize(list) == n)
|
||||
|
@ -229,10 +224,10 @@ auto loadFilePriorities(tr_variant* dict, tr_torrent* tor)
|
|||
}
|
||||
}
|
||||
|
||||
ret = tr_resume::FilePriorities;
|
||||
return tr_resume::FilePriorities;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -306,10 +301,8 @@ auto loadSpeedLimits(tr_variant* dict, tr_torrent* tor)
|
|||
return ret;
|
||||
}
|
||||
|
||||
auto loadRatioLimits(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadRatioLimits(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
auto ret = tr_resume::fields_t{};
|
||||
|
||||
if (tr_variant* d = nullptr; tr_variantDictFindDict(dict, TR_KEY_ratio_limit, &d))
|
||||
{
|
||||
if (auto dratio = double{}; tr_variantDictFindReal(d, TR_KEY_ratio_limit, &dratio))
|
||||
|
@ -322,16 +315,14 @@ auto loadRatioLimits(tr_variant* dict, tr_torrent* tor)
|
|||
tor->set_seed_ratio_mode(static_cast<tr_ratiolimit>(i));
|
||||
}
|
||||
|
||||
ret = tr_resume::Ratiolimit;
|
||||
return tr_resume::Ratiolimit;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto loadIdleLimits(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadIdleLimits(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
auto ret = tr_resume::fields_t{};
|
||||
|
||||
if (tr_variant* d = nullptr; tr_variantDictFindDict(dict, TR_KEY_idle_limit, &d))
|
||||
{
|
||||
if (auto imin = int64_t{}; tr_variantDictFindInt(d, TR_KEY_idle_limit, &imin))
|
||||
|
@ -344,10 +335,10 @@ auto loadIdleLimits(tr_variant* dict, tr_torrent* tor)
|
|||
tor->set_idle_limit_mode(static_cast<tr_idlelimit>(i));
|
||||
}
|
||||
|
||||
ret = tr_resume::Idlelimit;
|
||||
return tr_resume::Idlelimit;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -357,26 +348,23 @@ void saveName(tr_variant* dict, tr_torrent const* tor)
|
|||
tr_variantDictAddStrView(dict, TR_KEY_name, tor->name());
|
||||
}
|
||||
|
||||
auto loadName(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadName(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
auto ret = tr_resume::fields_t{};
|
||||
|
||||
auto name = std::string_view{};
|
||||
if (!tr_variantDictFindStrView(dict, TR_KEY_name, &name))
|
||||
{
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
name = tr_strv_strip(name);
|
||||
if (std::empty(name))
|
||||
{
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
tor->set_name(name);
|
||||
ret |= tr_resume::Name;
|
||||
|
||||
return ret;
|
||||
return tr_resume::Name;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -391,14 +379,12 @@ void saveFilenames(tr_variant* dict, tr_torrent const* tor)
|
|||
}
|
||||
}
|
||||
|
||||
auto loadFilenames(tr_variant* dict, tr_torrent* tor)
|
||||
tr_resume::fields_t loadFilenames(tr_variant* dict, tr_torrent* tor)
|
||||
{
|
||||
auto ret = tr_resume::fields_t{};
|
||||
|
||||
tr_variant* list = nullptr;
|
||||
if (!tr_variantDictFindList(dict, TR_KEY_files, &list))
|
||||
{
|
||||
return ret;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const n_files = tor->file_count();
|
||||
|
@ -412,8 +398,7 @@ auto loadFilenames(tr_variant* dict, tr_torrent* tor)
|
|||
}
|
||||
}
|
||||
|
||||
ret |= tr_resume::Filenames;
|
||||
return ret;
|
||||
return tr_resume::Filenames;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -451,7 +436,7 @@ void rawToBitfield(tr_bitfield& bitfield, uint8_t const* raw, size_t rawlen)
|
|||
}
|
||||
}
|
||||
|
||||
void saveProgress(tr_variant* dict, tr_torrent const* tor, tr_torrent::ResumeHelper const& helper)
|
||||
void saveProgress(tr_variant* dict, tr_torrent::ResumeHelper const& helper)
|
||||
{
|
||||
tr_variant* const prog = tr_variantDictAddDict(dict, TR_KEY_progress, 4);
|
||||
|
||||
|
@ -467,13 +452,7 @@ void saveProgress(tr_variant* dict, tr_torrent const* tor, tr_torrent::ResumeHel
|
|||
// add the 'checked pieces' bitfield
|
||||
bitfieldToRaw(helper.checked_pieces(), tr_variantDictAdd(prog, TR_KEY_pieces));
|
||||
|
||||
/* add the progress */
|
||||
if (tor->is_seed())
|
||||
{
|
||||
tr_variantDictAddStrView(prog, TR_KEY_have, "all"sv);
|
||||
}
|
||||
|
||||
/* add the blocks bitfield */
|
||||
// add the blocks bitfield
|
||||
bitfieldToRaw(helper.blocks(), tr_variantDictAdd(prog, TR_KEY_blocks));
|
||||
}
|
||||
|
||||
|
@ -489,7 +468,7 @@ void saveProgress(tr_variant* dict, tr_torrent const* tor, tr_torrent::ResumeHel
|
|||
* pieces cleared from the bitset.
|
||||
*
|
||||
* Second approach (2.20 - 3.00): the 'progress' dict had a
|
||||
* 'time_checked' entry which was a list with fileCount items.
|
||||
* 'time_checked' entry which was a list with file_count items.
|
||||
* Each item was either a list of per-piece timestamps, or a
|
||||
* single timestamp if either all or none of the pieces had been
|
||||
* tested more recently than the file's mtime.
|
||||
|
@ -497,7 +476,7 @@ void saveProgress(tr_variant* dict, tr_torrent const* tor, tr_torrent::ResumeHel
|
|||
* First approach (pre-2.20) had an "mtimes" list identical to
|
||||
* 3.10, but not the 'pieces' bitfield.
|
||||
*/
|
||||
auto loadProgress(tr_variant* dict, tr_torrent* tor, tr_torrent::ResumeHelper& helper)
|
||||
tr_resume::fields_t loadProgress(tr_variant* dict, tr_torrent* tor, tr_torrent::ResumeHelper& helper)
|
||||
{
|
||||
if (tr_variant* prog = nullptr; tr_variantDictFindDict(dict, TR_KEY_progress, &prog))
|
||||
{
|
||||
|
@ -590,24 +569,13 @@ auto loadProgress(tr_variant* dict, tr_torrent* tor, tr_torrent::ResumeHelper& h
|
|||
rawToBitfield(blocks, buf, buflen);
|
||||
}
|
||||
}
|
||||
else if (auto sv = std::string_view{}; tr_variantDictFindStrView(prog, TR_KEY_have, &sv))
|
||||
{
|
||||
if (sv == "all"sv)
|
||||
{
|
||||
blocks.set_has_all();
|
||||
}
|
||||
else
|
||||
{
|
||||
err = "Invalid value for HAVE";
|
||||
}
|
||||
}
|
||||
else if (tr_variantDictFindRaw(prog, TR_KEY_bitfield, &raw, &rawlen))
|
||||
{
|
||||
blocks.set_raw(raw, rawlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = "Couldn't find 'pieces' or 'have' or 'bitfield'";
|
||||
err = "Couldn't find 'blocks' or 'bitfield'";
|
||||
}
|
||||
|
||||
if (err != nullptr)
|
||||
|
@ -622,7 +590,7 @@ auto loadProgress(tr_variant* dict, tr_torrent* tor, tr_torrent::ResumeHelper& h
|
|||
return tr_resume::Progress;
|
||||
}
|
||||
|
||||
return tr_resume::fields_t{};
|
||||
return {};
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -914,7 +882,7 @@ void save(tr_torrent* const tor, tr_torrent::ResumeHelper const& helper)
|
|||
{
|
||||
saveFilePriorities(&top, tor);
|
||||
saveDND(&top, tor);
|
||||
saveProgress(&top, tor, helper);
|
||||
saveProgress(&top, helper);
|
||||
}
|
||||
|
||||
saveSpeedLimits(&top, tor);
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
if (std::size(src) >= TrUnixAddrStrLen)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Unix socket path must be fewer than {count} characters (including '{prefix}' prefix)"),
|
||||
fmt::runtime(_("Unix socket path must be fewer than {count} characters (including '{prefix}' prefix)")),
|
||||
fmt::arg("count", TrUnixAddrStrLen - 1),
|
||||
fmt::arg("prefix", TrUnixSocketPrefix)));
|
||||
return false;
|
||||
|
@ -614,8 +614,9 @@ bool bindUnixSocket(
|
|||
|
||||
if (chmod(addr.sun_path, socket_mode) != 0)
|
||||
{
|
||||
tr_logAddWarn(
|
||||
fmt::format(_("Couldn't set RPC socket mode to {mode:#o}, defaulting to 0755"), fmt::arg("mode", socket_mode)));
|
||||
tr_logAddWarn(fmt::format(
|
||||
fmt::runtime(_("Couldn't set RPC socket mode to {mode:#o}, defaulting to 0755")),
|
||||
fmt::arg("mode", socket_mode)));
|
||||
}
|
||||
|
||||
return evhttp_bind_listener(httpd, lev) != nullptr;
|
||||
|
@ -677,10 +678,10 @@ void start_server(tr_rpc_server* server)
|
|||
}
|
||||
|
||||
tr_logAddError(fmt::format(
|
||||
tr_ngettext(
|
||||
fmt::runtime(tr_ngettext(
|
||||
"Couldn't bind to {address} after {count} attempt, giving up",
|
||||
"Couldn't bind to {address} after {count} attempts, giving up",
|
||||
ServerStartRetryCount),
|
||||
ServerStartRetryCount)),
|
||||
fmt::arg("address", addr_port_str),
|
||||
fmt::arg("count", ServerStartRetryCount)));
|
||||
}
|
||||
|
@ -689,7 +690,9 @@ void start_server(tr_rpc_server* server)
|
|||
evhttp_set_gencb(httpd, handle_request, server);
|
||||
server->httpd.reset(httpd);
|
||||
|
||||
tr_logAddInfo(fmt::format(_("Listening for RPC and Web requests on '{address}'"), fmt::arg("address", addr_port_str)));
|
||||
tr_logAddInfo(fmt::format(
|
||||
fmt::runtime(_("Listening for RPC and Web requests on '{address}'")),
|
||||
fmt::arg("address", addr_port_str)));
|
||||
}
|
||||
|
||||
rpc_server_start_retry_cancel(server);
|
||||
|
@ -717,7 +720,7 @@ void stop_server(tr_rpc_server* server)
|
|||
}
|
||||
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("Stopped listening for RPC and Web requests on '{address}'"),
|
||||
fmt::runtime(_("Stopped listening for RPC and Web requests on '{address}'")),
|
||||
fmt::arg("address", server->bind_address_->to_string(server->port()))));
|
||||
}
|
||||
|
||||
|
@ -739,7 +742,7 @@ auto parse_whitelist(std::string_view whitelist)
|
|||
auto const pos = whitelist.find_first_of(" ,;"sv);
|
||||
auto const token = tr_strv_strip(whitelist.substr(0, pos));
|
||||
list.emplace_back(token);
|
||||
tr_logAddInfo(fmt::format(_("Added '{entry}' to host whitelist"), fmt::arg("entry", token)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Added '{entry}' to host whitelist")), fmt::arg("entry", token)));
|
||||
whitelist = pos == std::string_view::npos ? ""sv : whitelist.substr(pos + 1);
|
||||
}
|
||||
|
||||
|
@ -859,7 +862,8 @@ void tr_rpc_server::load(Settings&& settings)
|
|||
{
|
||||
// NOTE: bind_address_ is default initialized to INADDR_ANY
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("The '{key}' setting is '{value}' but must be an IPv4 or IPv6 address or a Unix socket path. Using default value '0.0.0.0'"),
|
||||
fmt::runtime(_(
|
||||
"The '{key}' setting is '{value}' but must be an IPv4 or IPv6 address or a Unix socket path. Using default value '0.0.0.0'")),
|
||||
fmt::arg("key", tr_quark_get_string_view(TR_KEY_rpc_bind_address)),
|
||||
fmt::arg("value", settings_.bind_address_str)));
|
||||
}
|
||||
|
@ -872,7 +876,7 @@ void tr_rpc_server::load(Settings&& settings)
|
|||
if (this->is_enabled())
|
||||
{
|
||||
auto const rpc_uri = bind_address_->to_string(port()) + settings_.url;
|
||||
tr_logAddInfo(fmt::format(_("Serving RPC and Web requests on {address}"), fmt::arg("address", rpc_uri)));
|
||||
tr_logAddInfo(fmt::format(fmt::runtime(_("Serving RPC and Web requests on {address}")), fmt::arg("address", rpc_uri)));
|
||||
session->run_in_session_thread(start_server, this);
|
||||
|
||||
if (this->is_whitelist_enabled())
|
||||
|
@ -888,7 +892,8 @@ void tr_rpc_server::load(Settings&& settings)
|
|||
|
||||
if (!std::empty(web_client_dir_))
|
||||
{
|
||||
tr_logAddInfo(fmt::format(_("Serving RPC and Web requests from '{path}'"), fmt::arg("path", web_client_dir_)));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Serving RPC and Web requests from '{path}'")), fmt::arg("path", web_client_dir_)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1155,7 +1155,7 @@ void onPortTested(tr_web::FetchResponse const& web_response)
|
|||
tr_idle_function_done(
|
||||
data,
|
||||
fmt::format(
|
||||
_("Couldn't test port: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't test port: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_webGetResponseStr(status)),
|
||||
fmt::arg("error_code", status)));
|
||||
return;
|
||||
|
@ -1210,7 +1210,7 @@ void onBlocklistFetched(tr_web::FetchResponse const& web_response)
|
|||
tr_idle_function_done(
|
||||
data,
|
||||
fmt::format(
|
||||
_("Couldn't fetch blocklist: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't fetch blocklist: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_webGetResponseStr(status)),
|
||||
fmt::arg("error_code", status)));
|
||||
return;
|
||||
|
@ -1255,7 +1255,7 @@ void onBlocklistFetched(tr_web::FetchResponse const& web_response)
|
|||
tr_idle_function_done(
|
||||
data,
|
||||
fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -1337,7 +1337,7 @@ void onMetadataFetched(tr_web::FetchResponse const& web_response)
|
|||
tr_idle_function_done(
|
||||
data->data,
|
||||
fmt::format(
|
||||
_("Couldn't fetch torrent: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't fetch torrent: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_webGetResponseStr(status)),
|
||||
fmt::arg("error_code", status)));
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ tr_sys_file_t create_lockfile(std::string_view session_id)
|
|||
if (error)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't create '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't create '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", lockfile_path),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -147,7 +147,7 @@ bool tr_session_id::is_local(std::string_view session_id) noexcept
|
|||
if (error)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't open session lock file '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't open session lock file '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", lockfile_path),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
|
|
@ -414,7 +414,7 @@ tr_session::BoundSocket::BoundSocket(
|
|||
}
|
||||
|
||||
tr_logAddInfo(fmt::format(
|
||||
_("Listening to incoming peer connections on {hostport}"),
|
||||
fmt::runtime(_("Listening to incoming peer connections on {hostport}")),
|
||||
fmt::arg("hostport", tr_socket_address::display_name(addr, port))));
|
||||
event_add(ev_.get(), nullptr);
|
||||
}
|
||||
|
@ -732,7 +732,8 @@ void tr_session::initImpl(init_data& data)
|
|||
|
||||
blocklists_.load(blocklist_dir_, blocklist_enabled());
|
||||
|
||||
tr_logAddInfo(fmt::format(_("Transmission version {version} starting"), fmt::arg("version", LONG_VERSION_STRING)));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Transmission version {version} starting")), fmt::arg("version", LONG_VERSION_STRING)));
|
||||
|
||||
setSettings(settings, true);
|
||||
|
||||
|
@ -1418,7 +1419,8 @@ void tr_sessionClose(tr_session* session, size_t timeout_secs)
|
|||
TR_ASSERT(session != nullptr);
|
||||
TR_ASSERT(!session->am_in_session_thread());
|
||||
|
||||
tr_logAddInfo(fmt::format(_("Transmission version {version} shutting down"), fmt::arg("version", LONG_VERSION_STRING)));
|
||||
tr_logAddInfo(
|
||||
fmt::format(fmt::runtime(_("Transmission version {version} shutting down")), fmt::arg("version", LONG_VERSION_STRING)));
|
||||
|
||||
auto closed_promise = std::promise<void>{};
|
||||
auto closed_future = closed_promise.get_future();
|
||||
|
@ -1465,7 +1467,7 @@ void session_load_torrents(tr_session* session, tr_ctor* ctor, std::promise<size
|
|||
if (n_torrents != 0U)
|
||||
{
|
||||
tr_logAddInfo(fmt::format(
|
||||
tr_ngettext("Loaded {count} torrent", "Loaded {count} torrents", n_torrents),
|
||||
fmt::runtime(tr_ngettext("Loaded {count} torrent", "Loaded {count} torrents", n_torrents)),
|
||||
fmt::arg("count", n_torrents)));
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uintX_t
|
||||
#include <ctime> // time_t
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <chrono>
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int64_t, uint32_t
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
|
|
@ -20,6 +20,12 @@ namespace libtransmission
|
|||
class Settings
|
||||
{
|
||||
public:
|
||||
virtual ~Settings() = default;
|
||||
Settings(Settings const& settings) = default;
|
||||
Settings& operator=(Settings const& other) = default;
|
||||
Settings(Settings&& settings) noexcept = default;
|
||||
Settings& operator=(Settings&& other) noexcept = default;
|
||||
|
||||
void load(tr_variant const& src);
|
||||
|
||||
[[nodiscard]] tr_variant save() const;
|
||||
|
|
|
@ -32,15 +32,15 @@ namespace
|
|||
|
||||
using file_func_t = std::function<void(char const* filename)>;
|
||||
|
||||
bool isFolder(std::string_view path)
|
||||
bool is_folder(std::string_view path)
|
||||
{
|
||||
auto const info = tr_sys_path_get_info(path);
|
||||
return info && info->isFolder();
|
||||
}
|
||||
|
||||
bool isEmptyFolder(char const* path)
|
||||
bool is_empty_folder(char const* path)
|
||||
{
|
||||
if (!isFolder(path))
|
||||
if (!is_folder(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -63,9 +63,9 @@ bool isEmptyFolder(char const* path)
|
|||
return true;
|
||||
}
|
||||
|
||||
void depthFirstWalk(char const* path, file_func_t const& func, std::optional<int> max_depth = {})
|
||||
void depth_first_walk(char const* path, file_func_t const& func, std::optional<int> max_depth = {})
|
||||
{
|
||||
if (isFolder(path) && (!max_depth || *max_depth > 0))
|
||||
if (is_folder(path) && (!max_depth || *max_depth > 0))
|
||||
{
|
||||
if (auto const odir = tr_sys_dir_open(path); odir != TR_BAD_SYS_DIR)
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ void depthFirstWalk(char const* path, file_func_t const& func, std::optional<int
|
|||
continue;
|
||||
}
|
||||
|
||||
depthFirstWalk(tr_pathbuf{ path, '/', name }.c_str(), func, max_depth ? *max_depth - 1 : max_depth);
|
||||
depth_first_walk(tr_pathbuf{ path, '/', name }.c_str(), func, max_depth ? *max_depth - 1 : max_depth);
|
||||
}
|
||||
|
||||
tr_sys_dir_close(odir);
|
||||
|
@ -88,7 +88,7 @@ void depthFirstWalk(char const* path, file_func_t const& func, std::optional<int
|
|||
func(path);
|
||||
}
|
||||
|
||||
bool isJunkFile(std::string_view filename)
|
||||
bool is_junk_file(std::string_view filename)
|
||||
{
|
||||
auto const base = tr_sys_path_basename(filename);
|
||||
|
||||
|
@ -141,9 +141,9 @@ std::optional<tr_torrent_files::FoundFile> tr_torrent_files::find(
|
|||
return {};
|
||||
}
|
||||
|
||||
bool tr_torrent_files::hasAnyLocalData(std::string_view const* paths, size_t n_paths) const
|
||||
bool tr_torrent_files::has_any_local_data(std::string_view const* paths, size_t n_paths) const
|
||||
{
|
||||
for (tr_file_index_t i = 0, n = fileCount(); i < n; ++i)
|
||||
for (tr_file_index_t i = 0, n = file_count(); i < n; ++i)
|
||||
{
|
||||
if (find(i, paths, n_paths))
|
||||
{
|
||||
|
@ -180,7 +180,7 @@ bool tr_torrent_files::move(
|
|||
|
||||
auto err = bool{};
|
||||
|
||||
for (tr_file_index_t i = 0, n = fileCount(); i < n; ++i)
|
||||
for (tr_file_index_t i = 0, n = file_count(); i < n; ++i)
|
||||
{
|
||||
auto const found = find(i, std::data(paths), std::size(paths));
|
||||
if (!found)
|
||||
|
@ -210,7 +210,7 @@ bool tr_torrent_files::move(
|
|||
{
|
||||
auto const remove_empty_directories = [](char const* filename)
|
||||
{
|
||||
if (isEmptyFolder(filename))
|
||||
if (is_empty_folder(filename))
|
||||
{
|
||||
tr_sys_path_remove(filename, nullptr);
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
|
|||
|
||||
// move the local data to the tmpdir
|
||||
auto const paths = std::array<std::string_view, 1>{ parent.sv() };
|
||||
for (tr_file_index_t idx = 0, n_files = fileCount(); idx < n_files; ++idx)
|
||||
for (tr_file_index_t idx = 0, n_files = file_count(); idx < n_files; ++idx)
|
||||
{
|
||||
if (auto const found = find(idx, std::data(paths), std::size(paths)); found)
|
||||
{
|
||||
|
@ -261,7 +261,7 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
|
|||
// because we'll need it below in the 'remove junk' phase
|
||||
auto const path = tr_pathbuf{ parent, '/', tmpdir_prefix };
|
||||
auto top_files = std::set<std::string>{ std::string{ path } };
|
||||
depthFirstWalk(
|
||||
depth_first_walk(
|
||||
tmpdir,
|
||||
[&parent, &tmpdir, &top_files](char const* filename)
|
||||
{
|
||||
|
@ -285,8 +285,8 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
|
|||
// the folder hierarchy by removing top-level files & folders first.
|
||||
// But that can fail -- e.g. `func` might refuse to remove nonempty
|
||||
// directories -- so plan B is to remove everything bottom-up.
|
||||
depthFirstWalk(tmpdir, func_wrapper, 1);
|
||||
depthFirstWalk(tmpdir, func_wrapper);
|
||||
depth_first_walk(tmpdir, func_wrapper, 1);
|
||||
depth_first_walk(tmpdir, func_wrapper);
|
||||
tr_sys_path_remove(tmpdir);
|
||||
|
||||
// OK we've removed the local data.
|
||||
|
@ -294,23 +294,23 @@ void tr_torrent_files::remove(std::string_view parent_in, std::string_view tmpdi
|
|||
// Remove the first two categories and leave the third alone.
|
||||
auto const remove_junk = [](char const* filename)
|
||||
{
|
||||
if (isEmptyFolder(filename) || isJunkFile(filename))
|
||||
if (is_empty_folder(filename) || is_junk_file(filename))
|
||||
{
|
||||
tr_sys_path_remove(filename);
|
||||
}
|
||||
};
|
||||
for (auto const& filename : top_files)
|
||||
{
|
||||
depthFirstWalk(filename.c_str(), remove_junk);
|
||||
depth_first_walk(filename.c_str(), remove_junk);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// `isUnixReservedFile` and `isWin32ReservedFile` kept as `maybe_unused`
|
||||
// `is_unix_reserved_file` and `is_win32_reserved_file` kept as `maybe_unused`
|
||||
// for potential support of different filesystems on the same OS
|
||||
[[nodiscard, maybe_unused]] bool isUnixReservedFile(std::string_view in) noexcept
|
||||
[[nodiscard, maybe_unused]] bool is_unix_reserved_file(std::string_view in) noexcept
|
||||
{
|
||||
static auto constexpr ReservedNames = std::array<std::string_view, 2>{
|
||||
"."sv,
|
||||
|
@ -325,7 +325,7 @@ namespace
|
|||
// COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
|
||||
// Also avoid these names followed immediately by an extension;
|
||||
// for example, NUL.txt is not recommended.
|
||||
[[nodiscard, maybe_unused]] bool isWin32ReservedFile(std::string_view in) noexcept
|
||||
[[nodiscard, maybe_unused]] bool is_win32_reserved_file(std::string_view in) noexcept
|
||||
{
|
||||
if (std::empty(in))
|
||||
{
|
||||
|
@ -365,18 +365,22 @@ namespace
|
|||
[in_upper_sv](auto const& prefix) { return tr_strv_starts_with(in_upper_sv, prefix); });
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isReservedFile(std::string_view in) noexcept
|
||||
[[nodiscard]] bool is_reserved_file(std::string_view in, bool os_specific) noexcept
|
||||
{
|
||||
if (!os_specific)
|
||||
{
|
||||
return is_unix_reserved_file(in) || is_win32_reserved_file(in);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
return isWin32ReservedFile(in);
|
||||
return is_win32_reserved_file(in);
|
||||
#else
|
||||
return isUnixReservedFile(in);
|
||||
return is_unix_reserved_file(in);
|
||||
#endif
|
||||
}
|
||||
|
||||
// `isUnixReservedChar` and `isWin32ReservedChar` kept as `maybe_unused`
|
||||
// `is_unix_reserved_char` and `is_win32_reserved_char` kept as `maybe_unused`
|
||||
// for potential support of different filesystems on the same OS
|
||||
[[nodiscard, maybe_unused]] auto constexpr isUnixReservedChar(unsigned char ch) noexcept
|
||||
[[nodiscard, maybe_unused]] auto constexpr is_unix_reserved_char(unsigned char ch) noexcept
|
||||
{
|
||||
return ch == '/';
|
||||
}
|
||||
|
@ -385,7 +389,7 @@ namespace
|
|||
// Use any character in the current code page for a name, including Unicode
|
||||
// characters and characters in the extended character set (128–255),
|
||||
// except for the following:
|
||||
[[nodiscard, maybe_unused]] auto constexpr isWin32ReservedChar(unsigned char ch) noexcept
|
||||
[[nodiscard, maybe_unused]] auto constexpr is_win32_reserved_char(unsigned char ch) noexcept
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
|
@ -404,17 +408,21 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto constexpr isReservedChar(unsigned char ch) noexcept
|
||||
[[nodiscard]] auto constexpr is_reserved_char(unsigned char ch, bool os_specific) noexcept
|
||||
{
|
||||
if (!os_specific)
|
||||
{
|
||||
return is_unix_reserved_char(ch) || is_win32_reserved_char(ch);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
return isWin32ReservedChar(ch);
|
||||
return is_win32_reserved_char(ch);
|
||||
#else
|
||||
return isUnixReservedChar(ch);
|
||||
return is_unix_reserved_char(ch);
|
||||
#endif
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations
|
||||
void appendSanitizedComponent(std::string_view in, tr_pathbuf& out)
|
||||
void append_sanitized_component(std::string_view in, tr_pathbuf& out, bool os_specific)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// remove leading and trailing spaces
|
||||
|
@ -428,27 +436,27 @@ void appendSanitizedComponent(std::string_view in, tr_pathbuf& out)
|
|||
#endif
|
||||
|
||||
// replace reserved filenames with an underscore
|
||||
if (isReservedFile(in))
|
||||
if (is_reserved_file(in, os_specific))
|
||||
{
|
||||
out.append('_');
|
||||
}
|
||||
|
||||
// replace reserved characters with an underscore
|
||||
static auto constexpr AddChar = [](auto ch)
|
||||
auto const add_char = [os_specific](auto ch)
|
||||
{
|
||||
return isReservedChar(ch) ? '_' : ch;
|
||||
return is_reserved_char(ch, os_specific) ? '_' : ch;
|
||||
};
|
||||
std::transform(std::begin(in), std::end(in), std::back_inserter(out), AddChar);
|
||||
std::transform(std::begin(in), std::end(in), std::back_inserter(out), add_char);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void tr_torrent_files::makeSubpathPortable(std::string_view path, tr_pathbuf& append_me)
|
||||
void tr_torrent_files::sanitize_subpath(std::string_view path, tr_pathbuf& append_me, bool os_specific)
|
||||
{
|
||||
auto segment = std::string_view{};
|
||||
while (tr_strv_sep(&path, &segment, '/'))
|
||||
{
|
||||
appendSanitizedComponent(segment, append_me);
|
||||
append_sanitized_component(segment, append_me, os_specific);
|
||||
append_me.append('/');
|
||||
}
|
||||
|
||||
|
|
|
@ -35,17 +35,17 @@ public:
|
|||
return std::empty(files_);
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 size_t fileCount() const noexcept
|
||||
[[nodiscard]] TR_CONSTEXPR20 size_t file_count() const noexcept
|
||||
{
|
||||
return std::size(files_);
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 uint64_t fileSize(tr_file_index_t file_index) const
|
||||
[[nodiscard]] TR_CONSTEXPR20 uint64_t file_size(tr_file_index_t file_index) const
|
||||
{
|
||||
return files_.at(file_index).size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto totalSize() const noexcept
|
||||
[[nodiscard]] constexpr auto total_size() const noexcept
|
||||
{
|
||||
return total_size_;
|
||||
}
|
||||
|
@ -55,12 +55,12 @@ public:
|
|||
return files_.at(file_index).path_;
|
||||
}
|
||||
|
||||
void setPath(tr_file_index_t file_index, std::string_view path)
|
||||
void set_path(tr_file_index_t file_index, std::string_view path)
|
||||
{
|
||||
files_.at(file_index).setPath(path);
|
||||
files_.at(file_index).set_path(path);
|
||||
}
|
||||
|
||||
void insertSubpathPrefix(std::string_view path)
|
||||
void insert_subpath_prefix(std::string_view path)
|
||||
{
|
||||
auto const buf = tr_pathbuf{ path, '/' };
|
||||
|
||||
|
@ -76,7 +76,7 @@ public:
|
|||
files_.reserve(n_files);
|
||||
}
|
||||
|
||||
void shrinkToFit()
|
||||
void shrink_to_fit()
|
||||
{
|
||||
files_.shrink_to_fit();
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ public:
|
|||
total_size_ = uint64_t{};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto sortedByPath() const
|
||||
[[nodiscard]] auto sorted_by_path() const
|
||||
{
|
||||
auto ret = std::vector<std::pair<std::string /*path*/, uint64_t /*size*/>>{};
|
||||
ret.reserve(std::size(files_));
|
||||
|
@ -153,20 +153,20 @@ public:
|
|||
};
|
||||
|
||||
[[nodiscard]] std::optional<FoundFile> find(tr_file_index_t file, std::string_view const* paths, size_t n_paths) const;
|
||||
[[nodiscard]] bool hasAnyLocalData(std::string_view const* paths, size_t n_paths) const;
|
||||
[[nodiscard]] bool has_any_local_data(std::string_view const* paths, size_t n_paths) const;
|
||||
|
||||
static void makeSubpathPortable(std::string_view path, tr_pathbuf& append_me);
|
||||
static void sanitize_subpath(std::string_view path, tr_pathbuf& append_me, bool os_specific = true);
|
||||
|
||||
[[nodiscard]] static auto makeSubpathPortable(std::string_view path)
|
||||
[[nodiscard]] static auto sanitize_subpath(std::string_view path, bool os_specific = true)
|
||||
{
|
||||
auto tmp = tr_pathbuf{};
|
||||
makeSubpathPortable(path, tmp);
|
||||
sanitize_subpath(path, tmp, os_specific);
|
||||
return std::string{ tmp.sv() };
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool isSubpathPortable(std::string_view path)
|
||||
[[nodiscard]] static bool is_subpath_sanitized(std::string_view path, bool os_specific = true)
|
||||
{
|
||||
return makeSubpathPortable(path) == path;
|
||||
return sanitize_subpath(path, os_specific) == path;
|
||||
}
|
||||
|
||||
static constexpr std::string_view PartialFileSuffix = ".part";
|
||||
|
@ -175,7 +175,7 @@ private:
|
|||
struct file_t
|
||||
{
|
||||
public:
|
||||
void setPath(std::string_view subpath)
|
||||
void set_path(std::string_view subpath)
|
||||
{
|
||||
if (path_ != subpath)
|
||||
{
|
||||
|
|
|
@ -68,6 +68,15 @@ tr_metadata_download::tr_metadata_download(std::string_view log_name, int64_t co
|
|||
create_all_needed(n);
|
||||
}
|
||||
|
||||
void tr_torrent::do_magnet_idle_work()
|
||||
{
|
||||
if (auto& m = metadata_download_; m && m->is_complete())
|
||||
{
|
||||
tr_logAddDebugTor(this, fmt::format("we now have all the metainfo!"));
|
||||
on_have_all_metainfo();
|
||||
}
|
||||
}
|
||||
|
||||
void tr_torrent::maybe_start_metadata_transfer(int64_t const size) noexcept
|
||||
{
|
||||
if (has_metainfo() || metadata_download_)
|
||||
|
@ -262,20 +271,20 @@ void tr_torrent::on_have_all_metainfo()
|
|||
m.reset();
|
||||
}
|
||||
|
||||
bool tr_metadata_download::set_metadata_piece(int64_t const piece, void const* const data, size_t const len)
|
||||
void tr_metadata_download::set_metadata_piece(int64_t const piece, void const* const data, size_t const len)
|
||||
{
|
||||
TR_ASSERT(data != nullptr);
|
||||
|
||||
// sanity test: is `piece` in range?
|
||||
if (piece < 0 || piece >= piece_count_)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// sanity test: is `len` the right size?
|
||||
if (get_piece_length(piece) != len)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// do we need this piece?
|
||||
|
@ -286,7 +295,7 @@ bool tr_metadata_download::set_metadata_piece(int64_t const piece, void const* c
|
|||
[piece](auto const& item) { return item.piece == piece; });
|
||||
if (iter == std::end(needed))
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const offset = piece * MetadataPieceSize;
|
||||
|
@ -294,8 +303,6 @@ bool tr_metadata_download::set_metadata_piece(int64_t const piece, void const* c
|
|||
|
||||
needed.erase(iter);
|
||||
tr_logAddDebugMagnet(this, fmt::format("saving metainfo piece {}... {} remain", piece, std::size(needed)));
|
||||
|
||||
return std::empty(needed);
|
||||
}
|
||||
|
||||
void tr_torrent::set_metadata_piece(int64_t const piece, void const* const data, size_t const len)
|
||||
|
@ -304,10 +311,9 @@ void tr_torrent::set_metadata_piece(int64_t const piece, void const* const data,
|
|||
|
||||
tr_logAddDebugTor(this, fmt::format("got metadata piece {} of {} bytes", piece, len));
|
||||
|
||||
if (auto& m = metadata_download_; m && m->set_metadata_piece(piece, data, len))
|
||||
if (auto& m = metadata_download_)
|
||||
{
|
||||
tr_logAddDebugTor(this, fmt::format("we now have all the metainfo!"));
|
||||
on_have_all_metainfo();
|
||||
m->set_metadata_piece(piece, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <ctime> // time_t
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -39,7 +38,12 @@ public:
|
|||
return size > 0 && size <= std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
bool set_metadata_piece(int64_t piece, void const* data, size_t len);
|
||||
[[nodiscard]] auto is_complete() const noexcept
|
||||
{
|
||||
return std::empty(pieces_needed_);
|
||||
}
|
||||
|
||||
void set_metadata_piece(int64_t piece, void const* data, size_t len);
|
||||
|
||||
[[nodiscard]] std::optional<int64_t> get_next_metadata_request(time_t now) noexcept;
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
|
|||
{
|
||||
file_subpath_ += '/';
|
||||
}
|
||||
tr_torrent_files::makeSubpathPortable(currentKey(), file_subpath_);
|
||||
tr_torrent_files::sanitize_subpath(currentKey(), file_subpath_);
|
||||
}
|
||||
else if (pathIs(InfoKey))
|
||||
{
|
||||
|
@ -298,7 +298,7 @@ struct MetainfoHandler final : public transmission::benc::BasicHandler<MaxBencDe
|
|||
{
|
||||
file_subpath_ += '/';
|
||||
}
|
||||
tr_torrent_files::makeSubpathPortable(value, file_subpath_);
|
||||
tr_torrent_files::sanitize_subpath(value, file_subpath_);
|
||||
}
|
||||
else if (current_key == AttrKey)
|
||||
{
|
||||
|
@ -488,10 +488,10 @@ private:
|
|||
}
|
||||
|
||||
auto root = tr_pathbuf{};
|
||||
tr_torrent_files::makeSubpathPortable(tm_.name_, root);
|
||||
tr_torrent_files::sanitize_subpath(tm_.name_, root);
|
||||
if (!std::empty(root))
|
||||
{
|
||||
tm_.files_.insertSubpathPrefix(root);
|
||||
tm_.files_.insert_subpath_prefix(root);
|
||||
}
|
||||
|
||||
TR_ASSERT(info_dict_begin_[0] == 'd');
|
||||
|
@ -522,7 +522,7 @@ private:
|
|||
// In the single file case, length maps to the length of the file in bytes.
|
||||
if (tm_.file_count() == 0 && length_ != 0 && !std::empty(tm_.name_))
|
||||
{
|
||||
tm_.files_.add(tm_.name_, length_);
|
||||
tm_.files_.add(tr_torrent_files::sanitize_subpath(tm_.name_), length_);
|
||||
}
|
||||
|
||||
if (auto const has_metainfo = tm_.info_dict_size() != 0U; has_metainfo)
|
||||
|
@ -546,7 +546,7 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
tm_.block_info_ = tr_block_info{ tm_.files_.totalSize(), piece_size_ };
|
||||
tm_.block_info_ = tr_block_info{ tm_.files_.total_size(), piece_size_ };
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -710,7 +710,7 @@ bool tr_torrent_metainfo::migrate_file(
|
|||
{
|
||||
tr_logAddError(
|
||||
fmt::format(
|
||||
_("Migrated torrent file from '{old_path}' to '{path}'"),
|
||||
fmt::runtime(_("Migrated torrent file from '{old_path}' to '{path}'")),
|
||||
fmt::arg("old_path", old_filename),
|
||||
fmt::arg("path", new_filename)),
|
||||
name);
|
||||
|
|
|
@ -44,11 +44,11 @@ public:
|
|||
}
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto file_count() const noexcept
|
||||
{
|
||||
return files().fileCount();
|
||||
return files().file_count();
|
||||
}
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto file_size(tr_file_index_t i) const
|
||||
{
|
||||
return files().fileSize(i);
|
||||
return files().file_size(i);
|
||||
}
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto const& file_subpath(tr_file_index_t i) const
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ public:
|
|||
|
||||
void set_file_subpath(tr_file_index_t i, std::string_view subpath)
|
||||
{
|
||||
files_.setPath(i, subpath);
|
||||
files_.set_path(i, subpath);
|
||||
}
|
||||
|
||||
/// BLOCK INFO
|
||||
|
|
|
@ -135,7 +135,7 @@ bool tr_torrentSetMetainfoFromFile(tr_torrent* tor, tr_torrent_metainfo const* m
|
|||
if (error)
|
||||
{
|
||||
tor->error().set_local_error(fmt::format(
|
||||
_("Couldn't use metainfo from '{path}' for '{magnet}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't use metainfo from '{path}' for '{magnet}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("magnet", tor->magnet()),
|
||||
fmt::arg("error", error.message()),
|
||||
|
@ -399,7 +399,7 @@ void torrentCallScript(tr_torrent const* tor, std::string const& script)
|
|||
{ "TR_TORRENT_TRACKERS"sv, trackers_str },
|
||||
};
|
||||
|
||||
tr_logAddInfoTor(tor, fmt::format(_("Calling script '{path}'"), fmt::arg("path", script)));
|
||||
tr_logAddInfoTor(tor, fmt::format(fmt::runtime(_("Calling script '{path}'")), fmt::arg("path", script)));
|
||||
|
||||
auto error = tr_error{};
|
||||
if (!tr_spawn_async(std::data(cmd), env, TR_IF_WIN32("\\", "/"), &error))
|
||||
|
@ -407,7 +407,7 @@ void torrentCallScript(tr_torrent const* tor, std::string const& script)
|
|||
tr_logAddWarnTor(
|
||||
tor,
|
||||
fmt::format(
|
||||
_("Couldn't call script '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't call script '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", script),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -446,7 +446,7 @@ void tr_torrent::stop_if_seed_limit_reached()
|
|||
session->onRatioLimitHit(this);
|
||||
}
|
||||
/* if we're seeding and reach our inactivity limit, stop the torrent */
|
||||
else if (auto const secs_left = idle_seconds_left(tr_time()); secs_left && *secs_left == 0U)
|
||||
else if (auto const secs_left = idle_seconds_left(tr_time()); secs_left && *secs_left <= 0U)
|
||||
{
|
||||
tr_logAddInfoTor(this, _("Seeding idle limit reached; pausing torrent"));
|
||||
|
||||
|
@ -756,6 +756,10 @@ void tr_torrent::stop_now()
|
|||
TR_ASSERT(session->am_in_session_thread());
|
||||
auto const lock = unique_lock();
|
||||
|
||||
auto const now = tr_time();
|
||||
seconds_downloading_before_current_start_ = seconds_downloading(now);
|
||||
seconds_seeding_before_current_start_ = seconds_seeding(now);
|
||||
|
||||
is_running_ = false;
|
||||
is_stopping_ = false;
|
||||
mark_changed();
|
||||
|
@ -895,7 +899,7 @@ void tr_torrent::on_metainfo_completed()
|
|||
// Potentially, we are in `tr_torrent::init`,
|
||||
// and we don't want any file created before `tr_torrent::start`
|
||||
// so we Verify but we don't Create files.
|
||||
session->queue_session_thread(tr_torrentVerify, this);
|
||||
tr_torrentVerify(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1031,7 +1035,7 @@ void tr_torrent::init(tr_ctor const& ctor)
|
|||
if (error)
|
||||
{
|
||||
this->error().set_local_error(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -1123,7 +1127,7 @@ void tr_torrent::set_location_in_session_thread(std::string_view const path, boo
|
|||
if (error)
|
||||
{
|
||||
this->error().set_local_error(fmt::format(
|
||||
_("Couldn't move '{old_path}' to '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't move '{old_path}' to '{path}': {error} ({error_code})")),
|
||||
fmt::arg("old_path", current_dir()),
|
||||
fmt::arg("path", path),
|
||||
fmt::arg("error", error.message()),
|
||||
|
@ -1208,7 +1212,7 @@ bool tr_torrent::has_any_local_data() const
|
|||
|
||||
auto paths = std::array<std::string_view, 4>{};
|
||||
auto const n_paths = buildSearchPathArray(this, std::data(paths));
|
||||
return files().hasAnyLocalData(std::data(paths), n_paths);
|
||||
return files().has_any_local_data(std::data(paths), n_paths);
|
||||
}
|
||||
|
||||
void tr_torrentSetDownloadDir(tr_torrent* tor, char const* path)
|
||||
|
@ -1604,6 +1608,39 @@ std::optional<std::string> tr_torrent::VerifyMediator::find_file(tr_file_index_t
|
|||
return {};
|
||||
}
|
||||
|
||||
void tr_torrent::update_file_path(tr_file_index_t file, std::optional<bool> has_file) const
|
||||
{
|
||||
auto const found = find_file(file);
|
||||
if (!found)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const has = has_file ? *has_file : this->has_file(file);
|
||||
auto const needs_suffix = session->isIncompleteFileNamingEnabled() && !has;
|
||||
auto const oldpath = found->filename();
|
||||
auto const newpath = needs_suffix ?
|
||||
tr_pathbuf{ found->base(), '/', file_subpath(file), tr_torrent_files::PartialFileSuffix } :
|
||||
tr_pathbuf{ found->base(), '/', file_subpath(file) };
|
||||
|
||||
if (tr_sys_path_is_same(oldpath, newpath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto error = tr_error{}; !tr_sys_path_rename(oldpath, newpath, &error))
|
||||
{
|
||||
tr_logAddErrorTor(
|
||||
this,
|
||||
fmt::format(
|
||||
fmt::runtime(_("Couldn't move '{old_path}' to '{path}': {error} ({error_code})")),
|
||||
fmt::arg("old_path", oldpath),
|
||||
fmt::arg("path", newpath),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
}
|
||||
}
|
||||
|
||||
void tr_torrent::VerifyMediator::on_verify_queued()
|
||||
{
|
||||
tr_logAddTraceTor(tor_, "Queued for verification");
|
||||
|
@ -1621,7 +1658,7 @@ void tr_torrent::VerifyMediator::on_piece_checked(tr_piece_index_t const piece,
|
|||
{
|
||||
auto const had_piece = tor_->has_piece(piece);
|
||||
|
||||
if (has_piece || had_piece)
|
||||
if (has_piece != had_piece)
|
||||
{
|
||||
tor_->set_has_piece(piece, has_piece);
|
||||
tor_->set_dirty();
|
||||
|
@ -1660,6 +1697,11 @@ void tr_torrent::VerifyMediator::on_verify_done(bool const aborted)
|
|||
return;
|
||||
}
|
||||
|
||||
for (tr_file_index_t file = 0, n_files = tor->file_count(); file < n_files; ++file)
|
||||
{
|
||||
tor->update_file_path(file, {});
|
||||
}
|
||||
|
||||
tor->recheck_completeness();
|
||||
|
||||
if (tor->verify_done_callback_)
|
||||
|
@ -1972,7 +2014,7 @@ bool tr_torrent::set_announce_list(tr_announce_list announce_list)
|
|||
if (save_error.has_value())
|
||||
{
|
||||
error().set_local_error(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", save_error.message()),
|
||||
fmt::arg("error_code", save_error.code())));
|
||||
|
@ -2025,7 +2067,7 @@ void tr_torrent::on_tracker_response(tr_tracker_event const* event)
|
|||
tr_logAddWarnTor(
|
||||
this,
|
||||
fmt::format(
|
||||
_("Tracker warning: '{warning}' ({url})"),
|
||||
fmt::runtime(_("Tracker warning: '{warning}' ({url})")),
|
||||
fmt::arg("warning", event->text),
|
||||
fmt::arg("url", tr_urlTrackerLogName(event->announce_url))));
|
||||
error_.set_tracker_warning(event->announce_url, event->text);
|
||||
|
@ -2128,27 +2170,7 @@ void tr_torrent::on_file_completed(tr_file_index_t const file)
|
|||
/* if the torrent's current filename isn't the same as the one in the
|
||||
* metadata -- for example, if it had the ".part" suffix appended to
|
||||
* it until now -- then rename it to match the one in the metadata */
|
||||
if (auto found = find_file(file); found)
|
||||
{
|
||||
if (auto const& file_subpath = this->file_subpath(file); file_subpath != found->subpath())
|
||||
{
|
||||
auto const& oldpath = found->filename();
|
||||
auto const newpath = tr_pathbuf{ found->base(), '/', file_subpath };
|
||||
auto error = tr_error{};
|
||||
|
||||
if (!tr_sys_path_rename(oldpath, newpath, &error))
|
||||
{
|
||||
tr_logAddErrorTor(
|
||||
this,
|
||||
fmt::format(
|
||||
_("Couldn't move '{old_path}' to '{path}': {error} ({error_code})"),
|
||||
fmt::arg("old_path", oldpath),
|
||||
fmt::arg("path", newpath),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
}
|
||||
}
|
||||
}
|
||||
update_file_path(file, true);
|
||||
}
|
||||
|
||||
void tr_torrent::on_piece_completed(tr_piece_index_t const piece)
|
||||
|
@ -2161,7 +2183,7 @@ void tr_torrent::on_piece_completed(tr_piece_index_t const piece)
|
|||
// if this piece completes any file, invoke the fileCompleted func for it
|
||||
for (auto [file, file_end] = fpm_.file_span_for_piece(piece); file < file_end; ++file)
|
||||
{
|
||||
if (completion_.has_blocks(block_span_for_file(file)))
|
||||
if (has_file(file))
|
||||
{
|
||||
on_file_completed(file);
|
||||
}
|
||||
|
@ -2544,7 +2566,7 @@ tr_bitfield const& tr_torrent::ResumeHelper::checked_pieces() const noexcept
|
|||
return tor_.checked_pieces_;
|
||||
}
|
||||
|
||||
void tr_torrent::ResumeHelper::load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*fileCount()*/)
|
||||
void tr_torrent::ResumeHelper::load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*file_count()*/)
|
||||
{
|
||||
TR_ASSERT(std::size(checked) == tor_.piece_count());
|
||||
tor_.checked_pieces_ = checked;
|
||||
|
|
|
@ -70,7 +70,7 @@ struct tr_torrent
|
|||
class ResumeHelper
|
||||
{
|
||||
public:
|
||||
void load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*fileCount()*/);
|
||||
void load_checked_pieces(tr_bitfield const& checked, time_t const* mtimes /*file_count()*/);
|
||||
void load_blocks(tr_bitfield blocks);
|
||||
void load_date_added(time_t when) noexcept;
|
||||
void load_date_done(time_t when) noexcept;
|
||||
|
@ -325,6 +325,11 @@ struct tr_torrent
|
|||
return completion_.has_none();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_file(tr_file_index_t file) const
|
||||
{
|
||||
return completion_.has_blocks(block_span_for_file(file));
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_piece(tr_piece_index_t piece) const
|
||||
{
|
||||
return completion_.has_piece(piece);
|
||||
|
@ -372,7 +377,7 @@ struct tr_torrent
|
|||
|
||||
void amount_done_bins(float* tab, int n_tabs) const
|
||||
{
|
||||
return completion_.amount_done(tab, n_tabs);
|
||||
completion_.amount_done(tab, n_tabs);
|
||||
}
|
||||
|
||||
/// FILE <-> PIECE
|
||||
|
@ -827,7 +832,7 @@ struct tr_torrent
|
|||
if (auto const latest = std::max(date_started_, date_active_); latest != 0)
|
||||
{
|
||||
TR_ASSERT(now >= latest);
|
||||
return now - latest;
|
||||
return static_cast<size_t>(std::max(now - latest, time_t{ 0 }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -887,6 +892,8 @@ struct tr_torrent
|
|||
|
||||
void do_idle_work()
|
||||
{
|
||||
do_magnet_idle_work();
|
||||
|
||||
if (needs_completeness_check_)
|
||||
{
|
||||
needs_completeness_check_ = false;
|
||||
|
@ -1244,8 +1251,11 @@ private:
|
|||
void create_empty_files() const;
|
||||
void recheck_completeness();
|
||||
|
||||
void do_magnet_idle_work();
|
||||
[[nodiscard]] bool use_new_metainfo(tr_error* error);
|
||||
|
||||
void update_file_path(tr_file_index_t file, std::optional<bool> has_file) const;
|
||||
|
||||
void set_location_in_session_thread(std::string_view path, bool move_from_old_path, int volatile* setme_state);
|
||||
|
||||
void rename_path_in_session_thread(
|
||||
|
|
|
@ -549,7 +549,7 @@ private:
|
|||
if (line_stream.bad() || std::empty(addrstr))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't parse '{filename}' line: '{line}'"),
|
||||
fmt::runtime(_("Couldn't parse '{filename}' line: '{line}'")),
|
||||
fmt::arg("filename", filename),
|
||||
fmt::arg("line", line)));
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ private:
|
|||
if (int const rc = getaddrinfo(name, port_str.c_str(), &hints, &info); rc != 0)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't look up '{address}:{port}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't look up '{address}:{port}': {error} ({error_code})")),
|
||||
fmt::arg("address", name),
|
||||
fmt::arg("port", port_in.host()),
|
||||
fmt::arg("error", gai_strerror(rc)),
|
||||
|
|
|
@ -9,18 +9,18 @@
|
|||
#include <cstdint> // uint16_t
|
||||
#include <cstring>
|
||||
#include <ctime> // time_t
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h> /* socket(), bind() */
|
||||
#include <netinet/in.h> /* sockaddr_in */
|
||||
#include <sys/socket.h> /* socket(), bind() */
|
||||
#endif
|
||||
|
||||
#include <event2/event.h>
|
||||
|
@ -47,6 +47,8 @@ using namespace std::literals;
|
|||
namespace
|
||||
{
|
||||
|
||||
using ipp_t = std::underlying_type_t<tr_address_type>;
|
||||
|
||||
// opaque value, allowing the sending client to filter out its
|
||||
// own announces if it receives them via multicast loopback
|
||||
auto makeCookie()
|
||||
|
@ -62,8 +64,8 @@ auto makeCookie()
|
|||
return std::string{ std::data(buf), std::size(buf) };
|
||||
}
|
||||
|
||||
constexpr char const* const McastGroup = "239.192.152.143"; /**<LPD multicast group */
|
||||
auto constexpr McastPort = tr_port::from_host(6771); /**<LPD source and destination UPD port */
|
||||
auto constexpr McastSockAddr = std::array{ "239.192.152.143:6771"sv, "[ff15::efc0:988f]:6771"sv };
|
||||
static_assert(std::size(McastSockAddr) == NUM_TR_AF_INET_TYPES);
|
||||
|
||||
/*
|
||||
* A LSD announce is formatted as follows:
|
||||
|
@ -84,14 +86,23 @@ auto constexpr McastPort = tr_port::from_host(6771); /**<LPD source and destinat
|
|||
* multiple infohashes the packet length should not exceed 1400
|
||||
* bytes to avoid MTU/fragmentation problems.
|
||||
*/
|
||||
auto makeAnnounceMsg(std::string_view cookie, tr_port port, std::vector<std::string_view> const& info_hash_strings)
|
||||
std::string makeAnnounceMsg(
|
||||
tr_address_type ip_protocol,
|
||||
std::string_view cookie,
|
||||
tr_port port,
|
||||
std::vector<std::string_view> const& info_hash_strings)
|
||||
{
|
||||
TR_ASSERT(tr_address::is_valid(ip_protocol));
|
||||
if (!tr_address::is_valid(ip_protocol))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ret = fmt::format(
|
||||
"BT-SEARCH * HTTP/1.1\r\n"
|
||||
"Host: {:s}:{:d}\r\n"
|
||||
"Host: {:s}\r\n"
|
||||
"Port: {:d}\r\n",
|
||||
McastGroup,
|
||||
McastPort.host(),
|
||||
McastSockAddr[ip_protocol],
|
||||
port.host());
|
||||
|
||||
for (auto const& info_hash : info_hash_strings)
|
||||
|
@ -230,11 +241,17 @@ public:
|
|||
|
||||
~tr_lpd_impl() override
|
||||
{
|
||||
event_.reset();
|
||||
|
||||
if (mcast_socket_ != TR_BAD_SOCKET)
|
||||
for (auto& event : events_)
|
||||
{
|
||||
tr_net_close_socket(mcast_socket_);
|
||||
event.reset();
|
||||
}
|
||||
|
||||
for (auto const sock : mcast_sockets_)
|
||||
{
|
||||
if (sock != TR_BAD_SOCKET)
|
||||
{
|
||||
tr_net_close_socket(sock);
|
||||
}
|
||||
}
|
||||
|
||||
tr_logAddTrace("Done uninitialising Local Peer Discovery");
|
||||
|
@ -243,19 +260,34 @@ public:
|
|||
private:
|
||||
bool init(struct event_base* event_base)
|
||||
{
|
||||
if (initImpl(event_base))
|
||||
ipp_t n_success = NUM_TR_AF_INET_TYPES;
|
||||
if (!initImpl<TR_AF_INET>(event_base))
|
||||
{
|
||||
return true;
|
||||
auto const err = sockerrno;
|
||||
tr_net_close_socket(mcast_sockets_[TR_AF_INET]);
|
||||
mcast_sockets_[TR_AF_INET] = TR_BAD_SOCKET;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't initialize {ip_protocol} LPD: {error} ({error_code})"),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(TR_AF_INET)),
|
||||
fmt::arg("error", tr_strerror(err)),
|
||||
fmt::arg("error_code", err)));
|
||||
--n_success;
|
||||
}
|
||||
|
||||
auto const err = sockerrno;
|
||||
tr_net_close_socket(mcast_socket_);
|
||||
mcast_socket_ = TR_BAD_SOCKET;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't initialize LPD: {error} ({error_code})"),
|
||||
fmt::arg("error", tr_strerror(err)),
|
||||
fmt::arg("error_code", err)));
|
||||
return false;
|
||||
if (!initImpl<TR_AF_INET6>(event_base))
|
||||
{
|
||||
auto const err = sockerrno;
|
||||
tr_net_close_socket(mcast_sockets_[TR_AF_INET6]);
|
||||
mcast_sockets_[TR_AF_INET6] = TR_BAD_SOCKET;
|
||||
tr_logAddWarn(fmt::format(
|
||||
fmt::runtime(_("Couldn't initialize {ip_protocol} LPD: {error} ({error_code})")),
|
||||
fmt::arg("ip_protocol", tr_ip_protocol_to_sv(TR_AF_INET6)),
|
||||
fmt::arg("error", tr_strerror(err)),
|
||||
fmt::arg("error_code", err)));
|
||||
--n_success;
|
||||
}
|
||||
|
||||
return n_success != 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -263,76 +295,80 @@ private:
|
|||
*
|
||||
* For the most part, this means setting up an appropriately configured multicast socket
|
||||
* and event-based message handling.
|
||||
*
|
||||
* @remark Since the LPD service does not use another protocol family yet, this code is
|
||||
* IPv4 only for the time being.
|
||||
*/
|
||||
template<tr_address_type ip_protocol>
|
||||
bool initImpl(struct event_base* event_base)
|
||||
{
|
||||
auto const opt_on = 1;
|
||||
auto& sock = mcast_sockets_[ip_protocol];
|
||||
|
||||
static_assert(AnnounceScope > 0);
|
||||
static_assert(tr_address::is_valid(ip_protocol));
|
||||
|
||||
tr_logAddDebug("Initialising Local Peer Discovery");
|
||||
tr_logAddDebug(fmt::format("Initialising {} Local Peer Discovery", tr_ip_protocol_to_sv(ip_protocol)));
|
||||
|
||||
/* setup datagram socket */
|
||||
// setup datagram socket
|
||||
sock = socket(tr_ip_protocol_to_af(ip_protocol), SOCK_DGRAM, 0);
|
||||
|
||||
if (sock == TR_BAD_SOCKET)
|
||||
{
|
||||
mcast_socket_ = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mcast_socket_ == TR_BAD_SOCKET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (evutil_make_socket_nonblocking(sock) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (evutil_make_socket_nonblocking(mcast_socket_) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setsockopt(mcast_socket_, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char const*>(&opt_on), sizeof(opt_on)) ==
|
||||
-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char const*>(&opt_on), sizeof(opt_on)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if HAVE_SO_REUSEPORT
|
||||
if (setsockopt(mcast_socket_, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char const*>(&opt_on), sizeof(opt_on)) ==
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char const*>(&opt_on), sizeof(opt_on)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if constexpr (ip_protocol == TR_AF_INET6)
|
||||
{
|
||||
// must be done before binding on Linux
|
||||
if (evutil_make_listen_socket_ipv6only(sock) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto const mcast_sockaddr = tr_socket_address::from_string(McastSockAddr[ip_protocol]);
|
||||
TR_ASSERT(mcast_sockaddr);
|
||||
auto const [mcast_ss, mcast_sslen] = mcast_sockaddr->to_sockaddr();
|
||||
|
||||
auto const [bind_ss, bind_sslen] = tr_socket_address::to_sockaddr(tr_address::any(ip_protocol), mcast_sockaddr->port());
|
||||
if (bind(sock, reinterpret_cast<sockaddr const*>(&bind_ss), bind_sslen) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (ip_protocol == TR_AF_INET)
|
||||
{
|
||||
std::memcpy(&mcast_addr_, &mcast_ss, mcast_sslen);
|
||||
|
||||
// we want to join that LPD multicast group
|
||||
struct ip_mreq mcast_req = {};
|
||||
mcast_req.imr_multiaddr = mcast_addr_.sin_addr;
|
||||
mcast_req.imr_interface = mediator_.bind_address(ip_protocol).addr.addr4;
|
||||
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char const*>(&mcast_req), sizeof(mcast_req)) ==
|
||||
-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto const [bind_ss, bind_sslen] = tr_socket_address::to_sockaddr(mediator_.bind_address(TR_AF_INET), McastPort);
|
||||
|
||||
if (bind(mcast_socket_, reinterpret_cast<sockaddr const*>(&bind_ss), bind_sslen) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const mcast_addr = tr_address::from_string(McastGroup);
|
||||
TR_ASSERT(mcast_addr);
|
||||
auto const [mcast_ss, mcast_sslen] = tr_socket_address::to_sockaddr(*mcast_addr, McastPort);
|
||||
std::memcpy(&mcast_addr_, &mcast_ss, mcast_sslen);
|
||||
|
||||
/* we want to join that LPD multicast group */
|
||||
ip_mreq mcast_req = {};
|
||||
mcast_req.imr_multiaddr = mcast_addr_.sin_addr;
|
||||
mcast_req.imr_interface = reinterpret_cast<sockaddr_in const*>(&bind_ss)->sin_addr;
|
||||
|
||||
// configure outbound multicast TTL
|
||||
if (setsockopt(
|
||||
mcast_socket_,
|
||||
IPPROTO_IP,
|
||||
IP_ADD_MEMBERSHIP,
|
||||
reinterpret_cast<char const*>(&mcast_req),
|
||||
sizeof(mcast_req)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* configure outbound multicast TTL */
|
||||
if (setsockopt(
|
||||
mcast_socket_,
|
||||
sock,
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_TTL,
|
||||
reinterpret_cast<char const*>(&AnnounceScope),
|
||||
|
@ -340,12 +376,71 @@ private:
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setsockopt(
|
||||
sock,
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
reinterpret_cast<char const*>(&mcast_req.imr_interface),
|
||||
sizeof(mcast_req.imr_interface)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// needed to announce to BT clients on the same interface
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char const*>(&opt_on), sizeof(opt_on)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // TR_AF_INET6
|
||||
{
|
||||
std::memcpy(&mcast6_addr_, &mcast_ss, mcast_sslen);
|
||||
|
||||
// we want to join that LPD multicast group
|
||||
struct ipv6_mreq mcast_req = {};
|
||||
mcast_req.ipv6mr_multiaddr = mcast6_addr_.sin6_addr;
|
||||
mcast_req.ipv6mr_interface = mediator_.bind_address(ip_protocol).to_interface_index().value_or(0);
|
||||
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, reinterpret_cast<char const*>(&mcast_req), sizeof(mcast_req)) ==
|
||||
-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// configure outbound multicast TTL
|
||||
if (setsockopt(
|
||||
sock,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_MULTICAST_HOPS,
|
||||
reinterpret_cast<char const*>(&AnnounceScope),
|
||||
sizeof(AnnounceScope)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setsockopt(
|
||||
sock,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_MULTICAST_IF,
|
||||
reinterpret_cast<char const*>(&mcast_req.ipv6mr_interface),
|
||||
sizeof(mcast_req.ipv6mr_interface)) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// needed to announce to BT clients on the same interface
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, reinterpret_cast<char const*>(&opt_on), sizeof(opt_on)) ==
|
||||
-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
event_.reset(event_new(event_base, mcast_socket_, EV_READ | EV_PERSIST, event_callback, this));
|
||||
event_add(event_.get(), nullptr);
|
||||
events_[ip_protocol].reset(event_new(event_base, sock, EV_READ | EV_PERSIST, event_callback<ip_protocol>, this));
|
||||
event_add(events_[ip_protocol].get(), nullptr);
|
||||
|
||||
tr_logAddDebug("Local Peer Discovery initialised");
|
||||
tr_logAddDebug(fmt::format("{} Local Peer Discovery initialised", tr_ip_protocol_to_sv(ip_protocol)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -354,27 +449,34 @@ private:
|
|||
* @brief Processing of timeout notifications and incoming data on the socket
|
||||
* @note maximum rate of read events is limited according to @a lpd_maxAnnounceCap
|
||||
* @see DoS */
|
||||
template<tr_address_type ip_protocol>
|
||||
static void event_callback(evutil_socket_t /*s*/, short type, void* vself)
|
||||
{
|
||||
if ((type & EV_READ) != 0)
|
||||
{
|
||||
static_cast<tr_lpd_impl*>(vself)->onCanRead();
|
||||
static_cast<tr_lpd_impl*>(vself)->onCanRead(ip_protocol);
|
||||
}
|
||||
}
|
||||
|
||||
void onCanRead()
|
||||
void onCanRead(tr_address_type ip_protocol)
|
||||
{
|
||||
TR_ASSERT(tr_address::is_valid(ip_protocol));
|
||||
if (!tr_address::is_valid(ip_protocol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mediator_.allowsLPD())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// process announcement from foreign peer
|
||||
struct sockaddr_in foreign_addr = {};
|
||||
struct sockaddr_storage foreign_addr = {};
|
||||
auto addr_len = socklen_t{ sizeof(foreign_addr) };
|
||||
auto foreign_msg = std::array<char, MaxDatagramLength>{};
|
||||
auto const res = recvfrom(
|
||||
mcast_socket_,
|
||||
mcast_sockets_[ip_protocol],
|
||||
std::data(foreign_msg),
|
||||
MaxDatagramLength,
|
||||
0,
|
||||
|
@ -386,6 +488,7 @@ private:
|
|||
{
|
||||
return;
|
||||
}
|
||||
TR_ASSERT(tr_af_to_ip_protocol(foreign_addr.ss_family) == ip_protocol);
|
||||
|
||||
// If it doesn't look like a BEP14 message, discard it
|
||||
auto const msg = std::string_view{ std::data(foreign_msg), static_cast<size_t>(res) };
|
||||
|
@ -410,10 +513,14 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
auto [peer_addr, compact] = tr_address::from_compact_ipv4(reinterpret_cast<std::byte*>(&foreign_addr.sin_addr));
|
||||
auto peer_sockaddr = tr_socket_address::from_sockaddr(reinterpret_cast<sockaddr*>(&foreign_addr));
|
||||
if (!peer_sockaddr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (auto const& hash_string : parsed->info_hash_strings)
|
||||
{
|
||||
if (!mediator_.onPeerFound(hash_string, peer_addr, parsed->port))
|
||||
if (!mediator_.onPeerFound(hash_string, peer_sockaddr->address(), parsed->port))
|
||||
{
|
||||
tr_logAddDebug(fmt::format("Cannot serve torrent #{:s}", hash_string));
|
||||
}
|
||||
|
@ -462,19 +569,30 @@ private:
|
|||
std::sort(std::begin(torrents), std::end(torrents), TorrentComparator);
|
||||
|
||||
// cram in as many as will fit in a message
|
||||
auto const baseline_size = std::size(makeAnnounceMsg(cookie_, mediator_.port(), {}));
|
||||
auto const size_with_one = std::size(makeAnnounceMsg(cookie_, mediator_.port(), { torrents.front().info_hash_str }));
|
||||
auto const size_per_hash = size_with_one - baseline_size;
|
||||
auto baseline_size = size_t{};
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
baseline_size = std::max(
|
||||
baseline_size,
|
||||
std::size(makeAnnounceMsg(static_cast<tr_address_type>(ipp), cookie_, mediator_.port(), {})));
|
||||
}
|
||||
auto const size_per_hash = std::size(torrents.front().info_hash_str);
|
||||
auto const max_torrents_per_announce = (MaxDatagramLength - baseline_size) / size_per_hash;
|
||||
auto const torrents_this_announce = std::min(std::size(torrents), max_torrents_per_announce);
|
||||
auto info_hash_strings = std::vector<std::string_view>{};
|
||||
info_hash_strings.resize(std::min(std::size(torrents), max_torrents_per_announce));
|
||||
info_hash_strings.reserve(torrents_this_announce);
|
||||
std::transform(
|
||||
std::begin(torrents),
|
||||
std::begin(torrents) + std::size(info_hash_strings),
|
||||
std::begin(info_hash_strings),
|
||||
std::begin(torrents) + torrents_this_announce,
|
||||
std::back_inserter(info_hash_strings),
|
||||
[](auto const& tor) { return tor.info_hash_str; });
|
||||
|
||||
if (!sendAnnounce(info_hash_strings))
|
||||
auto success = false;
|
||||
for (ipp_t ipp = 0; ipp < NUM_TR_AF_INET_TYPES; ++ipp)
|
||||
{
|
||||
success |= sendAnnounce(static_cast<tr_address_type>(ipp), info_hash_strings);
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -508,28 +626,40 @@ private:
|
|||
* matter). A listening client on the same network might react by adding us to his
|
||||
* peer pool for torrent t.
|
||||
*/
|
||||
bool sendAnnounce(std::vector<std::string_view> const& info_hash_strings)
|
||||
bool sendAnnounce(tr_address_type ip_protocol, std::vector<std::string_view> const& info_hash_strings)
|
||||
{
|
||||
auto const announce = makeAnnounceMsg(cookie_, mediator_.port(), info_hash_strings);
|
||||
TR_ASSERT(tr_address::is_valid(ip_protocol));
|
||||
if (!tr_address::is_valid(ip_protocol))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mcast_sockets_[ip_protocol] == TR_BAD_SOCKET)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto const announce = makeAnnounceMsg(ip_protocol, cookie_, mediator_.port(), info_hash_strings);
|
||||
TR_ASSERT(std::size(announce) <= MaxDatagramLength);
|
||||
auto const res = sendto(
|
||||
mcast_socket_,
|
||||
mcast_sockets_[ip_protocol],
|
||||
std::data(announce),
|
||||
std::size(announce),
|
||||
0,
|
||||
reinterpret_cast<sockaddr const*>(&mcast_addr_),
|
||||
sizeof(mcast_addr_));
|
||||
auto const sent = res == static_cast<int>(std::size(announce));
|
||||
return sent;
|
||||
ip_protocol == TR_AF_INET ? reinterpret_cast<sockaddr const*>(&mcast_addr_) :
|
||||
reinterpret_cast<sockaddr const*>(&mcast6_addr_),
|
||||
ip_protocol == TR_AF_INET ? sizeof(mcast_addr_) : sizeof(mcast6_addr_));
|
||||
return res == static_cast<int>(std::size(announce));
|
||||
}
|
||||
|
||||
std::string const cookie_ = makeCookie();
|
||||
Mediator& mediator_;
|
||||
tr_socket_t mcast_socket_ = TR_BAD_SOCKET; /**multicast socket */
|
||||
libtransmission::evhelpers::event_unique_ptr event_;
|
||||
std::array<tr_socket_t, NUM_TR_AF_INET_TYPES> mcast_sockets_ = { TR_BAD_SOCKET, TR_BAD_SOCKET }; // multicast sockets
|
||||
std::array<libtransmission::evhelpers::event_unique_ptr, NUM_TR_AF_INET_TYPES> events_;
|
||||
|
||||
static auto constexpr MaxDatagramLength = size_t{ 1400 };
|
||||
sockaddr_in mcast_addr_ = {}; /**<initialized from the above constants in init() */
|
||||
sockaddr_in mcast_addr_ = {}; // initialized from the above constants in init()
|
||||
sockaddr_in6 mcast6_addr_ = {}; // initialized from the above constants in init()
|
||||
|
||||
// BEP14: "To avoid causing multicast storms on large networks a
|
||||
// client should send no more than 1 announce per minute."
|
||||
|
@ -546,12 +676,11 @@ private:
|
|||
static auto constexpr MaxIncomingPerSecond = 10;
|
||||
static auto constexpr MaxIncomingPerUpkeep = std::chrono::duration_cast<std::chrono::seconds>(DosInterval).count() *
|
||||
MaxIncomingPerSecond;
|
||||
// @brief throw away messages after this number exceeds MaxIncomingPerUpkeep
|
||||
size_t messages_received_since_upkeep_ = 0U;
|
||||
size_t messages_received_since_upkeep_ = 0U; // throw away messages after this number exceeds MaxIncomingPerUpkeep
|
||||
|
||||
static auto constexpr TorrentAnnounceIntervalSec = time_t{ 240U }; // how frequently to reannounce the same torrent
|
||||
static auto constexpr TtlSameSubnet = 1;
|
||||
static auto constexpr AnnounceScope = int{ TtlSameSubnet }; /**<the maximum scope for LPD datagrams */
|
||||
static auto constexpr AnnounceScope = int{ TtlSameSubnet }; // the maximum scope for LPD datagrams
|
||||
};
|
||||
|
||||
std::unique_ptr<tr_lpd> tr_lpd::create(Mediator& mediator, struct event_base* event_base)
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename ContiguousRange>
|
||||
[[nodiscard]] constexpr auto operator==(ContiguousRange const& x) const noexcept
|
||||
[[nodiscard]] constexpr bool operator==(ContiguousRange const& x) const noexcept
|
||||
{
|
||||
return sv() == x;
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ void event_callback(evutil_socket_t s, [[maybe_unused]] short type, void* vsessi
|
|||
}
|
||||
else if (n_read >= 8 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] <= 3)
|
||||
{
|
||||
if (!session->announcer_udp_->handle_message(std::data(buf), n_read))
|
||||
if (!session->announcer_udp_->handle_message(std::data(buf), n_read, from_sa, fromlen))
|
||||
{
|
||||
tr_logAddTrace("Couldn't parse UDP tracker packet.");
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ tr_session::tr_udp_core::tr_udp_core(tr_session& session, tr_port udp_port)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't make IPv4 socket non-blocking {address}: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't make IPv4 socket non-blocking {address}: {error} ({error_code})")),
|
||||
fmt::arg("address", tr_socket_address::display_name(addr, udp_port_)),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -179,7 +179,7 @@ tr_session::tr_udp_core::tr_udp_core(tr_session& session, tr_port udp_port)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't bind IPv4 socket {address}: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't bind IPv4 socket {address}: {error} ({error_code})")),
|
||||
fmt::arg("address", tr_socket_address::display_name(addr, udp_port_)),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -213,7 +213,7 @@ tr_session::tr_udp_core::tr_udp_core(tr_session& session, tr_port udp_port)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't make IPv6 socket non-blocking {address}: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't make IPv6 socket non-blocking {address}: {error} ({error_code})")),
|
||||
fmt::arg("address", tr_socket_address::display_name(addr, udp_port_)),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -224,7 +224,7 @@ tr_session::tr_udp_core::tr_udp_core(tr_session& session, tr_port udp_port)
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't bind IPv6 socket {address}: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't bind IPv6 socket {address}: {error} ({error_code})")),
|
||||
fmt::arg("address", tr_socket_address::display_name(addr, udp_port_)),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <locale>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -122,7 +121,7 @@ bool tr_file_read(std::string_view filename, std::vector<char>& contents, tr_err
|
|||
if (*error)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message()),
|
||||
fmt::arg("error_code", error->code())));
|
||||
|
@ -131,7 +130,7 @@ bool tr_file_read(std::string_view filename, std::vector<char>& contents, tr_err
|
|||
|
||||
if (!info || !info->isFile())
|
||||
{
|
||||
tr_logAddError(fmt::format(_("Couldn't read '{path}': Not a regular file"), fmt::arg("path", filename)));
|
||||
tr_logAddError(fmt::format(fmt::runtime(_("Couldn't read '{path}': Not a regular file")), fmt::arg("path", filename)));
|
||||
error->set(TR_ERROR_EISDIR, "Not a regular file"sv);
|
||||
return false;
|
||||
}
|
||||
|
@ -141,7 +140,7 @@ bool tr_file_read(std::string_view filename, std::vector<char>& contents, tr_err
|
|||
if (fd == TR_BAD_SYS_FILE)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message()),
|
||||
fmt::arg("error_code", error->code())));
|
||||
|
@ -152,7 +151,7 @@ bool tr_file_read(std::string_view filename, std::vector<char>& contents, tr_err
|
|||
if (!tr_sys_file_read(fd, std::data(contents), info->size, nullptr, error))
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message()),
|
||||
fmt::arg("error_code", error->code())));
|
||||
|
@ -606,7 +605,7 @@ bool tr_file_move(std::string_view oldpath_in, std::string_view newpath_in, tr_e
|
|||
if (auto log_error = tr_error{}; !tr_sys_path_remove(oldpath, &log_error))
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't remove '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't remove '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", oldpath),
|
||||
fmt::arg("error", log_error.message()),
|
||||
fmt::arg("error_code", log_error.code())));
|
||||
|
|
|
@ -233,7 +233,7 @@ std::optional<tr_variant> tr_variant_serde::parse_json(std::string_view input)
|
|||
error_.set(
|
||||
EILSEQ,
|
||||
fmt::format(
|
||||
_("Couldn't parse JSON at position {position} '{text}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't parse JSON at position {position} '{text}': {error} ({error_code})")),
|
||||
fmt::arg("position", err_offset),
|
||||
fmt::arg("text", std::string_view{ begin + err_offset, std::min(size_t{ 16U }, size - err_offset) }),
|
||||
fmt::arg("error", rapidjson::GetParseError_En(err_code)),
|
||||
|
|
|
@ -866,7 +866,7 @@ bool tr_variant_serde::to_file(tr_variant const& var, std::string_view filename)
|
|||
if (error_)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't save '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error_.message()),
|
||||
fmt::arg("error_code", error_.code())));
|
||||
|
|
|
@ -96,5 +96,5 @@ private:
|
|||
std::atomic<bool> stop_current_ = false;
|
||||
std::condition_variable stop_current_cv_;
|
||||
|
||||
std::chrono::milliseconds sleep_per_seconds_during_verify_;
|
||||
std::chrono::milliseconds sleep_per_seconds_during_verify_ = {};
|
||||
};
|
||||
|
|
|
@ -83,7 +83,7 @@ private:
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't watch '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't watch '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", dirname()),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -95,7 +95,7 @@ private:
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't watch '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't watch '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", dirname()),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -107,7 +107,7 @@ private:
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't watch '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't watch '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", dirname()),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
|
@ -139,7 +139,7 @@ private:
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read event: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read event: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
break;
|
||||
|
@ -148,7 +148,7 @@ private:
|
|||
if (nread != sizeof(ev))
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read event: expected {expected_size}, got {actual_size}"),
|
||||
fmt::runtime(_("Couldn't read event: expected {expected_size}, got {actual_size}")),
|
||||
fmt::arg("expected_size", sizeof(ev)),
|
||||
fmt::arg("actual_size", nread)));
|
||||
break;
|
||||
|
@ -165,7 +165,7 @@ private:
|
|||
{
|
||||
auto const error_code = errno;
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read filename: {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read filename: {error} ({error_code})")),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
break;
|
||||
|
@ -174,7 +174,7 @@ private:
|
|||
if (nread != ev.len)
|
||||
{
|
||||
tr_logAddError(fmt::format(
|
||||
_("Couldn't read filename: expected {expected_size}, got {actual_size}"),
|
||||
fmt::runtime(_("Couldn't read filename: expected {expected_size}, got {actual_size}")),
|
||||
fmt::arg("expected_size", sizeof(ev)),
|
||||
fmt::arg("actual_size", nread)));
|
||||
break;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include <cerrno> // for errno
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <fcntl.h> // for open()
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace
|
|||
if (error && !TR_ERROR_IS_ENOENT(error.code()))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Skipping '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Skipping '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", path),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
@ -88,7 +88,7 @@ void BaseWatchdir::processFile(std::string_view basename)
|
|||
|
||||
if (now - info.first_kick_at > timeoutDuration())
|
||||
{
|
||||
tr_logAddWarn(fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", basename)));
|
||||
tr_logAddWarn(fmt::format(fmt::runtime(_("Couldn't add torrent file '{path}'")), fmt::arg("path", basename)));
|
||||
pending_.erase(iter);
|
||||
}
|
||||
else
|
||||
|
@ -115,7 +115,7 @@ void BaseWatchdir::scan()
|
|||
if (error)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::runtime(_("Couldn't read '{path}': {error} ({error_code})")),
|
||||
fmt::arg("path", dirname()),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code())));
|
||||
|
|
|
@ -291,6 +291,19 @@ std::string_view getSiteName(std::string_view host)
|
|||
|
||||
return host;
|
||||
}
|
||||
|
||||
// Not part of the RFC3986 standard, but included for convenience
|
||||
// when using the result with API that does not accept IPv6 address
|
||||
// strings that are wrapped in square brackets (e.g. inet_pton())
|
||||
std::string_view getHostWoBrackets(std::string_view host)
|
||||
{
|
||||
if (tr_strv_starts_with(host, '['))
|
||||
{
|
||||
host.remove_prefix(1);
|
||||
host.remove_suffix(1);
|
||||
}
|
||||
return host;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<tr_url_parsed_t> tr_urlParse(std::string_view url)
|
||||
|
@ -363,6 +376,7 @@ std::optional<tr_url_parsed_t> tr_urlParse(std::string_view url)
|
|||
{
|
||||
parsed.host = tr_strv_sep(&remain, ':');
|
||||
}
|
||||
parsed.host_wo_brackets = getHostWoBrackets(parsed.host);
|
||||
parsed.sitename = getSiteName(parsed.host);
|
||||
parsed.port = parsePort(!std::empty(remain) ? remain : getPortForScheme(parsed.scheme));
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ struct tr_url_parsed_t
|
|||
std::string_view scheme; // "http"
|
||||
std::string_view authority; // "example.com:80"
|
||||
std::string_view host; // "example.com"
|
||||
std::string_view host_wo_brackets; // "example.com" ("[::1]" -> "::1")
|
||||
std::string_view sitename; // "example"
|
||||
std::string_view path; // /"over/there"
|
||||
std::string_view query; // "name=ferret"
|
||||
|
|
|
@ -181,8 +181,9 @@ public:
|
|||
if (curl_ssl_verify)
|
||||
{
|
||||
auto const* bundle = std::empty(curl_ca_bundle) ? "none" : curl_ca_bundle.c_str();
|
||||
tr_logAddInfo(
|
||||
fmt::format(_("Will verify tracker certs using envvar CURL_CA_BUNDLE: {bundle}"), fmt::arg("bundle", bundle)));
|
||||
tr_logAddInfo(fmt::format(
|
||||
fmt::runtime(_("Will verify tracker certs using envvar CURL_CA_BUNDLE: {bundle}")),
|
||||
fmt::arg("bundle", bundle)));
|
||||
tr_logAddInfo(_("NB: this only works if you built against libcurl with openssl or gnutls, NOT nss"));
|
||||
tr_logAddInfo(_("NB: Invalid certs will appear as 'Could not connect to tracker' like many other errors"));
|
||||
}
|
||||
|
@ -460,7 +461,7 @@ public:
|
|||
if (code != NoResponseCode && code != PartialContentResponseCode)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't fetch '{url}': expected HTTP response code {expected_code}, got {actual_code}"),
|
||||
fmt::runtime(_("Couldn't fetch '{url}': expected HTTP response code {expected_code}, got {actual_code}")),
|
||||
fmt::arg("url", task->url()),
|
||||
fmt::arg("expected_code", PartialContentResponseCode),
|
||||
fmt::arg("actual_code", code)));
|
||||
|
|
|
@ -381,14 +381,14 @@ static void removeKeRangerRansomware()
|
|||
[NSValueTransformer setValueTransformer:iconTransformer forName:@"ExpandedPathToIconTransformer"];
|
||||
}
|
||||
|
||||
void onStartQueue(tr_session* /*session*/, tr_torrent* tor, void* vself)
|
||||
void onStartQueue(tr_session* /*session*/, tr_torrent* /*tor*/, void* /*vself*/)
|
||||
{
|
||||
auto* controller = (__bridge Controller*)(vself);
|
||||
auto const hashstr = @(tr_torrentView(tor).hash_string);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
auto* const torrent = [controller torrentForHash:hashstr];
|
||||
[torrent startQueue];
|
||||
//posting asynchronously with coalescing to prevent stack overflow on lots of torrents changing state at the same time
|
||||
[NSNotificationQueue.defaultQueue enqueueNotification:[NSNotification notificationWithName:@"UpdateTorrentsState" object:nil]
|
||||
postingStyle:NSPostASAP
|
||||
coalesceMask:NSNotificationCoalescingOnName
|
||||
forModes:nil];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -812,8 +812,7 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
|
||||
[nc addObserver:self.fWindow selector:@selector(makeKeyWindow) name:@"MakeWindowKey" object:nil];
|
||||
|
||||
#warning rename
|
||||
[nc addObserver:self selector:@selector(fullUpdateUI) name:@"UpdateQueue" object:nil];
|
||||
[nc addObserver:self selector:@selector(fullUpdateUI) name:@"UpdateTorrentsState" object:nil];
|
||||
|
||||
[nc addObserver:self selector:@selector(applyFilter) name:@"ApplyFilter" object:nil];
|
||||
|
||||
|
|
|
@ -463,7 +463,7 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
|
|||
[FileRenameSheetController presentSheetForTorrent:torrent modalForWindow:self.fOutline.window completionHandler:^(BOOL didRename) {
|
||||
if (didRename)
|
||||
{
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateQueue" object:self];
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateTorrentsState" object:nil];
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:@"ResetInspector" object:self
|
||||
userInfo:@{ @"Torrent" : torrent }];
|
||||
}
|
||||
|
|
|
@ -323,7 +323,6 @@ static NSTimeInterval const kUpdateSeconds = 0.75;
|
|||
}
|
||||
}
|
||||
|
||||
#warning don't cut off end
|
||||
- (CGFloat)tableView:(NSTableView*)tableView heightOfRow:(NSInteger)row
|
||||
{
|
||||
NSString* message = self.fDisplayedMessages[row][@"Message"];
|
||||
|
|
|
@ -879,7 +879,7 @@ static NSString* const kWebUIURLFormat = @"http://localhost:%ld/";
|
|||
tr_sessionSetQueueEnabled(self.fHandle, TR_UP, [self.fDefaults boolForKey:@"QueueSeed"]);
|
||||
|
||||
//handle if any transfers switch from queued to paused
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateQueue" object:self];
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateTorrentsState" object:nil];
|
||||
}
|
||||
|
||||
- (void)setQueueNumber:(id)sender
|
||||
|
|
|
@ -219,7 +219,7 @@ OSStatus GeneratePreviewForURL(void* /*thisInterface*/, QLPreviewRequestRef prev
|
|||
|
||||
FileTreeNode root{};
|
||||
|
||||
for (auto const& [path, size] : metainfo.files().sortedByPath())
|
||||
for (auto const& [path, size] : metainfo.files().sorted_by_path())
|
||||
{
|
||||
FileTreeNode* curNode = &root;
|
||||
size_t level = 0;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user