Merge branch 'main' into patch-1
This commit is contained in:
commit
5f4f19a54c
|
@ -55,7 +55,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
|||
ConstructorInitializerIndentWidth: 4
|
||||
FixNamespaceComments: true
|
||||
IndentGotoLabels: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
QualifierAlignment: Right
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
|
|
|
@ -7,6 +7,9 @@ cmake-build-*/*
|
|||
libtransmission/version.h
|
||||
web/node_modules/*
|
||||
|
||||
# android
|
||||
android/*
|
||||
|
||||
# third-party maintained projects
|
||||
third-party/*
|
||||
|
||||
|
|
|
@ -12,18 +12,19 @@ jobs:
|
|||
what-to-make:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
make-cli: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.cli-changed == '1' }}
|
||||
make-daemon: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.daemon-changed == '1' }}
|
||||
make-dist: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.dist-changed == '1' }}
|
||||
make-android: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.android-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-cli: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.cli-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-daemon: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.daemon-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-dist: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.dist-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-docs: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.docs-changed == '1' }}
|
||||
make-gtk: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.gtk-changed == '1' }}
|
||||
make-mac: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.mac-changed == '1' }}
|
||||
make-qt: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.qt-changed == '1' }}
|
||||
make-source-tarball: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.any-code-changed == '1' }}
|
||||
make-tests: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.tests-changed == '1' }}
|
||||
make-utils: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.utils-changed == '1' || steps.check-diffs.outputs.tests-changed == '1' }}
|
||||
make-gtk: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.gtk-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-mac: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.mac-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-qt: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.qt-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-source-tarball: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.any-code-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-tests: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.tests-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-utils: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.utils-changed == '1' || steps.check-diffs.outputs.tests-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
make-web: 'false' # this is handled in the webapp workflow
|
||||
test-style: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.our-code-changed == '1' }}
|
||||
test-style: ${{ steps.check-main-push.outputs.is-main-push == '1' || steps.check-diffs.outputs.our-code-changed == '1' || steps.check-diffs.outputs.ci-actions-changed == '1' }}
|
||||
steps:
|
||||
- name: Check Push to Main Branch
|
||||
id: check-main-push
|
||||
|
@ -36,7 +37,7 @@ jobs:
|
|||
fi
|
||||
- name: Get Source
|
||||
id: get-source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
path: src
|
||||
|
@ -53,18 +54,20 @@ jobs:
|
|||
git diff --exit-code "$MERGE_BASE" -- "$@"
|
||||
echo "$name-changed=$?" >> "$GITHUB_OUTPUT"
|
||||
}
|
||||
get_changes cli CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission cli
|
||||
get_changes any-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web third-party
|
||||
get_changes our-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web
|
||||
get_changes daemon CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission daemon
|
||||
get_changes dist dist release
|
||||
get_changes docs docs
|
||||
get_changes gtk CMakeLists.txt cmake third-party libtransmission gtk
|
||||
get_changes mac CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission macosx Transmission.xcodeproj
|
||||
get_changes qt CMakeLists.txt cmake third-party libtransmission qt
|
||||
get_changes tests CMakeLists.txt cmake third-party libtransmission utils tests
|
||||
get_changes utils CMakeLists.txt cmake third-party libtransmission utils
|
||||
get_changes web CMakeLists.txt cmake third-party libtransmission web
|
||||
get_changes android CMakeLists.txt cmake third-party libtransmission android
|
||||
get_changes cli CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission cli
|
||||
get_changes any-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web third-party
|
||||
get_changes our-code CMakeLists.txt cmake Transmission.xcodeproj libtransmission cli daemon gtk macosx qt utils tests web
|
||||
get_changes daemon CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission daemon
|
||||
get_changes dist dist release
|
||||
get_changes docs docs
|
||||
get_changes gtk CMakeLists.txt cmake third-party libtransmission gtk
|
||||
get_changes mac CMakeLists.txt cmake Transmission.xcodeproj third-party libtransmission macosx
|
||||
get_changes qt CMakeLists.txt cmake third-party libtransmission qt
|
||||
get_changes tests CMakeLists.txt cmake third-party libtransmission utils tests
|
||||
get_changes utils CMakeLists.txt cmake third-party libtransmission utils
|
||||
get_changes web CMakeLists.txt cmake third-party libtransmission web
|
||||
get_changes ci-actions .github/workflows/actions.yml
|
||||
cat "$GITHUB_OUTPUT"
|
||||
|
||||
code-style:
|
||||
|
@ -80,15 +83,15 @@ jobs:
|
|||
echo '${{ toJSON(runner) }}'
|
||||
cat /etc/os-release
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
set -ex
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"
|
||||
sudo apt-get install -y clang-format-15 npm
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main"
|
||||
sudo apt-get install -y clang-format-17 npm
|
||||
- name: Check for style diffs
|
||||
id: check-for-diffs
|
||||
working-directory: .
|
||||
|
@ -100,7 +103,7 @@ jobs:
|
|||
cat style.diff
|
||||
set -e
|
||||
- name: Upload Diffs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ steps.check-for-diffs.outputs.differs == '1' }}
|
||||
with:
|
||||
name: code-style.diff
|
||||
|
@ -113,7 +116,7 @@ jobs:
|
|||
echo "When CI is done, the above patch will be uploaded as 'code-style.diff' to https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/ ."
|
||||
exit 1
|
||||
|
||||
sanitizer-tests:
|
||||
sanitizer-tests-ubuntu:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [ what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
|
@ -144,8 +147,13 @@ 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@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
path: src
|
||||
|
@ -175,6 +183,48 @@ jobs:
|
|||
- name: Test with sanitizers
|
||||
run: cmake -E chdir obj ctest -j $(nproc) --build-config Debug --output-on-failure
|
||||
|
||||
sanitizer-tests-macos:
|
||||
runs-on: macos-14
|
||||
needs: [ what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
steps:
|
||||
- name: Show Configuration
|
||||
run: |
|
||||
echo '${{ toJSON(needs) }}'
|
||||
echo '${{ toJSON(runner) }}'
|
||||
sw_vers
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
brew install cmake gettext libdeflate libevent libpsl miniupnpc ninja node pkg-config
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
path: src
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-S src \
|
||||
-B obj \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_CXX_FLAGS='-gdwarf-4 -fno-omit-frame-pointer -fsanitize=address,undefined' \
|
||||
-DCMAKE_C_FLAGS='-gdwarf-4 -fno-omit-frame-pointer -fsanitize=address,undefined' \
|
||||
-DCMAKE_INSTALL_PREFIX=pfx \
|
||||
-DENABLE_CLI=OFF \
|
||||
-DENABLE_DAEMON=OFF \
|
||||
-DENABLE_GTK=OFF \
|
||||
-DENABLE_MAC=OFF \
|
||||
-DENABLE_QT=OFF \
|
||||
-DENABLE_TESTS=ON \
|
||||
-DENABLE_UTILS=ON \
|
||||
-DREBUILD_WEB=OFF \
|
||||
-DRUN_CLANG_TIDY=OFF
|
||||
- name: Make
|
||||
run: cmake --build obj --config Debug --target libtransmission-test transmission-show
|
||||
- name: Test with sanitizers
|
||||
run: cmake -E chdir obj ctest -j $(nproc) --build-config Debug --output-on-failure
|
||||
|
||||
clang-tidy-libtransmission:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [ what-to-make ]
|
||||
|
@ -206,7 +256,7 @@ jobs:
|
|||
ninja-build \
|
||||
npm
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
path: src
|
||||
|
@ -227,8 +277,8 @@ jobs:
|
|||
run: |
|
||||
if grep 'warning:' makelog; then exit 1; fi
|
||||
|
||||
macos-12:
|
||||
runs-on: macos-12
|
||||
macos-14-arm64:
|
||||
runs-on: macos-14
|
||||
needs: [ 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-mac == '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' }}
|
||||
steps:
|
||||
|
@ -247,7 +297,7 @@ jobs:
|
|||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: brew install qt
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src
|
||||
submodules: recursive
|
||||
|
@ -259,14 +309,14 @@ jobs:
|
|||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=pfx \
|
||||
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
|
||||
-DCMAKE_OSX_ARCHITECTURES='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=${{ (needs.what-to-make.outputs.make-gtk == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_MAC=${{ (needs.what-to-make.outputs.make-mac == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_QT=${{ (needs.what-to-make.outputs.make-qt == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_TESTS=OFF \
|
||||
-DENABLE_TESTS=${{ (needs.what-to-make.outputs.make-tests == 'true') && 'ON' || '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 \
|
||||
|
@ -280,11 +330,47 @@ jobs:
|
|||
run: cmake -E chdir obj ctest -j $(sysctl -n hw.logicalcpu) --build-config RelWithDebInfo --output-on-failure
|
||||
- name: Install
|
||||
run: cmake --build obj --config RelWithDebInfo --target install/strip
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
||||
# Only verify build support on old macOS
|
||||
macos-11:
|
||||
runs-on: macos-11
|
||||
needs: [ what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-mac == 'true' }}
|
||||
steps:
|
||||
- name: Show Configuration
|
||||
run: |
|
||||
echo '${{ toJSON(needs) }}'
|
||||
echo '${{ toJSON(runner) }}'
|
||||
sw_vers
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
brew install cmake gettext libdeflate libevent libpsl miniupnpc ninja node pkg-config
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src
|
||||
submodules: recursive
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-S src \
|
||||
-B obj \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=pfx \
|
||||
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
|
||||
-DCMAKE_PREFIX_PATH=`brew --prefix`/opt/qt \
|
||||
-DENABLE_MAC=${{ (needs.what-to-make.outputs.make-mac == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_TESTS=OFF \
|
||||
-DENABLE_WERROR=ON \
|
||||
-DRUN_CLANG_TIDY=OFF
|
||||
- name: Make
|
||||
run: cmake --build obj --config RelWithDebInfo
|
||||
|
||||
alpine-musl:
|
||||
needs: [ what-to-make ]
|
||||
runs-on: ubuntu-22.04
|
||||
|
@ -324,7 +410,7 @@ jobs:
|
|||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: apk add --upgrade qt5-qtbase-dev qt5-qtsvg-dev qt5-qttools-dev
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src
|
||||
submodules: recursive
|
||||
|
@ -355,7 +441,7 @@ jobs:
|
|||
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@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
@ -385,11 +471,11 @@ jobs:
|
|||
nodejs
|
||||
& "C:\Program Files\OpenSSL\unins000.exe" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- | Out-Host
|
||||
(Join-Path $Env:ProgramFiles NASM) | Out-File $Env:GITHUB_PATH -Append
|
||||
(Join-Path ${Env:ProgramFiles(x86)} 'WiX Toolset v3.11' bin) | Out-File $Env:GITHUB_PATH -Append
|
||||
(Join-Path (Get-Item -Path "${Env:ProgramFiles(x86)}\WiX Toolset v3.*")[0].FullName bin) | Out-File $Env:GITHUB_PATH -Append
|
||||
|
||||
Install-Module -Name Pscx -RequiredVersion 4.0.0-beta4 -AllowPrerelease -Force
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src
|
||||
submodules: recursive
|
||||
|
@ -404,7 +490,7 @@ jobs:
|
|||
exit 1
|
||||
}
|
||||
- name: Get Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.DEPS_PREFIX }}
|
||||
|
@ -427,13 +513,13 @@ jobs:
|
|||
-G Ninja `
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||
-DCMAKE_INSTALL_PREFIX=pfx `
|
||||
-DCMAKE_PREFIX_PATH="${Env:DepsPrefix}" `
|
||||
-DCMAKE_PREFIX_PATH="${Env:DEPS_PREFIX}" `
|
||||
-DENABLE_CLI=${{ (needs.what-to-make.outputs.make-cli == 'true') && 'ON' || 'OFF' }} `
|
||||
-DENABLE_DAEMON=${{ (needs.what-to-make.outputs.make-daemon == 'true' || needs.what-to-make.outputs.make-dist == 'true') && 'ON' || 'OFF' }} `
|
||||
-DENABLE_GTK=OFF `
|
||||
-DENABLE_MAC=OFF `
|
||||
-DENABLE_QT=${{ (needs.what-to-make.outputs.make-dist == 'true' || needs.what-to-make.outputs.make-qt == 'true') && 'ON' || 'OFF' }} `
|
||||
-DENABLE_TESTS=ON `
|
||||
-DENABLE_TESTS=${{ (needs.what-to-make.outputs.make-tests == 'true') && 'ON' || 'OFF' }} `
|
||||
-DENABLE_UTILS=ON `
|
||||
-DREBUILD_WEB=${{ (needs.what-to-make.outputs.make-web == 'true') && 'ON' || 'OFF' }} `
|
||||
-DENABLE_WERROR=ON `
|
||||
|
@ -452,11 +538,11 @@ jobs:
|
|||
run: |
|
||||
Import-VisualStudioVars -VisualStudioVersion 2022 -Architecture ${{ matrix.arch }}
|
||||
cmake --build obj --config RelWithDebInfo --target pack-msi
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}-${{ matrix.arch }}
|
||||
path: pfx/**/*
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}-${{ matrix.arch }}-msi
|
||||
path: obj/dist/msi/*.msi
|
||||
|
@ -482,7 +568,7 @@ jobs:
|
|||
libssl-dev \
|
||||
ninja-build
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: src
|
||||
submodules: recursive
|
||||
|
@ -494,12 +580,73 @@ jobs:
|
|||
-G Ninja
|
||||
- name: Create source tarball
|
||||
run: cmake --build obj --target package_source
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
path: obj/transmission*.tar.*
|
||||
|
||||
macos-12-from-tarball:
|
||||
macos-14-universal-from-tarball:
|
||||
needs: [ make-source-tarball, what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-mac == 'true' }}
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Show Configuration
|
||||
run: |
|
||||
echo '${{ toJSON(needs) }}'
|
||||
echo '${{ toJSON(runner) }}'
|
||||
sw_vers
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
brew install cmake gettext ninja node pkg-config
|
||||
- name: Get Source
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
- name: Extract Source
|
||||
run: mkdir src && tar xf transmission*.tar.* -C src --strip-components 1
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-S src \
|
||||
-B obj \
|
||||
-G Ninja \
|
||||
-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 \
|
||||
-DENABLE_MAC=${{ (needs.what-to-make.outputs.make-mac == 'true') && 'ON' || 'OFF' }} \
|
||||
-DENABLE_QT=OFF \
|
||||
-DENABLE_TESTS=${{ (needs.what-to-make.outputs.make-tests == 'true') && 'ON' || '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 \
|
||||
-DUSE_SYSTEM_EVENT2=OFF \
|
||||
-DUSE_SYSTEM_DEFLATE=OFF \
|
||||
-DUSE_SYSTEM_DHT=OFF \
|
||||
-DUSE_SYSTEM_MINIUPNPC=OFF \
|
||||
-DUSE_SYSTEM_NATPMP=OFF \
|
||||
-DUSE_SYSTEM_UTP=OFF \
|
||||
-DUSE_SYSTEM_B64=OFF \
|
||||
-DUSE_SYSTEM_PSL=OFF
|
||||
- name: Make
|
||||
run: cmake --build obj --config RelWithDebInfo
|
||||
- name: Test
|
||||
if: ${{ needs.what-to-make.outputs.make-tests == 'true' }}
|
||||
env:
|
||||
TMPDIR: /private/tmp
|
||||
run: cmake -E chdir obj ctest -j $(sysctl -n hw.logicalcpu) --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/**/*
|
||||
|
||||
macos-12-x86_64-from-tarball:
|
||||
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-mac == '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: macos-12
|
||||
|
@ -519,7 +666,7 @@ jobs:
|
|||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: brew install qt
|
||||
- name: Get Source
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
- name: Extract Source
|
||||
|
@ -561,7 +708,7 @@ jobs:
|
|||
run: cmake -E chdir obj ctest -j $(sysctl -n hw.logicalcpu) --build-config RelWithDebInfo --output-on-failure
|
||||
- name: Install
|
||||
run: cmake --build obj --config RelWithDebInfo --target install/strip
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
@ -608,7 +755,7 @@ jobs:
|
|||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: apt-get install -y --no-install-recommends qtbase5-dev libqt5svg5-dev qttools5-dev
|
||||
- name: Get Source
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
- name: Extract Source
|
||||
|
@ -638,7 +785,7 @@ jobs:
|
|||
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@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
@ -684,7 +831,7 @@ jobs:
|
|||
if: ${{ needs.what-to-make.outputs.make-qt == 'true' }}
|
||||
run: dnf install -y qt6-qtbase-devel qt6-qtsvg-devel qt6-qttools-devel
|
||||
- name: Get Source
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
- name: Extract Source
|
||||
|
@ -715,7 +862,7 @@ jobs:
|
|||
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@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
@ -757,7 +904,7 @@ jobs:
|
|||
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/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
- name: Extract Source
|
||||
|
@ -789,7 +936,57 @@ jobs:
|
|||
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@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binaries-${{ github.job }}
|
||||
path: pfx/**/*
|
||||
|
||||
android:
|
||||
needs: [ what-to-make ]
|
||||
if: ${{ needs.what-to-make.outputs.make-android == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
VCPKG_DEFAULT_TRIPLET: arm64-android
|
||||
steps:
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
set -ex
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
ninja-build
|
||||
|
||||
- name: Get Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
gradle-version: 7.6
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Install NDK
|
||||
run: sdkmanager "ndk;26.1.10909125"
|
||||
|
||||
- name: Setup vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db # 2024.01.12
|
||||
|
||||
- name: Install vcpkg packages
|
||||
run: |
|
||||
vcpkg install openssl curl
|
||||
|
||||
- name: Build Transmission
|
||||
working-directory: ./android
|
||||
run: |
|
||||
gradle build
|
||||
|
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
@ -77,14 +77,14 @@ jobs:
|
|||
|
||||
- name: Initialize CodeQL
|
||||
if: ${{ matrix.language == 'javascript' }}
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config-file: ./.github/codeql/codeql-config-js.yml
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: ${{ matrix.language == 'cpp' }}
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
|
@ -94,6 +94,6 @@ jobs:
|
|||
ninja -C _build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
update-generated-files: ${{ steps.check-diffs.outputs.web-changed == '1' && steps.check-main-push.outputs.is-main-push == '1'}}
|
||||
steps:
|
||||
- name: Get source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2 # >1 needed for merge base
|
||||
- name: Check push-to-main
|
||||
|
@ -58,7 +58,7 @@ jobs:
|
|||
if: ${{ needs.decide-what-jobs-to-run.outputs.test-style == 'true' }}
|
||||
steps:
|
||||
- name: Get source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
set -e # abort if any command fails
|
||||
|
@ -79,7 +79,7 @@ jobs:
|
|||
echo ===
|
||||
set -e # undo set +e
|
||||
- name: Upload diffs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ steps.check-for-diffs.outputs.differs == '1' }}
|
||||
with:
|
||||
name: code-style.diff
|
||||
|
@ -98,7 +98,7 @@ jobs:
|
|||
if: ${{ needs.decide-what-jobs-to-run.outputs.test-generated-files == 'true' }}
|
||||
steps:
|
||||
- name: Get source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2 # >1 needed for merge base
|
||||
- name: Check for changes to generated files
|
||||
|
@ -111,9 +111,12 @@ jobs:
|
|||
echo
|
||||
echo Please undo your changes to these files:
|
||||
git diff --exit-code --name-only --merge-base "origin/$GITHUB_BASE_REF" -- \
|
||||
web/public_html/transmission-app.css \
|
||||
web/public_html/transmission-app.css.LEGAL.txt \
|
||||
web/public_html/transmission-app.css.map \
|
||||
web/public_html/transmission-app.js \
|
||||
web/public_html/transmission-app.js.map \
|
||||
web/public_html/transmission-app.js.LICENSE.txt
|
||||
web/public_html/transmission-app.js.LEGAL.txt \
|
||||
web/public_html/transmission-app.js.map
|
||||
|
||||
update-generated-files:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -128,7 +131,7 @@ jobs:
|
|||
set -e # abort if any command fails
|
||||
sudo apt-get install -y npm
|
||||
- name: Get source
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2 # >1 needed for merge base
|
||||
- name: Generate webapp files
|
||||
|
@ -140,7 +143,7 @@ jobs:
|
|||
git diff --name-only
|
||||
git add --update web
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
branch: 'chore/update-webapp-files'
|
||||
commit-message: 'chore: update generated transmission-web files'
|
||||
|
|
|
@ -23,9 +23,15 @@ node_modules/
|
|||
/third-party/miniupnp/miniupnpcstrings.h
|
||||
/third-party/suffixes_dafsa.h
|
||||
/web/public_html/transmission-app.js.map
|
||||
/android/.cxx
|
||||
/android/.gradle
|
||||
/android/build
|
||||
|
||||
# clangd compile commands
|
||||
compile_commands.json
|
||||
|
||||
# CLion IDE build directory
|
||||
/cmake-build-*/
|
||||
|
||||
# CMake user presets
|
||||
CMakeUserPresets.json
|
|
@ -1,59 +1,64 @@
|
|||
[submodule "third-party/dht"]
|
||||
path = third-party/dht
|
||||
url = https://github.com/transmission/dht
|
||||
url = https://github.com/transmission/dht.git
|
||||
branch = post-0.27-transmission
|
||||
[submodule "third-party/libb64"]
|
||||
path = third-party/libb64
|
||||
url = https://github.com/transmission/libb64
|
||||
url = https://github.com/transmission/libb64.git
|
||||
branch = post-1.2.1-transmission
|
||||
[submodule "third-party/libevent"]
|
||||
path = third-party/libevent
|
||||
url = https://github.com/transmission/libevent
|
||||
url = https://github.com/transmission/libevent.git
|
||||
branch = post-2.0.22-transmission
|
||||
[submodule "third-party/libnatpmp"]
|
||||
path = third-party/libnatpmp
|
||||
url = https://github.com/transmission/libnatpmp
|
||||
url = https://github.com/transmission/libnatpmp.git
|
||||
branch = post-20151025-transmission
|
||||
[submodule "third-party/libutp"]
|
||||
path = third-party/libutp
|
||||
url = https://github.com/transmission/libutp
|
||||
url = https://github.com/transmission/libutp.git
|
||||
branch = post-3.4-transmission
|
||||
[submodule "third-party/miniupnpc"]
|
||||
path = third-party/miniupnpc
|
||||
url = https://github.com/transmission/miniupnpc
|
||||
branch = post-2.0.20170509-transmission
|
||||
[submodule "third-party/miniupnp"]
|
||||
path = third-party/miniupnp
|
||||
# synced with https://github.com/miniupnp/miniupnp.git
|
||||
url = https://github.com/transmission/miniupnp.git
|
||||
[submodule "third-party/googletest"]
|
||||
path = third-party/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "third-party/utfcpp"]
|
||||
branch = post-3.2.1-transmission
|
||||
path = third-party/utfcpp
|
||||
url = https://github.com/transmission/utfcpp
|
||||
# synced with https://github.com/nemtrif/utfcpp.git
|
||||
url = https://github.com/transmission/utfcpp.git
|
||||
[submodule "third-party/libdeflate"]
|
||||
path = third-party/libdeflate
|
||||
url = https://github.com/transmission/libdeflate
|
||||
branch = v1.17.x
|
||||
# synced with https://github.com/ebiggers/libdeflate.git
|
||||
url = https://github.com/transmission/libdeflate.git
|
||||
[submodule "third-party/libpsl"]
|
||||
path = third-party/libpsl
|
||||
url = https://github.com/transmission/libpsl.git
|
||||
branch = post-3.0.0-transmission
|
||||
[submodule "third-party/fmt"]
|
||||
path = third-party/fmt
|
||||
# synced with https://github.com/fmtlib/fmt.git
|
||||
url = https://github.com/transmission/fmt.git
|
||||
branch = 9-x-y
|
||||
[submodule "third-party/fast_float"]
|
||||
path = third-party/fast_float
|
||||
url = https://github.com/transmission/fast_float
|
||||
# synced with https://github.com/fastfloat/fast_float.git
|
||||
url = https://github.com/transmission/fast_float.git
|
||||
[submodule "third-party/wide-integer"]
|
||||
path = third-party/wide-integer
|
||||
url = https://github.com/transmission/wide-integer
|
||||
# synced with https://github.com/ckormanyos/wide-integer.git
|
||||
url = https://github.com/transmission/wide-integer.git
|
||||
[submodule "third-party/small"]
|
||||
path = third-party/small
|
||||
# synced with https://github.com/alandefreitas/small.git
|
||||
url = https://github.com/transmission/small.git
|
||||
[submodule "third-party/rapidjson"]
|
||||
path = third-party/rapidjson
|
||||
# synced with https://github.com/Tencent/rapidjson.git
|
||||
url = https://github.com/transmission/rapidjson.git
|
||||
fetchRecurseSubmodules = false
|
||||
[submodule "third-party/rpavlik-cmake-modules"]
|
||||
path = third-party/rpavlik-cmake-modules
|
||||
# synced with https://github.com/rpavlik/cmake-modules.git
|
||||
url = https://github.com/transmission/rpavlik-cmake-modules.git
|
||||
|
|
12
.tx/config
12
.tx/config
|
@ -12,72 +12,84 @@ file_filter = macosx/<lang>.lproj/Localizable.strings
|
|||
source_file = macosx/en.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-AddMagnetWindow]
|
||||
file_filter = macosx/<lang>.lproj/AddMagnetWindow.strings
|
||||
source_file = macosx/en.lproj/AddMagnetWindow.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-AddWindow]
|
||||
file_filter = macosx/<lang>.lproj/AddWindow.strings
|
||||
source_file = macosx/en.lproj/AddWindow.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-Creator]
|
||||
file_filter = macosx/<lang>.lproj/Creator.strings
|
||||
source_file = macosx/en.lproj/Creator.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-GlobalOptionsPopover]
|
||||
file_filter = macosx/<lang>.lproj/GlobalOptionsPopover.strings
|
||||
source_file = macosx/en.lproj/GlobalOptionsPopover.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-GroupRules]
|
||||
file_filter = macosx/<lang>.lproj/GroupRules.strings
|
||||
source_file = macosx/en.lproj/GroupRules.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-InfoActivityView]
|
||||
file_filter = macosx/<lang>.lproj/InfoActivityView.strings
|
||||
source_file = macosx/en.lproj/InfoActivityView.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-InfoGeneralView]
|
||||
file_filter = macosx/<lang>.lproj/InfoGeneralView.strings
|
||||
source_file = macosx/en.lproj/InfoGeneralView.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-InfoOptionsView]
|
||||
file_filter = macosx/<lang>.lproj/InfoOptionsView.strings
|
||||
source_file = macosx/en.lproj/InfoOptionsView.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-MainMenu]
|
||||
file_filter = macosx/<lang>.lproj/MainMenu.strings
|
||||
source_file = macosx/en.lproj/MainMenu.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-PrefsWindow]
|
||||
file_filter = macosx/<lang>.lproj/PrefsWindow.strings
|
||||
source_file = macosx/en.lproj/PrefsWindow.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:mac-ql]
|
||||
file_filter = macosx/QuickLookPlugin/<lang>.lproj/Localizable.strings
|
||||
source_file = macosx/QuickLookPlugin/en.lproj/Localizable.strings
|
||||
source_lang = en
|
||||
type = STRINGS
|
||||
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-CN, zh_TW: zh-TW
|
||||
|
||||
[o:transmissionbt:p:transmissionbt:r:qt]
|
||||
file_filter = qt/translations/transmission_<lang>.ts
|
||||
|
|
1
AUTHORS
1
AUTHORS
|
@ -11,7 +11,6 @@ Contributor Team
|
|||
Dmitry Serov (@DevilDimon) (macOS client)
|
||||
Dzmitry Neviadomski (@nevack) (macOS client)
|
||||
FX Coudert (@fxcoudert) (macOS client)
|
||||
Gary Elshaw (@GaryElshaw)
|
||||
John Clay (@JohnClay)
|
||||
SweetP Pro (@sweetppro) (macOS client)
|
||||
Yat Ho (@tearfur) (libtransmission)
|
||||
|
|
|
@ -18,6 +18,10 @@ if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
|||
FORCE)
|
||||
endif()
|
||||
|
||||
if(VCPKG_TARGET_ANDROID)
|
||||
include(cmake/VcpkgAndroid.cmake)
|
||||
endif()
|
||||
|
||||
project(transmission)
|
||||
|
||||
set(TR_THIRD_PARTY_DIR_NAME third-party)
|
||||
|
@ -105,6 +109,19 @@ else()
|
|||
set(TR_STABLE_RELEASE 1)
|
||||
endif()
|
||||
|
||||
# derived from above: CFBundleVersion
|
||||
# note: CFBundleVersion only honors 3 numbers, so third number has to hold both patch and beta info.
|
||||
# 5.0.1-dev -> 14719.0.100
|
||||
# 5.0.1-beta.1 -> 14719.0.101
|
||||
# 5.0.1 -> 14719.0.199
|
||||
math(EXPR CFBUNDLE_1 "${TR_VERSION_MAJOR} + 14714")
|
||||
math(EXPR CFBUNDLE_2 "${TR_VERSION_MINOR}")
|
||||
math(EXPR CFBUNDLE_3 "${TR_VERSION_PATCH} * 100 + 0${TR_STABLE_RELEASE} * 99 + 0${TR_VERSION_BETA_NUMBER}")
|
||||
set(CFBUNDLE_VERSION "${CFBUNDLE_1}.${CFBUNDLE_2}.${CFBUNDLE_3}")
|
||||
unset(CFBUNDLE_1)
|
||||
unset(CFBUNDLE_2)
|
||||
unset(CFBUNDLE_3)
|
||||
|
||||
# derived from above: semver version string. https://semver.org/
|
||||
# '4.0.0-beta.1'
|
||||
# '4.0.0-beta.1.dev' (a dev release between beta 1 and 2)
|
||||
|
@ -493,7 +510,13 @@ if(NOT USE_SYSTEM_NATPMP)
|
|||
NATPMP_STATICLIB)
|
||||
endif()
|
||||
|
||||
tr_add_external_auto_library(MINIUPNPC miniupnpc miniupnpc
|
||||
if(WIN32)
|
||||
# https://github.com/miniupnp/miniupnp/pull/304
|
||||
set(TR_MINIUPNPC_LIBNAME libminiupnpc)
|
||||
else()
|
||||
set(TR_MINIUPNPC_LIBNAME miniupnpc)
|
||||
endif()
|
||||
tr_add_external_auto_library(MINIUPNPC miniupnp/miniupnpc ${TR_MINIUPNPC_LIBNAME}
|
||||
TARGET miniupnpc::libminiupnpc
|
||||
CMAKE_ARGS
|
||||
-DUPNPC_BUILD_STATIC=ON
|
||||
|
@ -504,9 +527,10 @@ if(NOT USE_SYSTEM_MINIUPNPC)
|
|||
INTERFACE
|
||||
MINIUPNP_STATICLIB)
|
||||
|
||||
set(MINIUPNPC_VERSION 1.9)
|
||||
set(MINIUPNPC_API_VERSION 12)
|
||||
set(MINIUPNPC_VERSION 2.2)
|
||||
set(MINIUPNPC_API_VERSION 17)
|
||||
endif()
|
||||
unset(TR_MINIUPNPC_LIBNAME)
|
||||
|
||||
target_compile_definitions(miniupnpc::libminiupnpc
|
||||
INTERFACE
|
||||
|
@ -626,7 +650,6 @@ else()
|
|||
-Wextra
|
||||
-Wcast-align
|
||||
-Wduplicated-cond
|
||||
-Wexit-time-destructors
|
||||
-Wextra-semi
|
||||
-Wextra-semi-stmt
|
||||
-Wextra-tokens
|
||||
|
|
|
@ -41,7 +41,7 @@ On macOS, Transmission is usually built with Xcode. Everywhere else, it's CMake
|
|||
- Prefer `enum class` over `enum`
|
||||
- Prefer new-style headers, e.g. `<cstring>` over `<string.h>`
|
||||
- Fix any warnings in new code before merging
|
||||
- Run `./code-style.sh` on your code to ensure the whole codebase has consistent indentation.
|
||||
- Run `./code_style.sh` on your code to ensure the whole codebase has consistent indentation.
|
||||
|
||||
Note that Transmission existed in C for over a decade and those idioms don't change overnight. "Follow the C++ core guidelines" can be difficult when working with older code, and the maintainers will understand that when reviewing your PRs. :smiley:
|
||||
|
||||
|
|
2
COPYING
2
COPYING
|
@ -1,4 +1,4 @@
|
|||
Copyright 2005-2023. All code is copyrighted by the respective authors.
|
||||
Copyright 2005-2024. All code is copyrighted by the respective authors.
|
||||
|
||||
Transmission can be redistributed and/or modified under the terms of
|
||||
the GNU GPLv2 (http://www.gnu.org/licenses/license-list.html#GPLv2),
|
||||
|
|
|
@ -400,8 +400,10 @@
|
|||
C3D9062F27B7F7E200EF2386 /* libpsl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3D9062127B7E3C900EF2386 /* libpsl.a */; };
|
||||
C809AEE7291ECFD000BFDBE1 /* NSDataAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = C809AEE6291ECFD000BFDBE1 /* NSDataAdditions.mm */; };
|
||||
C82B30312953337B0001BD6E /* NSDataAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = C809AEE6291ECFD000BFDBE1 /* NSDataAdditions.mm */; };
|
||||
C83B17212B7341BC00B2EAE4 /* tr-assert.cc in Sources */ = {isa = PBXBuildFile; fileRef = C1425B321EE9C5EA001DB85F /* tr-assert.cc */; };
|
||||
C841A28129197724009F18E8 /* NSKeyedUnarchiverAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = C841A28029197724009F18E8 /* NSKeyedUnarchiverAdditions.mm */; };
|
||||
C843FC8429C51B9400491854 /* utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = C843FC8329C51B9400491854 /* utils.mm */; };
|
||||
C843FC8729C8B40800491854 /* VersionComparator.mm in Sources */ = {isa = PBXBuildFile; fileRef = C843FC8629C8B40800491854 /* VersionComparator.mm */; };
|
||||
C86BCD9928228A9600F45599 /* SparkleProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = C86BCD9828228A9600F45599 /* SparkleProxy.mm */; };
|
||||
C87369652809984200573C90 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C87369642809984200573C90 /* UserNotifications.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
C8748D8A29891EA100D9E979 /* suffixes_dafsa.h in Headers */ = {isa = PBXBuildFile; fileRef = C8748D8929891EA100D9E979 /* suffixes_dafsa.h */; };
|
||||
|
@ -433,26 +435,27 @@
|
|||
C8B27BA328153F6300A22B5D /* create.cc in Sources */ = {isa = PBXBuildFile; fileRef = C887BEC02807FCE900867D3C /* create.cc */; };
|
||||
C8B27BA428153F6600A22B5D /* edit.cc in Sources */ = {isa = PBXBuildFile; fileRef = C887BEC22807FCE900867D3C /* edit.cc */; };
|
||||
C8B27BA528153F6900A22B5D /* show.cc in Sources */ = {isa = PBXBuildFile; fileRef = C887BEC32807FCE900867D3C /* show.cc */; };
|
||||
C8ED0FB1281C10F100B44472 /* addr_is_reserved.c in Sources */ = {isa = PBXBuildFile; fileRef = C8ED0FAF281C10F100B44472 /* addr_is_reserved.c */; };
|
||||
C8ED0FB2281C10F100B44472 /* addr_is_reserved.h in Headers */ = {isa = PBXBuildFile; fileRef = C8ED0FB0281C10F100B44472 /* addr_is_reserved.h */; };
|
||||
CAB35C64252F6F5E00552A55 /* mime-types.h in Headers */ = {isa = PBXBuildFile; fileRef = CAB35C62252F6F5E00552A55 /* mime-types.h */; };
|
||||
CCEBA596277340F6DF9F4480 /* session-alt-speeds.cc in Sources */ = {isa = PBXBuildFile; fileRef = CCEBA596277340F6DF9F4481 /* session-alt-speeds.cc */; };
|
||||
CCEBA596277340F6DF9F4482 /* session-alt-speeds.h in Headers */ = {isa = PBXBuildFile; fileRef = CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */; };
|
||||
D5C306568A7346FFFB8EFAD0 /* session-settings.cc in Sources */ = {isa = PBXBuildFile; fileRef = D5C306568A7346FFFB8EFAD1 /* session-settings.cc */; };
|
||||
D5C306568A7346FFFB8EFAD2 /* session-settings.h in Headers */ = {isa = PBXBuildFile; fileRef = D5C306568A7346FFFB8EFAD3 /* session-settings.h */; };
|
||||
D9057D68C13B75636539B680 /* variant-converters.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9057D68C13B75636539B681 /* variant-converters.cc */; };
|
||||
E138A9780C04D88F00C5426C /* ProgressGradients.mm in Sources */ = {isa = PBXBuildFile; fileRef = E138A9760C04D88F00C5426C /* ProgressGradients.mm */; };
|
||||
E23B55A5FC3B557F7746D510 /* interned-string.h in Headers */ = {isa = PBXBuildFile; fileRef = E23B55A5FC3B557F7746D511 /* interned-string.h */; settings = {ATTRIBUTES = (Project, ); }; };
|
||||
E71A5565279C2DD600EBFA1E /* tr-assert.mm in Sources */ = {isa = PBXBuildFile; fileRef = E71A5564279C2DD600EBFA1E /* tr-assert.mm */; };
|
||||
E975121263DD973CAF4AEBA0 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = E975121263DD973CAF4AEBA1 /* timer.h */; };
|
||||
E975121263DD973CAF4AEBA2 /* timer-ev.h in Headers */ = {isa = PBXBuildFile; fileRef = E975121263DD973CAF4AEBA3 /* timer-ev.h */; };
|
||||
E975121263DD973CAF4AEBA4 /* timer-ev.cc in Sources */ = {isa = PBXBuildFile; fileRef = E975121263DD973CAF4AEBA5 /* timer-ev.cc */; };
|
||||
ED20B87C28589274005FA6BE /* common_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = ED20B87B28589274005FA6BE /* common_defs.h */; };
|
||||
ED20B87F285892C5005FA6BE /* crc32_multipliers.h in Headers */ = {isa = PBXBuildFile; fileRef = ED20B87D285892C5005FA6BE /* crc32_multipliers.h */; };
|
||||
ED20B880285892C5005FA6BE /* crc32_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = ED20B87E285892C5005FA6BE /* crc32_tables.h */; };
|
||||
ED67FB422B70FCE400D8A037 /* settings.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED67FB402B70FCE400D8A037 /* settings.cc */; };
|
||||
ED67FB432B70FCE400D8A037 /* settings.h in Headers */ = {isa = PBXBuildFile; fileRef = ED67FB412B70FCE400D8A037 /* settings.h */; };
|
||||
ED86936F2ADAE34D00342B1A /* DefaultAppHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */; };
|
||||
ED8A163F2735A8AA000D61F9 /* peer-mgr-active-requests.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8A163B2735A8AA000D61F9 /* peer-mgr-active-requests.h */; };
|
||||
ED8A16402735A8AA000D61F9 /* peer-mgr-active-requests.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED8A163C2735A8AA000D61F9 /* peer-mgr-active-requests.cc */; };
|
||||
ED8A16412735A8AA000D61F9 /* peer-mgr-wishlist.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8A163D2735A8AA000D61F9 /* peer-mgr-wishlist.h */; };
|
||||
ED8A16422735A8AA000D61F9 /* peer-mgr-wishlist.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED8A163E2735A8AA000D61F9 /* peer-mgr-wishlist.cc */; };
|
||||
ED9862972B979AA2002F3035 /* Utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED9862962B979AA2002F3035 /* Utils.mm */; };
|
||||
EDBAAC8C29E486BC00D9495F /* global-ip-cache.h in Headers */ = {isa = PBXBuildFile; fileRef = EDBAAC8B29E486BC00D9495F /* global-ip-cache.h */; };
|
||||
EDBAAC8E29E486C200D9495F /* global-ip-cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = EDBAAC8D29E486C200D9495F /* global-ip-cache.cc */; };
|
||||
EDBDFA9E25AFCCA60093D9C1 /* evutil_time.c in Sources */ = {isa = PBXBuildFile; fileRef = EDBDFA9D25AFCCA60093D9C1 /* evutil_time.c */; };
|
||||
|
@ -654,7 +657,7 @@
|
|||
455C093B2877672C0003A078 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C093C2877672E0003A078 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C093D287767300003A078 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C093E287767320003A078 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C093E287767320003A078 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/PrefsWindow.strings"; sourceTree = "<group>"; };
|
||||
455C093F287767350003A078 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C0940287767380003A078 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C09412877673A0003A078 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
|
@ -665,7 +668,7 @@
|
|||
457DC1E32873930500ED04C4 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
457DC1E42873930900ED04C4 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
457DC1E52873930D00ED04C4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
457DC1E62873931100ED04C4 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
457DC1E62873931100ED04C4 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
457DC1E72873931500ED04C4 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
457DC1E82873931900ED04C4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
457DC1E92873931E00ED04C4 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
|
@ -678,14 +681,14 @@
|
|||
45C688C92875AC8100A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688CA2875AC8300A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688CB2875AC8700A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688CC2875AC8900A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688CC2875AC8900A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/AddMagnetWindow.strings"; sourceTree = "<group>"; };
|
||||
45C688CD2875AC8D00A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688CE2875AC8F00A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688CF2875AC9200A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
45C688D02875ADF000A2EB25 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
45C688D12875ADF500A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
45C688D22875ADF800A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
45C688D32875ADFB00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
45C688D32875ADFB00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/GroupRules.strings"; sourceTree = "<group>"; };
|
||||
45C688D42875ADFE00A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
45C688D52875AE0100A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
45C688D62875AE0500A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
|
@ -696,7 +699,7 @@
|
|||
45C688DB2875B06200A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
45C688DC2875B06400A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
45C688DD2875B06700A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
45C688DE2875B06900A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
45C688DE2875B06900A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoGeneralView.strings"; sourceTree = "<group>"; };
|
||||
45C688DF2875B06D00A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
45C688E02875B06F00A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
45C688E12875B07300A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
|
@ -705,7 +708,7 @@
|
|||
45C688E42875B17F00A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
45C688E52875B18200A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
45C688E62875B18700A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
45C688E72875B18A00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
45C688E72875B18A00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoActivityView.strings"; sourceTree = "<group>"; };
|
||||
45C688E82875B19000A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
45C688E92875B19200A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
45C688EA2875B19500A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
|
@ -714,7 +717,7 @@
|
|||
45C688ED2875B2C000A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
45C688EE2875B2C200A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
45C688EF2875B2C400A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
45C688F02875B2C700A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
45C688F02875B2C700A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoOptionsView.strings"; sourceTree = "<group>"; };
|
||||
45C688F12875B2CA00A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
45C688F22875B2CD00A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
45C688F32875B2CF00A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
|
@ -723,7 +726,7 @@
|
|||
45C688F62875B3F600A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
45C688F72875B3F800A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
45C688F82875B3FB00A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
45C688F92875B3FF00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
45C688F92875B3FF00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/GlobalOptionsPopover.strings"; sourceTree = "<group>"; };
|
||||
45C688FA2875B40300A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
45C688FB2875B40600A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
45C688FC2875B40900A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
|
@ -732,7 +735,7 @@
|
|||
45C688FF28762FEC00A2EB25 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
45C6890028762FEF00A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
45C6890128762FF200A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
45C6890228762FF800A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
45C6890228762FF800A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/AddWindow.strings"; sourceTree = "<group>"; };
|
||||
45C6890328762FFD00A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
45C6890428762FFF00A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
45C689052876300200A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
|
@ -741,7 +744,7 @@
|
|||
45C6890828763C3200A2EB25 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
45C6890928763C3600A2EB25 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
45C6890A28763C3900A2EB25 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
45C6890B28763C3C00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = pt_PT.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
45C6890B28763C3C00A2EB25 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Creator.strings"; sourceTree = "<group>"; };
|
||||
45C6890C28763C3F00A2EB25 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
45C6890D28763C4300A2EB25 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
45C6890E28763C4600A2EB25 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
|
@ -927,8 +930,8 @@
|
|||
A25E74450AF5089E006F11AE /* ExpandedPathToPathTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExpandedPathToPathTransformer.h; sourceTree = "<group>"; };
|
||||
A25E74460AF5089E006F11AE /* ExpandedPathToIconTransformer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ExpandedPathToIconTransformer.mm; sourceTree = "<group>"; };
|
||||
A25E74470AF5089E006F11AE /* ExpandedPathToIconTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExpandedPathToIconTransformer.h; sourceTree = "<group>"; };
|
||||
A2613F9811B3383200472893 /* pt_PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_PT; path = pt_PT.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A2613F9911B3383200472893 /* pt_PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_PT; path = pt_PT.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A2613F9811B3383200472893 /* pt-PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
A2613F9911B3383200472893 /* pt-PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
A263C5661560A3CF0082A3D1 /* da */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A263C5671560A4210082A3D1 /* da */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A263C6B1F6718E2486DB20E1 /* tr-buffer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = "tr-buffer.h"; sourceTree = "<group>"; };
|
||||
|
@ -959,7 +962,7 @@
|
|||
A28393FD10D54A79005C0240 /* de */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A28393FF10D54A96005C0240 /* de */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A284214212DA663E00FBDDBB /* tr-udp.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "tr-udp.cc"; sourceTree = "<group>"; };
|
||||
A28B3A2D160E1BC900D4A2BC /* pt_PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pt_PT; path = pt_PT.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A28B3A2D160E1BC900D4A2BC /* pt-PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
A28E1DDF0CFFD8EC00E16385 /* ButtonToolbarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ButtonToolbarItem.h; sourceTree = "<group>"; };
|
||||
A28F4F750E085BDC003A3882 /* ColorTextField.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorTextField.h; sourceTree = "<group>"; };
|
||||
A28F4F760E085BDC003A3882 /* ColorTextField.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ColorTextField.mm; sourceTree = "<group>"; };
|
||||
|
@ -1153,6 +1156,136 @@
|
|||
C1846B87294F781800A98F30 /* wildmat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wildmat.h; sourceTree = "<group>"; };
|
||||
C1846B88294F781800A98F30 /* wildmat.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wildmat.c; sourceTree = "<group>"; };
|
||||
C1846B9E294F7A3400A98F30 /* libwildmat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libwildmat.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C1A6BB3C2B3E69CB00C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB3D2B3E6A0700C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB3E2B3E6A0F00C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB3F2B3E6A1800C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB402B3E6A2200C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB412B3E6A2900C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB422B3E6A3000C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB432B3E6A3A00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BB442B3E6A3F00C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB452B3E6A4C00C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB462B3E6ACE00C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB472B3E6ADA00C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB482B3E6ADF00C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB492B3E6AE800C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB4A2B3E6AEE00C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB4B2B3E6AF500C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB4C2B3E6AFC00C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB4D2B3E6B0200C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C1A6BB4E2B3E6B0800C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB4F2B3E6B0D00C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB532B3E6C6600C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB542B3E6C7900C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB552B3E6C8700C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB562B3E6C8D00C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB572B3E6C9000C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB582B3E6C9300C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB592B3E6C9B00C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB5A2B3E6C9C00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C1A6BB5B2B3E6CA100C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB5C2B3E6CA300C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB5D2B3E6CD600C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB5E2B3E6CD800C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB5F2B3E6CDA00C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB602B3E6CDB00C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB612B3E6CDD00C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB622B3E6CDF00C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/PrefsWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB632B3E6CE000C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB642B3E6CE200C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB652B3E6CE400C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/PrefsWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB662B3E6CE500C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/PrefsWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB672B3E6CF000C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB682B3E6CF200C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB692B3E6CF300C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB6A2B3E6CF500C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB6B2B3E6CF600C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB6C2B3E6CF800C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Creator.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB6D2B3E6CFA00C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB6E2B3E6CFC00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Creator.strings; sourceTree = "<group>"; };
|
||||
C1A6BB6F2B3E6D0200C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Creator.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB702B3E6D0300C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Creator.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB712B3E6D1000C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB722B3E6D1200C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB732B3E6D1400C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB742B3E6D1500C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB752B3E6D1600C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB762B3E6D1800C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/AddWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB772B3E6D1900C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB782B3E6D1B00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/AddWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB792B3E6D1E00C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/AddWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB7A2B3E6D2000C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/AddWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB7B2B3E6D6200C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB7C2B3E6D6400C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB7D2B3E6D6500C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB7E2B3E6D6700C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB7F2B3E6D6800C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB802B3E6D6A00C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/AddMagnetWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB812B3E6D6B00C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB822B3E6D6D00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/AddMagnetWindow.strings; sourceTree = "<group>"; };
|
||||
C1A6BB832B3E6D6F00C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/AddMagnetWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB842B3E6D7100C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/AddMagnetWindow.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB852B3E6D8100C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB862B3E6D8300C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB872B3E6D8400C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB882B3E6D8600C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB892B3E6D8700C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB8A2B3E6D8900C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/GroupRules.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB8B2B3E6D8A00C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB8C2B3E6D8B00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/GroupRules.strings; sourceTree = "<group>"; };
|
||||
C1A6BB8D2B3E6D8D00C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/GroupRules.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB8E2B3E6D8F00C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/GroupRules.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB8F2B3E6DA200C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB902B3E6DA500C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB912B3E6DA600C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB922B3E6DA700C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB932B3E6DA900C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB942B3E6DAA00C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoGeneralView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB952B3E6DAB00C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB962B3E6DAD00C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoGeneralView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB972B3E6DAF00C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoGeneralView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB982B3E6DB000C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/InfoGeneralView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB992B3E6DBE00C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB9A2B3E6DC000C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB9B2B3E6DC100C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB9C2B3E6DC300C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB9D2B3E6DC400C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BB9E2B3E6DC600C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoActivityView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BB9F2B3E6DC700C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA02B3E6DC900C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoActivityView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA12B3E6DCA00C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoActivityView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBA22B3E6DCC00C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/InfoActivityView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBA32B3E6DDC00C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA42B3E6DDE00C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA52B3E6DDF00C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA62B3E6DE000C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA72B3E6DE200C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBA82B3E6DE300C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoOptionsView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBA92B3E6DE500C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBAA2B3E6DE600C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoOptionsView.strings; sourceTree = "<group>"; };
|
||||
C1A6BBAB2B3E6DE800C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoOptionsView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBAC2B3E6DEA00C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/InfoOptionsView.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBAD2B3E6E0900C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBAE2B3E6E0B00C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBAF2B3E6E0C00C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB02B3E6E0E00C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB12B3E6E0F00C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB22B3E6E1000C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/GlobalOptionsPopover.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBB32B3E6E1200C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB42B3E6E1300C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/GlobalOptionsPopover.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB52B3E6E1600C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/GlobalOptionsPopover.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBB62B3E6E1A00C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/GlobalOptionsPopover.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBB72B3E6E2900C4A151 /* eu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB82B3E6E2B00C4A151 /* he */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBB92B3E6E2C00C4A151 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBBA2B3E6E2E00C4A151 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBBB2B3E6E2F00C4A151 /* pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBBC2B3E6E3100C4A151 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBBD2B3E6E3200C4A151 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBBE2B3E6E3300C4A151 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C1A6BBBF2B3E6E3600C4A151 /* zh-CN */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C1A6BBC02B3E6E3700C4A151 /* zh-TW */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C1BF7BA71F2A3CB7008E88A7 /* upnpdev.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = upnpdev.c; sourceTree = "<group>"; };
|
||||
C1BF7BA91F2A3CCE008E88A7 /* upnpdev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = upnpdev.h; sourceTree = "<group>"; };
|
||||
C1F690FC1AD0627500D95CF0 /* daemon-posix.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "daemon-posix.cc"; sourceTree = "<group>"; };
|
||||
|
@ -1206,6 +1339,8 @@
|
|||
C841A27F29197724009F18E8 /* NSKeyedUnarchiverAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSKeyedUnarchiverAdditions.h; sourceTree = "<group>"; };
|
||||
C841A28029197724009F18E8 /* NSKeyedUnarchiverAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NSKeyedUnarchiverAdditions.mm; sourceTree = "<group>"; };
|
||||
C843FC8329C51B9400491854 /* utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = utils.mm; sourceTree = "<group>"; };
|
||||
C843FC8529C8B40800491854 /* VersionComparator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VersionComparator.h; sourceTree = "<group>"; };
|
||||
C843FC8629C8B40800491854 /* VersionComparator.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VersionComparator.mm; sourceTree = "<group>"; };
|
||||
C86BCD9828228A9600F45599 /* SparkleProxy.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SparkleProxy.mm; sourceTree = "<group>"; };
|
||||
C87369642809984200573C90 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
|
||||
C8748D8929891EA100D9E979 /* suffixes_dafsa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = suffixes_dafsa.h; path = "third-party/suffixes_dafsa.h"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1217,28 +1352,30 @@
|
|||
C8B27B7F28153F2B00A22B5D /* transmission-create */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "transmission-create"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C8B27B9028153F3100A22B5D /* transmission-edit */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "transmission-edit"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C8B27BA128153F3400A22B5D /* transmission-show */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "transmission-show"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C8ED0FAF281C10F100B44472 /* addr_is_reserved.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = addr_is_reserved.c; sourceTree = "<group>"; };
|
||||
C8ED0FB0281C10F100B44472 /* addr_is_reserved.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = addr_is_reserved.h; sourceTree = "<group>"; };
|
||||
CAB35C62252F6F5E00552A55 /* mime-types.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "mime-types.h"; sourceTree = "<group>"; };
|
||||
CCEBA596277340F6DF9F4481 /* session-alt-speeds.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "session-alt-speeds.cc"; sourceTree = "<group>"; };
|
||||
CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = "session-alt-speeds.h"; sourceTree = "<group>"; };
|
||||
D5C306568A7346FFFB8EFAD1 /* session-settings.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "session-settings.cc"; sourceTree = "<group>"; };
|
||||
D5C306568A7346FFFB8EFAD3 /* session-settings.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = "session-settings.h"; sourceTree = "<group>"; };
|
||||
D9057D68C13B75636539B681 /* variant-converters.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "variant-converters.cc"; sourceTree = "<group>"; };
|
||||
E138A9750C04D88F00C5426C /* ProgressGradients.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProgressGradients.h; sourceTree = "<group>"; };
|
||||
E138A9760C04D88F00C5426C /* ProgressGradients.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ProgressGradients.mm; sourceTree = "<group>"; };
|
||||
E23B55A5FC3B557F7746D511 /* interned-string.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = "interned-string.h"; sourceTree = "<group>"; };
|
||||
E71A5564279C2DD600EBFA1E /* tr-assert.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "tr-assert.mm"; sourceTree = "<group>"; };
|
||||
E975121263DD973CAF4AEBA1 /* timer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = timer.h; sourceTree = "<group>"; };
|
||||
E975121263DD973CAF4AEBA3 /* timer-ev.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "timer-ev.h"; sourceTree = "<group>"; };
|
||||
E975121263DD973CAF4AEBA5 /* timer-ev.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "timer-ev.cc"; sourceTree = "<group>"; };
|
||||
ED20B87B28589274005FA6BE /* common_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common_defs.h; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
ED8A163C2735A8AA000D61F9 /* peer-mgr-active-requests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "peer-mgr-active-requests.cc"; sourceTree = "<group>"; };
|
||||
ED8A163D2735A8AA000D61F9 /* peer-mgr-wishlist.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "peer-mgr-wishlist.h"; sourceTree = "<group>"; };
|
||||
ED8A163E2735A8AA000D61F9 /* peer-mgr-wishlist.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "peer-mgr-wishlist.cc"; sourceTree = "<group>"; };
|
||||
ED9862952B979AA2002F3035 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; };
|
||||
ED9862962B979AA2002F3035 /* Utils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Utils.mm; sourceTree = "<group>"; };
|
||||
EDBAAC8B29E486BC00D9495F /* global-ip-cache.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "global-ip-cache.h"; sourceTree = "<group>"; };
|
||||
EDBAAC8D29E486C200D9495F /* global-ip-cache.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "global-ip-cache.cc"; sourceTree = "<group>"; };
|
||||
EDBDFA9D25AFCCA60093D9C1 /* evutil_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = evutil_time.c; sourceTree = "<group>"; };
|
||||
|
@ -1439,6 +1576,8 @@
|
|||
C86BCD9828228A9600F45599 /* SparkleProxy.mm */,
|
||||
4DF0C5AA0899190500DD8943 /* Controller.h */,
|
||||
4DF0C5A90899190500DD8943 /* Controller.mm */,
|
||||
C843FC8529C8B40800491854 /* VersionComparator.h */,
|
||||
C843FC8629C8B40800491854 /* VersionComparator.mm */,
|
||||
4DFBC2DD09C0970D00D5C571 /* Torrent.h */,
|
||||
4DFBC2DE09C0970D00D5C571 /* Torrent.mm */,
|
||||
A27F0F310E19AD9800B2DB97 /* TorrentGroup.h */,
|
||||
|
@ -1537,6 +1676,8 @@
|
|||
A222EA7A0E6C32C4009FB003 /* BlocklistScheduler.mm */,
|
||||
ED86936D2ADAE34D00342B1A /* DefaultAppHelper.h */,
|
||||
ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */,
|
||||
ED9862952B979AA2002F3035 /* Utils.h */,
|
||||
ED9862962B979AA2002F3035 /* Utils.mm */,
|
||||
A2AB883916A399A6008FAD50 /* VDKQueue */,
|
||||
);
|
||||
name = Sources;
|
||||
|
@ -1752,9 +1893,8 @@
|
|||
BEFC1E140C07861A00B0BB3C /* session.h */,
|
||||
CCEBA596277340F6DF9F4481 /* session-alt-speeds.cc */,
|
||||
CCEBA596277340F6DF9F4483 /* session-alt-speeds.h */,
|
||||
D5C306568A7346FFFB8EFAD1 /* session-settings.cc */,
|
||||
D5C306568A7346FFFB8EFAD3 /* session-settings.h */,
|
||||
D9057D68C13B75636539B681 /* variant-converters.cc */,
|
||||
ED67FB402B70FCE400D8A037 /* settings.cc */,
|
||||
ED67FB412B70FCE400D8A037 /* settings.h */,
|
||||
A25D2CBB0CF4C7190096A262 /* stats.cc */,
|
||||
A25D2CBA0CF4C7190096A262 /* stats.h */,
|
||||
C11DEA141FCD31C0009E22B9 /* subprocess-posix.cc */,
|
||||
|
@ -1775,7 +1915,6 @@
|
|||
2B9BA6C508B488FE586A0AB3 /* torrents.h */,
|
||||
C1425B321EE9C5EA001DB85F /* tr-assert.cc */,
|
||||
C1425B331EE9C5EA001DB85F /* tr-assert.h */,
|
||||
E71A5564279C2DD600EBFA1E /* tr-assert.mm */,
|
||||
A263C6B1F6718E2486DB20E1 /* tr-buffer.h */,
|
||||
A22CFCA60FC24ED80009BD3E /* tr-dht.cc */,
|
||||
A22CFCA70FC24ED80009BD3E /* tr-dht.h */,
|
||||
|
@ -1962,39 +2101,12 @@
|
|||
BE1183410CE15DF00002D0F3 /* libminiupnp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A22B00AE116A9E90003315FC /* connecthostport.c */,
|
||||
BE1183610CE160D50002D0F3 /* igd_desc_parse.c */,
|
||||
BE1183620CE160D50002D0F3 /* minixml.c */,
|
||||
BE1183630CE160D50002D0F3 /* miniwget.c */,
|
||||
BE1183640CE160D50002D0F3 /* minissdpc.c */,
|
||||
BE1183650CE160D50002D0F3 /* minisoap.c */,
|
||||
BE1183660CE160D50002D0F3 /* upnpreplyparse.c */,
|
||||
BE1183670CE160D50002D0F3 /* upnpcommands.c */,
|
||||
BE1183680CE160D50002D0F3 /* miniupnpc.c */,
|
||||
A20162CB13DE497000E15488 /* portlistingparse.c */,
|
||||
A20162C713DE48BF00E15488 /* receivedata.c */,
|
||||
C1BF7BA71F2A3CB7008E88A7 /* upnpdev.c */,
|
||||
C12F19771E1AE3C30005E93F /* upnperrors.c */,
|
||||
A22B00AF116A9E90003315FC /* connecthostport.h */,
|
||||
BE11834E0CE160C50002D0F3 /* miniupnpc_declspec.h */,
|
||||
BE11834F0CE160C50002D0F3 /* igd_desc_parse.h */,
|
||||
BE1183500CE160C50002D0F3 /* minixml.h */,
|
||||
BE1183510CE160C50002D0F3 /* miniwget.h */,
|
||||
BE1183520CE160C50002D0F3 /* minisoap.h */,
|
||||
A2F8CD420F3D0F4A00DB356A /* miniupnpcstrings.h */,
|
||||
A20162CF13DE49E500E15488 /* miniupnpctypes.h */,
|
||||
BE1183530CE160C50002D0F3 /* upnpreplyparse.h */,
|
||||
BE1183540CE160C50002D0F3 /* upnpcommands.h */,
|
||||
BE1183550CE160C50002D0F3 /* miniupnpc.h */,
|
||||
BE1183560CE160C50002D0F3 /* minissdpc.h */,
|
||||
A25485390EB66CBB004539DA /* codelength.h */,
|
||||
A20162CC13DE497000E15488 /* portlistingparse.h */,
|
||||
A20162C813DE48BF00E15488 /* receivedata.h */,
|
||||
C1BF7BA91F2A3CCE008E88A7 /* upnpdev.h */,
|
||||
C12F197A1E1AE4460005E93F /* upnperrors.h */,
|
||||
C891A007281C02F3002E745F /* include */,
|
||||
C8734FB02B9EA39F00EF2AD9 /* src */,
|
||||
);
|
||||
name = libminiupnp;
|
||||
path = "third-party/miniupnpc";
|
||||
path = "third-party/miniupnp/miniupnpc";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BE75C3570C72A0D600DBEFE0 /* libevent */ = {
|
||||
|
@ -2149,6 +2261,51 @@
|
|||
name = Compatibility;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C8734FB02B9EA39F00EF2AD9 /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C8ED0FAF281C10F100B44472 /* addr_is_reserved.c */,
|
||||
A22B00AE116A9E90003315FC /* connecthostport.c */,
|
||||
BE1183610CE160D50002D0F3 /* igd_desc_parse.c */,
|
||||
BE1183650CE160D50002D0F3 /* minisoap.c */,
|
||||
BE1183640CE160D50002D0F3 /* minissdpc.c */,
|
||||
BE1183680CE160D50002D0F3 /* miniupnpc.c */,
|
||||
BE1183630CE160D50002D0F3 /* miniwget.c */,
|
||||
BE1183620CE160D50002D0F3 /* minixml.c */,
|
||||
A20162CB13DE497000E15488 /* portlistingparse.c */,
|
||||
A20162C713DE48BF00E15488 /* receivedata.c */,
|
||||
BE1183670CE160D50002D0F3 /* upnpcommands.c */,
|
||||
C1BF7BA71F2A3CB7008E88A7 /* upnpdev.c */,
|
||||
C12F19771E1AE3C30005E93F /* upnperrors.c */,
|
||||
BE1183660CE160D50002D0F3 /* upnpreplyparse.c */,
|
||||
C8ED0FB0281C10F100B44472 /* addr_is_reserved.h */,
|
||||
A25485390EB66CBB004539DA /* codelength.h */,
|
||||
A22B00AF116A9E90003315FC /* connecthostport.h */,
|
||||
BE1183520CE160C50002D0F3 /* minisoap.h */,
|
||||
BE1183560CE160C50002D0F3 /* minissdpc.h */,
|
||||
BE1183500CE160C50002D0F3 /* minixml.h */,
|
||||
A20162C813DE48BF00E15488 /* receivedata.h */,
|
||||
);
|
||||
path = src;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C891A007281C02F3002E745F /* include */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BE11834F0CE160C50002D0F3 /* igd_desc_parse.h */,
|
||||
BE11834E0CE160C50002D0F3 /* miniupnpc_declspec.h */,
|
||||
BE1183550CE160C50002D0F3 /* miniupnpc.h */,
|
||||
A20162CF13DE49E500E15488 /* miniupnpctypes.h */,
|
||||
BE1183510CE160C50002D0F3 /* miniwget.h */,
|
||||
A20162CC13DE497000E15488 /* portlistingparse.h */,
|
||||
BE1183540CE160C50002D0F3 /* upnpcommands.h */,
|
||||
C1BF7BA91F2A3CCE008E88A7 /* upnpdev.h */,
|
||||
C12F197A1E1AE4460005E93F /* upnperrors.h */,
|
||||
BE1183530CE160C50002D0F3 /* upnpreplyparse.h */,
|
||||
);
|
||||
path = include;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E1B6FBF80C0D719B0015FE4D /* Info Window */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2243,7 +2400,6 @@
|
|||
BEFC1E450C07861A00B0BB3C /* net.h in Headers */,
|
||||
BEFC1E4D0C07861A00B0BB3C /* session.h in Headers */,
|
||||
CCEBA596277340F6DF9F4482 /* session-alt-speeds.h in Headers */,
|
||||
D5C306568A7346FFFB8EFAD2 /* session-settings.h in Headers */,
|
||||
BEFC1E4E0C07861A00B0BB3C /* inout.h in Headers */,
|
||||
BEFC1E520C07861A00B0BB3C /* open-files.h in Headers */,
|
||||
ED8A163F2735A8AA000D61F9 /* peer-mgr-active-requests.h in Headers */,
|
||||
|
@ -2268,6 +2424,7 @@
|
|||
A29DF8BE0DB2545F00D04E5A /* verify.h in Headers */,
|
||||
C1FEE57B1C3223CC00D62832 /* watchdir.h in Headers */,
|
||||
A2AAB6650DE0D08B00E04DDA /* blocklist.h in Headers */,
|
||||
ED67FB432B70FCE400D8A037 /* settings.h in Headers */,
|
||||
A2A4E9210DE0F7E9000CE197 /* web.h in Headers */,
|
||||
A25E03E20E4015380086C225 /* tr-getopt.h in Headers */,
|
||||
A21FBBAB0EDA78C300BC3C51 /* bandwidth.h in Headers */,
|
||||
|
@ -2340,6 +2497,7 @@
|
|||
BE11835D0CE160C50002D0F3 /* upnpreplyparse.h in Headers */,
|
||||
C1BF7BAA1F2A3CCE008E88A7 /* upnpdev.h in Headers */,
|
||||
BE1183600CE160C50002D0F3 /* minissdpc.h in Headers */,
|
||||
C8ED0FB2281C10F100B44472 /* addr_is_reserved.h in Headers */,
|
||||
A254853C0EB66CD4004539DA /* codelength.h in Headers */,
|
||||
A2F8CD430F3D0F4A00DB356A /* miniupnpcstrings.h in Headers */,
|
||||
A22B00B2116A9E9F003315FC /* connecthostport.h in Headers */,
|
||||
|
@ -2784,8 +2942,17 @@
|
|||
de,
|
||||
da,
|
||||
"pt-PT",
|
||||
pt_PT,
|
||||
Base,
|
||||
eu,
|
||||
he,
|
||||
hu,
|
||||
ja,
|
||||
pl,
|
||||
"pt-BR",
|
||||
sv,
|
||||
uk,
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
);
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* Transmission */;
|
||||
projectDirPath = "";
|
||||
|
@ -2888,15 +3055,16 @@
|
|||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"third-party/miniupnpc/VERSION",
|
||||
"third-party/miniupnpc/miniupnpcstrings.h.in",
|
||||
"third-party/miniupnp/miniupnpc/VERSION",
|
||||
"third-party/miniupnp/miniupnpc/miniupnpcstrings.h.in",
|
||||
"third-party/miniupnp/miniupnpc/updateminiupnpcstrings.sh",
|
||||
);
|
||||
outputPaths = (
|
||||
"third-party/miniupnpc/miniupnpcstrings.h",
|
||||
"third-party/miniupnp/miniupnpc/miniupnpcstrings.h",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "cd third-party/miniupnpc\nsh updateminiupnpcstrings.sh \"$SCRIPT_INPUT_FILE_0\" \"$SCRIPT_INPUT_FILE_1\" \"$SCRIPT_OUTPUT_FILE_0\"\n";
|
||||
shellScript = "cd third-party/miniupnp/miniupnpc\nsh updateminiupnpcstrings.sh\n";
|
||||
};
|
||||
BE75C3510C729EE100DBEFE0 /* Copy libevent headers */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
@ -2928,7 +3096,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "cd third-party/miniupnpc && rm -f miniupnp && ln -s . miniupnp\n";
|
||||
shellScript = "cd third-party/miniupnp && rm -f miniupnp && ln -s . miniupnp\n";
|
||||
};
|
||||
C12F197E1E1AE6D50005E93F /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
@ -2990,8 +3158,6 @@
|
|||
ED8A16402735A8AA000D61F9 /* peer-mgr-active-requests.cc in Sources */,
|
||||
BEFC1E2F0C07861A00B0BB3C /* session.cc in Sources */,
|
||||
CCEBA596277340F6DF9F4480 /* session-alt-speeds.cc in Sources */,
|
||||
D5C306568A7346FFFB8EFAD0 /* session-settings.cc in Sources */,
|
||||
D9057D68C13B75636539B680 /* variant-converters.cc in Sources */,
|
||||
BEFC1E320C07861A00B0BB3C /* torrent.cc in Sources */,
|
||||
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */,
|
||||
BE7AA337F6752914B0C416B2 /* utils-ev.cc in Sources */,
|
||||
|
@ -3019,6 +3185,7 @@
|
|||
4D36BA770CA2F00800A63CA5 /* peer-mgr.cc in Sources */,
|
||||
C1077A50183EB29600634C22 /* file-posix.cc in Sources */,
|
||||
ED8A16422735A8AA000D61F9 /* peer-mgr-wishlist.cc in Sources */,
|
||||
C83B17212B7341BC00B2EAE4 /* tr-assert.cc in Sources */,
|
||||
4D36BA790CA2F00800A63CA5 /* peer-msgs.cc in Sources */,
|
||||
A25D2CBD0CF4C73E0096A262 /* stats.cc in Sources */,
|
||||
A201527E0D1C270F0081714F /* torrent-ctor.cc in Sources */,
|
||||
|
@ -3028,12 +3195,12 @@
|
|||
A2A4E9220DE0F7EB000CE197 /* web.cc in Sources */,
|
||||
A292A6E80DFB45FC004B9C0A /* webseed.cc in Sources */,
|
||||
A25E03E30E4015380086C225 /* tr-getopt.cc in Sources */,
|
||||
E71A5565279C2DD600EBFA1E /* tr-assert.mm in Sources */,
|
||||
C1305EBE186A13B100F03351 /* file.cc in Sources */,
|
||||
A21FBBAC0EDA78C300BC3C51 /* bandwidth.cc in Sources */,
|
||||
C1033E081A3279B800EF44D8 /* crypto-utils-ccrypto.cc in Sources */,
|
||||
A22CFCA80FC24ED80009BD3E /* tr-dht.cc in Sources */,
|
||||
0A6169A70FE5C9A200C66CE6 /* bitfield.cc in Sources */,
|
||||
ED67FB422B70FCE400D8A037 /* settings.cc in Sources */,
|
||||
1BB44E07B1B52E28291B4E32 /* file-piece-map.cc in Sources */,
|
||||
A25964A6106D73A800453B31 /* announcer.cc in Sources */,
|
||||
66F977825E65AD498C028BB0 /* announce-list.cc in Sources */,
|
||||
|
@ -3141,6 +3308,7 @@
|
|||
A209EB9D1142E59A002B02D1 /* InfoPeersViewController.mm in Sources */,
|
||||
A209EBCE1142F2B4002B02D1 /* InfoFileViewController.mm in Sources */,
|
||||
A209EBF91142FEEE002B02D1 /* InfoOptionsViewController.mm in Sources */,
|
||||
ED9862972B979AA2002F3035 /* Utils.mm in Sources */,
|
||||
A21F15AC11729A8B00CF5A9C /* AddMagnetWindowController.mm in Sources */,
|
||||
A2661D6112D0E8D9004F69D5 /* FilterBarView.mm in Sources */,
|
||||
A2F7CF5F13035FFD0016FF10 /* URLSheetWindowController.mm in Sources */,
|
||||
|
@ -3149,6 +3317,7 @@
|
|||
A2B5B4E91880665E0071A66A /* ShareTorrentFileHelper.mm in Sources */,
|
||||
A22BAE281388040500FB022F /* NSMutableArrayAdditions.mm in Sources */,
|
||||
A2966E8713DAF74C007B52DF /* GlobalOptionsPopoverViewController.mm in Sources */,
|
||||
C843FC8729C8B40800491854 /* VersionComparator.mm in Sources */,
|
||||
A234EA541453563B000F3E97 /* NSImageAdditions.mm in Sources */,
|
||||
A2AB883E16A399A6008FAD50 /* VDKQueue.mm in Sources */,
|
||||
4534164229B0EA8600F544C9 /* SmallTorrentCell.mm in Sources */,
|
||||
|
@ -3201,6 +3370,7 @@
|
|||
C12F19791E1AE3C30005E93F /* upnperrors.c in Sources */,
|
||||
BE11836E0CE160D50002D0F3 /* upnpreplyparse.c in Sources */,
|
||||
C1BF7BA81F2A3CB7008E88A7 /* upnpdev.c in Sources */,
|
||||
C8ED0FB1281C10F100B44472 /* addr_is_reserved.c in Sources */,
|
||||
BE11836F0CE160D50002D0F3 /* upnpcommands.c in Sources */,
|
||||
BE1183700CE160D50002D0F3 /* miniupnpc.c in Sources */,
|
||||
A22B00B3116A9EA4003315FC /* connecthostport.c in Sources */,
|
||||
|
@ -3427,9 +3597,19 @@
|
|||
A26AF1050D2855FC00FF7140 /* ru */,
|
||||
A202FF5D0DDA9275009938FF /* it */,
|
||||
A28393FD10D54A79005C0240 /* de */,
|
||||
A2613F9811B3383200472893 /* pt_PT */,
|
||||
A2613F9811B3383200472893 /* pt-PT */,
|
||||
A263C5671560A4210082A3D1 /* da */,
|
||||
A2A9D11A187DD75200C52A1F /* tr */,
|
||||
C1A6BB462B3E6ACE00C4A151 /* eu */,
|
||||
C1A6BB472B3E6ADA00C4A151 /* he */,
|
||||
C1A6BB482B3E6ADF00C4A151 /* hu */,
|
||||
C1A6BB492B3E6AE800C4A151 /* ja */,
|
||||
C1A6BB4A2B3E6AEE00C4A151 /* pl */,
|
||||
C1A6BB4B2B3E6AF500C4A151 /* pt-BR */,
|
||||
C1A6BB4C2B3E6AFC00C4A151 /* sv */,
|
||||
C1A6BB4D2B3E6B0200C4A151 /* uk */,
|
||||
C1A6BB4E2B3E6B0800C4A151 /* zh-CN */,
|
||||
C1A6BB4F2B3E6B0D00C4A151 /* zh-TW */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3447,6 +3627,16 @@
|
|||
45C688E82875B19000A2EB25 /* ru */,
|
||||
45C688E92875B19200A2EB25 /* es */,
|
||||
45C688EA2875B19500A2EB25 /* tr */,
|
||||
C1A6BB992B3E6DBE00C4A151 /* eu */,
|
||||
C1A6BB9A2B3E6DC000C4A151 /* he */,
|
||||
C1A6BB9B2B3E6DC100C4A151 /* hu */,
|
||||
C1A6BB9C2B3E6DC300C4A151 /* ja */,
|
||||
C1A6BB9D2B3E6DC400C4A151 /* pl */,
|
||||
C1A6BB9E2B3E6DC600C4A151 /* pt-BR */,
|
||||
C1A6BB9F2B3E6DC700C4A151 /* sv */,
|
||||
C1A6BBA02B3E6DC900C4A151 /* uk */,
|
||||
C1A6BBA12B3E6DCA00C4A151 /* zh-CN */,
|
||||
C1A6BBA22B3E6DCC00C4A151 /* zh-TW */,
|
||||
);
|
||||
name = InfoActivityView.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3464,6 +3654,16 @@
|
|||
45C688DF2875B06D00A2EB25 /* ru */,
|
||||
45C688E02875B06F00A2EB25 /* es */,
|
||||
45C688E12875B07300A2EB25 /* tr */,
|
||||
C1A6BB8F2B3E6DA200C4A151 /* eu */,
|
||||
C1A6BB902B3E6DA500C4A151 /* he */,
|
||||
C1A6BB912B3E6DA600C4A151 /* hu */,
|
||||
C1A6BB922B3E6DA700C4A151 /* ja */,
|
||||
C1A6BB932B3E6DA900C4A151 /* pl */,
|
||||
C1A6BB942B3E6DAA00C4A151 /* pt-BR */,
|
||||
C1A6BB952B3E6DAB00C4A151 /* sv */,
|
||||
C1A6BB962B3E6DAD00C4A151 /* uk */,
|
||||
C1A6BB972B3E6DAF00C4A151 /* zh-CN */,
|
||||
C1A6BB982B3E6DB000C4A151 /* zh-TW */,
|
||||
);
|
||||
name = InfoGeneralView.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3481,6 +3681,16 @@
|
|||
45C688F12875B2CA00A2EB25 /* ru */,
|
||||
45C688F22875B2CD00A2EB25 /* es */,
|
||||
45C688F32875B2CF00A2EB25 /* tr */,
|
||||
C1A6BBA32B3E6DDC00C4A151 /* eu */,
|
||||
C1A6BBA42B3E6DDE00C4A151 /* he */,
|
||||
C1A6BBA52B3E6DDF00C4A151 /* hu */,
|
||||
C1A6BBA62B3E6DE000C4A151 /* ja */,
|
||||
C1A6BBA72B3E6DE200C4A151 /* pl */,
|
||||
C1A6BBA82B3E6DE300C4A151 /* pt-BR */,
|
||||
C1A6BBA92B3E6DE500C4A151 /* sv */,
|
||||
C1A6BBAA2B3E6DE600C4A151 /* uk */,
|
||||
C1A6BBAB2B3E6DE800C4A151 /* zh-CN */,
|
||||
C1A6BBAC2B3E6DEA00C4A151 /* zh-TW */,
|
||||
);
|
||||
name = InfoOptionsView.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3498,6 +3708,16 @@
|
|||
45C688D62875AE0500A2EB25 /* tr */,
|
||||
45C688D72875AE0800A2EB25 /* fr */,
|
||||
45C688D82875AE0C00A2EB25 /* nl */,
|
||||
C1A6BB852B3E6D8100C4A151 /* eu */,
|
||||
C1A6BB862B3E6D8300C4A151 /* he */,
|
||||
C1A6BB872B3E6D8400C4A151 /* hu */,
|
||||
C1A6BB882B3E6D8600C4A151 /* ja */,
|
||||
C1A6BB892B3E6D8700C4A151 /* pl */,
|
||||
C1A6BB8A2B3E6D8900C4A151 /* pt-BR */,
|
||||
C1A6BB8B2B3E6D8A00C4A151 /* sv */,
|
||||
C1A6BB8C2B3E6D8B00C4A151 /* uk */,
|
||||
C1A6BB8D2B3E6D8D00C4A151 /* zh-CN */,
|
||||
C1A6BB8E2B3E6D8F00C4A151 /* zh-TW */,
|
||||
);
|
||||
name = GroupRules.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3515,6 +3735,16 @@
|
|||
45C688CD2875AC8D00A2EB25 /* ru */,
|
||||
45C688CE2875AC8F00A2EB25 /* es */,
|
||||
45C688CF2875AC9200A2EB25 /* tr */,
|
||||
C1A6BB7B2B3E6D6200C4A151 /* eu */,
|
||||
C1A6BB7C2B3E6D6400C4A151 /* he */,
|
||||
C1A6BB7D2B3E6D6500C4A151 /* hu */,
|
||||
C1A6BB7E2B3E6D6700C4A151 /* ja */,
|
||||
C1A6BB7F2B3E6D6800C4A151 /* pl */,
|
||||
C1A6BB802B3E6D6A00C4A151 /* pt-BR */,
|
||||
C1A6BB812B3E6D6B00C4A151 /* sv */,
|
||||
C1A6BB822B3E6D6D00C4A151 /* uk */,
|
||||
C1A6BB832B3E6D6F00C4A151 /* zh-CN */,
|
||||
C1A6BB842B3E6D7100C4A151 /* zh-TW */,
|
||||
);
|
||||
name = AddMagnetWindow.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3532,6 +3762,16 @@
|
|||
45C688FA2875B40300A2EB25 /* ru */,
|
||||
45C688FB2875B40600A2EB25 /* es */,
|
||||
45C688FC2875B40900A2EB25 /* tr */,
|
||||
C1A6BBAD2B3E6E0900C4A151 /* eu */,
|
||||
C1A6BBAE2B3E6E0B00C4A151 /* he */,
|
||||
C1A6BBAF2B3E6E0C00C4A151 /* hu */,
|
||||
C1A6BBB02B3E6E0E00C4A151 /* ja */,
|
||||
C1A6BBB12B3E6E0F00C4A151 /* pl */,
|
||||
C1A6BBB22B3E6E1000C4A151 /* pt-BR */,
|
||||
C1A6BBB32B3E6E1200C4A151 /* sv */,
|
||||
C1A6BBB42B3E6E1300C4A151 /* uk */,
|
||||
C1A6BBB52B3E6E1600C4A151 /* zh-CN */,
|
||||
C1A6BBB62B3E6E1A00C4A151 /* zh-TW */,
|
||||
);
|
||||
name = GlobalOptionsPopover.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3546,9 +3786,19 @@
|
|||
A202FF5F0DDA9275009938FF /* it */,
|
||||
A291477F0E195A0C00F60CB2 /* en */,
|
||||
A28393FF10D54A96005C0240 /* de */,
|
||||
A2613F9911B3383200472893 /* pt_PT */,
|
||||
A2613F9911B3383200472893 /* pt-PT */,
|
||||
A263C5661560A3CF0082A3D1 /* da */,
|
||||
A2A9D119187DD75100C52A1F /* tr */,
|
||||
C1A6BB3C2B3E69CB00C4A151 /* eu */,
|
||||
C1A6BB3D2B3E6A0700C4A151 /* he */,
|
||||
C1A6BB3E2B3E6A0F00C4A151 /* hu */,
|
||||
C1A6BB3F2B3E6A1800C4A151 /* ja */,
|
||||
C1A6BB402B3E6A2200C4A151 /* pl */,
|
||||
C1A6BB412B3E6A2900C4A151 /* pt-BR */,
|
||||
C1A6BB422B3E6A3000C4A151 /* sv */,
|
||||
C1A6BB432B3E6A3A00C4A151 /* uk */,
|
||||
C1A6BB442B3E6A3F00C4A151 /* zh-CN */,
|
||||
C1A6BB452B3E6A4C00C4A151 /* zh-TW */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3566,6 +3816,16 @@
|
|||
455C093F287767350003A078 /* ru */,
|
||||
455C0940287767380003A078 /* es */,
|
||||
455C09412877673A0003A078 /* tr */,
|
||||
C1A6BB5D2B3E6CD600C4A151 /* eu */,
|
||||
C1A6BB5E2B3E6CD800C4A151 /* he */,
|
||||
C1A6BB5F2B3E6CDA00C4A151 /* hu */,
|
||||
C1A6BB602B3E6CDB00C4A151 /* ja */,
|
||||
C1A6BB612B3E6CDD00C4A151 /* pl */,
|
||||
C1A6BB622B3E6CDF00C4A151 /* pt-BR */,
|
||||
C1A6BB632B3E6CE000C4A151 /* sv */,
|
||||
C1A6BB642B3E6CE200C4A151 /* uk */,
|
||||
C1A6BB652B3E6CE400C4A151 /* zh-CN */,
|
||||
C1A6BB662B3E6CE500C4A151 /* zh-TW */,
|
||||
);
|
||||
name = PrefsWindow.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3583,6 +3843,16 @@
|
|||
457DC1E72873931500ED04C4 /* ru */,
|
||||
457DC1E82873931900ED04C4 /* es */,
|
||||
457DC1E92873931E00ED04C4 /* tr */,
|
||||
C1A6BB532B3E6C6600C4A151 /* eu */,
|
||||
C1A6BB542B3E6C7900C4A151 /* he */,
|
||||
C1A6BB552B3E6C8700C4A151 /* hu */,
|
||||
C1A6BB562B3E6C8D00C4A151 /* ja */,
|
||||
C1A6BB572B3E6C9000C4A151 /* pl */,
|
||||
C1A6BB582B3E6C9300C4A151 /* pt-BR */,
|
||||
C1A6BB592B3E6C9B00C4A151 /* sv */,
|
||||
C1A6BB5A2B3E6C9C00C4A151 /* uk */,
|
||||
C1A6BB5B2B3E6CA100C4A151 /* zh-CN */,
|
||||
C1A6BB5C2B3E6CA300C4A151 /* zh-TW */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3598,8 +3868,18 @@
|
|||
A2FB07F115F8208300933543 /* nl */,
|
||||
A2D8CFBF15FA177A0056E93D /* ru */,
|
||||
A27F4483160B4AF50048CD4C /* fr */,
|
||||
A28B3A2D160E1BC900D4A2BC /* pt_PT */,
|
||||
A28B3A2D160E1BC900D4A2BC /* pt-PT */,
|
||||
A2CA772B187F063A00154956 /* tr */,
|
||||
C1A6BBB72B3E6E2900C4A151 /* eu */,
|
||||
C1A6BBB82B3E6E2B00C4A151 /* he */,
|
||||
C1A6BBB92B3E6E2C00C4A151 /* hu */,
|
||||
C1A6BBBA2B3E6E2E00C4A151 /* ja */,
|
||||
C1A6BBBB2B3E6E2F00C4A151 /* pl */,
|
||||
C1A6BBBC2B3E6E3100C4A151 /* pt-BR */,
|
||||
C1A6BBBD2B3E6E3200C4A151 /* sv */,
|
||||
C1A6BBBE2B3E6E3300C4A151 /* uk */,
|
||||
C1A6BBBF2B3E6E3600C4A151 /* zh-CN */,
|
||||
C1A6BBC02B3E6E3700C4A151 /* zh-TW */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3617,6 +3897,16 @@
|
|||
45C6890328762FFD00A2EB25 /* ru */,
|
||||
45C6890428762FFF00A2EB25 /* es */,
|
||||
45C689052876300200A2EB25 /* tr */,
|
||||
C1A6BB712B3E6D1000C4A151 /* eu */,
|
||||
C1A6BB722B3E6D1200C4A151 /* he */,
|
||||
C1A6BB732B3E6D1400C4A151 /* hu */,
|
||||
C1A6BB742B3E6D1500C4A151 /* ja */,
|
||||
C1A6BB752B3E6D1600C4A151 /* pl */,
|
||||
C1A6BB762B3E6D1800C4A151 /* pt-BR */,
|
||||
C1A6BB772B3E6D1900C4A151 /* sv */,
|
||||
C1A6BB782B3E6D1B00C4A151 /* uk */,
|
||||
C1A6BB792B3E6D1E00C4A151 /* zh-CN */,
|
||||
C1A6BB7A2B3E6D2000C4A151 /* zh-TW */,
|
||||
);
|
||||
name = AddWindow.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3634,6 +3924,16 @@
|
|||
45C6890C28763C3F00A2EB25 /* ru */,
|
||||
45C6890D28763C4300A2EB25 /* es */,
|
||||
45C6890E28763C4600A2EB25 /* tr */,
|
||||
C1A6BB672B3E6CF000C4A151 /* eu */,
|
||||
C1A6BB682B3E6CF200C4A151 /* he */,
|
||||
C1A6BB692B3E6CF300C4A151 /* hu */,
|
||||
C1A6BB6A2B3E6CF500C4A151 /* ja */,
|
||||
C1A6BB6B2B3E6CF600C4A151 /* pl */,
|
||||
C1A6BB6C2B3E6CF800C4A151 /* pt-BR */,
|
||||
C1A6BB6D2B3E6CFA00C4A151 /* sv */,
|
||||
C1A6BB6E2B3E6CFC00C4A151 /* uk */,
|
||||
C1A6BB6F2B3E6D0200C4A151 /* zh-CN */,
|
||||
C1A6BB702B3E6D0300C4A151 /* zh-TW */,
|
||||
);
|
||||
name = Creator.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3667,6 +3967,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"third-party/dht",
|
||||
|
@ -3688,7 +3989,7 @@
|
|||
"-DWIDE_INTEGER_DISABLE_IOSTREAM",
|
||||
"-DRAPIDJSON_HAS_STDSTRING=1",
|
||||
"-DHAVE_FLOCK",
|
||||
"-DHAVE_STRLCPY",
|
||||
"-DWITH_CCRYPTO",
|
||||
);
|
||||
PRODUCT_NAME = transmission;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
|
@ -3875,6 +4176,7 @@
|
|||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
"-DFMT_HEADER_ONLY",
|
||||
"-DWITH_CCRYPTO",
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.m0k.transmission;
|
||||
|
@ -3920,6 +4222,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"third-party/dht",
|
||||
|
@ -3941,7 +4244,7 @@
|
|||
"-DWIDE_INTEGER_DISABLE_IOSTREAM",
|
||||
"-DRAPIDJSON_HAS_STDSTRING=1",
|
||||
"-DHAVE_FLOCK",
|
||||
"-DHAVE_STRLCPY",
|
||||
"-DWITH_CCRYPTO",
|
||||
);
|
||||
PRODUCT_NAME = transmission;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
|
@ -4082,6 +4385,7 @@
|
|||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
"-DFMT_HEADER_ONLY",
|
||||
"-DWITH_CCRYPTO",
|
||||
"-DNDEBUG",
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
|
||||
|
@ -4094,6 +4398,7 @@
|
|||
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;
|
||||
PRODUCT_NAME = dht;
|
||||
|
@ -4104,6 +4409,7 @@
|
|||
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;
|
||||
PRODUCT_NAME = dht;
|
||||
|
@ -4114,6 +4420,7 @@
|
|||
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;
|
||||
PRODUCT_NAME = dht;
|
||||
|
@ -4185,6 +4492,7 @@
|
|||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
"-DFMT_HEADER_ONLY",
|
||||
"-DWITH_CCRYPTO",
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.m0k.transmission;
|
||||
|
@ -4247,6 +4555,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"third-party/dht",
|
||||
|
@ -4268,7 +4577,7 @@
|
|||
"-DWIDE_INTEGER_DISABLE_IOSTREAM",
|
||||
"-DRAPIDJSON_HAS_STDSTRING=1",
|
||||
"-DHAVE_FLOCK",
|
||||
"-DHAVE_STRLCPY",
|
||||
"-DWITH_CCRYPTO",
|
||||
);
|
||||
PRODUCT_NAME = transmission;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = (
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.android.library"
|
||||
|
||||
|
||||
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
|
||||
|
||||
def getExtOrDefault(name) {
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Transmission_" + name]
|
||||
}
|
||||
|
||||
def getExtOrIntegerDefault(name) {
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Transmission_" + name]).toInteger()
|
||||
}
|
||||
|
||||
android {
|
||||
ndkVersion getExtOrDefault("ndkVersion")
|
||||
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
||||
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
abiFilters "arm64-v8a"
|
||||
arguments "-DVCPKG_TARGET_ANDROID=ON", "-DWITH_CRYPTO=openssl"
|
||||
}
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version "3.22.1"
|
||||
path "../CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable "GradleCompatible"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Transmission_kotlinVersion=1.7.0
|
||||
# quotactl is defined from >= 26
|
||||
Transmission_minSdkVersion=26
|
||||
Transmission_targetSdkVersion=31
|
||||
Transmission_compileSdkVersion=31
|
||||
Transmission_ndkVersion=26.1.10909125
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.transmissionbt.transmission">
|
||||
<application android:label="Transmission">
|
||||
</application>
|
||||
</manifest>
|
|
@ -61,8 +61,8 @@ for:
|
|||
choco install python3 --pre
|
||||
choco install nasm
|
||||
choco install jom
|
||||
choco install wixtoolset --version 3.11.2
|
||||
choco install ccache
|
||||
choco install wixtoolset --version 3.14.0
|
||||
choco install ccache --version 4.8.3
|
||||
|
||||
Remove-Item -Path (Join-Path $Env:SystemDrive OpenSSL-Win32) -Recurse
|
||||
Remove-Item -Path (Join-Path $Env:SystemDrive OpenSSL-Win64) -Recurse
|
||||
|
@ -74,7 +74,7 @@ for:
|
|||
$Env:PATH = @(
|
||||
(Join-Path $Env:SystemDrive Strawberry perl bin)
|
||||
(Join-Path $Env:ProgramFiles NASM)
|
||||
(Join-Path ${Env:ProgramFiles(x86)} 'WiX Toolset v3.11' bin)
|
||||
(Join-Path (Get-Item -Path "${Env:ProgramFiles(x86)}\WiX Toolset v3.*")[0].FullName bin)
|
||||
$Env:PATH
|
||||
) -join [System.IO.Path]::PathSeparator
|
||||
|
||||
|
|
306
cli/cli.cc
306
cli/cli.cc
|
@ -30,23 +30,25 @@ using namespace libtransmission::Values;
|
|||
|
||||
#define SPEED_K_STR "kB/s"
|
||||
|
||||
static auto constexpr LineWidth = int{ 80 };
|
||||
namespace
|
||||
{
|
||||
auto constexpr LineWidth = int{ 80 };
|
||||
|
||||
static char constexpr MyConfigName[] = "transmission";
|
||||
static char constexpr MyReadableName[] = "transmission-cli";
|
||||
static char constexpr Usage
|
||||
char constexpr MyConfigName[] = "transmission";
|
||||
char constexpr MyReadableName[] = "transmission-cli";
|
||||
char constexpr Usage
|
||||
[] = "A fast and easy BitTorrent client\n"
|
||||
"\n"
|
||||
"Usage: transmission-cli [options] <file|url|magnet>";
|
||||
|
||||
static bool showVersion = false;
|
||||
static bool verify = false;
|
||||
static sig_atomic_t gotsig = false;
|
||||
static sig_atomic_t manualUpdate = false;
|
||||
bool showVersion = false;
|
||||
bool verify = false;
|
||||
sig_atomic_t gotsig = false;
|
||||
sig_atomic_t manualUpdate = false;
|
||||
|
||||
static char const* torrentPath = nullptr;
|
||||
char const* torrentPath = nullptr;
|
||||
|
||||
static auto constexpr Options = std::array<tr_option, 20>{
|
||||
auto constexpr Options = std::array<tr_option, 20>{
|
||||
{ { 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
|
||||
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
|
||||
{ 'd', "downlimit", "Set max download speed in " SPEED_K_STR, "d", true, "<speed>" },
|
||||
|
@ -76,11 +78,11 @@ static auto constexpr Options = std::array<tr_option, 20>{
|
|||
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
|
||||
};
|
||||
|
||||
static int parseCommandLine(tr_variant*, int argc, char const** argv);
|
||||
int parseCommandLine(tr_variant*, int argc, char const** argv);
|
||||
|
||||
static void sigHandler(int signal);
|
||||
void sigHandler(int signal);
|
||||
|
||||
static std::string tr_strlratio(double ratio)
|
||||
[[nodiscard]] std::string tr_strlratio(double ratio)
|
||||
{
|
||||
if (static_cast<int>(ratio) == TR_RATIO_NA)
|
||||
{
|
||||
|
@ -94,27 +96,27 @@ static std::string tr_strlratio(double ratio)
|
|||
|
||||
if (ratio < 10.0)
|
||||
{
|
||||
return fmt::format(FMT_STRING("{:.2f}"), ratio);
|
||||
return fmt::format("{:.2f}", ratio);
|
||||
}
|
||||
|
||||
if (ratio < 100.0)
|
||||
{
|
||||
return fmt::format(FMT_STRING("{:.1f}"), ratio);
|
||||
return fmt::format("{:.1f}", ratio);
|
||||
}
|
||||
|
||||
return fmt::format(FMT_STRING("{:.0f}"), ratio);
|
||||
return fmt::format("{:.0f}", ratio);
|
||||
}
|
||||
|
||||
static bool waitingOnWeb;
|
||||
bool waitingOnWeb;
|
||||
|
||||
static void onTorrentFileDownloaded(tr_web::FetchResponse const& response)
|
||||
void onTorrentFileDownloaded(tr_web::FetchResponse const& response)
|
||||
{
|
||||
auto* ctor = static_cast<tr_ctor*>(response.user_data);
|
||||
tr_ctorSetMetainfo(ctor, std::data(response.body), std::size(response.body), nullptr);
|
||||
waitingOnWeb = false;
|
||||
}
|
||||
|
||||
static std::string getStatusStr(tr_stat const* st)
|
||||
[[nodiscard]] std::string getStatusStr(tr_stat const* st)
|
||||
{
|
||||
if (st->activity == TR_STATUS_CHECK_WAIT)
|
||||
{
|
||||
|
@ -124,7 +126,7 @@ static std::string getStatusStr(tr_stat const* st)
|
|||
if (st->activity == TR_STATUS_CHECK)
|
||||
{
|
||||
return fmt::format(
|
||||
FMT_STRING("Verifying local files ({:.2f}%, {:.2f}% valid)"),
|
||||
"Verifying local files ({:.2f}%, {:.2f}% valid)",
|
||||
tr_truncd(100 * st->recheckProgress, 2),
|
||||
tr_truncd(100 * st->percentDone, 2));
|
||||
}
|
||||
|
@ -132,7 +134,7 @@ static std::string getStatusStr(tr_stat const* st)
|
|||
if (st->activity == TR_STATUS_DOWNLOAD)
|
||||
{
|
||||
return fmt::format(
|
||||
FMT_STRING("Progress: {:.1f}%, dl from {:d} of {:d} peers ({:s}), ul to {:d} ({:s}) [{:s}]"),
|
||||
"Progress: {:.1f}%, dl from {:d} of {:d} peers ({:s}), ul to {:d} ({:s}) [{:s}]",
|
||||
tr_truncd(100 * st->percentDone, 1),
|
||||
st->peersSendingToUs,
|
||||
st->peersConnected,
|
||||
|
@ -145,7 +147,7 @@ static std::string getStatusStr(tr_stat const* st)
|
|||
if (st->activity == TR_STATUS_SEED)
|
||||
{
|
||||
return fmt::format(
|
||||
FMT_STRING("Seeding, uploading to {:d} of {:d} peer(s), {:s} [{:s}]"),
|
||||
"Seeding, uploading to {:d} of {:d} peer(s), {:s} [{:s}]",
|
||||
st->peersGettingFromUs,
|
||||
st->peersConnected,
|
||||
Speed{ st->pieceUploadSpeed_KBps, Speed::Units::KByps }.to_string(),
|
||||
|
@ -155,7 +157,7 @@ static std::string getStatusStr(tr_stat const* st)
|
|||
return "";
|
||||
}
|
||||
|
||||
static std::string getConfigDir(int argc, char const** argv)
|
||||
[[nodiscard]] std::string getConfigDir(int argc, char const** argv)
|
||||
{
|
||||
int c;
|
||||
char const* my_optarg;
|
||||
|
@ -175,6 +177,133 @@ static std::string getConfigDir(int argc, char const** argv)
|
|||
return tr_getDefaultConfigDir(MyConfigName);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
int parseCommandLine(tr_variant* d, int argc, char const** argv)
|
||||
{
|
||||
int c;
|
||||
char const* my_optarg;
|
||||
|
||||
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &my_optarg)) != TR_OPT_DONE)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'b':
|
||||
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, true);
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, false);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, atoi(my_optarg));
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, true);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
tr_variantDictAddStr(d, TR_KEY_script_torrent_done_filename, my_optarg);
|
||||
tr_variantDictAddBool(d, TR_KEY_script_torrent_done_enabled, true);
|
||||
break;
|
||||
|
||||
case 'g': /* handled above */
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, true);
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, false);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
tr_variantDictAddInt(d, TR_KEY_peer_port, atoi(my_optarg));
|
||||
break;
|
||||
|
||||
case 't':
|
||||
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, my_optarg);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_up, atoi(my_optarg));
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, true);
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, false);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verify = true;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
showVersion = true;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
tr_variantDictAddStr(d, TR_KEY_download_dir, my_optarg);
|
||||
break;
|
||||
|
||||
case 910:
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_REQUIRED);
|
||||
break;
|
||||
|
||||
case 911:
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_PREFERRED);
|
||||
break;
|
||||
|
||||
case 912:
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_CLEAR_PREFERRED);
|
||||
break;
|
||||
|
||||
case 500:
|
||||
tr_variantDictAddBool(d, TR_KEY_sequentialDownload, true);
|
||||
break;
|
||||
|
||||
case TR_OPT_UNK:
|
||||
if (torrentPath == nullptr)
|
||||
{
|
||||
torrentPath = my_optarg;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sigHandler(int signal)
|
||||
{
|
||||
switch (signal)
|
||||
{
|
||||
case SIGINT:
|
||||
gotsig = true;
|
||||
break;
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
case SIGHUP:
|
||||
manualUpdate = true;
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int tr_main(int argc, char* argv[])
|
||||
{
|
||||
auto const init_mgr = tr_lib_init();
|
||||
|
@ -192,7 +321,7 @@ int tr_main(int argc, char* argv[])
|
|||
|
||||
/* load the defaults from config file + libtransmission defaults */
|
||||
auto const config_dir = getConfigDir(argc, (char const**)argv);
|
||||
auto settings = tr_sessionLoadSettings(config_dir.c_str(), MyConfigName);
|
||||
auto settings = tr_sessionLoadSettings(nullptr, config_dir.c_str(), MyConfigName);
|
||||
|
||||
/* the command line overrides defaults */
|
||||
if (parseCommandLine(&settings, argc, (char const**)argv) != 0)
|
||||
|
@ -335,132 +464,3 @@ int tr_main(int argc, char* argv[])
|
|||
tr_sessionClose(h);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
****
|
||||
***/
|
||||
|
||||
static int parseCommandLine(tr_variant* d, int argc, char const** argv)
|
||||
{
|
||||
int c;
|
||||
char const* my_optarg;
|
||||
|
||||
while ((c = tr_getopt(Usage, argc, argv, std::data(Options), &my_optarg)) != TR_OPT_DONE)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'b':
|
||||
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, true);
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
tr_variantDictAddBool(d, TR_KEY_blocklist_enabled, false);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_down, atoi(my_optarg));
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, true);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_down_enabled, false);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
tr_variantDictAddStr(d, TR_KEY_script_torrent_done_filename, my_optarg);
|
||||
tr_variantDictAddBool(d, TR_KEY_script_torrent_done_enabled, true);
|
||||
break;
|
||||
|
||||
case 'g': /* handled above */
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, true);
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, false);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
tr_variantDictAddInt(d, TR_KEY_peer_port, atoi(my_optarg));
|
||||
break;
|
||||
|
||||
case 't':
|
||||
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, my_optarg);
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
tr_variantDictAddInt(d, TR_KEY_speed_limit_up, atoi(my_optarg));
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, true);
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, false);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verify = true;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
showVersion = true;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
tr_variantDictAddStr(d, TR_KEY_download_dir, my_optarg);
|
||||
break;
|
||||
|
||||
case 910:
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_REQUIRED);
|
||||
break;
|
||||
|
||||
case 911:
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_ENCRYPTION_PREFERRED);
|
||||
break;
|
||||
|
||||
case 912:
|
||||
tr_variantDictAddInt(d, TR_KEY_encryption, TR_CLEAR_PREFERRED);
|
||||
break;
|
||||
|
||||
case 500:
|
||||
tr_variantDictAddBool(d, TR_KEY_sequentialDownload, true);
|
||||
break;
|
||||
|
||||
case TR_OPT_UNK:
|
||||
if (torrentPath == nullptr)
|
||||
{
|
||||
torrentPath = my_optarg;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sigHandler(int signal)
|
||||
{
|
||||
switch (signal)
|
||||
{
|
||||
case SIGINT:
|
||||
gotsig = true;
|
||||
break;
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
case SIGHUP:
|
||||
manualUpdate = true;
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,6 +159,7 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME)
|
|||
set(${ID}_LIBRARIES ${${ID}_LIBRARY})
|
||||
|
||||
set(${ID}_EXT_PROJ_CMAKE_ARGS)
|
||||
|
||||
if(APPLE)
|
||||
string(REPLACE ";" "$<SEMICOLON>" ${ID}_CMAKE_OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}")
|
||||
list(APPEND ${ID}_EXT_PROJ_CMAKE_ARGS
|
||||
|
@ -167,6 +168,21 @@ macro(tr_add_external_auto_library ID DIRNAME LIBNAME)
|
|||
"-DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT}")
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
list(APPEND ${ID}_EXT_PROJ_CMAKE_ARGS
|
||||
"-DANDROID_PLATFORM=${ANDROID_PLATFORM}"
|
||||
"-DANDROID_NDK=${ANDROID_NDK}"
|
||||
"-DANDROID_ABI=${ANDROID_ABI}"
|
||||
"-DANDROID_STL=${ANDROID_STL}"
|
||||
"-DCMAKE_ANDROID_NDK=${CMAKE_ANDROID_NDK}"
|
||||
"-DCMAKE_ANDROID_ARCH_ABI=${CMAKE_ANDROID_ARCH_ABI}")
|
||||
endif()
|
||||
|
||||
if(VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
|
||||
list(APPEND ${ID}_EXT_PROJ_CMAKE_ARGS
|
||||
"-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
${${ID}_UPSTREAM_TARGET}
|
||||
PREFIX "${TR_THIRD_PARTY_BINARY_DIR}/${DIRNAME}.bld"
|
||||
|
|
|
@ -32,7 +32,7 @@ BEGIN
|
|||
VALUE "FileDescription", "${TR_FILE_DESCRIPTION}"
|
||||
VALUE "FileVersion", LONG_VERSION_STRING
|
||||
VALUE "InternalName", "${TR_INTERNAL_NAME}"
|
||||
VALUE "LegalCopyright", "2005-2023 Transmission Project"
|
||||
VALUE "LegalCopyright", "2005-2024 Transmission Project"
|
||||
VALUE "OriginalFilename", "${TR_ORIGINAL_FILENAME}"
|
||||
VALUE "ProductName", "Transmission"
|
||||
VALUE "ProductVersion", LONG_VERSION_STRING
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
# Copied from https://github.com/microsoft/vcpkg-docs/blob/main/vcpkg/examples/vcpkg_android_example_cmake_script/cmake/vcpkg_android.cmake
|
||||
#
|
||||
# vcpkg_android.cmake
|
||||
#
|
||||
# Helper script when using vcpkg with cmake. It should be triggered via the variable VCPKG_TARGET_ANDROID
|
||||
#
|
||||
# For example:
|
||||
# if (VCPKG_TARGET_ANDROID)
|
||||
# include("cmake/vcpkg_android.cmake")
|
||||
# endif()
|
||||
#
|
||||
# This script will:
|
||||
# 1 & 2. check the presence of needed env variables: ANDROID_NDK_HOME and VCPKG_ROOT
|
||||
# 3. set VCPKG_TARGET_TRIPLET according to ANDROID_ABI
|
||||
# 4. Combine vcpkg and Android toolchains by setting CMAKE_TOOLCHAIN_FILE
|
||||
# and VCPKG_CHAINLOAD_TOOLCHAIN_FILE
|
||||
|
||||
# Note: VCPKG_TARGET_ANDROID is not an official vcpkg variable.
|
||||
# it is introduced for the need of this script
|
||||
|
||||
if (VCPKG_TARGET_ANDROID)
|
||||
|
||||
#
|
||||
# 1. Check the presence of environment variable ANDROID_NDK_HOME
|
||||
#
|
||||
if (NOT DEFINED ENV{ANDROID_NDK_HOME})
|
||||
message(FATAL_ERROR "
|
||||
Please set an environment variable ANDROID_NDK_HOME
|
||||
For example:
|
||||
export ANDROID_NDK_HOME=/home/your-account/Android/Sdk/ndk-bundle
|
||||
Or:
|
||||
export ANDROID_NDK_HOME=/home/your-account/Android/android-ndk-r21b
|
||||
")
|
||||
endif()
|
||||
|
||||
#
|
||||
# 2. Check the presence of environment variable VCPKG_ROOT
|
||||
#
|
||||
if (NOT DEFINED ENV{VCPKG_ROOT})
|
||||
message(FATAL_ERROR "
|
||||
Please set an environment variable VCPKG_ROOT
|
||||
For example:
|
||||
export VCPKG_ROOT=/path/to/vcpkg
|
||||
")
|
||||
endif()
|
||||
|
||||
|
||||
#
|
||||
# 3. Set VCPKG_TARGET_TRIPLET according to ANDROID_ABI
|
||||
#
|
||||
# There are four different Android ABI, each of which maps to
|
||||
# a vcpkg triplet. The following table outlines the mapping from vcpkg architectures to android architectures
|
||||
#
|
||||
# |VCPKG_TARGET_TRIPLET | ANDROID_ABI |
|
||||
# |---------------------------|----------------------|
|
||||
# |arm64-android | arm64-v8a |
|
||||
# |arm-android | armeabi-v7a |
|
||||
# |x64-android | x86_64 |
|
||||
# |x86-android | x86 |
|
||||
#
|
||||
# The variable must be stored in the cache in order to successfully the two toolchains.
|
||||
#
|
||||
if (ANDROID_ABI MATCHES "arm64-v8a")
|
||||
set(VCPKG_TARGET_TRIPLET "arm64-android" CACHE STRING "" FORCE)
|
||||
elseif(ANDROID_ABI MATCHES "armeabi-v7a")
|
||||
set(VCPKG_TARGET_TRIPLET "arm-android" CACHE STRING "" FORCE)
|
||||
elseif(ANDROID_ABI MATCHES "x86_64")
|
||||
set(VCPKG_TARGET_TRIPLET "x64-android" CACHE STRING "" FORCE)
|
||||
elseif(ANDROID_ABI MATCHES "x86")
|
||||
set(VCPKG_TARGET_TRIPLET "x86-android" CACHE STRING "" FORCE)
|
||||
else()
|
||||
message(FATAL_ERROR "
|
||||
Please specify ANDROID_ABI
|
||||
For example
|
||||
cmake ... -DANDROID_ABI=armeabi-v7a
|
||||
|
||||
Possible ABIs are: arm64-v8a, armeabi-v7a, x64-android, x86-android
|
||||
")
|
||||
endif()
|
||||
message("vcpkg_android.cmake: VCPKG_TARGET_TRIPLET was set to ${VCPKG_TARGET_TRIPLET}")
|
||||
|
||||
|
||||
#
|
||||
# 4. Combine vcpkg and Android toolchains
|
||||
#
|
||||
|
||||
# vcpkg and android both provide dedicated toolchains:
|
||||
#
|
||||
# vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
|
||||
# android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
|
||||
#
|
||||
# When using vcpkg, the vcpkg toolchain shall be specified first.
|
||||
# However, vcpkg provides a way to preload and additional toolchain,
|
||||
# with the VCPKG_CHAINLOAD_TOOLCHAIN_FILE option.
|
||||
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE $ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake)
|
||||
set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
|
||||
message("vcpkg_android.cmake: CMAKE_TOOLCHAIN_FILE was set to ${CMAKE_TOOLCHAIN_FILE}")
|
||||
message("vcpkg_android.cmake: VCPKG_CHAINLOAD_TOOLCHAIN_FILE was set to ${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}")
|
||||
|
||||
endif(VCPKG_TARGET_ANDROID)
|
|
@ -40,10 +40,10 @@ find_cfiles() {
|
|||
! \( $(get_find_path_args $(trim_comments .clang-format-ignore)) \) "$@"
|
||||
}
|
||||
|
||||
# We're targeting clang-format version 15 and other versions give slightly
|
||||
# different results, so prefer `clang-format-15` if it's installed.
|
||||
# We're targeting clang-format version 17 and other versions give slightly
|
||||
# different results, so prefer `clang-format-17` if it's installed.
|
||||
clang_format_exe_names=(
|
||||
'clang-format-15'
|
||||
'clang-format-17'
|
||||
'clang-format'
|
||||
)
|
||||
for name in ${clang_format_exe_names[@]}; do
|
||||
|
|
|
@ -52,4 +52,10 @@ foreach(P daemon)
|
|||
FILES ${TR_NAME}-${P}.1
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
endif()
|
||||
|
||||
if (WITH_SYSTEMD)
|
||||
install(
|
||||
FILES ${TR_NAME}-${P}.service
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/systemd/system)
|
||||
endif()
|
||||
endforeach()
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
static void set_system_error(tr_error& error, int code, std::string_view message)
|
||||
{
|
||||
error.set(code, fmt::format(FMT_STRING("{:s}: {:s} ({:d}"), message, tr_strerror(code), code));
|
||||
error.set(code, fmt::format("{:s}: {:s} ({:d}", message, tr_strerror(code), code));
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_SIGNALFD_H
|
||||
|
|
|
@ -23,27 +23,29 @@
|
|||
#define SERVICE_CONTROL_PRESHUTDOWN 0x0000000F
|
||||
#endif
|
||||
|
||||
static LPCWSTR const service_name = L"TransmissionDaemon";
|
||||
namespace
|
||||
{
|
||||
LPCWSTR constexpr service_name = L"TransmissionDaemon";
|
||||
|
||||
// If we can get rid of this global variable...
|
||||
static tr_daemon* daemon;
|
||||
|
||||
// ...these becomes a good candidates for being converted to 'class tr_daemon' members.
|
||||
static SERVICE_STATUS_HANDLE status_handle = nullptr;
|
||||
static DWORD current_state = SERVICE_STOPPED;
|
||||
static HANDLE service_thread = nullptr;
|
||||
static HANDLE service_stop_thread = nullptr;
|
||||
SERVICE_STATUS_HANDLE status_handle = nullptr;
|
||||
DWORD current_state = SERVICE_STOPPED;
|
||||
HANDLE service_thread = nullptr;
|
||||
HANDLE service_stop_thread = nullptr;
|
||||
|
||||
static void set_system_error(tr_error& error, DWORD code, char const* message)
|
||||
void set_system_error(tr_error& error, DWORD code, char const* message)
|
||||
{
|
||||
auto const system_message = tr_win32_format_message(code);
|
||||
error.set(code, fmt::format(FMT_STRING("{:s} ({:#08x}): {:s})"), message, code, system_message));
|
||||
error.set(code, fmt::format("{:s} ({:#08x}): {:s})", message, code, system_message));
|
||||
}
|
||||
|
||||
static void do_log_system_error(char const* file, int line, tr_log_level level, DWORD code, char const* message)
|
||||
void do_log_system_error(char const* file, int line, tr_log_level level, DWORD code, char const* message)
|
||||
{
|
||||
auto const system_message = tr_win32_format_message(code);
|
||||
tr_logAddMessage(file, line, level, "tr_daemon", fmt::format("[tr_daemon] {} ({:#x}): {}", message, code, system_message));
|
||||
tr_logAddMessage(file, line, level, fmt::format("{} ({:#x}): {}", message, code, system_message), "tr_daemon");
|
||||
}
|
||||
|
||||
#define log_system_error(level, code, message) \
|
||||
|
@ -57,13 +59,13 @@ static void do_log_system_error(char const* file, int line, tr_log_level level,
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
static BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/)
|
||||
BOOL WINAPI handle_console_ctrl(DWORD /*control_type*/)
|
||||
{
|
||||
daemon->stop();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void update_service_status(
|
||||
void update_service_status(
|
||||
DWORD new_state,
|
||||
DWORD win32_exit_code,
|
||||
DWORD service_specific_exit_code,
|
||||
|
@ -91,7 +93,7 @@ static void update_service_status(
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int __stdcall service_stop_thread_main(void* param)
|
||||
unsigned int __stdcall service_stop_thread_main(void* param)
|
||||
{
|
||||
daemon->stop();
|
||||
|
||||
|
@ -107,7 +109,7 @@ static unsigned int __stdcall service_stop_thread_main(void* param)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void stop_service(void)
|
||||
void stop_service(void)
|
||||
{
|
||||
if (service_stop_thread != nullptr)
|
||||
{
|
||||
|
@ -128,7 +130,7 @@ static void stop_service(void)
|
|||
}
|
||||
}
|
||||
|
||||
static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/, LPVOID /*event_data*/, LPVOID /*context*/)
|
||||
DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/, LPVOID /*event_data*/, LPVOID /*context*/)
|
||||
{
|
||||
switch (control_code)
|
||||
{
|
||||
|
@ -150,12 +152,12 @@ static DWORD WINAPI handle_service_ctrl(DWORD control_code, DWORD /*event_type*/
|
|||
return ERROR_CALL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static unsigned int __stdcall service_thread_main(void* /*context*/)
|
||||
unsigned int __stdcall service_thread_main(void* /*context*/)
|
||||
{
|
||||
return daemon->start(false);
|
||||
}
|
||||
|
||||
static VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
|
||||
VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
|
||||
{
|
||||
status_handle = RegisterServiceCtrlHandlerExW(service_name, &handle_service_ctrl, nullptr);
|
||||
|
||||
|
@ -199,6 +201,7 @@ static VOID WINAPI service_main(DWORD /*argc*/, LPWSTR* /*argv*/)
|
|||
|
||||
update_service_status(SERVICE_STOPPED, NO_ERROR, exit_code, 0, 0);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool tr_daemon::setup_signals([[maybe_unused]] struct event*& sig_ev)
|
||||
{
|
||||
|
|
251
daemon/daemon.cc
251
daemon/daemon.cc
|
@ -6,7 +6,6 @@
|
|||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstdio> /* printf */
|
||||
#include <cstdlib> /* atoi */
|
||||
#include <iostream>
|
||||
#include <iterator> /* std::back_inserter */
|
||||
#include <memory>
|
||||
|
@ -63,21 +62,21 @@ struct tr_torrent;
|
|||
using namespace std::literals;
|
||||
using libtransmission::Watchdir;
|
||||
|
||||
static char constexpr MyName[] = "transmission-daemon";
|
||||
static char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
|
||||
" https://transmissionbt.com/\n"
|
||||
"A fast and easy BitTorrent client\n"
|
||||
"\n"
|
||||
"transmission-daemon is a headless Transmission session that can be\n"
|
||||
"controlled via transmission-qt, transmission-remote, or its web interface.\n"
|
||||
"\n"
|
||||
"Usage: transmission-daemon [options]";
|
||||
namespace
|
||||
{
|
||||
char constexpr MyName[] = "transmission-daemon";
|
||||
char constexpr Usage[] = "Transmission " LONG_VERSION_STRING
|
||||
" https://transmissionbt.com/\n"
|
||||
"A fast and easy BitTorrent client\n"
|
||||
"\n"
|
||||
"transmission-daemon is a headless Transmission session that can be\n"
|
||||
"controlled via transmission-qt, transmission-remote, or its web interface.\n"
|
||||
"\n"
|
||||
"Usage: transmission-daemon [options]";
|
||||
|
||||
/***
|
||||
**** Config File
|
||||
***/
|
||||
// --- Config File
|
||||
|
||||
static auto constexpr Options = std::array<tr_option, 45>{
|
||||
auto constexpr Options = std::array<tr_option, 45>{
|
||||
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
|
||||
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
|
||||
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
|
||||
|
@ -145,39 +144,7 @@ static auto constexpr Options = std::array<tr_option, 45>{
|
|||
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
|
||||
};
|
||||
|
||||
bool tr_daemon::reopen_log_file(char const* filename)
|
||||
{
|
||||
auto error = tr_error{};
|
||||
tr_sys_file_t const old_log_file = logfile_;
|
||||
tr_sys_file_t const new_log_file = tr_sys_file_open(
|
||||
filename,
|
||||
TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_APPEND,
|
||||
0666,
|
||||
&error);
|
||||
|
||||
if (new_log_file == TR_BAD_SYS_FILE)
|
||||
{
|
||||
auto const errmsg = fmt::format(
|
||||
"Couldn't open '{path}': {error} ({error_code})",
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error.message()),
|
||||
fmt::arg("error_code", error.code()));
|
||||
fmt::print(stderr, "{:s}\n", errmsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
logfile_ = new_log_file;
|
||||
logfile_flush_ = tr_sys_file_flush_possible(logfile_);
|
||||
|
||||
if (old_log_file != TR_BAD_SYS_FILE)
|
||||
{
|
||||
tr_sys_file_close(old_log_file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string getConfigDir(int argc, char const* const* argv)
|
||||
[[nodiscard]] std::string getConfigDir(int argc, char const* const* argv)
|
||||
{
|
||||
int c;
|
||||
char const* optstr;
|
||||
|
@ -196,7 +163,7 @@ static std::string getConfigDir(int argc, char const* const* argv)
|
|||
return tr_getDefaultConfigDir(MyName);
|
||||
}
|
||||
|
||||
static auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view basename)
|
||||
auto onFileAdded(tr_session* session, std::string_view dirname, std::string_view basename)
|
||||
{
|
||||
auto const lowercase = tr_strlower(basename);
|
||||
auto const is_torrent = tr_strv_ends_with(lowercase, ".torrent"sv);
|
||||
|
@ -281,7 +248,7 @@ static auto onFileAdded(tr_session* session, std::string_view dirname, std::stri
|
|||
return Watchdir::Action::Done;
|
||||
}
|
||||
|
||||
static char const* levelName(tr_log_level level)
|
||||
[[nodiscard]] constexpr char const* levelName(tr_log_level level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
|
@ -300,8 +267,8 @@ static char const* levelName(tr_log_level level)
|
|||
}
|
||||
}
|
||||
|
||||
static void printMessage(
|
||||
tr_sys_file_t file,
|
||||
void printMessage(
|
||||
FILE* ostream,
|
||||
tr_log_level level,
|
||||
std::string_view name,
|
||||
std::string_view message,
|
||||
|
@ -319,11 +286,11 @@ static void printMessage(
|
|||
fmt::format_to(std::back_inserter(out), "{:s} {:s} ({:s}:{:d})", name, message, filename, line);
|
||||
}
|
||||
|
||||
if (file != TR_BAD_SYS_FILE)
|
||||
if (ostream != nullptr)
|
||||
{
|
||||
auto timestr = std::array<char, 64>{};
|
||||
tr_logGetTimeStr(std::data(timestr), std::size(timestr));
|
||||
tr_sys_file_write_line(file, fmt::format("[{:s}] {:s} {:s}", std::data(timestr), levelName(level), std::data(out)));
|
||||
fmt::print(ostream, "[{:s}] {:s} {:s}\n", std::data(timestr), levelName(level), out.c_str());
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
|
@ -362,24 +329,81 @@ static void printMessage(
|
|||
#endif
|
||||
}
|
||||
|
||||
static void pumpLogMessages(tr_sys_file_t file, bool flush)
|
||||
void pumpLogMessages(FILE* log_stream)
|
||||
{
|
||||
tr_log_message* list = tr_logGetQueue();
|
||||
|
||||
for (tr_log_message const* l = list; l != nullptr; l = l->next)
|
||||
{
|
||||
printMessage(file, l->level, l->name, l->message, l->file, l->line);
|
||||
printMessage(log_stream, l->level, l->name, l->message, l->file, l->line);
|
||||
}
|
||||
|
||||
if (flush && file != TR_BAD_SYS_FILE)
|
||||
// two reasons to not flush stderr:
|
||||
// 1. it's usually redundant, since stderr flushes itself
|
||||
// 2. when running as a systemd unit, it's redirected to a socket
|
||||
if (log_stream != stderr)
|
||||
{
|
||||
tr_sys_file_flush(file);
|
||||
fflush(log_stream);
|
||||
}
|
||||
|
||||
tr_logFreeQueue(list);
|
||||
}
|
||||
|
||||
void tr_daemon::report_status(void)
|
||||
void periodic_update(evutil_socket_t /*fd*/, short /*what*/, void* arg)
|
||||
{
|
||||
static_cast<tr_daemon*>(arg)->periodic_update();
|
||||
}
|
||||
|
||||
tr_rpc_callback_status on_rpc_callback(tr_session* /*session*/, tr_rpc_callback_type type, tr_torrent* /*tor*/, void* arg)
|
||||
{
|
||||
if (type == TR_RPC_SESSION_CLOSE)
|
||||
{
|
||||
static_cast<tr_daemon*>(arg)->stop();
|
||||
}
|
||||
return TR_RPC_OK;
|
||||
}
|
||||
|
||||
tr_variant load_settings(char const* config_dir)
|
||||
{
|
||||
auto app_defaults = tr_variant::make_map();
|
||||
tr_variantDictAddStr(&app_defaults, TR_KEY_watch_dir, ""sv);
|
||||
tr_variantDictAddBool(&app_defaults, TR_KEY_watch_dir_enabled, false);
|
||||
tr_variantDictAddBool(&app_defaults, TR_KEY_watch_dir_force_generic, false);
|
||||
tr_variantDictAddBool(&app_defaults, TR_KEY_rpc_enabled, true);
|
||||
tr_variantDictAddBool(&app_defaults, TR_KEY_start_paused, false);
|
||||
return tr_sessionLoadSettings(&app_defaults, config_dir, MyName);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool tr_daemon::reopen_log_file(char const* filename)
|
||||
{
|
||||
auto* const old_stream = log_stream_;
|
||||
|
||||
auto* new_stream = std::fopen(filename, "a");
|
||||
if (new_stream == nullptr)
|
||||
{
|
||||
auto const err = errno;
|
||||
auto const errmsg = fmt::format(
|
||||
"Couldn't open '{path}': {error} ({error_code})",
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", tr_strerror(err)),
|
||||
fmt::arg("error_code", err));
|
||||
fmt::print(stderr, "{:s}\n", errmsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_stream_ = new_stream;
|
||||
|
||||
if (old_stream != nullptr && old_stream != stderr)
|
||||
{
|
||||
fclose(old_stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tr_daemon::report_status()
|
||||
{
|
||||
double const up = tr_sessionGetRawSpeed_KBps(my_session_, TR_UP);
|
||||
double const dn = tr_sessionGetRawSpeed_KBps(my_session_, TR_DOWN);
|
||||
|
@ -394,36 +418,17 @@ void tr_daemon::report_status(void)
|
|||
}
|
||||
}
|
||||
|
||||
void tr_daemon::periodic_update(void)
|
||||
void tr_daemon::periodic_update()
|
||||
{
|
||||
pumpLogMessages(logfile_, logfile_flush_);
|
||||
pumpLogMessages(log_stream_);
|
||||
report_status();
|
||||
}
|
||||
|
||||
static void periodic_update(evutil_socket_t /*fd*/, short /*what*/, void* arg)
|
||||
{
|
||||
static_cast<tr_daemon*>(arg)->periodic_update();
|
||||
}
|
||||
|
||||
static tr_rpc_callback_status on_rpc_callback(
|
||||
tr_session* /*session*/,
|
||||
tr_rpc_callback_type type,
|
||||
tr_torrent* /*tor*/,
|
||||
void* arg)
|
||||
{
|
||||
if (type == TR_RPC_SESSION_CLOSE)
|
||||
{
|
||||
static_cast<tr_daemon*>(arg)->stop();
|
||||
}
|
||||
return TR_RPC_OK;
|
||||
}
|
||||
|
||||
bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_settings, bool* foreground, int* exit_code)
|
||||
{
|
||||
int c;
|
||||
char const* optstr;
|
||||
|
||||
paused_ = false;
|
||||
*dump_settings = false;
|
||||
*foreground = false;
|
||||
|
||||
|
@ -501,7 +506,10 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
break;
|
||||
|
||||
case 'p':
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_rpc_port, atoi(optstr));
|
||||
if (auto const rpc_port = tr_num_parse<uint16_t>(optstr); rpc_port)
|
||||
{
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_rpc_port, *rpc_port);
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
|
@ -525,7 +533,10 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
break;
|
||||
|
||||
case 'P':
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_peer_port, atoi(optstr));
|
||||
if (auto const peer_port = tr_num_parse<uint16_t>(optstr); peer_port)
|
||||
{
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_peer_port, *peer_port);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
|
@ -537,15 +548,21 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
break;
|
||||
|
||||
case 'L':
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_global, atoi(optstr));
|
||||
if (auto const peer_limit_global = tr_num_parse<int64_t>(optstr); peer_limit_global && *peer_limit_global >= 0)
|
||||
{
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_global, *peer_limit_global);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_per_torrent, atoi(optstr));
|
||||
if (auto const peer_limit_tor = tr_num_parse<int64_t>(optstr); peer_limit_tor && *peer_limit_tor >= 0)
|
||||
{
|
||||
tr_variantDictAddInt(&settings_, TR_KEY_peer_limit_per_torrent, *peer_limit_tor);
|
||||
}
|
||||
break;
|
||||
|
||||
case 800:
|
||||
paused_ = true;
|
||||
tr_variantDictAddBool(&settings_, TR_KEY_start_paused, true);
|
||||
break;
|
||||
|
||||
case 910:
|
||||
|
@ -573,7 +590,10 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
break;
|
||||
|
||||
case 953:
|
||||
tr_variantDictAddReal(&settings_, TR_KEY_ratio_limit, atof(optstr));
|
||||
if (auto const ratio_limit = tr_num_parse<double>(optstr); optstr)
|
||||
{
|
||||
tr_variantDictAddReal(&settings_, TR_KEY_ratio_limit, *ratio_limit);
|
||||
}
|
||||
tr_variantDictAddBool(&settings_, TR_KEY_ratio_limit_enabled, true);
|
||||
break;
|
||||
|
||||
|
@ -582,7 +602,7 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
break;
|
||||
|
||||
case 'x':
|
||||
tr_variantDictAddStr(&settings_, key_pidfile_, optstr);
|
||||
tr_variantDictAddStr(&settings_, TR_KEY_pidfile, optstr);
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
|
@ -643,7 +663,7 @@ bool tr_daemon::parse_args(int argc, char const* const* argv, bool* dump_setting
|
|||
return true;
|
||||
}
|
||||
|
||||
void tr_daemon::reconfigure(void)
|
||||
void tr_daemon::reconfigure()
|
||||
{
|
||||
if (my_session_ == nullptr)
|
||||
{
|
||||
|
@ -663,35 +683,24 @@ void tr_daemon::reconfigure(void)
|
|||
configDir = tr_sessionGetConfigDir(my_session_);
|
||||
tr_logAddInfo(fmt::format(_("Reloading settings from '{path}'"), fmt::arg("path", configDir)));
|
||||
|
||||
auto newsettings = tr_variant::make_map();
|
||||
tr_variantDictAddBool(&newsettings, TR_KEY_rpc_enabled, true);
|
||||
newsettings.merge(tr_sessionLoadSettings(configDir, MyName));
|
||||
|
||||
tr_sessionSet(my_session_, newsettings);
|
||||
tr_sessionSet(my_session_, load_settings(configDir));
|
||||
tr_sessionReloadBlocklists(my_session_);
|
||||
}
|
||||
}
|
||||
|
||||
void tr_daemon::stop(void)
|
||||
void tr_daemon::stop()
|
||||
{
|
||||
event_base_loopexit(ev_base_, nullptr);
|
||||
}
|
||||
|
||||
int tr_daemon::start([[maybe_unused]] bool foreground)
|
||||
{
|
||||
bool boolVal;
|
||||
bool pidfile_created = false;
|
||||
tr_session* session = nullptr;
|
||||
struct event* status_ev = nullptr;
|
||||
struct event* sig_ev = nullptr;
|
||||
auto watchdir = std::unique_ptr<Watchdir>{};
|
||||
char const* const cdir = this->config_dir_.c_str();
|
||||
|
||||
sd_notifyf(0, "MAINPID=%d\n", (int)getpid());
|
||||
|
||||
/* setup event state */
|
||||
ev_base_ = event_base_new();
|
||||
|
||||
event* sig_ev = nullptr;
|
||||
if (ev_base_ == nullptr || !setup_signals(sig_ev))
|
||||
{
|
||||
auto const error_code = errno;
|
||||
|
@ -699,20 +708,22 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
_("Couldn't initialize daemon: {error} ({error_code})"),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code));
|
||||
printMessage(logfile_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
|
||||
printMessage(log_stream_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
|
||||
cleanup_signals(sig_ev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start the session */
|
||||
session = tr_sessionInit(cdir, true, settings_);
|
||||
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_sessionSaveSettings(session, cdir, settings_);
|
||||
|
||||
auto sv = std::string_view{};
|
||||
(void)tr_variantDictFindStrView(&settings_, key_pidfile_, &sv);
|
||||
(void)tr_variantDictFindStrView(&settings_, TR_KEY_pidfile, &sv);
|
||||
auto const sz_pid_filename = std::string{ sv };
|
||||
auto pidfile_created = false;
|
||||
if (!std::empty(sz_pid_filename))
|
||||
{
|
||||
auto error = tr_error{};
|
||||
|
@ -740,7 +751,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
}
|
||||
}
|
||||
|
||||
if (tr_variantDictFindBool(&settings_, TR_KEY_rpc_authentication_required, &boolVal) && boolVal)
|
||||
if (auto tmp_bool = false; tr_variantDictFindBool(&settings_, TR_KEY_rpc_authentication_required, &tmp_bool) && tmp_bool)
|
||||
{
|
||||
tr_logAddInfo(_("Requiring authentication"));
|
||||
}
|
||||
|
@ -754,10 +765,11 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
}
|
||||
|
||||
/* maybe add a watchdir */
|
||||
if (tr_variantDictFindBool(&settings_, TR_KEY_watch_dir_enabled, &boolVal) && boolVal)
|
||||
auto watchdir = std::unique_ptr<Watchdir>{};
|
||||
if (auto tmp_bool = false; tr_variantDictFindBool(&settings_, TR_KEY_watch_dir_enabled, &tmp_bool) && tmp_bool)
|
||||
{
|
||||
auto force_generic = bool{ false };
|
||||
(void)tr_variantDictFindBool(&settings_, key_watch_dir_force_generic_, &force_generic);
|
||||
auto force_generic = false;
|
||||
(void)tr_variantDictFindBool(&settings_, TR_KEY_watch_dir_force_generic, &force_generic);
|
||||
|
||||
auto dir = std::string_view{};
|
||||
(void)tr_variantDictFindStrView(&settings_, TR_KEY_watch_dir, &dir);
|
||||
|
@ -780,7 +792,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
{
|
||||
tr_ctor* ctor = tr_ctorNew(my_session_);
|
||||
|
||||
if (paused_)
|
||||
if (auto paused = false; tr_variantDictFindBool(&settings_, TR_KEY_start_paused, &paused) && paused)
|
||||
{
|
||||
tr_ctorSetPaused(ctor, TR_FORCE, true);
|
||||
}
|
||||
|
@ -799,6 +811,7 @@ int tr_daemon::start([[maybe_unused]] bool foreground)
|
|||
#endif
|
||||
|
||||
/* Create new timer event to report daemon status */
|
||||
event* status_ev;
|
||||
{
|
||||
constexpr auto one_sec = timeval{ 1, 0 }; // 1 second
|
||||
status_ev = event_new(ev_base_, -1, EV_PERSIST, &::periodic_update, this);
|
||||
|
@ -855,7 +868,7 @@ CLEANUP:
|
|||
|
||||
tr_sessionSaveSettings(my_session_, cdir, settings_);
|
||||
tr_sessionClose(my_session_);
|
||||
pumpLogMessages(logfile_, logfile_flush_);
|
||||
pumpLogMessages(log_stream_);
|
||||
printf(" done.\n");
|
||||
|
||||
/* shutdown */
|
||||
|
@ -885,9 +898,7 @@ bool tr_daemon::init(int argc, char const* const argv[], bool* foreground, int*
|
|||
config_dir_ = getConfigDir(argc, argv);
|
||||
|
||||
/* load settings from defaults + config file */
|
||||
settings_ = tr_variant::make_map();
|
||||
tr_variantDictAddBool(&settings_, TR_KEY_rpc_enabled, true);
|
||||
settings_.merge(tr_sessionLoadSettings(config_dir_.c_str(), MyName));
|
||||
settings_ = load_settings(config_dir_.c_str());
|
||||
|
||||
bool dumpSettings;
|
||||
|
||||
|
@ -896,31 +907,27 @@ bool tr_daemon::init(int argc, char const* const argv[], bool* foreground, int*
|
|||
/* overwrite settings from the command line */
|
||||
if (!parse_args(argc, argv, &dumpSettings, foreground, ret))
|
||||
{
|
||||
goto EXIT_EARLY;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*foreground && logfile_ == TR_BAD_SYS_FILE)
|
||||
if (*foreground && log_stream_ == nullptr)
|
||||
{
|
||||
logfile_ = tr_sys_file_get_std(TR_STD_SYS_FILE_ERR);
|
||||
logfile_flush_ = tr_sys_file_flush_possible(logfile_);
|
||||
log_stream_ = stderr;
|
||||
}
|
||||
|
||||
if (dumpSettings)
|
||||
{
|
||||
fmt::print("{:s}\n", tr_variant_serde::json().to_string(settings_));
|
||||
goto EXIT_EARLY;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
EXIT_EARLY:
|
||||
return false;
|
||||
}
|
||||
|
||||
void tr_daemon::handle_error(tr_error const& error) const
|
||||
{
|
||||
auto const errmsg = fmt::format("Couldn't daemonize: {:s} ({:d})", error.message(), error.code());
|
||||
printMessage(logfile_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
|
||||
printMessage(log_stream_, TR_LOG_ERROR, MyName, errmsg, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
int tr_main(int argc, char* argv[])
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#ifdef HAVE_SYS_SIGNALFD_H
|
||||
|
@ -13,8 +14,8 @@
|
|||
|
||||
#include <libtransmission/variant.h>
|
||||
#include <libtransmission/quark.h>
|
||||
#include <libtransmission/file.h>
|
||||
|
||||
struct event_base;
|
||||
struct tr_error;
|
||||
struct tr_session;
|
||||
|
||||
|
@ -45,17 +46,13 @@ private:
|
|||
#ifdef HAVE_SYS_SIGNALFD_H
|
||||
int sigfd_ = -1;
|
||||
#endif /* signalfd API */
|
||||
bool paused_ = false;
|
||||
bool seen_hup_ = false;
|
||||
std::string config_dir_;
|
||||
tr_variant settings_ = {};
|
||||
bool logfile_flush_ = false;
|
||||
tr_session* my_session_ = nullptr;
|
||||
char const* log_file_name_ = nullptr;
|
||||
struct event_base* ev_base_ = nullptr;
|
||||
tr_sys_file_t logfile_ = TR_BAD_SYS_FILE;
|
||||
tr_quark key_pidfile_ = tr_quark_new("pidfile");
|
||||
tr_quark key_watch_dir_force_generic_ = tr_quark_new("watch-dir-force-generic");
|
||||
FILE* log_stream_ = nullptr;
|
||||
|
||||
bool parse_args(int argc, char const* const* argv, bool* dump_settings, bool* foreground, int* exit_code);
|
||||
bool reopen_log_file(char const* filename);
|
||||
|
|
|
@ -8,10 +8,31 @@ User=transmission
|
|||
Type=notify
|
||||
ExecStart=/usr/bin/transmission-daemon -f --log-level=error
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
|
||||
# Hardening
|
||||
CapabilityBoundingSet=
|
||||
DevicePolicy=closed
|
||||
KeyringMode=private
|
||||
LockPersonality=true
|
||||
NoNewPrivileges=true
|
||||
MemoryDenyWriteExecute=true
|
||||
ProtectSystem=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectClock=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectControlGroups=true
|
||||
ProtectKernelModules=true
|
||||
ProtectSystem=true
|
||||
ProtectHostname=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectProc=invisible
|
||||
RestrictNamespaces=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
RestrictRealtime=true
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallArchitectures=native
|
||||
SystemCallErrorNumber=EPERM
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -7,7 +7,7 @@ Software prerequisites:
|
|||
* Xcode 11.3.1 or newer
|
||||
|
||||
Building the project on Mac requires the source to be retrieved from GitHub. Pre-packaged source code will not compile.
|
||||
```console
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/transmission/transmission Transmission
|
||||
```
|
||||
|
||||
|
@ -21,7 +21,7 @@ Transmission has an Xcode project file for building in Xcode.
|
|||
|
||||
### Building the native app with CMake ###
|
||||
Build the app:
|
||||
```console
|
||||
```bash
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
cmake --build build -t transmission-mac
|
||||
open ./build/macosx/Transmission.app
|
||||
|
@ -29,7 +29,7 @@ open ./build/macosx/Transmission.app
|
|||
|
||||
### Building the GTK app with CMake ###
|
||||
Install GTK and build the app:
|
||||
```console
|
||||
```bash
|
||||
brew install gtk4 gtkmm4
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_GTK=ON -DENABLE_MAC=OFF
|
||||
cmake --build build -t transmission-gtk
|
||||
|
@ -39,20 +39,52 @@ cmake --build build -t transmission-gtk
|
|||
## On Unix ##
|
||||
### Prerequisites ###
|
||||
|
||||
#### Debian 11 and Newer ####
|
||||
#### Debian 12 / Bookworm ####
|
||||
On Debian, you can build transmission with a few dependencies on top of a base installation.
|
||||
|
||||
For building transmission-daemon you will need basic dependencies:
|
||||
```bash
|
||||
$ sudo apt install build-essential cmake git libcurl4-openssl-dev libssl-dev
|
||||
```
|
||||
These packages are not mandatory for a working binary. Transmission brings its own libraries if they aren't installed, except for `libsystemd-dev`.
|
||||
```bash
|
||||
$ sudo apt install libb64-dev libdeflate-dev libevent-dev libminiupnpc-dev libnatpmp-dev libpsl-dev libsystemd-dev
|
||||
```
|
||||
|
||||
You likely want to install transmission as a native GUI application.
|
||||
There are two options, GTK and Qt.
|
||||
|
||||
GTK 3 client:
|
||||
```bash
|
||||
$ sudo apt install gettext libgtkmm-3.0-dev
|
||||
```
|
||||
|
||||
Qt5 client:
|
||||
```bash
|
||||
$ sudo apt install libqt5svg5-dev qttools5-dev
|
||||
```
|
||||
Qt6 client:
|
||||
```bash
|
||||
$ sudo apt install qt6-svg-dev qt6-tools-dev
|
||||
```
|
||||
|
||||
Then you can begin [building.](#building-transmission-from-git-first-time)
|
||||
|
||||
#### Debian 11 / Bullseye ####
|
||||
On Debian, you can build transmission with a few dependencies on top of a base installation.
|
||||
|
||||
For building transmission-daemon you will need basic dependencies
|
||||
```bash
|
||||
$ sudo apt install git build-essential cmake libcurl4-openssl-dev libssl-dev python3
|
||||
```
|
||||
You likely want to install transmission as a native GUI application. There are two options, GTK and QT.
|
||||
You likely want to install transmission as a native GUI application. There are two options, GTK and Qt.
|
||||
|
||||
For GTK 3 client, two additional packages are required
|
||||
```bash
|
||||
$ sudo apt install libgtkmm-3.0-dev gettext
|
||||
```
|
||||
For QT client, one additional package is needed on top of basic dependencies
|
||||
|
||||
For Qt client, one additional package is needed on top of basic dependencies
|
||||
```bash
|
||||
$ sudo apt install qttools5-dev
|
||||
```
|
||||
|
|
|
@ -54,7 +54,7 @@ Here is a sample of the three basic types: respectively Boolean, Number and Stri
|
|||
* **blocklist-url:** String (default = https://www.example.com/blocklist)
|
||||
* **blocklist-enabled:** Boolean (default = false)
|
||||
|
||||
#### [Files and Locations](./ConfigFiles.md)
|
||||
#### [Files and Locations](./Configuration-Files.md)
|
||||
|
||||
* **download-dir:** String (default = [default locations](Configuration-Files.md#Locations))
|
||||
* **incomplete-dir:** String (default = [default locations](Configuration-Files.md#Locations)) Directory to keep files in until torrent is complete.
|
||||
|
@ -76,27 +76,27 @@ Here is a sample of the three basic types: respectively Boolean, Number and Stri
|
|||
* **default-trackers:** String (default = "") A list of double-newline separated tracker announce URLs. These are used for all torrents in addition to the per torrent trackers specified in the torrent file. If a tracker is only meant to be a backup, it should be separated from its main tracker by a single newline character. If a tracker should be used additionally to another tracker it should be separated by two newlines. (e.g. "udp://tracker.example.invalid:1337/announce\n\nudp://tracker.another-example.invalid:6969/announce\nhttps://backup-tracker.another-example.invalid:443/announce\n\nudp://tracker.yet-another-example.invalid:1337/announce", in this case tracker.example.invalid, tracker.another-example.invalid and tracker.yet-another-example.invalid would be used as trackers and backup-tracker.another-example.invalid as backup in case tracker.another-example.invalid is unreachable.
|
||||
* **dht-enabled:** Boolean (default = true) Enable [Distributed Hash Table (DHT)](https://wiki.theory.org/BitTorrentSpecification#Distributed_Hash_Table).
|
||||
* **encryption:** Number (0 = Prefer unencrypted connections, 1 = Prefer encrypted connections, 2 = Require encrypted connections; default = 1) [Encryption](https://wiki.vuze.com/w/Message_Stream_Encryption) preference. Encryption may help get around some ISP filtering, but at the cost of slightly higher CPU use.
|
||||
* **lazy-bitfield-enabled:** Boolean (default = true) May help get around some ISP filtering. [Vuze specification](https://wiki.vuze.com/w/Commandline_options#Network_Options).
|
||||
* **lpd-enabled:** Boolean (default = false) Enable [Local Peer Discovery (LPD)](https://en.wikipedia.org/wiki/Local_Peer_Discovery).
|
||||
* **message-level:** Number (0 = None, 1 = Critical, 2 = Error, 3 = Warn, 4 = Info, 5 = Debug, 6 = Trace; default = 2) Set verbosity of Transmission's log messages.
|
||||
* **pex-enabled:** Boolean (default = true) Enable [Peer Exchange (PEX)](https://en.wikipedia.org/wiki/Peer_exchange).
|
||||
* **pidfile:** String Path to file in which daemon PID will be stored (transmission-daemon only)
|
||||
* **prefetch-enabled:** Boolean (default = true). When enabled, Transmission will hint to the OS which piece data it's about to read from disk in order to satisfy requests from peers. On Linux, this is done by passing `POSIX_FADV_WILLNEED` to [posix_fadvise()](https://www.kernel.org/doc/man-pages/online/pages/man2/posix_fadvise.2.html). On macOS, this is done by passing `F_RDADVISE` to [fcntl()](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html).
|
||||
* **pidfile:** String Path to file in which daemon PID will be stored (_transmission-daemon only_)
|
||||
* **scrape-paused-torrents-enabled:** Boolean (default = true)
|
||||
* **script-torrent-added-enabled:** Boolean (default = false) Run a script when a torrent is added to Transmission. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page
|
||||
* **script-torrent-added-enabled:** Boolean (default = false) Run a script when a torrent is added to Transmission. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page.
|
||||
* **script-torrent-added-filename:** String (default = "") Path to script.
|
||||
* **script-torrent-done-enabled:** Boolean (default = false) Run a script when a torrent is done downloading. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page
|
||||
* **script-torrent-done-enabled:** Boolean (default = false) Run a script when a torrent is done downloading. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page.
|
||||
* **script-torrent-done-filename:** String (default = "") Path to script.
|
||||
* **script-torrent-done-seeding-enabled:** Boolean (default = false) Run a script when a torrent is done seeding. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page
|
||||
* **script-torrent-done-seeding-enabled:** Boolean (default = false) Run a script when a torrent is done seeding. Environmental variables are passed in as detailed on the [Scripts](./Scripts.md) page.
|
||||
* **script-torrent-done-seeding-filename:** String (default = "") Path to script.
|
||||
* **start_paused**: Boolean (default = false) Pause the torrents when daemon starts. _Note: transmission-daemon only._
|
||||
* **tcp-enabled:** Boolean (default = true) Optionally disable TCP connection to other peers. Never disable TCP when you also disable µTP, because then your client would not be able to communicate. Disabling TCP might also break webseeds. Unless you have a good reason, you should not set this to false.
|
||||
* **torrent-added-verify-mode:** String ("fast", "full", default: "fast") Whether newly-added torrents' local data should be fully verified when added, or wait and verify them on-demand later. See [#2626](https://github.com/transmission/transmission/pull/2626) for more discussion.
|
||||
* **utp-enabled:** Boolean (default = true) Enable [Micro Transport Protocol (µTP)](https://en.wikipedia.org/wiki/Micro_Transport_Protocol)
|
||||
* **preferred-transport:** String ("utp" = Prefer µTP, "tcp" = Prefer TCP; default = "utp") Choose your preferred transport protocol (has no effect if one of them is disabled).
|
||||
* **sleep-per-seconds-during-verify:** Number (default = 100) Controls the duration in milliseconds for which the verification process will pause to reduce disk I/O pressure.
|
||||
|
||||
#### Peers
|
||||
* **bind-address-ipv4:** String (default = "0.0.0.0") 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 "::".
|
||||
* **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)
|
||||
|
@ -122,7 +122,7 @@ Here is a sample of the three basic types: respectively Boolean, Number and Stri
|
|||
* **anti-brute-force-threshold:**: Number (default = 100) After this amount of failed authentication attempts is surpassed, the RPC server will deny any further authentication attempts until it is restarted. This is not tracked per IP but in total.
|
||||
* **rpc-authentication-required:** Boolean (default = false)
|
||||
* **rpc-bind-address:** String (default = "0.0.0.0") Where to listen for RPC connections
|
||||
* **rpc-enabled:** Boolean (default = true)
|
||||
* **rpc-enabled:** Boolean (default = true \[transmission-daemon\], false \[others\])
|
||||
* **rpc-host-whitelist:** String (Comma-delimited list of domain names. Wildcards allowed using '\*'. Example: "*.foo.org,example.com", Default: "", Always allowed: "localhost", "localhost.", all the IP addresses. Added in v2.93)
|
||||
* **rpc-host-whitelist-enabled:** Boolean (default = true. Added in v2.93)
|
||||
* **rpc-password:** String. You can enter this in as plaintext when Transmission is not running, and then Transmission will salt the value on startup and re-save the salted version as a security measure. **Note:** Transmission treats passwords starting with the character `{` as salted, so when you first create your password, the plaintext password you enter must not begin with `{`.
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
**We strongly recommend that you use the [current version](https://github.com/transmission/transmission/releases/latest) of Transmission unless you have a legacy operating system version shown below.**
|
||||
|
||||
If you have a legacy operating system version, this table shows the best Transmission version to use:
|
||||
* **Mac OS X 10.7 (Lion), 10.8 (Mountain Lion), 10.9 (Mavericks)**
|
||||
|
||||
* **OS X 10.10 (Yosemite), OS X 10.11 (El Capitan), macOS 10.12 (Sierra)**
|
||||
Latest Version: [Transmission 3.00](https://github.com/transmission/transmission-releases/raw/master/Transmission-3.00.dmg)
|
||||
SHA256 hash: [`f9984b6ba51a02bb8f880c538b28e2c7d6a3b7a22257a166cc3e1d55a133ab34`](https://www.virustotal.com/gui/file/f9984b6ba51a02bb8f880c538b28e2c7d6a3b7a22257a166cc3e1d55a133ab34/detection)
|
||||
* **Mac OS X 10.7 (Lion), OS X 10.8 (Mountain Lion), OS X 10.9 (Mavericks)**
|
||||
Latest version: [Transmission 2.94](https://github.com/transmission/transmission-releases/raw/master/Transmission-2.94.dmg)
|
||||
SHA256 hash: [`2cae915ae0e37fc5983406ca7fbd53a054a7153d3bfd7a6cef117a8a28d8a78a`](https://www.virustotal.com/gui/file/2cae915ae0e37fc5983406ca7fbd53a054a7153d3bfd7a6cef117a8a28d8a78a/detection)
|
||||
|
||||
* **Mac OS X 10.6 Intel (Snow Leopard)**
|
||||
Latest version: [Transmission 2.84](https://github.com/transmission/transmission-releases/raw/master/Transmission-2.84.dmg)
|
||||
SHA256 hash: [`53d08a55a5ca55010d409acb10f0285a649b8879085cad83f2cbcb7faa489ad5`](https://www.virustotal.com/en/file/53d08a55a5ca55010d409acb10f0285a649b8879085cad83f2cbcb7faa489ad5/analysis/)
|
||||
|
|
|
@ -27,6 +27,7 @@ Transmission can be set to invoke a script when downloads complete. The environm
|
|||
* `TR_TORRENT_ID`
|
||||
* `TR_TORRENT_LABELS` - A comma-delimited list of the torrent's labels
|
||||
* `TR_TORRENT_NAME` - Name of torrent (not filename)
|
||||
* `TR_TORRENT_PRIORITY` - The priority of the torrent (Low is "-1", Normal is "0", High is "1")
|
||||
* `TR_TORRENT_TRACKERS` - A comma-delimited list of the torrent's trackers' announce URLs
|
||||
|
||||
[Here is an example script](https://trac.transmissionbt.com/browser/trunk/extras/send-email-when-torrent-done.sh) that sends an email when a torrent finishes.
|
||||
|
|
|
@ -37,6 +37,8 @@ The file contains the following **per-torrent** properties:
|
|||
<tr><td><tt>bitfield</tt></td><td></td></tr>
|
||||
</table>
|
||||
|
||||
The file format is bencoding, as described in [bep_0003](https://www.bittorrent.org/beps/bep_0003.html).
|
||||
|
||||
## Constants
|
||||
<table>
|
||||
<tr><td>Maximum number of remembered peers</td><td><tt>MAX_REMEMBERED_PEERS</tt></td><td>200</td></tr>
|
||||
|
|
|
@ -259,8 +259,8 @@ The 'source' column here corresponds to the data structure there.
|
|||
| `priorities`| array (see below)| n/a
|
||||
| `primary-mime-type`| string| tr_torrent
|
||||
| `queuePosition`| number| tr_stat
|
||||
| `rateDownload (B/s)`| number| tr_stat
|
||||
| `rateUpload (B/s)`| number| tr_stat
|
||||
| `rateDownload` (B/s)| number| tr_stat
|
||||
| `rateUpload` (B/s)| number| tr_stat
|
||||
| `recheckProgress`| double| tr_stat
|
||||
| `secondsDownloading`| number| tr_stat
|
||||
| `secondsSeeding`| number| tr_stat
|
||||
|
@ -303,7 +303,7 @@ The 'source' column here corresponds to the data structure there.
|
|||
| Key | Value Type | transmission.h source
|
||||
|:--|:--|:--
|
||||
| `bytesCompleted` | number | tr_file_view
|
||||
| `wanted` | number | tr_file_view (**Note:** For backwards compatibility, this is serialized as an array of `0` or `1` that should be treated as booleans)
|
||||
| `wanted` | boolean | tr_file_view (**Note:** Not to be confused with `torrent-get.wanted`, which is an array of 0/1 instead of boolean)
|
||||
| `priority` | number | tr_file_view
|
||||
|
||||
`peers`: an array of objects, each containing:
|
||||
|
@ -400,8 +400,10 @@ The 'source' column here corresponds to the data structure there.
|
|||
| `tier` | number | tr_tracker_view
|
||||
|
||||
|
||||
`wanted`: An array of `tr_torrentFileCount()` Booleans true if the corresponding file is to be downloaded. (Source: `tr_file_view`)
|
||||
`wanted`: An array of `tr_torrentFileCount()` 0/1, 1 (true) if the corresponding file is to be downloaded. (Source: `tr_file_view`)
|
||||
|
||||
**Note:** For backwards compatibility, in `4.x.x`, `wanted` is serialized as an array of `0` or `1` that should be treated as booleans.
|
||||
This will be fixed in `5.0.0` to return an array of booleans.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -673,7 +675,7 @@ Response arguments:
|
|||
| Key | Value Type | Description
|
||||
| :-- | :-- | :--
|
||||
| `port-is-open` | boolean | true if port is open, false if port is closed
|
||||
| `ipProtocol` | string | `ipv4` if the test was carried out on IPv4, `ipv6` if the test was carried out on IPv6, unset if an error occured
|
||||
| `ipProtocol` | string | `ipv4` if the test was carried out on IPv4, `ipv6` if the test was carried out on IPv6, unset if it cannot be determined
|
||||
|
||||
### 4.5 Session shutdown
|
||||
This method tells the transmission session to shut down.
|
||||
|
|
|
@ -11,7 +11,7 @@ IncludeCategories:
|
|||
SortPriority: 4
|
||||
- Regex: '^<(cairo|gdk|gio|glib|gtk|pango)mm([-./]|config)'
|
||||
Priority: 5
|
||||
- Regex: '^<(fmt)/'
|
||||
- Regex: '^<(fmt|small)/'
|
||||
Priority: 6
|
||||
- Regex: '^<(cairo|gdk|gio|glib|gtk|pango)[-./]'
|
||||
Priority: 8
|
||||
|
|
|
@ -7,11 +7,13 @@ Checks: >
|
|||
-bugprone-narrowing-conversions,
|
||||
cert-*,
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
hicpp-*,
|
||||
misc-*,
|
||||
-misc-include-cleaner,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
|
@ -19,8 +21,10 @@ Checks: >
|
|||
-readability-function-cognitive-complexity,
|
||||
-readability-identifier-length,
|
||||
-readability-magic-numbers,
|
||||
-readability-qualified-auto,
|
||||
-readability-redundant-access-specifiers
|
||||
|
||||
CheckOptions:
|
||||
misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: true
|
||||
modernize-pass-by-value.ValuesOnly: true
|
||||
- { key: cppcoreguidelines-avoid-do-while.IgnoreMacros, value: true }
|
||||
- { key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic, value: true }
|
||||
- { key: modernize-pass-by-value.ValuesOnly, value: true }
|
||||
|
|
|
@ -10,24 +10,21 @@
|
|||
#include "Session.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/quark.h>
|
||||
|
||||
#include <giomm/liststore.h>
|
||||
#include <giomm/menuattributeiter.h>
|
||||
#include <giomm/menulinkiter.h>
|
||||
#include <giomm/simpleaction.h>
|
||||
#include <glibmm/i18n.h>
|
||||
#include <glibmm/variant.h>
|
||||
|
||||
#include <array>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
#include <giomm/liststore.h>
|
||||
#include <giomm/menuattributeiter.h>
|
||||
#include <giomm/menulinkiter.h>
|
||||
#include <gtkmm/shortcut.h>
|
||||
#include <gtkmm/shortcutaction.h>
|
||||
#include <gtkmm/shortcuttrigger.h>
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
#include <gtkmm/stylecontext.h>
|
||||
#include <gtkmm/window.h>
|
||||
|
||||
#include <small/set.hpp>
|
||||
|
||||
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
#include <gtkmm/droptarget.h>
|
||||
#include <gtkmm/eventcontrollerfocus.h>
|
||||
|
@ -70,11 +72,9 @@
|
|||
#include <iterator> // std::back_inserter
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
@ -101,7 +101,7 @@ namespace
|
|||
auto const AppIconName = "transmission"sv; // TODO(C++20): Use ""s
|
||||
|
||||
char const* const LICENSE =
|
||||
"Copyright 2005-2023. All code is copyrighted by the respective authors.\n"
|
||||
"Copyright 2005-2024. All code is copyrighted by the respective authors.\n"
|
||||
"\n"
|
||||
"Transmission can be redistributed and/or modified under the terms of the "
|
||||
"GNU GPL, versions 2 or 3, or by any future license endorsed by Mnemosyne LLC."
|
||||
|
@ -479,11 +479,13 @@ bool Application::Impl::on_rpc_changed_idle(tr_rpc_callback_type type, tr_torren
|
|||
auto const newvals = tr_sessionGetSettings(session);
|
||||
|
||||
// determine which settings changed
|
||||
auto changed_keys = std::set<tr_quark>{};
|
||||
auto changed_keys = small::set<tr_quark>{};
|
||||
auto& oldvals = gtr_pref_get_all();
|
||||
auto const serde = tr_variant_serde::benc();
|
||||
if (auto const* const newvals_map = newvals.get_if<tr_variant::Map>(); newvals_map != nullptr)
|
||||
{
|
||||
changed_keys.reserve(std::size(*newvals_map));
|
||||
|
||||
for (auto const& [key, newval] : *newvals_map)
|
||||
{
|
||||
bool changed = true;
|
||||
|
@ -1013,7 +1015,7 @@ void Application::Impl::on_app_exit()
|
|||
p->attach(*icon, 0, 0, 1, 2);
|
||||
|
||||
auto* top_label = Gtk::make_managed<Gtk::Label>();
|
||||
top_label->set_markup(fmt::format(FMT_STRING("<b>{:s}</b>"), _("Closing Connections…")));
|
||||
top_label->set_markup(fmt::format("<b>{:s}</b>", _("Closing Connections…")));
|
||||
top_label->set_halign(TR_GTK_ALIGN(START));
|
||||
top_label->set_valign(TR_GTK_ALIGN(CENTER));
|
||||
p->attach(*top_label, 1, 0, 1, 1);
|
||||
|
@ -1451,7 +1453,7 @@ bool Application::Impl::call_rpc_for_selected_torrents(std::string const& method
|
|||
|
||||
if (tr_variantListSize(ids) != 0)
|
||||
{
|
||||
tr_rpc_request_exec_json(session, &top, nullptr, nullptr);
|
||||
tr_rpc_request_exec(session, top, {});
|
||||
invoked = true;
|
||||
}
|
||||
|
||||
|
@ -1473,7 +1475,7 @@ void Application::Impl::start_all_torrents()
|
|||
|
||||
tr_variantInitDict(&request, 1);
|
||||
tr_variantDictAddStrView(&request, TR_KEY_method, "torrent-start"sv);
|
||||
tr_rpc_request_exec_json(session, &request, nullptr, nullptr);
|
||||
tr_rpc_request_exec(session, request, {});
|
||||
}
|
||||
|
||||
void Application::Impl::pause_all_torrents()
|
||||
|
@ -1483,7 +1485,7 @@ void Application::Impl::pause_all_torrents()
|
|||
|
||||
tr_variantInitDict(&request, 1);
|
||||
tr_variantDictAddStrView(&request, TR_KEY_method, "torrent-stop"sv);
|
||||
tr_rpc_request_exec_json(session, &request, nullptr, nullptr);
|
||||
tr_rpc_request_exec(session, request, {});
|
||||
}
|
||||
|
||||
void Application::Impl::copy_magnet_link_to_clipboard(Glib::RefPtr<Torrent> const& torrent) const
|
||||
|
@ -1517,7 +1519,7 @@ void Application::Impl::actions_handler(Glib::ustring const& action_name)
|
|||
else if (action_name == "open-torrent")
|
||||
{
|
||||
auto w = std::shared_ptr<TorrentFileChooserDialog>(TorrentFileChooserDialog::create(*wind_, core_));
|
||||
gtr_window_on_close(*w, [w]() mutable { w.reset(); });
|
||||
w->signal_response().connect([w](int /*response*/) mutable { w.reset(); });
|
||||
w->show();
|
||||
}
|
||||
else if (action_name == "show-stats")
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdlib> // abort()
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
@ -446,7 +447,7 @@ void DetailsDialog::Impl::torrent_set_bool(tr_quark key, bool value)
|
|||
tr_variantListAddInt(ids, id);
|
||||
}
|
||||
|
||||
core_->exec(&top);
|
||||
core_->exec(top);
|
||||
}
|
||||
|
||||
void DetailsDialog::Impl::torrent_set_int(tr_quark key, int value)
|
||||
|
@ -464,7 +465,7 @@ void DetailsDialog::Impl::torrent_set_int(tr_quark key, int value)
|
|||
tr_variantListAddInt(ids, id);
|
||||
}
|
||||
|
||||
core_->exec(&top);
|
||||
core_->exec(top);
|
||||
}
|
||||
|
||||
void DetailsDialog::Impl::torrent_set_real(tr_quark key, double value)
|
||||
|
@ -482,7 +483,7 @@ void DetailsDialog::Impl::torrent_set_real(tr_quark key, double value)
|
|||
tr_variantListAddInt(ids, id);
|
||||
}
|
||||
|
||||
core_->exec(&top);
|
||||
core_->exec(top);
|
||||
}
|
||||
|
||||
void DetailsDialog::Impl::options_page_init(Glib::RefPtr<Gtk::Builder> const& /*builder*/)
|
||||
|
@ -607,12 +608,12 @@ void gtr_text_buffer_set_text(Glib::RefPtr<Gtk::TextBuffer> const& b, Glib::ustr
|
|||
|
||||
[[nodiscard]] std::string get_date_string(time_t t)
|
||||
{
|
||||
return t == 0 ? _("N/A") : fmt::format(FMT_STRING("{:%x}"), fmt::localtime(t));
|
||||
return t == 0 ? _("N/A") : fmt::format("{:%x}", fmt::localtime(t));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string get_date_time_string(time_t t)
|
||||
{
|
||||
return t == 0 ? _("N/A") : fmt::format(FMT_STRING("{:%c}"), fmt::localtime(t));
|
||||
return t == 0 ? _("N/A") : fmt::format("{:%c}", fmt::localtime(t));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -1326,7 +1327,7 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
|
|||
|
||||
auto make_key = [](tr_torrent const* tor, tr_peer_stat const* ps)
|
||||
{
|
||||
return fmt::format(FMT_STRING("{:d}.{:s}"), tr_torrentId(tor), ps->addr);
|
||||
return fmt::format("{:d}.{:s}", tr_torrentId(tor), ps->addr);
|
||||
};
|
||||
|
||||
/* step 3: add any new peers */
|
||||
|
@ -1388,13 +1389,13 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
|
|||
|
||||
void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& torrents)
|
||||
{
|
||||
auto has_any_webseeds = bool{ false };
|
||||
auto has_any_webseeds = false;
|
||||
auto& hash = webseed_hash_;
|
||||
auto const& store = webseed_store_;
|
||||
|
||||
auto make_key = [](tr_torrent const* tor, char const* url)
|
||||
{
|
||||
return fmt::format(FMT_STRING("{:d}.{:s}"), tr_torrentId(tor), url);
|
||||
return fmt::format("{:d}.{:s}", tr_torrentId(tor), url);
|
||||
};
|
||||
|
||||
/* step 1: mark all webseeds as not-updated */
|
||||
|
@ -1956,7 +1957,7 @@ void buildTrackerSummary(
|
|||
gstr << text_dir_mark.at(static_cast<int>(direction));
|
||||
gstr << (tracker.isBackup ? "<i>" : "<b>");
|
||||
gstr << Glib::Markup::escape_text(
|
||||
!key.empty() ? fmt::format(FMT_STRING("{:s} - {:s}"), tracker.host_and_port, key) : tracker.host_and_port);
|
||||
!key.empty() ? fmt::format("{:s} - {:s}", tracker.host_and_port, key) : tracker.host_and_port);
|
||||
gstr << (tracker.isBackup ? "</i>" : "</b>");
|
||||
|
||||
if (!tracker.isBackup)
|
||||
|
@ -2373,7 +2374,7 @@ void AddTrackerDialog::on_response(int response)
|
|||
auto* const trackers = tr_variantDictAddList(args, TR_KEY_trackerAdd, 1);
|
||||
tr_variantListAddStr(trackers, url.raw());
|
||||
|
||||
core_->exec(&top);
|
||||
core_->exec(top);
|
||||
parent_.refresh();
|
||||
}
|
||||
else
|
||||
|
@ -2420,7 +2421,7 @@ void DetailsDialog::Impl::on_tracker_list_remove_button_clicked()
|
|||
auto* const trackers = tr_variantDictAddList(args, TR_KEY_trackerRemove, 1);
|
||||
tr_variantListAddInt(trackers, tracker_id);
|
||||
|
||||
core_->exec(&top);
|
||||
core_->exec(top);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
PropertyInfo const& get_property(PropertyType index) const noexcept
|
||||
[[nodiscard]] PropertyInfo const& get_property(PropertyType index) const noexcept
|
||||
{
|
||||
auto const id = static_cast<PropertyIdType>(index);
|
||||
g_assert(id > 0);
|
||||
|
|
|
@ -35,9 +35,9 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
@ -50,7 +50,7 @@ using namespace std::literals;
|
|||
namespace
|
||||
{
|
||||
|
||||
enum
|
||||
enum : uint16_t
|
||||
{
|
||||
/* these two fields could be any number at all so long as they're not
|
||||
* TR_PRI_LOW, TR_PRI_NORMAL, TR_PRI_HIGH, true, or false */
|
||||
|
@ -285,7 +285,7 @@ bool refreshFilesForeach(
|
|||
if (new_progress != old_progress)
|
||||
{
|
||||
(*iter)[file_cols.prog] = new_progress;
|
||||
(*iter)[file_cols.prog_str] = fmt::format(FMT_STRING("{:d}%"), new_progress);
|
||||
(*iter)[file_cols.prog_str] = fmt::format("{:d}%", new_progress);
|
||||
}
|
||||
|
||||
return false; /* keep walking */
|
||||
|
@ -659,7 +659,7 @@ void renderPriority(Gtk::CellRenderer* renderer, Gtk::TreeModel::const_iterator
|
|||
}
|
||||
|
||||
/* build a filename from tr_torrentGetCurrentDir() + the model's FC_LABELs */
|
||||
std::string buildFilename(tr_torrent const* tor, Gtk::TreeModel::iterator const& iter)
|
||||
std::string build_filename(tr_torrent const* tor, Gtk::TreeModel::iterator const& iter)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
for (auto child = iter; child; child = child->parent())
|
||||
|
@ -672,37 +672,52 @@ std::string buildFilename(tr_torrent const* tor, Gtk::TreeModel::iterator const&
|
|||
return Glib::build_filename(tokens);
|
||||
}
|
||||
|
||||
std::optional<std::string> get_filename_to_open(tr_torrent const* tor, Gtk::TreeModel::iterator const& iter)
|
||||
{
|
||||
auto file = Gio::File::create_for_path(build_filename(tor, iter));
|
||||
|
||||
// if the selected file is complete, use it
|
||||
if (iter->get_value(file_cols.prog) == 100 && file->query_exists())
|
||||
{
|
||||
return file->get_path();
|
||||
}
|
||||
|
||||
// use nearest existing ancestor instead
|
||||
for (;;)
|
||||
{
|
||||
file = file->get_parent();
|
||||
|
||||
if (!file)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (file->query_exists())
|
||||
{
|
||||
return file->get_path();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void FileList::Impl::onRowActivated(Gtk::TreeModel::Path const& path, Gtk::TreeViewColumn* /*col*/)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if (auto const* tor = core_->find_torrent(torrent_id_); tor != nullptr)
|
||||
auto const* const tor = core_->find_torrent(torrent_id_);
|
||||
if (tor == nullptr)
|
||||
{
|
||||
if (auto const iter = store_->get_iter(path); iter)
|
||||
{
|
||||
auto filename = buildFilename(tor, iter);
|
||||
auto const prog = iter->get_value(file_cols.prog);
|
||||
|
||||
/* if the file's not done, walk up the directory tree until we find
|
||||
* an ancestor that exists, and open that instead */
|
||||
if (!filename.empty() && (prog < 100 || !Glib::file_test(filename, TR_GLIB_FILE_TEST(EXISTS))))
|
||||
{
|
||||
do
|
||||
{
|
||||
filename = Glib::path_get_dirname(filename);
|
||||
} while (!filename.empty() && !Glib::file_test(filename, TR_GLIB_FILE_TEST(EXISTS)));
|
||||
}
|
||||
|
||||
if (handled = !filename.empty(); handled)
|
||||
{
|
||||
gtr_open_file(filename);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// return handled;
|
||||
auto const iter = store_->get_iter(path);
|
||||
if (!iter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto const filename = get_filename_to_open(tor, iter); filename)
|
||||
{
|
||||
gtr_open_file(*filename);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileList::Impl::onViewPathToggled(Gtk::TreeViewColumn* col, Gtk::TreeModel::Path const& path)
|
||||
|
@ -725,24 +740,25 @@ bool FileList::Impl::onViewPathToggled(Gtk::TreeViewColumn* col, Gtk::TreeModel:
|
|||
|
||||
if (cid == file_cols.priority.index())
|
||||
{
|
||||
auto priority = iter->get_value(file_cols.priority);
|
||||
auto const old_priority = iter->get_value(file_cols.priority);
|
||||
auto new_priority = TR_PRI_NORMAL;
|
||||
|
||||
switch (priority)
|
||||
switch (old_priority)
|
||||
{
|
||||
case TR_PRI_NORMAL:
|
||||
priority = TR_PRI_HIGH;
|
||||
new_priority = TR_PRI_HIGH;
|
||||
break;
|
||||
|
||||
case TR_PRI_HIGH:
|
||||
priority = TR_PRI_LOW;
|
||||
new_priority = TR_PRI_LOW;
|
||||
break;
|
||||
|
||||
default:
|
||||
priority = TR_PRI_NORMAL;
|
||||
new_priority = TR_PRI_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
tr_torrentSetFilePriorities(tor, indexBuf.data(), indexBuf.size(), priority);
|
||||
tr_torrentSetFilePriorities(tor, indexBuf.data(), indexBuf.size(), new_priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
|
||||
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
#include <gtkmm/filterlistmodel.h>
|
||||
#else
|
||||
#include <gtkmm/treemodelfilter.h>
|
||||
#endif
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
@ -45,13 +43,19 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
{
|
||||
using ActivityType = TorrentFilter::Activity;
|
||||
using TrackerType = TorrentFilter::Tracker;
|
||||
|
||||
constexpr auto ActivitySeparator = static_cast<ActivityType>(-1);
|
||||
constexpr auto TrackerSeparator = static_cast<TrackerType>(-1);
|
||||
} // namespace
|
||||
|
||||
class FilterBar::Impl
|
||||
{
|
||||
using FilterModel = IF_GTKMM4(Gtk::FilterListModel, Gtk::TreeModelFilter);
|
||||
|
||||
using TrackerType = TorrentFilter::Tracker;
|
||||
using ActivityType = TorrentFilter::Activity;
|
||||
|
||||
public:
|
||||
Impl(FilterBar& widget, Glib::RefPtr<Session> const& core);
|
||||
~Impl();
|
||||
|
@ -118,15 +122,10 @@ private:
|
|||
sigc::connection update_filter_models_on_change_tag_;
|
||||
};
|
||||
|
||||
/***
|
||||
****
|
||||
**** TRACKERS
|
||||
****
|
||||
***/
|
||||
// --- TRACKERS
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class TrackerFilterModelColumns : public Gtk::TreeModelColumnRecord
|
||||
{
|
||||
public:
|
||||
|
@ -141,7 +140,7 @@ public:
|
|||
|
||||
Gtk::TreeModelColumn<Glib::ustring> displayname; /* human-readable name; ie, Legaltorrents */
|
||||
Gtk::TreeModelColumn<int> count; /* how many matches there are */
|
||||
Gtk::TreeModelColumn<int> type;
|
||||
Gtk::TreeModelColumn<TrackerType> type;
|
||||
Gtk::TreeModelColumn<Glib::ustring> sitename; // pattern-matching text; see tr_parsed_url.sitename
|
||||
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> pixbuf;
|
||||
};
|
||||
|
@ -202,7 +201,7 @@ bool FilterBar::Impl::tracker_filter_model_update()
|
|||
/* Walk through all the torrents, tallying how many matches there are
|
||||
* for the various categories. Also make a sorted list of all tracker
|
||||
* hosts s.t. we can merge it with the existing list */
|
||||
auto n_torrents = int{ 0 };
|
||||
auto n_torrents = 0;
|
||||
auto site_infos = std::unordered_map<std::string /*site*/, site_info>{};
|
||||
for (auto i = 0U, count = torrents_model->get_n_items(); i < count; ++i)
|
||||
{
|
||||
|
@ -298,7 +297,7 @@ bool FilterBar::Impl::tracker_filter_model_update()
|
|||
add->set_value(tracker_filter_cols.sitename, Glib::ustring{ site.sitename });
|
||||
add->set_value(tracker_filter_cols.displayname, get_name_from_host(site.sitename));
|
||||
add->set_value(tracker_filter_cols.count, site.count);
|
||||
add->set_value(tracker_filter_cols.type, static_cast<int>(TrackerType::HOST));
|
||||
add->set_value(tracker_filter_cols.type, TrackerType::HOST);
|
||||
auto path = tracker_model_->get_path(add);
|
||||
core_->favicon_cache().load(
|
||||
site.announce_url,
|
||||
|
@ -322,17 +321,17 @@ Glib::RefPtr<Gtk::TreeStore> FilterBar::Impl::tracker_filter_model_new()
|
|||
|
||||
auto iter = store->append();
|
||||
iter->set_value(tracker_filter_cols.displayname, Glib::ustring(_("All")));
|
||||
iter->set_value(tracker_filter_cols.type, static_cast<int>(TrackerType::ALL));
|
||||
iter->set_value(tracker_filter_cols.type, TrackerType::ALL);
|
||||
|
||||
iter = store->append();
|
||||
iter->set_value(tracker_filter_cols.type, -1);
|
||||
iter->set_value(tracker_filter_cols.type, TrackerSeparator);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
bool FilterBar::Impl::is_it_a_separator(Gtk::TreeModel::const_iterator const& iter)
|
||||
{
|
||||
return iter->get_value(tracker_filter_cols.type) == -1;
|
||||
return iter->get_value(tracker_filter_cols.type) == TrackerSeparator;
|
||||
}
|
||||
|
||||
void FilterBar::Impl::render_pixbuf_func(Gtk::CellRendererPixbuf& cell_renderer, Gtk::TreeModel::const_iterator const& iter)
|
||||
|
@ -406,7 +405,7 @@ public:
|
|||
|
||||
Gtk::TreeModelColumn<Glib::ustring> name;
|
||||
Gtk::TreeModelColumn<int> count;
|
||||
Gtk::TreeModelColumn<int> type;
|
||||
Gtk::TreeModelColumn<ActivityType> type;
|
||||
Gtk::TreeModelColumn<Glib::ustring> icon_name;
|
||||
};
|
||||
|
||||
|
@ -416,7 +415,7 @@ ActivityFilterModelColumns const activity_filter_cols;
|
|||
|
||||
bool FilterBar::Impl::activity_is_it_a_separator(Gtk::TreeModel::const_iterator const& iter)
|
||||
{
|
||||
return iter->get_value(activity_filter_cols.type) == -1;
|
||||
return iter->get_value(activity_filter_cols.type) == ActivitySeparator;
|
||||
}
|
||||
|
||||
void FilterBar::Impl::status_model_update_count(Gtk::TreeModel::iterator const& iter, int n)
|
||||
|
@ -434,7 +433,7 @@ bool FilterBar::Impl::activity_filter_model_update()
|
|||
for (auto& row : activity_model_->children())
|
||||
{
|
||||
auto const type = row.get_value(activity_filter_cols.type);
|
||||
if (type == -1)
|
||||
if (type == ActivitySeparator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -487,7 +486,7 @@ Glib::RefPtr<Gtk::ListStore> FilterBar::Impl::activity_filter_model_new()
|
|||
Glib::ustring();
|
||||
auto const iter = store->append();
|
||||
iter->set_value(activity_filter_cols.name, name);
|
||||
iter->set_value(activity_filter_cols.type, static_cast<int>(type.type));
|
||||
iter->set_value(activity_filter_cols.type, type.type);
|
||||
iter->set_value(activity_filter_cols.icon_name, Glib::ustring(type.icon_name != nullptr ? type.icon_name : ""));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
#include <glibmm/object.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
template<typename T>
|
||||
class FilterBase : public IF_GTKMM4(Gtk::Filter, Glib::Object)
|
||||
{
|
||||
public:
|
||||
#if !GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
enum class Change{
|
||||
enum class Change : uint8_t{
|
||||
DIFFERENT,
|
||||
LESS_STRICT,
|
||||
MORE_STRICT,
|
||||
|
|
|
@ -30,6 +30,13 @@ FilterListModel<ItemT>::FilterListModel(Glib::RefPtr<Gtk::TreeModel> const& mode
|
|||
: Gtk::TreeModelFilter(model)
|
||||
, matches_all_(filter->matches_all())
|
||||
, matches_none_(filter->matches_none())
|
||||
, signal_changed_tag_{ filter->signal_changed().connect(
|
||||
[this, filter](auto /*changes*/)
|
||||
{
|
||||
matches_all_ = filter->matches_all();
|
||||
matches_none_ = filter->matches_none();
|
||||
refilter();
|
||||
}) }
|
||||
{
|
||||
static auto const& self_col = ItemT::get_columns().self;
|
||||
|
||||
|
@ -53,14 +60,6 @@ FilterListModel<ItemT>::FilterListModel(Glib::RefPtr<Gtk::TreeModel> const& mode
|
|||
|
||||
set_visible_func(filter_func);
|
||||
|
||||
signal_changed_tag_ = filter->signal_changed().connect(
|
||||
[this, filter](auto /*changes*/)
|
||||
{
|
||||
matches_all_ = filter->matches_all();
|
||||
matches_none_ = filter->matches_none();
|
||||
refilter();
|
||||
});
|
||||
|
||||
signal_row_inserted().connect([this](auto const& path, auto const& /*iter*/)
|
||||
{ signal_items_changed_.emit(path.front(), 0, 1); });
|
||||
signal_row_deleted().connect([this](auto const& path) { signal_items_changed_.emit(path.front(), 1, 0); });
|
||||
|
|
|
@ -53,7 +53,7 @@ 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");
|
||||
label_.set_markup(fmt::format(FMT_STRING("<i>{:s}</i>"), text));
|
||||
label_.set_markup(fmt::format("<i>{:s}</i>", text));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
auto inline constexpr GUI_PAD_SMALL = int{ 3 };
|
||||
auto inline constexpr GUI_PAD = int{ 6 };
|
||||
auto inline constexpr GUI_PAD_BIG = int{ 12 };
|
||||
auto inline constexpr GUI_PAD_LARGE = int{ 12 };
|
||||
auto inline constexpr GUI_PAD_SMALL = 3;
|
||||
auto inline constexpr GUI_PAD = 6;
|
||||
auto inline constexpr GUI_PAD_BIG = 12;
|
||||
auto inline constexpr GUI_PAD_LARGE = 12;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <gtkmm/treemodel.h>
|
||||
#include <gtkmm/treemodelcolumn.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
@ -25,7 +26,7 @@ class ListModelAdapter
|
|||
using IdGetter = std::function<int(Glib::RefPtr<Glib::ObjectBase const> const&)>;
|
||||
using ValueGetter = std::function<void(Glib::RefPtr<Glib::ObjectBase const> const&, int, Glib::ValueBase&)>;
|
||||
|
||||
enum class PositionAdjustment
|
||||
enum class PositionAdjustment : int8_t
|
||||
{
|
||||
DECREMENT = -1,
|
||||
INCREMENT = 1,
|
||||
|
|
|
@ -542,7 +542,7 @@ MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& bui
|
|||
file_radio_->signal_toggled().connect([this]() { onSourceToggled(file_radio_, file_chooser_); });
|
||||
file_chooser_->signal_selection_changed().connect([this]() { onChooserChosen(file_chooser_); });
|
||||
|
||||
pieces_lb_->set_markup(fmt::format(FMT_STRING("<i>{:s}</i>"), _("No source selected")));
|
||||
pieces_lb_->set_markup(fmt::format("<i>{:s}</i>", _("No source selected")));
|
||||
|
||||
piece_size_scale_->set_visible(false);
|
||||
piece_size_scale_->signal_value_changed().connect([this]() { onPieceSizeUpdated(); });
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "Session.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/log.h>
|
||||
|
||||
#include <giomm/simpleaction.h>
|
||||
|
@ -25,7 +24,7 @@
|
|||
#include <glibmm/variant.h>
|
||||
#include <gtkmm/cellrenderertext.h>
|
||||
#include <gtkmm/combobox.h>
|
||||
#include <gtkmm/filechooserdialog.h>
|
||||
#include <gtkmm/filechoosernative.h>
|
||||
#include <gtkmm/liststore.h>
|
||||
#include <gtkmm/messagedialog.h>
|
||||
#include <gtkmm/treemodel.h>
|
||||
|
@ -37,9 +36,10 @@
|
|||
#include <fmt/core.h>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
class MessageLogColumnsModel : public Gtk::TreeModelColumnRecord
|
||||
{
|
||||
|
@ -72,15 +72,15 @@ private:
|
|||
bool onRefresh();
|
||||
|
||||
void onSaveRequest();
|
||||
void onSaveDialogResponse(std::shared_ptr<Gtk::FileChooserDialog>& d, int response);
|
||||
void doSave(Gtk::Window& parent, Glib::ustring const& filename);
|
||||
void onSaveDialogResponse(Glib::RefPtr<Gtk::FileChooserNative>& d, int response);
|
||||
void doSave(std::string const& filename);
|
||||
|
||||
void onClearRequest();
|
||||
void onPauseToggled(Gio::SimpleAction& action);
|
||||
|
||||
void scroll_to_bottom();
|
||||
void level_combo_changed_cb(Gtk::ComboBox* combo_box);
|
||||
void level_combo_init(Gtk::ComboBox* level_combo) const;
|
||||
static void level_combo_init(Gtk::ComboBox* level_combo);
|
||||
|
||||
[[nodiscard]] bool is_pinned_to_new() const;
|
||||
[[nodiscard]] bool isRowVisible(Gtk::TreeModel::const_iterator const& iter) const;
|
||||
|
@ -96,7 +96,14 @@ private:
|
|||
tr_log_level maxLevel_ = TR_LOG_INFO;
|
||||
bool isPaused_ = false;
|
||||
sigc::connection refresh_tag_;
|
||||
std::map<tr_log_level, char const*> const level_names_;
|
||||
|
||||
static auto inline const level_names_ = std::array<std::pair<tr_log_level, char const*>, 5U>{ {
|
||||
{ TR_LOG_CRITICAL, C_("Logging level", "Critical") },
|
||||
{ TR_LOG_ERROR, C_("Logging level", "Error") },
|
||||
{ TR_LOG_WARN, C_("Logging level", "Warning") },
|
||||
{ TR_LOG_INFO, C_("Logging level", "Information") },
|
||||
{ TR_LOG_DEBUG, C_("Logging level", "Debug") },
|
||||
} };
|
||||
};
|
||||
|
||||
namespace
|
||||
|
@ -157,13 +164,15 @@ void MessageLogWindow::Impl::scroll_to_bottom()
|
|||
*****
|
||||
****/
|
||||
|
||||
void MessageLogWindow::Impl::level_combo_init(Gtk::ComboBox* level_combo) const
|
||||
// static
|
||||
void MessageLogWindow::Impl::level_combo_init(Gtk::ComboBox* level_combo)
|
||||
{
|
||||
auto const pref_level = static_cast<tr_log_level>(gtr_pref_int_get(TR_KEY_message_level));
|
||||
auto const default_level = TR_LOG_INFO;
|
||||
|
||||
auto has_pref_level = false;
|
||||
auto items = std::vector<std::pair<Glib::ustring, int>>{};
|
||||
items.reserve(std::size(level_names_));
|
||||
for (auto const& [level, name] : level_names_)
|
||||
{
|
||||
items.emplace_back(name, level);
|
||||
|
@ -201,21 +210,24 @@ Glib::ustring gtr_asctime(time_t t)
|
|||
|
||||
} // namespace
|
||||
|
||||
void MessageLogWindow::Impl::doSave(Gtk::Window& parent, Glib::ustring const& filename)
|
||||
void MessageLogWindow::Impl::doSave(std::string const& filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto stream = std::ofstream();
|
||||
stream.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
stream.open(Glib::locale_from_utf8(filename), std::ios_base::trunc);
|
||||
stream.open(filename, std::ios_base::trunc);
|
||||
|
||||
for (auto const& row : store_->children())
|
||||
{
|
||||
auto const* const node = row.get_value(message_log_cols.tr_msg);
|
||||
auto const date = gtr_asctime(node->when);
|
||||
|
||||
auto const it = level_names_.find(node->level);
|
||||
auto const* const level_str = it != std::end(level_names_) ? it->second : "???";
|
||||
auto const iter = std::find_if(
|
||||
std::begin(level_names_),
|
||||
std::end(level_names_),
|
||||
[key = node->level](auto const& item) { return item.first == key; });
|
||||
auto const* const level_str = iter != std::end(level_names_) ? iter->second : "???";
|
||||
|
||||
fmt::print(stream, "{}\t{}\t{}\t{}\n", date, level_str, node->name, node->message);
|
||||
}
|
||||
|
@ -223,10 +235,10 @@ void MessageLogWindow::Impl::doSave(Gtk::Window& parent, Glib::ustring const& fi
|
|||
catch (std::ios_base::failure const& e)
|
||||
{
|
||||
auto w = std::make_shared<Gtk::MessageDialog>(
|
||||
parent,
|
||||
window_,
|
||||
fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("path", Glib::filename_to_utf8(filename)),
|
||||
fmt::arg("error", e.code().message()),
|
||||
fmt::arg("error_code", e.code().value())),
|
||||
false,
|
||||
|
@ -238,22 +250,21 @@ void MessageLogWindow::Impl::doSave(Gtk::Window& parent, Glib::ustring const& fi
|
|||
}
|
||||
}
|
||||
|
||||
void MessageLogWindow::Impl::onSaveDialogResponse(std::shared_ptr<Gtk::FileChooserDialog>& d, int response)
|
||||
void MessageLogWindow::Impl::onSaveDialogResponse(Glib::RefPtr<Gtk::FileChooserNative>& d, int response)
|
||||
{
|
||||
if (response == TR_GTK_RESPONSE_TYPE(ACCEPT))
|
||||
{
|
||||
doSave(*d, d->get_file()->get_path());
|
||||
}
|
||||
auto const filename = response == TR_GTK_RESPONSE_TYPE(ACCEPT) ? d->get_file()->get_path() : std::string();
|
||||
|
||||
d.reset();
|
||||
|
||||
if (!filename.empty())
|
||||
{
|
||||
doSave(filename);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLogWindow::Impl::onSaveRequest()
|
||||
{
|
||||
auto d = std::make_shared<Gtk::FileChooserDialog>(window_, _("Save Log"), TR_GTK_FILE_CHOOSER_ACTION(SAVE));
|
||||
d->add_button(_("_Cancel"), TR_GTK_RESPONSE_TYPE(CANCEL));
|
||||
d->add_button(_("_Save"), TR_GTK_RESPONSE_TYPE(ACCEPT));
|
||||
|
||||
auto d = Gtk::FileChooserNative::create(_("Save Log"), window_, TR_GTK_FILE_CHOOSER_ACTION(SAVE), _("_Save"), _("_Cancel"));
|
||||
d->signal_response().connect([this, d](int response) mutable { onSaveDialogResponse(d, response); });
|
||||
d->show();
|
||||
}
|
||||
|
@ -479,13 +490,6 @@ MessageLogWindow::Impl::Impl(
|
|||
, refresh_tag_(Glib::signal_timeout().connect_seconds(
|
||||
sigc::mem_fun(*this, &Impl::onRefresh),
|
||||
SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS))
|
||||
, level_names_{ {
|
||||
{ TR_LOG_CRITICAL, C_("Logging level", "Critical") },
|
||||
{ TR_LOG_ERROR, C_("Logging level", "Error") },
|
||||
{ TR_LOG_WARN, C_("Logging level", "Warning") },
|
||||
{ TR_LOG_INFO, C_("Logging level", "Information") },
|
||||
{ TR_LOG_DEBUG, C_("Logging level", "Debug") },
|
||||
} }
|
||||
{
|
||||
/**
|
||||
*** toolbar
|
||||
|
|
|
@ -118,7 +118,7 @@ void OptionsDialog::Impl::addResponseCB(int response)
|
|||
{
|
||||
if (response == TR_GTK_RESPONSE_TYPE(ACCEPT))
|
||||
{
|
||||
tr_torrentSetPriority(tor_, gtr_combo_box_get_active_enum(*priority_combo_));
|
||||
tr_torrentSetPriority(tor_, static_cast<tr_priority_t>(gtr_combo_box_get_active_enum(*priority_combo_)));
|
||||
|
||||
if (run_check_->get_active())
|
||||
{
|
||||
|
@ -353,8 +353,6 @@ void TorrentFileChooserDialog::onOpenDialogResponse(int response, Glib::RefPtr<S
|
|||
|
||||
core->add_files(files, do_start, do_prompt, do_notify);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
std::unique_ptr<TorrentFileChooserDialog> TorrentFileChooserDialog::create(
|
||||
|
@ -365,13 +363,10 @@ std::unique_ptr<TorrentFileChooserDialog> TorrentFileChooserDialog::create(
|
|||
}
|
||||
|
||||
TorrentFileChooserDialog::TorrentFileChooserDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
|
||||
: Gtk::FileChooserDialog(parent, _("Open a Torrent"), TR_GTK_FILE_CHOOSER_ACTION(OPEN))
|
||||
: Gtk::FileChooserNative(_("Open a Torrent"), parent, TR_GTK_FILE_CHOOSER_ACTION(OPEN), _("_Open"), _("_Cancel"))
|
||||
{
|
||||
set_modal(true);
|
||||
|
||||
add_button(_("_Cancel"), TR_GTK_RESPONSE_TYPE(CANCEL));
|
||||
add_button(_("_Open"), TR_GTK_RESPONSE_TYPE(ACCEPT));
|
||||
|
||||
set_select_multiple(true);
|
||||
addTorrentFilters(this);
|
||||
signal_response().connect([this, core](int response) { onOpenDialogResponse(response, core); });
|
||||
|
@ -391,7 +386,6 @@ TorrentFileChooserDialog::TorrentFileChooserDialog(Gtk::Window& parent, Glib::Re
|
|||
|
||||
void TorrentUrlChooserDialog::onOpenURLResponse(int response, Gtk::Entry const& entry, Glib::RefPtr<Session> const& core)
|
||||
{
|
||||
|
||||
if (response == TR_GTK_RESPONSE_TYPE(CANCEL))
|
||||
{
|
||||
close();
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <gtkmm/builder.h>
|
||||
#include <gtkmm/dialog.h>
|
||||
#include <gtkmm/entry.h>
|
||||
#include <gtkmm/filechooserdialog.h>
|
||||
#include <gtkmm/filechoosernative.h>
|
||||
#include <gtkmm/window.h>
|
||||
|
||||
#include <memory>
|
||||
|
@ -38,7 +38,7 @@ private:
|
|||
void onOpenURLResponse(int response, Gtk::Entry const& entry, Glib::RefPtr<Session> const& core);
|
||||
};
|
||||
|
||||
class TorrentFileChooserDialog : public Gtk::FileChooserDialog
|
||||
class TorrentFileChooserDialog : public Gtk::FileChooserNative
|
||||
{
|
||||
public:
|
||||
~TorrentFileChooserDialog() override = default;
|
||||
|
|
|
@ -5,17 +5,21 @@
|
|||
|
||||
#include "PathButton.h"
|
||||
|
||||
#include "GtkCompat.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <giomm/file.h>
|
||||
#include <glibmm/error.h>
|
||||
#include <glibmm/i18n.h>
|
||||
#include <glibmm/property.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/filechooserdialog.h>
|
||||
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
#include <glibmm/error.h>
|
||||
#include <glibmm/property.h>
|
||||
#include <gtkmm/dialog.h>
|
||||
#include <gtkmm/filechoosernative.h>
|
||||
#include <gtkmm/image.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/separator.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -142,10 +146,12 @@ void PathButton::Impl::show_dialog()
|
|||
{
|
||||
auto const title = title_.get_value();
|
||||
|
||||
auto dialog = std::make_shared<Gtk::FileChooserDialog>(!title.empty() ? title : _("Select a File"), action_.get_value());
|
||||
auto dialog = Gtk::FileChooserNative::create(
|
||||
!title.empty() ? title : _("Select a File"),
|
||||
action_.get_value(),
|
||||
_("_Open"),
|
||||
_("_Cancel"));
|
||||
dialog->set_transient_for(gtr_widget_get_window(widget_));
|
||||
dialog->add_button(_("_Cancel"), Gtk::ResponseType::CANCEL);
|
||||
dialog->add_button(_("_Open"), Gtk::ResponseType::ACCEPT);
|
||||
dialog->set_modal(true);
|
||||
|
||||
if (!current_file_.empty())
|
||||
|
@ -167,7 +173,7 @@ void PathButton::Impl::show_dialog()
|
|||
dialog->signal_response().connect(
|
||||
[this, dialog](int response) mutable
|
||||
{
|
||||
if (response == Gtk::ResponseType::ACCEPT)
|
||||
if (response == TR_GTK_RESPONSE_TYPE(ACCEPT))
|
||||
{
|
||||
set_filename(dialog->get_file()->get_path());
|
||||
selection_changed_.emit();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "Percents.h"
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/utils.h>
|
||||
|
||||
std::string Percents::to_string() const
|
||||
|
|
37
gtk/Prefs.cc
37
gtk/Prefs.cc
|
@ -25,13 +25,9 @@ void gtr_pref_init(std::string_view config_dir)
|
|||
gl_confdir = config_dir;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
**** Preferences
|
||||
****
|
||||
***/
|
||||
|
||||
[[nodiscard]] static std::string get_default_download_dir()
|
||||
namespace
|
||||
{
|
||||
[[nodiscard]] std::string get_default_download_dir()
|
||||
{
|
||||
if (auto dir = Glib::get_user_special_dir(TR_GLIB_USER_DIRECTORY(DOWNLOAD)); !std::empty(dir))
|
||||
{
|
||||
|
@ -50,7 +46,7 @@ void gtr_pref_init(std::string_view config_dir)
|
|||
* This is where we initialize the preferences file with the default values.
|
||||
* If you add a new preferences key, you /must/ add a default value here.
|
||||
*/
|
||||
[[nodiscard]] static tr_variant get_default_app_settings()
|
||||
[[nodiscard]] tr_variant get_default_app_settings()
|
||||
{
|
||||
auto const dir = get_default_download_dir();
|
||||
|
||||
|
@ -88,7 +84,7 @@ void gtr_pref_init(std::string_view config_dir)
|
|||
return tr_variant{ std::move(map) };
|
||||
}
|
||||
|
||||
static void ensure_sound_cmd_is_a_list(tr_variant* dict)
|
||||
void ensure_sound_cmd_is_a_list(tr_variant* dict)
|
||||
{
|
||||
tr_quark const key = TR_KEY_torrent_complete_sound_command;
|
||||
tr_variant* list = nullptr;
|
||||
|
@ -106,23 +102,20 @@ static void ensure_sound_cmd_is_a_list(tr_variant* dict)
|
|||
tr_variantListAddStr(list, "transmission torrent downloaded"sv);
|
||||
}
|
||||
|
||||
static tr_variant& getPrefs()
|
||||
tr_variant& getPrefs()
|
||||
{
|
||||
static auto settings = tr_variant{};
|
||||
|
||||
if (!settings.has_value())
|
||||
{
|
||||
settings = get_default_app_settings();
|
||||
settings.merge(tr_sessionLoadSettings(gl_confdir.c_str(), nullptr));
|
||||
auto const app_defaults = get_default_app_settings();
|
||||
settings.merge(tr_sessionLoadSettings(&app_defaults, gl_confdir.c_str(), nullptr));
|
||||
ensure_sound_cmd_is_a_list(&settings);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
} // namespace
|
||||
|
||||
tr_variant& gtr_pref_get_all()
|
||||
{
|
||||
|
@ -153,9 +146,7 @@ void gtr_pref_double_set(tr_quark const key, double value)
|
|||
tr_variantDictAddReal(&getPrefs(), key, value);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
bool gtr_pref_flag_get(tr_quark const key)
|
||||
{
|
||||
|
@ -169,9 +160,7 @@ void gtr_pref_flag_set(tr_quark const key, bool value)
|
|||
tr_variantDictAddBool(&getPrefs(), key, value);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
std::vector<std::string> gtr_pref_strv_get(tr_quark const key)
|
||||
{
|
||||
|
@ -207,9 +196,7 @@ void gtr_pref_string_set(tr_quark const key, std::string_view value)
|
|||
tr_variantDictAddStr(&getPrefs(), key, value);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
void gtr_pref_save(tr_session* session)
|
||||
{
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "Utils.h"
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/version.h>
|
||||
#include <libtransmission/web-utils.h>
|
||||
|
||||
#include <glibmm/date.h>
|
||||
|
@ -43,11 +42,14 @@
|
|||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace libtransmission::Values;
|
||||
|
||||
|
@ -879,10 +881,23 @@ public:
|
|||
TR_DISABLE_COPY_MOVE(NetworkPage)
|
||||
|
||||
private:
|
||||
enum PortTestStatus : uint8_t
|
||||
{
|
||||
PORT_TEST_UNKNOWN = 0U,
|
||||
PORT_TEST_CHECKING,
|
||||
PORT_TEST_OPEN,
|
||||
PORT_TEST_CLOSED,
|
||||
PORT_TEST_ERROR
|
||||
};
|
||||
|
||||
void portTestSetSensitive();
|
||||
void updatePortStatusText();
|
||||
void onCorePrefsChanged(tr_quark key);
|
||||
void onPortTested(bool isOpen);
|
||||
void onPortTested(std::optional<bool> result, Session::PortTestIpProtocol ip_protocol);
|
||||
void onPortTest();
|
||||
|
||||
static std::string_view getPortStatusText(PortTestStatus status) noexcept;
|
||||
|
||||
private:
|
||||
Glib::RefPtr<Session> core_;
|
||||
|
||||
|
@ -892,15 +907,18 @@ private:
|
|||
|
||||
sigc::connection portTag_;
|
||||
sigc::connection prefsTag_;
|
||||
|
||||
std::array<PortTestStatus, Session::NUM_PORT_TEST_IP_PROTOCOL> portTestStatus_ = {};
|
||||
};
|
||||
|
||||
void NetworkPage::onCorePrefsChanged(tr_quark const key)
|
||||
{
|
||||
if (key == TR_KEY_peer_port)
|
||||
{
|
||||
gtr_label_set_text(*portLabel_, _("Status unknown"));
|
||||
portButton_->set_sensitive(true);
|
||||
portSpin_->set_sensitive(true);
|
||||
portTestStatus_[Session::PORT_TEST_IPV4] = PORT_TEST_UNKNOWN;
|
||||
portTestStatus_[Session::PORT_TEST_IPV6] = PORT_TEST_UNKNOWN;
|
||||
updatePortStatusText();
|
||||
portTestSetSensitive();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,28 +928,92 @@ NetworkPage::~NetworkPage()
|
|||
portTag_.disconnect();
|
||||
}
|
||||
|
||||
void NetworkPage::onPortTested(bool isOpen)
|
||||
std::string_view NetworkPage::getPortStatusText(PortTestStatus const status) noexcept
|
||||
{
|
||||
portLabel_->set_markup(fmt::format(
|
||||
isOpen ? _("Port is {markup_begin}open{markup_end}") : _("Port is {markup_begin}closed{markup_end}"),
|
||||
fmt::arg("markup_begin", "<b>"),
|
||||
fmt::arg("markup_end", "</b>")));
|
||||
portButton_->set_sensitive(true);
|
||||
portSpin_->set_sensitive(true);
|
||||
switch (status)
|
||||
{
|
||||
case PORT_TEST_UNKNOWN:
|
||||
return C_("Port test status", "unknown");
|
||||
case PORT_TEST_CHECKING:
|
||||
return C_("Port test status", "checking…");
|
||||
case PORT_TEST_OPEN:
|
||||
return C_("Port test status", "open");
|
||||
case PORT_TEST_CLOSED:
|
||||
return C_("Port test status", "closed");
|
||||
case PORT_TEST_ERROR:
|
||||
return C_("Port test status", "error");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPage::updatePortStatusText()
|
||||
{
|
||||
auto const status_ipv4 = getPortStatusText(portTestStatus_[Session::PORT_TEST_IPV4]);
|
||||
auto const status_ipv6 = getPortStatusText(portTestStatus_[Session::PORT_TEST_IPV6]);
|
||||
|
||||
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(
|
||||
_("Status: <b>{status_ipv4}</b> (IPv4), <b>{status_ipv6}</b> (IPv6)"),
|
||||
fmt::arg("status_ipv4", status_ipv4),
|
||||
fmt::arg("status_ipv6", status_ipv6)));
|
||||
}
|
||||
|
||||
void NetworkPage::portTestSetSensitive()
|
||||
{
|
||||
// Depend on the RPC call status instead of the UI status, so that the widgets
|
||||
// won't be enabled even if the port peer port changed while we have port-test
|
||||
// RPC call(s) in-flight.
|
||||
auto const sensitive = !core_->port_test_pending(Session::PORT_TEST_IPV4) &&
|
||||
!core_->port_test_pending(Session::PORT_TEST_IPV6);
|
||||
portButton_->set_sensitive(sensitive);
|
||||
portSpin_->set_sensitive(sensitive);
|
||||
}
|
||||
|
||||
void NetworkPage::onPortTested(std::optional<bool> const result, Session::PortTestIpProtocol const ip_protocol)
|
||||
{
|
||||
auto constexpr ResultToStatus = [](std::optional<bool> const res)
|
||||
{
|
||||
if (!res)
|
||||
{
|
||||
return PORT_TEST_ERROR;
|
||||
}
|
||||
if (!*res)
|
||||
{
|
||||
return PORT_TEST_CLOSED;
|
||||
}
|
||||
return PORT_TEST_OPEN;
|
||||
};
|
||||
|
||||
// Only update the UI if the current status is "checking", so that
|
||||
// we won't show the port test results for the old peer port if it
|
||||
// changed while we have port-test RPC call(s) in-flight.
|
||||
if (auto& status = portTestStatus_[ip_protocol]; status == PORT_TEST_CHECKING)
|
||||
{
|
||||
status = ResultToStatus(result);
|
||||
updatePortStatusText();
|
||||
}
|
||||
portTestSetSensitive();
|
||||
}
|
||||
|
||||
void NetworkPage::onPortTest()
|
||||
{
|
||||
portButton_->set_sensitive(false);
|
||||
portSpin_->set_sensitive(false);
|
||||
portLabel_->set_text(_("Testing TCP port…"));
|
||||
portTestStatus_[Session::PORT_TEST_IPV4] = PORT_TEST_CHECKING;
|
||||
portTestStatus_[Session::PORT_TEST_IPV6] = PORT_TEST_CHECKING;
|
||||
updatePortStatusText();
|
||||
|
||||
if (!portTag_.connected())
|
||||
{
|
||||
portTag_ = core_->signal_port_tested().connect([this](bool is_open) { onPortTested(is_open); });
|
||||
portTag_ = core_->signal_port_tested().connect(
|
||||
[this](std::optional<bool> status, Session::PortTestIpProtocol ip_protocol) { onPortTested(status, ip_protocol); });
|
||||
}
|
||||
|
||||
core_->port_test();
|
||||
core_->port_test(Session::PORT_TEST_IPV4);
|
||||
core_->port_test(Session::PORT_TEST_IPV6);
|
||||
|
||||
portTestSetSensitive();
|
||||
}
|
||||
|
||||
NetworkPage::NetworkPage(
|
||||
|
@ -945,6 +1027,7 @@ NetworkPage::NetworkPage(
|
|||
, portSpin_(init_spin_button("listening_port_spin", TR_KEY_peer_port, 1, std::numeric_limits<uint16_t>::max(), 1))
|
||||
{
|
||||
portButton_->signal_clicked().connect([this]() { onPortTest(); });
|
||||
updatePortStatusText();
|
||||
|
||||
prefsTag_ = core_->signal_prefs_changed().connect([this](auto key) { onCorePrefsChanged(key); });
|
||||
|
||||
|
|
|
@ -35,5 +35,5 @@ private:
|
|||
std::unique_ptr<Impl> const impl_;
|
||||
};
|
||||
|
||||
auto inline constexpr MAIN_WINDOW_REFRESH_INTERVAL_SECONDS = int{ 2 };
|
||||
auto inline constexpr SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS = int{ 2 };
|
||||
auto inline constexpr MAIN_WINDOW_REFRESH_INTERVAL_SECONDS = 2;
|
||||
auto inline constexpr SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS = 2;
|
||||
|
|
108
gtk/Session.cc
108
gtk/Session.cc
|
@ -18,7 +18,6 @@
|
|||
#include <libtransmission/log.h>
|
||||
#include <libtransmission/rpcimpl.h>
|
||||
#include <libtransmission/torrent-metainfo.h>
|
||||
#include <libtransmission/tr-assert.h>
|
||||
#include <libtransmission/utils.h> // tr_time()
|
||||
#include <libtransmission/variant.h>
|
||||
#include <libtransmission/web-utils.h> // tr_urlIsValid()
|
||||
|
@ -45,12 +44,14 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes> // PRId64
|
||||
#include <cstring> // strstr
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
@ -75,15 +76,18 @@ public:
|
|||
|
||||
size_t get_active_torrent_count() const;
|
||||
|
||||
bool get_port_test_pending(PortTestIpProtocol ip_protocol);
|
||||
void set_port_test_pending(bool pending, PortTestIpProtocol ip_protocol);
|
||||
|
||||
void update();
|
||||
void torrents_added();
|
||||
|
||||
void add_files(std::vector<Glib::RefPtr<Gio::File>> const& files, bool do_start, bool do_prompt, bool do_notify);
|
||||
int add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify);
|
||||
void add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify);
|
||||
void add_torrent(Glib::RefPtr<Torrent> const& torrent, bool do_notify);
|
||||
bool add_from_url(Glib::ustring const& url);
|
||||
|
||||
void send_rpc_request(tr_variant const* request, int64_t tag, std::function<void(tr_variant&)> const& response_func);
|
||||
void send_rpc_request(tr_variant const& request, int64_t tag, std::function<void(tr_variant&)> const& response_func);
|
||||
|
||||
void commit_prefs_change(tr_quark key);
|
||||
|
||||
|
@ -170,7 +174,7 @@ private:
|
|||
sigc::signal<void(bool)> signal_blocklist_updated_;
|
||||
sigc::signal<void(bool)> signal_busy_;
|
||||
sigc::signal<void(tr_quark)> signal_prefs_changed_;
|
||||
sigc::signal<void(bool)> signal_port_tested_;
|
||||
sigc::signal<void(std::optional<bool>, PortTestIpProtocol)> signal_port_tested_;
|
||||
sigc::signal<void(std::unordered_set<tr_torrent_id_t> const&, Torrent::ChangeFlags)> signal_torrents_changed_;
|
||||
|
||||
Glib::RefPtr<Gio::FileMonitor> monitor_;
|
||||
|
@ -183,6 +187,7 @@ private:
|
|||
bool inhibit_allowed_ = false;
|
||||
bool have_inhibit_cookie_ = false;
|
||||
bool dbus_error_ = false;
|
||||
std::array<bool, NUM_PORT_TEST_IP_PROTOCOL> port_test_pending_ = {};
|
||||
guint inhibit_cookie_ = 0;
|
||||
gint busy_count_ = 0;
|
||||
Glib::RefPtr<Gio::ListStore<Torrent>> raw_model_;
|
||||
|
@ -690,12 +695,12 @@ Glib::RefPtr<Torrent> Session::Impl::create_new_torrent(tr_ctor* ctor)
|
|||
return Torrent::create(tor);
|
||||
}
|
||||
|
||||
int Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
|
||||
void Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
|
||||
{
|
||||
auto const* metainfo = tr_ctorGetMetainfo(ctor);
|
||||
if (metainfo == nullptr)
|
||||
{
|
||||
return TR_PARSE_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tr_torrentFindFromMetainfo(get_session(), metainfo) != nullptr)
|
||||
|
@ -709,18 +714,17 @@ int Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
|
|||
}
|
||||
|
||||
tr_ctorFree(ctor);
|
||||
return TR_PARSE_DUPLICATE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!do_prompt)
|
||||
{
|
||||
add_torrent(create_new_torrent(ctor), do_notify);
|
||||
tr_ctorFree(ctor);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
signal_add_prompt_.emit(ctor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
|
@ -842,8 +846,7 @@ 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()))
|
||||
<< std::endl;
|
||||
std::cerr << fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", file->get_parse_name())) << '\n';
|
||||
}
|
||||
|
||||
return handled;
|
||||
|
@ -968,7 +971,7 @@ void Session::start_now(tr_torrent_id_t id)
|
|||
auto* args = tr_variantDictAddDict(&top, TR_KEY_arguments, 1);
|
||||
auto* ids = tr_variantDictAddList(args, TR_KEY_ids, 1);
|
||||
tr_variantListAddInt(ids, id);
|
||||
exec(&top);
|
||||
exec(top);
|
||||
}
|
||||
|
||||
void Session::Impl::update()
|
||||
|
@ -1187,19 +1190,16 @@ bool core_read_rpc_response_idle(tr_variant& response)
|
|||
return false;
|
||||
}
|
||||
|
||||
void core_read_rpc_response(tr_session* /*session*/, tr_variant* response, gpointer /*user_data*/)
|
||||
void core_read_rpc_response(tr_session* /*session*/, tr_variant&& response)
|
||||
{
|
||||
auto owned_response = std::make_shared<tr_variant>();
|
||||
*owned_response.get() = false;
|
||||
std::swap(*owned_response, *response);
|
||||
|
||||
auto owned_response = std::make_shared<tr_variant>(std::move(response));
|
||||
Glib::signal_idle().connect([owned_response]() mutable { return core_read_rpc_response_idle(*owned_response); });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Session::Impl::send_rpc_request(
|
||||
tr_variant const* request,
|
||||
tr_variant const& request,
|
||||
int64_t tag,
|
||||
std::function<void(tr_variant&)> const& response_func)
|
||||
{
|
||||
|
@ -1217,7 +1217,7 @@ void Session::Impl::send_rpc_request(
|
|||
gtr_message(fmt::format("request: [{}]", tr_variantToStr(request, TR_VARIANT_FMT_JSON_LEAN)));
|
||||
#endif
|
||||
|
||||
tr_rpc_request_exec_json(session_, request, core_read_rpc_response, nullptr);
|
||||
tr_rpc_request_exec(session_, request, core_read_rpc_response);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1225,33 +1225,67 @@ void Session::Impl::send_rpc_request(
|
|||
**** Sending a test-port request via RPC
|
||||
***/
|
||||
|
||||
void Session::port_test()
|
||||
void Session::port_test(PortTestIpProtocol const ip_protocol)
|
||||
{
|
||||
auto const tag = nextTag;
|
||||
++nextTag;
|
||||
static auto constexpr IpStr = std::array{ "ipv4"sv, "ipv6"sv };
|
||||
|
||||
if (port_test_pending(ip_protocol))
|
||||
{
|
||||
return;
|
||||
}
|
||||
impl_->set_port_test_pending(true, ip_protocol);
|
||||
|
||||
auto const tag = nextTag++;
|
||||
|
||||
auto arguments_map = tr_variant::Map{ 1U };
|
||||
arguments_map.try_emplace(TR_KEY_ipProtocol, tr_variant::unmanaged_string(IpStr[ip_protocol]));
|
||||
|
||||
auto request_map = tr_variant::Map{ 3U };
|
||||
request_map.try_emplace(TR_KEY_method, tr_variant::unmanaged_string("port-test"sv));
|
||||
request_map.try_emplace(TR_KEY_tag, tag);
|
||||
request_map.try_emplace(TR_KEY_arguments, std::move(arguments_map));
|
||||
|
||||
tr_variant request;
|
||||
tr_variantInitDict(&request, 2);
|
||||
tr_variantDictAddStrView(&request, TR_KEY_method, "port-test");
|
||||
tr_variantDictAddInt(&request, TR_KEY_tag, tag);
|
||||
impl_->send_rpc_request(
|
||||
&request,
|
||||
tr_variant{ std::move(request_map) },
|
||||
tag,
|
||||
[this](auto& response)
|
||||
[this, ip_protocol](tr_variant& response)
|
||||
{
|
||||
tr_variant* args = nullptr;
|
||||
bool is_open = false;
|
||||
impl_->set_port_test_pending(false, ip_protocol);
|
||||
|
||||
if (!tr_variantDictFindDict(&response, TR_KEY_arguments, &args) ||
|
||||
!tr_variantDictFindBool(args, TR_KEY_port_is_open, &is_open))
|
||||
auto status = std::optional<bool>{};
|
||||
if (tr_variant* args = nullptr; tr_variantDictFindDict(&response, TR_KEY_arguments, &args))
|
||||
{
|
||||
is_open = false;
|
||||
if (auto result = bool{}; tr_variantDictFindBool(args, TR_KEY_port_is_open, &result))
|
||||
{
|
||||
status = result;
|
||||
}
|
||||
}
|
||||
|
||||
impl_->signal_port_tested().emit(is_open);
|
||||
// If for whatever reason the status optional is empty here,
|
||||
// then something must have gone wrong with the port test,
|
||||
// so the UI should show the "error" state
|
||||
impl_->signal_port_tested().emit(status, ip_protocol);
|
||||
});
|
||||
}
|
||||
|
||||
bool Session::port_test_pending(Session::PortTestIpProtocol ip_protocol) const noexcept
|
||||
{
|
||||
return impl_->get_port_test_pending(ip_protocol);
|
||||
}
|
||||
|
||||
bool Session::Impl::get_port_test_pending(Session::PortTestIpProtocol ip_protocol)
|
||||
{
|
||||
return ip_protocol < NUM_PORT_TEST_IP_PROTOCOL && port_test_pending_[ip_protocol];
|
||||
}
|
||||
|
||||
void Session::Impl::set_port_test_pending(bool pending, Session::PortTestIpProtocol ip_protocol)
|
||||
{
|
||||
if (ip_protocol < NUM_PORT_TEST_IP_PROTOCOL)
|
||||
{
|
||||
port_test_pending_[ip_protocol] = pending;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
**** Updating a blocklist via RPC
|
||||
***/
|
||||
|
@ -1266,7 +1300,7 @@ void Session::blocklist_update()
|
|||
tr_variantDictAddStrView(&request, TR_KEY_method, "blocklist-update");
|
||||
tr_variantDictAddInt(&request, TR_KEY_tag, tag);
|
||||
impl_->send_rpc_request(
|
||||
&request,
|
||||
request,
|
||||
tag,
|
||||
[this](auto& response)
|
||||
{
|
||||
|
@ -1292,7 +1326,7 @@ void Session::blocklist_update()
|
|||
****
|
||||
***/
|
||||
|
||||
void Session::exec(tr_variant const* request)
|
||||
void Session::exec(tr_variant const& request)
|
||||
{
|
||||
auto const tag = nextTag;
|
||||
++nextTag;
|
||||
|
@ -1391,7 +1425,7 @@ sigc::signal<void(tr_quark)>& Session::signal_prefs_changed()
|
|||
return impl_->signal_prefs_changed();
|
||||
}
|
||||
|
||||
sigc::signal<void(bool)>& Session::signal_port_tested()
|
||||
sigc::signal<void(std::optional<bool>, Session::PortTestIpProtocol)>& Session::signal_port_tested()
|
||||
{
|
||||
return impl_->signal_port_tested();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#include <gtkmm/treemodel.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
@ -29,13 +31,20 @@
|
|||
class Session : public Glib::Object
|
||||
{
|
||||
public:
|
||||
enum ErrorCode
|
||||
enum ErrorCode : uint16_t
|
||||
{
|
||||
ERR_ADD_TORRENT_ERR = TR_PARSE_ERR,
|
||||
ERR_ADD_TORRENT_DUP = TR_PARSE_DUPLICATE,
|
||||
ERR_ADD_TORRENT_ERR = 1,
|
||||
ERR_ADD_TORRENT_DUP = 2,
|
||||
ERR_NO_MORE_TORRENTS = 1000 /* finished adding a batch */
|
||||
};
|
||||
|
||||
enum PortTestIpProtocol : uint8_t
|
||||
{
|
||||
PORT_TEST_IPV4,
|
||||
PORT_TEST_IPV6,
|
||||
NUM_PORT_TEST_IP_PROTOCOL // Must always be the last value
|
||||
};
|
||||
|
||||
using Model = IF_GTKMM4(Gio::ListModel, Gtk::TreeModel);
|
||||
|
||||
public:
|
||||
|
@ -127,11 +136,12 @@ public:
|
|||
***
|
||||
**/
|
||||
|
||||
void port_test();
|
||||
void port_test(PortTestIpProtocol ip_protocol);
|
||||
bool port_test_pending(PortTestIpProtocol ip_protocol) const noexcept;
|
||||
|
||||
void blocklist_update();
|
||||
|
||||
void exec(tr_variant const* request);
|
||||
void exec(tr_variant const& request);
|
||||
|
||||
void open_folder(tr_torrent_id_t torrent_id) const;
|
||||
|
||||
|
@ -140,7 +150,7 @@ public:
|
|||
sigc::signal<void(bool)>& signal_blocklist_updated();
|
||||
sigc::signal<void(bool)>& signal_busy();
|
||||
sigc::signal<void(tr_quark)>& signal_prefs_changed();
|
||||
sigc::signal<void(bool)>& signal_port_tested();
|
||||
sigc::signal<void(std::optional<bool>, PortTestIpProtocol)>& signal_port_tested();
|
||||
sigc::signal<void(std::unordered_set<tr_torrent_id_t> const&, Torrent::ChangeFlags)>& signal_torrents_changed();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -20,7 +20,7 @@ class SorterBase : public IF_GTKMM4(Gtk::Sorter, Glib::Object)
|
|||
{
|
||||
public:
|
||||
#if !GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
enum class Change{
|
||||
enum class Change : uint8_t{
|
||||
DIFFERENT,
|
||||
INVERTED,
|
||||
LESS_STRICT,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
static auto constexpr TR_RESPONSE_RESET = int{ 1 };
|
||||
static auto constexpr TR_RESPONSE_RESET = 1;
|
||||
|
||||
class StatsDialog::Impl
|
||||
{
|
||||
|
|
|
@ -140,15 +140,15 @@ public:
|
|||
|
||||
std::string_view mime_type;
|
||||
|
||||
Storage have_unchecked = {};
|
||||
Storage have_valid = {};
|
||||
Storage left_until_done = {};
|
||||
Storage size_when_done = {};
|
||||
Storage total_size = {};
|
||||
Storage uploaded_ever = {};
|
||||
Storage have_unchecked;
|
||||
Storage have_valid;
|
||||
Storage left_until_done;
|
||||
Storage size_when_done;
|
||||
Storage total_size;
|
||||
Storage uploaded_ever;
|
||||
|
||||
Speed speed_down = {};
|
||||
Speed speed_up = {};
|
||||
Speed speed_down;
|
||||
Speed speed_up;
|
||||
|
||||
size_t queue_position = {};
|
||||
|
||||
|
@ -163,12 +163,12 @@ public:
|
|||
int active_peers_up = {};
|
||||
int error_code = {};
|
||||
|
||||
Percents activity_percent_done = {};
|
||||
Percents metadata_percent_complete = {};
|
||||
Percents percent_complete = {};
|
||||
Percents percent_done = {};
|
||||
Percents recheck_progress = {};
|
||||
Percents seed_ratio_percent_done = {};
|
||||
Percents activity_percent_done;
|
||||
Percents metadata_percent_complete;
|
||||
Percents percent_complete;
|
||||
Percents percent_done;
|
||||
Percents recheck_progress;
|
||||
Percents seed_ratio_percent_done;
|
||||
|
||||
uint16_t peers_connected = {};
|
||||
uint16_t peers_getting_from_us = {};
|
||||
|
@ -442,7 +442,7 @@ Glib::ustring Torrent::Impl::get_short_status_text() const
|
|||
case TR_STATUS_DOWNLOAD:
|
||||
case TR_STATUS_SEED:
|
||||
return fmt::format(
|
||||
FMT_STRING("{:s} {:s}"),
|
||||
"{:s} {:s}",
|
||||
get_short_transfer_text(),
|
||||
fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(cache_.ratio))));
|
||||
|
||||
|
@ -548,7 +548,7 @@ Glib::ustring Torrent::Impl::get_long_status_text() const
|
|||
default:
|
||||
if (auto const buf = get_short_transfer_text(); !std::empty(buf))
|
||||
{
|
||||
status_str += fmt::format(FMT_STRING(" - {:s}"), buf);
|
||||
status_str += fmt::format(" - {:s}", buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
|
@ -38,7 +39,7 @@ public:
|
|||
Gtk::TreeModelColumn<Glib::ustring> name_collated;
|
||||
};
|
||||
|
||||
enum class ChangeFlag
|
||||
enum class ChangeFlag : uint8_t
|
||||
{
|
||||
ACTIVE_PEER_COUNT,
|
||||
ACTIVE_PEERS_DOWN,
|
||||
|
|
|
@ -36,16 +36,10 @@
|
|||
#include <cstring> // strchr()
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
/* #define TEST_RTL */
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -240,7 +234,7 @@ void set_error_color(
|
|||
Gtk::Widget& widget,
|
||||
Gtk::CellRendererState flags)
|
||||
{
|
||||
static auto const error_color_name = Glib::ustring("tr_error_color"s);
|
||||
static auto const error_color_name = Glib::ustring{ "tr_error_color" };
|
||||
|
||||
auto color = Gdk::RGBA();
|
||||
if (torrent.get_error_code() != 0 && (flags & TR_GTK_CELL_RENDERER_STATE(SELECTED)) == Gtk::CellRendererState{} &&
|
||||
|
@ -256,9 +250,9 @@ void set_error_color(
|
|||
|
||||
std::optional<Gdk::RGBA> get_progress_bar_color(Torrent const& torrent, Gtk::Widget const& widget)
|
||||
{
|
||||
static auto const down_color_name = Glib::ustring("tr_transfer_down_color"s);
|
||||
static auto const up_color_name = Glib::ustring("tr_transfer_up_color"s);
|
||||
static auto const idle_color_name = Glib::ustring("tr_transfer_idle_color"s);
|
||||
static auto const down_color_name = Glib::ustring{ "tr_transfer_down_color" };
|
||||
static auto const up_color_name = Glib::ustring{ "tr_transfer_up_color" };
|
||||
static auto const idle_color_name = Glib::ustring{ "tr_transfer_idle_color" };
|
||||
|
||||
auto const* color_name = &idle_color_name;
|
||||
switch (torrent.get_activity())
|
||||
|
@ -420,7 +414,7 @@ void TorrentCellRenderer::Impl::render_compact(
|
|||
icon_renderer_->render(context, widget, icon_area, icon_area, flags);
|
||||
|
||||
progress_renderer_->property_value() = percent_done;
|
||||
progress_renderer_->property_text() = fmt::format(FMT_STRING("{:d}%"), percent_done);
|
||||
progress_renderer_->property_text() = fmt::format("{:d}%", percent_done);
|
||||
progress_renderer_->property_sensitive() = sensitive;
|
||||
render_progress_bar(context, widget, prog_area, flags, progress_color);
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
TorrentFilter::TorrentFilter()
|
||||
: Glib::ObjectBase(typeid(TorrentFilter))
|
||||
{
|
||||
|
@ -127,7 +130,7 @@ void TorrentFilter::update(Torrent::ChangeFlags changes)
|
|||
|
||||
if (activity_type_ != Activity::ALL)
|
||||
{
|
||||
static auto const activity_flags = std::map<Activity, Torrent::ChangeFlags>({
|
||||
static constexpr auto ActivityFlags = std::array<std::pair<Activity, Torrent::ChangeFlags>, 7U>{ {
|
||||
{ Activity::DOWNLOADING, Flag::ACTIVITY },
|
||||
{ Activity::SEEDING, Flag::ACTIVITY },
|
||||
{ Activity::ACTIVE, Flag::ACTIVE_PEER_COUNT | Flag::ACTIVITY },
|
||||
|
@ -135,10 +138,13 @@ void TorrentFilter::update(Torrent::ChangeFlags changes)
|
|||
{ Activity::FINISHED, Flag::FINISHED },
|
||||
{ Activity::VERIFYING, Flag::ACTIVITY },
|
||||
{ Activity::ERROR, Flag::ERROR_CODE },
|
||||
});
|
||||
} };
|
||||
|
||||
auto const activity_flags_it = activity_flags.find(activity_type_);
|
||||
refilter_needed = activity_flags_it != activity_flags.end() && changes.test(activity_flags_it->second);
|
||||
auto const iter = std::find_if(
|
||||
std::begin(ActivityFlags),
|
||||
std::end(ActivityFlags),
|
||||
[key = activity_type_](auto const& row) { return row.first == key; });
|
||||
refilter_needed = iter != std::end(ActivityFlags) && changes.test(iter->second);
|
||||
}
|
||||
|
||||
if (!refilter_needed)
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
#include <glibmm/refptr.h>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class TorrentFilter : public FilterBase<Torrent>
|
||||
{
|
||||
public:
|
||||
enum class Activity
|
||||
enum class Activity : int8_t
|
||||
{
|
||||
ALL,
|
||||
DOWNLOADING,
|
||||
|
@ -26,7 +28,7 @@ public:
|
|||
ERROR,
|
||||
};
|
||||
|
||||
enum class Tracker
|
||||
enum class Tracker : int8_t
|
||||
{
|
||||
ALL,
|
||||
HOST,
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <libtransmission/utils.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
|
@ -178,7 +180,8 @@ TorrentSorter::TorrentSorter()
|
|||
|
||||
void TorrentSorter::set_mode(std::string_view mode)
|
||||
{
|
||||
static auto const compare_funcs = std::map<std::string_view, CompareFunc>({
|
||||
static auto constexpr DefaultCompareFunc = &compare_by_name;
|
||||
static auto constexpr CompareFuncs = std::array<std::pair<std::string_view, CompareFunc>, 9U>{ {
|
||||
{ "sort-by-activity"sv, &compare_by_activity },
|
||||
{ "sort-by-age"sv, &compare_by_age },
|
||||
{ "sort-by-name"sv, &compare_by_name },
|
||||
|
@ -188,14 +191,13 @@ void TorrentSorter::set_mode(std::string_view mode)
|
|||
{ "sort-by-size"sv, &compare_by_size },
|
||||
{ "sort-by-state"sv, &compare_by_state },
|
||||
{ "sort-by-time-left"sv, &compare_by_eta },
|
||||
});
|
||||
|
||||
auto compare_func = &compare_by_name;
|
||||
if (auto const compare_func_it = compare_funcs.find(mode); compare_func_it != compare_funcs.end())
|
||||
{
|
||||
compare_func = compare_func_it->second;
|
||||
}
|
||||
} };
|
||||
|
||||
auto const iter = std::find_if(
|
||||
std::begin(CompareFuncs),
|
||||
std::end(CompareFuncs),
|
||||
[key = mode](auto const& row) { return row.first == key; });
|
||||
auto const compare_func = iter != std::end(CompareFuncs) ? iter->second : DefaultCompareFunc;
|
||||
if (compare_func_ == compare_func)
|
||||
{
|
||||
return;
|
||||
|
@ -224,8 +226,7 @@ int TorrentSorter::compare(Torrent const& lhs, Torrent const& rhs) const
|
|||
void TorrentSorter::update(Torrent::ChangeFlags changes)
|
||||
{
|
||||
using Flag = Torrent::ChangeFlag;
|
||||
|
||||
static auto const compare_flags = std::map<CompareFunc, Torrent::ChangeFlags>({
|
||||
static auto constexpr CompareFlags = std::array<std::pair<CompareFunc, Torrent::ChangeFlags>, 9U>{ {
|
||||
{ &compare_by_activity, Flag::ACTIVE_PEER_COUNT | Flag::QUEUE_POSITION | Flag::SPEED_DOWN | Flag::SPEED_UP },
|
||||
{ &compare_by_age, Flag::ADDED_DATE | Flag::NAME },
|
||||
{ &compare_by_eta, Flag::ETA | Flag::NAME },
|
||||
|
@ -235,10 +236,13 @@ void TorrentSorter::update(Torrent::ChangeFlags changes)
|
|||
{ &compare_by_ratio, Flag::QUEUE_POSITION | Flag::RATIO },
|
||||
{ &compare_by_size, Flag::NAME | Flag::TOTAL_SIZE },
|
||||
{ &compare_by_state, Flag::ACTIVITY | Flag::QUEUE_POSITION },
|
||||
});
|
||||
} };
|
||||
|
||||
if (auto const compare_flags_it = compare_flags.find(compare_func_);
|
||||
compare_flags_it != compare_flags.end() && changes.test(compare_flags_it->second))
|
||||
if (auto const iter = std::find_if(
|
||||
std::begin(CompareFlags),
|
||||
std::end(CompareFlags),
|
||||
[key = compare_func_](auto const& row) { return row.first == key; });
|
||||
iter != std::end(CompareFlags) && changes.test(iter->second))
|
||||
{
|
||||
changed(Change::DIFFERENT);
|
||||
}
|
||||
|
|
19
gtk/Utils.cc
19
gtk/Utils.cc
|
@ -26,8 +26,6 @@
|
|||
#include <glibmm/quark.h>
|
||||
#include <glibmm/spawn.h>
|
||||
#include <gtkmm/cellrenderertext.h>
|
||||
#include <gtkmm/eventcontroller.h>
|
||||
#include <gtkmm/gesture.h>
|
||||
#include <gtkmm/liststore.h>
|
||||
#include <gtkmm/messagedialog.h>
|
||||
#include <gtkmm/treemodel.h>
|
||||
|
@ -35,6 +33,8 @@
|
|||
|
||||
#if GTKMM_CHECK_VERSION(4, 0, 0)
|
||||
#include <gdkmm/clipboard.h>
|
||||
#include <gtkmm/eventcontroller.h>
|
||||
#include <gtkmm/gesture.h>
|
||||
#include <gtkmm/gestureclick.h>
|
||||
#else
|
||||
#include <gdkmm/window.h>
|
||||
|
@ -45,7 +45,6 @@
|
|||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
@ -54,6 +53,8 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
#if GTK_CHECK_VERSION(4, 0, 0) && defined(GDK_WINDOWING_X11)
|
||||
#include <optional>
|
||||
|
||||
#include <gdk/x11/gdkx.h>
|
||||
#endif
|
||||
|
||||
|
@ -645,9 +646,7 @@ void gtr_open_uri(Glib::ustring const& uri)
|
|||
}
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -735,9 +734,7 @@ void gtr_priority_combo_init(Gtk::ComboBox& combo)
|
|||
});
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
void gtr_widget_set_visible(Gtk::Widget& widget, bool is_visible)
|
||||
{
|
||||
|
@ -847,9 +844,7 @@ void gtr_window_raise([[maybe_unused]] Gtk::Window& window)
|
|||
#endif
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
// ---
|
||||
|
||||
void gtr_unrecognized_url_dialog(Gtk::Widget& parent, Glib::ustring const& url)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
@ -52,7 +53,7 @@ void gtr_error(std::string const& message);
|
|||
****
|
||||
***/
|
||||
|
||||
enum class GtrUnicode
|
||||
enum class GtrUnicode : uint8_t
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
|
@ -260,7 +261,7 @@ T* gtr_get_widget_derived(Glib::RefPtr<Gtk::Builder> const& builder, Glib::ustri
|
|||
template<typename F>
|
||||
void gtr_window_on_close(Gtk::Window& widget, F&& callback)
|
||||
{
|
||||
auto bool_callback = [callback = std::move(callback)]() mutable -> bool
|
||||
auto bool_callback = [callback = std::forward<F>(callback)]() mutable -> bool
|
||||
{
|
||||
if constexpr (std::is_same_v<void, std::invoke_result_t<decltype(callback)>>)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,10 @@ Copyright 2017 Endless Mobile, Inc.
|
|||
<project_license>GPL-2.0 OR GPL-3.0</project_license>
|
||||
|
||||
<name>Transmission</name>
|
||||
<developer_name>The Transmission Project</developer_name>
|
||||
<developer id="transmissionbt.com">
|
||||
<name>The Transmission Project</name>
|
||||
</developer>
|
||||
<summary>Download and share files over BitTorrent</summary>
|
||||
|
||||
<description>
|
||||
|
@ -40,6 +44,7 @@ Copyright 2017 Endless Mobile, Inc.
|
|||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image type="source">https://raw.githubusercontent.com/transmission/transmission/main/gtk/screenshots/a.png</image>
|
||||
<caption>Main window, showing an ongoing download of Fedora Workstation</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<update_contact>info_AT_transmissionbt.com</update_contact>
|
||||
|
@ -47,6 +52,8 @@ Copyright 2017 Endless Mobile, Inc.
|
|||
<content_attribute id="social-info">mild</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release date="2023-12-06" version="4.0.5" />
|
||||
<release date="2023-08-23" version="4.0.4" />
|
||||
<release date="2023-04-14" version="4.0.3" />
|
||||
<release date="2023-03-16" version="4.0.2" />
|
||||
<release date="2023-02-23" version="4.0.1" />
|
||||
|
@ -54,4 +61,5 @@ Copyright 2017 Endless Mobile, Inc.
|
|||
<release date="2020-05-03" version="3.00" />
|
||||
</releases>
|
||||
<translation type="gettext">transmission-gtk</translation>
|
||||
<launchable type="desktop-id">transmission-gtk.desktop</launchable>
|
||||
</component>
|
||||
|
|
|
@ -1245,14 +1245,18 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="listening_port_spin">
|
||||
<object class="GtkLabel" id="listening_port_status_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">...</property>
|
||||
<property name="xalign">0</property>
|
||||
<attributes>
|
||||
<attribute name="style" value="italic"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -1262,14 +1266,10 @@
|
|||
<property name="hexpand">True</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="listening_port_status_label">
|
||||
<object class="GtkSpinButton" id="listening_port_spin">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Status unknown</property>
|
||||
<property name="xalign">0</property>
|
||||
<attributes>
|
||||
<attribute name="style" value="italic"/>
|
||||
</attributes>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -1295,7 +1295,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -868,12 +868,16 @@
|
|||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="listening_port_spin">
|
||||
<property name="focusable">1</property>
|
||||
<object class="GtkLabel" id="listening_port_status_label">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="label">...</property>
|
||||
<property name="xalign">0</property>
|
||||
<attributes>
|
||||
<attribute name="style" value="italic"/>
|
||||
</attributes>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">0</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
|
@ -882,13 +886,13 @@
|
|||
<property name="hexpand">1</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="listening_port_status_label">
|
||||
<object class="GtkSpinButton" id="listening_port_spin">
|
||||
<property name="focusable">1</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="label" translatable="1">Status unknown</property>
|
||||
<property name="xalign">0</property>
|
||||
<attributes>
|
||||
<attribute name="style" value="italic"></attribute>
|
||||
</attributes>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -901,7 +905,7 @@
|
|||
</child>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">1</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
|
|
|
@ -9,6 +9,7 @@ Checks: >
|
|||
cert-*,
|
||||
-cert-err58-cpp,
|
||||
clang-analyzer-*,
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||
cppcoreguidelines-avoid-do-while,
|
||||
cppcoreguidelines-avoid-goto,
|
||||
cppcoreguidelines-avoid-reference-coroutine-parameters,
|
||||
|
@ -23,11 +24,13 @@ Checks: >
|
|||
cppcoreguidelines-virtual-class-destructor,
|
||||
google-explicit-constructor,
|
||||
misc-*,
|
||||
-misc-include-cleaner,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
-performance-move-const-arg,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-readability-function-cognitive-complexity,
|
||||
|
|
|
@ -12,7 +12,7 @@ check_symbol_exists(SO_REUSEPORT "sys/types.h;sys/socket.h" HAVE_SO_REUSEPORT)
|
|||
|
||||
add_compile_options(
|
||||
# equivalent of XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES for this directory
|
||||
$<$<AND:$<BOOL:${APPLE}>,$<CXX_COMPILER_ID:AppleClang,Clang>,$<COMPILE_LANGUAGE:C,CXX>>:-fobjc-arc>)
|
||||
$<$<AND:$<BOOL:${APPLE}>,$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>,$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:CXX>>>:-fobjc-arc>)
|
||||
|
||||
add_library(${TR_NAME} STATIC)
|
||||
|
||||
|
@ -113,11 +113,12 @@ target_sources(${TR_NAME}
|
|||
session-alt-speeds.h
|
||||
session-id.cc
|
||||
session-id.h
|
||||
session-settings.cc
|
||||
session-thread.cc
|
||||
session-thread.h
|
||||
session.cc
|
||||
session.h
|
||||
settings.cc
|
||||
settings.h
|
||||
stats.cc
|
||||
stats.h
|
||||
subprocess-posix.cc
|
||||
|
@ -140,7 +141,6 @@ target_sources(${TR_NAME}
|
|||
torrents.h
|
||||
tr-assert.cc
|
||||
tr-assert.h
|
||||
tr-assert.mm
|
||||
tr-buffer.h
|
||||
tr-dht.cc
|
||||
tr-dht.h
|
||||
|
@ -160,7 +160,6 @@ target_sources(${TR_NAME}
|
|||
utils.h
|
||||
utils.mm
|
||||
variant-benc.cc
|
||||
variant-converters.cc
|
||||
variant-json.cc
|
||||
variant.cc
|
||||
variant.h
|
||||
|
@ -205,10 +204,7 @@ tr_allow_compile_if(
|
|||
[=[[WITH_KQUEUE]]=]
|
||||
watchdir-kqueue.cc
|
||||
[=[[APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"]]=]
|
||||
tr-assert.mm
|
||||
utils.mm
|
||||
[=[[NOT (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")]]=]
|
||||
tr-assert.cc
|
||||
[=[[WIN32]]=]
|
||||
file-win32.cc
|
||||
subprocess-win32.cc
|
||||
|
@ -230,6 +226,10 @@ target_compile_definitions(${TR_NAME}
|
|||
$<$<BOOL:${USE_SYSTEM_B64}>:USE_SYSTEM_B64>
|
||||
$<$<BOOL:${HAVE_SO_REUSEPORT}>:HAVE_SO_REUSEPORT=1>
|
||||
PUBLIC
|
||||
$<$<STREQUAL:${CRYPTO_PKG},ccrypto>:WITH_CCRYPTO>
|
||||
$<$<STREQUAL:${CRYPTO_PKG},mbedtls>:WITH_MBEDTLS>
|
||||
$<$<STREQUAL:${CRYPTO_PKG},openssl>:WITH_OPENSSL>
|
||||
$<$<STREQUAL:${CRYPTO_PKG},wolfssl>:WITH_WOLFSSL>
|
||||
$<$<NOT:$<BOOL:${ENABLE_NLS}>>:DISABLE_GETTEXT>)
|
||||
|
||||
tr_target_compile_definitions_for_headers(${TR_NAME}
|
||||
|
@ -271,11 +271,14 @@ target_include_directories(${TR_NAME}
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_BINARY_DIR}/..)
|
||||
|
||||
if(ANDROID)
|
||||
find_library(log-lib log)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TR_NAME}
|
||||
PRIVATE
|
||||
Threads::Threads
|
||||
deflate::deflate
|
||||
transmission::crypto_impl
|
||||
CURL::libcurl
|
||||
FastFloat::fast_float
|
||||
psl::psl
|
||||
|
@ -295,7 +298,9 @@ target_link_libraries(${TR_NAME}
|
|||
$<$<BOOL:${WIN32}>:crypt32>
|
||||
$<$<BOOL:${WIN32}>:shlwapi>
|
||||
"$<$<BOOL:${APPLE}>:-framework Foundation>"
|
||||
"$<$<BOOL:${ANDROID}>:${log-lib}>"
|
||||
PUBLIC
|
||||
transmission::crypto_impl
|
||||
fmt::fmt-header-only
|
||||
small::small
|
||||
libevent::event)
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <small/map.hpp>
|
||||
|
||||
#include "libtransmission/transmission.h"
|
||||
|
@ -78,23 +76,31 @@ bool tr_announce_list::replace(tr_tracker_id_t id, std::string_view announce_url
|
|||
return add(announce_url_sv, tier);
|
||||
}
|
||||
|
||||
bool tr_announce_list::add(std::string_view announce_url_sv, tr_tracker_tier_t tier)
|
||||
bool tr_announce_list::add(std::string_view announce_url, tr_tracker_tier_t tier)
|
||||
{
|
||||
auto const announce = tr_urlParseTracker(announce_url_sv);
|
||||
if (!announce || !can_add(*announce))
|
||||
// Make sure the announce URL is usable before we intern it.
|
||||
if (auto const announce = tr_urlParseTracker(announce_url); !announce || !can_add(*announce))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse again with the interned string so that `parsed` fields all
|
||||
// point to the interned addresses. This second call should never
|
||||
// fail, but check anyway to make the linter happy.
|
||||
auto const announce_interned = tr_interned_string{ announce_url };
|
||||
auto const parsed = tr_urlParseTracker(announce_interned.sv());
|
||||
if (!parsed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tracker = tracker_info{};
|
||||
tracker.announce = announce_url_sv;
|
||||
tracker.tier = get_tier(tier, *announce);
|
||||
tracker.announce = announce_interned;
|
||||
tracker.announce_parsed = *parsed;
|
||||
tracker.tier = get_tier(tier, *parsed);
|
||||
tracker.id = next_unique_id();
|
||||
tracker.host_and_port = fmt::format("{:s}:{:d}", announce->host, announce->port);
|
||||
tracker.sitename = announce->sitename;
|
||||
tracker.query = announce->query;
|
||||
|
||||
if (auto const scrape_str = announce_to_scrape(announce_url_sv); scrape_str)
|
||||
if (auto const scrape_str = announce_to_scrape(tracker.announce.sv()); scrape_str)
|
||||
{
|
||||
tracker.scrape = *scrape_str;
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
#include "libtransmission/transmission.h"
|
||||
|
||||
#include "libtransmission/interned-string.h"
|
||||
#include "libtransmission/tr-macros.h"
|
||||
#include "libtransmission/tr-macros.h" // TR_CONSTEXPR20
|
||||
#include "libtransmission/variant.h"
|
||||
#include "libtransmission/web-utils.h"
|
||||
|
||||
struct tr_error;
|
||||
struct tr_url_parsed_t;
|
||||
|
@ -27,9 +28,7 @@ public:
|
|||
{
|
||||
tr_interned_string announce;
|
||||
tr_interned_string scrape;
|
||||
tr_interned_string host_and_port; // 'example.org:80'
|
||||
tr_interned_string sitename; // 'example'
|
||||
tr_interned_string query; // 'name=ferret'
|
||||
tr_url_parsed_t announce_parsed;
|
||||
tr_tracker_tier_t tier = 0;
|
||||
tr_tracker_id_t id = 0;
|
||||
|
||||
|
@ -102,12 +101,12 @@ public:
|
|||
|
||||
void add_to_map(tr_variant::Map& setme) const;
|
||||
|
||||
bool add(std::string_view announce_url_sv)
|
||||
bool add(std::string_view announce_url)
|
||||
{
|
||||
return add(announce_url_sv, this->nextTier());
|
||||
return add(announce_url, this->nextTier());
|
||||
}
|
||||
|
||||
bool add(std::string_view announce_url_sv, tr_tracker_tier_t tier);
|
||||
bool add(std::string_view announce_url, tr_tracker_tier_t tier);
|
||||
void add(tr_announce_list const& src);
|
||||
bool remove(std::string_view announce_url);
|
||||
bool remove(tr_tracker_id_t id);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "libtransmission/interned-string.h"
|
||||
#include "libtransmission/net.h"
|
||||
#include "libtransmission/peer-mgr.h" // tr_pex
|
||||
#include "libtransmission/tr-macros.h" // tr_peer_id_t
|
||||
|
||||
struct tr_url_parsed_t;
|
||||
|
||||
|
@ -34,8 +35,6 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
|
||||
void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view benc, std::string_view log_name);
|
||||
|
||||
tr_interned_string tr_announcerGetKey(tr_url_parsed_t const& parsed);
|
||||
|
||||
[[nodiscard]] constexpr std::string_view tr_announce_event_get_string(tr_announce_event e)
|
||||
{
|
||||
switch (e)
|
||||
|
|
|
@ -46,14 +46,13 @@ namespace
|
|||
{
|
||||
void verboseLog(std::string_view description, tr_direction direction, std::string_view message)
|
||||
{
|
||||
auto& out = std::cerr;
|
||||
static bool const verbose = tr_env_key_exists("TR_CURL_VERBOSE");
|
||||
if (!verbose)
|
||||
if (static bool const verbose = tr_env_key_exists("TR_CURL_VERBOSE"); !verbose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const direction_sv = direction == TR_DOWN ? "<< "sv : ">> "sv;
|
||||
auto& out = std::cerr;
|
||||
out << description << '\n' << "[raw]"sv << direction_sv;
|
||||
for (unsigned char const ch : message)
|
||||
{
|
||||
|
@ -319,7 +318,7 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
tr_announce_response& response_;
|
||||
std::string_view const log_name_;
|
||||
std::optional<size_t> row_;
|
||||
tr_pex pex_ = {};
|
||||
tr_pex pex_;
|
||||
|
||||
explicit AnnounceHandler(tr_announce_response& response, std::string_view log_name)
|
||||
: response_{ response }
|
||||
|
@ -340,7 +339,7 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
{
|
||||
BasicHandler::EndDict(context);
|
||||
|
||||
if (pex_.is_valid_for_peers())
|
||||
if (pex_.is_valid())
|
||||
{
|
||||
response_.pex.push_back(pex_);
|
||||
pex_ = {};
|
||||
|
@ -480,7 +479,7 @@ public:
|
|||
|
||||
private:
|
||||
tr_scrape_response response_ = {};
|
||||
tr_scrape_response_func response_func_ = {};
|
||||
tr_scrape_response_func response_func_;
|
||||
std::string log_name_;
|
||||
};
|
||||
|
||||
|
|
|
@ -43,12 +43,13 @@
|
|||
#include "libtransmission/peer-mgr.h" // for tr_pex::fromCompact4()
|
||||
#include "libtransmission/tr-assert.h"
|
||||
#include "libtransmission/tr-buffer.h"
|
||||
#include "libtransmission/tr-strbuf.h"
|
||||
#include "libtransmission/utils.h"
|
||||
#include "libtransmission/web-utils.h"
|
||||
|
||||
#define logwarn(interned, msg) tr_logAddWarn(msg, (interned).sv())
|
||||
#define logdbg(interned, msg) tr_logAddDebug(msg, (interned).sv())
|
||||
#define logtrace(interned, msg) tr_logAddTrace(msg, (interned).sv())
|
||||
#define logwarn(name, msg) tr_logAddWarn(msg, name)
|
||||
#define logdbg(name, msg) tr_logAddDebug(msg, name)
|
||||
#define logtrace(name, msg) tr_logAddTrace(msg, name)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -69,7 +70,7 @@ auto tau_transaction_new()
|
|||
}
|
||||
|
||||
// used in the "action" field of a request. Values defined in bep 15.
|
||||
enum tau_action_t
|
||||
enum tau_action_t : uint8_t
|
||||
{
|
||||
TAU_ACTION_CONNECT = 0,
|
||||
TAU_ACTION_ANNOUNCE = 1,
|
||||
|
@ -253,7 +254,7 @@ struct tau_announce_request
|
|||
return created_at_ + TR_ANNOUNCE_TIMEOUT_SEC.count();
|
||||
}
|
||||
|
||||
enum tau_announce_event
|
||||
enum tau_announce_event : uint8_t
|
||||
{
|
||||
// Used in the "event" field of an announce request.
|
||||
// These values come from BEP 15
|
||||
|
@ -300,9 +301,13 @@ struct tau_tracker
|
|||
{
|
||||
using Mediator = tr_announcer_udp::Mediator;
|
||||
|
||||
tau_tracker(Mediator& mediator, tr_interned_string key_in, tr_interned_string host_in, tr_port port_in)
|
||||
: key{ key_in }
|
||||
, host{ host_in }
|
||||
tau_tracker(
|
||||
Mediator& mediator,
|
||||
std::string_view const interned_authority,
|
||||
std::string_view const interned_host,
|
||||
tr_port const port_in)
|
||||
: authority{ interned_authority }
|
||||
, host{ interned_host }
|
||||
, port{ port_in }
|
||||
, mediator_{ mediator }
|
||||
{
|
||||
|
@ -329,13 +334,13 @@ struct tau_tracker
|
|||
{
|
||||
this->connection_id = buf.to_uint64();
|
||||
this->connection_expiration_time = tr_time() + TauConnectionTtlSecs;
|
||||
logdbg(this->key, fmt::format("Got a new connection ID from tracker: {}", this->connection_id));
|
||||
logdbg(log_name(), fmt::format("Got a new connection ID from tracker: {}", this->connection_id));
|
||||
}
|
||||
else if (action == TAU_ACTION_ERROR)
|
||||
{
|
||||
std::string errmsg = !std::empty(buf) ? buf.to_string() : _("Connection failed");
|
||||
this->failAll(true, false, errmsg);
|
||||
logdbg(this->key, std::move(errmsg));
|
||||
logdbg(log_name(), std::move(errmsg));
|
||||
}
|
||||
|
||||
this->upkeep();
|
||||
|
@ -363,12 +368,12 @@ struct tau_tracker
|
|||
if (!addr_pending_dns_ && addr_expires_at_ <= now)
|
||||
{
|
||||
addr_.reset();
|
||||
addr_pending_dns_ = std::async(std::launch::async, lookup, this->host, this->port, this->key);
|
||||
addr_pending_dns_ = std::async(std::launch::async, lookup, this->log_name(), this->host, this->port);
|
||||
return;
|
||||
}
|
||||
|
||||
logtrace(
|
||||
this->key,
|
||||
log_name(),
|
||||
fmt::format(
|
||||
"connected {} ({} {}) -- connecting_at {}",
|
||||
is_connected(now),
|
||||
|
@ -381,7 +386,7 @@ struct tau_tracker
|
|||
{
|
||||
this->connecting_at = now;
|
||||
this->connection_transaction_id = tau_transaction_new();
|
||||
logtrace(this->key, fmt::format("Trying to connect. Transaction ID is {}", this->connection_transaction_id));
|
||||
logtrace(log_name(), fmt::format("Trying to connect. Transaction ID is {}", this->connection_transaction_id));
|
||||
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint64(0x41727101980LL);
|
||||
|
@ -416,7 +421,10 @@ private:
|
|||
return connection_id != tau_connection_t{} && now < connection_expiration_time;
|
||||
}
|
||||
|
||||
[[nodiscard]] static MaybeSockaddr lookup(tr_interned_string host, tr_port port, tr_interned_string logname)
|
||||
[[nodiscard]] static MaybeSockaddr lookup(
|
||||
std::string_view const interned_log_name,
|
||||
std::string_view const interned_host,
|
||||
tr_port const port)
|
||||
{
|
||||
auto szport = std::array<char, 16>{};
|
||||
*fmt::format_to(std::data(szport), "{:d}", port.host()) = '\0';
|
||||
|
@ -427,13 +435,14 @@ private:
|
|||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
addrinfo* info = nullptr;
|
||||
if (int const rc = getaddrinfo(host.c_str(), std::data(szport), &hints, &info); rc != 0)
|
||||
auto const szhost = tr_pathbuf{ interned_host };
|
||||
if (int const rc = getaddrinfo(szhost.c_str(), std::data(szport), &hints, &info); rc != 0)
|
||||
{
|
||||
logwarn(
|
||||
logname,
|
||||
interned_log_name,
|
||||
fmt::format(
|
||||
_("Couldn't look up '{address}:{port}': {error} ({error_code})"),
|
||||
fmt::arg("address", host.sv()),
|
||||
fmt::arg("address", interned_host),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("error", gai_strerror(rc)),
|
||||
fmt::arg("error_code", static_cast<int>(rc))));
|
||||
|
@ -445,7 +454,7 @@ private:
|
|||
memcpy(&ss, info->ai_addr, len);
|
||||
freeaddrinfo(info);
|
||||
|
||||
logdbg(logname, "DNS lookup succeeded");
|
||||
logdbg(interned_log_name, "DNS lookup succeeded");
|
||||
return std::make_pair(ss, len);
|
||||
}
|
||||
|
||||
|
@ -486,7 +495,7 @@ private:
|
|||
{
|
||||
if (auto& req = *it; req.expiresAt() <= now)
|
||||
{
|
||||
logtrace(this->key, fmt::format("timeout {} req {}", name, fmt::ptr(&req)));
|
||||
logtrace(log_name(), fmt::format("timeout {} req {}", name, fmt::ptr(&req)));
|
||||
req.fail(false, true, "");
|
||||
it = requests.erase(it);
|
||||
}
|
||||
|
@ -525,7 +534,7 @@ private:
|
|||
continue;
|
||||
}
|
||||
|
||||
logdbg(this->key, fmt::format("sending req {}", fmt::ptr(&req)));
|
||||
logdbg(log_name(), fmt::format("sending req {}", fmt::ptr(&req)));
|
||||
req.sent_at = now;
|
||||
send_request(std::data(req.payload), std::size(req.payload));
|
||||
|
||||
|
@ -542,7 +551,7 @@ private:
|
|||
|
||||
void send_request(std::byte const* payload, size_t payload_len)
|
||||
{
|
||||
logdbg(this->key, fmt::format("sending request w/connection id {}", this->connection_id));
|
||||
logdbg(log_name(), fmt::format("sending request w/connection id {}", this->connection_id));
|
||||
|
||||
auto buf = PayloadBuffer{};
|
||||
buf.add_uint64(this->connection_id);
|
||||
|
@ -552,8 +561,13 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
tr_interned_string const key;
|
||||
tr_interned_string const host;
|
||||
[[nodiscard]] constexpr std::string_view log_name() const noexcept
|
||||
{
|
||||
return authority;
|
||||
}
|
||||
|
||||
std::string_view const authority; // interned
|
||||
std::string_view const host; // interned
|
||||
tr_port const port;
|
||||
|
||||
time_t connecting_at = 0;
|
||||
|
@ -567,13 +581,13 @@ public:
|
|||
private:
|
||||
Mediator& mediator_;
|
||||
|
||||
std::optional<std::future<MaybeSockaddr>> addr_pending_dns_ = {};
|
||||
std::optional<std::future<MaybeSockaddr>> addr_pending_dns_;
|
||||
|
||||
MaybeSockaddr addr_ = {};
|
||||
MaybeSockaddr addr_;
|
||||
time_t addr_expires_at_ = 0;
|
||||
|
||||
static inline constexpr auto DnsRetryIntervalSecs = time_t{ 3600 };
|
||||
static inline constexpr auto ConnectionRequestTtl = int{ 30 };
|
||||
static constexpr auto DnsRetryIntervalSecs = time_t{ 3600 };
|
||||
static constexpr auto ConnectionRequestTtl = 30;
|
||||
};
|
||||
|
||||
// --- SESSION
|
||||
|
@ -646,7 +660,7 @@ public:
|
|||
// is it a connection response?
|
||||
if (tracker.connecting_at != 0 && transaction_id == tracker.connection_transaction_id)
|
||||
{
|
||||
logtrace(tracker.key, fmt::format("{} is my connection request!", transaction_id));
|
||||
logtrace(tracker.log_name(), fmt::format("{} is my connection request!", transaction_id));
|
||||
tracker.on_connection_response(action_id, buf);
|
||||
return true;
|
||||
}
|
||||
|
@ -660,7 +674,7 @@ public:
|
|||
[&transaction_id](auto const& req) { return req.transaction_id == transaction_id; });
|
||||
it != std::end(reqs))
|
||||
{
|
||||
logtrace(tracker.key, fmt::format("{} is an announce request!", transaction_id));
|
||||
logtrace(tracker.log_name(), fmt::format("{} is an announce request!", transaction_id));
|
||||
auto req = *it;
|
||||
it = reqs.erase(it);
|
||||
req.onResponse(action_id, buf);
|
||||
|
@ -677,7 +691,7 @@ public:
|
|||
[&transaction_id](auto const& req) { return req.transaction_id == transaction_id; });
|
||||
it != std::end(reqs))
|
||||
{
|
||||
logtrace(tracker.key, fmt::format("{} is a scrape request!", transaction_id));
|
||||
logtrace(tracker.log_name(), fmt::format("{} is a scrape request!", transaction_id));
|
||||
auto req = *it;
|
||||
it = reqs.erase(it);
|
||||
req.onResponse(action_id, buf);
|
||||
|
@ -698,7 +712,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 announce_url)
|
||||
tau_tracker* getTrackerFromUrl(tr_interned_string const announce_url)
|
||||
{
|
||||
// build a lookup key for this tracker
|
||||
auto const parsed = tr_urlParseTracker(announce_url);
|
||||
|
@ -709,20 +723,19 @@ private:
|
|||
}
|
||||
|
||||
// see if we already have it
|
||||
auto const key = tr_announcerGetKey(*parsed);
|
||||
auto const authority = parsed->authority;
|
||||
for (auto& tracker : trackers_)
|
||||
{
|
||||
if (tracker.key == key)
|
||||
if (tracker.authority == authority)
|
||||
{
|
||||
return &tracker;
|
||||
}
|
||||
}
|
||||
|
||||
// we don't have it -- build a new one
|
||||
trackers_.emplace_back(mediator_, key, tr_interned_string(parsed->host), tr_port::from_host(parsed->port));
|
||||
auto* const tracker = &trackers_.back();
|
||||
logtrace(tracker->key, "New tau_tracker created");
|
||||
return tracker;
|
||||
auto& tracker = trackers_.emplace_back(mediator_, authority, parsed->host, 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
|
||||
|
|
|
@ -51,17 +51,17 @@ using namespace std::literals;
|
|||
namespace
|
||||
{
|
||||
/* unless the tracker says otherwise, rescrape this frequently */
|
||||
auto constexpr DefaultScrapeIntervalSec = int{ 60 * 30 };
|
||||
auto constexpr DefaultScrapeIntervalSec = 60 * 30;
|
||||
|
||||
/* the value of the 'numwant' argument passed in tracker requests. */
|
||||
auto constexpr Numwant = int{ 80 };
|
||||
auto constexpr Numwant = 80;
|
||||
|
||||
/* how often to announce & scrape */
|
||||
auto constexpr MaxAnnouncesPerUpkeep = int{ 20 };
|
||||
auto constexpr MaxScrapesPerUpkeep = int{ 20 };
|
||||
auto constexpr MaxAnnouncesPerUpkeep = 20;
|
||||
auto constexpr MaxScrapesPerUpkeep = 20;
|
||||
|
||||
/* how many infohashes to remove when we get a scrape-too-long error */
|
||||
auto constexpr TrMultiscrapeStep = int{ 5 };
|
||||
auto constexpr TrMultiscrapeStep = 5;
|
||||
|
||||
struct StopsCompare
|
||||
{
|
||||
|
@ -240,9 +240,8 @@ std::unique_ptr<tr_announcer> tr_announcer::create(tr_session* session, tr_annou
|
|||
struct tr_tracker
|
||||
{
|
||||
explicit tr_tracker(tr_announcer_impl* announcer, tr_announce_list::tracker_info const& info)
|
||||
: host_and_port{ info.host_and_port }
|
||||
, announce_url{ info.announce }
|
||||
, sitename{ info.sitename }
|
||||
: announce_url{ info.announce }
|
||||
, announce_parsed{ info.announce_parsed }
|
||||
, scrape_info{ std::empty(info.scrape) ? nullptr : announcer->scrape_info(info.scrape) }
|
||||
, id{ info.id }
|
||||
{
|
||||
|
@ -335,9 +334,8 @@ struct tr_tracker
|
|||
return false;
|
||||
}
|
||||
|
||||
tr_interned_string const host_and_port;
|
||||
tr_interned_string const announce_url;
|
||||
std::string_view const sitename;
|
||||
tr_url_parsed_t const announce_parsed;
|
||||
tr_scrape_info* const scrape_info;
|
||||
|
||||
std::string tracker_id;
|
||||
|
@ -353,17 +351,6 @@ private:
|
|||
std::optional<int64_t> downloader_count_;
|
||||
};
|
||||
|
||||
// format: `${host}:${port}`
|
||||
tr_interned_string tr_announcerGetKey(tr_url_parsed_t const& parsed)
|
||||
{
|
||||
auto buf = std::array<char, 1024>{};
|
||||
auto* const begin = std::data(buf);
|
||||
auto const* const end = fmt::format_to_n(begin, std::size(buf), "{:s}:{:d}", parsed.host, parsed.port).out;
|
||||
auto const sv = std::string_view{ begin, static_cast<size_t>(end - begin) };
|
||||
|
||||
return tr_interned_string{ sv };
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
/** @brief A group of trackers in a single tier, as per the multitracker spec */
|
||||
|
@ -463,11 +450,12 @@ struct tr_tier
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string buildLogName() const
|
||||
[[nodiscard]] auto buildLogName() const
|
||||
{
|
||||
auto const* const current_tracker = currentTracker();
|
||||
auto const host_and_port_sv = current_tracker == nullptr ? "?"sv : current_tracker->host_and_port.sv();
|
||||
return fmt::format("{:s} at {:s}", tor->name(), host_and_port_sv);
|
||||
auto const* const tracker = currentTracker();
|
||||
return tracker != nullptr ?
|
||||
fmt::format("{:s} at {:s}:{:d}", tor->name(), tracker->announce_parsed.host, tracker->announce_parsed.port) :
|
||||
fmt::format("{:s} at ?", tor->name());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool canManualAnnounce() const
|
||||
|
@ -536,10 +524,10 @@ struct tr_tier
|
|||
|
||||
private:
|
||||
// unless the tracker says otherwise, this is the announce interval
|
||||
static auto constexpr DefaultAnnounceIntervalSec = int{ 60 * 10 };
|
||||
static auto constexpr DefaultAnnounceIntervalSec = 60 * 10;
|
||||
|
||||
// unless the tracker says otherwise, this is the announce min_interval
|
||||
static auto constexpr DefaultAnnounceMinIntervalSec = int{ 60 * 2 };
|
||||
static auto constexpr DefaultAnnounceMinIntervalSec = 60 * 2;
|
||||
|
||||
[[nodiscard]] static time_t getNextScrapeTime(tr_session const* session, tr_tier const* tier, time_t interval_secs)
|
||||
{
|
||||
|
@ -1243,27 +1231,29 @@ namespace on_scrape_done_helpers
|
|||
|
||||
void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char const* errmsg)
|
||||
{
|
||||
// increment the error count
|
||||
auto* current_tracker = tier->currentTracker();
|
||||
if (current_tracker != nullptr)
|
||||
if (auto* const current_tracker = tier->currentTracker(); current_tracker != nullptr)
|
||||
{
|
||||
++current_tracker->consecutive_failures;
|
||||
|
||||
tr_logAddDebugTier(
|
||||
tier,
|
||||
fmt::format(
|
||||
"Tracker '{}' scrape error: {} (Can retry in {} seconds)",
|
||||
current_tracker->announce_parsed.authority,
|
||||
errmsg,
|
||||
current_tracker->getRetryInterval()));
|
||||
}
|
||||
|
||||
// set the error message
|
||||
tier->last_scrape_str = errmsg != nullptr ? errmsg : "";
|
||||
tier->lastScrapeSucceeded = false;
|
||||
|
||||
// switch to the next tracker
|
||||
current_tracker = tier->useNextTracker();
|
||||
|
||||
// schedule a rescrape
|
||||
auto const interval = current_tracker->getRetryInterval();
|
||||
auto const* const host_and_port_cstr = current_tracker->host_and_port.c_str();
|
||||
tr_logAddDebugTier(
|
||||
tier,
|
||||
fmt::format("Tracker '{}' scrape error: {} (Retrying in {} seconds)", host_and_port_cstr, errmsg, interval));
|
||||
tier->lastScrapeSucceeded = false;
|
||||
tier->scheduleNextScrape(interval);
|
||||
if (auto* const current_tracker = tier->useNextTracker(); current_tracker != nullptr)
|
||||
{
|
||||
// schedule a rescrape
|
||||
tier->scheduleNextScrape(current_tracker->getRetryInterval());
|
||||
}
|
||||
}
|
||||
|
||||
void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response const& response)
|
||||
|
@ -1293,7 +1283,7 @@ void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response const&
|
|||
if (multiscrape_max != n)
|
||||
{
|
||||
// don't log the full URL, since that might have a personal announce id
|
||||
tr_logAddDebug(fmt::format(FMT_STRING("Reducing multiscrape max to {:d}"), n), tr_urlTrackerLogName(url));
|
||||
tr_logAddDebug(fmt::format("Reducing multiscrape max to {:d}", n), tr_urlTrackerLogName(url));
|
||||
|
||||
multiscrape_max = n;
|
||||
}
|
||||
|
@ -1485,9 +1475,9 @@ int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
|||
}
|
||||
|
||||
/* prefer swarms where we might download */
|
||||
if (auto const is_done_a = a->tor->is_done(), is_done_b = b->tor->is_done(); is_done_a != is_done_b)
|
||||
if (auto const val = tr_compare_3way(a->tor->is_done(), b->tor->is_done()); val != 0)
|
||||
{
|
||||
return is_done_a ? 1 : -1;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* prefer larger stats, to help ensure stats get recorded when stopping on shutdown */
|
||||
|
@ -1617,17 +1607,20 @@ namespace tracker_view_helpers
|
|||
{
|
||||
[[nodiscard]] auto trackerView(tr_torrent const& tor, size_t tier_index, tr_tier const& tier, tr_tracker const& tracker)
|
||||
{
|
||||
auto const& announce = tracker.announce_parsed;
|
||||
auto const now = tr_time();
|
||||
auto view = tr_tracker_view{};
|
||||
|
||||
view.host_and_port = tracker.host_and_port.c_str();
|
||||
*fmt::format_to_n(
|
||||
std::data(view.host_and_port),
|
||||
std::size(view.host_and_port) - 1U,
|
||||
"{:s}:{:d}",
|
||||
announce.host,
|
||||
announce.port)
|
||||
.out = '\0';
|
||||
*fmt::format_to_n(std::data(view.sitename), std::size(view.sitename) - 1U, "{:s}", announce.sitename).out = '\0';
|
||||
view.announce = tracker.announce_url.c_str();
|
||||
view.scrape = tracker.scrape_info == nullptr ? "" : tracker.scrape_info->scrape_url.c_str();
|
||||
*std::copy_n(
|
||||
std::begin(tracker.sitename),
|
||||
std::min(std::size(tracker.sitename), sizeof(view.sitename) - 1),
|
||||
view.sitename) = '\0';
|
||||
|
||||
view.id = tracker.id;
|
||||
view.tier = tier_index;
|
||||
view.isBackup = &tracker != tier.currentTracker();
|
||||
|
@ -1651,7 +1644,8 @@ namespace tracker_view_helpers
|
|||
view.lastScrapeTime = tier.lastScrapeTime;
|
||||
view.lastScrapeSucceeded = tier.lastScrapeSucceeded;
|
||||
view.lastScrapeTimedOut = tier.lastScrapeTimedOut;
|
||||
tr_strlcpy(view.lastScrapeResult, tier.last_scrape_str.c_str(), sizeof(view.lastScrapeResult));
|
||||
auto& buf = view.lastScrapeResult;
|
||||
*fmt::format_to_n(buf, sizeof(buf) - 1, "{:s}", tier.last_scrape_str).out = '\0';
|
||||
}
|
||||
|
||||
if (tier.isScraping)
|
||||
|
@ -1681,7 +1675,8 @@ namespace tracker_view_helpers
|
|||
view.lastAnnounceSucceeded = tier.lastAnnounceSucceeded;
|
||||
view.lastAnnounceTimedOut = tier.lastAnnounceTimedOut;
|
||||
view.lastAnnouncePeerCount = tier.lastAnnouncePeerCount;
|
||||
tr_strlcpy(view.lastAnnounceResult, tier.last_announce_str.c_str(), sizeof(view.lastAnnounceResult));
|
||||
auto& buf = view.lastAnnounceResult;
|
||||
*fmt::format_to_n(buf, sizeof(buf) - 1, "{:s}", tier.last_announce_str).out = '\0';
|
||||
}
|
||||
|
||||
if (tier.isAnnouncing)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility> // for std::swap()
|
||||
#include <vector>
|
||||
|
@ -28,14 +29,14 @@ using namespace libtransmission::Values;
|
|||
|
||||
Speed tr_bandwidth::get_speed(RateControl& r, unsigned int interval_msec, uint64_t now)
|
||||
{
|
||||
if (now == 0)
|
||||
if (now == 0U)
|
||||
{
|
||||
now = tr_time_msec();
|
||||
}
|
||||
|
||||
if (now != r.cache_time_)
|
||||
{
|
||||
uint64_t bytes = 0;
|
||||
uint64_t bytes = 0U;
|
||||
uint64_t const cutoff = now - interval_msec;
|
||||
|
||||
for (int i = r.newest_; r.date_[i] > cutoff;)
|
||||
|
@ -78,14 +79,15 @@ void tr_bandwidth::notify_bandwidth_consumed_bytes(uint64_t const now, RateContr
|
|||
}
|
||||
|
||||
/* invalidate cache_val*/
|
||||
r.cache_time_ = 0;
|
||||
r.cache_time_ = 0U;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
tr_bandwidth::tr_bandwidth(tr_bandwidth* new_parent)
|
||||
tr_bandwidth::tr_bandwidth(tr_bandwidth* parent, bool is_group)
|
||||
: priority_(is_group ? std::numeric_limits<tr_priority_t>::max() : TR_PRI_NORMAL)
|
||||
{
|
||||
this->set_parent(new_parent);
|
||||
set_parent(parent);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -135,7 +137,7 @@ void tr_bandwidth::set_parent(tr_bandwidth* new_parent)
|
|||
#endif
|
||||
|
||||
new_parent->children_.push_back(this);
|
||||
this->parent_ = new_parent;
|
||||
parent_ = new_parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +148,7 @@ void tr_bandwidth::allocate_bandwidth(
|
|||
uint64_t period_msec,
|
||||
std::vector<std::shared_ptr<tr_peerIo>>& peer_pool)
|
||||
{
|
||||
auto const priority = std::max(parent_priority, this->priority_);
|
||||
auto const priority = std::min(parent_priority, priority_);
|
||||
|
||||
// set the available bandwidth
|
||||
for (auto const dir : { TR_UP, TR_DOWN })
|
||||
|
@ -159,14 +161,15 @@ void tr_bandwidth::allocate_bandwidth(
|
|||
}
|
||||
|
||||
// add this bandwidth's peer, if any, to the peer pool
|
||||
if (auto shared = this->peer_.lock(); shared)
|
||||
if (auto shared = peer_.lock(); shared)
|
||||
{
|
||||
TR_ASSERT(tr_isPriority(priority));
|
||||
shared->set_priority(priority);
|
||||
peer_pool.push_back(std::move(shared));
|
||||
}
|
||||
|
||||
// traverse & repeat for the subtree
|
||||
for (auto* child : this->children_)
|
||||
for (auto* child : children_)
|
||||
{
|
||||
child->allocate_bandwidth(priority, period_msec, peer_pool);
|
||||
}
|
||||
|
@ -186,7 +189,7 @@ void tr_bandwidth::phase_one(std::vector<tr_peerIo*>& peers, tr_direction dir)
|
|||
// process until we run out of bandwidth and/or peers that can use it.
|
||||
for (size_t n_unfinished = std::size(peers); n_unfinished > 0U;)
|
||||
{
|
||||
for (size_t i = 0; i < n_unfinished;)
|
||||
for (size_t i = 0U; i < n_unfinished;)
|
||||
{
|
||||
// Value of 3000 bytes chosen so that when using µTP we'll send a full-size
|
||||
// frame right away and leave enough buffered data for the next frame to go
|
||||
|
@ -223,7 +226,7 @@ void tr_bandwidth::allocate(uint64_t period_msec)
|
|||
// allocateBandwidth () is a helper function with two purposes:
|
||||
// 1. allocate bandwidth to b and its subtree
|
||||
// 2. accumulate an array of all the peerIos from b and its subtree.
|
||||
this->allocate_bandwidth(TR_PRI_LOW, period_msec, refs);
|
||||
allocate_bandwidth(std::numeric_limits<tr_priority_t>::max(), period_msec, refs);
|
||||
|
||||
for (auto const& io : refs)
|
||||
{
|
||||
|
@ -239,8 +242,13 @@ void tr_bandwidth::allocate(uint64_t period_msec)
|
|||
normal.push_back(io.get());
|
||||
[[fallthrough]];
|
||||
|
||||
default:
|
||||
case TR_PRI_LOW:
|
||||
low.push_back(io.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
TR_ASSERT_MSG(false, "invalid priority");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,14 +279,14 @@ size_t tr_bandwidth::clamp(tr_direction const dir, size_t byte_count) const noex
|
|||
{
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
if (this->band_[dir].is_limited_)
|
||||
if (band_[dir].is_limited_)
|
||||
{
|
||||
byte_count = std::min(byte_count, this->band_[dir].bytes_left_);
|
||||
byte_count = std::min(byte_count, band_[dir].bytes_left_);
|
||||
}
|
||||
|
||||
if (this->parent_ != nullptr && this->band_[dir].honor_parent_limits_ && byte_count > 0)
|
||||
if (parent_ != nullptr && band_[dir].honor_parent_limits_ && byte_count > 0U)
|
||||
{
|
||||
byte_count = this->parent_->clamp(dir, byte_count);
|
||||
byte_count = parent_->clamp(dir, byte_count);
|
||||
}
|
||||
|
||||
return byte_count;
|
||||
|
@ -288,39 +296,23 @@ void tr_bandwidth::notify_bandwidth_consumed(tr_direction dir, size_t byte_count
|
|||
{
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
Band* band = &this->band_[dir];
|
||||
auto& band = band_[dir];
|
||||
|
||||
if (band->is_limited_ && is_piece_data)
|
||||
if (band.is_limited_ && is_piece_data)
|
||||
{
|
||||
band->bytes_left_ -= std::min(band->bytes_left_, byte_count);
|
||||
band.bytes_left_ -= std::min(band.bytes_left_, byte_count);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DIRECTION
|
||||
|
||||
if (dir == DEBUG_DIRECTION && band_->isLimited)
|
||||
{
|
||||
fprintf(
|
||||
stderr,
|
||||
"%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
|
||||
this,
|
||||
byte_count,
|
||||
is_piece_data ? "piece" : "raw",
|
||||
oldBytesLeft,
|
||||
band_->bytesLeft);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
notify_bandwidth_consumed_bytes(now, band->raw_, byte_count);
|
||||
notify_bandwidth_consumed_bytes(now, band.raw_, byte_count);
|
||||
|
||||
if (is_piece_data)
|
||||
{
|
||||
notify_bandwidth_consumed_bytes(now, band->piece_, byte_count);
|
||||
notify_bandwidth_consumed_bytes(now, band.piece_, byte_count);
|
||||
}
|
||||
|
||||
if (this->parent_ != nullptr)
|
||||
if (parent_ != nullptr)
|
||||
{
|
||||
this->parent_->notify_bandwidth_consumed(dir, byte_count, is_piece_data, now);
|
||||
parent_->notify_bandwidth_consumed(dir, byte_count, is_piece_data, now);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,17 +321,17 @@ void tr_bandwidth::notify_bandwidth_consumed(tr_direction dir, size_t byte_count
|
|||
tr_bandwidth_limits tr_bandwidth::get_limits() const
|
||||
{
|
||||
auto limits = tr_bandwidth_limits{};
|
||||
limits.up_limit = this->get_desired_speed(TR_UP);
|
||||
limits.down_limit = this->get_desired_speed(TR_DOWN);
|
||||
limits.up_limited = this->is_limited(TR_UP);
|
||||
limits.down_limited = this->is_limited(TR_DOWN);
|
||||
limits.up_limit = get_desired_speed(TR_UP);
|
||||
limits.down_limit = get_desired_speed(TR_DOWN);
|
||||
limits.up_limited = is_limited(TR_UP);
|
||||
limits.down_limited = is_limited(TR_DOWN);
|
||||
return limits;
|
||||
}
|
||||
|
||||
void tr_bandwidth::set_limits(tr_bandwidth_limits const& limits)
|
||||
{
|
||||
this->set_desired_speed(TR_UP, limits.up_limit);
|
||||
this->set_desired_speed(TR_DOWN, limits.down_limit);
|
||||
this->set_limited(TR_UP, limits.up_limited);
|
||||
this->set_limited(TR_DOWN, limits.down_limited);
|
||||
set_desired_speed(TR_UP, limits.up_limit);
|
||||
set_desired_speed(TR_DOWN, limits.down_limit);
|
||||
set_limited(TR_UP, limits.up_limited);
|
||||
set_limited(TR_DOWN, limits.down_limited);
|
||||
}
|
||||
|
|
|
@ -80,15 +80,15 @@ struct tr_bandwidth
|
|||
private:
|
||||
using Speed = libtransmission::Values::Speed;
|
||||
|
||||
static constexpr size_t HistoryMSec = 2000U;
|
||||
static constexpr size_t GranularityMSec = 250U;
|
||||
static constexpr size_t HistorySize = HistoryMSec / GranularityMSec;
|
||||
static constexpr auto HistoryMSec = 2000U;
|
||||
static constexpr auto GranularityMSec = 250U;
|
||||
static constexpr auto HistorySize = HistoryMSec / GranularityMSec;
|
||||
|
||||
public:
|
||||
explicit tr_bandwidth(tr_bandwidth* new_parent);
|
||||
explicit tr_bandwidth(tr_bandwidth* parent, bool is_group = false);
|
||||
|
||||
tr_bandwidth()
|
||||
: tr_bandwidth{ nullptr }
|
||||
explicit tr_bandwidth(bool is_group = false)
|
||||
: tr_bandwidth{ nullptr, is_group }
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ public:
|
|||
*/
|
||||
void set_peer(std::weak_ptr<tr_peerIo> peer) noexcept
|
||||
{
|
||||
this->peer_ = std::move(peer);
|
||||
peer_ = std::move(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,14 +123,14 @@ public:
|
|||
|
||||
void set_parent(tr_bandwidth* new_parent);
|
||||
|
||||
[[nodiscard]] constexpr tr_priority_t get_priority() const noexcept
|
||||
[[nodiscard]] constexpr auto get_priority() const noexcept
|
||||
{
|
||||
return this->priority_;
|
||||
return priority_;
|
||||
}
|
||||
|
||||
constexpr void set_priority(tr_priority_t prio) noexcept
|
||||
{
|
||||
this->priority_ = prio;
|
||||
priority_ = prio;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,7 +143,7 @@ public:
|
|||
{
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return get_speed(this->band_[dir].raw_, HistoryMSec, now);
|
||||
return get_speed(band_[dir].raw_, HistoryMSec, now);
|
||||
}
|
||||
|
||||
/** @brief Get the number of piece data bytes read or sent by this bandwidth subtree. */
|
||||
|
@ -151,7 +151,7 @@ public:
|
|||
{
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return get_speed(this->band_[dir].piece_, HistoryMSec, now);
|
||||
return get_speed(band_[dir].piece_, HistoryMSec, now);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,8 +161,8 @@ public:
|
|||
*/
|
||||
constexpr bool set_desired_speed(tr_direction dir, Speed desired_speed)
|
||||
{
|
||||
auto& value = this->band_[dir].desired_speed_;
|
||||
bool const did_change = desired_speed != value;
|
||||
auto& value = band_[dir].desired_speed_;
|
||||
auto const did_change = desired_speed != value;
|
||||
value = desired_speed;
|
||||
return did_change;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ public:
|
|||
*/
|
||||
[[nodiscard]] constexpr auto get_desired_speed(tr_direction dir) const
|
||||
{
|
||||
return this->band_[dir].desired_speed_;
|
||||
return band_[dir].desired_speed_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_maxed_out(tr_direction dir, uint64_t now_msec) const noexcept
|
||||
|
@ -193,8 +193,8 @@ public:
|
|||
*/
|
||||
constexpr bool set_limited(tr_direction dir, bool is_limited)
|
||||
{
|
||||
bool& value = this->band_[dir].is_limited_;
|
||||
bool const did_change = is_limited != value;
|
||||
auto& value = band_[dir].is_limited_;
|
||||
auto const did_change = is_limited != value;
|
||||
value = is_limited;
|
||||
return did_change;
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ public:
|
|||
*/
|
||||
[[nodiscard]] constexpr bool is_limited(tr_direction dir) const noexcept
|
||||
{
|
||||
return this->band_[dir].is_limited_;
|
||||
return band_[dir].is_limited_;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,17 +215,15 @@ public:
|
|||
*/
|
||||
constexpr bool honor_parent_limits(tr_direction direction, bool is_enabled)
|
||||
{
|
||||
bool& value = this->band_[direction].honor_parent_limits_;
|
||||
bool const did_change = is_enabled != value;
|
||||
auto& value = band_[direction].honor_parent_limits_;
|
||||
auto const did_change = is_enabled != value;
|
||||
value = is_enabled;
|
||||
return did_change;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool are_parent_limits_honored(tr_direction direction) const
|
||||
{
|
||||
TR_ASSERT(tr_isDirection(direction));
|
||||
|
||||
return this->band_[direction].honor_parent_limits_;
|
||||
return band_[direction].honor_parent_limits_;
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_bandwidth_limits get_limits() const;
|
||||
|
@ -274,7 +272,12 @@ private:
|
|||
std::vector<tr_bandwidth*> children_;
|
||||
tr_bandwidth* parent_ = nullptr;
|
||||
std::weak_ptr<tr_peerIo> peer_;
|
||||
tr_priority_t priority_ = 0;
|
||||
tr_priority_t priority_;
|
||||
};
|
||||
|
||||
/* @} */
|
||||
|
||||
constexpr auto tr_isPriority(tr_priority_t p)
|
||||
{
|
||||
return p == TR_PRI_LOW || p == TR_PRI_NORMAL || p == TR_PRI_HIGH;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "libtransmission/block-info.h"
|
||||
#include "libtransmission/tr-assert.h" // TR_ASSERT
|
||||
|
||||
void tr_block_info::init_sizes(uint64_t total_size_in, uint32_t piece_size_in) noexcept
|
||||
void tr_block_info::init_sizes(uint64_t const total_size_in, uint32_t const piece_size_in) noexcept
|
||||
{
|
||||
TR_ASSERT(piece_size_in == 0 || piece_size_in >= BlockSize);
|
||||
if (piece_size_in == 0)
|
||||
|
@ -19,8 +19,8 @@ void tr_block_info::init_sizes(uint64_t total_size_in, uint32_t piece_size_in) n
|
|||
|
||||
total_size_ = total_size_in;
|
||||
piece_size_ = piece_size_in;
|
||||
n_pieces_ = (total_size_ + piece_size_ - 1) / piece_size_;
|
||||
n_blocks_ = (total_size_ + BlockSize - 1) / BlockSize;
|
||||
n_pieces_ = static_cast<tr_piece_index_t>((total_size_ + piece_size_ - 1) / piece_size_);
|
||||
n_blocks_ = static_cast<tr_block_index_t>((total_size_ + BlockSize - 1) / BlockSize);
|
||||
|
||||
uint32_t remainder = total_size_ % piece_size_;
|
||||
final_piece_size_ = remainder != 0U ? remainder : piece_size_;
|
||||
|
|
|
@ -11,37 +11,26 @@
|
|||
|
||||
struct tr_block_info
|
||||
{
|
||||
private:
|
||||
uint64_t total_size_ = 0;
|
||||
uint32_t piece_size_ = 0;
|
||||
tr_piece_index_t n_pieces_ = 0;
|
||||
|
||||
tr_block_index_t n_blocks_ = 0;
|
||||
// should be same type as BlockSize
|
||||
uint32_t final_block_size_ = 0;
|
||||
// should be same type as piece_size
|
||||
uint32_t final_piece_size_ = 0;
|
||||
|
||||
public:
|
||||
static auto constexpr BlockSize = uint32_t{ 1024 * 16 };
|
||||
static auto constexpr BlockSize = uint32_t{ 1024U * 16U };
|
||||
|
||||
tr_block_info() noexcept = default;
|
||||
tr_block_info() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
tr_block_info(uint64_t total_size_in, uint32_t piece_size_in) noexcept
|
||||
tr_block_info(uint64_t const total_size_in, uint32_t const piece_size_in) noexcept
|
||||
{
|
||||
init_sizes(total_size_in, piece_size_in);
|
||||
}
|
||||
|
||||
void init_sizes(uint64_t total_size_in, uint32_t piece_size_in) noexcept;
|
||||
|
||||
[[nodiscard]] constexpr auto block_count() const noexcept
|
||||
{
|
||||
return n_blocks_;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto block_size(tr_block_index_t block) const noexcept
|
||||
[[nodiscard]] constexpr auto block_size(tr_block_index_t const block) const noexcept
|
||||
{
|
||||
return block + 1 == n_blocks_ ? final_block_size_ : BlockSize;
|
||||
return block + 1U == n_blocks_ ? final_block_size_ : BlockSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto piece_count() const noexcept
|
||||
|
@ -54,9 +43,9 @@ public:
|
|||
return piece_size_;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto piece_size(tr_piece_index_t piece) const noexcept
|
||||
[[nodiscard]] constexpr auto piece_size(tr_piece_index_t const piece) const noexcept
|
||||
{
|
||||
return piece + 1 == n_pieces_ ? final_piece_size_ : piece_size();
|
||||
return piece + 1U == n_pieces_ ? final_piece_size_ : piece_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto total_size() const noexcept
|
||||
|
@ -66,14 +55,6 @@ public:
|
|||
|
||||
struct Location
|
||||
{
|
||||
uint64_t byte = 0;
|
||||
|
||||
tr_piece_index_t piece = 0;
|
||||
uint32_t piece_offset = 0;
|
||||
|
||||
tr_block_index_t block = 0;
|
||||
uint32_t block_offset = 0;
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(Location const& that) const noexcept
|
||||
{
|
||||
return this->byte == that.byte;
|
||||
|
@ -83,10 +64,18 @@ public:
|
|||
{
|
||||
return this->byte < that.byte;
|
||||
}
|
||||
|
||||
uint64_t byte = {};
|
||||
|
||||
tr_piece_index_t piece = {};
|
||||
uint32_t piece_offset = {};
|
||||
|
||||
tr_block_index_t block = {};
|
||||
uint32_t block_offset = {};
|
||||
};
|
||||
|
||||
// Location of the torrent's nth byte
|
||||
[[nodiscard]] constexpr auto byte_loc(uint64_t byte_idx) const noexcept
|
||||
[[nodiscard]] constexpr auto byte_loc(uint64_t const byte_idx) const noexcept
|
||||
{
|
||||
auto loc = Location{};
|
||||
|
||||
|
@ -96,13 +85,13 @@ public:
|
|||
|
||||
if (byte_idx == total_size()) // handle 0-byte files at the end of a torrent
|
||||
{
|
||||
loc.block = block_count() - 1;
|
||||
loc.piece = piece_count() - 1;
|
||||
loc.block = block_count() - 1U;
|
||||
loc.piece = piece_count() - 1U;
|
||||
}
|
||||
else
|
||||
{
|
||||
loc.block = byte_idx / BlockSize;
|
||||
loc.piece = byte_idx / piece_size();
|
||||
loc.block = static_cast<tr_block_index_t>(byte_idx / BlockSize);
|
||||
loc.piece = static_cast<tr_piece_index_t>(byte_idx / piece_size());
|
||||
}
|
||||
|
||||
loc.block_offset = static_cast<uint32_t>(loc.byte - (uint64_t{ loc.block } * BlockSize));
|
||||
|
@ -113,28 +102,28 @@ public:
|
|||
}
|
||||
|
||||
// Location of the first byte in `block`.
|
||||
[[nodiscard]] constexpr auto block_loc(tr_block_index_t block) const noexcept
|
||||
[[nodiscard]] constexpr auto block_loc(tr_block_index_t const block) const noexcept
|
||||
{
|
||||
return byte_loc(uint64_t{ block } * BlockSize);
|
||||
}
|
||||
|
||||
// Location of the first byte (+ optional offset and length) in `piece`
|
||||
[[nodiscard]] constexpr auto piece_loc(tr_piece_index_t piece, uint32_t offset = 0, uint32_t length = 0) const noexcept
|
||||
[[nodiscard]] constexpr auto piece_loc(tr_piece_index_t piece, uint32_t offset = {}, uint32_t length = {}) const noexcept
|
||||
{
|
||||
return byte_loc(uint64_t{ piece } * piece_size() + offset + length);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr tr_block_span_t block_span_for_piece(tr_piece_index_t piece) const noexcept
|
||||
[[nodiscard]] constexpr tr_block_span_t block_span_for_piece(tr_piece_index_t const piece) const noexcept
|
||||
{
|
||||
if (!is_initialized())
|
||||
{
|
||||
return { 0U, 0U };
|
||||
}
|
||||
|
||||
return { piece_loc(piece).block, piece_last_loc(piece).block + 1 };
|
||||
return { piece_loc(piece).block, piece_last_loc(piece).block + 1U };
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr tr_byte_span_t byte_span_for_piece(tr_piece_index_t piece) const noexcept
|
||||
[[nodiscard]] constexpr tr_byte_span_t byte_span_for_piece(tr_piece_index_t const piece) const noexcept
|
||||
{
|
||||
if (!is_initialized())
|
||||
{
|
||||
|
@ -146,14 +135,26 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
void init_sizes(uint64_t total_size_in, uint32_t piece_size_in) noexcept;
|
||||
|
||||
// Location of the last byte in `piece`.
|
||||
[[nodiscard]] constexpr Location piece_last_loc(tr_piece_index_t piece) const noexcept
|
||||
[[nodiscard]] constexpr Location piece_last_loc(tr_piece_index_t const piece) const noexcept
|
||||
{
|
||||
return byte_loc(static_cast<uint64_t>(piece) * piece_size() + piece_size(piece) - 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool is_initialized() const noexcept
|
||||
{
|
||||
return piece_size_ != 0;
|
||||
return piece_size_ != 0U;
|
||||
}
|
||||
|
||||
uint64_t total_size_ = {};
|
||||
|
||||
tr_block_index_t n_blocks_ = {};
|
||||
tr_piece_index_t n_pieces_ = {};
|
||||
|
||||
uint32_t final_block_size_ = {};
|
||||
|
||||
uint32_t piece_size_ = {};
|
||||
uint32_t final_piece_size_ = {};
|
||||
};
|
||||
|
|
|
@ -204,10 +204,12 @@ std::optional<address_range_t> parseCidrLine(std::string_view line)
|
|||
return {};
|
||||
}
|
||||
|
||||
auto const mask = uint32_t{ 0xFFFFFFFF } << (32 - *pflen);
|
||||
auto const ip_u = htonl(addrpair.first.addr.addr4.s_addr);
|
||||
addrpair.first.addr.addr4.s_addr = ntohl(ip_u & mask);
|
||||
addrpair.second.addr.addr4.s_addr = ntohl(ip_u | (~mask));
|
||||
auto const mask = ~(~uint32_t{ 0 } >> *pflen);
|
||||
auto const ip_u = ntohl(addrpair.first.addr.addr4.s_addr);
|
||||
auto tmp = htonl(ip_u & mask);
|
||||
std::tie(addrpair.first, std::ignore) = tr_address::from_compact_ipv4(reinterpret_cast<std::byte*>(&tmp));
|
||||
tmp = htonl(ip_u | (~mask));
|
||||
std::tie(addrpair.second, std::ignore) = tr_address::from_compact_ipv4(reinterpret_cast<std::byte*>(&tmp));
|
||||
return addrpair;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <cstdint> // uint8_t
|
||||
#include <iterator> // std::distance(), std::next(), std::prev()
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric> // std::accumulate()
|
||||
#include <utility> // std::make_pair()
|
||||
#include <vector>
|
||||
|
@ -25,9 +24,9 @@
|
|||
#include "libtransmission/torrents.h"
|
||||
#include "libtransmission/tr-assert.h"
|
||||
|
||||
Cache::Key Cache::make_key(tr_torrent const* torrent, tr_block_info::Location loc) noexcept
|
||||
Cache::Key Cache::make_key(tr_torrent const& tor, tr_block_info::Location const loc) noexcept
|
||||
{
|
||||
return std::make_pair(torrent->id(), loc.block);
|
||||
return std::make_pair(tor.id(), loc.block);
|
||||
}
|
||||
|
||||
Cache::CIter Cache::find_span_end(CIter span_begin, CIter end) noexcept
|
||||
|
@ -104,7 +103,7 @@ int Cache::write_contiguous(CIter const begin, CIter const end) const
|
|||
|
||||
auto const loc = tor->block_loc(block);
|
||||
|
||||
if (auto const err = tr_ioWrite(tor, loc, outlen, out); err != 0)
|
||||
if (auto const err = tr_ioWrite(*tor, loc, outlen, out); err != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
@ -116,8 +115,6 @@ int Cache::write_contiguous(CIter const begin, CIter const end) const
|
|||
|
||||
int Cache::set_limit(Memory const max_size)
|
||||
{
|
||||
auto const lock = std::lock_guard{ mutex_ };
|
||||
|
||||
max_blocks_ = get_max_blocks(max_size);
|
||||
tr_logAddDebug(fmt::format("Maximum cache size set to {} ({} blocks)", max_size.to_string(), max_blocks_));
|
||||
|
||||
|
@ -132,7 +129,7 @@ Cache::Cache(tr_torrents const& torrents, Memory const max_size)
|
|||
|
||||
// ---
|
||||
|
||||
int Cache::write_block(tr_torrent_id_t tor_id, tr_block_index_t block, std::unique_ptr<BlockData> writeme)
|
||||
int Cache::write_block(tr_torrent_id_t const tor_id, tr_block_index_t const block, std::unique_ptr<BlockData> writeme)
|
||||
{
|
||||
if (max_blocks_ == 0U)
|
||||
{
|
||||
|
@ -142,11 +139,9 @@ int Cache::write_block(tr_torrent_id_t tor_id, tr_block_index_t block, std::uniq
|
|||
// already has a cache layer for the very purpose of this cache
|
||||
// https://github.com/transmission/transmission/pull/5668
|
||||
auto* const tor = torrents_.get(tor_id);
|
||||
return tr_ioWrite(tor, tor->block_loc(block), std::size(*writeme), std::data(*writeme));
|
||||
return tor == nullptr ? EINVAL : tr_ioWrite(*tor, tor->block_loc(block), std::size(*writeme), std::data(*writeme));
|
||||
}
|
||||
|
||||
auto const lock = std::lock_guard{ mutex_ };
|
||||
|
||||
auto const key = Key{ tor_id, block };
|
||||
auto iter = std::lower_bound(std::begin(blocks_), std::end(blocks_), key, CompareCacheBlockByKey);
|
||||
if (iter == std::end(blocks_) || iter->key != key)
|
||||
|
@ -163,12 +158,12 @@ int Cache::write_block(tr_torrent_id_t tor_id, tr_block_index_t block, std::uniq
|
|||
return cache_trim();
|
||||
}
|
||||
|
||||
Cache::CIter Cache::get_block(tr_torrent const* torrent, tr_block_info::Location const& loc) noexcept
|
||||
Cache::CIter Cache::get_block(tr_torrent const& tor, tr_block_info::Location const& loc) noexcept
|
||||
{
|
||||
if (auto const [begin, end] = std::equal_range(
|
||||
std::begin(blocks_),
|
||||
std::end(blocks_),
|
||||
make_key(torrent, loc),
|
||||
make_key(tor, loc),
|
||||
CompareCacheBlockByKey);
|
||||
begin < end)
|
||||
{
|
||||
|
@ -178,29 +173,15 @@ Cache::CIter Cache::get_block(tr_torrent const* torrent, tr_block_info::Location
|
|||
return std::end(blocks_);
|
||||
}
|
||||
|
||||
int Cache::read_block(tr_torrent* torrent, tr_block_info::Location const& loc, size_t len, uint8_t* setme)
|
||||
int Cache::read_block(tr_torrent const& tor, tr_block_info::Location const& loc, size_t len, uint8_t* setme)
|
||||
{
|
||||
auto lock = std::unique_lock{ mutex_ };
|
||||
if (auto const iter = get_block(torrent, loc); iter != std::end(blocks_))
|
||||
if (auto const iter = get_block(tor, loc); iter != std::end(blocks_))
|
||||
{
|
||||
std::copy_n(std::begin(*iter->buf), len, setme);
|
||||
return {};
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
return tr_ioRead(torrent, loc, len, setme);
|
||||
}
|
||||
|
||||
int Cache::prefetch_block(tr_torrent* torrent, tr_block_info::Location const& loc, size_t len)
|
||||
{
|
||||
auto lock = std::unique_lock{ mutex_ };
|
||||
if (auto const iter = get_block(torrent, loc); iter != std::end(blocks_))
|
||||
{
|
||||
return {}; // already have it
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
return tr_ioPrefetch(torrent, loc, len);
|
||||
return tr_ioRead(tor, loc, len, setme);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -223,22 +204,18 @@ int Cache::flush_span(CIter const begin, CIter const end)
|
|||
return {};
|
||||
}
|
||||
|
||||
int Cache::flush_file(tr_torrent const* const torrent, tr_file_index_t const file)
|
||||
int Cache::flush_file(tr_torrent const& tor, tr_file_index_t const file)
|
||||
{
|
||||
auto const tor_id = torrent->id();
|
||||
auto const [block_begin, block_end] = torrent->block_span_for_file(file);
|
||||
auto const tor_id = tor.id();
|
||||
auto const [block_begin, block_end] = tor.block_span_for_file(file);
|
||||
|
||||
auto const lock = std::lock_guard{ mutex_ };
|
||||
return flush_span(
|
||||
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, block_begin), CompareCacheBlockByKey),
|
||||
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, block_end), CompareCacheBlockByKey));
|
||||
}
|
||||
|
||||
int Cache::flush_torrent(tr_torrent const* torrent)
|
||||
int Cache::flush_torrent(tr_torrent_id_t const tor_id)
|
||||
{
|
||||
auto const tor_id = torrent->id();
|
||||
|
||||
auto const lock = std::lock_guard{ mutex_ };
|
||||
return flush_span(
|
||||
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, 0), CompareCacheBlockByKey),
|
||||
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id + 1, 0), CompareCacheBlockByKey));
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for intX_t, uintX_t
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <mutex>
|
||||
#include <utility> // for std::pair
|
||||
#include <vector>
|
||||
|
||||
|
@ -39,10 +38,9 @@ public:
|
|||
// @return any error code from cacheTrim()
|
||||
int write_block(tr_torrent_id_t tor, tr_block_index_t block, std::unique_ptr<BlockData> writeme);
|
||||
|
||||
int read_block(tr_torrent* torrent, tr_block_info::Location const& loc, size_t len, uint8_t* setme);
|
||||
int prefetch_block(tr_torrent* torrent, tr_block_info::Location const& loc, size_t len);
|
||||
int flush_torrent(tr_torrent const* torrent);
|
||||
int flush_file(tr_torrent const* torrent, tr_file_index_t file);
|
||||
int read_block(tr_torrent const& tor, tr_block_info::Location const& loc, size_t len, uint8_t* setme);
|
||||
int flush_torrent(tr_torrent_id_t tor_id);
|
||||
int flush_file(tr_torrent const& tor, tr_file_index_t file);
|
||||
|
||||
private:
|
||||
using Key = std::pair<tr_torrent_id_t, tr_block_index_t>;
|
||||
|
@ -56,7 +54,7 @@ private:
|
|||
using Blocks = std::vector<CacheBlock>;
|
||||
using CIter = Blocks::const_iterator;
|
||||
|
||||
[[nodiscard]] static Key make_key(tr_torrent const* torrent, tr_block_info::Location loc) noexcept;
|
||||
[[nodiscard]] static Key make_key(tr_torrent const& tor, tr_block_info::Location loc) noexcept;
|
||||
|
||||
[[nodiscard]] static std::pair<CIter, CIter> find_biggest_span(CIter begin, CIter end) noexcept;
|
||||
|
||||
|
@ -79,7 +77,7 @@ private:
|
|||
return max_size.base_quantity() / tr_block_info::BlockSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] CIter get_block(tr_torrent const* torrent, tr_block_info::Location const& loc) noexcept;
|
||||
[[nodiscard]] CIter get_block(tr_torrent const& tor, tr_block_info::Location const& loc) noexcept;
|
||||
|
||||
tr_torrents const& torrents_;
|
||||
|
||||
|
@ -91,15 +89,13 @@ private:
|
|||
mutable size_t cache_writes_ = 0;
|
||||
mutable size_t cache_write_bytes_ = 0;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
static constexpr struct
|
||||
{
|
||||
[[nodiscard]] constexpr bool operator()(Key const& key, CacheBlock const& block)
|
||||
[[nodiscard]] constexpr bool operator()(Key const& key, CacheBlock const& block) const
|
||||
{
|
||||
return key < block.key;
|
||||
}
|
||||
[[nodiscard]] constexpr bool operator()(CacheBlock const& block, Key const& key)
|
||||
[[nodiscard]] constexpr bool operator()(CacheBlock const& block, Key const& key) const
|
||||
{
|
||||
return block.key < key;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
#include "libtransmission/block-info.h"
|
||||
#include "libtransmission/completion.h"
|
||||
#include "libtransmission/tr-assert.h"
|
||||
#include "libtransmission/torrent.h"
|
||||
|
||||
tr_completion::tr_completion(tr_torrent const* tor, tr_block_info const* block_info)
|
||||
: tr_completion{ [tor](tr_piece_index_t const piece) { return tor->piece_is_wanted(piece); }, block_info }
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t tr_completion::compute_has_valid() const
|
||||
{
|
||||
|
@ -55,7 +61,7 @@ uint64_t tr_completion::compute_size_when_done() const
|
|||
auto size = uint64_t{ 0 };
|
||||
for (tr_piece_index_t piece = 0, n_pieces = block_info_->piece_count(); piece < n_pieces; ++piece)
|
||||
{
|
||||
if (tor_->piece_is_wanted(piece))
|
||||
if (piece_is_wanted_(piece))
|
||||
{
|
||||
size += block_info_->piece_size(piece);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
|
@ -26,21 +27,18 @@
|
|||
*/
|
||||
struct tr_completion
|
||||
{
|
||||
struct torrent_view
|
||||
{
|
||||
virtual bool piece_is_wanted(tr_piece_index_t piece) const = 0;
|
||||
using PieceIsWantedFunc = std::function<bool(tr_piece_index_t piece)>;
|
||||
|
||||
virtual ~torrent_view() = default;
|
||||
};
|
||||
|
||||
explicit tr_completion(torrent_view const* tor, tr_block_info const* block_info)
|
||||
: tor_{ tor }
|
||||
tr_completion(PieceIsWantedFunc&& piece_is_wanted, tr_block_info const* block_info)
|
||||
: piece_is_wanted_{ std::move(piece_is_wanted) }
|
||||
, block_info_{ block_info }
|
||||
, blocks_{ block_info_->block_count() }
|
||||
{
|
||||
blocks_.set_has_none();
|
||||
}
|
||||
|
||||
tr_completion(tr_torrent const* tor, tr_block_info const* block_info);
|
||||
|
||||
[[nodiscard]] constexpr tr_bitfield const& blocks() const noexcept
|
||||
{
|
||||
return blocks_;
|
||||
|
@ -175,7 +173,7 @@ private:
|
|||
|
||||
void remove_block(tr_block_index_t block);
|
||||
|
||||
torrent_view const* tor_;
|
||||
PieceIsWantedFunc piece_is_wanted_;
|
||||
tr_block_info const* block_info_;
|
||||
|
||||
tr_bitfield blocks_{ 0 };
|
||||
|
|
|
@ -93,101 +93,72 @@ bool check_ccrypto_result(CCCryptorStatus result, char const* file, long line)
|
|||
|
||||
} // namespace
|
||||
|
||||
// ---
|
||||
// --- sha1
|
||||
|
||||
namespace
|
||||
tr_sha1::tr_sha1()
|
||||
{
|
||||
|
||||
class Sha1Impl final : public tr_sha1
|
||||
{
|
||||
public:
|
||||
Sha1Impl()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
~Sha1Impl() override = default;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
CC_SHA1_Init(&handle_);
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
static auto constexpr Max = static_cast<size_t>(std::numeric_limits<CC_LONG>::max());
|
||||
auto const* sha_data = static_cast<uint8_t const*>(data);
|
||||
while (data_length > 0)
|
||||
{
|
||||
auto const n_bytes = static_cast<CC_LONG>(std::min(data_length, Max));
|
||||
CC_SHA1_Update(&handle_, sha_data, n_bytes);
|
||||
data_length -= n_bytes;
|
||||
sha_data += n_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha1_digest_t finish() override
|
||||
{
|
||||
auto digest = tr_sha1_digest_t{};
|
||||
CC_SHA1_Final(reinterpret_cast<unsigned char*>(std::data(digest)), &handle_);
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
CC_SHA1_CTX handle_ = {};
|
||||
};
|
||||
|
||||
class Sha256Impl final : public tr_sha256
|
||||
{
|
||||
public:
|
||||
Sha256Impl()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
~Sha256Impl() override = default;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
CC_SHA256_Init(&handle_);
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
static auto constexpr Max = static_cast<size_t>(std::numeric_limits<CC_LONG>::max());
|
||||
auto const* sha_data = static_cast<uint8_t const*>(data);
|
||||
while (data_length > 0)
|
||||
{
|
||||
auto const n_bytes = static_cast<CC_LONG>(std::min(data_length, Max));
|
||||
CC_SHA256_Update(&handle_, sha_data, n_bytes);
|
||||
data_length -= n_bytes;
|
||||
sha_data += n_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha256_digest_t finish() override
|
||||
{
|
||||
auto digest = tr_sha256_digest_t{};
|
||||
CC_SHA256_Final(reinterpret_cast<unsigned char*>(std::data(digest)), &handle_);
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
CC_SHA256_CTX handle_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<tr_sha1> tr_sha1::create()
|
||||
{
|
||||
return std::make_unique<Sha1Impl>();
|
||||
clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<tr_sha256> tr_sha256::create()
|
||||
tr_sha1::~tr_sha1()
|
||||
{
|
||||
return std::make_unique<Sha256Impl>();
|
||||
}
|
||||
|
||||
void tr_sha1::clear()
|
||||
{
|
||||
CC_SHA1_Init(&handle_);
|
||||
}
|
||||
|
||||
void tr_sha1::add(void const* data, size_t data_length)
|
||||
{
|
||||
if (data_length == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CC_SHA1_Update(&handle_, data, data_length);
|
||||
}
|
||||
|
||||
tr_sha1_digest_t tr_sha1::finish()
|
||||
{
|
||||
auto digest = tr_sha1_digest_t{};
|
||||
CC_SHA1_Final(reinterpret_cast<unsigned char*>(std::data(digest)), &handle_);
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// --- sha256
|
||||
|
||||
tr_sha256::tr_sha256()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
tr_sha256::~tr_sha256()
|
||||
{
|
||||
}
|
||||
|
||||
void tr_sha256::clear()
|
||||
{
|
||||
CC_SHA256_Init(&handle_);
|
||||
}
|
||||
|
||||
void tr_sha256::add(void const* data, size_t data_length)
|
||||
{
|
||||
if (data_length == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CC_SHA256_Update(&handle_, data, data_length);
|
||||
}
|
||||
|
||||
tr_sha256_digest_t tr_sha256::finish()
|
||||
{
|
||||
auto digest = tr_sha256_digest_t{};
|
||||
CC_SHA256_Final(reinterpret_cast<unsigned char*>(std::data(digest)), &handle_);
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
|
|
@ -25,9 +25,13 @@
|
|||
#define TR_CRYPTO_X509_FALLBACK
|
||||
#include "libtransmission/crypto-utils-fallback.cc" // NOLINT(bugprone-suspicious-include)
|
||||
|
||||
// ---
|
||||
#if !defined(WITH_MBEDTLS)
|
||||
#error mbedtls module
|
||||
#endif
|
||||
|
||||
static void log_mbedtls_error(int error_code, char const* file, int line)
|
||||
namespace
|
||||
{
|
||||
void log_mbedtls_error(int error_code, char const* file, int line)
|
||||
{
|
||||
if (tr_logLevelIsActive(TR_LOG_ERROR))
|
||||
{
|
||||
|
@ -48,7 +52,7 @@ static void log_mbedtls_error(int error_code, char const* file, int line)
|
|||
|
||||
#define log_error(error_code) log_mbedtls_error((error_code), __FILE__, __LINE__)
|
||||
|
||||
static bool check_mbedtls_result(int result, int expected_result, char const* file, int line)
|
||||
bool check_mbedtls_result(int result, int expected_result, char const* file, int line)
|
||||
{
|
||||
bool const ret = result == expected_result;
|
||||
|
||||
|
@ -65,14 +69,14 @@ static bool check_mbedtls_result(int result, int expected_result, char const* fi
|
|||
|
||||
// ---
|
||||
|
||||
static int my_rand(void* /*context*/, unsigned char* buffer, size_t buffer_size)
|
||||
int my_rand(void* /*context*/, unsigned char* buffer, size_t buffer_size)
|
||||
{
|
||||
// since we're initializing tr_rand_buffer()'s rng, we can't use tr_rand_buffer() here
|
||||
tr_rand_buffer_std(buffer, buffer_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static mbedtls_ctr_drbg_context* get_rng()
|
||||
mbedtls_ctr_drbg_context* get_rng()
|
||||
{
|
||||
static mbedtls_ctr_drbg_context rng;
|
||||
static bool rng_initialized = false;
|
||||
|
@ -97,125 +101,102 @@ static mbedtls_ctr_drbg_context* get_rng()
|
|||
return &rng;
|
||||
}
|
||||
|
||||
static std::recursive_mutex rng_mutex_;
|
||||
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class Sha1Impl final : public tr_sha1
|
||||
{
|
||||
public:
|
||||
Sha1Impl()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
~Sha1Impl() override = default;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
mbedtls_sha1_init(&handle_);
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha1_starts_ret(&handle_);
|
||||
#else
|
||||
mbedtls_sha1_starts(&handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
if (data_length > 0U)
|
||||
{
|
||||
#if 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);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha1_digest_t finish() override
|
||||
{
|
||||
auto digest = tr_sha1_digest_t{};
|
||||
auto* const digest_as_uchar = reinterpret_cast<unsigned char*>(std::data(digest));
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha1_finish_ret(&handle_, digest_as_uchar);
|
||||
#else
|
||||
mbedtls_sha1_finish(&handle_, digest_as_uchar);
|
||||
#endif
|
||||
|
||||
mbedtls_sha1_free(&handle_);
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
mbedtls_sha1_context handle_ = {};
|
||||
};
|
||||
|
||||
class Sha256Impl final : public tr_sha256
|
||||
{
|
||||
public:
|
||||
Sha256Impl()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
~Sha256Impl() override = default;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
mbedtls_sha256_init(&handle_);
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha256_starts_ret(&handle_, 0);
|
||||
#else
|
||||
mbedtls_sha256_starts(&handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
if (data_length > 0U)
|
||||
{
|
||||
#if 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);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha256_digest_t finish() override
|
||||
{
|
||||
auto digest = tr_sha256_digest_t{};
|
||||
auto* const digest_as_uchar = reinterpret_cast<unsigned char*>(std::data(digest));
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha256_finish_ret(&handle_, digest_as_uchar);
|
||||
#else
|
||||
mbedtls_sha256_finish(&handle_, digest_as_uchar);
|
||||
#endif
|
||||
|
||||
mbedtls_sha256_free(&handle_);
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
mbedtls_sha256_context handle_ = {};
|
||||
};
|
||||
std::recursive_mutex rng_mutex_;
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<tr_sha1> tr_sha1::create()
|
||||
// --- sha1
|
||||
|
||||
tr_sha1::tr_sha1()
|
||||
{
|
||||
return std::make_unique<Sha1Impl>();
|
||||
clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<tr_sha256> tr_sha256::create()
|
||||
tr_sha1::~tr_sha1() = default;
|
||||
|
||||
void tr_sha1::clear()
|
||||
{
|
||||
return std::make_unique<Sha256Impl>();
|
||||
mbedtls_sha1_init(&handle_);
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha1_starts_ret(&handle_);
|
||||
#else
|
||||
mbedtls_sha1_starts(&handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tr_sha1::add(void const* data, size_t data_length)
|
||||
{
|
||||
if (data_length == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if 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);
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
mbedtls_sha1_finish_ret(&handle_, digest_as_uchar);
|
||||
#else
|
||||
mbedtls_sha1_finish(&handle_, digest_as_uchar);
|
||||
#endif
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// --- sha256
|
||||
|
||||
tr_sha256::tr_sha256()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
tr_sha256::~tr_sha256() = default;
|
||||
|
||||
void tr_sha256::clear()
|
||||
{
|
||||
mbedtls_sha256_init(&handle_);
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x02070000
|
||||
mbedtls_sha256_starts_ret(&handle_, 0);
|
||||
#else
|
||||
mbedtls_sha256_starts(&handle_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tr_sha256::add(void const* data, size_t data_length)
|
||||
{
|
||||
if (data_length == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if 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);
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
mbedtls_sha256_finish_ret(&handle_, digest_as_uchar);
|
||||
#else
|
||||
mbedtls_sha256_finish(&handle_, digest_as_uchar);
|
||||
#endif
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -229,6 +210,21 @@ bool tr_rand_buffer_crypto(void* buffer, size_t length)
|
|||
|
||||
TR_ASSERT(buffer != nullptr);
|
||||
|
||||
auto const lock = std::lock_guard(rng_mutex_);
|
||||
return check_result(mbedtls_ctr_drbg_random(get_rng(), static_cast<unsigned char*>(buffer), length));
|
||||
auto constexpr ChunkSize = size_t{ MBEDTLS_CTR_DRBG_MAX_REQUEST };
|
||||
static_assert(ChunkSize > 0U);
|
||||
|
||||
auto const lock = std::scoped_lock{ rng_mutex_ };
|
||||
|
||||
for (auto offset = size_t{ 0 }; offset < length; offset += ChunkSize)
|
||||
{
|
||||
if (!check_result(mbedtls_ctr_drbg_random(
|
||||
get_rng(),
|
||||
static_cast<unsigned char*>(buffer) + offset,
|
||||
std::min(ChunkSize, length - offset))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -29,37 +29,43 @@
|
|||
#include "libtransmission/tr-macros.h" // tr_sha1_digest_t, tr_sha25...
|
||||
#include "libtransmission/utils.h"
|
||||
|
||||
#if !defined(WITH_OPENSSL)
|
||||
#error OPENSSL module
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
void log_openssl_error(char const* file, int line)
|
||||
{
|
||||
unsigned long const error_code = ERR_get_error();
|
||||
|
||||
if (tr_logLevelIsActive(TR_LOG_ERROR))
|
||||
if (!tr_logLevelIsActive(TR_LOG_ERROR))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const error_code = ERR_get_error();
|
||||
|
||||
if (static bool strings_loaded = false; !strings_loaded)
|
||||
{
|
||||
if (static bool strings_loaded = false; !strings_loaded)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000 || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000)
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_crypto_strings();
|
||||
#else
|
||||
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr);
|
||||
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr);
|
||||
#endif
|
||||
|
||||
strings_loaded = true;
|
||||
}
|
||||
|
||||
auto buf = std::array<char, 512>{};
|
||||
ERR_error_string_n(error_code, std::data(buf), std::size(buf));
|
||||
tr_logAddMessage(
|
||||
file,
|
||||
line,
|
||||
TR_LOG_ERROR,
|
||||
fmt::format(
|
||||
_("{crypto_library} error: {error} ({error_code})"),
|
||||
fmt::arg("crypto_library", "OpenSSL"),
|
||||
fmt::arg("error", std::data(buf)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
strings_loaded = true;
|
||||
}
|
||||
|
||||
auto buf = std::array<char, 512>{};
|
||||
ERR_error_string_n(error_code, std::data(buf), std::size(buf));
|
||||
tr_logAddMessage(
|
||||
file,
|
||||
line,
|
||||
TR_LOG_ERROR,
|
||||
fmt::format(
|
||||
_("{crypto_library} error: {error} ({error_code})"),
|
||||
fmt::arg("crypto_library", "OpenSSL"),
|
||||
fmt::arg("error", std::data(buf)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
}
|
||||
|
||||
#define log_error() log_openssl_error(__FILE__, __LINE__)
|
||||
|
@ -78,136 +84,84 @@ bool check_openssl_result(int result, int expected_result, bool expected_equal,
|
|||
|
||||
#define check_result(result) check_openssl_result((result), 1, true, __FILE__, __LINE__)
|
||||
|
||||
namespace sha_helpers
|
||||
void digest_add_bytes(EVP_MD_CTX* ctx, void const* data, size_t data_length)
|
||||
{
|
||||
|
||||
class ShaHelper
|
||||
{
|
||||
public:
|
||||
using EvpFunc = decltype((EVP_sha1));
|
||||
|
||||
explicit ShaHelper(EvpFunc evp_func)
|
||||
: evp_func_{ evp_func }
|
||||
if (data_length != 0U)
|
||||
{
|
||||
clear();
|
||||
EVP_DigestUpdate(ctx, data, data_length);
|
||||
}
|
||||
|
||||
void clear() const
|
||||
{
|
||||
EVP_DigestInit_ex(handle_.get(), evp_func_(), nullptr);
|
||||
}
|
||||
|
||||
void update(void const* data, size_t data_length) const
|
||||
{
|
||||
if (data_length != 0U)
|
||||
{
|
||||
EVP_DigestUpdate(handle_.get(), data, data_length);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DigestType>
|
||||
[[nodiscard]] DigestType digest()
|
||||
{
|
||||
TR_ASSERT(handle_ != nullptr);
|
||||
|
||||
unsigned int hash_length = 0;
|
||||
auto digest = DigestType{};
|
||||
auto* const digest_as_uchar = reinterpret_cast<unsigned char*>(std::data(digest));
|
||||
[[maybe_unused]] bool const ok = check_result(EVP_DigestFinal_ex(handle_.get(), digest_as_uchar, &hash_length));
|
||||
TR_ASSERT(!ok || hash_length == std::size(digest));
|
||||
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
struct MessageDigestDeleter
|
||||
{
|
||||
void operator()(EVP_MD_CTX* ctx) const noexcept
|
||||
{
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
EvpFunc evp_func_;
|
||||
std::unique_ptr<EVP_MD_CTX, MessageDigestDeleter> const handle_{ EVP_MD_CTX_create() };
|
||||
};
|
||||
|
||||
class Sha1Impl final : public tr_sha1
|
||||
{
|
||||
public:
|
||||
Sha1Impl() = default;
|
||||
Sha1Impl(Sha1Impl&&) = delete;
|
||||
Sha1Impl(Sha1Impl const&) = delete;
|
||||
~Sha1Impl() override = default;
|
||||
Sha1Impl& operator=(Sha1Impl&&) = delete;
|
||||
Sha1Impl& operator=(Sha1Impl const&) = delete;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
helper_.clear();
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
helper_.update(data, data_length);
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha1_digest_t finish() override
|
||||
{
|
||||
return helper_.digest<tr_sha1_digest_t>();
|
||||
}
|
||||
|
||||
private:
|
||||
ShaHelper helper_{ EVP_sha1 };
|
||||
};
|
||||
|
||||
class Sha256Impl final : public tr_sha256
|
||||
{
|
||||
public:
|
||||
Sha256Impl() = default;
|
||||
Sha256Impl(Sha256Impl&&) = delete;
|
||||
Sha256Impl(Sha256Impl const&) = delete;
|
||||
~Sha256Impl() override = default;
|
||||
Sha256Impl& operator=(Sha256Impl&&) = delete;
|
||||
Sha256Impl& operator=(Sha256Impl const&) = delete;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
helper_.clear();
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
helper_.update(data, data_length);
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha256_digest_t finish() override
|
||||
{
|
||||
return helper_.digest<tr_sha256_digest_t>();
|
||||
}
|
||||
|
||||
private:
|
||||
ShaHelper helper_{ EVP_sha256 };
|
||||
};
|
||||
|
||||
} // namespace sha_helpers
|
||||
} // namespace
|
||||
|
||||
// --- sha
|
||||
|
||||
std::unique_ptr<tr_sha1> tr_sha1::create()
|
||||
{
|
||||
using namespace sha_helpers;
|
||||
|
||||
return std::make_unique<Sha1Impl>();
|
||||
}
|
||||
|
||||
std::unique_ptr<tr_sha256> tr_sha256::create()
|
||||
template<typename DigestType>
|
||||
DigestType digest_finish(EVP_MD_CTX* ctx)
|
||||
{
|
||||
using namespace sha_helpers;
|
||||
unsigned int hash_length = 0;
|
||||
auto digest = DigestType{};
|
||||
auto* const digest_as_uchar = reinterpret_cast<unsigned char*>(std::data(digest));
|
||||
[[maybe_unused]] bool const ok = check_result(EVP_DigestFinal_ex(ctx, digest_as_uchar, &hash_length));
|
||||
TR_ASSERT(!ok || hash_length == std::size(digest));
|
||||
return digest;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
return std::make_unique<Sha256Impl>();
|
||||
// --- sha1
|
||||
|
||||
tr_sha1::tr_sha1()
|
||||
: handle_{ EVP_MD_CTX_create() }
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
tr_sha1::~tr_sha1()
|
||||
{
|
||||
EVP_MD_CTX_destroy(handle_);
|
||||
}
|
||||
|
||||
void tr_sha1::clear()
|
||||
{
|
||||
EVP_DigestInit_ex(handle_, EVP_sha1(), nullptr);
|
||||
}
|
||||
|
||||
void tr_sha1::add(void const* data, size_t data_length)
|
||||
{
|
||||
digest_add_bytes(handle_, data, data_length);
|
||||
}
|
||||
|
||||
tr_sha1_digest_t tr_sha1::finish()
|
||||
{
|
||||
auto digest = digest_finish<tr_sha1_digest_t>(handle_);
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// --- sha256
|
||||
|
||||
tr_sha256::tr_sha256()
|
||||
: handle_{ EVP_MD_CTX_create() }
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
tr_sha256::~tr_sha256()
|
||||
{
|
||||
EVP_MD_CTX_destroy(handle_);
|
||||
}
|
||||
|
||||
void tr_sha256::clear()
|
||||
{
|
||||
EVP_DigestInit_ex(handle_, EVP_sha256(), nullptr);
|
||||
}
|
||||
|
||||
void tr_sha256::add(void const* data, size_t data_length)
|
||||
{
|
||||
digest_add_bytes(handle_, data, data_length);
|
||||
}
|
||||
|
||||
tr_sha256_digest_t tr_sha256::finish()
|
||||
{
|
||||
auto digest = digest_finish<tr_sha256_digest_t>(handle_);
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// --- x509
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
#include "libtransmission/tr-assert.h"
|
||||
#include "libtransmission/utils.h"
|
||||
|
||||
#ifndef WITH_WOLFSSL
|
||||
#error wolfssl module
|
||||
#endif
|
||||
|
||||
#if LIBWOLFSSL_VERSION_HEX >= 0x04000000 // 4.0.0
|
||||
using TR_WC_RNG = WC_RNG;
|
||||
#else
|
||||
|
@ -31,9 +35,9 @@ using TR_WC_RNG = RNG;
|
|||
#define TR_CRYPTO_X509_FALLBACK
|
||||
#include "crypto-utils-fallback.cc" // NOLINT(bugprone-suspicious-include)
|
||||
|
||||
// ---
|
||||
|
||||
static void log_wolfssl_error(int error_code, char const* file, int line)
|
||||
namespace
|
||||
{
|
||||
void log_wolfssl_error(int error_code, char const* file, int line)
|
||||
{
|
||||
if (tr_logLevelIsActive(TR_LOG_ERROR))
|
||||
{
|
||||
|
@ -49,7 +53,7 @@ static void log_wolfssl_error(int error_code, char const* file, int line)
|
|||
}
|
||||
}
|
||||
|
||||
static bool check_wolfssl_result(int result, char const* file, int line)
|
||||
bool check_wolfssl_result(int result, char const* file, int line)
|
||||
{
|
||||
bool const ret = result == 0;
|
||||
|
||||
|
@ -65,7 +69,7 @@ static bool check_wolfssl_result(int result, char const* file, int line)
|
|||
|
||||
// ---
|
||||
|
||||
static TR_WC_RNG* get_rng()
|
||||
TR_WC_RNG* get_rng()
|
||||
{
|
||||
static TR_WC_RNG rng;
|
||||
static bool rng_initialized = false;
|
||||
|
@ -83,93 +87,68 @@ static TR_WC_RNG* get_rng()
|
|||
return &rng;
|
||||
}
|
||||
|
||||
static std::mutex rng_mutex_;
|
||||
|
||||
// ---
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class Sha1Impl final : public tr_sha1
|
||||
{
|
||||
public:
|
||||
Sha1Impl()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
~Sha1Impl() override = default;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
wc_InitSha(&handle_);
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
if (data_length > 0U)
|
||||
{
|
||||
wc_ShaUpdate(&handle_, static_cast<byte const*>(data), data_length);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha1_digest_t finish() override
|
||||
{
|
||||
auto digest = tr_sha1_digest_t{};
|
||||
wc_ShaFinal(&handle_, reinterpret_cast<byte*>(std::data(digest)));
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
wc_Sha handle_ = {};
|
||||
};
|
||||
|
||||
class Sha256Impl final : public tr_sha256
|
||||
{
|
||||
public:
|
||||
Sha256Impl()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
~Sha256Impl() override = default;
|
||||
|
||||
void clear() override
|
||||
{
|
||||
wc_InitSha256(&handle_);
|
||||
}
|
||||
|
||||
void add(void const* data, size_t data_length) override
|
||||
{
|
||||
if (data_length > 0U)
|
||||
{
|
||||
wc_Sha256Update(&handle_, static_cast<byte const*>(data), data_length);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] tr_sha256_digest_t finish() override
|
||||
{
|
||||
auto digest = tr_sha256_digest_t{};
|
||||
wc_Sha256Final(&handle_, reinterpret_cast<byte*>(std::data(digest)));
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
private:
|
||||
wc_Sha256 handle_ = {};
|
||||
};
|
||||
std::mutex rng_mutex_;
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<tr_sha1> tr_sha1::create()
|
||||
// --- sha1
|
||||
|
||||
tr_sha1::tr_sha1()
|
||||
{
|
||||
return std::make_unique<Sha1Impl>();
|
||||
clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<tr_sha256> tr_sha256::create()
|
||||
tr_sha1::~tr_sha1() = default;
|
||||
|
||||
void tr_sha1::clear()
|
||||
{
|
||||
return std::make_unique<Sha256Impl>();
|
||||
wc_InitSha(&handle_);
|
||||
}
|
||||
|
||||
void tr_sha1::add(void const* data, size_t data_length)
|
||||
{
|
||||
if (data_length > 0U)
|
||||
{
|
||||
wc_ShaUpdate(&handle_, static_cast<byte const*>(data), data_length);
|
||||
}
|
||||
}
|
||||
|
||||
tr_sha1_digest_t tr_sha1::finish()
|
||||
{
|
||||
auto digest = tr_sha1_digest_t{};
|
||||
wc_ShaFinal(&handle_, reinterpret_cast<byte*>(std::data(digest)));
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// --- sha256
|
||||
|
||||
tr_sha256::tr_sha256()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
tr_sha256::~tr_sha256() = default;
|
||||
|
||||
void tr_sha256::clear()
|
||||
{
|
||||
wc_InitSha256(&handle_);
|
||||
}
|
||||
|
||||
void tr_sha256::add(void const* data, size_t data_length)
|
||||
{
|
||||
if (data_length > 0U)
|
||||
{
|
||||
wc_Sha256Update(&handle_, static_cast<byte const*>(data), data_length);
|
||||
}
|
||||
}
|
||||
|
||||
tr_sha256_digest_t tr_sha256::finish()
|
||||
{
|
||||
auto digest = tr_sha256_digest_t{};
|
||||
wc_Sha256Final(&handle_, reinterpret_cast<byte*>(std::data(digest)));
|
||||
clear();
|
||||
return digest;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
@ -183,6 +162,6 @@ bool tr_rand_buffer_crypto(void* buffer, size_t length)
|
|||
|
||||
TR_ASSERT(buffer != nullptr);
|
||||
|
||||
auto const lock = std::lock_guard(rng_mutex_);
|
||||
auto const lock = std::lock_guard{ rng_mutex_ };
|
||||
return check_result(wc_RNG_GenerateBlock(get_rng(), static_cast<byte*>(buffer), length));
|
||||
}
|
||||
|
|
|
@ -19,6 +19,28 @@
|
|||
#include "libtransmission/tr-macros.h" // tr_sha1_digest_t, tr_sha256_d...
|
||||
#include "libtransmission/tr-strbuf.h"
|
||||
|
||||
#if defined(WITH_CCRYPTO)
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
using tr_sha1_context_t = CC_SHA1_CTX;
|
||||
using tr_sha256_context_t = CC_SHA256_CTX;
|
||||
#elif defined(WITH_MBEDTLS)
|
||||
#include <mbedtls/sha1.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
using tr_sha1_context_t = mbedtls_sha1_context;
|
||||
using tr_sha256_context_t = mbedtls_sha256_context;
|
||||
#elif defined(WITH_OPENSSL)
|
||||
#include <openssl/evp.h>
|
||||
using tr_sha1_context_t = EVP_MD_CTX*;
|
||||
using tr_sha256_context_t = EVP_MD_CTX*;
|
||||
#elif defined(WITH_WOLFSSL)
|
||||
#include <wolfssl/wolfcrypt/sha.h>
|
||||
#include <wolfssl/wolfcrypt/sha256.h>
|
||||
using tr_sha1_context_t = wc_Sha;
|
||||
using tr_sha256_context_t = wc_Sha256;
|
||||
#else
|
||||
#error no crypto library specified
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup utils Utilities
|
||||
* @{
|
||||
|
@ -27,39 +49,53 @@
|
|||
class tr_sha1
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<tr_sha1> create();
|
||||
virtual ~tr_sha1() = default;
|
||||
tr_sha1();
|
||||
tr_sha1(tr_sha1&&) = delete;
|
||||
tr_sha1(tr_sha1 const&) = delete;
|
||||
tr_sha1& operator=(tr_sha1&&) = delete;
|
||||
tr_sha1& operator=(tr_sha1 const&) = delete;
|
||||
~tr_sha1();
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual void add(void const* data, size_t data_length) = 0;
|
||||
[[nodiscard]] virtual tr_sha1_digest_t finish() = 0;
|
||||
void add(void const* data, size_t data_length);
|
||||
[[nodiscard]] tr_sha1_digest_t finish();
|
||||
void clear();
|
||||
|
||||
template<typename... T>
|
||||
[[nodiscard]] static tr_sha1_digest_t digest(T const&... args)
|
||||
[[nodiscard]] static auto digest(T const&... args)
|
||||
{
|
||||
auto context = tr_sha1::create();
|
||||
(context->add(std::data(args), std::size(args)), ...);
|
||||
return context->finish();
|
||||
auto context = tr_sha1{};
|
||||
(context.add(std::data(args), std::size(args)), ...);
|
||||
return context.finish();
|
||||
}
|
||||
|
||||
private:
|
||||
tr_sha1_context_t handle_;
|
||||
};
|
||||
|
||||
class tr_sha256
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<tr_sha256> create();
|
||||
virtual ~tr_sha256() = default;
|
||||
tr_sha256();
|
||||
tr_sha256(tr_sha256&&) = delete;
|
||||
tr_sha256(tr_sha256 const&) = delete;
|
||||
tr_sha256& operator=(tr_sha256&&) = delete;
|
||||
tr_sha256& operator=(tr_sha256 const&) = delete;
|
||||
~tr_sha256();
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual void add(void const* data, size_t data_length) = 0;
|
||||
[[nodiscard]] virtual tr_sha256_digest_t finish() = 0;
|
||||
void add(void const* data, size_t data_length);
|
||||
[[nodiscard]] tr_sha256_digest_t finish();
|
||||
void clear();
|
||||
|
||||
template<typename... T>
|
||||
[[nodiscard]] static tr_sha256_digest_t digest(T const&... args)
|
||||
[[nodiscard]] static auto digest(T const&... args)
|
||||
{
|
||||
auto context = tr_sha256::create();
|
||||
(context->add(std::data(args), std::size(args)), ...);
|
||||
return context->finish();
|
||||
auto context = tr_sha256{};
|
||||
(context.add(std::data(args), std::size(args)), ...);
|
||||
return context.finish();
|
||||
}
|
||||
|
||||
private:
|
||||
tr_sha256_context_t handle_;
|
||||
};
|
||||
|
||||
/** @brief Opaque SSL context type. */
|
||||
|
|
|
@ -42,12 +42,23 @@ public:
|
|||
return has_value();
|
||||
}
|
||||
|
||||
void set(int code, std::string&& message)
|
||||
{
|
||||
code_ = code;
|
||||
message_ = std::move(message);
|
||||
}
|
||||
|
||||
void set(int code, std::string_view message)
|
||||
{
|
||||
code_ = code;
|
||||
message_.assign(message);
|
||||
}
|
||||
|
||||
void set(int code, char const* const message)
|
||||
{
|
||||
set(code, std::string_view{ message != nullptr ? message : "" });
|
||||
}
|
||||
|
||||
void prefix_message(std::string_view prefix)
|
||||
{
|
||||
message_.insert(std::begin(message_), std::begin(prefix), std::end(prefix));
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
#include <libtransmission/file.h>
|
||||
#include <libtransmission/utils.h>
|
||||
#include <libtransmission/utils.h> // for tr_file_save()
|
||||
#include <libtransmission/web-utils.h>
|
||||
#include <libtransmission/web.h>
|
||||
|
||||
|
@ -44,9 +44,9 @@ public:
|
|||
return iter != std::end(icons_) ? &iter->second : nullptr;
|
||||
}
|
||||
|
||||
void load(
|
||||
void load( //
|
||||
std::string_view url_in,
|
||||
IconFunc callback = [](Icon const&) {})
|
||||
IconFunc callback = [](Icon const&) { /*default callback is a no-op */ })
|
||||
{
|
||||
std::call_once(scan_once_flag_, &FaviconCache::scan_file_cache, this);
|
||||
|
||||
|
@ -99,8 +99,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
static inline constexpr auto Width = 16;
|
||||
static inline constexpr auto Height = 16;
|
||||
static constexpr auto Width = 16;
|
||||
static constexpr auto Height = 16;
|
||||
|
||||
private:
|
||||
class InFlightData
|
||||
|
@ -112,6 +112,9 @@ private:
|
|||
{
|
||||
}
|
||||
|
||||
InFlightData(InFlightData const&) = delete;
|
||||
InFlightData& operator=(InFlightData const&) = delete;
|
||||
|
||||
[[nodiscard]] constexpr auto const& sitename() const noexcept
|
||||
{
|
||||
return sitename_;
|
||||
|
@ -133,7 +136,7 @@ private:
|
|||
|
||||
[[nodiscard]] auto get_responses()
|
||||
{
|
||||
auto lock = std::lock_guard{ responses_mutex_ };
|
||||
auto lock = std::scoped_lock{ responses_mutex_ };
|
||||
|
||||
auto tmp = decltype(responses_){};
|
||||
std::swap(tmp, responses_);
|
||||
|
@ -142,7 +145,7 @@ private:
|
|||
|
||||
void add_response(std::string contents, long code)
|
||||
{
|
||||
auto lock = std::lock_guard{ responses_mutex_ };
|
||||
auto lock = std::scoped_lock{ responses_mutex_ };
|
||||
|
||||
responses_.emplace_back(std::move(contents), code);
|
||||
}
|
||||
|
@ -201,7 +204,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void mark_site_as_scraped(std::string_view sitename)
|
||||
void mark_site_as_scraped(std::string_view sitename) const
|
||||
{
|
||||
if (auto ofs = std::ofstream{ scraped_sitenames_filename_, std::ios_base::out | std::ios_base::app }; ofs.is_open())
|
||||
{
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
#else
|
||||
#include <sys/quota.h> /* quotactl() */
|
||||
#endif
|
||||
#if !defined(btodb) && defined(QIF_DQBLKSIZE_BITS)
|
||||
#define btodb(num) ((num) >> QIF_DQBLKSIZE_BITS)
|
||||
#endif
|
||||
#ifdef HAVE_GETMNTENT
|
||||
#ifdef __sun
|
||||
#include <fcntl.h>
|
||||
|
|
|
@ -19,7 +19,17 @@
|
|||
#include "libtransmission/torrent-metainfo.h"
|
||||
#include "libtransmission/tr-assert.h"
|
||||
|
||||
void tr_file_piece_map::reset(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files)
|
||||
tr_file_piece_map::tr_file_piece_map(tr_torrent_metainfo const& tm)
|
||||
{
|
||||
reset(tm);
|
||||
}
|
||||
|
||||
tr_file_piece_map::tr_file_piece_map(tr_block_info const& block_info, uint64_t const* const file_sizes, size_t const n_files)
|
||||
{
|
||||
reset(block_info, file_sizes, n_files);
|
||||
}
|
||||
|
||||
void tr_file_piece_map::reset(tr_block_info const& block_info, uint64_t const* const file_sizes, size_t const n_files)
|
||||
{
|
||||
file_bytes_.resize(n_files);
|
||||
file_bytes_.shrink_to_fit();
|
||||
|
@ -75,19 +85,58 @@ void tr_file_piece_map::reset(tr_torrent_metainfo const& tm)
|
|||
reset({ tm.total_size(), tm.piece_size() }, std::data(file_sizes), std::size(file_sizes));
|
||||
}
|
||||
|
||||
tr_file_piece_map::file_span_t tr_file_piece_map::file_span(tr_piece_index_t piece) const
|
||||
namespace
|
||||
{
|
||||
static constexpr auto Compare = CompareToSpan<tr_piece_index_t>{ false };
|
||||
template<typename T>
|
||||
struct CompareToSpan
|
||||
{
|
||||
using span_t = tr_file_piece_map::index_span_t<T>;
|
||||
|
||||
[[nodiscard]] constexpr int compare(T item, span_t span) const // <=>
|
||||
{
|
||||
if (item < span.begin)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (item >= span.end)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(T const item, span_t const span) const // <
|
||||
{
|
||||
return compare(item, span) < 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr int compare(span_t const span, T const item) const // <=>
|
||||
{
|
||||
return -compare(item, span);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(span_t const span, T const item) const // <
|
||||
{
|
||||
return compare(span, item) < 0;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
tr_file_piece_map::file_span_t tr_file_piece_map::file_span_for_piece(tr_piece_index_t const piece) const
|
||||
{
|
||||
static constexpr auto Compare = CompareToSpan<tr_piece_index_t>{};
|
||||
auto const begin = std::begin(file_pieces_);
|
||||
auto const& [equal_begin, equal_end] = std::equal_range(begin, std::end(file_pieces_), piece, Compare);
|
||||
auto const [equal_begin, equal_end] = std::equal_range(begin, std::end(file_pieces_), piece, Compare);
|
||||
return { static_cast<tr_file_index_t>(equal_begin - begin), static_cast<tr_file_index_t>(equal_end - begin) };
|
||||
}
|
||||
|
||||
tr_file_piece_map::file_offset_t tr_file_piece_map::file_offset(uint64_t offset, bool include_empty_files) const
|
||||
tr_file_piece_map::file_offset_t tr_file_piece_map::file_offset(uint64_t const offset) const
|
||||
{
|
||||
auto const compare = CompareToSpan<uint64_t>{ include_empty_files };
|
||||
static constexpr auto Compare = CompareToSpan<uint64_t>{};
|
||||
auto const begin = std::begin(file_bytes_);
|
||||
auto const it = std::lower_bound(begin, std::end(file_bytes_), offset, compare);
|
||||
auto const it = std::lower_bound(begin, std::end(file_bytes_), offset, Compare);
|
||||
tr_file_index_t const file_index = std::distance(begin, it);
|
||||
auto const file_offset = offset - it->begin;
|
||||
return file_offset_t{ file_index, file_offset };
|
||||
|
@ -95,13 +144,7 @@ tr_file_piece_map::file_offset_t tr_file_piece_map::file_offset(uint64_t offset,
|
|||
|
||||
// ---
|
||||
|
||||
void tr_file_priorities::reset(tr_file_piece_map const* fpm)
|
||||
{
|
||||
fpm_ = fpm;
|
||||
priorities_ = {};
|
||||
}
|
||||
|
||||
void tr_file_priorities::set(tr_file_index_t file, tr_priority_t new_priority)
|
||||
void tr_file_priorities::set(tr_file_index_t const file, tr_priority_t const new_priority)
|
||||
{
|
||||
if (std::empty(priorities_))
|
||||
{
|
||||
|
@ -110,14 +153,14 @@ void tr_file_priorities::set(tr_file_index_t file, tr_priority_t new_priority)
|
|||
return;
|
||||
}
|
||||
|
||||
priorities_.assign(std::size(*fpm_), TR_PRI_NORMAL);
|
||||
priorities_.assign(fpm_->file_count(), TR_PRI_NORMAL);
|
||||
priorities_.shrink_to_fit();
|
||||
}
|
||||
|
||||
priorities_[file] = new_priority;
|
||||
}
|
||||
|
||||
void tr_file_priorities::set(tr_file_index_t const* files, size_t n, tr_priority_t new_priority)
|
||||
void tr_file_priorities::set(tr_file_index_t const* const files, size_t const n, tr_priority_t const new_priority)
|
||||
{
|
||||
for (size_t i = 0U; i < n; ++i)
|
||||
{
|
||||
|
@ -125,9 +168,9 @@ void tr_file_priorities::set(tr_file_index_t const* files, size_t n, tr_priority
|
|||
}
|
||||
}
|
||||
|
||||
tr_priority_t tr_file_priorities::file_priority(tr_file_index_t file) const
|
||||
tr_priority_t tr_file_priorities::file_priority(tr_file_index_t const file) const
|
||||
{
|
||||
TR_ASSERT(file < std::size(*fpm_));
|
||||
TR_ASSERT(file < fpm_->file_count());
|
||||
|
||||
if (std::empty(priorities_))
|
||||
{
|
||||
|
@ -137,7 +180,7 @@ tr_priority_t tr_file_priorities::file_priority(tr_file_index_t file) const
|
|||
return priorities_[file];
|
||||
}
|
||||
|
||||
tr_priority_t tr_file_priorities::piece_priority(tr_piece_index_t piece) const
|
||||
tr_priority_t tr_file_priorities::piece_priority(tr_piece_index_t const piece) const
|
||||
{
|
||||
// increase priority if a file begins or ends in this piece
|
||||
// because that makes life easier for code/users using at incomplete files.
|
||||
|
@ -148,7 +191,7 @@ tr_priority_t tr_file_priorities::piece_priority(tr_piece_index_t piece) const
|
|||
}
|
||||
|
||||
// check the priorities of the files that touch this piece
|
||||
if (auto const [begin_file, end_file] = fpm_->file_span(piece); end_file <= std::size(priorities_))
|
||||
if (auto const [begin_file, end_file] = fpm_->file_span_for_piece(piece); end_file <= std::size(priorities_))
|
||||
{
|
||||
auto const begin = std::begin(priorities_) + begin_file;
|
||||
auto const end = std::begin(priorities_) + end_file;
|
||||
|
@ -163,33 +206,33 @@ tr_priority_t tr_file_priorities::piece_priority(tr_piece_index_t piece) const
|
|||
|
||||
// ---
|
||||
|
||||
void tr_files_wanted::reset(tr_file_piece_map const* fpm)
|
||||
tr_files_wanted::tr_files_wanted(tr_file_piece_map const* const fpm)
|
||||
: fpm_{ fpm }
|
||||
, wanted_{ fpm->file_count() }
|
||||
{
|
||||
fpm_ = fpm;
|
||||
wanted_ = tr_bitfield{ std::size(*fpm) };
|
||||
wanted_.set_has_all(); // by default we want all files
|
||||
}
|
||||
|
||||
void tr_files_wanted::set(tr_file_index_t file, bool wanted)
|
||||
void tr_files_wanted::set(tr_file_index_t const file, bool const wanted)
|
||||
{
|
||||
wanted_.set(file, wanted);
|
||||
}
|
||||
|
||||
void tr_files_wanted::set(tr_file_index_t const* files, size_t n, bool wanted)
|
||||
void tr_files_wanted::set(tr_file_index_t const* const files, size_t const n_files, bool const wanted)
|
||||
{
|
||||
for (size_t i = 0U; i < n; ++i)
|
||||
for (size_t idx = 0U; idx < n_files; ++idx)
|
||||
{
|
||||
set(files[i], wanted);
|
||||
set(files[idx], wanted);
|
||||
}
|
||||
}
|
||||
|
||||
bool tr_files_wanted::piece_wanted(tr_piece_index_t piece) const
|
||||
bool tr_files_wanted::piece_wanted(tr_piece_index_t const piece) const
|
||||
{
|
||||
if (wanted_.has_all())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto const [begin, end] = fpm_->file_span(piece);
|
||||
auto const [begin, end] = fpm_->file_span_for_piece(piece);
|
||||
return wanted_.count(begin, end) != 0U;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include "libtransmission/bitfield.h"
|
||||
#include "libtransmission/tr-macros.h" // TR_CONSTEXPR20
|
||||
#include "libtransmission/utils.h"
|
||||
|
||||
struct tr_block_info;
|
||||
struct tr_torrent_metainfo;
|
||||
|
@ -43,102 +42,43 @@ public:
|
|||
};
|
||||
|
||||
using file_offset_t = offset_t<tr_file_index_t>;
|
||||
explicit tr_file_piece_map(tr_torrent_metainfo const& tm);
|
||||
tr_file_piece_map(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files);
|
||||
|
||||
explicit tr_file_piece_map(tr_torrent_metainfo const& tm)
|
||||
{
|
||||
reset(tm);
|
||||
}
|
||||
|
||||
tr_file_piece_map(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files)
|
||||
{
|
||||
reset(block_info, file_sizes, n_files);
|
||||
}
|
||||
|
||||
void reset(tr_torrent_metainfo const& tm);
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 piece_span_t piece_span(tr_file_index_t file) const noexcept
|
||||
[[nodiscard]] TR_CONSTEXPR20 piece_span_t piece_span_for_file(tr_file_index_t const file) const noexcept
|
||||
{
|
||||
return file_pieces_[file];
|
||||
}
|
||||
|
||||
[[nodiscard]] file_span_t file_span(tr_piece_index_t piece) const;
|
||||
[[nodiscard]] file_span_t file_span_for_piece(tr_piece_index_t piece) const;
|
||||
|
||||
[[nodiscard]] file_offset_t file_offset(uint64_t offset, bool include_empty_files) const;
|
||||
[[nodiscard]] file_offset_t file_offset(uint64_t offset) const;
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 size_t size() const
|
||||
[[nodiscard]] TR_CONSTEXPR20 size_t file_count() const
|
||||
{
|
||||
return std::size(file_pieces_);
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 bool empty() const noexcept
|
||||
{
|
||||
return std::empty(file_pieces_);
|
||||
}
|
||||
|
||||
// TODO(ckerr) minor wart here, two identical span types
|
||||
[[nodiscard]] TR_CONSTEXPR20 tr_byte_span_t byte_span(tr_file_index_t file) const
|
||||
[[nodiscard]] TR_CONSTEXPR20 auto byte_span_for_file(tr_file_index_t const file) const
|
||||
{
|
||||
auto const& span = file_bytes_[file];
|
||||
return tr_byte_span_t{ span.begin, span.end };
|
||||
}
|
||||
|
||||
[[nodiscard]] TR_CONSTEXPR20 bool is_edge_piece(tr_piece_index_t piece) const
|
||||
[[nodiscard]] TR_CONSTEXPR20 bool is_edge_piece(tr_piece_index_t const piece) const
|
||||
{
|
||||
return std::binary_search(std::begin(edge_pieces_), std::end(edge_pieces_), piece);
|
||||
}
|
||||
|
||||
private:
|
||||
using byte_span_t = index_span_t<uint64_t>;
|
||||
|
||||
void reset(tr_torrent_metainfo const& tm);
|
||||
void reset(tr_block_info const& block_info, uint64_t const* file_sizes, size_t n_files);
|
||||
|
||||
using byte_span_t = index_span_t<uint64_t>;
|
||||
std::vector<byte_span_t> file_bytes_;
|
||||
|
||||
std::vector<piece_span_t> file_pieces_;
|
||||
|
||||
std::vector<tr_piece_index_t> edge_pieces_;
|
||||
|
||||
template<typename T>
|
||||
struct CompareToSpan
|
||||
{
|
||||
using span_t = index_span_t<T>;
|
||||
|
||||
[[nodiscard]] constexpr int compare(T item, span_t span) const // <=>
|
||||
{
|
||||
if (include_empty_span && span.begin == span.end)
|
||||
{
|
||||
return tr_compare_3way(item, span.begin);
|
||||
}
|
||||
|
||||
if (item < span.begin)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (item >= span.end)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(T item, span_t span) const // <
|
||||
{
|
||||
return compare(item, span) < 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr int compare(span_t span, T item) const // <=>
|
||||
{
|
||||
return -compare(item, span);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator()(span_t span, T item) const // <
|
||||
{
|
||||
return compare(span, item) < 0;
|
||||
}
|
||||
|
||||
bool const include_empty_span;
|
||||
};
|
||||
};
|
||||
|
||||
class tr_file_priorities
|
||||
|
@ -149,7 +89,6 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void reset(tr_file_piece_map const*);
|
||||
void set(tr_file_index_t file, tr_priority_t priority);
|
||||
void set(tr_file_index_t const* files, size_t n, tr_priority_t priority);
|
||||
|
||||
|
@ -164,12 +103,7 @@ private:
|
|||
class tr_files_wanted
|
||||
{
|
||||
public:
|
||||
explicit tr_files_wanted(tr_file_piece_map const* fpm)
|
||||
: wanted_{ std::size(*fpm) }
|
||||
{
|
||||
reset(fpm);
|
||||
}
|
||||
void reset(tr_file_piece_map const* fpm);
|
||||
explicit tr_files_wanted(tr_file_piece_map const* fpm);
|
||||
|
||||
void set(tr_file_index_t file, bool wanted);
|
||||
void set(tr_file_index_t const* files, size_t n, bool wanted);
|
||||
|
|
|
@ -273,7 +273,7 @@ std::string_view tr_sys_path_dirname(std::string_view path)
|
|||
|
||||
auto const has_root = path[0] == '/';
|
||||
auto end = std::string_view::npos;
|
||||
auto matched_slash = bool{ true };
|
||||
auto matched_slash = true;
|
||||
|
||||
for (auto i = len - 1; i >= 1U; --i)
|
||||
{
|
||||
|
@ -545,37 +545,6 @@ char* tr_sys_path_native_separators(char* path)
|
|||
return path;
|
||||
}
|
||||
|
||||
tr_sys_file_t tr_sys_file_get_std(tr_std_sys_file_t std_file, tr_error* error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
|
||||
switch (std_file)
|
||||
{
|
||||
case TR_STD_SYS_FILE_IN:
|
||||
ret = STDIN_FILENO;
|
||||
break;
|
||||
|
||||
case TR_STD_SYS_FILE_OUT:
|
||||
ret = STDOUT_FILENO;
|
||||
break;
|
||||
|
||||
case TR_STD_SYS_FILE_ERR:
|
||||
ret = STDERR_FILENO;
|
||||
break;
|
||||
|
||||
default:
|
||||
TR_ASSERT_MSG(false, fmt::format(FMT_STRING("unknown standard file {:d}"), static_cast<int>(std_file)));
|
||||
|
||||
if (error != nullptr)
|
||||
{
|
||||
error->set_from_errno(EINVAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_file_t tr_sys_file_open(char const* path, int flags, int permissions, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(path != nullptr);
|
||||
|
@ -797,37 +766,6 @@ bool tr_sys_file_write_at(
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_flush(tr_sys_file_t handle, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
bool const ret = (fsync(handle) != -1);
|
||||
|
||||
if (error != nullptr && !ret)
|
||||
{
|
||||
error->set_from_errno(errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_flush_possible(tr_sys_file_t handle, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
if (struct stat statbuf = {}; fstat(handle, &statbuf) == 0)
|
||||
{
|
||||
return S_ISREG(statbuf.st_mode);
|
||||
}
|
||||
|
||||
if (error != nullptr)
|
||||
{
|
||||
error->set_from_errno(errno);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tr_sys_file_truncate(tr_sys_file_t handle, uint64_t size, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
@ -842,58 +780,6 @@ bool tr_sys_file_truncate(tr_sys_file_t handle, uint64_t size, tr_error* error)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_advise(
|
||||
[[maybe_unused]] tr_sys_file_t handle,
|
||||
[[maybe_unused]] uint64_t offset,
|
||||
[[maybe_unused]] uint64_t size,
|
||||
[[maybe_unused]] tr_sys_file_advice_t advice,
|
||||
[[maybe_unused]] tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
TR_ASSERT(size > 0);
|
||||
TR_ASSERT(advice == TR_SYS_FILE_ADVICE_WILL_NEED || advice == TR_SYS_FILE_ADVICE_DONT_NEED);
|
||||
|
||||
bool ret = true;
|
||||
|
||||
#if defined(HAVE_POSIX_FADVISE)
|
||||
|
||||
int const native_advice = advice == TR_SYS_FILE_ADVICE_WILL_NEED ?
|
||||
POSIX_FADV_WILLNEED :
|
||||
(advice == TR_SYS_FILE_ADVICE_DONT_NEED ? POSIX_FADV_DONTNEED : POSIX_FADV_NORMAL);
|
||||
|
||||
TR_ASSERT(native_advice != POSIX_FADV_NORMAL);
|
||||
|
||||
if (int const code = posix_fadvise(handle, offset, size, native_advice); code != 0)
|
||||
{
|
||||
if (error != nullptr)
|
||||
{
|
||||
error->set_from_errno(errno);
|
||||
}
|
||||
|
||||
ret = false;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
if (advice == TR_SYS_FILE_ADVICE_WILL_NEED)
|
||||
{
|
||||
auto radv = radvisory{};
|
||||
radv.ra_offset = offset;
|
||||
radv.ra_count = size;
|
||||
|
||||
ret = fcntl(handle, F_RDADVISE, &radv) != -1;
|
||||
|
||||
if (error != nullptr && !ret)
|
||||
{
|
||||
error->set_from_errno(errno);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace preallocate_helpers
|
||||
|
@ -1029,8 +915,6 @@ bool tr_sys_file_lock([[maybe_unused]] tr_sys_file_t handle, [[maybe_unused]] in
|
|||
TR_ASSERT(
|
||||
!!(operation & TR_SYS_FILE_LOCK_SH) + !!(operation & TR_SYS_FILE_LOCK_EX) + !!(operation & TR_SYS_FILE_LOCK_UN) == 1);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
#if defined(F_OFD_SETLK)
|
||||
|
||||
struct flock fl = {};
|
||||
|
@ -1048,62 +932,68 @@ bool tr_sys_file_lock([[maybe_unused]] tr_sys_file_t handle, [[maybe_unused]] in
|
|||
case TR_SYS_FILE_LOCK_UN:
|
||||
fl.l_type = F_UNLCK;
|
||||
break;
|
||||
|
||||
default:
|
||||
errno = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
fl.l_whence = SEEK_SET;
|
||||
|
||||
do
|
||||
{
|
||||
ret = fcntl(handle, (operation & TR_SYS_FILE_LOCK_NB) != 0 ? F_OFD_SETLK : F_OFD_SETLKW, &fl) != -1;
|
||||
} while (!ret && errno == EINTR);
|
||||
int const native_operation = (operation & TR_SYS_FILE_LOCK_NB) != 0 ? F_OFD_SETLK : F_OFD_SETLKW;
|
||||
|
||||
if (!ret && errno == EAGAIN)
|
||||
auto result = std::optional<bool>{};
|
||||
while (!result)
|
||||
{
|
||||
errno = EWOULDBLOCK;
|
||||
if (fcntl(handle, native_operation, &fl) != -1)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(HAVE_FLOCK)
|
||||
|
||||
int native_operation = 0;
|
||||
int const native_operation = //
|
||||
(((operation & TR_SYS_FILE_LOCK_SH) != 0) ? LOCK_SH : 0) | //
|
||||
(((operation & TR_SYS_FILE_LOCK_EX) != 0) ? LOCK_EX : 0) | //
|
||||
(((operation & TR_SYS_FILE_LOCK_NB) != 0) ? LOCK_NB : 0) | //
|
||||
(((operation & TR_SYS_FILE_LOCK_UN) != 0) ? LOCK_UN : 0);
|
||||
|
||||
if ((operation & TR_SYS_FILE_LOCK_SH) != 0)
|
||||
auto result = std::optional<bool>{};
|
||||
while (!result)
|
||||
{
|
||||
native_operation |= LOCK_SH;
|
||||
if (flock(handle, native_operation) != -1)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((operation & TR_SYS_FILE_LOCK_EX) != 0)
|
||||
{
|
||||
native_operation |= LOCK_EX;
|
||||
}
|
||||
|
||||
if ((operation & TR_SYS_FILE_LOCK_NB) != 0)
|
||||
{
|
||||
native_operation |= LOCK_NB;
|
||||
}
|
||||
|
||||
if ((operation & TR_SYS_FILE_LOCK_UN) != 0)
|
||||
{
|
||||
native_operation |= LOCK_UN;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ret = flock(handle, native_operation) != -1;
|
||||
} while (!ret && errno == EINTR);
|
||||
|
||||
#else
|
||||
|
||||
errno = ENOSYS;
|
||||
ret = false;
|
||||
auto const result = std::optional<bool>{ false };
|
||||
|
||||
#endif
|
||||
|
||||
if (error != nullptr && !ret)
|
||||
if (!*result && errno == EAGAIN)
|
||||
{
|
||||
errno = EWOULDBLOCK;
|
||||
}
|
||||
|
||||
if (error != nullptr && !*result)
|
||||
{
|
||||
error->set_from_errno(errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return *result;
|
||||
}
|
||||
|
||||
std::string tr_sys_dir_get_current(tr_error* error)
|
||||
|
@ -1150,7 +1040,7 @@ namespace
|
|||
{
|
||||
if (error != nullptr)
|
||||
{
|
||||
error->set(ENOTDIR, fmt::format(FMT_STRING("File is in the way: {:s}"), path));
|
||||
error->set(ENOTDIR, fmt::format("File is in the way: {:s}", path));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1180,7 +1070,7 @@ bool tr_sys_dir_create(char const* path, int flags, int permissions, tr_error* e
|
|||
{
|
||||
TR_ASSERT(path != nullptr);
|
||||
|
||||
auto ret = bool{ false };
|
||||
auto ret = false;
|
||||
auto local_error = tr_error{};
|
||||
|
||||
if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
|
||||
|
|
|
@ -43,19 +43,21 @@ struct tr_sys_dir_win32
|
|||
std::string utf8_name;
|
||||
};
|
||||
|
||||
static auto constexpr NativeLocalPathPrefix = L"\\\\?\\"sv;
|
||||
static auto constexpr NativeUncPathPrefix = L"\\\\?\\UNC\\"sv;
|
||||
namespace
|
||||
{
|
||||
auto constexpr NativeLocalPathPrefix = L"\\\\?\\"sv;
|
||||
auto constexpr NativeUncPathPrefix = L"\\\\?\\UNC\\"sv;
|
||||
|
||||
static void set_system_error(tr_error* error, DWORD code)
|
||||
void set_system_error(tr_error* error, DWORD code)
|
||||
{
|
||||
if (error != nullptr)
|
||||
{
|
||||
auto const message = tr_win32_format_message(code);
|
||||
error->set(code, !std::empty(message) ? message : fmt::format(FMT_STRING("Unknown error: {:#08x}"), code));
|
||||
error->set(code, !std::empty(message) ? message : fmt::format("Unknown error: {:#08x}", code));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_system_error_if_file_found(tr_error* error, DWORD code)
|
||||
void set_system_error_if_file_found(tr_error* error, DWORD code)
|
||||
{
|
||||
if (code != ERROR_FILE_NOT_FOUND && code != ERROR_PATH_NOT_FOUND && code != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
|
@ -63,7 +65,7 @@ static void set_system_error_if_file_found(tr_error* error, DWORD code)
|
|||
}
|
||||
}
|
||||
|
||||
static constexpr time_t filetime_to_unix_time(FILETIME const& t)
|
||||
constexpr time_t filetime_to_unix_time(FILETIME const& t)
|
||||
{
|
||||
uint64_t tmp = 0;
|
||||
tmp |= t.dwHighDateTime;
|
||||
|
@ -75,7 +77,7 @@ static constexpr time_t filetime_to_unix_time(FILETIME const& t)
|
|||
return tmp / 1000000UL;
|
||||
}
|
||||
|
||||
static constexpr auto stat_to_sys_path_info(DWORD attributes, DWORD size_low, DWORD size_high, FILETIME const& mtime)
|
||||
constexpr auto stat_to_sys_path_info(DWORD attributes, DWORD size_low, DWORD size_high, FILETIME const& mtime)
|
||||
{
|
||||
auto info = tr_sys_path_info{};
|
||||
|
||||
|
@ -101,19 +103,19 @@ static constexpr auto stat_to_sys_path_info(DWORD attributes, DWORD size_low, DW
|
|||
return info;
|
||||
}
|
||||
|
||||
static auto constexpr Slashes = "\\/"sv;
|
||||
auto constexpr Slashes = "\\/"sv;
|
||||
|
||||
static constexpr bool is_slash(char c)
|
||||
constexpr bool is_slash(char c)
|
||||
{
|
||||
return tr_strv_contains(Slashes, c);
|
||||
}
|
||||
|
||||
static constexpr bool is_unc_path(std::string_view path)
|
||||
constexpr bool is_unc_path(std::string_view path)
|
||||
{
|
||||
return std::size(path) >= 2 && is_slash(path[0]) && path[1] == path[0];
|
||||
}
|
||||
|
||||
static bool is_valid_path(std::string_view path)
|
||||
bool is_valid_path(std::string_view path)
|
||||
{
|
||||
if (is_unc_path(path))
|
||||
{
|
||||
|
@ -140,11 +142,6 @@ static bool is_valid_path(std::string_view path)
|
|||
return path.find_first_of("<>:\"|?*"sv) == path.npos;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace path_to_native_path_helpers
|
||||
{
|
||||
|
||||
auto path_to_fixed_native_path(std::string_view path)
|
||||
{
|
||||
auto wide_path = tr_win32_utf8_to_native(path);
|
||||
|
@ -168,15 +165,10 @@ auto path_to_fixed_native_path(std::string_view path)
|
|||
return wide_path;
|
||||
}
|
||||
|
||||
} // namespace path_to_native_path_helpers
|
||||
} // namespace
|
||||
|
||||
/* Extending maximum path length limit up to ~32K. See "Naming Files, Paths, and Namespaces"
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx for more info */
|
||||
static auto path_to_native_path(std::string_view path)
|
||||
auto path_to_native_path(std::string_view path)
|
||||
{
|
||||
using namespace path_to_native_path_helpers;
|
||||
|
||||
if (is_unc_path(path))
|
||||
{
|
||||
// UNC path: "\\server\share" -> "\\?\UNC\server\share"
|
||||
|
@ -197,7 +189,7 @@ static auto path_to_native_path(std::string_view path)
|
|||
return path_to_fixed_native_path(path);
|
||||
}
|
||||
|
||||
static std::string native_path_to_path(std::wstring_view wide_path)
|
||||
std::string native_path_to_path(std::wstring_view wide_path)
|
||||
{
|
||||
if (std::empty(wide_path))
|
||||
{
|
||||
|
@ -221,7 +213,7 @@ static std::string native_path_to_path(std::wstring_view wide_path)
|
|||
return tr_win32_native_to_utf8(wide_path);
|
||||
}
|
||||
|
||||
static tr_sys_file_t open_file(std::string_view path, DWORD access, DWORD disposition, DWORD flags, tr_error* error)
|
||||
tr_sys_file_t open_file(std::string_view path, DWORD access, DWORD disposition, DWORD flags, tr_error* error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
|
||||
|
@ -245,7 +237,7 @@ static tr_sys_file_t open_file(std::string_view path, DWORD access, DWORD dispos
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool create_dir(std::string_view path, int flags, int /*permissions*/, bool okay_if_exists, tr_error* error)
|
||||
bool create_dir(std::string_view path, int flags, int /*permissions*/, bool okay_if_exists, tr_error* error)
|
||||
{
|
||||
bool ret;
|
||||
DWORD error_code = ERROR_SUCCESS;
|
||||
|
@ -290,7 +282,7 @@ static bool create_dir(std::string_view path, int flags, int /*permissions*/, bo
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void create_temp_path(
|
||||
void create_temp_path(
|
||||
char* path_template,
|
||||
void (*callback)(char const* path, void* param, tr_error* error),
|
||||
void* callback_param,
|
||||
|
@ -339,6 +331,72 @@ static void create_temp_path(
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<tr_sys_path_info> tr_sys_file_get_info_(tr_sys_file_t handle, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
auto attributes = BY_HANDLE_FILE_INFORMATION{};
|
||||
if (GetFileInformationByHandle(handle, &attributes))
|
||||
{
|
||||
return stat_to_sys_path_info(
|
||||
attributes.dwFileAttributes,
|
||||
attributes.nFileSizeLow,
|
||||
attributes.nFileSizeHigh,
|
||||
attributes.ftLastWriteTime);
|
||||
}
|
||||
|
||||
set_system_error(error, GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<BY_HANDLE_FILE_INFORMATION> get_file_info(char const* path, tr_error* error)
|
||||
{
|
||||
auto const wpath = path_to_native_path(path);
|
||||
if (std::empty(wpath))
|
||||
{
|
||||
set_system_error_if_file_found(error, GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const handle = CreateFileW(wpath.c_str(), 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
set_system_error_if_file_found(error, GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO: Use GetFileInformationByHandleEx on >= Server 2012
|
||||
auto info = BY_HANDLE_FILE_INFORMATION{};
|
||||
if (!GetFileInformationByHandle(handle, &info))
|
||||
{
|
||||
set_system_error_if_file_found(error, GetLastError());
|
||||
CloseHandle(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
return info;
|
||||
}
|
||||
|
||||
void file_open_temp_callback(char const* path, void* param, tr_error* error)
|
||||
{
|
||||
auto* const result = static_cast<tr_sys_file_t*>(param);
|
||||
|
||||
TR_ASSERT(result != nullptr);
|
||||
|
||||
*result = open_file(path, GENERIC_READ | GENERIC_WRITE, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, error);
|
||||
}
|
||||
|
||||
void dir_create_temp_callback(char const* path, void* param, tr_error* error)
|
||||
{
|
||||
auto* const result = static_cast<bool*>(param);
|
||||
|
||||
TR_ASSERT(result != nullptr);
|
||||
|
||||
*result = create_dir(path, 0, 0, false, error);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool tr_sys_path_exists(char const* path, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(path != nullptr);
|
||||
|
@ -377,24 +435,6 @@ bool tr_sys_path_exists(char const* path, tr_error* error)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static std::optional<tr_sys_path_info> tr_sys_file_get_info_(tr_sys_file_t handle, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
auto attributes = BY_HANDLE_FILE_INFORMATION{};
|
||||
if (GetFileInformationByHandle(handle, &attributes))
|
||||
{
|
||||
return stat_to_sys_path_info(
|
||||
attributes.dwFileAttributes,
|
||||
attributes.nFileSizeLow,
|
||||
attributes.nFileSizeHigh,
|
||||
attributes.ftLastWriteTime);
|
||||
}
|
||||
|
||||
set_system_error(error, GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<tr_sys_path_info> tr_sys_path_get_info(std::string_view path, int flags, tr_error* error)
|
||||
{
|
||||
if (auto const wide_path = path_to_native_path(path); std::empty(wide_path))
|
||||
|
@ -449,35 +489,6 @@ bool tr_sys_path_is_relative(std::string_view path)
|
|||
return true;
|
||||
}
|
||||
|
||||
static std::optional<BY_HANDLE_FILE_INFORMATION> get_file_info(char const* path, tr_error* error)
|
||||
{
|
||||
auto const wpath = path_to_native_path(path);
|
||||
if (std::empty(wpath))
|
||||
{
|
||||
set_system_error_if_file_found(error, GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const handle = CreateFileW(wpath.c_str(), 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
set_system_error_if_file_found(error, GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO: Use GetFileInformationByHandleEx on >= Server 2012
|
||||
auto info = BY_HANDLE_FILE_INFORMATION{};
|
||||
if (!GetFileInformationByHandle(handle, &info))
|
||||
{
|
||||
set_system_error_if_file_found(error, GetLastError());
|
||||
CloseHandle(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
return info;
|
||||
}
|
||||
|
||||
bool tr_sys_path_is_same(char const* path1, char const* path2, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(path1 != nullptr);
|
||||
|
@ -808,42 +819,6 @@ char* tr_sys_path_native_separators(char* path)
|
|||
return path;
|
||||
}
|
||||
|
||||
tr_sys_file_t tr_sys_file_get_std(tr_std_sys_file_t std_file, tr_error* error)
|
||||
{
|
||||
tr_sys_file_t ret = TR_BAD_SYS_FILE;
|
||||
|
||||
switch (std_file)
|
||||
{
|
||||
case TR_STD_SYS_FILE_IN:
|
||||
ret = GetStdHandle(STD_INPUT_HANDLE);
|
||||
break;
|
||||
|
||||
case TR_STD_SYS_FILE_OUT:
|
||||
ret = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
break;
|
||||
|
||||
case TR_STD_SYS_FILE_ERR:
|
||||
ret = GetStdHandle(STD_ERROR_HANDLE);
|
||||
break;
|
||||
|
||||
default:
|
||||
TR_ASSERT_MSG(false, fmt::format(FMT_STRING("unknown standard file {:d}"), std_file));
|
||||
set_system_error(error, ERROR_INVALID_PARAMETER);
|
||||
return TR_BAD_SYS_FILE;
|
||||
}
|
||||
|
||||
if (ret == TR_BAD_SYS_FILE)
|
||||
{
|
||||
set_system_error(error, GetLastError());
|
||||
}
|
||||
else if (ret == nullptr)
|
||||
{
|
||||
ret = TR_BAD_SYS_FILE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
tr_sys_file_t tr_sys_file_open(char const* path, int flags, int /*permissions*/, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(path != nullptr);
|
||||
|
@ -899,15 +874,6 @@ tr_sys_file_t tr_sys_file_open(char const* path, int flags, int /*permissions*/,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void file_open_temp_callback(char const* path, void* param, tr_error* error)
|
||||
{
|
||||
auto* const result = static_cast<tr_sys_file_t*>(param);
|
||||
|
||||
TR_ASSERT(result != nullptr);
|
||||
|
||||
*result = open_file(path, GENERIC_READ | GENERIC_WRITE, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, error);
|
||||
}
|
||||
|
||||
tr_sys_file_t tr_sys_file_open_temp(char* path_template, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(path_template != nullptr);
|
||||
|
@ -1081,35 +1047,6 @@ bool tr_sys_file_write_at(
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_flush(tr_sys_file_t handle, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
bool ret = FlushFileBuffers(handle);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
set_system_error(error, GetLastError());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_flush_possible(tr_sys_file_t handle, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
DWORD type = GetFileType(handle);
|
||||
|
||||
if (type == FILE_TYPE_UNKNOWN)
|
||||
{
|
||||
set_system_error(error, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return type == FILE_TYPE_DISK;
|
||||
}
|
||||
|
||||
bool tr_sys_file_truncate(tr_sys_file_t handle, uint64_t size, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
@ -1127,24 +1064,6 @@ bool tr_sys_file_truncate(tr_sys_file_t handle, uint64_t size, tr_error* error)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_advise(
|
||||
[[maybe_unused]] tr_sys_file_t handle,
|
||||
uint64_t /*offset*/,
|
||||
[[maybe_unused]] uint64_t size,
|
||||
[[maybe_unused]] tr_sys_file_advice_t advice,
|
||||
tr_error* /*error*/)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
TR_ASSERT(size > 0);
|
||||
TR_ASSERT(advice == TR_SYS_FILE_ADVICE_WILL_NEED || advice == TR_SYS_FILE_ADVICE_DONT_NEED);
|
||||
|
||||
bool ret = true;
|
||||
|
||||
/* ??? */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_preallocate(tr_sys_file_t handle, uint64_t size, int flags, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
@ -1225,15 +1144,6 @@ bool tr_sys_dir_create(char const* path, int flags, int permissions, tr_error* e
|
|||
return create_dir(path, flags, permissions, true, error);
|
||||
}
|
||||
|
||||
static void dir_create_temp_callback(char const* path, void* param, tr_error* error)
|
||||
{
|
||||
auto* const result = static_cast<bool*>(param);
|
||||
|
||||
TR_ASSERT(result != nullptr);
|
||||
|
||||
*result = create_dir(path, 0, 0, false, error);
|
||||
}
|
||||
|
||||
bool tr_sys_dir_create_temp(char* path_template, tr_error* error)
|
||||
{
|
||||
TR_ASSERT(path_template != nullptr);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue