diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 53e4e94..8e71c95 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -6,8 +6,16 @@ -### What version of the product are you using? On what operating system? ### +### What version of the product are you using. (Please use the latest version of bittorrent-tracker-editor and your torrent client) ### + + + +### On what operating system? ### ### Please provide any additional information below. ### + + + +### Add a modified sintel torrent files example to reproduce the issue. [Download from Github: sintel](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/tree/master/test_torrent) ### diff --git a/.github/actions/install_lazarus/action.yaml b/.github/actions/install_lazarus/action.yaml new file mode 100644 index 0000000..f62e0c5 --- /dev/null +++ b/.github/actions/install_lazarus/action.yaml @@ -0,0 +1,101 @@ +# include this file in the cicd_windows.yaml, cicd_ubuntu.yaml, and cicd_macos.yaml like this: +# - name: Install Lazarus IDE +# uses: ./.github/actions/install_lazarus +# with: +# qt_version_ci: 6 # optional, only needed when building with qt5 or qt6 + + +# https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action +name: 'Install Lazaurus IDE' +description: 'download and build lazbuild' + +inputs: + qt_version_ci: # id of input + description: 'QT version' + required: false + +runs: + using: "composite" + steps: + - name: Install Lazarus IDE (Windows) + if: runner.os == 'Windows' + run: | + winget install lazarus --disable-interactivity --accept-source-agreements --silent + echo 'c:/lazarus' >> $GITHUB_PATH + shell: pwsh + + - name: Make PATH also in bash available (Windows) + if: runner.os == 'Windows' + run: | + echo 'c:/lazarus' >> $GITHUB_PATH + shell: bash + + - name: Install fpc (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y fpc + shell: bash + + - name: Install dependency for gtk2 + if: runner.os == 'Linux' && inputs.qt_version_ci == '' + run: sudo apt-get install -y libgtk2.0-dev + shell: bash + + - name: Install dependency for qt5 + if: runner.os == 'Linux' && inputs.qt_version_ci == '5' + run: sudo apt-get install -y libqt5x11extras5-dev + shell: bash + + - name: Install dependency for qt6 + if: runner.os == 'Linux' && inputs.qt_version_ci == '6' + run: sudo apt-get install -y qt6-base-dev + shell: bash + + - name: Install Free Pascal Compiler (FPC) multi arch version for macOS x86_64 and aarch64 (macOS) + if: runner.os == 'macOS' + run: brew install fpc + shell: bash + + - name: Download Lazarus source code into temp folder (Linux and macOS) + if: runner.os != 'Windows' + env: + # Linux and macOS need to build lazbuild from Lazarus source code. + # Copied the latest Lazarus source code from: https://sourceforge.net/projects/lazarus/files/Lazarus%20Zip%20_%20GZip/ + LAZARUS_URL_TAR_GZ: "https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/lazarus.tar.gz" + RUNNER_TEMP: ${{ runner.temp }} + run: | + #Download lazarus source code. Directory 'lazarus' will be created in the temp folder. + echo ${RUNNER_TEMP} + cd ${RUNNER_TEMP} + curl -L -O ${{ env.LAZARUS_URL_TAR_GZ }} + tar -xzf *.tar.gz + shell: bash + + - name: Build lazbuild from Lazarus source code (Linux and macOS) + if: runner.os != 'Windows' + env: + RUNNER_TEMP: ${{ runner.temp }} + run: | + # make lazbuild and put the link with extra parameter in the temp folder. + LAZARUS_DIR=${RUNNER_TEMP}/lazarus + cd "$LAZARUS_DIR" + make lazbuild + echo "$LAZARUS_DIR/lazbuild --primary-config-path=$LAZARUS_DIR --lazarusdir=$LAZARUS_DIR \$*" > ${RUNNER_TEMP}/lazbuild + chmod +x ${RUNNER_TEMP}/lazbuild + # Add lazbuild to the PATH variable. + echo ${RUNNER_TEMP} >> $GITHUB_PATH + shell: bash + + - name: Build libQTpas.so (qt5 or qt6) + if: runner.os == 'Linux' && inputs.qt_version_ci != '' + env: + RUNNER_TEMP: ${{ runner.temp }} + run: | + cd "${RUNNER_TEMP}/lazarus/lcl/interfaces/qt${{ inputs.qt_version_ci }}/cbindings/" + /usr/lib/qt${{ inputs.qt_version_ci }}/bin/qmake + make -j$(nproc) + sudo make install + shell: bash + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5b52009 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# Enable version updates for GitHub Actions at cron schedule every 6 months +# Can be manually triggered via menu: Insights > Dependency graph > "Recent update jobs" -> "Check for updates" + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "cron" + cronjob: "0 0 1 1/6 *" + commit-message: + prefix: "chore" + include: "scope" + open-pull-requests-limit: 5 diff --git a/.github/workflows/cicd_macos.yaml b/.github/workflows/cicd_macos.yaml new file mode 100644 index 0000000..92b4ed2 --- /dev/null +++ b/.github/workflows/cicd_macos.yaml @@ -0,0 +1,311 @@ +# CI/CD workflow for macOS systems to build bittorrent tracker editor app +# Uses GitHub Actions macOS runners to build the app for both Apple silicon (aarch64) and Intel Mac (x86_64) +# Then create a Universal binary using lipo tool. +# Finally create a dmg file for end user distribution. +# The build also codesign and notarize the dmg file if the required Apple developer certificate +# and notarization credentials are present in the GitHub secrets. +# The build is triggered on push, pull request, manual workflow dispatch and every 6 months cron job. +# +# macos-15-intel runner is used to build the x86_64 version. +# macos-latest runner is used to build the aarch64 version and also to create the Universal binary dmg file. +# The build uses Free Pascal Compiler (FPC) and Lazarus IDE to build the app. +# +# macos-15-intel runner is the last macOS runner that supports Intel architecture. +# Newer macOS runners only support Apple silicon (aarch64) architecture. +# Keep supporting Intel architecture build while there runners are still available. +# +# Must use macOS ditto tool to create zip files. +# Using zip command creates zip files that missing some metadata required for macOS apps. + + +name: CI/CD on macOS systems. + +permissions: + contents: write + +on: + push: + pull_request: + workflow_dispatch: + # Automatic cron build every 6 months to check if everything still works. + schedule: + - cron: "0 0 1 1/6 *" + +env: + MACOS_APP: enduser/trackereditor.app + PROGRAM_NAME_WITH_PATH: 'enduser/trackereditor' + BUILD_WITH_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + PROJECT_LPI: source/project/tracker_editor/trackereditor.lpi + RELEASE_DMG_FILE: trackereditor_macOS_notarized_universal_binary.dmg + +jobs: + build: + timeout-minutes: 60 + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. + fail-fast: false + + # Set up an include to perform the following build configurations. + matrix: + include: + - BUILD_TARGET: build_aarch64 + ARCH: aarch64 + RUNS_ON: macos-latest + + - BUILD_TARGET: build_x86_64 + ARCH: x86_64 + RUNS_ON: macos-15-intel + + name: ${{ matrix.BUILD_TARGET }} + runs-on: ${{ matrix.RUNS_ON }} + + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - name: Install Lazarus IDE + uses: ./.github/actions/install_lazarus + + - name: Build trackereditor app for Apple (${{ matrix.ARCH }}) + run: | + lazbuild --build-all --build-mode=Release --widgetset=cocoa --cpu=${{ matrix.ARCH }} ${{ env.PROJECT_LPI }} + shell: bash + + - name: Test App SSL connection + run: open "${{ env.PROGRAM_NAME_WITH_PATH }}" --args -TEST_SSL + shell: bash + + - name: Zip the macOS .app bundle for artifact upload + if: matrix.ARCH == 'aarch64' + run: ditto -c -k enduser/trackereditor.app enduser/trackereditor_app.zip + shell: bash + + - name: Rename built -aarch64 binary for artifact upload + run: | + mv ${{ env.PROGRAM_NAME_WITH_PATH }} ${{ env.PROGRAM_NAME_WITH_PATH }}-${{ matrix.ARCH }} + ditto -c -k ${{ env.PROGRAM_NAME_WITH_PATH }}-${{ matrix.ARCH }} ${{ env.PROGRAM_NAME_WITH_PATH }}-${{ matrix.ARCH }}.zip + shell: bash + + - name: Upload Artifact. + uses: actions/upload-artifact@v6 + with: + name: artifact-${{ matrix.ARCH }} + # Include both the zipped universal app bundle and the aarch64 binary zip + path: enduser/*.zip + if-no-files-found: error + + create_universal_macOS_binary: # Create Universal binary from aarch64 and x86_64 builds + needs: build + runs-on: macos-latest + timeout-minutes: 60 + + steps: + - uses: actions/checkout@v6 + with: + sparse-checkout: metainfo + + - name: Install create-dmg tool + run: brew install create-dmg + shell: bash + + - name: Download build artifacts from previous jobs + uses: actions/download-artifact@v7 + with: + path: enduser/ + merge-multiple: true + + - name: Display the downloaded artifact files + run: ls -R enduser/ + shell: bash + + - name: Unzip all the artifact files + run: | + ditto -xk enduser/trackereditor_app.zip enduser/trackereditor.app + ditto -xk ${{ env.PROGRAM_NAME_WITH_PATH }}-x86_64.zip enduser/ + ditto -xk ${{ env.PROGRAM_NAME_WITH_PATH }}-aarch64.zip enduser/ + # Remove the zip files after extraction + rm -f enduser/*.zip + shell: bash + + - name: Display the downloaded artifact files after unzip + run: ls -R enduser/ + shell: bash + + - name: Set correct version number in macOS .app bundle Info.plist from metainfo xml file + env: + METAINFO_FILE: metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.metainfo.xml + run: | + TRACKER_EDITOR_VERSION=$(xmllint --xpath "string(/component/releases/release[1]/@version)" $METAINFO_FILE) + echo Program version: $TRACKER_EDITOR_VERSION + plutil -replace CFBundleShortVersionString -string $TRACKER_EDITOR_VERSION ${{ env.MACOS_APP }}/Contents/Info.plist + shell: bash + + - name: Create and set app icon in macOS .app bundle + env: + ICON_FILE: 'metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.png' + run: | + + # ------ Create icon set and move it into the app + iconset_folder="temp_folder.iconset" + rm -rf "${iconset_folder}" + mkdir -p "${iconset_folder}" + + for s in 16 32 128 256 512; do + d=$(($s*2)) + sips -Z $s $ICON_FILE --out "${iconset_folder}/icon_${s}x$s.png" + sips -Z $d $ICON_FILE --out "${iconset_folder}/icon_${s}x$s@2x.png" + done + + # create .icns icon file + iconutil -c icns "${iconset_folder}" -o "iconfile.icns" + rm -r "${iconset_folder}" + + # move icon file to the app + mv -f "iconfile.icns" "${{ env.MACOS_APP }}/Contents/Resources" + + # add icon to plist xml file CFBundleIconFile = "iconfile" + plutil -insert CFBundleIconFile -string "iconfile" "${{ env.MACOS_APP }}/Contents/Info.plist" + shell: bash + + - name: Create a Universal macOS binary from aarch64 and x86_64 + run: | + # Create Universal binary using lipo tool + lipo -create -output ${{ env.PROGRAM_NAME_WITH_PATH }} ${{ env.PROGRAM_NAME_WITH_PATH }}-aarch64 ${{ env.PROGRAM_NAME_WITH_PATH }}-x86_64 + + # Remove the previous architecture specific binaries + rm -f ${{ env.PROGRAM_NAME_WITH_PATH }}-* + shell: bash + + - name: Replace the macOS .app bundle binary with the Universal binary + run: | + PROGRAM_NAME_ONLY=$(basename -- "${{ env.PROGRAM_NAME_WITH_PATH }}") + # remove the previous app (symbolic link) + rm -f "${{ env.MACOS_APP }}/Contents/MacOS/${PROGRAM_NAME_ONLY}" + # copy the program to the app version. + mv -f "${{ env.PROGRAM_NAME_WITH_PATH }}" "${{ env.MACOS_APP }}/Contents/MacOS" + ls -l "${{ env.MACOS_APP }}/Contents/MacOS/" + shell: bash + + - name: Display the enduser/ folder contents + run: ls -R enduser/ + shell: bash + + - name: Verify the Universal binary architectures + run: | + lipo -archs "${{ env.MACOS_APP }}"/Contents/MacOS/trackereditor + lipo -archs "${{ env.MACOS_APP }}"/Contents/MacOS/trackereditor | grep -Fq x86_64 + lipo -archs "${{ env.MACOS_APP }}"/Contents/MacOS/trackereditor | grep -Fq arm64 + shell: bash + + - name: Test App SSL connection + run: open "${{ env.MACOS_APP }}" --args -TEST_SSL + shell: bash + + - name: Codesign macOS app bundle. If certificate is present. + if: ${{ env.BUILD_WITH_CERTIFICATE != '' }} + # This macOS Codesign step is copied from: + # https://federicoterzi.com/blog/automatic-code-signing-and-notarization-for-macos-apps-using-github-actions/ + # This is a bit different from the previous version for Travis-CI build system to build bittorrent tracker editor + # More info https://developer.apple.com/forums/thread/128166 + env: + MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }} + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} + run: | + # Turn our base64-encoded certificate back to a regular .p12 file + echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12 + + # We need to create a new keychain, otherwise using the certificate will prompt + # with a UI dialog asking for the certificate password, which we can't + # use in a headless CI environment + + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain + security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain + + # sign the app. -sign is the developer cetificate ID + /usr/bin/codesign --timestamp --force --options runtime --sign "$MACOS_CERTIFICATE_NAME" "${{ env.MACOS_APP }}" + shell: bash + + - name: Display the enduser/ folder contents + run: ls -R enduser/ + shell: bash + + - name: Create dmg file from the enduser/ folder + run: | + # Build dmg image. https://github.com/create-dmg/create-dmg + create-dmg \ + --volname "bittorrent-tracker-editor" \ + --window-pos 200 120 \ + --window-size 800 400 \ + --icon "trackereditor.app" 200 190 \ + --app-drop-link 600 185 \ + ${{ env.RELEASE_DMG_FILE }} \ + "./enduser" + shell: bash + + - name: Codesign dmg file. If certificate is present. + if: ${{ env.BUILD_WITH_CERTIFICATE != '' }} + env: + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} + run: | + /usr/bin/codesign --timestamp --force --options runtime --sign "$MACOS_CERTIFICATE_NAME" "${{ env.RELEASE_DMG_FILE }}" + shell: bash + + - name: Notarize macOS DMG bundle. If certificate is present. + if: ${{ env.BUILD_WITH_CERTIFICATE != '' }} + env: + PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} + PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} + PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} + run: | + # Store the notarization credentials so that we can prevent a UI password dialog + # from blocking the CI + + echo "Create keychain profile" + xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" + + # We can't notarize an app bundle directly, but we need to compress it as an archive. + # Therefore, we create a zip file containing our app bundle, so that we can send it to the + # notarization service + + echo "Creating temp notarization archive" + ditto -c -k --keepParent "${{ env.RELEASE_DMG_FILE }}" "notarization.zip" + + # Here we send the notarization request to the Apple's Notarization service, waiting for the result. + # This typically takes a few seconds inside a CI environment, but it might take more depending on the App + # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if + # you're curious + + echo "Notarize app" + xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait + + # Finally, we need to "attach the staple" to our executable, which will allow our app to be + # validated by macOS even when an internet connection is not available. + echo "Attach staple" + xcrun stapler staple "${{ env.RELEASE_DMG_FILE }}" + shell: bash + + - name: Use diferent .dmg file name for non signed/notarize version + if: ${{ env.BUILD_WITH_CERTIFICATE == '' }} + run: mv ${{ env.RELEASE_DMG_FILE }} trackereditor_macOS_UNSIGNED_universal_binary.dmg + shell: bash + + - name: Upload Artifact. Signed/Notarize is optional. + uses: actions/upload-artifact@v6 + with: + name: artifact-${{ runner.os }} + path: "*.dmg" + compression-level: 0 # no compression. Content is already a zip file + if-no-files-found: error + + - name: Notarize file release to end user. If certificate is present. + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') && (env.BUILD_WITH_CERTIFICATE != '') + with: + files: ${{ env.RELEASE_DMG_FILE }} diff --git a/.github/workflows/cicd_ubuntu.yaml b/.github/workflows/cicd_ubuntu.yaml new file mode 100644 index 0000000..82de8e1 --- /dev/null +++ b/.github/workflows/cicd_ubuntu.yaml @@ -0,0 +1,153 @@ +name: CI/CD on Linux systems. + +permissions: + contents: write + +on: + push: + pull_request: + workflow_dispatch: + # Automatic cron build every 6 months to check if everything still works. + schedule: + - cron: "0 0 1 1/6 *" + +jobs: + build: + timeout-minutes: 60 + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. + fail-fast: false + + # Set up an include to perform the following build configurations. + matrix: + include: + - BUILD_TARGET: gtk2_amd64 + RELEASE_FILE_NAME: trackereditor_linux_amd64_gtk2.zip + LAZ_OPT: --widgetset=gtk2 + RUNS_ON: ubuntu-24.04 + + - BUILD_TARGET: qt5_amd64 + RELEASE_FILE_NAME: trackereditor_linux_amd64_qt5.zip + LAZ_OPT: --widgetset=qt5 + QT_VERSION_CI: '5' + RUNS_ON: ubuntu-24.04 + + - BUILD_TARGET: qt6_amd64 + RELEASE_FILE_NAME: trackereditor_linux_amd64_qt6.zip + LAZ_OPT: --widgetset=qt6 + QT_VERSION_CI: '6' + RUNS_ON: ubuntu-24.04 + + - BUILD_TARGET: AppImage_amd64 + RELEASE_FILE_NAME: trackereditor_linux_amd64_qt6.AppImage + LAZ_OPT: --widgetset=qt6 + QT_VERSION_CI: '6' + LINUX_DEPLOY_FILE_CPU: x86_64 + RUNS_ON: ubuntu-22.04 + + - BUILD_TARGET: AppImage_arm64 + RELEASE_FILE_NAME: trackereditor_linux_arm64_qt6.AppImage + LAZ_OPT: --widgetset=qt6 + QT_VERSION_CI: '6' + LINUX_DEPLOY_FILE_CPU: aarch64 + RUNS_ON: ubuntu-22.04-arm + + name: ${{ matrix.BUILD_TARGET }} + runs-on: ${{ matrix.RUNS_ON }} + + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - name: Install Lazarus IDE + uses: ./.github/actions/install_lazarus + with: + qt_version_ci: ${{ matrix.QT_VERSION_CI }} + + - name: Install dependency for all build + run: | + sudo apt-get update + sudo apt-get install -y xvfb + shell: bash + + - name: Install dependency for AppImage + if: matrix.BUILD_TARGET == 'AppImage_amd64' || matrix.BUILD_TARGET == 'AppImage_arm64' + run: | + # Add wayland plugin and platform theme + sudo apt-get install -y fuse qt6-wayland qt6-xdgdesktopportal-platformtheme qt6-gtk-platformtheme + # Download/Install AppImage tools + curl -L -O https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.LINUX_DEPLOY_FILE_CPU }}.AppImage + curl -L -O https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.LINUX_DEPLOY_FILE_CPU }}.AppImage + curl -L -O https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-${{ matrix.LINUX_DEPLOY_FILE_CPU }}.AppImage + chmod +x linuxdeploy-*.AppImage + shell: bash + + - name: Build trackereditor + # Build trackereditor project (Release mode) + run: lazbuild --build-all --build-mode=Release ${{ matrix.LAZ_OPT }} source/project/tracker_editor/trackereditor.lpi + shell: bash + + - name: Test if OpenSSL works on Linux CI + run: xvfb-run --auto-servernum enduser/trackereditor -TEST_SSL + shell: bash + + - name: Copy libQtpas.so before releasing the Qt5/Qt6 zip file format. + if: matrix.BUILD_TARGET == 'qt5_amd64' || matrix.BUILD_TARGET == 'qt6_amd64' + run: | + cp -av /usr/lib/*/libQt?Pas.* ${{ github.workspace }}/enduser + cat < ${{ github.workspace }}/enduser/missing_libQtPas.so.txt + Start program with: + env LD_LIBRARY_PATH=. ./trackereditor + + This uses libQtpas.so, which is necessary for this program. + Some Linux OS stores may also offer this libQtpas.so + https://archlinux.org/packages/extra/x86_64/qt5pas/ + https://archlinux.org/packages/extra/x86_64/qt6pas/ + EOF + shell: bash + + - name: Create a gtk2 or Qt5/Qt6 release in zip file format. + if: matrix.BUILD_TARGET != 'AppImage_amd64' + run: zip -j ${{ matrix.RELEASE_FILE_NAME }} enduser/*.txt enduser/libQt* enduser/trackereditor + shell: bash + + - name: Create AppImage + if: matrix.BUILD_TARGET == 'AppImage_amd64' || matrix.BUILD_TARGET == 'AppImage_arm64' + # LDAI_NO_APPSTREAM=1: skip checking AppStream metadata for issues + env: + LDAI_NO_APPSTREAM: 1 + LDAI_OUTPUT: ${{ matrix.RELEASE_FILE_NAME }} + QMAKE: /usr/lib/qt${{ matrix.QT_VERSION_CI }}/bin/qmake + EXTRA_QT_MODULES: waylandcompositor + EXTRA_PLATFORM_PLUGINS: libqwayland-generic.so;libqwayland-egl.so + DEPLOY_PLATFORM_THEMES: true + run: | + ./linuxdeploy-${{ matrix.LINUX_DEPLOY_FILE_CPU }}.AppImage \ + --output appimage \ + --appdir temp_appdir \ + --plugin qt \ + --executable enduser/trackereditor \ + --desktop-file metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.desktop \ + --icon-file metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.png + shell: bash + + - name: Test AppImage + if: matrix.BUILD_TARGET == 'AppImage_amd64' + run: xvfb-run --auto-servernum ./${{ matrix.RELEASE_FILE_NAME }} -TEST_SSL + shell: bash + + - name: Upload Artifact + uses: actions/upload-artifact@v6 + with: + name: artifact-${{ matrix.RELEASE_FILE_NAME }} + path: ${{ matrix.RELEASE_FILE_NAME }} + compression-level: 0 # no compression. Content is already a compress file + if-no-files-found: error + + - name: File release to end user + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') + with: + files: ${{ matrix.RELEASE_FILE_NAME }} diff --git a/.github/workflows/cicd_windows.yaml b/.github/workflows/cicd_windows.yaml new file mode 100644 index 0000000..05a2fba --- /dev/null +++ b/.github/workflows/cicd_windows.yaml @@ -0,0 +1,80 @@ +name: CI/CD on Windows systems. + +permissions: + contents: write + +on: + push: + pull_request: + workflow_dispatch: + # Automatic cron build every 6 months to check if everything still works. + schedule: + - cron: "0 0 1 1/6 *" + +jobs: + build: + runs-on: windows-latest + timeout-minutes: 60 + env: + RELEASE_ZIP_FILE: trackereditor_windows_amd64.zip + LAZ_OPT: + + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - name: Install Lazarus IDE + uses: ./.github/actions/install_lazarus + + - name: Download OpenSSL *.dll + run: | + # Need OpenSSL *.dll to download updated trackers from the internet. + # https://wiki.overbyte.eu/wiki/index.php/ICS_Download#Download_OpenSSL_Binaries + curl -L -O --output-dir enduser https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/libssl-3-x64.dll + curl -L -O --output-dir enduser https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/libcrypto-3-x64.dll + shell: bash + + - name: Build Release version + # Build trackereditor project (Release mode) + run: lazbuild --build-all --build-mode=Release ${{ env.LAZ_OPT }} source/project/tracker_editor/trackereditor.lpi + shell: bash + + - name: Build Unit Test on Windows + # Build unit test project (Debug mode) + run: lazbuild --build-all --build-mode=Debug ${{ env.LAZ_OPT }} source/project/unit_test/tracker_editor_test.lpi + shell: bash + + - name: Run Unit Test on Windows + # Also remove all the extra file created by test. + # We do not what it in the ZIP release files. + # Undo all changes made by testing. + run: | + set -e + enduser/test_trackereditor -a --format=plain + set +e + + # remove file created by unit test + rm -f enduser/console_log.txt + rm -f enduser/export_trackers.txt + git reset --hard + shell: bash + + - name: Create a zip file for Windows release. + run: | + Compress-Archive -Path enduser\*.txt, enduser\trackereditor.exe, enduser\*.dll -DestinationPath $Env:RELEASE_ZIP_FILE + shell: pwsh + + - name: Upload Artifact + uses: actions/upload-artifact@v6 + with: + name: artifact-${{ runner.os }} + path: ${{ env.RELEASE_ZIP_FILE }} + compression-level: 0 # no compression. Content is already a zip file + if-no-files-found: error + + - name: File release to end user + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') + with: + files: ${{ env.RELEASE_ZIP_FILE }} diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml new file mode 100644 index 0000000..2a77953 --- /dev/null +++ b/.github/workflows/snap.yml @@ -0,0 +1,28 @@ +name: Test build Ubuntu snap on Linux amd64 + +on: + push: + pull_request: + workflow_dispatch: + # Automatic cron build every 6 months to check if everything still works. + schedule: + - cron: "0 0 1 1/6 *" + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v6 + with: + submodules: true + + - uses: snapcore/action-build@v1 + + - name: Upload Artifact + uses: actions/upload-artifact@v6 + with: + name: artifact-snap + path: "*.snap" + compression-level: 0 # no compression. Content is already a zip file + if-no-files-found: error diff --git a/.gitmodules b/.gitmodules index 5db01de..adcb0d7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "travis-lazarus"] - path = travis-lazarus - url = https://github.com/nielsAD/travis-lazarus.git +[submodule "submodule/dcpcrypt"] + path = submodule/dcpcrypt + url = https://git.code.sf.net/p/lazarus-ccr/dcpcrypt diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9ecc833..0000000 --- a/.travis.yml +++ /dev/null @@ -1,73 +0,0 @@ -# Part of `travis-lazarus` (https://github.com/nielsAD/travis-lazarus) -# License: MIT - -# Suppress the warning with the online Travis WebLint -language: generic - -sudo: required -dist: trusty - -env: - global: - - LPI_EDITOR_PATH=source/project/tracker_editor - - LPI_UNIT_TEST_PATH=source/project/unit_test - - WINEPREFIX=~/.winelaz - - DISPLAY=:99.0 - -# For windows build (win32) use linux wine. -matrix: - include: - - os: linux - env: LAZ_VER=1.6.2 LAZ_ENV=wine WINEARCH=win32 LAZ_OPT="--os=win32 --cpu=i386" - - os: linux - env: LAZ_VER=1.6.2 - - os: osx - env: LAZ_VER=1.6.2 - osx_image: xcode9 - - -before_install: -# Start virtual display server - - Xvfb $DISPLAY & - -install: -# Install prerequisites (fpc/lazarus/wine/qemu) - - ./travis-lazarus/.travis.install.py - -# Install valgrind in linux only -# - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install valgrind -y; fi - -# install openSSL on macOS -# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi -# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install openssl; fi -# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then sudo brew link openssl --force; fi -# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then openssl version; fi - -script: -# Build trackereditor project (Debug mode) - - lazbuild --build-mode=Debug $LAZ_OPT $LPI_EDITOR_PATH/trackereditor.lpi - -# Build unit test project (Debug mode) - - lazbuild --build-mode=Debug $LAZ_OPT $LPI_UNIT_TEST_PATH/tracker_editor_test.lpi - -# Start the unit test - - source ./scripts/travis_unit_test.sh - -before_deploy: -# Build trackereditor project (Release mode) - - lazbuild --build-mode=Release $LAZ_OPT $LPI_EDITOR_PATH/trackereditor.lpi - -# Create the zip file for deployment - - source ./scripts/travis_deploy.sh - -deploy: - provider: releases - prerelease: false - skip_cleanup: true - api_key: - secure: "4SPoXhlWCKAf22vsrLUIm8CAMpwb+vXtb4VN4GL0F+DVEyb8JFMpc2o4Rd1bSP995c5EVm0FkOiBTHZNCU2nDY/xV6Mmp489fPVDXyFq4uZxveOXrIEbloRuMyyMZSpjKe3UaW3JRoPCsYDjv2o9ToqyBuE6YBbBoS/DicCmVUlJS6FfsC4m+RKwTTetIIfLXEFwyL0dyWDTf1c5hvnQEIwgzKRSJ8DjfwygqxgRJqrvOxi82bFIZDjoyXTSoMs2N38woTRdgrh6OwtI/W3PqrjKxI8s3B1JiUosHZjG3A5LbcDtdQZXp92uSsKSUdcyrVs4NoxrgXcQpKkER9SmBLLKp/y+839Dhe9HuKJKxZ3UTuDX2zBYHYfd5kxu9is0iQjUPRUFawwLTF4xmvQQFwHkI4mXY1BwOBsK7Ij+xhFK+0KT3BP9ZLLTgAQOCyNZftMyx+LXL0wrwkcySbfKK5g9MIOzXKJYuMS9wzsV5NJVN+iZOhnpmnUZ9q46SiEnWfoAsIiyhdwJp45hgfqVAoObWRlSOQiXvcqsnfJRD8c/NqI7D+/9GmSLcQs5Tpog9PxKA+KdQS0QuHElkVFPfo5kUiyItj8VCA0q/jz2urhRazxqL6kHMPiDi1aYgLq+ibRH081AygYmzQhIb7d97/ekdEfHSY/Tdtn3ozIL/2M=" - file: "${RELEASE_ZIP_FILE}" - on: - tags: true - repo: GerryFerdinandus/bittorrent-tracker-editor - diff --git a/README.md b/README.md index c1b17ce..98779fe 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,42 @@ **bittorrent-tracker-editor** will add/remove bittorrent tracker from the torrent file(s). -This software works on Windows XP SP3, Windows 7+, macOS and Linux. +This software works on Windows 7+, macOS and Linux. --- ## Software latest release: ## [![GitHub Latest release](https://img.shields.io/github/release/GerryFerdinandus/bittorrent-tracker-editor/all.svg)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases) - +[![GitHub commits since latest release (by SemVer including pre-releases)](https://img.shields.io/github/commits-since/gerryferdinandus/bittorrent-tracker-editor/latest)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/commits/main) --- -## Code Status: ## -Continuous integration|Status| Build operating system -------------|---------|--------- -Travis-CI |[![Build Status](https://travis-ci.org/GerryFerdinandus/bittorrent-tracker-editor.svg?branch=master)](https://travis-ci.org/GerryFerdinandus/bittorrent-tracker-editor) |Linux, macOS and Windows (via Wine) -AppVeyor |[![Build status](https://ci.appveyor.com/api/projects/status/yq8u0hptl006my57/branch/master?svg=true)](https://ci.appveyor.com/project/GerryFerdinandus/bittorrent-tracker-editor/branch/master) | Windows Server - +## Build Status: ## +Continuous integration|Status| Generate an executable file for the operating system| Download link +------------|---------|---------|---------- +GitHub Actions |[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/gerryferdinandus/bittorrent-tracker-editor/cicd_ubuntu.yaml?label=Ubuntu)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/actions/workflows/cicd_ubuntu.yaml)[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/gerryferdinandus/bittorrent-tracker-editor/cicd_windows.yaml?label=Windows)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/actions/workflows/cicd_windows.yaml)[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/gerryferdinandus/bittorrent-tracker-editor/cicd_macos.yaml?label=macOS)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/actions/workflows/cicd_macos.yaml)|Linux(amd64), Windows(amd64) and macOS(Universal)|[![GitHub Latest release](https://img.shields.io/github/release/GerryFerdinandus/bittorrent-tracker-editor/all.svg)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases) +GitHub Actions (Ubuntu snap) |[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/gerryferdinandus/bittorrent-tracker-editor/snap.yml)](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/actions/workflows/snap.yml)|Linux (amd64 and arm64)|[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-white.svg)](https://snapcraft.io/bittorrent-tracker-editor) +Flathub build server||Linux (amd64 and arm64)|Download on Flathub --- ## Warning: ## -There is no backup function in this software. Used it at your own risk. Bittorrent work fine without this program. You probably don't need this software. +There is no backup function in this software. Use it at your own risk. Bittorrent works fine without this program. You probably don't need this software. --- ## Which program to use for add/remove bittorrent trackers? ## * **Edit one torrent file:** You can use http://torrenteditor.com/ - * **Edit multiple torrent file:** Use this program. It is made for changing multiple torrent files. + * **Edit multiple torrent files:** Use this program. It is made for changing multiple torrent files. --- ## Features: ## * Select one torrent file or a folder with torrent files. - * Add one or more tracker at the same time. - * Remove one or more tracker at the same time. - * Remove all the tracker to create tracker less torrent. DHT torrent + * Add one or more trackers at the same time. + * Remove one or more trackers at the same time. + * Remove all the trackers to create trackerless torrent. DHT torrent * Change public/private flag. Warning: This will change the torrent info HASH. - * Preset add/remove tracker via add\_trackers.txt and remove\_trackers.txt files when present in the same folder as the executable file. + * Preset add/remove tracker via add\_trackers.txt and remove\_trackers.txt files when present in the same folder as the executable file. (For linux snap version use: home/snap/bittorrent-tracker-editor/common/ ) * Optional start as console program. (See readme.txt inside download) * Show torrent files content. + * Download stable trackers from newTrackon or ngosang. --- @@ -47,12 +48,31 @@ There is no backup function in this software. Used it at your own risk. Bittorre --- ## Software history: ## +### 1.33.1 ### + * FIX: Cannot open torrent file V2 format. ([Issue 51](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/51)) + +### 1.33.0 ### + * ADD: Support for OpenSSL 3 + * FIX: Handle dark theme on MacOS. ([Issue 49](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/49)) + * ADD: Direct download support for ngosang via menu. + * ADD: Extra tabpage 'private torrent'. For issue 31 and 34 + * ADD: Check box 'Skip Announce Check in the URL' ([Issue 31](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/31)) + * ADD: Command parameter '-SAC' -> 'Skip Announce Check' in the URL ([Issue 31](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/31)) + * ADD: Support 'Info Source' tag for private trackers ([Issue 34](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/34)) + * ADD: Command parameter '-SOURCE' -> info Source tag for private trackers. See readme.txt file ([Issue 34](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/34)) + * FiX: support for '/announce.php'([Issue 27](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/27)) + * FIX: There was an issue with uploading tracker list to newTrackon. + * FIX: WebTorrent do not have '/announce' ([Issue 24](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/24)) + * ADD: Wrong tracker URL format from torrent files should be unselected by default ([Issue 22](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/22)) + * ADD: Upload trackers to newTrackon ([Issue 23](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/23)) + * ADD: Verify the working status of public trackers. (Data from newTrackon) ([Issue 21](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/21)) + ### 1.32.0 ### * ADD: Add more options for updating the torrent tracker list. ([Issue 8](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/8)) - * ADD: Add trackers but keep the original unique trackers list instact. ([Issue 12](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/12)) + * ADD: Add trackers but keep the original unique trackers list intact. ([Issue 12](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/12)) * ADD: Randomize tracker order for each torrent file. ([Issue 19](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/19)) * FIX: Trackers with https ([Issue 9](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/9)) - * FIX: Read torrent file with string size langer that 1MB ([Issue 10](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/10)) + * FIX: Read torrent file with string size larger that 1MB ([Issue 10](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/10)) * FIX: Give user a warning when torrent file is read only file. ([Issue 14](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/14)) * FIX: Give user a warning when torrent file update failed. ([Issue 15](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/15)) * FIX: Can not be opened by dragging a file. ([Issue 17](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/17)) @@ -60,40 +80,41 @@ There is no backup function in this software. Used it at your own risk. Bittorre * FIX: WebSocket ws:// and wss:// should be accepted as input. ([Issue 20](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/20)) ### 1.31 ### - * Add: Edit comment in data/info grid column. - * FIX: ([Issue 6](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/6)) + * ADD: Edit comment in data/info grid column. + * FIX: The data/info column can be moved but it is not updated correctly when torrent is reloaded. ([Issue 6](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/6)) ### 1.30 ### - * Add: Tab page Files/trackers/info - * Add: Optional start as console program. (See readme.txt inside download) - * Add: remove\_trackers.txt will remove specific trackers form torrent. - * Add: export\_trackers.txt is created after updating the torrent. - * Add: drag and drop of trackers file (with '.txt' file extension) - * FIX: ([Issue 4](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/4)) + ([Issue 5](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/5)) + * ADD: Tab page Files/trackers/info + * ADD: Optional start as console program. (See readme.txt inside download) + * ADD: remove\_trackers.txt will remove specific trackers from torrent. + * ADD: export\_trackers.txt is created after updating the torrent. + * ADD: drag and drop of trackers file (with '.txt' file extension) + * FIX: Can not remove duplicate Tracker automatically ([Issue 4](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/4)) + * FIX: Can't open some of .torrent files. ([Issue 5](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/5)) ### 1.21 ### * FIX: Support for Unicode in filename. (Chinese etc.) ### 1.20 ### - * Add: Tab page torrent info/data. - * Add: Drag & Drop torrent files or a folder with torrent files inside. + * ADD: Tab page torrent info/data. + * ADD: Drag & Drop torrent files or a folder with torrent files inside. ### 1.10 ### - * Add: Tab page for public/private flag. ([Issue 1](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/1)) - * Add: Load tracker list from file via menu or at start-up, when file add\_trackers.txt is present in the same folder as the executable file. + * ADD: Tab page for public/private flag. ([Issue 1](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues/1)) + * ADD: Load tracker list from file via menu or at start-up, when file add\_trackers.txt is present in the same folder as the executable file. ### 1.00 ### * First release --- -![](pictures/trackereditor.png?raw=true "Trackers List") +![](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/trackereditor_list_windows.png "Trackers List") -This screen shot show the program, after a folder is selected with 97 torrent files inside. The normal procedure is to deselect the trackers that no longer working. Optionally add your own trackers. And press the 'Update torrent' +This screen shot show the program, after a folder is selected with torrent files inside. The normal procedure is to deselect the trackers that are no longer working. Optionally add your own trackers. And select the 'Update torrent' menu. --- -![](pictures/filestrackersinfo.png?raw=true "Files/Trackers/Info") +![](https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/trackereditor_info_windows.png "Files/Trackers/Info") --- @@ -103,3 +124,7 @@ This screen shot show the program, after a folder is selected with 97 torrent fi --- This program is developed using [Lazarus](http://lazarus.freepascal.org/) RAD and [Free Pascal](http://www.freepascal.org/) compiler. + +--- + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index ba87eef..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: '{branch}-{build}' -image: Visual Studio 2017 -init: -- cmd: >- - echo %LAZARUS_URL% - - appveyor DownloadFile %LAZARUS_URL% -FileName "c:\lazarus.exe" -environment: - LAZARUS_URL: '"http://mirrors.iwi.me/lazarus/releases/Lazarus Windows 32 bits/Lazarus 1.6.2/lazarus-1.6.2-fpc-3.0.0-win32.exe"' -install: -- cmd: >- - git submodule update --init --recursive - - c:\lazarus.exe /VERYSILENT /DIR="c:\lazarus" -build_script: -- cmd: >- - c:\lazarus\lazbuild --build-mode=Debug C:\projects\bittorrent-tracker-editor\source\project\tracker_editor\trackereditor.lpi - - c:\lazarus\lazbuild --build-mode=Debug C:\projects\bittorrent-tracker-editor\source\project\unit_test\tracker_editor_test.lpi -test_script: -- cmd: C:\projects\bittorrent-tracker-editor\enduser\test_trackereditor.exe -a --format=plain \ No newline at end of file diff --git a/enduser/add_trackers.txt b/enduser/add_trackers.txt index f1d2079..b382aaf 100644 --- a/enduser/add_trackers.txt +++ b/enduser/add_trackers.txt @@ -1,4 +1,3 @@ -udp://tracker.openbittorrent.com:80/announce -udp://tracker.publicbt.com:80/announce -udp://tracker.istole.it:80/announce -udp://open.demonii.com:1337/announce +udp://tracker.coppersurfer.tk:6969/announce +udp://tracker.opentrackr.org:1337/announce +wss://tracker.openwebtorrent.com diff --git a/enduser/libeay32.dll b/enduser/libeay32.dll deleted file mode 100644 index b66c6bb..0000000 Binary files a/enduser/libeay32.dll and /dev/null differ diff --git a/enduser/readme.txt b/enduser/readme.txt index 49f7537..2787c3d 100644 --- a/enduser/readme.txt +++ b/enduser/readme.txt @@ -4,14 +4,71 @@ If you do not want to use it this way, then just remove both the files 'add_trac These '.txt' files must be place in the same directory as the program. -------------------- +--- Linux usage example : 1 (public trackers) +Update all the torrent files with only the latest tested stable tracker. + +curl https://newtrackon.com/api/stable --output add_trackers.txt +echo > remove_trackers.txt +./trackereditor ../test_torrent -U0 + +Line 1: This will download the latest stable trackers into add_trackers.txt +Line 2: Remove_trackers.txt is now a empty file. All trackers from the present torrent will be REMOVED. +Line 3: Update all the torrent files inside the test_torrent folder. + +note -U0 parameter can be change to -U4 (sort by name) +This is my prefer setting for the rtorrent client. +rtorrent client announce to all the trackers inside the torrent file. +I prefer to see all the trackers in alphabetical order inside rtorrent client console view. +rtorrent client need to have the 'session' folder cleared and restart rtorrent to make this working. +This can be run via regular cron job to keep the client running with 100% functional trackers. + +--- Linux usage example: 2 (public trackers) +Mix the latest tested stable tracker with the present trackers already present inside the torrent files. + +curl https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt --output add_trackers.txt +curl https://raw.githubusercontent.com/ngosang/trackerslist/master/blacklist.txt --output remove_trackers.txt +./trackereditor ../test_torrent -U0 + +Line 1: This will download the latest stable trackers into add_trackers.txt +Line 2: Remove_trackers.txt now contain blacklisted trackers +Line 3: Update all the torrent files inside the test_torrent folder. + +Diference betwean example 1 vs 2 +Example 1 is guarantee that all the trackers are working. +Example 2 You are responseble for the trackers working that are not part of add_trackers.txt + +--- Linux usage example: 3 (private trackers) +In add_trackers.txt file manualy the private tracker URL +echo > remove_trackers.txt +./trackereditor ../test_torrent -U0 -SAC -SOURCE "abcd" + +Line 2: Remove_trackers.txt is now a empty file. All trackers from the present torrent will be REMOVED. +Line 3: Update all the torrent files inside the test_torrent folder. + -SAC (Skip Annouce Check) This is needed to skip private tracker URL check. + -SOURCE Add private tracker source tag "abcd" + +-SOURCE is optionally. +-SOURCE "" Empty sting will remove all the source tag +-------------------- + +Usage example Windows desktop short cut for private tracker user. +This is the same idea as "Usage example: 3 (private trackers)" +But start it from the desktop shortcut (double click) and not from windows console via bat file etc. + +Desktop shortcut can have extra parameter append. +C:\Users\root\Documents\github\bittorrent-tracker-editor\enduser\trackereditor.exe ..\test_torrent -U0 -SAC -SOURCE abc -Console mode: +Make sure that add_trackers.txt is filled with the private URL +And remove_trackers.txt is a empty file. + +-------------------- + +Console mode windows example: Start program with a parameter to torrent file or dir trackereditor.exe "C:\dir\torrent\file.torrent" -U4 trackereditor.exe "C:\dir\torrent" -U4 What tracker will be added/removed depend the content of the add_trackers.txt and remove_trackers.txt files. --------------------- Additional Files: Trackers file and log file These 3 files can be optionally present in the same dir as the trackereditor executable file. @@ -97,3 +154,7 @@ Console parameter: -U6 Console parameter: -U7 Randomize the trackers list. + +-------------------- +Acknowledgment: +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) diff --git a/enduser/ssleay32.dll b/enduser/ssleay32.dll deleted file mode 100644 index 2ebf965..0000000 Binary files a/enduser/ssleay32.dll and /dev/null differ diff --git a/enduser/trackereditor.app/Contents/Info.plist b/enduser/trackereditor.app/Contents/Info.plist deleted file mode 100644 index 36c8919..0000000 --- a/enduser/trackereditor.app/Contents/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - trackereditor - CFBundleName - trackereditor - CFBundleIdentifier - com.company.trackereditor - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleSignature - trac - CFBundleShortVersionString - 0.1 - CFBundleVersion - 1 - CSResourcesFileMapped - - CFBundleDocumentTypes - - - CFBundleTypeRole - Viewer - CFBundleTypeExtensions - - * - - CFBundleTypeOSTypes - - fold - disk - **** - - - - CFBundleIconFile - iconfile - NSHighResolutionCapable - - - diff --git a/enduser/trackereditor.app/Contents/MacOS/.gitkeep b/enduser/trackereditor.app/Contents/MacOS/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/enduser/trackereditor.app/Contents/PkgInfo b/enduser/trackereditor.app/Contents/PkgInfo deleted file mode 100644 index 6f749b0..0000000 --- a/enduser/trackereditor.app/Contents/PkgInfo +++ /dev/null @@ -1 +0,0 @@ -APPL???? diff --git a/enduser/trackereditor.app/Contents/Resources/iconfile.icns b/enduser/trackereditor.app/Contents/Resources/iconfile.icns deleted file mode 100644 index 3120700..0000000 Binary files a/enduser/trackereditor.app/Contents/Resources/iconfile.icns and /dev/null differ diff --git a/enduser/version.txt b/enduser/version.txt index 06cae73..5b91cbf 100644 --- a/enduser/version.txt +++ b/enduser/version.txt @@ -1,3 +1,22 @@ +------ Version 1.33.1 +FIX: Cannot open torrent file V2 format (Issue 51) +Compiler Lazarus: v3.6 + +------ Version 1.33 +ADD: Verify the working status of public trackers. (Issue 21) +ADD: Wrong tracker URL format from torrent files should be unselected by default (Issue 22) +ADD: Upload trackers to newTrackon (Issue 23) +FIX: WebTorrent do not have '/announce' (Issue 24) +FiX: add support for '/announce.php'(Issue 27) +ADD: Extra tabpage 'private torrent'. For issue 31 and 34 +ADD: Check box 'Skip Announce Check in the URL' (Issue 31) +ADD: Command parameter '-SAC' -> 'Skip Announce Check' in the URL (Issue 31) +ADD: Support 'Info Source' tag for private trackers (issue 34) +ADD: Command parameter '-SOURCE' -> info Source tag for private trackers. See readme.txt file (issue 34) +ADD: Direct download support for ngosang via menu (Issue 35) +ADD: Support for OpenSSL 3 +FIX: Handle dark theme on MacOS. (Issue 49) +Compiler Lazarus: v2.26 ------ Version 1.32 ADD: Add more options for updating the torrent tracker list. (Issue 8) @@ -10,6 +29,7 @@ FIX: Give user a warning when torrent file update failed. (Issue 15) FIX: Can not be opened by dragging a file. (Issue 17) FIX: Need sanitize URL tracker. (Issue 18) FIX: WebSocket ws:// and wss:// should be accepted as input. (Issue 20) +Compiler Lazarus: v1.62 ------ Version 1.31 ADD: tab page 'Files/Trackers/Info' show how many files count in one torrent diff --git a/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.desktop b/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.desktop new file mode 100644 index 0000000..798d0ca --- /dev/null +++ b/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=bittorrent-tracker-editor +Comment=Add or remove tracker from torrent files. +Categories=Utility;Qt; +Icon=io.github.gerryferdinandus.bittorrent-tracker-editor +Exec=trackereditor +Terminal=false diff --git a/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.metainfo.xml b/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.metainfo.xml new file mode 100644 index 0000000..e2aa6d8 --- /dev/null +++ b/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.metainfo.xml @@ -0,0 +1,78 @@ + + + io.github.gerryferdinandus.bittorrent-tracker-editor + bittorrent-tracker-editor + Add or remove tracker from torrent files. + Gerry Ferdinandus + io.github.gerryferdinandus.bittorrent-tracker-editor.desktop + + CC0-1.0 + MIT + + https://github.com/GerryFerdinandus/bittorrent-tracker-editor + https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues + + + + +

+ Warning: There is no backup function in this software. Use it at your own risk. +

+
    +
  • Select one torrent file or a folder with torrent files
  • +
  • Add one or more trackers at the same time
  • +
  • Remove one or more trackers at the same time
  • +
  • Remove all the trackers to create trackerless torrent. DHT torrent
  • +
  • Change public/private flag. Warning: This will change the torrent info HASH
  • +
  • Show torrent files content
  • +
  • Download stable trackers from newTrackon or ngosang
  • +
+

+ BitTorrent works fine without this program. You probably don't need this software. +

+
+ + + + The main window + + https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/trackereditor_list_linux.png + + + Torrent info + + https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/trackereditor_info_linux.png + + + + + + +

This release fixes the following bugs:

+
    +
  • Cannot open torrent file V2 format
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Verify the working status of public trackers.
  • +
  • Direct download support for ngosang via menu.
  • +
  • Extra tabpage 'private torrent'.
  • +
  • Support 'Info Source' tag for private trackers
  • +
  • Wrong tracker URL format from torrent files should be unselected by default
  • +
  • Upload trackers to newTrackon
  • +
+

This release fixes the following bugs:

+
    +
  • support for '/announce.php'
  • +
  • WebTorrent do not have '/announce'
  • +
+
+
+
+
\ No newline at end of file diff --git a/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.png b/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.png new file mode 100644 index 0000000..392dbf6 Binary files /dev/null and b/metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.png differ diff --git a/pictures/filestrackersinfo.png b/pictures/filestrackersinfo.png deleted file mode 100644 index 46e6128..0000000 Binary files a/pictures/filestrackersinfo.png and /dev/null differ diff --git a/pictures/trackereditor.png b/pictures/trackereditor.png deleted file mode 100644 index af03308..0000000 Binary files a/pictures/trackereditor.png and /dev/null differ diff --git a/scripts/install_and_run_unit_test_in_desktop_ubuntu.sh b/scripts/install_and_run_unit_test_in_desktop_ubuntu.sh deleted file mode 100644 index 5b5202b..0000000 --- a/scripts/install_and_run_unit_test_in_desktop_ubuntu.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# for local testing of the project in Ubuntu 14.04 (via VirtualBox) - -#create download folder 'download_FPC' -cd ~ -mkdir download_FPC -cd download_FPC - -#download the FPC + lazarus IDE (1.6.2) -wget http://mirrors.iwi.me/lazarus/releases/Lazarus%20Linux%20amd64%20DEB/Lazarus%201.6.2/fpc_3.0.0-151205_amd64.deb -wget http://mirrors.iwi.me/lazarus/releases/Lazarus%20Linux%20amd64%20DEB/Lazarus%201.6.2/fpc-src_3.0.0-151205_amd64.deb -wget http://mirrors.iwi.me/lazarus/releases/Lazarus%20Linux%20amd64%20DEB/Lazarus%201.6.2/lazarus-project_1.6.2-1_amd64.deb - -#Install the downloaded FPC + lazarus IDE -sudo dpkg -i ./*.deb -#install all the software dependency it needed -sudo apt install -f -y - -#install git -sudo apt-get install git -y - -#Download the source code in the home folder -cd ~ -git clone --recursive https://github.com/GerryFerdinandus/bittorrent-tracker-editor.git - -#build the source code trackereditor. (optional: --build-mode=Release) -lazbuild --build-mode=Debug bittorrent-tracker-editor/source/project/tracker_editor/trackereditor.lpi - -#build the source code tracker_editor_test -lazbuild --build-mode=Debug bittorrent-tracker-editor/source/project/unit_test/tracker_editor_test.lpi - -#Run the unit test -./bittorrent-tracker-editor/enduser/test_trackereditor -a --format=plain - - - - - - diff --git a/scripts/travis_deploy.sh b/scripts/travis_deploy.sh deleted file mode 100644 index 057add9..0000000 --- a/scripts/travis_deploy.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -#Create a zip file for Windows, Linux and Apple macOS - -#this will be set later -unset RELEASE_ZIP_FILE - -#----------- check for Windows, Linux and Apple macOS build -if [ "$TRAVIS_OS_NAME" = "linux" ] -then - - #wine = windows - if [ "$LAZ_ENV" = "wine" ] - then - - #windows - export RELEASE_ZIP_FILE="trackereditor_win32.zip" - zip -j $RELEASE_ZIP_FILE enduser/*.txt enduser/trackereditor.exe enduser/libeay32.dll enduser/ssleay32.dll - - else - - #linux - export RELEASE_ZIP_FILE="trackereditor_linux_amd64.zip" - zip -j $RELEASE_ZIP_FILE enduser/*.txt enduser/trackereditor - - fi - -elif [ "$TRAVIS_OS_NAME" = "osx" ] -then - #Apple os x - export RELEASE_ZIP_FILE="trackereditor_macOS.zip" - cd enduser - - #move the executable to the application bundle - mv trackereditor trackereditor.app/Contents/MacOS - - #move the trackers list to application bundle - mv add_trackers.txt trackereditor.app/Contents/MacOS - mv remove_trackers.txt trackereditor.app/Contents/MacOS - - #Create the zip file. - zip -j ../$RELEASE_ZIP_FILE *.txt - zip -r ../$RELEASE_ZIP_FILE trackereditor.app - cd .. - -fi - -echo "Created $RELEASE_ZIP_FILE for GitHub" diff --git a/scripts/travis_unit_test.sh b/scripts/travis_unit_test.sh deleted file mode 100644 index 38354fc..0000000 --- a/scripts/travis_unit_test.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -#Run unit test in Windows, Linux and macOS - - -#----------- check for Windows, Linux and macOS build -if [ "$TRAVIS_OS_NAME" = "linux" ] -then - - #wine = windows - if [ "$LAZ_ENV" = "wine" ] - then - - #windows - # does not work. - #wine enduser/test_trackereditor.exe -a --format=plain - - # Will exit with status of last command. - exit - - else - - #linux - enduser/test_trackereditor -a --format=plain - - # Will exit with status of last command. - exit - - fi - -elif [ "$TRAVIS_OS_NAME" = "osx" ] -then - #macOS - # does not work. - #enduser/test_trackereditor -a --format=plain - - # Will exit with status of last command. - exit - -fi diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 0000000..bc6fe21 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,88 @@ +name: bittorrent-tracker-editor +adopt-info: mainprogram +icon: metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.png + +base: core24 +grade: stable +confinement: strict + +platforms: + amd64: + arm64: + +apps: + bittorrent-tracker-editor: + desktop: io.github.gerryferdinandus.bittorrent-tracker-editor.desktop + extensions: + - kde-neon-6 + command: trackereditor + plugs: + - home + - network + - removable-media + +parts: + build_lazarus: + source: . + plugin: nil + build-packages: + - curl + - build-essential + - fpc + - libxkbcommon-dev + + build-environment: + - LAZARUS_URL_TAR_GZ: "https://github.com/GerryFerdinandus/bittorrent-tracker-editor/releases/download/V1.32.0/lazarus.tar.gz" + - LAZARUS_QT_VERSION: "6" + - LIB_DIR: "/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR" + - LAZARUS_DIR: "$PWD/lazarus" + override-build: | + #Download lazarus source code. Directory 'lazarus' will be created in the root. + curl -L -O $LAZARUS_URL_TAR_GZ + tar -xzf *.tar.gz + + #--- Create libQTpas.so and put it in /usr/lib/ and $CRAFT_PART_INSTALL + cd "$LAZARUS_DIR/lcl/interfaces/qt${LAZARUS_QT_VERSION}/cbindings/" + /snap/kde-qt6-core24-sdk/current/usr/bin/qt6/qmake6 + make -j$(nproc) + + # copy the libQTpas.so to /usr/lib/ (needed for compile-time linking) + cp -av libQt${LAZARUS_QT_VERSION}Pas.* $LIB_DIR/ + + # copy the libQTpas.so to snap install directory (needed for run-time linking) + cp -av --parents $LIB_DIR/libQt${LAZARUS_QT_VERSION}Pas.* $CRAFT_PART_INSTALL + + #--- Make lazbuild and put the link with extra parameter in /usr/bin/ + cd "$LAZARUS_DIR" + make lazbuild + echo "$LAZARUS_DIR/lazbuild --primary-config-path=$LAZARUS_DIR --lazarusdir=$LAZARUS_DIR \$*" > /usr/bin/lazbuild + chmod 777 /usr/bin/lazbuild + + mainprogram: # Build and add to snap the main program: trackereditor + after: [build_lazarus] + source: . + plugin: nil + parse-info: [metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.metainfo.xml] + override-build: | + lazbuild --build-mode=Release --widgetset=qt6 source/project/tracker_editor/trackereditor.lpi + install enduser/trackereditor $CRAFT_PART_INSTALL/ + install metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.desktop $CRAFT_PART_INSTALL/ + +# -------------------------------------------------------------- +# Only 3 files are explicitly added in this snap +# - main program: enduser/trackereditor +# - desktop file: metainfo/io.github.gerryferdinandus.bittorrent-tracker-editor.desktop +# - libQt6Pas.so created during the build_lazarus part +# +# Create snap. Run from the project root folder: +# snapcraft pack --verbosity verbose +# +# To look what is inside the snap file. Directory 'squashfs-root' will be created in the root folder: +# unsquashfs *.snap +# +# Install the snap: +# sudo snap install --devmode ./*.snap +# +# Run the snap +# snap run bittorrent-tracker-editor +# -------------------------------------------------------------- diff --git a/source/code/bencode.pas b/source/code/bencode.pas index f059564..27fcddc 100644 --- a/source/code/bencode.pas +++ b/source/code/bencode.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: CC0-1.0 { public domain code? Source code from: @@ -105,6 +106,7 @@ constructor TBEncoded.Create(Stream: TStream); var X: char; begin + Result := ''; // loop until we come across it X := ' '; repeat diff --git a/source/code/controler_trackerlist_online.pas b/source/code/controler_trackerlist_online.pas index a2103c2..fb4705b 100644 --- a/source/code/controler_trackerlist_online.pas +++ b/source/code/controler_trackerlist_online.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit controler_trackerlist_online; { @@ -12,6 +13,8 @@ interface type + TDefaultChecked = function(const TrackerURL: utf8string): boolean of object; + { TControlerTrackerListOnline } TControlerTrackerListOnline = class @@ -20,6 +23,7 @@ TControlerTrackerListOnline = class FNewTrackon: TNewTrackon; FTrackerListOnline: TTrackerListOnline; FTrackerList: TStringList; + FDefaultChecked: TDefaultChecked; //The collumn must be in this design order. FSelect, //< 0 @@ -31,9 +35,8 @@ TControlerTrackerListOnline = class procedure SetChecked(index: integer; AValue: boolean); procedure ShowTrackerStatus(Visible: boolean); procedure AppendRow(Checked: boolean; Status: TTrackerListOnlineStatus; - const TrackerURL: UTF8String); + const TrackerURL: utf8string); public - property Checked[index: integer]: boolean read GetChecked write SetChecked; function TrackerURL(index: integer): string; function TrackerStatus(index: integer): TTrackerListOnlineStatus; @@ -43,9 +46,14 @@ TControlerTrackerListOnline = class //must be called if the tracker list is updated procedure UpdateView; - function DownloadTrackers: boolean; + function DownloadTrackers_All_Live_Stable: boolean; - constructor Create(StringGridTorrentURL: TStringGrid; TrackerList: TStringList); + //Submit tracker to newTrackon via http POST + function SubmitTrackers(TrackerList: TStringList; + out TrackersSendCount: integer): boolean; + + constructor Create(StringGridTorrentURL: TStringGrid; TrackerList: TStringList; + DefaultChecked: TDefaultChecked); destructor Destroy; override; end; @@ -57,23 +65,45 @@ implementation { TControlerTrackerListOnline } -function TControlerTrackerListOnline.DownloadTrackers: boolean; +function IsDarkTheme: boolean; + // by "Hansaplast" & "Alextp" from Lazarus forum + function _Level(C: TColor): double; + begin + Result := Red(C) * 0.3 + Green(C) * 0.59 + Blue(C) * 0.11; + end; + begin - Result := FNewTrackon.DownloadTrackers; + Result := _Level(ColorToRGB(clWindow)) < _Level(ColorToRGB(clWindowText)); +end; + +function TControlerTrackerListOnline.DownloadTrackers_All_Live_Stable: boolean; +begin + Result := FNewTrackon.Download_All_Live_Stable; UpdateView; ShowTrackerStatus(Result); end; +function TControlerTrackerListOnline.SubmitTrackers(TrackerList: TStringList; + out TrackersSendCount: integer): boolean; +begin + Result := FNewTrackon.SubmitTrackers(TrackerList, TrackersSendCount); +end; + constructor TControlerTrackerListOnline.Create(StringGridTorrentURL: TStringGrid; - TrackerList: TStringList); + TrackerList: TStringList; DefaultChecked: TDefaultChecked); begin FTrackerList := TrackerList; + FDefaultChecked := DefaultChecked; FStringGridTorrentURL := StringGridTorrentURL; FStringGridTorrentURL.RowCount := 1; FStringGridTorrentURL.FixedRows := 1; - FStringGridTorrentURL.AlternateColor := clCream; + + if not IsDarkTheme then + begin // The dark theme for Linux and macOS cannot use AlternateColor. Text will be invisible. + FStringGridTorrentURL.AlternateColor := clCream; + end; FSelect := FStringGridTorrentURL.Columns.Add; FSelect.Title.Caption := 'Keep'; @@ -129,7 +159,7 @@ procedure TControlerTrackerListOnline.SetChecked(index: integer; AValue: boolean procedure TControlerTrackerListOnline.AppendRow(Checked: boolean; - Status: TTrackerListOnlineStatus; const TrackerURL: UTF8String); + Status: TTrackerListOnlineStatus; const TrackerURL: utf8string); var CheckedStr, StatusStr: string; begin @@ -165,20 +195,17 @@ function TControlerTrackerListOnline.StableTrackers: TStringList; procedure TControlerTrackerListOnline.UpdateView; var tracker: string; - DeafultChecked: boolean; begin //Clear all the previeus data in the view FStringGridTorrentURL.RowCount := FStringGridTorrentURL.FixedRows; - //the default status is is put all the checkox to true. => keep all trackers - DeafultChecked := True; - FStringGridTorrentURL.BeginUpdate; //Show the TrackerList list in string grid view for tracker in FTrackerList do begin - AppendRow(DeafultChecked, FTrackerListOnline.TrackerStatus(tracker), tracker); + AppendRow(FDefaultChecked(tracker), FTrackerListOnline.TrackerStatus( + tracker), tracker); end; //make sure all text are fit inside the columns diff --git a/source/code/controler_treeview_torrent_data.pas b/source/code/controler_treeview_torrent_data.pas new file mode 100644 index 0000000..93fa146 --- /dev/null +++ b/source/code/controler_treeview_torrent_data.pas @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: MIT +unit controler_treeview_torrent_data; + +{ + Show the torrent files content in a tree view format. +} +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Controls, ComCtrls, Menus, StdCtrls, DecodeTorrent; + +type + + { Tcontroler_treeview_torrent_data } + Tcontroler_treeview_torrent_data_items = ( + ctv_ShowAllFiles = 0, + ctv_ShowAllTrackers = 1, + ctv_ShowAllInfo = 2, + ctv_ShowEverything, + ctv_HideAll + ); + + Tcontroler_treeview_torrent_data = class + private + FTotalFileInsideTorrent: integer; + FTotalFileSizeInsideTorrent: int64; + + FGroupBoxTorrentContents: TGroupBox; + FPopupMenuTorrentFilesContent: TPopupMenu; + FOwner: TWinControl; + FTreeViewFileContents: TTreeView; + FTreeNodeRoot: TTreeNode; + FMenuItemTorrentFilesTreeHideAll: TMenuItem; + FMenuItemTorrentFilesTreeShowTrackers: TMenuItem; + FMenuItemTorrentFilesTreeShowInfo: TMenuItem; + FMenuItemTorrentFilesTreeShowAll: TMenuItem; + FMenuItemTorrentFilesTreeShowFiles: TMenuItem; + procedure FillThePopupMenu; + procedure AddMenuItem(var MenuItem: TMenuItem; Onclick: TNotifyEvent; + const Caption: string; tag: integer); + procedure MenuItemTorrentFilesTreeShowOrHideItemClick(Sender: TObject); + procedure MenuItemTorrentFilesTreeHideAllClick(Sender: TObject); + procedure MenuItemTorrentFilesTreeShowAllClick(Sender: TObject); + procedure Clear; + procedure MenuItemTorrentFilesTreeSyncWithPopupMenu; + public + //called before AddOneTorrentFileDecoded + procedure BeginUpdate; + + //called after AddOneTorrentFileDecoded + procedure EndUpdate; + + //Add every torrent one by one to the list + procedure AddOneTorrentFileDecoded(DecodeTorrent: TDecodeTorrent); + + constructor Create(Owner: TWinControl); + + destructor Destroy; override; + end; + + + +implementation + +uses torrent_miscellaneous; + +const + TORRENT_FILES_CONTENTS_FORM_CAPTION = + 'Show all the files inside the torrents. (Use right mouse for popup menu.)'; + + { Tcontroler_treeview_torrent_data } + +procedure Tcontroler_treeview_torrent_data.FillThePopupMenu; +begin + FPopupMenuTorrentFilesContent := TPopupMenu.Create(FTreeViewFileContents); + FTreeViewFileContents.PopupMenu := FPopupMenuTorrentFilesContent; + + //There are 5 menu items the user can select/click on + //Create all these 5 menu items. + + //'Show Everything' + AddMenuItem(FMenuItemTorrentFilesTreeShowAll, @MenuItemTorrentFilesTreeShowAllClick, + 'Show Everything', Ord(ctv_ShowEverything));//-1 + + //'Hide All' + AddMenuItem(FMenuItemTorrentFilesTreeHideAll, @MenuItemTorrentFilesTreeHideAllClick, + 'Hide All', Ord(ctv_HideAll));//0 + + //'Show All Files' + AddMenuItem(FMenuItemTorrentFilesTreeShowFiles, + @MenuItemTorrentFilesTreeShowOrHideItemClick, 'Show All Files', + Ord(ctv_ShowAllFiles)); + FMenuItemTorrentFilesTreeShowFiles.AutoCheck := True; + FMenuItemTorrentFilesTreeShowFiles.Checked := True; + + //'Show All Trackers' + AddMenuItem(FMenuItemTorrentFilesTreeShowTrackers, + @MenuItemTorrentFilesTreeShowOrHideItemClick, 'Show All Trackers', + Ord(ctv_ShowAllTrackers)); + FMenuItemTorrentFilesTreeShowTrackers.AutoCheck := True; + FMenuItemTorrentFilesTreeShowTrackers.Checked := True; + + //'Show All Info' + AddMenuItem(FMenuItemTorrentFilesTreeShowInfo, + @MenuItemTorrentFilesTreeShowOrHideItemClick, 'Show All Info', Ord(ctv_ShowAllInfo)); + FMenuItemTorrentFilesTreeShowInfo.AutoCheck := True; + FMenuItemTorrentFilesTreeShowInfo.Checked := True; + +end; + +procedure Tcontroler_treeview_torrent_data.AddMenuItem(var MenuItem: TMenuItem; + Onclick: TNotifyEvent; const Caption: string; tag: integer); +begin + MenuItem := TMenuItem.Create(FPopupMenuTorrentFilesContent); + MenuItem.OnClick := Onclick; + MenuItem.Caption := Caption; + MenuItem.Tag := tag; + FPopupMenuTorrentFilesContent.Items.Add(MenuItem); +end; + +constructor Tcontroler_treeview_torrent_data.Create(Owner: TWinControl); +begin + inherited Create; + FOwner := Owner; + + //create TGroupBox + FGroupBoxTorrentContents := TGroupBox.Create(FOwner); + FGroupBoxTorrentContents.Parent := FOwner; + FGroupBoxTorrentContents.Align := alClient; + FGroupBoxTorrentContents.Caption := TORRENT_FILES_CONTENTS_FORM_CAPTION; + + //create TTreeView + FTreeViewFileContents := TTreeView.Create(FGroupBoxTorrentContents); + FTreeViewFileContents.Parent := FGroupBoxTorrentContents; + FTreeViewFileContents.Align := alClient; + FTreeViewFileContents.ReadOnly := True; + FTreeViewFileContents.ShowRoot := False; + FTreeViewFileContents.Options := + FTreeViewFileContents.Options + [tvoAutoExpand, tvoReadOnly] - [tvoShowRoot]; + + //create popupmenu + FillThePopupMenu; + +end; + +destructor Tcontroler_treeview_torrent_data.Destroy; +begin + inherited Destroy; +end; + + +procedure Tcontroler_treeview_torrent_data.MenuItemTorrentFilesTreeHideAllClick( + Sender: TObject); +var + i, CountTorrents: integer; +begin + //hide all -> Show only torrent file names + + //user what to hide all the items. + //All the popup menu item must first be unchecked. + FMenuItemTorrentFilesTreeShowInfo.Checked := False; + FMenuItemTorrentFilesTreeShowFiles.Checked := False; + FMenuItemTorrentFilesTreeShowTrackers.Checked := False; + //Update the TorrentFilesTree + // MenuItemTorrentFilesTreeSyncWithPopupMenu; + + if not assigned(FTreeNodeRoot) then + exit; + + //how many torrent files are there. + CountTorrents := FTreeNodeRoot.Count; + if CountTorrents = 0 then + exit; + + //Show the torrent files names only. + for i := 0 to CountTorrents - 1 do + begin + FTreeNodeRoot.Items[i].Collapse(True); + end; + +end; + +procedure Tcontroler_treeview_torrent_data.MenuItemTorrentFilesTreeShowAllClick( + Sender: TObject); +begin + //show everything + if assigned(FTreeNodeRoot) then + FTreeNodeRoot.Expand(True); + + //user what to see all the items. + //All the popup menu item must first be checked. + FMenuItemTorrentFilesTreeShowInfo.Checked := True; + FMenuItemTorrentFilesTreeShowFiles.Checked := True; + FMenuItemTorrentFilesTreeShowTrackers.Checked := True; + //Update the TorrentFilesTree + // MenuItemTorrentFilesTreeSyncWithPopupMenu; +end; + +procedure Tcontroler_treeview_torrent_data.Clear; +begin + FTreeViewFileContents.Items.Clear; + FTreeNodeRoot := FTreeViewFileContents.Items.Add(nil, 'Torrent Files'); + FTotalFileInsideTorrent := 0; + FTotalFileSizeInsideTorrent := 0; +end; + +procedure Tcontroler_treeview_torrent_data.MenuItemTorrentFilesTreeSyncWithPopupMenu; +begin + MenuItemTorrentFilesTreeShowOrHideItemClick(FMenuItemTorrentFilesTreeShowTrackers); + MenuItemTorrentFilesTreeShowOrHideItemClick(FMenuItemTorrentFilesTreeShowInfo); + MenuItemTorrentFilesTreeShowOrHideItemClick(FMenuItemTorrentFilesTreeShowFiles); +end; + +procedure Tcontroler_treeview_torrent_data.BeginUpdate; +begin + //Called before loading torrent file. + + //do not update the view + FTreeViewFileContents.BeginUpdate; + + //Clear everything before adding new torrent files + Clear; +end; + +procedure Tcontroler_treeview_torrent_data.EndUpdate; +begin + //The view can be updated again + FTreeViewFileContents.EndUpdate; + + //Update the text about howmany files are there etc. + //Show the size of all the files inside the torrent + //http://en.wikipedia.org/wiki/Gigabyte + FGroupBoxTorrentContents.Caption := + TORRENT_FILES_CONTENTS_FORM_CAPTION + ' (Files count: ' + + IntToStr(FTotalFileInsideTorrent) + ') Files sizes: ' + + ByteSizeToBiggerSizeFormatStr(FTotalFileSizeInsideTorrent) + ''; + + //Sync the popup menu with show/hide items. + MenuItemTorrentFilesTreeSyncWithPopupMenu; + +end; + + +procedure Tcontroler_treeview_torrent_data.MenuItemTorrentFilesTreeShowOrHideItemClick( + Sender: TObject); +var + i, CountTorrents, itemsNr: integer; + ShowNode: boolean; +begin + //Show or hide all the items below the torrent files. + + //Get the top node. + if not assigned(FTreeNodeRoot) then + exit; + + //how many torrent files are there. + CountTorrents := FTreeNodeRoot.Count; + if CountTorrents = 0 then + exit; + + //The tag number define if it is for files, trackers or info items + itemsNr := TMenuItem(Sender).tag; + + //Must show or hide the items + ShowNode := TMenuItem(Sender).Checked; + + //process all the torrent files one by one. + for i := 0 to CountTorrents - 1 do + begin + if ShowNode then + begin + FTreeNodeRoot.Items[i].Expand(False); //Show the torrent name + child + FTreeNodeRoot.Items[i].Items[itemsNr].Expand(False); //expand child + end + else + begin + FTreeNodeRoot.Items[i].Items[itemsNr].Collapse(False); + end; + end; +end; + +procedure Tcontroler_treeview_torrent_data.AddOneTorrentFileDecoded( + DecodeTorrent: TDecodeTorrent); +var + CountFiles: integer; + TorrentFileNameStr, TrackerStr: utf8string; + TreeNodeTorrent, TreeNodeFiles, TreeNodeTrackers, TreeNodeInfo: TTreeNode; +begin + //--------------------- Fill the treeview with torrent files + + TorrentFileNameStr := ExtractFileName(DecodeTorrent.FilenameTorrent); + + //Add the torrent file name + size of all the files combined. + TorrentFileNameStr := TorrentFileNameStr + ' (Version: ' + + DecodeTorrent.TorrentVersionToString + ') SIZE: ' + + ByteSizeToBiggerSizeFormatStr(DecodeTorrent.TotalFileSize) + + ' Files: ' + IntToStr(DecodeTorrent.InfoFilesCount) + '' + + ' Tracker: ' + IntToStr(DecodeTorrent.TrackerList.Count) + ''; + + + TreeNodeTorrent := FTreeViewFileContents.Items.AddChild(FTreeNodeRoot, + //FTrackerList.TorrentFileNameList[RowIndex]); //With directory path + TorrentFileNameStr); //Without directory path + + //must be in this order (Files, Trackers, Info) + TreeNodeFiles := FTreeViewFileContents.Items.AddChild(TreeNodeTorrent, + 'Files V' + IntToStr(DecodeTorrent.InfoFilesVersion)); + + TreeNodeTrackers := FTreeViewFileContents.Items.AddChild(TreeNodeTorrent, + 'Trackers'); + TreeNodeInfo := FTreeViewFileContents.Items.AddChild(TreeNodeTorrent, 'Info'); + + //Show all the files inside the torrent + if DecodeTorrent.InfoFilesCount > 0 then + begin + for CountFiles := 0 to DecodeTorrent.InfoFilesCount - 1 do + begin + FTreeViewFileContents.Items.AddChild(TreeNodeFiles, + DecodeTorrent.InfoFilesNameIndex(CountFiles) + ' SIZE: ' + + ByteSizeToBiggerSizeFormatStr(DecodeTorrent.InfoFilesLengthIndex(CountFiles))); + end; + end; + + //Show a how many files are there + TreeNodeFiles.Text := TreeNodeFiles.Text + ' (' + IntToStr(TreeNodeFiles.Count) + ')'; + + //Show all the trackers inside the torrent + for TrackerStr in DecodeTorrent.TrackerList do + begin + FTreeViewFileContents.Items.AddChild(TreeNodeTrackers, TrackerStr); + end; + + //Show a how many trackers are there + TreeNodeTrackers.Text := TreeNodeTrackers.Text + ' (' + + IntToStr(TreeNodeTrackers.Count) + ')'; + + + //Show all the info of torrent + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Name: ' + DecodeTorrent.Name); + + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Comment: ' + + DecodeTorrent.Comment); + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Info Hash V1: ' + + DecodeTorrent.InfoHash_V1); + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Info Hash V2: ' + + DecodeTorrent.InfoHash_V2); + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Created On: ' + + DateTimeToStr(DecodeTorrent.CreatedDate)); + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Created By: ' + + DecodeTorrent.CreatedBy); + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Piece Lenght: ' + + IntToStr(DecodeTorrent.PieceLenght div 1024) + ' KiB'); + if DecodeTorrent.PrivateTorrent then + begin + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Private: yes'); + end + else + begin + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Private: no'); + end; + // some private torrent have info:source + if DecodeTorrent.InfoSource <> '' then + begin + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Source: ' + + DecodeTorrent.InfoSource); + end; + + if DecodeTorrent.MetaVersion > 0 then + begin // 'meta version'is in torrent file present + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Meta Version: ' + + IntToStr(DecodeTorrent.MetaVersion)); + end; + + FTreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Padding (beb 47): ' + + DecodeTorrent.PaddingToString); + + //All the files count inside the torrent must be added to FTotalFileInsideTorrent + Inc(FTotalFileInsideTorrent, DecodeTorrent.InfoFilesCount); + + //The file size of all files inside the torrent must be added to FTotalFileSizeInsideTorrent + Inc(FTotalFileSizeInsideTorrent, DecodeTorrent.TotalFileSize); + +end; + + + +end. diff --git a/source/code/controlergridtorrentdata.pas b/source/code/controlergridtorrentdata.pas index 4685b3e..a885460 100644 --- a/source/code/controlergridtorrentdata.pas +++ b/source/code/controlergridtorrentdata.pas @@ -1,22 +1,4 @@ -{ MIT licence -Copyright (c) Gerry Ferdinandus - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software -and associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -} - +// SPDX-License-Identifier: MIT unit controlergridtorrentdata; { @@ -46,14 +28,17 @@ TControlerGridTorrentData = class //The collumn must be in this design order. FTorrentFile, //0 FInfoFileName, //1 - FInfoHash, //2 - FCreatedOn, //3 - FCreatedBy, //4 - FComment, //5 - FPrivateTorrent, //6 - FPieceLength, //7 - FTotaSize, //8 - FIndexOrder //9 + FTorrentVersion, //2 + FPadding, //3 + FInfoHash, //4 + FCreatedOn, //5 + FCreatedBy, //6 + FComment, //7 + FPrivateTorrent, //8 + FInfoSource, //9 + FPieceLength, //10 + FTotaSize, //11 + FIndexOrder //12 : TGridColumn; FRowIsMovedNeedUpdate: boolean; @@ -66,14 +51,17 @@ TControlerGridTorrentData = class //All the string that can be written to grid. TorrentFile, //0 InfoFileName, //1 - InfoHash, //2 - CreatedOn, //3 - CreatedBy, //4 - Comment, //5 - PrivateTorrent, //6 - PieceLength, //7 - TotaSize, //8 - IndexOrder //9 + TorrentVersion, //2 + Padding, //3 + InfoHash, //4 + CreatedOn, //5 + CreatedBy, //6 + Comment, //7 + PrivateTorrent, //8 + InfoSource, //9 + PieceLength, //10 + TotaSize, //11 + IndexOrder //12 : UTF8String; procedure ClearAllImageIndex; @@ -88,7 +76,7 @@ implementation { TControlerGridTorrentData } const - COLUMN_COUNT = 10; + COLUMN_COUNT = 11; procedure TControlerGridTorrentData.StringGridTorrentDataColRowMoved(Sender: TObject; IsColumn: boolean; sIndex, tIndex: integer); @@ -151,11 +139,14 @@ procedure TControlerGridTorrentData.AppendRow; //write all the string to the cell. WriteCell(FTorrentFile, TorrentFile); WriteCell(FInfoFileName, InfoFileName); + WriteCell(FTorrentVersion, TorrentVersion); + WriteCell(FPadding, Padding); WriteCell(FInfoHash, InfoHash); WriteCell(FCreatedOn, CreatedOn); WriteCell(FCreatedBy, CreatedBy); WriteCell(FComment, Comment); WriteCell(FPrivateTorrent, PrivateTorrent); + WriteCell(FInfoSource, InfoSource); WriteCell(FPieceLength, PieceLength); WriteCell(FTotaSize, TotaSize); WriteCell(FIndexOrder, IndexOrder); @@ -198,14 +189,17 @@ constructor TControlerGridTorrentData.Create(StringGridTorrentData: TStringGrid) //Track the column AddColumn(FTorrentFile, 0); AddColumn(FInfoFileName, 1); - AddColumn(FInfoHash, 2); - AddColumn(FCreatedOn, 3); - AddColumn(FCreatedBy, 4); - AddColumn(FComment, 5); - AddColumn(FPrivateTorrent, 6); - AddColumn(FPieceLength, 7); - AddColumn(FTotaSize, 8); - AddColumn(FIndexOrder, 9); + AddColumn(FTorrentVersion, 2); + AddColumn(FPadding, 3); + AddColumn(FInfoHash, 4); + AddColumn(FCreatedOn, 5); + AddColumn(FCreatedBy, 6); + AddColumn(FComment, 7); + AddColumn(FPrivateTorrent, 8); + AddColumn(FInfoSource, 9); + AddColumn(FPieceLength, 10); + AddColumn(FTotaSize, 11); + AddColumn(FIndexOrder, 12); //Fillin the tag value UpdateColumnTag; diff --git a/source/code/decodetorrent.pas b/source/code/decodetorrent.pas index c0be621..56b3b70 100644 --- a/source/code/decodetorrent.pas +++ b/source/code/decodetorrent.pas @@ -1,22 +1,4 @@ -{ MIT licence -Copyright (c) Gerry Ferdinandus - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software -and associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -} - +// SPDX-License-Identifier: MIT unit DecodeTorrent; {$mode objfpc}{$H+} @@ -35,15 +17,16 @@ interface uses - Classes, SysUtils, contnrs, LazUTF8Classes, BEncode; + Classes, SysUtils, contnrs, BEncode; type - //Every torrent file have one or more files. - TDecodeTorrentFile = record - Filename: utf8string; - FileLength: int64; - end; + TTorrentVersion = ( + tv_V1, //< V1 version + tv_V2, //< V2 version + tv_Hybrid, //< V1 + V2 hybrid + tv_unknown //< Can not decode it + ); TDecodeTorrentFileNameAndLength = class Filename: utf8string; @@ -54,48 +37,66 @@ TDecodeTorrentFileNameAndLength = class { TDecodeTorrent } TDecodeTorrent = class private - FFilenameTorrent: UTF8String; + FFilenameTorrent: utf8string; FMemoryStream: TMemoryStream; FBEncoded: TBEncoded; FObjectListFileNameAndLength: TObjectList; FTotalFileSize: int64; + FTorrentVersion: TTorrentVersion; //Torrent file must have 'info' item. FBEncoded_Info: TBEncoded; FBEncoded_Comment: TBEncoded; - FInfoHash: utf8string; + FInfoHash_V1: utf8string; + FInfoHash_V2: utf8string; FCreatedBy: utf8string; FCreatedDate: TDateTime; FComment: utf8string; + FInfoSource: utf8string; FName: utf8string; FPieceLenght: int64; + FMetaVersion: int64; FPrivateTorrent: boolean; + FPaddingPresent_V1: boolean; + FPaddingPresent_V2: boolean; + FInfoFilesVersion: integer; function DecodeTorrent: boolean; overload; + function isPresentInfoFiles_V1: boolean; + function isPresentInfoFileTree_V2: boolean; + + function GetSha256(const Source: utf8string): utf8string; + function GetMetaVersion: int64; function GetAnnounceList: boolean; - function GetFileList: boolean; - function GetInfoHash: utf8string; + function GetFileList_V1: boolean; + function GetFileList_V2: boolean; + function GetOneFileTorrent: boolean; function GetCreatedBy: utf8string; function GetCreatedDate: TDateTime; function GetComment: utf8string; function GetName: utf8string; function GetPieceLenght: int64; function GetPrivateTorrent: boolean; + function GetInfoSource: utf8string; procedure SetComment(const AValue: utf8string); public //All the trackers inside this torrent file TrackerList: TStringList; - property FilenameTorrent: UTF8String read FFilenameTorrent; + property MetaVersion: int64 read FMetaVersion; + property TorrentVersion: TTorrentVersion read FTorrentVersion; + property PaddingPresent_V1: boolean read FPaddingPresent_V1; + property PaddingPresent_V2: boolean read FPaddingPresent_V2; + + property FilenameTorrent: utf8string read FFilenameTorrent; - //Every torrent file have one or more files. - // TorrentFilesArray: Array of TDecodeTorrentFile; property TotalFileSize: int64 read FTotalFileSize; //Info hash - property InfoHash: utf8string read FInfoHash; + property InfoHash_V1: utf8string read FInfoHash_V1; + property InfoHash_V2: utf8string read FInfoHash_V2; //Created by property CreatedBy: utf8string read FCreatedBy; @@ -114,19 +115,28 @@ TDecodeTorrent = class //public/private flag property PrivateTorrent: boolean read FPrivateTorrent; - procedure RemovePrivateTorrentFlag; - procedure AddPrivateTorrentFlag; + function RemovePrivateTorrentFlag: boolean; + function AddPrivateTorrentFlag: boolean; - //info.files + //info.source + property InfoSource: utf8string read FInfoSource; + function InfoSourceRemove: boolean; + function InfoSourceAdd(const Value: utf8string): boolean; + + //info.files and info. file tree + property InfoFilesVersion: integer read FInfoFilesVersion; function InfoFilesCount: integer; function InfoFilesNameIndex(index: integer): utf8string; function InfoFilesLengthIndex(index: integer): int64; //Announce list - procedure RemoveAnnounce; - procedure RemoveAnnounceList; - procedure ChangeAnnounce(const TrackerURL: utf8string); - procedure ChangeAnnounceList(StringList: TStringList); + function RemoveAnnounce: boolean; + function RemoveAnnounceList: boolean; + function ChangeAnnounce(const TrackerURL: utf8string): boolean; + function ChangeAnnounceList(StringList: TStringList): boolean; + + function TorrentVersionToString: utf8string; + function PaddingToString: utf8string; //Load torrent file function DecodeTorrent(const Filename: utf8string): boolean; overload; @@ -142,7 +152,7 @@ TDecodeTorrent = class implementation -uses dateutils, SHA1, FileUtil, LazUTF8; +uses dateutils, SHA1, DCPsha256, FileUtil, LazUTF8; function SortFileName(Item1, Item2: Pointer): integer; begin @@ -160,7 +170,7 @@ function Sort_(Item1, Item2: Pointer): integer; constructor TDecodeTorrent.Create; begin inherited; - //Every torrent have + //Every torrent have file list FObjectListFileNameAndLength := TObjectList.Create; //List for all the trackers. @@ -188,13 +198,13 @@ destructor TDecodeTorrent.Destroy; function TDecodeTorrent.DecodeTorrent(const Filename: utf8string): boolean; var - S: TFileStreamUtf8; + S: TFileStream; begin FFilenameTorrent := Filename; //Load torrent file in FMemoryStream. This will be process by DecodeTorrent(); try //Support FilenameTorrent with unicode. - S := TFileStreamUtf8.Create(FilenameTorrent, fmOpenRead or fmShareDenyWrite); + S := TFileStream.Create(FilenameTorrent, fmOpenRead or fmShareDenyWrite); try FMemoryStream.LoadFromStream(S); finally @@ -223,7 +233,7 @@ function TDecodeTorrent.GetAnnounceList: boolean; var TempBEncoded: TBEncoded; i, Count: integer; - TrackerStr: UTF8String; + TrackerStr: utf8string; begin //return false, if crash at decoding. Announce is optional in torrent file. TrackerList.Clear; @@ -265,90 +275,176 @@ function TDecodeTorrent.GetAnnounceList: boolean; end; end; -function TDecodeTorrent.GetFileList: boolean; +function TDecodeTorrent.GetFileList_V1: boolean; var - //TempBEncodedInfo, - TempBEncodedInfoFiles, TempBEncodedInfoFilesPath: TBEncoded; - TempBEncodedInfoFilesData: TBEncodedData; - - i, x, countFiles, countPath: integer; - FilenameWithPathStr, Filename: utf8string; - - DecodeTorrentFileName: TDecodeTorrentFileNameAndLength; + P_File: Pointer; + P_Item: Pointer; + P_Path: Pointer; FileLength: int64; + InfoFiles: TBEncoded; + NodeData: TBEncodedData; + ThisIsFileWithPadding: boolean; + FilenameWithPathStr: utf8string; + DecodeTorrentFileName: TDecodeTorrentFileNameAndLength; begin -{ info/files/path -> all the files names - or - info/name -> one file name only -} - //return false if there is no file at all. This must not be posible. - + // return false if there is no file at all. This must not be posible. + // info/files/path -> all the files names + FInfoFilesVersion := 1; FObjectListFileNameAndLength.Clear; FTotalFileSize := 0; - try + try {find 'info.files' } - TempBEncodedInfoFiles := FBEncoded_Info.ListData.FindElement('files'); + InfoFiles := FBEncoded_Info.ListData.FindElement('files'); - if assigned(TempBEncodedInfoFiles) then + if assigned(InfoFiles) then begin //'info.files' found - countFiles := TempBEncodedInfoFiles.ListData.Count; - if countFiles > 0 then - begin - for i := 0 to countFiles - 1 do - begin - //Get the info.files node. - TempBEncodedInfoFilesData := TempBEncodedInfoFiles.ListData.Items[i]; - - //Get the file name with path - FilenameWithPathStr := ''; - TempBEncodedInfoFilesPath := - TempBEncodedInfoFilesData.Data.ListData.FindElement('path'); - countPath := TempBEncodedInfoFilesPath.ListData.Count; - for x := 0 to countPath - 1 do + + for P_File in InfoFiles.ListData do + begin // Every file need to be process one by one + + ThisIsFileWithPadding := False; + for P_Item in TBEncodedData(P_File).Data.ListData do + begin // Every item inside the file must be process one by one + NodeData := TBEncodedData(P_Item); + + // Get the file name with path + if NodeData.Header = 'path' then + begin + FilenameWithPathStr := ''; + for P_Path in NodeData.Data.ListData do + begin + FilenameWithPathStr := + FilenameWithPathStr + DirectorySeparator + TBEncodedData( + P_Path).Data.StringData; + end; + Continue; + end; + + // Get the file length + if NodeData.Header = 'length' then begin - FilenameWithPathStr := - FilenameWithPathStr + DirectorySeparator + - TempBEncodedInfoFilesPath.ListData.Items[x].Data.StringData; + FileLength := NodeData.Data.IntegerData; + Continue; end; + // check if this is padding + if NodeData.Header = 'attr' then + begin + ThisIsFileWithPadding := UTF8Pos('p', NodeData.Data.StringData) > 0; + if ThisIsFileWithPadding and not FPaddingPresent_V1 then + begin + FPaddingPresent_V1 := True; + end; + end; + end; // Every item inside the file - //Get the file length - FileLength := TempBEncodedInfoFilesData.Data.ListData.FindElement( - 'length').IntegerData; + // one file is decoded. Now add it to the list + DecodeTorrentFileName := TDecodeTorrentFileNameAndLength.Create; + DecodeTorrentFileName.Filename := FilenameWithPathStr; + DecodeTorrentFileName.FileLength := FileLength; + FObjectListFileNameAndLength.Add(DecodeTorrentFileName); - DecodeTorrentFileName := TDecodeTorrentFileNameAndLength.Create; - DecodeTorrentFileName.Filename := FilenameWithPathStr; - DecodeTorrentFileName.FileLength := FileLength; - FObjectListFileNameAndLength.Add(DecodeTorrentFileName); - //add it to the total sum of all files inside the torrent. + //add FileLength to the total sum of all files inside the torrent. + if not ThisIsFileWithPadding then + begin Inc(FTotalFileSize, FileLength); end; + end; // Every file need to be process one by one + end; //'info.files' found + + //There is file found inside the torrent? + Result := FObjectListFileNameAndLength.Count > 0; + + except + //Can not found items that should be present. + Result := False; + end; + +end; + +function TDecodeTorrent.GetFileList_V2: boolean; +var + BEncodedFileTree: TBEncoded; + + procedure ProcessPathOrFile(const Node: TBEncoded; const path: string); + var + P_Item: Pointer; + FileLength: int64; + NodeData: TBEncodedData; + ThisIsFileWithPadding: boolean; + DecodeTorrentFileName: TDecodeTorrentFileNameAndLength; + begin + // Everyting in the tree is befDictionary + ThisIsFileWithPadding := False; + FileLength := -1; // -1 is no file length found yet. + + + for P_Item in node.ListData do + begin // read all the befDictionary list items one by one + NodeData := TBEncodedData(P_Item); + + // Found a new path node + if NodeData.Data.Format = befDictionary then + begin + ProcessPathOrFile(NodeData.Data, path + DirectorySeparator + NodeData.Header); + Continue; end; - end - else //there is no 'info.files' found. This is an 'one file' torrent. - begin// Look for'info.name' and 'info.length' - //Get the file name - Filename := FBEncoded_Info.ListData.FindElement('name').StringData; - FileLength := FBEncoded_Info.ListData.FindElement('length').IntegerData; + // check if this present dictionary is a padding node + if (NodeData.Data.Format = befString) and (NodeData.Header = 'attr') then + begin + ThisIsFileWithPadding := (UTF8Pos('p', NodeData.Data.StringData) > 0); + if ThisIsFileWithPadding and not FPaddingPresent_V2 then + begin + FPaddingPresent_V2 := True; + end; + Continue; + end; + // Found a file node? This is at the end of the tree node. + if (NodeData.Data.Format = befInteger) and (NodeData.Header = 'length') then + begin + FileLength := NodeData.Data.IntegerData; + end; + + end; // read all the befDictionary list items one by one + + // One dictionary list have been process + // Is this a dictionary list with a file inside? + if FileLength >= 0 then + begin DecodeTorrentFileName := TDecodeTorrentFileNameAndLength.Create; - DecodeTorrentFileName.Filename := Filename; + DecodeTorrentFileName.Filename := ExtractFileDir(path); DecodeTorrentFileName.FileLength := FileLength; FObjectListFileNameAndLength.Add(DecodeTorrentFileName); - - Inc(FTotalFileSize, FileLength); + //add FileLength to the total sum of all files inside the torrent. + if not ThisIsFileWithPadding then + begin + Inc(FTotalFileSize, DecodeTorrentFileName.FileLength); + end; end; + end; + +begin + Result := False; + FInfoFilesVersion := 2; + FObjectListFileNameAndLength.Clear; + FTotalFileSize := 0; + try + {find 'info.file tree' } + BEncodedFileTree := FBEncoded_Info.ListData.FindElement('file tree'); + if assigned(BEncodedFileTree) then + begin //'info.file tree' found + ProcessPathOrFile(BEncodedFileTree, ''); + end; - //There is file found inside the torrent. + //There is file found inside the torrent? Result := FObjectListFileNameAndLength.Count > 0; - //We prefer that the file name are in sorted order. - FObjectListFileNameAndLength.Sort(@SortFileName); except //Can not found items that should be present. @@ -357,6 +453,32 @@ function TDecodeTorrent.GetFileList: boolean; end; +function TDecodeTorrent.GetOneFileTorrent: boolean; +var + Filename: utf8string; + DecodeTorrentFileName: TDecodeTorrentFileNameAndLength; + TempBEncoded: TBEncoded; +begin + try + // This is a torrent without file list/tree + TempBEncoded := FBEncoded_Info.ListData.FindElement('length'); + Result := assigned(TempBEncoded); + if Result then + begin + FInfoFilesVersion := 1; + FObjectListFileNameAndLength.Clear; + Filename := FBEncoded_Info.ListData.FindElement('name').StringData; + DecodeTorrentFileName := TDecodeTorrentFileNameAndLength.Create; + DecodeTorrentFileName.Filename := Filename; + DecodeTorrentFileName.FileLength := TempBEncoded.IntegerData; + FObjectListFileNameAndLength.Add(DecodeTorrentFileName); + FTotalFileSize := TempBEncoded.IntegerData; + end; + except + Result := False; + end; +end; + function TDecodeTorrent.DecodeTorrent: boolean; begin Result := False; @@ -367,20 +489,19 @@ function TDecodeTorrent.DecodeTorrent: boolean; FreeAndNil(FBEncoded); end; - //the torrent file inside FMemoryStream -> BEnencode it - FMemoryStream.Position := 0; - FBEncoded := TBEncoded.Create(FMemoryStream); - - - - - //Read the tracker list and file list inside the torrent file. - FTotalFileSize := 0; + // Clear List that will be filed later. TrackerList.Clear; - FObjectListFileNameAndLength.Clear; + + // Reset to default value FTotalFileSize := 0; + FTorrentVersion := tv_unknown; + FPaddingPresent_V1 := False; + FPaddingPresent_V2 := False; + //the torrent file inside FMemoryStream -> BEnencode it + FMemoryStream.Position := 0; + FBEncoded := TBEncoded.Create(FMemoryStream); //torrent file MUST begin with befDictionary. if FBEncoded.Format <> befDictionary then @@ -391,22 +512,131 @@ function TDecodeTorrent.DecodeTorrent: boolean; if not assigned(FBEncoded_Info) then exit; //error + // Is this V1,V2 or hybrid torrent type? + if isPresentInfoFiles_V1 then FTorrentVersion := tv_V1; + if isPresentInfoFileTree_V2 then + begin + if FTorrentVersion = tv_V1 then + FTorrentVersion := tv_Hybrid + else + FTorrentVersion := tv_V2; + end; //Accept torrent only when there is no issue in reading AnnounceList and file list - if GetAnnounceList and GetFileList then + if GetAnnounceList then begin - Result := True; + case FTorrentVersion of + tv_V1: Result := GetFileList_V1; + tv_V2: Result := GetFileList_V2; + tv_Hybrid: + begin // Only V2 is actualy used. V1 need to be read to look for padding. + Result := GetFileList_V1; + if Result then GetFileList_V2; + end; + else + Assert(False, 'Missing torrent version'); + end; + end; + + if not Result then + begin // There is nothing found in tree list. Maybe this is a Torrent with one file? + Result := GetOneFileTorrent; + if Result then FTorrentVersion := tv_V1; end; - FInfoHash := GetInfoHash; + // FInfoHash_V1 := GetInfoHash; FCreatedBy := GetCreatedBy; FCreatedDate := GetCreatedDate; FComment := GetComment; FName := GetName; FPieceLenght := GetPieceLenght; FPrivateTorrent := GetPrivateTorrent; + FInfoSource := GetInfoSource; + FMetaVersion := GetMetaVersion; + except + Result := False; + end; +end; +function TDecodeTorrent.isPresentInfoFiles_V1: boolean; +var + Info: TBEncoded; + str: utf8string; +begin + try + {find 'info.files' } + Info := FBEncoded_Info.ListData.FindElement('files'); + Result := assigned(info); + if Result then + begin + str := ''; + TBEncoded.Encode(FBEncoded_Info, str); + FInfoHash_V1 := UpperCase(SHA1Print(SHA1String(str))); + end + else + begin + FInfoHash_V1 := 'N/A'; + end; except + Result := False; + end; +end; + +function TDecodeTorrent.isPresentInfoFileTree_V2: boolean; +var + Info: TBEncoded; + str: utf8string; +begin + try + {find 'info.file tree' } + Info := FBEncoded_Info.ListData.FindElement('file tree'); + Result := assigned(info); + if Result then + begin + str := ''; + TBEncoded.Encode(FBEncoded_Info, str); + FInfoHash_V2 := UpperCase(GetSha256(str)); + end + else + begin + FInfoHash_V2 := 'N/A'; + end; + except + Result := False; + end; +end; + +function TDecodeTorrent.GetSha256(const Source: utf8string): utf8string; +var + Hash: TDCP_sha256; + Digest: array[0..31] of byte; + i: integer; +begin + Digest[0] := 0; // suppres compiler warning. + Hash := TDCP_sha256.Create(nil); + Hash.Init; + Hash.UpdateStr(Source); + Hash.Final(Digest); + Result := ''; + for i := Low(Digest) to High(Digest) do + begin + Result := Result + IntToHex(Digest[i], 2); + end; + Hash.Free; +end; + +function TDecodeTorrent.GetMetaVersion: int64; +var + TempBEncoded: TBEncoded; +begin + Result := 0; + try + {find 'meta version' } + TempBEncoded := FBEncoded_Info.ListData.FindElement('meta version'); + if assigned(TempBEncoded) then + Result := TempBEncoded.IntegerData; + except + Result := 0; end; end; @@ -426,7 +656,6 @@ function TDecodeTorrent.InfoFilesLengthIndex(index: integer): int64; Result := TDecodeTorrentFileNameAndLength(FObjectListFileNameAndLength[index]).FileLength; end; - function TDecodeTorrent.GetPrivateTorrent: boolean; var TempBEncoded: TBEncoded; @@ -438,12 +667,27 @@ function TDecodeTorrent.GetPrivateTorrent: boolean; if assigned(TempBEncoded) then Result := TempBEncoded.IntegerData = 1; except + Result := False; + end; +end; + +function TDecodeTorrent.GetInfoSource: utf8string; +var + TempBEncoded: TBEncoded; +begin + Result := ''; + try + {find 'source' } + TempBEncoded := FBEncoded_Info.ListData.FindElement('source'); + if assigned(TempBEncoded) then + Result := TempBEncoded.StringData; + except + Result := ''; end; end; procedure TDecodeTorrent.SetComment(const AValue: utf8string); var - // Encoded: TBEncoded; Data: TBEncodedData; begin if FComment = AValue then @@ -473,11 +717,11 @@ procedure TDecodeTorrent.SetComment(const AValue: utf8string); end; except + FComment := AValue; end; end; - { try Encoded := TBEncoded.Create; @@ -492,17 +736,19 @@ procedure TDecodeTorrent.SetComment(const AValue: utf8string); } -procedure TDecodeTorrent.RemovePrivateTorrentFlag; +function TDecodeTorrent.RemovePrivateTorrentFlag: boolean; begin try FBEncoded_Info.ListData.RemoveElement('private'); + Result := True; except + Result := False; end; //read databack again - GetPrivateTorrent; + FPrivateTorrent := GetPrivateTorrent; end; -procedure TDecodeTorrent.AddPrivateTorrentFlag; +function TDecodeTorrent.AddPrivateTorrentFlag: boolean; var Encoded: TBEncoded; Data: TBEncodedData; @@ -516,39 +762,78 @@ procedure TDecodeTorrent.AddPrivateTorrentFlag; Data.Header := 'private'; FBEncoded_Info.ListData.Add(Data); FBEncoded_Info.ListData.Sort(@sort_);//text must be in alfabetical order. + Result := True; + except + Result := False; + end; + //read databack again + FPrivateTorrent := GetPrivateTorrent; +end; + +function TDecodeTorrent.InfoSourceRemove: boolean; +begin + try + FBEncoded_Info.ListData.RemoveElement('source'); + Result := True; except + Result := False; end; //read databack again - GetPrivateTorrent; + FInfoSource := GetInfoSource; +end; + +function TDecodeTorrent.InfoSourceAdd(const Value: utf8string): boolean; +var + Encoded: TBEncoded; + Data: TBEncodedData; +begin//remove the old one and create a new one + InfoSourceRemove; + try + Encoded := TBEncoded.Create; + Encoded.Format := befString; + Encoded.StringData := Value; + Data := TBEncodedData.Create(Encoded); + Data.Header := 'source'; + FBEncoded_Info.ListData.Add(Data); + FBEncoded_Info.ListData.Sort(@sort_);//text must be in alfabetical order. + Result := True; + except + Result := False; + end; + FInfoSource := GetInfoSource; end; -procedure TDecodeTorrent.RemoveAnnounce; +function TDecodeTorrent.RemoveAnnounce: boolean; begin try FBEncoded.ListData.RemoveElement('announce'); + Result := True; except + Result := False; end; end; -procedure TDecodeTorrent.RemoveAnnounceList; +function TDecodeTorrent.RemoveAnnounceList: boolean; begin try FBEncoded.ListData.RemoveElement('announce-list'); + Result := True; except + Result := False; end; end; function TDecodeTorrent.SaveTorrent(const Filename: utf8string): boolean; var str: utf8string; - S: TFileStreamUTF8; + S: TFileStream; begin try //Encode it to string format str := ''; TBEncoded.Encode(FBEncoded, str); //Write string to file. Support filename with unicode. - S := TFileStreamUTF8.Create(FileName, fmCreate); + S := TFileStream.Create(FileName, fmCreate); try Result := s.Write(Str[1], length(Str)) = length(Str); finally @@ -561,7 +846,7 @@ function TDecodeTorrent.SaveTorrent(const Filename: utf8string): boolean; -procedure TDecodeTorrent.ChangeAnnounce(const TrackerURL: utf8string); +function TDecodeTorrent.ChangeAnnounce(const TrackerURL: utf8string): boolean; var Encoded: TBEncoded; Data: TBEncodedData; @@ -575,19 +860,21 @@ procedure TDecodeTorrent.ChangeAnnounce(const TrackerURL: utf8string); Data.Header := 'announce'; FBEncoded.ListData.Add(Data); FBEncoded.ListData.Sort(@sort_);//text must be in alfabetical order. + Result := True; except + Result := False; end; end; -procedure TDecodeTorrent.ChangeAnnounceList(StringList: TStringList); +function TDecodeTorrent.ChangeAnnounceList(StringList: TStringList): boolean; var EncodedListRoot, EncodedList, EncodedString: TBEncoded; DataRootBEncodedData: TBEncodedData; i: integer; begin + Result := True; //remove the present one. RemoveAnnounceList; - //if there is nothing in the list then exit. if StringList.Count = 0 then Exit; @@ -622,17 +909,40 @@ procedure TDecodeTorrent.ChangeAnnounceList(StringList: TStringList); FBEncoded.ListData.Sort(@sort_);//text must be in alfabetical order. except + Result := False; end; end; -function TDecodeTorrent.GetInfoHash: utf8string; +function TDecodeTorrent.TorrentVersionToString: utf8string; begin - Result := ''; - try - //The info.value will be hash with SHA1 - TBEncoded.Encode(FBEncoded_Info, Result); - Result := UpperCase(SHA1Print(SHA1String(Result))); - except + case FTorrentVersion of + tv_V1: Result := 'V1'; + tv_V2: Result := 'V2'; + tv_Hybrid: Result := 'Hybrid (V1&V2)'; + tv_unknown: Result := 'unknown'; + else + Result := 'TorrentVersionToString: unkown value'; + end; +end; + +function TDecodeTorrent.PaddingToString: utf8string; +begin + case FTorrentVersion of + tv_V1: + begin + Result := BoolToStr(FPaddingPresent_V1, 'Yes', 'No'); + end; + tv_V2: + begin + Result := BoolToStr(FPaddingPresent_V2, 'Yes', 'No'); + end; + tv_Hybrid: + begin // Show only V2 hash. No space for both V1 and V2 + Result := 'V1:' + BoolToStr(FPaddingPresent_V1, 'Yes', 'No') + + ' V2:' + BoolToStr(FPaddingPresent_V2, 'Yes', 'No'); + end; + else + Result := 'N/A' end; end; @@ -645,8 +955,8 @@ function TDecodeTorrent.GetCreatedBy: utf8string; TempBEncoded := FBEncoded.ListData.FindElement('created by'); if assigned(TempBEncoded) then Result := TempBEncoded.StringData; - except + Result := ''; end; end; @@ -660,6 +970,7 @@ function TDecodeTorrent.GetCreatedDate: TDateTime; if assigned(TempBEncoded) then Result := UnixToDateTime(TempBEncoded.IntegerData); except + Result := 0; end; end; @@ -671,6 +982,7 @@ function TDecodeTorrent.GetComment: utf8string; if assigned(FBEncoded_Comment) then Result := UTF8Trim(FBEncoded_Comment.StringData); except + Result := ''; end; end; @@ -685,6 +997,7 @@ function TDecodeTorrent.GetName: utf8string; if assigned(TempBEncoded) then Result := TempBEncoded.StringData; except + Result := ''; end; end; @@ -699,6 +1012,7 @@ function TDecodeTorrent.GetPieceLenght: int64; if assigned(TempBEncoded) then Result := TempBEncoded.IntegerData; except + Result := 0; end; end; diff --git a/source/code/main.lfm b/source/code/main.lfm index e02b99d..3837612 100644 --- a/source/code/main.lfm +++ b/source/code/main.lfm @@ -1,11 +1,11 @@ object FormTrackerModify: TFormTrackerModify - Left = 1348 + Left = 429 Height = 607 - Top = 462 + Top = 183 Width = 1179 AllowDropFiles = True Caption = 'Bittorrent Tracker Editor' - ClientHeight = 587 + ClientHeight = 607 ClientWidth = 1179 Constraints.MinHeight = 500 Constraints.MinWidth = 700 @@ -16,10 +16,10 @@ object FormTrackerModify: TFormTrackerModify OnDropFiles = FormDropFiles OnShow = FormShow Position = poScreenCenter - LCLVersion = '1.6.2.0' + LCLVersion = '3.6.0.0' object PageControl: TPageControl Left = 0 - Height = 587 + Height = 607 Top = 0 Width = 1179 ActivePage = TabSheetTrackersList @@ -28,15 +28,15 @@ object FormTrackerModify: TFormTrackerModify TabOrder = 0 object TabSheetTrackersList: TTabSheet Caption = 'Trackers List' - ClientHeight = 561 + ClientHeight = 581 ClientWidth = 1171 object PanelTop: TPanel Left = 0 - Height = 561 + Height = 581 Top = 0 Width = 1171 Align = alClient - ClientHeight = 561 + ClientHeight = 581 ClientWidth = 1171 TabOrder = 0 object GroupBoxNewTracker: TGroupBox @@ -62,27 +62,26 @@ object FormTrackerModify: TFormTrackerModify end object GroupBoxPresentTracker: TGroupBox Left = 1 - Height = 353 + Height = 373 Top = 207 Width = 1169 Align = alClient Caption = 'Present trackers in all torrent files. Select the one that you want to keep.' - ClientHeight = 335 + ClientHeight = 355 ClientWidth = 1165 Constraints.MinHeight = 100 ParentBidiMode = False TabOrder = 1 object StringGridTrackerOnline: TStringGrid Left = 0 - Height = 335 + Height = 355 Top = 0 Width = 1165 Align = alClient - AlternateColor = clWhite ColCount = 0 FixedCols = 0 FixedRows = 0 - Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing, goSmoothScroll] + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goEditing, goSmoothScroll] RowCount = 0 TabOrder = 0 TitleFont.Height = -11 @@ -101,30 +100,30 @@ object FormTrackerModify: TFormTrackerModify end object TabSheetPublicPrivateTorrent: TTabSheet Caption = 'Public/Private' - ClientHeight = 561 + ClientHeight = 581 ClientWidth = 1171 object PanelTopPublicTorrent: TPanel Left = 0 - Height = 561 + Height = 581 Top = 0 Width = 1171 Align = alClient - ClientHeight = 561 + ClientHeight = 581 ClientWidth = 1171 TabOrder = 0 object GroupBoxPublicPrivateTorrent: TGroupBox Left = 1 - Height = 559 + Height = 579 Top = 1 Width = 1169 Align = alClient Caption = 'Checked items are public torrent. WARNING: change public/private setting will change torrent Hash info.' - ClientHeight = 541 + ClientHeight = 561 ClientWidth = 1165 TabOrder = 0 object CheckListBoxPublicPrivateTorrent: TCheckListBox Left = 0 - Height = 541 + Height = 561 Top = 0 Width = 1165 Align = alClient @@ -136,15 +135,15 @@ object FormTrackerModify: TFormTrackerModify end object TabSheetTorrentData: TTabSheet Caption = 'Data/Info' - ClientHeight = 561 + ClientHeight = 581 ClientWidth = 1171 object StringGridTorrentData: TStringGrid Left = 0 - Height = 561 + Height = 581 Top = 0 Width = 1171 Align = alClient - ColCount = 10 + ColCount = 13 ColumnClickSorts = True Columns = < item @@ -157,11 +156,21 @@ object FormTrackerModify: TFormTrackerModify Title.Caption = 'Info Filename' Width = 250 end + item + ReadOnly = True + Title.Caption = 'Torrent Version' + Width = 100 + end + item + ReadOnly = True + Title.Caption = 'Padding (beb 47)' + Width = 100 + end item MaxSize = 250 ReadOnly = True Title.Caption = 'Info Hash' - Width = 280 + Width = 439 end item ReadOnly = True @@ -182,6 +191,11 @@ object FormTrackerModify: TFormTrackerModify Title.Caption = 'Private' Width = 50 end + item + ReadOnly = True + Title.Caption = 'Source' + Width = 64 + end item Alignment = taRightJustify ReadOnly = True @@ -198,8 +212,10 @@ object FormTrackerModify: TFormTrackerModify end item ReadOnly = True + SizePriority = 0 Title.Alignment = taRightJustify Title.Caption = 'IndexOrder (internal used)' + Width = 0 Visible = False end> FixedCols = 0 @@ -210,11 +226,14 @@ object FormTrackerModify: TFormTrackerModify ColWidths = ( 250 250 - 280 + 100 + 100 + 439 145 145 160 50 + 64 100 100 0 @@ -223,48 +242,85 @@ object FormTrackerModify: TFormTrackerModify end object TabSheetTorrentsContents: TTabSheet Caption = 'Files/Trackers/Info' - ClientHeight = 561 + end + object TabSheetPrivateTrackers: TTabSheet + Caption = 'Private Trackers' + ClientHeight = 581 ClientWidth = 1171 - object GroupBoxTorrentContents: TGroupBox + object GroupBoxItemsForPrivateTrackers: TGroupBox Left = 0 - Height = 561 - Top = 0 - Width = 1171 - Align = alClient - Caption = 'Show all the files inside the torrents' - ClientHeight = 543 - ClientWidth = 1167 + Height = 176 + Hint = 'Private trackers URL may not end with /announce' + Top = 8 + Width = 288 + Caption = 'Items for private trackers' + ClientHeight = 158 + ClientWidth = 284 TabOrder = 0 - object TreeViewFileContents: TTreeView + object CheckBoxSkipAnnounceCheck: TCheckBox Left = 0 - Height = 543 + Height = 17 Top = 0 - Width = 1167 - Align = alClient - AutoExpand = True - DefaultItemHeight = 16 - PopupMenu = PopupMenuTorrentFilesContent - ReadOnly = True - ShowRoot = False + Width = 284 + Align = alTop + Caption = 'Skip Announce Check in the URL (-SAC)' + ParentShowHint = False + ShowHint = True TabOrder = 0 - Options = [tvoAutoExpand, tvoAutoItemHeight, tvoHideSelection, tvoKeepCollapsedNodes, tvoReadOnly, tvoShowButtons, tvoShowLines, tvoToolTips, tvoThemedDraw] + OnChange = CheckBoxSkipAnnounceCheckChange + end + object GroupBoxInfoSource: TGroupBox + Left = 0 + Height = 120 + Top = 38 + Width = 284 + Align = alBottom + Caption = 'Warning: This will change the torrent info HASH' + ClientHeight = 102 + ClientWidth = 280 + TabOrder = 1 + object CheckBoxRemoveAllSourceTag: TCheckBox + Left = 8 + Height = 17 + Top = 8 + Width = 130 + Caption = 'Remove all source tag' + TabOrder = 0 + OnChange = CheckBoxRemoveAllSourceTagChange + end + object LabeledEditInfoSource: TLabeledEdit + Left = 8 + Height = 21 + Hint = 'Keep empty if you do not want to add or change anything.' + Top = 56 + Width = 240 + EditLabel.Height = 13 + EditLabel.Width = 240 + EditLabel.Caption = 'Add/Change source tag value: (-SOURCE)' + EditLabel.ParentColor = False + ParentShowHint = False + ShowHint = True + TabOrder = 1 + TabStop = False + OnEditingDone = LabeledEditInfoSourceEditingDone + end end end end end object SelectDirectoryDialog1: TSelectDirectoryDialog Title = 'Select a directory with torrent files inside.' - left = 560 - top = 24 + Left = 560 + Top = 24 end object OpenDialog: TOpenDialog Filter = 'torrent|*.torrent' - left = 560 - top = 88 + Left = 560 + Top = 88 end object MainMenu: TMainMenu - left = 472 - top = 152 + Left = 472 + Top = 152 object MenuFile: TMenuItem Caption = '&File' object MenuOpenTorrentFile: TMenuItem @@ -297,17 +353,17 @@ object FormTrackerModify: TFormTrackerModify Caption = '-' end object MenuTrackersDeleteUnstableTrackers: TMenuItem - Caption = 'Delete UNSTABLE trackers' + Caption = 'Delete UNSTABLE trackers (Data from newTrackon)' OnClick = MenuTrackersDeleteTrackersWithStatusClick end object MenuTrackersDeleteDeadTrackers: TMenuItem Tag = 1 - Caption = 'Delete DEAD trackers' + Caption = 'Delete DEAD trackers (Data from newTrackon)' OnClick = MenuTrackersDeleteTrackersWithStatusClick end object MenuTrackersDeleteUnknownTrackers: TMenuItem Tag = 2 - Caption = 'Delete UNKNOWN trackers' + Caption = 'Delete UNKNOWN trackers (Data from newTrackon)' OnClick = MenuTrackersDeleteTrackersWithStatusClick end object MenuTrackersSeperator2: TMenuItem @@ -329,39 +385,39 @@ object FormTrackerModify: TFormTrackerModify object MenuUpdateTorrentAddAfter: TMenuItem Caption = 'Insert new trackers list &BEFORE, the original trackers list inside the torrent file' object MenuUpdateTorrentAddBeforeRemoveOriginal: TMenuItem - Caption = 'And remove possible duplicated trackers from the ORIGINAL trackers list.' + Caption = 'And remove possible duplicated trackers from the ORIGINAL trackers list.(-U0)' OnClick = MenuUpdateTorrentAddBeforeRemoveOriginalClick end object MenuUpdateTorrentAddBeforeRemoveNew: TMenuItem - Caption = 'And remove possible duplicated trackers from the NEW trackers list.' + Caption = 'And remove possible duplicated trackers from the NEW trackers list.(-U1)' OnClick = MenuUpdateTorrentAddBeforeRemoveNewClick end object MenuUpdateTorrentAddBeforeKeepOriginalInstactAndRemoveNothing: TMenuItem - Caption = 'Keep original tracker list unchanged and remove nothing.' + Caption = 'Keep original tracker list unchanged and remove nothing.(-U5)' OnClick = MenuUpdateTorrentAddBeforeKeepOriginalInstactAndRemoveNothingClick end end object MenuUpdateTorrentAddBefore: TMenuItem Caption = 'Append new trackers list &AFTER, the original trackers list inside the torrent file.' object MenuUpdateTorrentAddAfterRemoveOriginal: TMenuItem - Caption = 'And remove possible duplicated trackers from the ORIGINAL trackers list.' + Caption = 'And remove possible duplicated trackers from the ORIGINAL trackers list.(-U2)' OnClick = MenuUpdateTorrentAddAfterRemoveOriginalClick end object MenuUpdateTorrentAddAfterRemoveNew: TMenuItem - Caption = 'And remove possible duplicated trackers from the NEW trackers list.' + Caption = 'And remove possible duplicated trackers from the NEW trackers list.(-U3)' OnClick = MenuUpdateTorrentAddAfterRemoveNewClick end object MenuUpdateTorrentAddAfterKeepOriginalInstactAndRemoveNothing: TMenuItem - Caption = 'Keep original tracker list unchanged and remove nothing.' + Caption = 'Keep original tracker list unchanged and remove nothing.(-U6)' OnClick = MenuUpdateTorrentAddAfterKeepOriginalInstactAndRemoveNothingClick end end object MenuUpdateTorrentSort: TMenuItem - Caption = '&Sort the trackers list by name.' + Caption = '&Sort the trackers list by name.(-U4)' OnClick = MenuUpdateTorrentSortClick end object MenuUpdateRandomize: TMenuItem - Caption = '&Randomize the trackers list.' + Caption = '&Randomize the trackers list.(-U7)' OnClick = MenuUpdateRandomizeClick end end @@ -375,50 +431,67 @@ object FormTrackerModify: TFormTrackerModify Caption = 'Append Stable Trackers (From newTrackon)' OnClick = MenuItemOnlineCheckAppendStableTrackersClick end + object MenuItemOnlineCheckSubmitNewTrackon: TMenuItem + Caption = 'Upload trackers to newTrackon' + OnClick = MenuItemOnlineCheckSubmitNewTrackonClick + end + end + object MenuItem1: TMenuItem + Caption = 'ngosang/trackerslist' + object MenuItemNgosangAppendBest: TMenuItem + Caption = 'Append best' + OnClick = MenuItemNgosangAppendBestClick + end + object MenuItemNgosangAppendAll: TMenuItem + Caption = 'Append all' + OnClick = MenuItemNgosangAppendAllClick + end + object MenuItemNgosangAppendAllUdp: TMenuItem + Caption = 'Append all udp' + OnClick = MenuItemNgosangAppendAllUdpClick + end + object MenuItemNgosangAppendAllHttp: TMenuItem + Caption = 'Append all http' + OnClick = MenuItemNgosangAppendAllHttpClick + end + object MenuItemNgosangAppendAllHttps: TMenuItem + Caption = 'Append all https' + OnClick = MenuItemNgosangAppendAllHttpsClick + end + object MenuItemNgosangAppendAllWs: TMenuItem + Caption = 'Append all ws' + OnClick = MenuItemNgosangAppendAllWsClick + end + object MenuItemNgosangAppendAllBestIp: TMenuItem + Caption = 'Append best ip' + OnClick = MenuItemNgosangAppendAllBestIpClick + end + object MenuItemNgosangAppendAllIp: TMenuItem + Caption = 'Append all ip' + OnClick = MenuItemNgosangAppendAllIpClick + end end object MenuHelp: TMenuItem Caption = '&Help' object MenuHelpVisitWebsite: TMenuItem - Caption = '&Visit website' + Caption = '&Visit website tracker editor' OnClick = MenuHelpVisitWebsiteClick end object MenuHelpReportingIssue: TMenuItem Caption = '&Reporting Issue' OnClick = MenuHelpReportingIssueClick end - end - end - object PopupMenuTorrentFilesContent: TPopupMenu - left = 632 - top = 152 - object MenuItemTorrentFilesTreeShowAll: TMenuItem - Tag = -1 - Caption = 'Show Everything' - OnClick = MenuItemTorrentFilesTreeShowAllClick - end - object MenuItemTorrentFilesTreeHideAll: TMenuItem - Caption = 'Hide All' - OnClick = MenuItemTorrentFilesTreeHideAllClick - end - object MenuItemTorrentFilesTreeShowFiles: TMenuItem - AutoCheck = True - Caption = 'Show All Files' - Checked = True - OnClick = MenuItemTorrentFilesTreeShowOrHideItemClick - end - object MenuItemTorrentFilesTreeShowTrackers: TMenuItem - Tag = 1 - AutoCheck = True - Caption = 'Show All Trackers' - Checked = True - OnClick = MenuItemTorrentFilesTreeShowOrHideItemClick - end - object MenuItemTorrentFilesTreeShowInfo: TMenuItem - Tag = 2 - AutoCheck = True - Caption = 'Show All Info' - Checked = True - OnClick = MenuItemTorrentFilesTreeShowOrHideItemClick + object MenuHelpSeperator1: TMenuItem + Caption = '-' + end + object MenuHelpVisitNewTrackon: TMenuItem + Caption = 'Visit website new&Trackon' + OnClick = MenuHelpVisitNewTrackonClick + end + object MenuHelpVisitNgosang: TMenuItem + Caption = 'Visit website &ngosang (github)' + OnClick = MenuHelpVisitNgosangClick + end end end end diff --git a/source/code/main.pas b/source/code/main.pas index 1dfce6c..60f47be 100644 --- a/source/code/main.pas +++ b/source/code/main.pas @@ -1,7 +1,4 @@ -{ MIT licence - Copyright (c) Gerry Ferdinandus -} - +// SPDX-License-Identifier: MIT unit main; { @@ -20,7 +17,8 @@ interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, CheckLst, DecodeTorrent, LCLType, ActnList, Menus, ComCtrls, - Grids, controlergridtorrentdata, torrent_miscellaneous, controler_trackerlist_online; + Grids, controlergridtorrentdata, torrent_miscellaneous, + controler_trackerlist_online, controler_treeview_torrent_data, ngosang_trackerslist; type @@ -28,17 +26,34 @@ interface { TFormTrackerModify } TFormTrackerModify = class(TForm) + CheckBoxRemoveAllSourceTag: TCheckBox; + CheckBoxSkipAnnounceCheck: TCheckBox; CheckListBoxPublicPrivateTorrent: TCheckListBox; - GroupBoxTorrentContents: TGroupBox; + GroupBoxInfoSource: TGroupBox; + GroupBoxItemsForPrivateTrackers: TGroupBox; GroupBoxPublicPrivateTorrent: TGroupBox; GroupBoxNewTracker: TGroupBox; GroupBoxPresentTracker: TGroupBox; + LabeledEditInfoSource: TLabeledEdit; MainMenu: TMainMenu; MemoNewTrackers: TMemo; MenuFile: TMenuItem; MenuFileTorrentFolder: TMenuItem; MenuFileOpenTrackerList: TMenuItem; MenuHelpReportingIssue: TMenuItem; + MenuHelpSeperator1: TMenuItem; + MenuHelpVisitNewTrackon: TMenuItem; + MenuItem1: TMenuItem; + MenuHelpVisitNgosang: TMenuItem; + MenuItemNgosangAppendAllIp: TMenuItem; + MenuItemNgosangAppendAllBestIp: TMenuItem; + MenuItemNgosangAppendAllWs: TMenuItem; + MenuItemNgosangAppendAllHttps: TMenuItem; + MenuItemNgosangAppendAllHttp: TMenuItem; + MenuItemNgosangAppendAllUdp: TMenuItem; + MenuItemNgosangAppendBest: TMenuItem; + MenuItemNgosangAppendAll: TMenuItem; + MenuItemOnlineCheckSubmitNewTrackon: TMenuItem; MenuItemOnlineCheckAppendStableTrackers: TMenuItem; MenuTrackersDeleteDeadTrackers: TMenuItem; MenuTrackersDeleteUnstableTrackers: TMenuItem; @@ -57,11 +72,6 @@ TFormTrackerModify = class(TForm) MenuUpdateTorrentSort: TMenuItem; MenuUpdateTorrentAddAfter: TMenuItem; MenuUpdateTorrentAddBefore: TMenuItem; - MenuItemTorrentFilesTreeHideAll: TMenuItem; - MenuItemTorrentFilesTreeShowTrackers: TMenuItem; - MenuItemTorrentFilesTreeShowInfo: TMenuItem; - MenuItemTorrentFilesTreeShowAll: TMenuItem; - MenuItemTorrentFilesTreeShowFiles: TMenuItem; MenuTrackersAllTorrentArePrivate: TMenuItem; MenuTrackersAllTorrentArePublic: TMenuItem; MenuUpdateTorrent: TMenuItem; @@ -75,31 +85,40 @@ TFormTrackerModify = class(TForm) PageControl: TPageControl; PanelTopPublicTorrent: TPanel; PanelTop: TPanel; - PopupMenuTorrentFilesContent: TPopupMenu; SelectDirectoryDialog1: TSelectDirectoryDialog; Splitter1: TSplitter; StringGridTrackerOnline: TStringGrid; StringGridTorrentData: TStringGrid; + TabSheetPrivateTrackers: TTabSheet; TabSheetTorrentsContents: TTabSheet; TabSheetTorrentData: TTabSheet; TabSheetTrackersList: TTabSheet; TabSheetPublicPrivateTorrent: TTabSheet; - TreeViewFileContents: TTreeView; + procedure CheckBoxRemoveAllSourceTagChange(Sender: TObject); + procedure CheckBoxSkipAnnounceCheckChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); //Drag and drop '*.torrent' files/directory or 'tracker.txt' - procedure FormDropFiles(Sender: TObject; const FileNames: array of UTF8String); + procedure FormDropFiles(Sender: TObject; const FileNames: array of utf8string); //At start of the program the form will be show/hide procedure FormShow(Sender: TObject); + procedure LabeledEditInfoSourceEditingDone(Sender: TObject); procedure MenuHelpReportingIssueClick(Sender: TObject); + procedure MenuHelpVisitNewTrackonClick(Sender: TObject); procedure MenuHelpVisitWebsiteClick(Sender: TObject); - - //Popup menu in treeview show all/hide all/ individual items selection. - procedure MenuItemTorrentFilesTreeShowAllClick(Sender: TObject); - procedure MenuItemTorrentFilesTreeHideAllClick(Sender: TObject); - procedure MenuItemTorrentFilesTreeShowOrHideItemClick(Sender: TObject); + procedure MenuHelpVisitNgosangClick(Sender: TObject); + procedure MenuItemNgosangAppendAllBestIpClick(Sender: TObject); + procedure MenuItemNgosangAppendAllClick(Sender: TObject); + procedure MenuItemNgosangAppendAllHttpClick(Sender: TObject); + procedure MenuItemNgosangAppendAllHttpsClick(Sender: TObject); + procedure MenuItemNgosangAppendAllIpClick(Sender: TObject); + procedure MenuItemNgosangAppendAllUdpClick(Sender: TObject); + procedure MenuItemNgosangAppendAllWsClick(Sender: TObject); + + procedure MenuItemNgosangAppendBestClick(Sender: TObject); + procedure MenuItemOnlineCheckSubmitNewTrackonClick(Sender: TObject); //Select via menu torrent file or directory procedure MenuOpenTorrentFileClick(Sender: TObject); @@ -126,59 +145,55 @@ TFormTrackerModify = class(TForm) //Menu online check procedure MenuItemOnlineCheckAppendStableTrackersClick(Sender: TObject); procedure MenuItemOnlineCheckDownloadNewTrackonClick(Sender: TObject); - private { private declarations } FTrackerList: TTrackerList; FControlerTrackerListOnline: TControlerTrackerListOnline; + Fcontroler_treeview_torrent_data: Tcontroler_treeview_torrent_data; FDownloadStatus: boolean; - + FngosangTrackerList: TngosangTrackerList; // is the present torrent file being process FDecodePresentTorrent: TDecodeTorrent; - FConcoleMode, //user have start the program in console mode + FDragAndDropStartUp, // user have start the program via Drag And Drop + FConsoleMode, //user have start the program in console mode FFilePresentBanByUserList//There is a file 'remove_trackers.txt' detected : boolean; - + FFolderForTrackerListLoadAndSave: string; FLogFile, FTrackerFile: TextFile; - FTotalFileInsideTorrent: integer; - FTotalFileSizeInsideTorrent: int64; FProcessTimeStart, FProcessTimeTotal: TDateTime; - FTreeNodeRoot: TTreeNode; FControlerGridTorrentData: TControlerGridTorrentData; - + function CheckForAnnounce(const TrackerURL: utf8string): boolean; + procedure AppendTrackersToMemoNewTrackers(TrackerList: TStringList); + procedure ShowUserErrorMessage(ErrorText: string; const FormText: string = ''); + function TrackerWithURLAndAnnounce(const TrackerURL: utf8string): boolean; procedure UpdateTorrent; procedure ShowHourGlassCursor(HourGlass: boolean); - procedure ViewUpdateBegin(ClearView: boolean = True); + procedure ViewUpdateBegin; procedure ViewUpdateOneTorrentFileDecoded; procedure ViewUpdateEnd; procedure ViewUpdateFormCaption; procedure ClearAllTorrentFilesNameAndTrackerInside; - - procedure MenuItemTorrentFilesTreeSyncWithPopupMenu; procedure SaveTrackerFinalListToFile; - procedure ConsoleMode; - - + procedure ConsoleModeOrDragAndDropStartupMode; procedure UpdateViewRemoveTracker; - - - procedure ReloadAllTorrentAndRefreshView; + function ReloadAllTorrentAndRefreshView: boolean; function AddTorrentFileList(TorrentFileNameStringList: TStringList): boolean; - function ReadAddTrackerFileFromUser(const FileName: UTF8String): boolean; - function LoadTorrentViaDir(const Dir: UTF8String): boolean; - function DecodeTorrentFile(const FileName: UTF8String): boolean; + function ReadAddTrackerFileFromUser(const FileName: utf8string): boolean; + function LoadTorrentViaDir(const Dir: utf8string): boolean; + function DecodeTorrentFile(const FileName: utf8string): boolean; procedure UpdateTrackerInsideFileList; procedure UpdateTorrentTrackerList; - //procedure CombineFiveTrackerListToOne(TrackerListOrder: TTrackerListOrder); procedure ShowTrackerInsideFileList; + function TestConnectionSSL: boolean; procedure CheckedOnOffAllTrackers(Value: boolean); - function CopyUserInputNewTrackersToList: boolean; - procedure LoadTrackersTextFileAddTrackers; + function CopyUserInputNewTrackersToList(Temporary_SkipAnnounceCheck: boolean = + False): boolean; + procedure LoadTrackersTextFileAddTrackers(Temporary_SkipAnnounceCheck: boolean); procedure LoadTrackersTextFileRemoveTrackers; public { public declarations } @@ -189,48 +204,76 @@ TFormTrackerModify = class(TForm) implementation -uses LCLIntf, lazutf8, LazFileUtils, trackerlist_online; +uses fphttpclient, LCLIntf, lazutf8, LazFileUtils, trackerlist_online, LCLVersion; const - RECOMENDED_TRACKERS: array[0..2] of UTF8String = + RECOMENDED_TRACKERS: array[0..2] of utf8string = ( - 'udp://tracker.openbittorrent.com:80/announce', - 'udp://tracker.publicbt.com:80/announce', - 'udp://tracker.istole.it:80/announce' - // 'udp://open.demonii.com:1337/announce' + 'udp://tracker.coppersurfer.tk:6969/announce', + 'udp://tracker.opentrackr.org:1337/announce', + 'wss://tracker.openwebtorrent.com' ); + //program name and version (http://semver.org/) - FORM_CAPTION = 'Bittorrent tracker editor (1.32.0)'; - TORRENT_FILES_CONTENTS_FORM_CAPTION = - 'Show all the files inside the torrents. (Use right mouse for popup menu.)'; + PROGRAM_VERSION = '1.33.1'; + + FORM_CAPTION = 'Bittorrent tracker editor (' + PROGRAM_VERSION + + '/LCL ' + lcl_version + '/FPC ' + {$I %FPCVERSION%} + ')'; GROUPBOX_PRESENT_TRACKERS_CAPTION = 'Present trackers in all torrent files. Select the one that you want to keep. And added to all torrent files.'; - - //'add trackers' text file must be place in the same directory as the program. - ADD_TRACKERS_FILE_NAME = 'add_trackers.txt'; - - //'remove trackers' text file must be place in the same directory as the program. - REMOVE_TRACKERS_FILE_NAME = 'remove_trackers.txt'; - - //'export trackers' text file wil be created in the same directory as the program. - EXPORT_TRACKERS_FILE_NAME = 'export_trackers.txt'; - - //'log' text file will be saved in the same directory as the program - // only in the console mode. - LOG_FILE_NAME = 'console_log.txt'; - {$R *.lfm} { TFormTrackerModify } procedure TFormTrackerModify.FormCreate(Sender: TObject); begin + //Test the working for SSL connection + if TestConnectionSSL then + begin + // shutdown the GUI program + Application.terminate; + Exit; + end; + + {$IFDEF LINUX} + // If it is a Ubuntu snap program, save it a special folder + FFolderForTrackerListLoadAndSave := GetEnvironmentVariable('SNAP_USER_COMMON'); + // If it is a flatpak program, save it in a special folder + if GetEnvironmentVariable('container') = 'flatpak' then + begin + FFolderForTrackerListLoadAndSave := GetEnvironmentVariable('XDG_DATA_HOME'); + end; + // If it is a appimage program, save it in a present folder. + if GetEnvironmentVariable('APPIMAGE') <> '' then + begin // OWD = Path to working directory at the time the AppImage is called + FFolderForTrackerListLoadAndSave := GetEnvironmentVariable('OWD'); + end; + if FFolderForTrackerListLoadAndSave = '' then + begin + // No container detected. Save in the same place as the application file + FFolderForTrackerListLoadAndSave := ExtractFilePath(Application.ExeName); + end + else + begin + // Program run in a container. Must load file from a dedicated folder. + FFolderForTrackerListLoadAndSave := AppendPathDelim(FFolderForTrackerListLoadAndSave); + end; + {$ELSE} + // Save at the same place as the application file + FFolderForTrackerListLoadAndSave := ExtractFilePath(Application.ExeName); + {$ENDIF} //Create controler for StringGridTorrentData FControlerGridTorrentData := TControlerGridTorrentData.Create(StringGridTorrentData); + // Default there is an announce check + FTrackerList.SkipAnnounceCheck := False; + + // Default do not add source tag to the info. + FTrackerList.SourceTag := ''; + //Log file output string List. FTrackerList.LogStringList := TStringList.Create; @@ -245,7 +288,7 @@ procedure TFormTrackerModify.FormCreate(Sender: TObject); FTrackerList.TrackerBanByUserList.Duplicates := dupIgnore; FTrackerList.TrackerBanByUserList.Sorted := False; - //Create deselect tracker list where the user select via user interface checkbox + //Create deselect tracker list where the user select via user interface CheckBoxRemoveAllSourceTag FTrackerList.TrackerManualyDeselectedByUserList := TStringList.Create; FTrackerList.TrackerManualyDeselectedByUserList.Duplicates := dupIgnore; FTrackerList.TrackerManualyDeselectedByUserList.Sorted := False; @@ -270,41 +313,67 @@ procedure TFormTrackerModify.FormCreate(Sender: TObject); //must NOT be sorted. Must keep the original order intact. FTrackerList.TrackerFinalList.Sorted := False; - //Decoding class for torrent. FDecodePresentTorrent := TDecodeTorrent.Create; - //Create view for trackerURL with checkbox + //Create view for trackerURL with CheckBoxRemoveAllSourceTag FControlerTrackerListOnline := TControlerTrackerListOnline.Create(StringGridTrackerOnline, - FTrackerList.TrackerFromInsideTorrentFilesList); + FTrackerList.TrackerFromInsideTorrentFilesList, @TrackerWithURLAndAnnounce); + //Create view for treeview data of all the torrent files + Fcontroler_treeview_torrent_data := + Tcontroler_treeview_torrent_data.Create(TabSheetTorrentsContents); //start the program at mimimum visual size. (this is optional) Width := Constraints.MinWidth; Height := Constraints.MinHeight; + //there must be two command line or more for a console mode. + //one is for drag and drop via shortcut in windows mode. + FConsoleMode := ParamCount >= 2; + FDragAndDropStartUp := ParamCount = 1; + //Show the default trackers - LoadTrackersTextFileAddTrackers; + LoadTrackersTextFileAddTrackers(True); //Load the unwanted trackers list. LoadTrackersTextFileRemoveTrackers; + //Create download for ngosang tracker list + FngosangTrackerList := TngosangTrackerList.Create; + //Start program in console mode ( >= 2) + //or in windows mode via shortcut with drag/drop ( = 1) + if ParamCount > 0 then + begin + ConsoleModeOrDragAndDropStartupMode; + end; - //Check is program is started as console - ConsoleMode; + //There should be no more exception made for the drag and drop + FDragAndDropStartUp := False; //Update some captions - Caption := FORM_CAPTION; - GroupBoxTorrentContents.Caption := TORRENT_FILES_CONTENTS_FORM_CAPTION; + ViewUpdateFormCaption; GroupBoxPresentTracker.Caption := GROUPBOX_PRESENT_TRACKERS_CAPTION; +end; +procedure TFormTrackerModify.CheckBoxSkipAnnounceCheckChange(Sender: TObject); +begin + FTrackerList.SkipAnnounceCheck := CheckBoxSkipAnnounceCheck.Checked; + ViewUpdateFormCaption; +end; + +procedure TFormTrackerModify.CheckBoxRemoveAllSourceTagChange(Sender: TObject); +begin + FTrackerList.RemoveAllSourceTag := TCheckBox(Sender).Checked; + LabeledEditInfoSource.Enabled := not FTrackerList.RemoveAllSourceTag; end; procedure TFormTrackerModify.FormDestroy(Sender: TObject); begin //The program is being closed. Free all the memory. + FngosangTrackerList.Free; FTrackerList.LogStringList.Free; FTrackerList.TrackerFinalList.Free; FDecodePresentTorrent.Free; @@ -338,23 +407,88 @@ procedure TFormTrackerModify.MenuHelpVisitWebsiteClick(Sender: TObject); OpenURL('https://github.com/GerryFerdinandus/bittorrent-tracker-editor'); end; -procedure TFormTrackerModify.MenuItemOnlineCheckAppendStableTrackersClick( - Sender: TObject); +procedure TFormTrackerModify.MenuHelpVisitNgosangClick(Sender: TObject); +begin + //newTrackon trackers is being used in this program. + OpenURL('https://github.com/ngosang/trackerslist'); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllBestIpClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_Best_IP); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_All); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllHttpClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_All_HTTP); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllHttpsClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_All_HTTPS); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllIpClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_All_IP); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllUdpClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_All_UDP); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendAllWsClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_All_WS); +end; + +procedure TFormTrackerModify.MenuItemNgosangAppendBestClick(Sender: TObject); +begin + AppendTrackersToMemoNewTrackers(FngosangTrackerList.TrackerList_Best); +end; + +procedure TFormTrackerModify.MenuItemOnlineCheckSubmitNewTrackonClick(Sender: TObject); var - tracker: UTF8String; + SendStatus: boolean; + TrackerSendCount: integer; + PopupStr: string; begin - //User want to use the downloaded tracker list. + try + screen.Cursor := crHourGlass; + SendStatus := FControlerTrackerListOnline.SubmitTrackers( + FTrackerList.TrackerFromInsideTorrentFilesList, TrackerSendCount); + finally + screen.Cursor := crDefault; + end; - //check if tracker is already downloaded - if not FDownloadStatus then + if SendStatus then begin - //Download it now. - MenuItemOnlineCheckDownloadNewTrackonClick(nil); + //Succesful upload + PopupStr := format('Successful upload of %d unique tracker URL', [TrackerSendCount]); + Application.MessageBox( + PChar(@PopupStr[1]), + '', MB_ICONINFORMATION + MB_OK); + end + else + begin + //something is wrong with uploading + ShowUserErrorMessage('Can not uploading the tracker list'); end; +end; +procedure TFormTrackerModify.AppendTrackersToMemoNewTrackers(TrackerList: TStringList); +var + tracker: utf8string; +begin //Append all the trackers to MemoNewTrackers MemoNewTrackers.Lines.BeginUpdate; - for Tracker in FControlerTrackerListOnline.StableTrackers do + for Tracker in TrackerList do begin MemoNewTrackers.Lines.Add(tracker); end; @@ -367,12 +501,28 @@ procedure TFormTrackerModify.MenuItemOnlineCheckAppendStableTrackersClick( end; end; +procedure TFormTrackerModify.MenuItemOnlineCheckAppendStableTrackersClick( + Sender: TObject); +begin + //User want to use the downloaded tracker list. + + //check if tracker is already downloaded + if not FDownloadStatus then + begin + //Download it now. + MenuItemOnlineCheckDownloadNewTrackonClick(nil); + end; + + //Append all the trackers to MemoNewTrackers + AppendTrackersToMemoNewTrackers(FControlerTrackerListOnline.StableTrackers); +end; + procedure TFormTrackerModify.MenuItemOnlineCheckDownloadNewTrackonClick( Sender: TObject); begin try screen.Cursor := crHourGlass; - FDownloadStatus := FControlerTrackerListOnline.DownloadTrackers; + FDownloadStatus := FControlerTrackerListOnline.DownloadTrackers_All_Live_Stable; finally screen.Cursor := crDefault; end; @@ -380,9 +530,45 @@ procedure TFormTrackerModify.MenuItemOnlineCheckDownloadNewTrackonClick( if not FDownloadStatus then begin //something is wrong with downloading - Application.MessageBox( - PChar('Can not downloading the trackers from internet'), - '', MB_ICONINFORMATION + MB_OK); + ShowUserErrorMessage('Can not downloading the trackers from internet'); + end; +end; + +function TFormTrackerModify.CheckForAnnounce(const TrackerURL: utf8string): boolean; +begin + Result := (not FTrackerList.SkipAnnounceCheck) and + (not WebTorrentTrackerURL(TrackerURL)) and (not FDragAndDropStartUp); +end; + +procedure TFormTrackerModify.ShowUserErrorMessage(ErrorText: string; + const FormText: string); +begin + if FConsoleMode then + begin + if FormText = '' then + FTrackerList.LogStringList.Add(ErrorText) + else + FTrackerList.LogStringList.Add(FormText + ' : ' + ErrorText); + end + else + begin + if FormText <> '' then + ErrorText := FormText + sLineBreak + ErrorText; + Application.MessageBox(PChar(@ErrorText[1]), '', MB_ICONERROR); + end; +end; + +function TFormTrackerModify.TrackerWithURLAndAnnounce( + const TrackerURL: utf8string): boolean; +begin + //Validate the begin of the URL + Result := ValidTrackerURL(TrackerURL); + if Result then + begin + if CheckForAnnounce(TrackerURL) then + begin + Result := TrackerURLWithAnnounce(TrackerURL); + end; end; end; @@ -431,95 +617,11 @@ procedure TFormTrackerModify.MenuUpdateRandomizeClick(Sender: TObject); UpdateTorrent; end; -procedure TFormTrackerModify.MenuItemTorrentFilesTreeHideAllClick(Sender: TObject); -var - i, CountTorrents: integer; -begin - //Show only torrent file names - - //user what to hide all the items. - //All the popup menu item must first be unchecked. - MenuItemTorrentFilesTreeShowInfo.Checked := False; - MenuItemTorrentFilesTreeShowFiles.Checked := False; - MenuItemTorrentFilesTreeShowTrackers.Checked := False; - //Update the TorrentFilesTree - // MenuItemTorrentFilesTreeSyncWithPopupMenu; - - if not assigned(FTreeNodeRoot) then - exit; - - //how many torrent files are there. - CountTorrents := FTreeNodeRoot.Count; - if CountTorrents = 0 then - exit; - - //Show the torrent files names only. - for i := 0 to CountTorrents - 1 do - begin - FTreeNodeRoot.Items[i].Collapse(True); - end; - -end; - -procedure TFormTrackerModify.MenuItemTorrentFilesTreeShowAllClick(Sender: TObject); -begin - //show everything - if assigned(FTreeNodeRoot) then - FTreeNodeRoot.Expand(True); - - //user what to see all the items. - //All the popup menu item must first be checked. - MenuItemTorrentFilesTreeShowInfo.Checked := True; - MenuItemTorrentFilesTreeShowFiles.Checked := True; - MenuItemTorrentFilesTreeShowTrackers.Checked := True; - //Update the TorrentFilesTree - // MenuItemTorrentFilesTreeSyncWithPopupMenu; -end; - -procedure TFormTrackerModify.MenuItemTorrentFilesTreeShowOrHideItemClick( - Sender: TObject); -var - i, CountTorrents, itemsNr: integer; - ShowNode: boolean; -begin - //Show or hide all the items below the torrent files. - - //Get the top node. - if not assigned(FTreeNodeRoot) then - exit; - - //how many torrent files are there. - CountTorrents := FTreeNodeRoot.Count; - if CountTorrents = 0 then - exit; - - //The tag number define if it is for files, trackers or info items - itemsNr := TMenuItem(Sender).tag; - - //Must show or hide the items - ShowNode := TMenuItem(Sender).Checked; - - //process all the torrent files one by one. - for i := 0 to CountTorrents - 1 do - begin - if ShowNode then - begin - FTreeNodeRoot.Items[i].Expand(False); //Show the torrent name + child - FTreeNodeRoot.Items[i].Items[itemsNr].Expand(False); //expand child - end - else - begin - FTreeNodeRoot.Items[i].Items[itemsNr].Collapse(False); - end; - end; -end; - procedure TFormTrackerModify.UpdateTorrent; var Reply, BoxStyle, i, CountTrackers: integer; PopUpMenuStr: string; - SomeFilesCannotBeWriten, SomeFilesAreReadOnly: boolean; - + SomeFilesCannotBeWriten, SomeFilesAreReadOnly, AllFilesAreReadBackCorrectly: boolean; begin //Update all the torrent files. @@ -533,12 +635,12 @@ procedure TFormTrackerModify.UpdateTorrent; try - if not FConcoleMode then + if not FConsoleMode then begin //Warn user before updating the torrent BoxStyle := MB_ICONWARNING + MB_OKCANCEL; - Reply := Application.MessageBox('Warning: There is no undo.', - 'Torrent files will be change!', BoxStyle); + Reply := Application.MessageBox('Torrent files will be change!' + + sLineBreak + 'Warning: There is no undo.', '', BoxStyle); if Reply <> idOk then begin ShowHourGlassCursor(True); @@ -550,15 +652,7 @@ procedure TFormTrackerModify.UpdateTorrent; //Must have some torrent selected if (FTrackerList.TorrentFileNameList.Count = 0) then begin - if FConcoleMode then - begin - FTrackerList.LogStringList.Add('ERROR: No torrent file selected'); - end - else - begin - Application.MessageBox('No torrent file selected', - '', MB_ICONERROR); - end; + ShowUserErrorMessage('ERROR: No torrent file selected'); ShowHourGlassCursor(True); exit; end; @@ -582,12 +676,12 @@ procedure TFormTrackerModify.UpdateTorrent; CountTrackers := FTrackerList.TrackerFinalList.Count; //In console mode we can ignore this warning - if not FConcoleMode and (CountTrackers = 0) then + if not FConsoleMode and (CountTrackers = 0) then begin //Torrent without a tracker is posible. But is this what the user realy want? a DHT torrent. BoxStyle := MB_ICONWARNING + MB_OKCANCEL; - Reply := Application.MessageBox( - 'Warning: Create torrent file without any URL of the tracker?', - 'There are no Trackers selected!', BoxStyle); + Reply := Application.MessageBox('There are no Trackers selected!' + + sLineBreak + 'Warning: Create torrent file without any URL of the tracker?', + '', BoxStyle); if Reply <> idOk then begin ShowHourGlassCursor(False); @@ -600,6 +694,11 @@ procedure TFormTrackerModify.UpdateTorrent; //initial value is false, will be set to true if read only files are found SomeFilesAreReadOnly := False; + if FTrackerList.TrackerListOrderForUpdatedTorrent = tloRandomize then + begin + Randomize; + end; + //process all the files one by one. //FTrackerList.TorrentFileNameList is not sorted it is still in sync with CheckListBoxPublicPrivateTorrent for i := 0 to FTrackerList.TorrentFileNameList.Count - 1 do @@ -653,18 +752,36 @@ procedure TFormTrackerModify.UpdateTorrent; //update the torrent public/private flag if CheckListBoxPublicPrivateTorrent.Checked[i] then begin + //Create a public torrent //if private torrent then make it public torrent by removing the private flag. if FDecodePresentTorrent.PrivateTorrent then FDecodePresentTorrent.RemovePrivateTorrentFlag; end else begin + //Create a private torrent FDecodePresentTorrent.AddPrivateTorrentFlag; end; //update the comment item FDecodePresentTorrent.Comment := FControlerGridTorrentData.ReadComment(i + 1); + //Update the source tag for private trackers + if FTrackerList.RemoveAllSourceTag then + begin + // This will delete info:source item + FDecodePresentTorrent.InfoSourceRemove; + end + else + begin + // Copy the new source tag, but it must not be empty. + // Empty FTrackerList.SourceTag is the same as do not change anything. + if FTrackerList.SourceTag <> '' then + begin + FDecodePresentTorrent.InfoSourceAdd(FTrackerList.SourceTag); + end; + end; + //save the torrent file. if not FDecodePresentTorrent.SaveTorrent(FTrackerList.TorrentFileNameList[i]) then begin @@ -673,11 +790,14 @@ procedure TFormTrackerModify.UpdateTorrent; end;//for + // Can not create a file inside the app + {$IFNDEF DARWIN} //Create tracker.txt file SaveTrackerFinalListToFile; + {$ENDIF} //Show/reload the just updated torrent files. - ReloadAllTorrentAndRefreshView; + AllFilesAreReadBackCorrectly := ReloadAllTorrentAndRefreshView; //make sure cursor is default again finally @@ -686,13 +806,19 @@ procedure TFormTrackerModify.UpdateTorrent; end; - if FConcoleMode then + if FConsoleMode then begin //When succesfull the log file shows, 3 lines, // OK + Count torrent files + Count Trackers - FTrackerList.LogStringList.Add('OK'); - FTrackerList.LogStringList.Add(IntToStr(FTrackerList.TorrentFileNameList.Count)); - FTrackerList.LogStringList.Add(IntToStr(CountTrackers)); + + //if there is already a items inside there there must be something wrong. + //Do not add 'OK' + if FTrackerList.LogStringList.Count = 0 then + begin + FTrackerList.LogStringList.Add(CONSOLE_SUCCESS_STATUS); + FTrackerList.LogStringList.Add(IntToStr(FTrackerList.TorrentFileNameList.Count)); + FTrackerList.LogStringList.Add(IntToStr(CountTrackers)); + end; end else begin @@ -723,6 +849,16 @@ procedure TFormTrackerModify.UpdateTorrent; end;//case + + //Check if there are some error that need to be notify to the end user. + + if not AllFilesAreReadBackCorrectly then + begin + //add warning if torrent files can not be read back again + PopUpMenuStr := PopUpMenuStr + + ' WARNING: Some torrent files can not be read back again after updating.'; + end; + if SomeFilesAreReadOnly then begin //add warning if read only files are detected. @@ -746,21 +882,13 @@ procedure TFormTrackerModify.UpdateTorrent; end; -procedure TFormTrackerModify.MenuItemTorrentFilesTreeSyncWithPopupMenu; -begin - MenuItemTorrentFilesTreeShowOrHideItemClick(MenuItemTorrentFilesTreeShowTrackers); - MenuItemTorrentFilesTreeShowOrHideItemClick(MenuItemTorrentFilesTreeShowInfo); - MenuItemTorrentFilesTreeShowOrHideItemClick(MenuItemTorrentFilesTreeShowFiles); -end; - procedure TFormTrackerModify.SaveTrackerFinalListToFile; var - TrackerStr: UTF8String; + TrackerStr: utf8string; begin //Create the tracker text file. The old one will be overwritten - AssignFile(FTrackerFile, ExtractFilePath(Application.ExeName) + - EXPORT_TRACKERS_FILE_NAME); + AssignFile(FTrackerFile, FFolderForTrackerListLoadAndSave + FILE_NAME_EXPORT_TRACKERS); ReWrite(FTrackerFile); for TrackerStr in FTrackerList.TrackerFinalList do begin @@ -775,134 +903,145 @@ procedure TFormTrackerModify.SaveTrackerFinalListToFile; CloseFile(FTrackerFile); end; -procedure TFormTrackerModify.ConsoleMode; +procedure TFormTrackerModify.ConsoleModeOrDragAndDropStartupMode; var - FileNameOrDirStr: UTF8String; + FileNameOrDirStr: utf8string; StringList: TStringList; + MustExitWithErrorCode: boolean; begin - //The first parameter[1] is path to file or dir. - //if program is started with two parameter then in must be a console mode. + // There are two options + //- + // One parameter only. Program startup via DragAndDrop + // The first parameter[1] is path to file or dir. - //update the torrent via console mode if there is a parameter detected. - if ParamCount > 0 then - begin - //there must be 2 command line items for console mode - FConcoleMode := ParamCount = 2; + //- + // Two parameter version. Always console mode. + // This is later version where there is more selection about the tracker list. - try - if FConcoleMode then - begin - //Create the log file. The old one will be overwritten - AssignFile(FLogFile, ExtractFilePath(Application.ExeName) + LOG_FILE_NAME); - ReWrite(FLogFile); - end; + //Will be set to True when error occure. + MustExitWithErrorCode := False; + ViewUpdateBegin; - //Get the startup command lime parameters. - //ConsoleModeDecodeParameter(FileNameOrDirStr, FTrackerList); + try + if FConsoleMode then + begin + //Create the log file. The old one will be overwritten + AssignFile(FLogFile, FFolderForTrackerListLoadAndSave + FILE_NAME_CONSOLE_LOG); + ReWrite(FLogFile); + end; - //If FTrackerList.LogStringList empty then there is no error. - // if FTrackerList.LogStringList.Text = '' then - if ConsoleModeDecodeParameter(FileNameOrDirStr, FTrackerList) then - begin - //There is no error. Proceed with reading the torrent files + //Get the startup command lime parameters. + if ConsoleModeDecodeParameter(FileNameOrDirStr, FTrackerList) then + begin + //There is no error. Proceed with reading the torrent files - if ExtractFileExt(FileNameOrDirStr) = '' then - begin //There is no file extention. It must be a folder. - if LoadTorrentViaDir(FileNameOrDirStr) then - begin - //Show all the tracker inside the torrent files. - ShowTrackerInsideFileList; - //Mark all trackers as selected - CheckedOnOffAllTrackers(True); - //Some tracker must be removed. Console and windows mode. - UpdateViewRemoveTracker; - - if FConcoleMode then - begin - //update torrent - UpdateTorrent; - end; + if ExtractFileExt(FileNameOrDirStr) = '' then + begin //There is no file extention. It must be a folder. + if LoadTorrentViaDir(FileNameOrDirStr) then + begin + //Show all the tracker inside the torrent files. + ShowTrackerInsideFileList; + //Some tracker must be removed. Console and windows mode. + UpdateViewRemoveTracker; + if FConsoleMode then + begin + //update torrent + UpdateTorrent; end; + end - else //a torrent file is selected? + else begin - if ExtractFileExt(FileNameOrDirStr) = '.torrent' then - begin - StringList := TStringList.Create; - try - //Convert Filenames to stringlist format. - StringList.Add(FileNameOrDirStr); + //failed to load the torrent via folders + ShowUserErrorMessage('Can not load torrent via folder'); + end; - //Extract all the trackers inside the torrent file - if AddTorrentFileList(StringList) then - begin - //Show all the tracker inside the torrent files. - ShowTrackerInsideFileList; - //Mark all trackers as selected - CheckedOnOffAllTrackers(True); - //Some tracker must be removed. Console and windows mode. - UpdateViewRemoveTracker; - - if FConcoleMode then - begin - //update torrent - UpdateTorrent; - end; + end + else //a single torrent file is selected? + begin + if ExtractFileExt(FileNameOrDirStr) = '.torrent' then + begin + StringList := TStringList.Create; + try + //Convert Filenames to stringlist format. + StringList.Add(FileNameOrDirStr); + + //Extract all the trackers inside the torrent file + if AddTorrentFileList(StringList) then + begin + //Show all the tracker inside the torrent files. + ShowTrackerInsideFileList; + //Some tracker must be removed. Console and windows mode. + UpdateViewRemoveTracker; + if FConsoleMode then + begin + //update torrent + UpdateTorrent; end; - finally - StringList.Free; + end + else + begin + //failed to load one torrent + ShowUserErrorMessage('Can not load torrent file.'); end; - end - else - begin //Error. this is not a torrent file - FTrackerList.LogStringList.Add('ERROR: No torrent file selected.'); + + finally + StringList.Free; end; + end + else + begin //Error. this is not a torrent file + ShowUserErrorMessage('ERROR: No torrent file selected.'); end; end; + end; - if FConcoleMode then + if FConsoleMode then + begin + //Write to log file. And close the file. + WriteLn(FLogFile, FTrackerList.LogStringList.Text); + CloseFile(FLogFile); + + //check if log data is success full + //if (no data) or (not CONSOLE_SUCCESS_STATUS) then error + MustExitWithErrorCode := FTrackerList.LogStringList.Count = 0; + if not MustExitWithErrorCode then begin - //Write to log file. And close the file. - WriteLn(FLogFile, FTrackerList.LogStringList.Text); - CloseFile(FLogFile); - - //Shutdown the console program - Application.terminate; + MustExitWithErrorCode := FTrackerList.LogStringList[0] <> CONSOLE_SUCCESS_STATUS; end; + end; - except - if FConcoleMode then - begin - //Shutdown the console program. - //This is needed or else the program will keep running forever. - //exit with error code - System.ExitCode := 1; - Application.terminate; - end - else - begin - //show to the end user that something is wrong - raise; - end; + except + //Shutdown the console program. + //This is needed or else the program will keep running forever. + //exit with error code + MustExitWithErrorCode := True; + end; - end; - end - else - begin //the program - FConcoleMode := False; + ViewUpdateEnd; + + if MustExitWithErrorCode then + begin + //exit with error code + System.ExitCode := 1; end; + if FConsoleMode then + begin + //Always shutdown the program when in console mode. + Application.terminate; + end; end; procedure TFormTrackerModify.UpdateViewRemoveTracker; var - TrackerStr: UTF8String; + TrackerStr: utf8string; i: integer; begin { @@ -955,20 +1094,21 @@ procedure TFormTrackerModify.UpdateViewRemoveTracker; -function TFormTrackerModify.DecodeTorrentFile(const FileName: UTF8String): boolean; +function TFormTrackerModify.DecodeTorrentFile(const FileName: utf8string): boolean; begin //Called when user add torrent files //False if something is wrong with decoding torrent. Result := FDecodePresentTorrent.DecodeTorrent(FileName); if Result then begin + //visual update this one torrent file. ViewUpdateOneTorrentFileDecoded; end; end; procedure TFormTrackerModify.UpdateTorrentTrackerList; var - TrackerStr: UTF8String; + TrackerStr: utf8string; begin //Copy the trackers found in one torrent file to FTrackerList.TrackerFromInsideTorrentFilesList for TrackerStr in FDecodePresentTorrent.TrackerList do @@ -988,7 +1128,7 @@ procedure TFormTrackerModify.CheckedOnOffAllTrackers(Value: boolean); var i: integer; begin - //Set all the trackers checkbox ON or OFF + //Set all the trackers CheckBoxRemoveAllSourceTag ON or OFF if FControlerTrackerListOnline.Count > 0 then begin for i := 0 to FControlerTrackerListOnline.Count - 1 do @@ -999,9 +1139,10 @@ procedure TFormTrackerModify.CheckedOnOffAllTrackers(Value: boolean); end; -function TFormTrackerModify.CopyUserInputNewTrackersToList: boolean; +function TFormTrackerModify.CopyUserInputNewTrackersToList( + Temporary_SkipAnnounceCheck: boolean): boolean; var - TrackerStrLoop, TrackerStr: UTF8String; + TrackerStrLoop, TrackerStr, ErrorStr: utf8string; begin { Called after 'update torrent' is selected. @@ -1009,6 +1150,9 @@ function TFormTrackerModify.CopyUserInputNewTrackersToList: boolean; } FTrackerList.TrackerAddedByUserList.Clear; + //Will set to false when error is detected + Result := True; + for TrackerStrLoop in MemoNewTrackers.Lines do begin TrackerStr := UTF8trim(TrackerStrLoop); @@ -1017,37 +1161,45 @@ function TFormTrackerModify.CopyUserInputNewTrackersToList: boolean; if TrackerStr = '' then continue; - //All the tracker must begin with 'http(s)://' or 'udp://' - if ValidTrackerURL(TrackerStr) then + Result := ValidTrackerURL(TrackerStr); + if Result then begin - AddButIngnoreDuplicates(FTrackerList.TrackerAddedByUserList, TrackerStr); + if CheckForAnnounce(TrackerStr) and (not Temporary_SkipAnnounceCheck) then + begin + Result := TrackerURLWithAnnounce(TrackerStr); + if not Result then + begin + ErrorStr := 'ERROR: Tracker URL must end with /announce or /announce.php'; + end; + end; end else begin - //There is error. Show the error and do not continue. - if FConcoleMode then - begin - FTrackerList.LogStringList.Add( - 'ERROR: Tracker URL must begin with http:// or udp://'); - end - else - begin - //Show error - Application.MessageBox(PChar(@TrackerStr[1]), - 'Error: Tracker URL must begin with http(s):// or udp://', MB_ICONERROR); - end; - //do not continue with error. - Result := False; - exit; + ErrorStr := 'ERROR: Tracker URL must begin with http://, http:// or udp://'; end; - end; - - Result := True; //no error + if Result then + begin + AddButIngnoreDuplicates(FTrackerList.TrackerAddedByUserList, TrackerStr); + end + else + begin + //do not continue the for loop + break; + end; - //Show the torrent list we have just created. - MemoNewTrackers.Text := FTrackerList.TrackerAddedByUserList.Text; + end;//for loop + if Result then + begin + //Show the torrent list we have just created. + MemoNewTrackers.Text := FTrackerList.TrackerAddedByUserList.Text; + end + else + begin + //There is error. Show the error. + ShowUserErrorMessage(ErrorStr, TrackerStr); + end; end; procedure TFormTrackerModify.UpdateTrackerInsideFileList; @@ -1087,15 +1239,16 @@ procedure TFormTrackerModify.UpdateTrackerInsideFileList; end; -procedure TFormTrackerModify.LoadTrackersTextFileAddTrackers; +procedure TFormTrackerModify.LoadTrackersTextFileAddTrackers( + Temporary_SkipAnnounceCheck: boolean); var i: integer; begin //Called at the start of the program. Load a trackers list from file //if no file is found the use the default tracker list. - if not ReadAddTrackerFileFromUser(ExtractFilePath(Application.ExeName) + - ADD_TRACKERS_FILE_NAME) then + if not ReadAddTrackerFileFromUser(FFolderForTrackerListLoadAndSave + + FILE_NAME_ADD_TRACKERS) then begin MemoNewTrackers.Lines.BeginUpdate; for i := low(RECOMENDED_TRACKERS) to high(RECOMENDED_TRACKERS) do @@ -1106,7 +1259,7 @@ procedure TFormTrackerModify.LoadTrackersTextFileAddTrackers; end; //Check for error in tracker list - if not CopyUserInputNewTrackersToList then + if not CopyUserInputNewTrackersToList(Temporary_SkipAnnounceCheck) then begin MemoNewTrackers.Lines.Clear; end; @@ -1114,9 +1267,9 @@ procedure TFormTrackerModify.LoadTrackersTextFileAddTrackers; procedure TFormTrackerModify.LoadTrackersTextFileRemoveTrackers; var - filename: UTF8String; + filename: utf8string; begin - filename := ExtractFilePath(Application.ExeName) + REMOVE_TRACKERS_FILE_NAME; + filename := FFolderForTrackerListLoadAndSave + FILE_NAME_REMOVE_TRACKERS; try FFilePresentBanByUserList := FileExistsUTF8(fileName); if FFilePresentBanByUserList then @@ -1164,12 +1317,12 @@ procedure TFormTrackerModify.MenuTrackersAllTorrentArePublicPrivateClick( i: integer; begin //Warn user about torrent Hash. - if Application.MessageBox( + if Application.MessageBox('Are you sure!' + sLineBreak + 'Warning: Changing the public/private torrent flag will change the info hash.', - 'Are you sure!', MB_ICONWARNING + MB_OKCANCEL) <> idOk then + '', MB_ICONWARNING + MB_OKCANCEL) <> idOk then exit; - //Set all the trackers publick/private checkbox ON or OFF + //Set all the trackers publick/private CheckBoxRemoveAllSourceTag ON or OFF if CheckListBoxPublicPrivateTorrent.Count > 0 then begin for i := 0 to CheckListBoxPublicPrivateTorrent.Count - 1 do @@ -1199,9 +1352,16 @@ procedure TFormTrackerModify.MenuHelpReportingIssueClick(Sender: TObject); OpenURL('https://github.com/GerryFerdinandus/bittorrent-tracker-editor/issues'); end; +procedure TFormTrackerModify.MenuHelpVisitNewTrackonClick(Sender: TObject); +begin + //newTrackon trackers is being used in this program. + //User should have direct link to the website for the status of the trackers. + OpenURL('https://newtrackon.com/'); +end; + function TFormTrackerModify.ReadAddTrackerFileFromUser( - const FileName: UTF8String): boolean; + const FileName: utf8string): boolean; var TrackerFileList: TStringList; begin @@ -1262,7 +1422,6 @@ procedure TFormTrackerModify. end; procedure TFormTrackerModify.MenuUpdateTorrentAddBeforeRemoveNewClick(Sender: TObject); - begin //User have selected to add new tracker. FTrackerList.TrackerListOrderForUpdatedTorrent := @@ -1287,7 +1446,7 @@ procedure TFormTrackerModify.MenuUpdateTorrentSortClick(Sender: TObject); -function TFormTrackerModify.LoadTorrentViaDir(const Dir: UTF8String): boolean; +function TFormTrackerModify.LoadTorrentViaDir(const Dir: utf8string): boolean; var TorrentFilesNameStringList: TStringList; begin @@ -1305,7 +1464,7 @@ function TFormTrackerModify.LoadTorrentViaDir(const Dir: UTF8String): boolean; procedure TFormTrackerModify.FormDropFiles(Sender: TObject; - const FileNames: array of UTF8String); + const FileNames: array of utf8string); var Count: integer; TorrentFileNameStringList, //for the torrent files @@ -1317,7 +1476,7 @@ procedure TFormTrackerModify.FormDropFiles(Sender: TObject; //ViewUpdateBegin must be called one time. Keep track of it. ViewUpdateBeginActiveOneTimeOnly: boolean; - FileNameOrDirStr: UTF8String; + FileNameOrDirStr: utf8string; begin //Drag and drop a folder or files? @@ -1368,6 +1527,7 @@ procedure TFormTrackerModify.FormDropFiles(Sender: TObject; MemoNewTrackers.Append(UTF8Trim(TrackerFileNameStringList.Text)); except //supress any error in loading the file + FileNameOrDirStr := FileNameOrDirStr; end; end; @@ -1434,17 +1594,22 @@ procedure TFormTrackerModify.FormDropFiles(Sender: TObject; procedure TFormTrackerModify.FormShow(Sender: TObject); begin //In console mode do not show the program. - if FConcoleMode then + if FConsoleMode then Visible := False; end; +procedure TFormTrackerModify.LabeledEditInfoSourceEditingDone(Sender: TObject); +begin + FTrackerList.SourceTag := TLabeledEdit(Sender).Text; +end; + function TFormTrackerModify.AddTorrentFileList(TorrentFileNameStringList: TStringList): boolean; //This called from 'add folder' or 'drag and drop' var Count: integer; - TorrentFileNameStr: UTF8String; + TorrentFileNameStr: utf8string; begin { Every torrent file must be decoded for the tracker list inside. This torrent tracker list is add to FTrackerList.TrackerFromInsideTorrentFilesList. @@ -1473,16 +1638,7 @@ function TFormTrackerModify.AddTorrentFileList(TorrentFileNameStringList: //Cancel everything. FTrackerList.TorrentFileNameList.Clear; FTrackerList.TrackerFromInsideTorrentFilesList.Clear; - if FConcoleMode then - begin - FTrackerList.LogStringList.Add('Error: Can not read torrent. ' + - TorrentFileNameStr); - end - else - begin - Application.MessageBox(PChar(@TorrentFileNameStr[1]), - 'Error: Can not read torrent.', MB_ICONERROR); - end; + ShowUserErrorMessage('Error: Can not read torrent.', TorrentFileNameStr); Result := False; exit; end; @@ -1492,9 +1648,9 @@ function TFormTrackerModify.AddTorrentFileList(TorrentFileNameStringList: end; -procedure TFormTrackerModify.ReloadAllTorrentAndRefreshView; +function TFormTrackerModify.ReloadAllTorrentAndRefreshView: boolean; var - TorrentFileStr: UTF8String; + TorrentFileStr: utf8string; begin { This is called after updating the torrent. @@ -1502,6 +1658,9 @@ procedure TFormTrackerModify.ReloadAllTorrentAndRefreshView; And show that everything is updated and OK } + //will be set to False if error occure + Result := True; + ViewUpdateBegin; //Copy all the trackers in inside the torrent files to FTrackerList.TrackerFromInsideTorrentFilesList FTrackerList.TrackerFromInsideTorrentFilesList.Clear; @@ -1510,6 +1669,11 @@ procedure TFormTrackerModify.ReloadAllTorrentAndRefreshView; if DecodeTorrentFile(TorrentFileStr) then begin UpdateTorrentTrackerList; + end + else + begin + //some files can not be read/decoded + Result := False; end; end; @@ -1527,54 +1691,47 @@ procedure TFormTrackerModify.ClearAllTorrentFilesNameAndTrackerInside; end; -procedure TFormTrackerModify.ViewUpdateBegin(ClearView: boolean); +procedure TFormTrackerModify.ViewUpdateBegin; begin //Called before loading torrent file. - FTotalFileInsideTorrent := 0; - FTotalFileSizeInsideTorrent := 0; + + Fcontroler_treeview_torrent_data.BeginUpdate; //Do not show being updating till finish updating data. StringGridTorrentData.BeginUpdate; - TreeViewFileContents.BeginUpdate; CheckListBoxPublicPrivateTorrent.Items.BeginUpdate; - if ClearView then - begin - //Clear all the user data 'View' elements. This will be filled with new data. - TreeViewFileContents.Items.Clear; - CheckListBoxPublicPrivateTorrent.Clear; //Use in update torrent! - StringGridTorrentData.Clear; - FControlerGridTorrentData.ClearAllImageIndex; - //RowCount is 0 after Clear. But must be 1 to make it work. - StringGridTorrentData.RowCount := 1; - end; - //root is 'Torrent Files' - FTreeNodeRoot := TreeViewFileContents.Items.Add(nil, 'Torrent Files'); + //Clear all the user data 'View' elements. This will be filled with new data. + CheckListBoxPublicPrivateTorrent.Clear; //Use in update torrent! + StringGridTorrentData.Clear; + FControlerGridTorrentData.ClearAllImageIndex; + //RowCount is 0 after Clear. But must be 1 to make it work. + StringGridTorrentData.RowCount := 1; end; procedure TFormTrackerModify.ViewUpdateOneTorrentFileDecoded; var - RowIndex, CountFiles: integer; - TorrentFileNameStr, TrackerStr, PrivateStr: UTF8String; + RowIndex: integer; + TorrentFileNameStr, PrivateStr: utf8string; DateTimeStr: string; - TreeNodeTorrent, TreeNodeFiles, TreeNodeTrackers, TreeNodeInfo: TTreeNode; begin //Called after loading torrent file. - + //There are 3 tab pages that need to be filled with new one torrent file data. TorrentFileNameStr := ExtractFileName(FDecodePresentTorrent.FilenameTorrent); + //--------------------- Fill the Tree view with new torrent data + Fcontroler_treeview_torrent_data.AddOneTorrentFileDecoded(FDecodePresentTorrent); + //--------------------- Add it to the checklist box Public/private torrent RowIndex := CheckListBoxPublicPrivateTorrent.Items.Add(TorrentFileNameStr); //Check it for public/private flag CheckListBoxPublicPrivateTorrent.Checked[RowIndex] := not FDecodePresentTorrent.PrivateTorrent; - //--------------------- Fill the Grid Torrent Data/Info - //date time in iso format if FDecodePresentTorrent.CreatedDate <> 0 then DateTimeToString(DateTimeStr, 'yyyy-MM-dd hh:nn:ss', @@ -1591,11 +1748,30 @@ procedure TFormTrackerModify.ViewUpdateOneTorrentFileDecoded; //Copy all the torrent info to the grid column. FControlerGridTorrentData.TorrentFile := TorrentFileNameStr; FControlerGridTorrentData.InfoFileName := FDecodePresentTorrent.Name; - FControlerGridTorrentData.InfoHash := FDecodePresentTorrent.InfoHash; + FControlerGridTorrentData.TorrentVersion := + FDecodePresentTorrent.TorrentVersionToString; + case FDecodePresentTorrent.TorrentVersion of + tv_V1: + begin + FControlerGridTorrentData.InfoHash := 'V1: ' + FDecodePresentTorrent.InfoHash_V1; + end; + tv_V2: + begin + FControlerGridTorrentData.InfoHash := 'V2: ' + FDecodePresentTorrent.InfoHash_V2; + end; + tv_Hybrid: + begin // Show only V2 hash. No space for both V1 and V2 + FControlerGridTorrentData.InfoHash := 'V2: ' + FDecodePresentTorrent.InfoHash_V2; + end; + else + FControlerGridTorrentData.InfoHash := 'N/A' + end; + FControlerGridTorrentData.Padding := FDecodePresentTorrent.PaddingToString; FControlerGridTorrentData.CreatedOn := DateTimeStr; FControlerGridTorrentData.CreatedBy := FDecodePresentTorrent.CreatedBy; FControlerGridTorrentData.Comment := FDecodePresentTorrent.Comment; FControlerGridTorrentData.PrivateTorrent := PrivateStr; + FControlerGridTorrentData.InfoSource := FDecodePresentTorrent.InfoSource; FControlerGridTorrentData.PieceLength := format('%6d', [FDecodePresentTorrent.PieceLenght div 1024]); //Show as KiBytes FControlerGridTorrentData.TotaSize := @@ -1607,117 +1783,29 @@ procedure TFormTrackerModify.ViewUpdateOneTorrentFileDecoded; //All the string data are filed. Copy it now to the grid FControlerGridTorrentData.AppendRow; - //--------------------- Fill the treeview with torrent files - - //Add the torrent file name + size of all the files combined. - TorrentFileNameStr := TorrentFileNameStr + ' SIZE: ' + - ByteSizeToBiggerSizeFormatStr(FDecodePresentTorrent.TotalFileSize) + - ' Files: ' + IntToStr(FDecodePresentTorrent.InfoFilesCount) + - '' + ' Tracker: ' + IntToStr(FDecodePresentTorrent.TrackerList.Count) + ''; - - - TreeNodeTorrent := TreeViewFileContents.Items.AddChild(FTreeNodeRoot, - //FTrackerList.TorrentFileNameList[RowIndex]); //With directory path - TorrentFileNameStr); //Without directory path - - TreeNodeFiles := TreeViewFileContents.Items.AddChild(TreeNodeTorrent, 'Files'); - TreeNodeTrackers := TreeViewFileContents.Items.AddChild(TreeNodeTorrent, - 'Trackers'); - TreeNodeInfo := TreeViewFileContents.Items.AddChild(TreeNodeTorrent, 'Info'); - - //Show all the files inside the torrent - if FDecodePresentTorrent.InfoFilesCount > 0 then - begin - for CountFiles := 0 to FDecodePresentTorrent.InfoFilesCount - 1 do - begin - TreeViewFileContents.Items.AddChild(TreeNodeFiles, - FDecodePresentTorrent.InfoFilesNameIndex(CountFiles) + - ' SIZE: ' + ByteSizeToBiggerSizeFormatStr( - FDecodePresentTorrent.InfoFilesLengthIndex(CountFiles))); - end; - end; - - //Show a how many files are there - TreeNodeFiles.Text := TreeNodeFiles.Text + ' (' + IntToStr(TreeNodeFiles.Count) + ')'; - - //Show all the trackers inside the torrent - for TrackerStr in FDecodePresentTorrent.TrackerList do - begin - TreeViewFileContents.Items.AddChild(TreeNodeTrackers, TrackerStr); - end; - - //Show a how many trackers are there - TreeNodeTrackers.Text := TreeNodeTrackers.Text + ' (' + - IntToStr(TreeNodeTrackers.Count) + ')'; - - - //Show all the info of torrent - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Name: ' + - FDecodePresentTorrent.Name); - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Comment: ' + - FDecodePresentTorrent.Comment); - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Info Hash: ' + - FDecodePresentTorrent.InfoHash); - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Created On: ' + DateTimeStr); - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Created By: ' + - FDecodePresentTorrent.CreatedBy); - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Piece Lenght: ' + - IntToStr(FDecodePresentTorrent.PieceLenght div 1024) + ' KiB'); - if FDecodePresentTorrent.PrivateTorrent then - begin - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Private: yes'); - end - else - begin - TreeViewFileContents.Items.AddChild(TreeNodeInfo, 'Private: no'); - end; - - //All the files count inside the torrent must be added to FTotalFileInsideTorrent - Inc(FTotalFileInsideTorrent, FDecodePresentTorrent.InfoFilesCount); - - //The file size of all files inside the torrent must be added to FTotalFileSizeInsideTorrent - Inc(FTotalFileSizeInsideTorrent, FDecodePresentTorrent.TotalFileSize); - end; procedure TFormTrackerModify.ViewUpdateEnd; begin - - //Called after loading torrent file - //Sync the popup menu with show/hide items. - MenuItemTorrentFilesTreeSyncWithPopupMenu; - + //Called after finish all torrent file loading. //Show what we have updated. - TreeViewFileContents.EndUpdate; + Fcontroler_treeview_torrent_data.EndUpdate; StringGridTorrentData.EndUpdate; CheckListBoxPublicPrivateTorrent.Items.EndUpdate; - //Show the size of all the files inside the torrent - //http://en.wikipedia.org/wiki/Gigabyte - GroupBoxTorrentContents.Caption := - TORRENT_FILES_CONTENTS_FORM_CAPTION + ' (Files count: ' + - IntToStr(FTotalFileInsideTorrent) + ') Files sizes: ' + - ByteSizeToBiggerSizeFormatStr(FTotalFileSizeInsideTorrent) + ''; - - GroupBoxPresentTracker.Caption := GROUPBOX_PRESENT_TRACKERS_CAPTION + ' (List count: ' + IntToStr(FTrackerList.TrackerFromInsideTorrentFilesList.Count) + ' )'; - - //Show all the tracker inside the torrent files. ShowTrackerInsideFileList; - //Mark all trackers as selected - CheckedOnOffAllTrackers(True); //Some tracker must be removed. Console and windows mode. UpdateViewRemoveTracker; - //Show user how many files are loaded ViewUpdateFormCaption; @@ -1734,10 +1822,16 @@ procedure TFormTrackerModify.ViewUpdateFormCaption; DecodeTime(FProcessTimeTotal, Hour, Minute, Second, MilliSecond); ProcessTimeStr := IntToStr((Second * 1000) + MilliSecond) + ' mSec'; } - //Show user how many files are loaded Caption := FORM_CAPTION + '( Torrent files: ' + IntToStr(FTrackerList.TorrentFileNameList.Count) + ' )'; + + if CheckBoxSkipAnnounceCheck.Checked then + begin + Caption := Caption + '(-SAC)'; + end; + + // + ' (Process Time: ' + ProcessTimeStr + ' )'; //for debug purpose. end; @@ -1756,4 +1850,25 @@ procedure TFormTrackerModify.ShowHourGlassCursor(HourGlass: boolean); end; +function TFormTrackerModify.TestConnectionSSL: boolean; +begin + Result := ParamCount = 1; + if Result then + begin + // Check for the correct parameter. + Result := UTF8Trim(ParamStr(1)) = '-TEST_SSL'; + if Result then + begin + // Check if there is SLL connection + try + TFPCustomHTTPClient.SimpleGet( + 'https://raw.githubusercontent.com/gerryferdinandus/bittorrent-tracker-editor/master/README.md'); + except + //No SLL or no internet connection. + System.ExitCode := 1; + end; + end; + end; +end; + end. diff --git a/source/code/newtrackon.pas b/source/code/newtrackon.pas index a100a43..affcf68 100644 --- a/source/code/newtrackon.pas +++ b/source/code/newtrackon.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit newtrackon; { @@ -29,11 +30,9 @@ interface TNewTrackon = class private FTRackerList: array [TNewTrackon_List] of TStringList; - FDownloadStatus: boolean; - procedure DownloadTracker(NewTrackon_List: TNewTrackon_List); + function DownloadTracker(NewTrackon_List: TNewTrackon_List): boolean; procedure CreateTrackerList_Dead; - public // all known trackers, dead or alive property TrackerList_All: TStringList read FTRackerList[ntl_URL_All]; @@ -53,8 +52,15 @@ TNewTrackon = class // trackers that no longer present in 'live' list property TrackerList_Dead: TStringList read FTrackerList[ntl_CREATE_DEAD]; - //Download all the trackers via API - function DownloadTrackers: boolean; + //Download all the types the trackers via API + function DownloadEverything: boolean; + + //Download only three types of the trackers via API + function Download_All_Live_Stable: boolean; + + //Submit tracker to newTrackon via http POST + function SubmitTrackers(TrackerList: TStringList; + out TrackersSendCount: integer): boolean; //create/destroy class object constructor Create; @@ -65,7 +71,7 @@ TNewTrackon = class implementation -uses fphttpclient, LazUTF8, torrent_miscellaneous; +uses fphttpclient, LazUTF8, torrent_miscellaneous, httpdefs; const URL: array [TNewTrackon_List] of string = @@ -80,6 +86,7 @@ implementation { TNewTrackon } + procedure TNewTrackon.CreateTrackerList_Dead; begin //FTrackerList_Dead = FTrackerList_All - FTrackerList_Live; @@ -87,40 +94,121 @@ procedure TNewTrackon.CreateTrackerList_Dead; RemoveTrackersFromList(TrackerList_Live, TrackerList_Dead); end; -procedure TNewTrackon.DownloadTracker(NewTrackon_List: TNewTrackon_List); +function TNewTrackon.DownloadTracker(NewTrackon_List: TNewTrackon_List): boolean; begin - if NewTrackon_List = ntl_CREATE_DEAD then - Exit; //there is no Dead tracker list to be downloaded - //download via URL and put the data in the TrackerList - //will create exception if something is wrong - FTRackerList[NewTrackon_List].DelimitedText := - TFPCustomHTTPClient.SimpleGet(URL[NewTrackon_List]); + try + //there is no Dead tracker list to be downloaded. so it can be skip + if NewTrackon_List <> ntl_CREATE_DEAD then + begin + //download via URL and put the data in the TrackerList + //will create exception if something is wrong + FTRackerList[NewTrackon_List].DelimitedText := + TFPCustomHTTPClient.SimpleGet(URL[NewTrackon_List]); + end; - //Clean up the tracker list + Result := True; + except + //No OpenSSL or web server is down + Result := False; + end; + + //Clean up the tracker list just downloaded SanatizeTrackerList(FTRackerList[NewTrackon_List]); end; -function TNewTrackon.DownloadTrackers: boolean; +function TNewTrackon.DownloadEverything: boolean; var i: TNewTrackon_List; begin - try - //download all the list one by one - for i in TNewTrackon_List do + //download all the list one by one + for i in TNewTrackon_List do + begin + Result := DownloadTracker(i); + if not Result then + Exit; + end; + + CreateTrackerList_Dead; +end; + +function TNewTrackon.Download_All_Live_Stable: boolean; +begin + Result := DownloadTracker(ntl_URL_All); + if Result then + begin + Result := DownloadTracker(ntl_URL_Stable); + if Result then begin - DownloadTracker(i);//< may create exception + Result := DownloadTracker(ntl_URL_Live); end; + end; - CreateTrackerList_Dead; + CreateTrackerList_Dead; +end; - FDownloadStatus := True; - except - //No OpenSSL or web server is down - FDownloadStatus := False; - end; +function TNewTrackon.SubmitTrackers(TrackerList: TStringList; + out TrackersSendCount: integer): boolean; +var + TrackerListToBeSend: TStringList; + FormData: string; + Trackers: string; + HTTPS: TFPHTTPClient; + Seperator: string; + +const + URL_POST = 'https://newtrackon.com/api/add'; +begin + TrackersSendCount := 0; - Result := FDownloadStatus; + //Must always first download the ALL tracker list if not already downloaded. + //To make sure it is always checking agains the most recent list + Result := DownloadTracker(ntl_URL_All); + + if Result then + begin + try + HTTPS := TFPHTTPClient.Create(nil); + TrackerListToBeSend := TStringList.Create; + TrackerListToBeSend.Assign(TrackerList); + + //remove all duplicate trackers before sending, + RemoveTrackersFromList(TrackerList_All, TrackerListToBeSend); + + //Give information back about how many unique tracker URL is send. + TrackersSendCount := TrackerListToBeSend.Count; + + if TrackersSendCount > 0 then + begin + + //this is the 'key' + FormData := 'new_trackers='; + + //This is the 'values' all seperated with one space '+' + Seperator := ''; + for Trackers in TrackerListToBeSend do + begin + FormData := FormData + Seperator + HTTPEncode(Trackers); + if Seperator = '' then + Seperator := '+'; + end; + + try + HTTPS.FormPost(URL_POST, FormData); + + //Check the response must be 204 + Result := HTTPS.ResponseStatusCode = 204; + except + //No OpenSSL or web server is down + Result := False; + end; + end; + + finally + TrackerListToBeSend.Free; + HTTPS.Free; + end; + end; end; constructor TNewTrackon.Create; diff --git a/source/code/ngosang_trackerslist.pas b/source/code/ngosang_trackerslist.pas index 2c87f1b..2bfebec 100644 --- a/source/code/ngosang_trackerslist.pas +++ b/source/code/ngosang_trackerslist.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit ngosang_trackerslist; { @@ -24,6 +25,7 @@ interface ntl_URL_All_HTTPS,//< Download from internet ntl_URL_All_IP,//< Download from internet ntl_URL_All_UDP,//< Download from internet + ntl_URL_All_WS,//< Download from internet ntl_URL_Best,//< Download from internet ntl_URL_Best_IP//< Download from internet ); @@ -33,29 +35,29 @@ interface TngosangTrackerList = class private FTRackerList: array [Tngosang_List] of TStringList; - - procedure DownloadTracker(ngosang_List: Tngosang_List); - + function DownloadTracker(ngosang_List: Tngosang_List): TStringList; public - property TrackerList_Blacklist: TStringList read FTRackerList[ntl_URL_Blacklist]; + property TrackerList_Blacklist: TStringList index ntl_URL_Blacklist + read DownloadTracker; - property TrackerList_All: TStringList read FTrackerList[ntl_URL_All]; + property TrackerList_All: TStringList index ntl_URL_All read DownloadTracker; - property TrackerList_All_HTTP: TStringList read FTrackerList[ntl_URL_All_HTTP]; + property TrackerList_All_HTTP: TStringList index ntl_URL_All_HTTP + read DownloadTracker; - property TrackerList_All_HTTPS: TStringList read FTrackerList[ntl_URL_All_HTTPS]; + property TrackerList_All_HTTPS: TStringList index ntl_URL_All_HTTPS + read DownloadTracker; - property TrackerList_All_IP: TStringList read FTrackerList[ntl_URL_All_IP]; + property TrackerList_All_IP: TStringList index ntl_URL_All_IP read DownloadTracker; - property TrackerList_All_UDP: TStringList read FTrackerList[ntl_URL_All_UDP]; + property TrackerList_All_UDP: TStringList index ntl_URL_All_UDP read DownloadTracker; - property TrackerList_Best: TStringList read FTrackerList[ntl_URL_Best]; + property TrackerList_All_WS: TStringList index ntl_URL_All_WS read DownloadTracker; - property TrackerList_Best_IP: TStringList read FTrackerList[ntl_URL_Best_IP]; + property TrackerList_Best: TStringList index ntl_URL_Best read DownloadTracker; - //Download all the trackers via API - function DownloadTrackers: boolean; + property TrackerList_Best_IP: TStringList index ntl_URL_Best_IP read DownloadTracker; //create/destroy class object constructor Create; @@ -64,7 +66,7 @@ TngosangTrackerList = class implementation -uses fphttpclient, LazUTF8, torrent_miscellaneous; +uses opensslsockets, OpenSSL, fphttpclient, LazUTF8, torrent_miscellaneous; const URL: array [Tngosang_List] of string = @@ -75,44 +77,35 @@ implementation 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_https.txt', 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_ip.txt', 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_udp.txt', + 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_ws.txt', 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt', 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best_ip.txt' ); { TngosangTrackerList } -procedure TngosangTrackerList.DownloadTracker(ngosang_List: Tngosang_List); -begin - //download via URL and put the data in the TrackerList - FTRackerList[ngosang_List].DelimitedText := - TFPCustomHTTPClient.SimpleGet(URL[ngosang_List]); - - //Clean up the tracker list - SanatizeTrackerList(FTRackerList[ngosang_List]); -end; - - -function TngosangTrackerList.DownloadTrackers: boolean; -var - i: Tngosang_List; +function TngosangTrackerList.DownloadTracker(ngosang_List: Tngosang_List): TStringList; begin try - //download all the list one by one - for i in Tngosang_List do - begin - DownloadTracker(i); - end; + //download via URL and put the data in the TrackerList + FTRackerList[ngosang_List].DelimitedText := + TFPCustomHTTPClient.SimpleGet(URL[ngosang_List]); + + //Clean up the tracker list + SanatizeTrackerList(FTRackerList[ngosang_List]); - Result := True; except //No OpenSSL or web server is down - Result := False; + FTRackerList[ngosang_List].Clear; end; + + Result := FTrackerList[ngosang_List]; end; constructor TngosangTrackerList.Create; var i: Tngosang_List; begin + //Create all the TStringList for i in Tngosang_List do begin @@ -134,5 +127,47 @@ destructor TngosangTrackerList.Destroy; inherited Destroy; end; -end. +initialization + // FPC 3.2.2 is missing support for the latest openSSL 3, will be fix in the future release. + // Latest openssl.pas https://gitlab.com/freepascal.org/fpc/source/-/blob/main/packages/openssl/src/openssl.pas?ref_type=heads + // Copy this newer SSL detection into the older openssl code used by the present FPC 3.2.2 +{$IFDEF VER3_2} +{$IFDEF WINDOWS} + DLLSSLName3 := {$IFDEF WIN64}'libssl-3-x64.dll'{$ELSE}'libssl-3.dll'{$ENDIF}; + DLLUtilName2 := {$IFDEF WIN64}'libcrypto-3-x64.dll'{$ELSE}'libcrypto-3.dll'{$ENDIF}; +{$ELSE WINDOWS} +{$IFDEF DARWIN} + if High(OpenSSL.DLLVersions) >= 19 then + begin + // macOS version + // LibreSSL + OpenSSL.DLLVersions[1] := '.48'; + OpenSSL.DLLVersions[2] := '.47'; + OpenSSL.DLLVersions[3] := '.46'; + OpenSSL.DLLVersions[4] := '.45'; + OpenSSL.DLLVersions[5] := '.44'; + OpenSSL.DLLVersions[6] := '.43'; + OpenSSL.DLLVersions[7] := '.35'; + + // OpenSSL + OpenSSL.DLLVersions[8] := '.3'; + OpenSSL.DLLVersions[9] := '.1.1'; + OpenSSL.DLLVersions[10] := '.11'; + OpenSSL.DLLVersions[11] := '.10'; + OpenSSL.DLLVersions[12] := '.1.0.6'; + OpenSSL.DLLVersions[13] := '.1.0.5'; + OpenSSL.DLLVersions[14] := '.1.0.4'; + OpenSSL.DLLVersions[15] := '.1.0.3'; + OpenSSL.DLLVersions[16] := '.1.0.2'; + OpenSSL.DLLVersions[17] := '.1.0.1'; + OpenSSL.DLLVersions[18] := '.1.0.0'; + OpenSSL.DLLVersions[19] := '.0.9.8'; + end; +{$ElSE DARWIN} + // Unix/Linux version of FPC need openSSL 3 in the detection list + OpenSSL.DLLVersions[Length(OpenSSL.DLLVersions) - 1] := '.3'; +{$ENDIF DARWIN} +{$ENDIF WINDOWS} +{$ENDIF VER3_2} +end. diff --git a/source/code/torrent_miscellaneous.pas b/source/code/torrent_miscellaneous.pas index a55e58e..525dbe1 100644 --- a/source/code/torrent_miscellaneous.pas +++ b/source/code/torrent_miscellaneous.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit torrent_miscellaneous; { @@ -84,6 +85,16 @@ TTrackerList = record //Log string text output LogStringList: TStringList; + // No announce check needed for some private trackers + SkipAnnounceCheck: boolean; + + // Private tracker may need extra 'info:source' variable + SourceTag: UTF8String; + + // This is needed if someone want to convert private torrent to public torrent + // Source tag is not used in public tracker. + RemoveAllSourceTag: Boolean; + end; @@ -102,6 +113,8 @@ function LoadTorrentViaDir(const Dir: UTF8String; function ValidTrackerURL(const TrackerURL: UTF8String): boolean; +function WebTorrentTrackerURL(const TrackerURL: UTF8String): boolean; + procedure CombineFiveTrackerListToOne(TrackerListOrder: TTrackerListOrder; var TrackerList: TTrackerList; PresentTorrentTrackerList: TStringList); @@ -111,7 +124,7 @@ function ConsoleModeDecodeParameter(out FileNameOrDirStr: UTF8String; function DecodeConsoleUpdateParameter(const ConsoleUpdateParameter: UTF8String; var TrackerList: TTrackerList): boolean; - +function TrackerURLWithAnnounce(const TrackerURL: UTF8String): boolean; const VALID_TRACKERS_URL: array[0..4] of UTF8String = @@ -124,6 +137,21 @@ function DecodeConsoleUpdateParameter(const ConsoleUpdateParameter: UTF8String; ); + //'add trackers' text file must be place in the same directory as the program. + FILE_NAME_ADD_TRACKERS: string = 'add_trackers.txt'; + + //'remove trackers' text file must be place in the same directory as the program. + FILE_NAME_REMOVE_TRACKERS: string = 'remove_trackers.txt'; + + //'export trackers' text file wil be created in the same directory as the program. + FILE_NAME_EXPORT_TRACKERS: string = 'export_trackers.txt'; + + //'log' text file will be saved in the same directory as the program + // only in the console mode. + FILE_NAME_CONSOLE_LOG: string = 'console_log.txt'; + + CONSOLE_SUCCESS_STATUS: string = 'OK'; + implementation uses LazUTF8, LazFileUtils; @@ -183,7 +211,6 @@ procedure RandomizeTrackerList(StringList: TStringList); //The order of the string list must be randomize if StringList.Count > 1 then begin - Randomize; for i := 0 to StringList.Count - 1 do begin StringList.Exchange(i, Random(StringList.Count)); @@ -219,8 +246,11 @@ function ByteSizeToBiggerSizeFormatStr(ByteSize: int64): string; Result := Format('%0.2f MiB', [ByteSize / (1024 * 1024)]) else if ByteSize >= (1024) then - Result := Format('%0.2f KiB', [ByteSize / 1024]); - Result := Result + Format(' (%d Bytes)', [ByteSize]); + Result := Format('%0.2f KiB', [ByteSize / 1024]) + else + Result := ''; + + Result := Result + Format(' (%d Bytes)', [ByteSize]); end; @@ -363,12 +393,14 @@ function ConsoleModeDecodeParameter(out FileNameOrDirStr: UTF8String; function ValidTrackerURL(const TrackerURL: UTF8String): boolean; begin //TrackerURL should be cleanup with UTF8trim() - Result := (Pos('http://', TrackerURL) = 1) or (Pos('https://', TrackerURL) = 1) or - (Pos('ws://', TrackerURL) = 1) or (Pos('wss://', TrackerURL) = 1) or - (Pos('udp://', TrackerURL) = 1); + Result := (Pos('udp://', TrackerURL) = 1) or (Pos('http://', TrackerURL) = 1) or + (Pos('https://', TrackerURL) = 1) or WebTorrentTrackerURL(TrackerURL); end; - +function WebTorrentTrackerURL(const TrackerURL: UTF8String): boolean; +begin + Result := (Pos('ws://', TrackerURL) = 1) or (Pos('wss://', TrackerURL) = 1); +end; procedure CombineFiveTrackerListToOne(TrackerListOrder: TTrackerListOrder; var TrackerList: TTrackerList; PresentTorrentTrackerList: TStringList); @@ -582,19 +614,29 @@ procedure CombineFiveTrackerListToOne(TrackerListOrder: TTrackerListOrder; function ConsoleModeDecodeParameter(out FileNameOrDirStr: UTF8String; var TrackerList: TTrackerList): boolean; +var + i: integer; begin { - Console mode can be started with 2 parameter - Update methode: -U0 , -U1, -U2, -U3, -U4 + Console mode can be started with example 2 parameter + Update methode: -U0 , -U1, -U2, -U3, -U4 etc. String with a link to folder or to torrent file. 'C:\dir' + Must keep backward compatible with the first and previeus release. + First or seccond parameter must be related to -Ux + + other parameter after are optional -SOC and -SOURCE + + example: + "path_to_folder" -U3 -SAC -SOURCE "ABC" + -U3 "path_to_folder" -SAC -SOURCE "ABC" } + Result := False; case Paramcount of 0: begin TrackerList.LogStringList.Add('ERROR: There are no parameter detected.'); - Result := False; exit; end; 1: @@ -605,7 +647,7 @@ function ConsoleModeDecodeParameter(out FileNameOrDirStr: UTF8String; TrackerList.TrackerListOrderForUpdatedTorrent := tloSort; Result := True; end; - 2: + else begin //Two parameters. The user can select the update methode. //Check for '-U' contruction as first parameter @@ -613,23 +655,54 @@ function ConsoleModeDecodeParameter(out FileNameOrDirStr: UTF8String; begin //Update parameter is the first parameter Result := DecodeConsoleUpdateParameter(ParamStr(1), TrackerList); + // second parameter is the file/folder FileNameOrDirStr := UTF8Trim(ParamStr(2)); - end - else + Exit; + end; + + //Check for '-U' contruction as second parameter + if (Pos('-U', ParamStr(2)) = 1) then begin - //Update parameter is the second parameter + // Update parameter is the second parameter Result := DecodeConsoleUpdateParameter(ParamStr(2), TrackerList); + // first parameter MUST be the file/folder FileNameOrDirStr := UTF8Trim(ParamStr(1)); end; + //Check for parameter -SAC and -SOURCE + for i := 2 to ParamCount do + begin + if ParamStr(i) = '-SAC' then + begin + TrackerList.SkipAnnounceCheck := True; + Continue; + end; + + if ParamStr(i) = '-SOURCE' then + begin + if ParamCount < i + 1 then + begin + TrackerList.LogStringList.Add( + 'ERROR: There is no value after -SOURCE'); + Result := False; + exit; + end; + + // parameter after -SOURCE must be the value. + TrackerList.SourceTag := ParamStr(i + 1); + // Empty '' -> remove all source tag + TrackerList.RemoveAllSourceTag := TrackerList.SourceTag = ''; + end; + end; + end; - else - begin - TrackerList.LogStringList.Add( - 'ERROR: There can only be maximum of 2 parameter. Not: ' + IntToStr(ParamCount)); - Result := False; - exit; - end; + //else + //begin + // TrackerList.LogStringList.Add( + // 'ERROR: There can only be maximum of 2 parameter. Not: ' + IntToStr(ParamCount)); + // Result := False; + // exit; + //end; end; end; @@ -688,10 +761,15 @@ function DecodeConsoleUpdateParameter(const ConsoleUpdateParameter: UTF8String; end; end; +function TrackerURLWithAnnounce(const TrackerURL: UTF8String): boolean; +const + ANNOUNCE_STRING: string = '/announce'; + ANNOUNCE_PHP_STRING: string = '/announce.php'; +begin + //TrackerURL must end with ANNOUNCE_STRING + Result := (RightStr(TrackerURL, length(ANNOUNCE_STRING)) = ANNOUNCE_STRING) or + (RightStr(TrackerURL, length(ANNOUNCE_PHP_STRING)) = ANNOUNCE_PHP_STRING); - - - - +end; end. diff --git a/source/code/trackerlist_online.pas b/source/code/trackerlist_online.pas index 507ae6c..65c09d7 100644 --- a/source/code/trackerlist_online.pas +++ b/source/code/trackerlist_online.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit trackerlist_online; { @@ -89,7 +90,10 @@ function TTrackerListOnline.TrackerListOnlineStatusToString( tos_dead: Result := 'Dead'; tos_unknown: Result := 'Unknown'; else - assert(True, 'Unknown TTrackerListOnlineStatus') + begin + Result := ''; + assert(True, 'Unknown TTrackerListOnlineStatus') + end; end; end; diff --git a/source/project/.gitignore b/source/project/.gitignore new file mode 100644 index 0000000..9f09d1d --- /dev/null +++ b/source/project/.gitignore @@ -0,0 +1,6 @@ +# ignore link resource file +link.res + +# ignore windows bat file +*.bat + diff --git a/source/project/tracker_editor/link.res b/source/project/tracker_editor/link.res deleted file mode 100644 index cd9f6b7..0000000 --- a/source/project/tracker_editor/link.res +++ /dev/null @@ -1,452 +0,0 @@ -SEARCH_DIR(C:\lazarus\lcl\units\i386-win32\win32\) -SEARCH_DIR(C:\lazarus\lcl\units\i386-win32\) -SEARCH_DIR(C:\lazarus\components\lazutils\lib\i386-win32\) -SEARCH_DIR(C:\lazarus\packager\units\i386-win32\) -SEARCH_DIR(.\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\httpd22\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\zorba\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\zlib\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-jedi\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\winceunits\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\unzip\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\tcl\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\symbolic\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\sqlite\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\sdl\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\rsvg\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\regexpr\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\pxlib\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\ptc\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\postgres\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\pcap\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\oracle\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\openssl\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\opengl\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\opencl\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\openal\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\oggvorbis\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\odbc\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\nvapi\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\numlib\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\mysql\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\mad\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\lua\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\libxml2\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\libsee\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\libpng\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\libgd\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\lexyacc\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\imagemagick\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\ibase\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\hermes\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\hash\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\gtk2\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\gtk1\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\graph\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\gmp\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\gdbint\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fv\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fppkg\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fpmkunit\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fpindexer\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fpgtk\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fftw\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-web\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-res\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-registry\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-process\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-passrc\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-net\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-json\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-js\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-fpcunit\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-extra\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-db\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\fastcgi\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\dblib\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\chm\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\cdrom\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\cairo\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\bzip2\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\aspell\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\a52\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\units\i386-win32\) -SEARCH_DIR(C:\lazarus\fpc\2.6.2\bin\i386-win32\) -INPUT( -D:\lazarus\source\trackereditor\src\lib\i386-win32\trackereditor.o -D:\lazarus\source\trackereditor\src\lib\i386-win32\trackereditor.or -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\system.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\fpintres.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\objpas.o -C:\lazarus\lcl\units\i386-win32\win32\interfaces.o -C:\lazarus\lcl\units\i386-win32\forms.o -D:\lazarus\source\trackereditor\src\lib\i386-win32\main.o -D:\lazarus\source\trackereditor\src\lib\i386-win32\bencode.o -D:\lazarus\source\trackereditor\src\lib\i386-win32\decodetorrent.o -C:\lazarus\lcl\units\i386-win32\interfacebase.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\types.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\classes.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\sysutils.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\math.o -C:\lazarus\lcl\units\i386-win32\lclstrconsts.o -C:\lazarus\lcl\units\i386-win32\lcltype.o -C:\lazarus\lcl\units\i386-win32\lclproc.o -C:\lazarus\lcl\units\i386-win32\lmessages.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpimage.o -C:\lazarus\lcl\units\i386-win32\graphtype.o -C:\lazarus\lcl\units\i386-win32\graphmath.o -C:\lazarus\lcl\units\i386-win32\intfgraphics.o -C:\lazarus\lcl\units\i386-win32\themes.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\windows.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\rtlconsts.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\typinfo.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\sysconst.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\windirs.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\win9xwsmanager.o -C:\lazarus\components\lazutils\lib\i386-win32\lazlogger.o -C:\lazarus\lcl\units\i386-win32\fpcadds.o -C:\lazarus\components\lazutils\lib\i386-win32\avglvltree.o -C:\lazarus\components\lazutils\lib\i386-win32\fileutil.o -C:\lazarus\lcl\units\i386-win32\wsreferences.o -C:\lazarus\components\lazutils\lib\i386-win32\lazmethodlist.o -C:\lazarus\components\lazutils\lib\i386-win32\lazutf8.o -C:\lazarus\components\lazutils\lib\i386-win32\lazloggerbase.o -C:\lazarus\components\lazutils\lib\i386-win32\lazclasses.o -C:\lazarus\components\lazutils\lib\i386-win32\masks.o -C:\lazarus\components\lazutils\lib\i386-win32\lazutilsstrconsts.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\contnrs.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\gettext.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\messages.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpreadbmp.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpwritebmp.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\bmpcomn.o -C:\lazarus\lcl\units\i386-win32\lclversion.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpreadpng.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpwritepng.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpreadtiff.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpwritetiff.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fptiffcmn.o -C:\lazarus\lcl\units\i386-win32\icnstypes.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpimgcmn.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\pngcomn.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\zstream.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\zbase.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\gzio.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\dos.o -C:\lazarus\fpc\2.6.2\units\i386-win32\hash\crc.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\zdeflate.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\zinflate.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\strings.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\trees.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\adler.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\infblock.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\infutil.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\infcodes.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\inftrees.o -C:\lazarus\fpc\2.6.2\units\i386-win32\paszlib\inffast.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\ctypes.o -C:\lazarus\lcl\units\i386-win32\graphics.o -C:\lazarus\lcl\units\i386-win32\lclintf.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpcanvas.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpreadpnm.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpwritepnm.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpreadjpeg.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpwritejpeg.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\fpreadgif.o -C:\lazarus\lcl\units\i386-win32\lresources.o -C:\lazarus\lcl\units\i386-win32\lclrescache.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-image\clipping.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jpeglib.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdapimin.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdatasrc.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdapistd.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jmorecfg.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdeferr.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jinclude.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jerror.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jmemmgr.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdmarker.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdinput.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcomapi.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jutils.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jmemnobs.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdmaster.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdcolor.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdsample.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdpostct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jddctmgr.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdphuff.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdhuff.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdcoefct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdmainct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jquant1.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jquant2.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdmerge.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jidctfst.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jidctint.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jidctflt.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jidctred.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcapistd.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcapimin.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jdatadst.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcparam.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcinit.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcmarker.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcphuff.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jchuff.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcmaster.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jccolor.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcsample.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcprepct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcdctmgr.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jccoefct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jcmainct.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jfdctint.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jfdctfst.o -C:\lazarus\fpc\2.6.2\units\i386-win32\pasjpeg\jfdctflt.o -C:\lazarus\lcl\units\i386-win32\dynqueue.o -C:\lazarus\lcl\units\i386-win32\lazconfigstorage.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\syncobjs.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\shellapi.o -C:\lazarus\lcl\units\i386-win32\utf8process.o -C:\lazarus\lcl\units\i386-win32\maps.o -C:\lazarus\components\lazutils\lib\i386-win32\lazutf8sysutils.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\activex.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\variants.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\varutils.o -C:\lazarus\lcl\units\i386-win32\tmschema.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-process\process.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-process\pipes.o -C:\lazarus\lcl\units\i386-win32\win32\win32int.o -C:\lazarus\lcl\units\i386-win32\translations.o -C:\lazarus\lcl\units\i386-win32\comctrls.o -C:\lazarus\lcl\units\i386-win32\controls.o -C:\lazarus\lcl\units\i386-win32\buttons.o -C:\lazarus\lcl\units\i386-win32\dialogs.o -C:\lazarus\lcl\units\i386-win32\stdctrls.o -C:\lazarus\lcl\units\i386-win32\win32\win32def.o -C:\lazarus\lcl\units\i386-win32\menus.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\commctrl.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\multimon.o -C:\lazarus\lcl\units\i386-win32\stringhashlist.o -C:\lazarus\components\lazutils\lib\i386-win32\lconvencoding.o -C:\lazarus\lcl\units\i386-win32\imglist.o -C:\lazarus\lcl\units\i386-win32\actnlist.o -C:\lazarus\lcl\units\i386-win32\extctrls.o -C:\lazarus\lcl\units\i386-win32\toolwin.o -C:\lazarus\lcl\units\i386-win32\wslclclasses.o -C:\lazarus\lcl\units\i386-win32\lclclasses.o -C:\lazarus\lcl\units\i386-win32\wsimglist.o -C:\lazarus\lcl\units\i386-win32\wsproc.o -C:\lazarus\lcl\units\i386-win32\wsfactory.o -C:\lazarus\lcl\units\i386-win32\propertystorage.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\rttiutils.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\strutils.o -C:\lazarus\lcl\units\i386-win32\wsmenus.o -C:\lazarus\lcl\units\i386-win32\customtimer.o -C:\lazarus\lcl\units\i386-win32\clipbrd.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\custapp.o -C:\lazarus\lcl\units\i386-win32\helpintfs.o -C:\lazarus\lcl\units\i386-win32\wscontrols.o -C:\lazarus\lcl\units\i386-win32\wsforms.o -C:\lazarus\lcl\units\i386-win32\extendedstrings.o -C:\lazarus\lcl\units\i386-win32\textstrings.o -C:\lazarus\lcl\units\i386-win32\wsstdctrls.o -C:\lazarus\lcl\units\i386-win32\popupnotifier.o -C:\lazarus\lcl\units\i386-win32\wsextctrls.o -C:\lazarus\lcl\units\i386-win32\imagelistcache.o -C:\lazarus\lcl\units\i386-win32\wsbuttons.o -C:\lazarus\lcl\units\i386-win32\wscomctrls.o -C:\lazarus\lcl\units\i386-win32\wstoolwin.o -C:\lazarus\lcl\units\i386-win32\buttonpanel.o -C:\lazarus\lcl\units\i386-win32\wsdialogs.o -C:\lazarus\lcl\units\i386-win32\win32\win32proc.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsfactory.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsbuttons.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsmenus.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsstdctrls.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsdialogs.o -C:\lazarus\lcl\units\i386-win32\win32\win32themes.o -C:\lazarus\lcl\units\i386-win32\win32\win32extra.o -C:\lazarus\lcl\units\i386-win32\lclmessageglue.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\shlobj.o -C:\lazarus\lcl\units\i386-win32\calendar.o -C:\lazarus\lcl\units\i386-win32\arrow.o -C:\lazarus\lcl\units\i386-win32\spin.o -C:\lazarus\lcl\units\i386-win32\extdlgs.o -C:\lazarus\lcl\units\i386-win32\checklst.o -C:\lazarus\lcl\units\i386-win32\grids.o -C:\lazarus\lcl\units\i386-win32\wscalendar.o -C:\lazarus\lcl\units\i386-win32\wsarrow.o -C:\lazarus\lcl\units\i386-win32\wsspin.o -C:\lazarus\lcl\units\i386-win32\wsextdlgs.o -C:\lazarus\lcl\units\i386-win32\wschecklst.o -C:\lazarus\lcl\units\i386-win32\dynamicarray.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\xmlconf.o -C:\lazarus\lcl\units\i386-win32\maskedit.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\dom.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\xmlread.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\xmlwrite.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\xmlutils.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-xml\dtdmodel.o -C:\lazarus\fpc\2.6.2\units\i386-win32\fcl-base\uriparser.o -C:\lazarus\lcl\units\i386-win32\wsgrids.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsarrow.o -C:\lazarus\lcl\units\i386-win32\win32\win32wscalendar.o -C:\lazarus\lcl\units\i386-win32\win32\win32wschecklst.o -C:\lazarus\lcl\units\i386-win32\win32\win32wscomctrls.o -C:\lazarus\lcl\units\i386-win32\win32\win32wscontrols.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsextctrls.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsextdlgs.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsforms.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsgrids.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsimglist.o -C:\lazarus\lcl\units\i386-win32\win32\win32wsspin.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\uxtheme.o -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\commdlg.o -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\sysinitpas.o -) -GROUP( -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpsystem.a -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpfpintres.a -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpsysutils.a -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpwindows.a -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpdos.a -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\libimpshellapi.a -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\libimpactivex.a -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpvarutils.a -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\libimpcommctrl.a -C:\lazarus\lcl\units\i386-win32\win32\libimpwin32extra.a -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\libimpshlobj.a -C:\lazarus\lcl\units\i386-win32\win32\libimpwin32wsforms.a -C:\lazarus\fpc\2.6.2\units\i386-win32\winunits-base\libimpcommdlg.a -C:\lazarus\fpc\2.6.2\units\i386-win32\rtl\libimpsysinitpas.a -) -SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api"); -OUTPUT_FORMAT(pei-i386) -ENTRY(_mainCRTStartup) -SECTIONS -{ - . = SIZEOF_HEADERS; - . = ALIGN(__section_alignment__); - .text __image_base__ + ( __section_alignment__ < 0x1000 ? . : __section_alignment__ ) : - { - *(.init) - *(.text .stub .text.* .gnu.linkonce.t.*) - *(SORT(.text$*)) - *(.glue_7t) - *(.glue_7) - . = ALIGN(8); - ___CTOR_LIST__ = .; __CTOR_LIST__ = . ; - LONG (-1); - *(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0); - ___DTOR_LIST__ = .; __DTOR_LIST__ = . ; - LONG (-1); - *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0); - *(.fini) - PROVIDE (etext = .); - *(.gcc_except_table) - } - .data BLOCK(__section_alignment__) : - { - __data_start__ = . ; - *(.data .data.* .gnu.linkonce.d.* .fpc*) - *(.data2) - *(SORT(.data$*)) - *(.jcr) - PROVIDE (__tls_index = .); - LONG (0); - __data_end__ = . ; - *(.data_cygwin_nocopy) - } - .rdata BLOCK(__section_alignment__) : - { - *(.rdata) - *(.rodata .rodata.* .gnu.linkonce.r.*) - *(SORT(.rdata$*)) - *(.eh_frame) - ___RUNTIME_PSEUDO_RELOC_LIST__ = .; - __RUNTIME_PSEUDO_RELOC_LIST__ = .; - *(.rdata_runtime_pseudo_reloc) - ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .; - __RUNTIME_PSEUDO_RELOC_LIST_END__ = .; - } - .pdata BLOCK(__section_alignment__) : { *(.pdata) } - .bss BLOCK(__section_alignment__) : - { - __bss_start__ = . ; - *(.bss .bss.* .gnu.linkonce.b.*) - *(SORT(.bss$*)) - *(COMMON) - __bss_end__ = . ; - } - .edata BLOCK(__section_alignment__) : { *(.edata) } - .idata BLOCK(__section_alignment__) : - { - SORT(*)(.idata$2) - SORT(*)(.idata$3) - /* These zeroes mark the end of the import list. */ - LONG (0); LONG (0); LONG (0); LONG (0); LONG (0); - SORT(*)(.idata$4) - SORT(*)(.idata$5) - SORT(*)(.idata$6) - SORT(*)(.idata$7) - } - .CRT BLOCK(__section_alignment__) : - { - ___crt_xc_start__ = . ; - *(SORT(.CRT$XC*)) /* C initialization */ - ___crt_xc_end__ = . ; - ___crt_xi_start__ = . ; - *(SORT(.CRT$XI*)) /* C++ initialization */ - ___crt_xi_end__ = . ; - ___crt_xl_start__ = . ; - *(SORT(.CRT$XL*)) /* TLS callbacks */ - /* ___crt_xl_end__ is defined in the TLS Directory support code */ - PROVIDE (___crt_xl_end__ = .); - ___crt_xp_start__ = . ; - *(SORT(.CRT$XP*)) /* Pre-termination */ - ___crt_xp_end__ = . ; - ___crt_xt_start__ = . ; - *(SORT(.CRT$XT*)) /* Termination */ - ___crt_xt_end__ = . ; - } - .tls BLOCK(__section_alignment__) : - { - ___tls_start__ = . ; - *(.tls .tls.*) - *(.tls$) - *(SORT(.tls$*)) - ___tls_end__ = . ; - } - .rsrc BLOCK(__section_alignment__) : - { - *(.rsrc) - *(SORT(.rsrc$*)) - } - .reloc BLOCK(__section_alignment__) : { *(.reloc) } - .stab BLOCK(__section_alignment__) (NOLOAD) : { *(.stab) } - .stabstr BLOCK(__section_alignment__) (NOLOAD) : { *(.stabstr) } - .debug_aranges BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_aranges) } - .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_pubnames) } - .debug_info BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_info) *(.gnu.linkonce.wi.*) } - .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_abbrev) } - .debug_line BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_line) } - .debug_frame BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_frame) } - .debug_str BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_str) } - .debug_loc BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_loc) } - .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_macinfo) } - .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_weaknames) } - .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_funcnames) } - .debug_typenames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_typenames) } - .debug_varnames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_varnames) } - .debug_ranges BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_ranges) } -} diff --git a/source/project/tracker_editor/ppas.bat b/source/project/tracker_editor/ppas.bat deleted file mode 100644 index a02c47b..0000000 --- a/source/project/tracker_editor/ppas.bat +++ /dev/null @@ -1,14 +0,0 @@ -@echo off -SET THEFILE=trackereditor.exe -echo Linking %THEFILE% -C:\lazarus\fpc\2.6.2\bin\i386-win32\ld.exe -b pei-i386 -m i386pe --gc-sections -s --subsystem windows --entry=_WinMainCRTStartup -o trackereditor.exe link.res -if errorlevel 1 goto linkend -C:\lazarus\fpc\2.6.2\bin\i386-win32\postw32.exe --subsystem gui --input trackereditor.exe --stack 16777216 -if errorlevel 1 goto linkend -goto end -:asmend -echo An error occured while assembling %THEFILE% -goto end -:linkend -echo An error occured while linking %THEFILE% -:end diff --git a/source/project/tracker_editor/trackereditor.lpi b/source/project/tracker_editor/trackereditor.lpi index e25fac9..9b49c62 100644 --- a/source/project/tracker_editor/trackereditor.lpi +++ b/source/project/tracker_editor/trackereditor.lpi @@ -1,14 +1,14 @@ - + + - <ResourceType Value="res"/> <UseXPManifest Value="True"/> @@ -17,9 +17,6 @@ <i18n> <EnableI18N LFM="False"/> </i18n> - <VersionInfo> - <StringTable ProductVersion=""/> - </VersionInfo> <BuildModes Count="3"> <Item1 Name="Default" Default="True"/> <Item2 Name="Debug"> @@ -31,7 +28,7 @@ </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> - <OtherUnitFiles Value="..\..\code"/> + <OtherUnitFiles Value="..\..\code;..\..\..\submodule\dcpcrypt\Hashes;..\..\..\submodule\dcpcrypt"/> <UnitOutputDirectory Value="..\..\..\lib\trackereditor\$(TargetCPU)-$(TargetOS)"/> </SearchPaths> <Parsing> @@ -72,9 +69,20 @@ </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> - <OtherUnitFiles Value="..\..\code"/> + <OtherUnitFiles Value="..\..\code;..\..\..\submodule\dcpcrypt\Hashes;..\..\..\submodule\dcpcrypt"/> <UnitOutputDirectory Value="..\..\..\lib\trackereditor\$(TargetCPU)-$(TargetOS)"/> </SearchPaths> + <Conditionals Value="If TargetOS = 'darwin' then +begin + If TargetCPU = 'aarch64' then + begin + CustomOptions += '-WM10.15'; + end + else + begin + CustomOptions += '-WM10.14'; + end; +end;"/> <CodeGeneration> <SmartLinkUnit Value="True"/> <Optimizations> @@ -97,20 +105,19 @@ </BuildModes> <PublishOptions> <Version Value="2"/> - <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/> - <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/> </PublishOptions> <RunParams> - <local> - <FormatVersion Value="1"/> - </local> + <FormatVersion Value="2"/> + <Modes Count="1"> + <Mode0 Name="default"/> + </Modes> </RunParams> <RequiredPackages Count="1"> <Item1> <PackageName Value="LCL"/> </Item1> </RequiredPackages> - <Units Count="9"> + <Units Count="14"> <Unit0> <Filename Value="trackereditor.lpr"/> <IsPartOfProject Value="True"/> @@ -152,6 +159,29 @@ <Filename Value="..\..\code\newtrackon.pas"/> <IsPartOfProject Value="True"/> </Unit8> + <Unit9> + <Filename Value="..\..\code\controler_treeview_torrent_data.pas"/> + <IsPartOfProject Value="True"/> + </Unit9> + <Unit10> + <Filename Value="..\..\code\ngosang_trackerslist.pas"/> + <IsPartOfProject Value="True"/> + </Unit10> + <Unit11> + <Filename Value="..\..\..\submodule\dcpcrypt\Hashes\dcpsha256.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="DCPsha256"/> + </Unit11> + <Unit12> + <Filename Value="..\..\..\submodule\dcpcrypt\dcpconst.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="DCPconst"/> + </Unit12> + <Unit13> + <Filename Value="..\..\..\submodule\dcpcrypt\dcpcrypt2.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="DCPcrypt2"/> + </Unit13> </Units> </ProjectOptions> <CompilerOptions> @@ -162,7 +192,7 @@ </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> - <OtherUnitFiles Value="..\..\code"/> + <OtherUnitFiles Value="..\..\code;..\..\..\submodule\dcpcrypt\Hashes;..\..\..\submodule\dcpcrypt"/> <UnitOutputDirectory Value="..\..\..\lib\trackereditor\$(TargetCPU)-$(TargetOS)"/> </SearchPaths> <CodeGeneration> @@ -170,7 +200,7 @@ </CodeGeneration> <Linking> <Debugging> - <GenerateDebugInfo Value="False"/> + <DebugInfoType Value="dsDwarf3"/> <StripSymbols Value="True"/> </Debugging> <LinkSmart Value="True"/> diff --git a/source/project/tracker_editor/trackereditor.lpr b/source/project/tracker_editor/trackereditor.lpr index f440df5..4b3a3e1 100644 --- a/source/project/tracker_editor/trackereditor.lpr +++ b/source/project/tracker_editor/trackereditor.lpr @@ -7,8 +7,9 @@ cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset - Forms, main, bencode, decodetorrent, controlergridtorrentdata, -controler_trackerlist_online, trackerlist_online; + Forms, DCPsha256, DCPconst, DCPcrypt2, main, bencode, decodetorrent, + controlergridtorrentdata, controler_trackerlist_online, trackerlist_online, + controler_treeview_torrent_data; {$R *.res} diff --git a/source/project/tracker_editor/trackereditor.res b/source/project/tracker_editor/trackereditor.res index e994dfa..5565379 100644 Binary files a/source/project/tracker_editor/trackereditor.res and b/source/project/tracker_editor/trackereditor.res differ diff --git a/source/project/unit_test/tracker_editor_test.lpi b/source/project/unit_test/tracker_editor_test.lpi index dc6a3f2..ed4628d 100644 --- a/source/project/unit_test/tracker_editor_test.lpi +++ b/source/project/unit_test/tracker_editor_test.lpi @@ -1,11 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <CONFIG> <ProjectOptions> - <Version Value="9"/> + <Version Value="12"/> <PathDelim Value="\"/> <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + <MainUnitHasScaledStatement Value="False"/> + <CompatibilityMode Value="True"/> + </Flags> <SessionStorage Value="None"/> - <MainUnit Value="0"/> <Title Value="tracker_editor_test"/> <UseAppBundle Value="False"/> <ResourceType Value="res"/> @@ -13,9 +18,6 @@ <i18n> <EnableI18N LFM="False"/> </i18n> - <VersionInfo> - <StringTable ProductVersion=""/> - </VersionInfo> <BuildModes Count="3"> <Item1 Name="Default" Default="True"/> <Item2 Name="Debug"> @@ -47,7 +49,6 @@ <Linking> <Debugging> <DebugInfoType Value="dsDwarf2Set"/> - <UseHeaptrc Value="True"/> <TrashVariables Value="True"/> <UseValgrind Value="True"/> <UseExternalDbgSyms Value="True"/> @@ -87,9 +88,16 @@ </PublishOptions> <RunParams> <local> - <FormatVersion Value="1"/> <CommandLineParams Value="-a --format=plain"/> </local> + <FormatVersion Value="2"/> + <Modes Count="1"> + <Mode0 Name="default"> + <local> + <CommandLineParams Value="-a --format=plain"/> + </local> + </Mode0> + </Modes> </RunParams> <RequiredPackages Count="2"> <Item1> @@ -147,7 +155,8 @@ </SearchPaths> <Linking> <Debugging> - <UseHeaptrc Value="True"/> + <DebugInfoType Value="dsDwarf3"/> + <UseLineInfoUnit Value="False"/> </Debugging> </Linking> </CompilerOptions> diff --git a/source/test/test_miscellaneous.pas b/source/test/test_miscellaneous.pas index ddc40a6..2d9548d 100644 --- a/source/test/test_miscellaneous.pas +++ b/source/test/test_miscellaneous.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit test_miscellaneous; {$mode objfpc}{$H+} @@ -5,11 +6,19 @@ interface uses - Classes, SysUtils, torrent_miscellaneous, ngosang_trackerslist; + Classes, SysUtils, torrent_miscellaneous; type - TVerifyTrackerResult = record + TStartupParameter = record TrackerListOrder: TTrackerListOrder; + SkipAnnounceCheck: boolean; + SourcePresent: boolean; + SourceText: UTF8String; + end; + + + TVerifyTrackerResult = record + StartupParameter: TStartupParameter; TrackerOriginal, TrackerAdded, @@ -20,6 +29,9 @@ TVerifyTrackerResult = record end; +function LoadConsoleLog(const FileNameWithPath: string; out StatusOK: boolean; + out TorrentFilesCount: integer; out TrackersCount: integer): boolean; + function GetProjectRootFolderWithPathDelimiter: string; function VerifyTrackerResult(var VerifyTracker: TVerifyTrackerResult): boolean; @@ -465,7 +477,7 @@ function VerifyTrackerResult(var VerifyTracker: TVerifyTrackerResult): boolean; end; //Must verify if the output is what we expected - case VerifyTracker.TrackerListOrder of + case VerifyTracker.StartupParameter.TrackerListOrder of // Console parameter: -U0 // Insert new trackers list BEFORE, the original trackers list inside the torrent file. // And remove possible duplicated trackers from the ORIGINAL trackers list. @@ -561,4 +573,54 @@ function VerifyTrackerResult(var VerifyTracker: TVerifyTrackerResult): boolean; end; end; +function LoadConsoleLog(const FileNameWithPath: string; out StatusOK: boolean; + out TorrentFilesCount: integer; out TrackersCount: integer): boolean; +var + ConsoleStringlist: TStringList; +begin +{ + console_log.txt is only created in console mode. + Show the in console mode the success/failure of the torrent update. + First line status: 'OK' or 'ERROR: xxxxxxx' xxxxxxx = error description + Second line files count: '1' + Third line tracker count: 23 + Second and third line info are only valid if the first line is 'OK' +} + + //Result = true if file can be readed as expected. + // with 2 lines of values if first line is 'OK' + + ConsoleStringlist := TStringList.Create; + try + ConsoleStringlist.LoadFromFile(FileNameWithPath); + + Result := ConsoleStringlist.Count > 0; + if Result then + begin + StatusOK := ConsoleStringlist[0] = CONSOLE_SUCCESS_STATUS; + end; + + if StatusOK then + begin + //The totall lines in the file must be 3 + Result := ConsoleStringlist.Count >= 3; + if Result then + begin + Result := TryStrToInt(ConsoleStringlist[1], TorrentFilesCount); + end; + + //read only if the TorrentFilesCount is successful + if Result then + begin + Result := TryStrToInt(ConsoleStringlist[2], TrackersCount); + end; + end; + + except + Result := False; + end; + + ConsoleStringlist.Free; +end; + end. diff --git a/source/test/test_newtrackon.pas b/source/test/test_newtrackon.pas index e4c556f..ab9bb21 100644 --- a/source/test/test_newtrackon.pas +++ b/source/test/test_newtrackon.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit test_newtrackon; {$mode objfpc}{$H+} @@ -18,14 +19,15 @@ TTestNewTrackon = class(TTestCase) procedure SetUp; override; procedure TearDown; override; published - procedure Test_DownloadAPI; + procedure Test_API_Download; + procedure Test_API_Upload; end; implementation -procedure TTestNewTrackon.Test_DownloadAPI; +procedure TTestNewTrackon.Test_API_Download; begin - Check(FNewTrackon.DownloadTrackers, 'Download the newtrackon API'); + Check(FNewTrackon.DownloadEverything, 'Download the newtrackon API'); Check(FNewTrackon.TrackerList_All.Count > 0, 'TrackerList_All should never be empty'); @@ -46,13 +48,38 @@ procedure TTestNewTrackon.Test_DownloadAPI; 'TrackerList_Dead should never be empty'); end; +procedure TTestNewTrackon.Test_API_Upload; +var + TrackerList: TStringList; + TrackersSendCount: integer; +begin + TrackerList := TStringList.Create; + + //Add two trackers + TrackerList.Add('udp://tracker.leechers-paradise.org:6969/announce'); + TrackerList.Add('udp://tracker.test.org:6969/announce');//dummy URL + TrackerList.Add('wss://tracker.openwebtorrent.com'); + + //Test if upload is OK + try + Check(FNewTrackon.SubmitTrackers(TrackerList, TrackersSendCount), + 'Upload the newtrackon API'); + Check(TrackersSendCount <= TrackerList.Count, 'TrackersSendCount have too high value'); + finally + TrackerList.Free; + end; + +end; + procedure TTestNewTrackon.SetUp; begin + WriteLn('TTestNewTrackon.SetUp'); FNewTrackon := TNewTrackon.Create; end; procedure TTestNewTrackon.TearDown; begin + WriteLn('TTestNewTrackon.TearDown'); FNewTrackon.Free; end; diff --git a/source/test/test_ngosang_trackers_list.pas b/source/test/test_ngosang_trackers_list.pas index ff1e717..13d694e 100644 --- a/source/test/test_ngosang_trackers_list.pas +++ b/source/test/test_ngosang_trackers_list.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit test_ngosang_trackers_list; {$mode objfpc}{$H+} @@ -23,8 +24,6 @@ implementation procedure TTestNgosangTrackersList.Test_DownloadAPI; begin - Check(FngosangTrackerList.DownloadTrackers, 'Download the ngosang API'); - Check(FngosangTrackerList.TrackerList_Blacklist.Count > 0, 'TrackerList_Blacklist should never be empty'); @@ -47,6 +46,10 @@ procedure TTestNgosangTrackersList.Test_DownloadAPI; FngosangTrackerList.TrackerList_All_UDP.Count > 0, 'TrackerList_All_UDP should never be empty'); + Check( + FngosangTrackerList.TrackerList_All_WS.Count > 0, + 'TrackerList_All_WS should never be empty'); + Check( FngosangTrackerList.TrackerList_Best.Count > 0, 'TrackerList_Best should never be empty'); @@ -58,11 +61,13 @@ procedure TTestNgosangTrackersList.Test_DownloadAPI; procedure TTestNgosangTrackersList.SetUp; begin + WriteLn('TTestNgosangTrackersList.SetUp'); FngosangTrackerList := TngosangTrackerList.Create; end; procedure TTestNgosangTrackersList.TearDown; begin + WriteLn('TTestNgosangTrackersList.TearDown'); FngosangTrackerList.Free; end; diff --git a/source/test/test_start_up_parameter.pas b/source/test/test_start_up_parameter.pas index a867afc..5285a7e 100644 --- a/source/test/test_start_up_parameter.pas +++ b/source/test/test_start_up_parameter.pas @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT unit test_start_up_parameter; { @@ -38,6 +39,11 @@ interface test_miscellaneous; type + TConsoleLogData = record + StatusOK: boolean; + TorrentFilesCount: integer; + TrackersCount: integer; + end; { TTestStartUpParameter } @@ -53,25 +59,30 @@ TTestStartUpParameter = class(TTestCase) FVerifyTrackerResult: TVerifyTrackerResult; FExitCode: integer; FCommandLine: string; + FConsoleLogData: TConsoleLogData; - procedure TestParameter(TrackerListOrder: TTrackerListOrder); + function ReadConsoleLogFile: boolean; + procedure TestParameter(const StartupParameter: TStartupParameter); procedure DownloadPreTestTrackerList; procedure LoadTrackerListAddAndRemoved; procedure CallExecutableFile; procedure CopyTrackerEndResultToVerifyTrackerResult; - procedure CreateEmptyTorrent(TrackerListOrder: TTrackerListOrder); + procedure CreateEmptyTorrent(const StartupParameter: TStartupParameter); procedure TestEmptyTorrentResult; - procedure CreateFilledTorrent(TrackerListOrder: TTrackerListOrder); + procedure CreateFilledTorrent(const StartupParameter: TStartupParameter); procedure DownloadNewTrackonTrackers; procedure Test_Paramater_Ux(TrackerListOrder: TTrackerListOrder); + procedure Add_One_URL(const StartupParameter: TStartupParameter; + const tracker_URL: string; TestMustBeSuccess: boolean); protected procedure SetUp; override; procedure TearDown; override; published - procedure Test_Create_Empty_DHT_torrent; - procedure Test_Create_Simple_Filled_torrent; - procedure Test_Create_Filled_And_Empty_Torrent_All_Mode; + procedure Test_Parameter_TEST_SSL; + procedure Test_Tracker_UserInput_All_Different_URL; + procedure Test_Tracker_UserInput_All_Different_URL_And_SAC; + procedure Test_Create_Empty_Torrent_And_Then_Filled_It_All_List_Order_Mode; procedure Test_Paramater_U0; procedure Test_Paramater_U1; @@ -92,10 +103,9 @@ implementation PROGRAME_TO_BE_TESTED_NAME = 'trackereditor'; TORRENT_FOLDER = 'test_torrent'; END_USER_FOLDER = 'enduser'; - FILE_NAME_CONSOLE_LOG = 'console_log.txt'; - FILE_NAME_EXPORT_TRACKERS = 'export_trackers.txt'; - FILE_NAME_ADD_TRACKERS = 'add_trackers.txt'; - FILE_NAME_REMOVE_TRACKERS = 'remove_trackers.txt'; + + //there are 5 test torrent files in 'test_torrent' folder. + TEST_TORRENT_FILES_COUNT = 5; procedure TTestStartUpParameter.Test_Paramater_U0; begin @@ -139,12 +149,33 @@ procedure TTestStartUpParameter.Test_Paramater_U7; Test_Paramater_Ux(tloRandomize); end; -procedure TTestStartUpParameter.TestParameter(TrackerListOrder: TTrackerListOrder); +procedure TTestStartUpParameter.Test_Parameter_TEST_SSL; begin - FVerifyTrackerResult.TrackerListOrder := TrackerListOrder; + // Check if SSL connection is working. + FCommandLine := '-TEST_SSL'; + CallExecutableFile; + // Exit code should be zero + CheckEquals(0, FExitCode); +end; + +//procedure TTestStartUpParameter.TestParameter(TrackerListOrder: TTrackerListOrder); +procedure TTestStartUpParameter.TestParameter(const StartupParameter: TStartupParameter); +begin + FVerifyTrackerResult.StartupParameter := StartupParameter; //Fill in the command line parameter '-Ux', x = number - FCommandLine := format('%s -U%d', [FFullPathToTorrent, Ord(TrackerListOrder)]); + FCommandLine := format('%s -U%d', [FFullPathToTorrent, + Ord(StartupParameter.TrackerListOrder)]); + + if StartupParameter.SkipAnnounceCheck then + begin + FCommandLine := FCommandLine + ' -SAC'; + end; + + if StartupParameter.SourcePresent then + begin + FCommandLine := FCommandLine + ' -SOURCE ' + StartupParameter.SourceText; + end; end; procedure TTestStartUpParameter.DownloadPreTestTrackerList; @@ -188,7 +219,7 @@ procedure TTestStartUpParameter.CopyTrackerEndResultToVerifyTrackerResult; end; procedure TTestStartUpParameter.CreateEmptyTorrent( - TrackerListOrder: TTrackerListOrder); + const StartupParameter: TStartupParameter); begin //write a empty remove trackers FVerifyTrackerResult.TrackerRemoved.Clear; @@ -201,7 +232,7 @@ procedure TTestStartUpParameter.CreateEmptyTorrent( FILE_NAME_ADD_TRACKERS); //Generate the command line parameter - TestParameter(TrackerListOrder); + TestParameter(StartupParameter); //call the tracker editor exe file CallExecutableFile; @@ -216,7 +247,8 @@ procedure TTestStartUpParameter.TestEmptyTorrentResult; 'The tracker list should be empty.'); end; -procedure TTestStartUpParameter.CreateFilledTorrent(TrackerListOrder: TTrackerListOrder); +procedure TTestStartUpParameter.CreateFilledTorrent( + const StartupParameter: TStartupParameter); begin DownloadNewTrackonTrackers; @@ -227,7 +259,7 @@ procedure TTestStartUpParameter.CreateFilledTorrent(TrackerListOrder: TTrackerLi //write a some trackers to add. This FVerifyTrackerResult.TrackerAdded.Clear; - FVerifyTrackerResult.TrackerAdded.Add('udp://1.test'); + FVerifyTrackerResult.TrackerAdded.Add('udp://1.test/announce'); //Add one torrent that later will be removed if FNewTrackon.TrackerList_Dead.Count > 0 then @@ -241,12 +273,12 @@ procedure TTestStartUpParameter.CreateFilledTorrent(TrackerListOrder: TTrackerLi FVerifyTrackerResult.TrackerAdded.Add(FNewTrackon.TrackerList_Live[0]); end; - FVerifyTrackerResult.TrackerAdded.Add('udp://2.test'); + FVerifyTrackerResult.TrackerAdded.Add('udp://2.test/announce'); FVerifyTrackerResult.TrackerAdded.SaveToFile(FFullPathToEndUser + FILE_NAME_ADD_TRACKERS); //Generate the command line parameter - TestParameter(TrackerListOrder); + TestParameter(StartupParameter); //call the tracker editor exe file CallExecutableFile; @@ -265,17 +297,25 @@ procedure TTestStartUpParameter.CreateFilledTorrent(TrackerListOrder: TTrackerLi procedure TTestStartUpParameter.DownloadNewTrackonTrackers; begin - Check(FNewTrackon.DownloadTrackers, 'Download Newtrackon failed'); + //download only one time + if FNewTrackon.TrackerList_All.Count = 0 then + begin + Check(FNewTrackon.DownloadEverything, 'Download Newtrackon failed'); + end; end; procedure TTestStartUpParameter.Test_Paramater_Ux(TrackerListOrder: TTrackerListOrder); var OK: boolean; + StartupParameter: TStartupParameter; begin //Create a torrent with fix torrent items //this is the pre test condition - CreateFilledTorrent(tloInsertNewBeforeAndKeepNewIntact); + StartupParameter.TrackerListOrder := tloInsertNewBeforeAndKeepNewIntact; + StartupParameter.SkipAnnounceCheck := False; + StartupParameter.SourcePresent := False; + CreateFilledTorrent(StartupParameter); //Download all the trackers .txt files DownloadPreTestTrackerList; @@ -284,7 +324,8 @@ procedure TTestStartUpParameter.Test_Paramater_Ux(TrackerListOrder: TTrackerList LoadTrackerListAddAndRemoved; //Generate the command line parameter - TestParameter(TrackerListOrder); + StartupParameter.TrackerListOrder := TrackerListOrder; + TestParameter(StartupParameter); //call the tracker editor exe file CallExecutableFile; @@ -298,43 +339,221 @@ procedure TTestStartUpParameter.Test_Paramater_Ux(TrackerListOrder: TTrackerList //check the exit code CheckEquals(0, FExitCode); + + //Check the logdata status + Check(ReadConsoleLogFile, 'Log data is not present'); + Check(FConsoleLogData.StatusOK); + Check(FConsoleLogData.TrackersCount > 0); + Check(FConsoleLogData.TorrentFilesCount = TEST_TORRENT_FILES_COUNT); +end; + +procedure TTestStartUpParameter.Add_One_URL(const StartupParameter: TStartupParameter; + const tracker_URL: string; TestMustBeSuccess: boolean); +begin + //add one tracker to the 'add_trackers' + FVerifyTrackerResult.TrackerAdded.Clear; + FVerifyTrackerResult.TrackerAdded.Add(tracker_URL); + + FVerifyTrackerResult.TrackerAdded.SaveToFile(FFullPathToEndUser + + FILE_NAME_ADD_TRACKERS); + + //Generate the command line parameter + TestParameter(StartupParameter); + + //call the tracker editor exe file + CallExecutableFile; + + //Check the logdata status + Check(ReadConsoleLogFile, 'Log data is not present'); + + + if TestMustBeSuccess then + begin + //check the exit code. Must be OK + CheckEquals(0, FExitCode, tracker_URL); + + //the result must be True + CheckTrue(FConsoleLogData.StatusOK, tracker_URL); + end + else + begin + //check the exit code. Must be an error + CheckNotEquals(0, FExitCode, tracker_URL); + + //the result must be false + CheckFalse(FConsoleLogData.StatusOK, tracker_URL); + end; + end; -procedure TTestStartUpParameter.Test_Create_Empty_DHT_torrent; + +procedure TTestStartUpParameter.Test_Tracker_UserInput_All_Different_URL; +var + TrackerListOrder: TTrackerListOrder; + TrackerURL: string; + StartupParameter: TStartupParameter; +const + ANNOUNCE = '/announce'; + ANNOUNCE_PHP = '/announce.php'; begin - CreateEmptyTorrent(tloInsertNewBeforeAndKeepNewIntact); - TestEmptyTorrentResult; + StartupParameter.SkipAnnounceCheck := False; + StartupParameter.SourcePresent := False; + + //Test if all the tracker update mode is working + for TrackerListOrder in TTrackerListOrder do + begin + StartupParameter.TrackerListOrder := TrackerListOrder; + + TrackerURL := 'udp://test.com'; + Add_One_URL(StartupParameter, TrackerURL, False); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE_PHP, True); + + TrackerURL := 'http://test.com'; + Add_One_URL(StartupParameter, TrackerURL, False); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE_PHP, True); + + TrackerURL := 'https://test.com'; + Add_One_URL(StartupParameter, TrackerURL, False); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE_PHP, True); + + //webtorrent may have NOT announce + TrackerURL := 'ws://test.com'; + Add_One_URL(StartupParameter, TrackerURL, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE, True); + + TrackerURL := 'wss://test.com'; + Add_One_URL(StartupParameter, TrackerURL, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE, True); + end; end; -procedure TTestStartUpParameter.Test_Create_Simple_Filled_torrent; +procedure TTestStartUpParameter.Test_Tracker_UserInput_All_Different_URL_And_SAC; +var + TrackerListOrder: TTrackerListOrder; + TrackerURL: string; + StartupParameter: TStartupParameter; +const + ANNOUNCE = '/announce'; + ANNOUNCE_PHP = '/announce.php'; + ANNOUNCE_private = '/announce?abcd'; + + procedure TestStartUpParameter; + begin + Add_One_URL(StartupParameter, TrackerURL, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE_PHP, True); + Add_One_URL(StartupParameter, TrackerURL + ANNOUNCE_private, True); + end; + + procedure TestAllURL; + begin + //Test if all the tracker update mode is working + for TrackerListOrder in TTrackerListOrder do + begin + StartupParameter.TrackerListOrder := TrackerListOrder; + + TrackerURL := 'udp://test.com'; + TestStartUpParameter; + + TrackerURL := 'http://test.com'; + TestStartUpParameter; + + TrackerURL := 'https://test.com'; + TestStartUpParameter; + + TrackerURL := 'ws://test.com'; + TestStartUpParameter; + + TrackerURL := 'wss://test.com'; + TestStartUpParameter; + end; + end; + begin - CreateFilledTorrent(tloInsertNewBeforeAndKeepNewIntact); + // Skip announce check. + StartupParameter.SkipAnnounceCheck := True; + StartupParameter.SourcePresent := False; + TestAllURL; + + // Skip announce check and add SOURCE + StartupParameter.SkipAnnounceCheck := True; + StartupParameter.SourcePresent := True; + StartupParameter.SourceText := 'ABCDE'; + TestAllURL; end; -procedure TTestStartUpParameter.Test_Create_Filled_And_Empty_Torrent_All_Mode; +procedure TTestStartUpParameter. +Test_Create_Empty_Torrent_And_Then_Filled_It_All_List_Order_Mode; var TrackerListOrder: TTrackerListOrder; + StartupParameter: TStartupParameter; begin //Test if all the mode that support empty torrent creation + //Empty torrent -> filled torrent -> empty torrent + + StartupParameter.SkipAnnounceCheck := False; + StartupParameter.SourcePresent := False; + for TrackerListOrder in TTrackerListOrder do begin + StartupParameter.TrackerListOrder := TrackerListOrder; - //todo: Is this by design that empty torrent is here rejected? + //It is by design that it can not create a empty torrent + //The 'KeepOriginalIntactAndRemoveNothing' prevent it from deleting the torrent + //must skip the test for this one if TrackerListOrder = tloInsertNewBeforeAndKeepOriginalIntactAndRemoveNothing then continue; - //todo: Is this by design that empty torrent is here rejected? + //It is by design that it can not create a empty torrent + //The 'KeepOriginalIntactAndRemoveNothing' prevent it from deleting the torrent + //must skip the test for this one if TrackerListOrder = tloAppendNewAfterAndKeepOriginalIntactAndRemoveNothing then continue; - CreateEmptyTorrent(TrackerListOrder); + //Create empty the torrent + CreateEmptyTorrent(StartupParameter); + //check the exit code + CheckEquals(0, FExitCode); + + TestEmptyTorrentResult; + //Check the logdata status + Check(ReadConsoleLogFile, 'Log data is not present'); + Check(FConsoleLogData.StatusOK); + Check(FConsoleLogData.TrackersCount = 0); + Check(FConsoleLogData.TorrentFilesCount = TEST_TORRENT_FILES_COUNT); + + //fill the empty torrent with data + CreateFilledTorrent(StartupParameter); + //check the exit code + CheckEquals(0, FExitCode); + + //Check the logdata status + Check(ReadConsoleLogFile, 'Log data is not present'); + Check(FConsoleLogData.StatusOK); + Check(FConsoleLogData.TrackersCount > 0); + Check(FConsoleLogData.TorrentFilesCount = TEST_TORRENT_FILES_COUNT); + + //Create empty the torrent again + CreateEmptyTorrent(StartupParameter); + //check the exit code + CheckEquals(0, FExitCode); + TestEmptyTorrentResult; - CreateFilledTorrent(TrackerListOrder); + //Check the log data status + Check(ReadConsoleLogFile, 'Log data is not present'); + Check(FConsoleLogData.StatusOK); + Check(FConsoleLogData.TrackersCount = 0); + Check(FConsoleLogData.TorrentFilesCount = TEST_TORRENT_FILES_COUNT); + end; end; procedure TTestStartUpParameter.SetUp; begin + WriteLn('TTestStartUpParameter.SetUp'); //Create all the TStringList items FVerifyTrackerResult.TrackerOriginal := TStringList.Create; FVerifyTrackerResult.TrackerAdded := TStringList.Create; @@ -367,6 +586,7 @@ procedure TTestStartUpParameter.SetUp; procedure TTestStartUpParameter.TearDown; begin + WriteLn('TTestStartUpParameter.TearDown'); //Free the TStringList items FVerifyTrackerResult.TrackerOriginal.Free; FVerifyTrackerResult.TrackerAdded.Free; @@ -378,8 +598,19 @@ procedure TTestStartUpParameter.TearDown; FNewTrackon.Free; end; +function TTestStartUpParameter.ReadConsoleLogFile: boolean; +begin + Result := LoadConsoleLog(FFullPathToEndUser + FILE_NAME_CONSOLE_LOG, + FConsoleLogData.StatusOK, FConsoleLogData.TorrentFilesCount, + FConsoleLogData.TrackersCount); +end; initialization - + // macOS version does not support startup parameter +{$IFNDEF CPUAARCH64} +{$IFNDEF DARWIN} RegisterTest(TTestStartUpParameter); +{$ENDIF} +{$ENDIF} + end. diff --git a/submodule/dcpcrypt b/submodule/dcpcrypt new file mode 160000 index 0000000..14586ed --- /dev/null +++ b/submodule/dcpcrypt @@ -0,0 +1 @@ +Subproject commit 14586ed66d15fd91530ed5dfaab8a8e4bb8959ff diff --git a/test_torrent/bittorrent-v2-hybrid-test.torrent b/test_torrent/bittorrent-v2-hybrid-test.torrent new file mode 100644 index 0000000..9a5c876 Binary files /dev/null and b/test_torrent/bittorrent-v2-hybrid-test.torrent differ diff --git a/test_torrent/bittorrent-v2-test.torrent b/test_torrent/bittorrent-v2-test.torrent new file mode 100644 index 0000000..8ad4c7e Binary files /dev/null and b/test_torrent/bittorrent-v2-test.torrent differ diff --git a/travis-lazarus b/travis-lazarus deleted file mode 160000 index dff47db..0000000 --- a/travis-lazarus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dff47db423d9f3170cf50843d05077dfbf2de863