diff --git a/.github/ISSUE_TEMPLATE/01_bug_report.yml b/.github/ISSUE_TEMPLATE/01_bug_report.yml
new file mode 100644
index 000000000..e9670e9de
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/01_bug_report.yml
@@ -0,0 +1,85 @@
+name: Bug Report
+description: 'Any bug about core nzbget, EXCLUDING extensions, performance issues or compiling nzbget.'
+labels: ['bug']
+body:
+- type: checkboxes
+ attributes:
+ label: Is there already an issue for your problem?
+ description: Please make sure you are not creating an already submitted Issue. Check closed issues as well, because your issue may have already been fixed.
+ options:
+ - label: I have checked older issues, open and closed
+ required: true
+- type: dropdown
+ attributes:
+ label: NZBGet Version
+ description: Which version of NZBGet has this bug?
+ options:
+ - v23-stable
+ - v23-testing
+ - v22-stable (nzbgetcom takeover)
+ - v22-testing (nzbgetcom takeover)
+ - v21 or earlier (orignal nzbget)
+ validations:
+ required: true
+- type: dropdown
+ attributes:
+ label: Platform
+ description: Select a specific platform for this bug report, choose All if it applies to all platforms
+ options:
+ - All
+ - Windows
+ - macOS
+ - NAS/Synology/QNAP
+ - Linux/Docker
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Environment
+ description: Please provide the details of the system NZBGet is running on. The more information you can provide, the better
+ placeholder: |
+ Device:
+ OS version: platform n.nn (32bit/64bit)
+ CPU architecture: (32bit/64bit)
+ Includes libs or tool that apply: 7zip x.x, unrar x.x, python3.11
+ Running in Docker: No/Yes
+ render: markdown
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Current Behavior
+ description: A concise description of what you're experiencing.
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. In this environment...
+ 2. With this config...
+ 3. Run '...'
+ 4. See error...
+ validations:
+ required: false
+- type: textarea
+ attributes:
+ label: Logs
+ description: |
+ Any logs or messages that apply
+ validations:
+ required: false
+- type: textarea
+ attributes:
+ label: Extra information
+ description: |
+ Please refer to any extra information - extra documentation, attach files or images
+ validations:
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/02_feature_request.yml b/.github/ISSUE_TEMPLATE/02_feature_request.yml
new file mode 100644
index 000000000..903c175ca
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/02_feature_request.yml
@@ -0,0 +1,36 @@
+name: Feature Request
+description: 'Feature requests or small enhancements'
+labels: ['enhancement']
+body:
+- type: checkboxes
+ attributes:
+ label: Is there already an issue for this request?
+ description: Please make sure you are not creating an already submitted Issue. Check closed issues as well, because your issue could have been resolved and waiting to be released.
+ options:
+ - label: I have checked older issues, open and closed
+ required: true
+- type: dropdown
+ attributes:
+ label: Platform
+ description: Select a specific platform for this request, choose All if it applies to all platforms
+ options:
+ - All
+ - Windows
+ - macOS
+ - NAS/Synology/QNAP
+ - Linux/Docker
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Describe the enhancement you'd like
+ description: A clear and concise description of what you want to happen.
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Extra information
+ description: |
+ Please refer to any extra information - competing product(s) and/or features, extra documentation, attach files or images
+ validations:
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/03_build.yml b/.github/ISSUE_TEMPLATE/03_build.yml
new file mode 100644
index 000000000..73f9f918a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/03_build.yml
@@ -0,0 +1,71 @@
+name: Building, compiling, contributing
+description: 'Problems building nzbget or questions about contributing'
+labels: ['question']
+body:
+- type: dropdown
+ attributes:
+ label: NZBGet Version
+ description: Version of NZBGet for the scope of this issue
+ options:
+ - v23-stable
+ - v23-testing
+ - v22-stable (nzbgetcom takeover)
+ - v22-testing (nzbgetcom takeover)
+ - v21 or earlier (orignal nzbget)
+ validations:
+ required: true
+- type: dropdown
+ attributes:
+ label: Platform
+ description: Select a specific platform for this issue, choose All if it applies to all platforms
+ options:
+ - All
+ - Windows
+ - macOS
+ - NAS/Synology/QNAP
+ - Linux/Docker
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Environment
+ description: Please provide the details of the system where the issue is observed
+ placeholder: |
+ Device:
+ OS version: platform n.nn (32bit/64bit)
+ CPU architecture: (32bit/64bit)
+ Includes libs or tool that apply: 7zip x.x, unrar x.x, python3.11
+ render: markdown
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Problem or Question
+ description: Describe the problem, expected behavior
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. In this environment...
+ 2. With this config...
+ 3. Run '...'
+ 4. See error...
+ validations:
+ required: false
+- type: textarea
+ attributes:
+ label: Logs
+ description: |
+ Any logs or messages that apply
+ validations:
+ required: false
+- type: textarea
+ attributes:
+ label: Extra information
+ description: |
+ Please refer to any extra information - extra documentation, attach files or images
+ validations:
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/04_extension.yml b/.github/ISSUE_TEMPLATE/04_extension.yml
new file mode 100644
index 000000000..31437b8d5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/04_extension.yml
@@ -0,0 +1,25 @@
+# this flow should definitely be impoved with a form - separate suggestions,
+# extensions master list and actual extensions questions, TBD
+
+name: Extension
+description: 'Questions, bugs, requests related to Extensions'
+labels: ['extension']
+body:
+- type: checkboxes
+ attributes:
+ label: Is there already an issue for this request?
+ description: Please make sure you are not creating an already submitted Issue. Check closed issues as well, because your issue could have been resolved and waiting to be released.
+ options:
+ - label: I have checked older issues, open and closed
+ required: true
+- type: textarea
+ attributes:
+ label: Describe your issue
+ description: Describe the issue with the extension. Provide as much information as possible
+ placeholder: |
+ Existing extension - details about environment - version of nzbget - version of extension - related libs or tools if any.
+ Please provide as much useful information as possible
+ ..
+ Or suggest new extension to be supported with Extension Manager
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..33f5c5936
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: true
+contact_links:
+ - name: Discussions
+ url: https://github.com/nzbgetcom/nzbget/discussions
+ about: Discuss, suggest. Any questions, including performance issues.
+ - name: NZBGet.com Website
+ url: https://nzbget.com/contact/
+ about: Contact Us via Email
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e628e6177..a9895bf94 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,6 +16,8 @@ jobs:
build-osx:
uses: ./.github/workflows/osx.yml
+ permissions:
+ actions: write
build-synology:
uses: ./.github/workflows/synology.yml
@@ -23,6 +25,14 @@ jobs:
build-qnap:
uses: ./.github/workflows/qnap.yml
+ build-linux-pkg:
+ uses: ./.github/workflows/linux-pkg.yml
+ with:
+ external_call: true
+ needs: [build-linux]
+ permissions:
+ actions: write
+
repack-qnap:
uses: ./.github/workflows/qnap-repack.yml
with:
@@ -35,7 +45,7 @@ jobs:
env:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
runs-on: ubuntu-latest
- needs: [build-windows, build-linux, build-osx, build-synology, build-qnap, repack-qnap]
+ needs: [build-windows, build-linux, build-osx, build-synology, build-qnap, repack-qnap, build-linux-pkg]
permissions:
actions: write
steps:
@@ -52,6 +62,8 @@ jobs:
mv nzbget-synology-packages/* builds || true
mv nzbget-qnap-packages/* builds || true
mv nzbget-qnap-native-packages/* builds || true
+ mv nzbget-deb-packages/* builds || true
+ mv nzbget-rpm-packages/* builds || true
cd builds
VERSION=$(ls | grep bin-windows-setup | cut -d - -f 2)
if [ "$GITHUB_REF_NAME" != "main" ]; then VERSION="$VERSION-testing"; fi
@@ -63,7 +75,7 @@ jobs:
echo "nzbget_signatures({" | tee $SIGS_FILE
echo | tee -a $SIGS_FILE
- for FILE in *.exe *.run *.zip *.spk *.qpkg; do
+ for FILE in *.exe *.run *.zip *.spk *.qpkg *.deb *.rpm; do
[ -f $FILE ] || continue
MD5=$(openssl dgst -md5 $FILE | cut -d ' ' -f 2)
@@ -103,8 +115,15 @@ jobs:
nzbget-qnap-packages
nzbget-qnap-native-packages
+ - name: Delete unneded linux packages artifacts
+ uses: geekyeggo/delete-artifact@v4
+ with:
+ name: |
+ nzbget-deb-packages
+ nzbget-rpm-packages
+
make-testing-release:
- runs-on: [self-hosted, linux]
+ runs-on: [self-hosted, nzbget-linux]
needs: [generate-signatures]
permissions:
contents: write
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6092c0ab0..0b71377ff 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -54,7 +54,11 @@ jobs:
if [[ "$TAG" == "" ]]; then
TAG="${GITHUB_REF_NAME/\//-}"
fi
- TAGS="${{ env.REGISTRY_IMAGE }}:$TAG,ghcr.io/${{ env.REGISTRY_IMAGE }}:$TAG"
+ if [[ "$GITHUB_REF_NAME" == "develop" ]] || [[ "$GITHUB_REF_NAME" == "main" ]] || [[ $GITHUB_REF == 'refs/tags/'* ]]; then
+ TAGS="${{ env.REGISTRY_IMAGE }}:$TAG,ghcr.io/${{ env.REGISTRY_IMAGE }}:$TAG"
+ else
+ TAGS="${{ env.REGISTRY_IMAGE }}:$TAG"
+ fi
echo "tags=$TAGS" >> $GITHUB_OUTPUT
echo "version=$TAG" >> $GITHUB_OUTPUT
@@ -72,7 +76,8 @@ jobs:
"MAKE_JOBS=2"
- name: Update Docker Hub Description
- uses: peter-evans/dockerhub-description@v3
+ if: github.ref_name == 'main'
+ uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
diff --git a/.github/workflows/linux-pkg.yml b/.github/workflows/linux-pkg.yml
new file mode 100644
index 000000000..f91c68da3
--- /dev/null
+++ b/.github/workflows/linux-pkg.yml
@@ -0,0 +1,59 @@
+name: linux packages
+
+on:
+ workflow_call:
+ inputs:
+ external_call:
+ description: 'To distinguish workflow_call from regular push / workflow_dispatch'
+ type: boolean
+ required: false
+ default: false
+ workflow_dispatch:
+
+jobs:
+ build-linux:
+ uses: ./.github/workflows/linux.yml
+ if: ${{ inputs.external_call == false }}
+
+ build-pkg:
+ runs-on: [self-hosted, nzbget-linux]
+ needs: [build-linux]
+ if: always()
+ permissions:
+ actions: write
+
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Download build artifacts
+ uses: actions/download-artifact@v4
+
+ - name: Build DEB and RPM packages
+ run: |
+ sudo apt-get update && sudo apt-get install rpm -y
+ bash linux/pkg/build-pkg.sh
+
+ - name: Upload DEB build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: nzbget-deb-packages
+ path: build/deb/*.deb
+ retention-days: 5
+
+ - name: Upload RPM build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: nzbget-rpm-packages
+ path: build/rpm/*.rpm
+ retention-days: 5
+
+ - name: Delete unneded linux artifacts
+ if: ${{ inputs.external_call == false }}
+ uses: geekyeggo/delete-artifact@v4
+ with:
+ name: |
+ nzbget-linux-installers
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 9e618da22..5e6e9989e 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -6,7 +6,7 @@ on:
jobs:
build:
- runs-on: [self-hosted, linux]
+ runs-on: [self-hosted, nzbget-linux]
steps:
@@ -29,7 +29,12 @@ jobs:
rm -rf /build/output
cp -r . /build/nzbget
cd /build
- docker run -e ALL_ARCHS="i686 x86_64 aarch64 armhf armel mipseb mipsel ppc6xx ppc500" -v /build:/build nzbget-build /build/scripts/build-nzbget-ci.sh
+ if [ "$GITHUB_REF_NAME" == "develop" ] || [ "$GITHUB_REF_NAME" == "main" ]; then
+ DEBUG=yes
+ else
+ DEBUG=no
+ fi
+ ALL_ARCHS="i686 x86_64 aarch64 armhf armel mipseb mipsel ppc6xx ppc500 riscv64" DEBUG=$DEBUG /build/scripts/build-nzbget-ci.sh
- name: Rename build artifacts
if: github.ref_name != 'main' && github.ref_name != 'develop'
diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml
index f4f1c4909..87ff84e83 100644
--- a/.github/workflows/osx.yml
+++ b/.github/workflows/osx.yml
@@ -5,8 +5,44 @@ on:
workflow_dispatch:
jobs:
- build:
- runs-on: [self-hosted, macos]
+ build-x64:
+ runs-on: [self-hosted, macos, x64]
+
+ steps:
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Build
+ run: |
+ if [ "$GITHUB_REF_NAME" != "main" ]; then
+ bash osx/build-nzbget-x64.sh testing
+ else
+ bash osx/build-nzbget-x64.sh
+ fi
+
+ - name: Rename build artifacts
+ if: github.ref_name != 'main' && github.ref_name != 'develop'
+ run: |
+ cd build
+ SUFFIX="${GITHUB_REF_NAME/\//-}"
+ for FILE in *.zip; do
+ [ -f $FILE ] || continue
+ NEW_FILE=${FILE/-bin-macos-x64.zip/-$SUFFIX-bin-macos-x64.zip}
+ mv $FILE $NEW_FILE
+ done
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: nzbget-osx-installers-x64
+ path: build/*.zip
+ retention-days: 5
+
+ build-universal:
+ runs-on: [self-hosted, macos, arm64]
steps:
@@ -25,7 +61,7 @@ jobs:
- name: Build
run: |
- bash osx/build-nzbget.sh
+ bash osx/build-nzbget-universal.sh
- name: Rename build artifacts
if: github.ref_name != 'main' && github.ref_name != 'develop'
@@ -41,6 +77,36 @@ jobs:
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
- name: nzbget-osx-installers
+ name: nzbget-osx-installers-universal
path: osx/build/Release/*.zip
retention-days: 5
+
+ combine-osx-artifacts:
+ runs-on: ubuntu-latest
+ needs: [build-x64, build-universal]
+ permissions:
+ actions: write
+ steps:
+
+ - name: Download build artifacts
+ uses: actions/download-artifact@v4
+
+ - name: Combine artifacts
+ run: |
+ mkdir -p nzbget-osx-installers
+ mv nzbget-osx-installers-x64/* nzbget-osx-installers
+ mv nzbget-osx-installers-universal/* nzbget-osx-installers
+
+ - name: Upload build artifacts with signatures
+ uses: actions/upload-artifact@v4
+ with:
+ name: nzbget-osx-installers
+ path: nzbget-osx-installers/*
+ retention-days: 5
+
+ - name: Delete unneded artifacts
+ uses: geekyeggo/delete-artifact@v4
+ with:
+ name: |
+ nzbget-osx-installers-x64
+ nzbget-osx-installers-universal
diff --git a/.github/workflows/qnap-repack.yml b/.github/workflows/qnap-repack.yml
index a628e9fd2..091385628 100644
--- a/.github/workflows/qnap-repack.yml
+++ b/.github/workflows/qnap-repack.yml
@@ -16,7 +16,7 @@ jobs:
if: ${{ inputs.external_call == false }}
repack:
- runs-on: [self-hosted, linux]
+ runs-on: [self-hosted, nzbget-qnap]
needs: [build-linux]
if: always()
permissions:
diff --git a/.github/workflows/qnap.yml b/.github/workflows/qnap.yml
index 896794598..258fb7029 100644
--- a/.github/workflows/qnap.yml
+++ b/.github/workflows/qnap.yml
@@ -6,7 +6,7 @@ on:
jobs:
build:
- runs-on: [self-hosted, linux]
+ runs-on: [self-hosted, nzbget-qnap]
steps:
diff --git a/.github/workflows/synology.yml b/.github/workflows/synology.yml
index 78b98b08e..1f996f3f3 100644
--- a/.github/workflows/synology.yml
+++ b/.github/workflows/synology.yml
@@ -6,7 +6,7 @@ on:
jobs:
build:
- runs-on: [self-hosted, linux]
+ runs-on: [self-hosted, nzbget-synology]
steps:
diff --git a/.github/workflows/windows-tests.yml b/.github/workflows/windows-tests.yml
index a449de7ed..8fed2240d 100644
--- a/.github/workflows/windows-tests.yml
+++ b/.github/workflows/windows-tests.yml
@@ -31,7 +31,7 @@ jobs:
cmake --version
mkdir build
cd build
- cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static
+ cmake .. -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBUILD_ONLY_TESTS=ON
cmake --build . --config Release -j 2
- name: Test
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 97b91e6d9..1a755298c 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -13,33 +13,29 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - name: Change version for non-release
- if: github.ref_name != 'main'
- run: |
- $Version = ((((Select-String -Path nzbget.vcxproj -Pattern ";VERSION=")[0] -split(';'))[2] -split('='))[1]) -replace '"', ''
- $Date=Get-Date -Format "yyyyMMdd"
- $NewVersion = "$Version-testing-$Date"
- (Get-Content nzbget.vcxproj) | ForEach-Object {$_ -replace "VERSION=`"$Version`"", "VERSION=`"$NewVersion`""} | Set-Content nzbget.vcxproj
- (Get-Content windows\nzbget-setup.nsi) | ForEach-Object {$_ -replace "`"DisplayVersion`" `"$Version`"", "`"DisplayVersion`" `"$NewVersion`""} | Set-Content windows\nzbget-setup.nsi
- "NEW_VERSION=$NewVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
-
- name: Build
run: |
- .\windows\build-nzbget-vs22.bat
+ $BuildParams="-BuildRelease -Build32 -Build64 -BuildSetup"
+ If (-not ($env:GITHUB_REF_NAME -eq "main")) {
+ $BuildParams="$BuildParams -BuildTesting"
+ }
+ If (($env:GITHUB_REF_NAME -eq "main") -or ($env:GITHUB_REF_NAME -eq "develop")) {
+ $BuildParams="$BuildParams -BuildDebug"
+ }
+ Invoke-Expression "windows\build-nzbget.ps1 $BuildParams"
- name: Rename build artifacts
if: github.ref_name != 'main' && github.ref_name != 'develop'
run: |
- $Output="c:\nzbget\build\output"
- $NewVersion=$env:NEW_VERSION
+ $Output="build"
$Suffix = $env:GITHUB_REF_NAME.Replace("/","-")
ForEach ($File In Get-ChildItem -Path $Output -Filter "*.exe") {
- Rename-Item -Path "$Output\$($File.Name)" -NewName $File.Name.Replace($NewVersion, "$NewVersion-$Suffix")
+ Rename-Item -Path "$Output\$($File.Name)" -NewName $File.Name.Replace("-bin-windows-setup.exe", "-$Suffix-bin-windows-setup.exe")
}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: nzbget-windows-installers
- path: C:\nzbget\build\output\*.exe
+ path: build\*.exe
retention-days: 5
diff --git a/.gitignore b/.gitignore
index a5d1eec1d..f10cec75f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,7 +26,6 @@
# GNU Autotools
.deps/
config.h
-config.h.in
config.h.in~
configure
configure~
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f7fed97c3..f5a5d1da3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,57 +1,129 @@
-cmake_minimum_required(VERSION 3.22)
+cmake_minimum_required(VERSION 3.13)
-set(VERSION "23.0")
-
-project(
- nzbget
- VERSION ${VERSION}
- DESCRIPTION "NZBGet is a binary downloader, which downloads files from Usenet"
- LANGUAGES C CXX
-)
+if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
+ message(FATAL_ERROR "In-source builds are not allowed. You should create separate directory for build files.")
+endif()
-option(ENABLE_TESTS "Enable tests" ON)
+set_property(GLOBAL PROPERTY PACKAGE)
+set_property(GLOBAL PROPERTY LIBS)
+set_property(GLOBAL PROPERTY INCLUDES)
+set(VERSION "24.0")
set(PACKAGE "nzbget")
-add_compile_definitions(HAVE_CONFIG_H=1)
-
-configure_file(
- ${CMAKE_SOURCE_DIR}/cmake_config.h.in
- ${CMAKE_BINARY_DIR}/config.h
-)
-
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_C_STANDARD 17)
+set(PACKAGE_BUGREPORT "https://github.com/nzbgetcom/nzbget/issues")
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_EXTENSIONS OFF)
-set(BUILD_SHARED_LIBS OFF)
-set(OPENSSL_USE_STATIC_LIBS ON)
-set(ZLIB_USE_STATIC_LIBS ON)
-set(Boost_USE_STATIC_LIBS ON)
-set(Boost_USE_MULTITHREADED ON)
-set(Boost_USE_STATIC_RUNTIME OFF)
-set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
-
-find_package(OpenSSL REQUIRED)
-find_package(ZLIB REQUIRED)
-find_package(LibXml2 REQUIRED)
-find_package(Boost REQUIRED COMPONENTS json)
-
-if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Weverything")
-elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall")
-elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2 /W4")
- set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} winmm.lib /NODEFAULTLIB:msvcrt.lib;libcmt.lib;msvcrtd.lib")
+set(CMAKE_CONFIGURATION_TYPES "Release" "Debug")
+
+string(REGEX MATCH "^([0-9]+)\\.([0-9]+)" VERSION_MATCH ${VERSION})
+set(VERSION_MAJOR ${CMAKE_MATCH_1})
+set(VERSION_MINOR ${CMAKE_MATCH_2})
+
+add_compile_definitions(HAVE_CONFIG_H=1)
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
endif()
-include_directories(lib/regex)
+option(BUILD_ONLY_TESTS "Build only tests (for CI)")
+
+project(
+ nzbget
+ VERSION ${VERSION}
+ DESCRIPTION "NZBGet is a binary downloader, which downloads files from Usenet"
+ LANGUAGES C CXX
+)
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang")
+ set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g0 -pthread -g -DDEBUG -Weverything -Wno-c++98-compat" CACHE STRING "" FORCE)
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ set(CMAKE_CXX_FLAGS "-O0 -g0 -pthread -g -DDEBUG -Wall -Wextra" CACHE STRING "" FORCE)
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_CXX_FLAGS "/Od /Zi /MTd /MP /W4 /EHs /DDEBUG /D_DEBUG /DWIN32 /wd4800 /wd4267" CACHE STRING "" FORCE)
+ set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} winmm.lib Dbghelp.lib libcpmtd.lib" CACHE STRING "" FORCE)
+ endif()
+elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang")
+ set(CMAKE_CXX_FLAGS "-O2 -g0 -pthread -DNDEBUG -Weverything -Wno-c++98-compat" CACHE STRING "" FORCE)
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s" CACHE STRING "" FORCE)
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ set(CMAKE_CXX_FLAGS "-O2 -g0 -pthread -DNDEBUG -Wall -Wextra" CACHE STRING "" FORCE)
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s" CACHE STRING "" FORCE)
+ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ set(CMAKE_CXX_FLAGS "/O2 /MT /MP /W4 /EHs /DNDEBUG /DWIN32 /wd4800 /wd4267" CACHE STRING "" FORCE)
+ set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} winmm.lib" CACHE STRING "" FORCE)
+ endif()
+endif()
+
+set(CMAKE_C_FLAGS_DEBUG ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE)
+set(CMAKE_C_FLAGS_RELEASE ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE)
+set(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE)
+set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE)
+
include_directories(${CMAKE_BINARY_DIR})
-add_subdirectory(lib)
+include_directories(${CMAKE_SOURCE_DIR})
+
+if(NOT BUILD_ONLY_TESTS)
+ include(daemon/sources.cmake)
+ add_executable(${PACKAGE} ${SRC})
+endif()
+
+if(WIN32)
+ include(cmake/windows.cmake)
+ if(NOT BUILD_ONLY_TESTS)
+ target_sources(${PACKAGE} PRIVATE ${CMAKE_SOURCE_DIR}/windows/resources/nzbget.rc)
+ configure_file(
+ ${CMAKE_SOURCE_DIR}/windows/resources/version.rc.in
+ ${CMAKE_BINARY_DIR}/version.rc
+ )
+ configure_file(
+ ${CMAKE_SOURCE_DIR}/windows/version.nsi.in
+ ${CMAKE_BINARY_DIR}/version.nsi
+ @ONLY
+ )
+ configure_file(
+ ${CMAKE_SOURCE_DIR}/windows/version-uninstall.nsi.in
+ ${CMAKE_BINARY_DIR}/version-uninstall.nsi
+ @ONLY
+ )
+ target_sources(${PACKAGE} PRIVATE ${CMAKE_BINARY_DIR}/version.rc)
+ endif()
+else()
+ include(cmake/posix.cmake)
+ if(NOT BUILD_ONLY_TESTS)
+ include(${CMAKE_SOURCE_DIR}/cmake/install.cmake)
+ endif()
+endif()
+
+configure_file(
+ ${CMAKE_SOURCE_DIR}/cmake/config.h.in
+ ${CMAKE_BINARY_DIR}/config.h
+)
+
+if(NOT BUILD_ONLY_TESTS)
+ target_link_libraries(${PACKAGE} PRIVATE ${LIBS})
+ target_include_directories(${PACKAGE} PRIVATE
+ ${CMAKE_SOURCE_DIR}/daemon/connect
+ ${CMAKE_SOURCE_DIR}/daemon/extension
+ ${CMAKE_SOURCE_DIR}/daemon/feed
+ ${CMAKE_SOURCE_DIR}/daemon/frontend
+ ${CMAKE_SOURCE_DIR}/daemon/main
+ ${CMAKE_SOURCE_DIR}/daemon/nntp
+ ${CMAKE_SOURCE_DIR}/daemon/nserv
+ ${CMAKE_SOURCE_DIR}/daemon/postprocess
+ ${CMAKE_SOURCE_DIR}/daemon/queue
+ ${CMAKE_SOURCE_DIR}/daemon/remote
+ ${CMAKE_SOURCE_DIR}/daemon/util
+ ${INCLUDES}
+ )
+endif()
-if(ENABLE_TESTS)
- include(CTest)
- add_subdirectory(tests)
+if(ENABLE_TESTS OR BUILD_ONLY_TESTS)
+ include(CTest)
+ add_subdirectory(tests)
endif()
diff --git a/ChangeLog b/ChangeLog.md
similarity index 97%
rename from ChangeLog
rename to ChangeLog.md
index 2b7bf2a2d..78edfdf2c 100644
--- a/ChangeLog
+++ b/ChangeLog.md
@@ -1,3 +1,63 @@
+nzbget-24.0
+ - Features:
+ - Dark theme and new icons
+ [#214](https://github.com/nzbgetcom/nzbget/commit/16ecaa5c3eb2efc43e965b796a4c75cdf1d959da);
+ - Added macOS x64 build support (macOS Mojave 10.14+)
+ [#194](https://github.com/nzbgetcom/nzbget/commit/90a89f8fb3b81432e192203e8a66419cfd3cd723);
+ - DEB/RPM packages support
+ [#230](https://github.com/nzbgetcom/nzbget/commit/1de712a428c031513431d0d75db7ca5ac51d3caa);
+ * [Instructions](https://nzbgetcom.github.io/).
+ - NewsServer Add UI - Default encryption and ports
+ [#225](https://github.com/nzbgetcom/nzbget/commit/cd1cab44b6052c3b02f14689fbafb65505f67a4e):
+ * moved Server.Encryption between Server.Host and Server.Port;
+ * made Server.Encryption to ON by default;
+ * made port depend on Server.Encryption value unless user has put something up:
+ - 563/443 for secure;
+ - 119/80 for unsecure.
+ - Improved error messages and help text in Extension Manager
+ [#166](https://github.com/nzbgetcom/nzbget/commit/d11ed84912fc486e6f717e56ccd973526920c0db):
+ * added 7-Zip exit codes decoder according to 7-Zip
+ [doc](https://documentation.help/7-Zip/exit_codes.htm);
+ * added a warning that SevenZipCmd may not be valid, in case of extension installation problems.
+ - Fixed stable/test release notifications
+ [#181](https://github.com/nzbgetcom/nzbget/commit/1c03b719f88ece7d67ad348f3aca0d3800ab4d54):
+ * added automatic checking for new testing releases;
+ * fixed notifications about the new stable/testing release;
+ * fixed "Uncaught ReferenceError: installedRev" error.
+
+ - Fixed and update links in webui
+ [#177](https://github.com/nzbgetcom/nzbget/commit/0cc6023bfdf7a1d22311dea7d81b8a6ea7a7d880):
+ * fixed license links;
+ * fixed links from nzbget.conf.
+
+ - For developers:
+ - Moved to CMake. Autotools and MSBuild are deprecated now and may be removed in future versions
+ [#182](https://github.com/nzbgetcom/nzbget/commit/56e4225fc73a6d1c7cdc6f647a4cac297e28e9f3):
+ * switched to CMake from autotools and MSBuild, which will simplify cross-platform development;
+ * fixed installing/uninstalling on FreeBSD and macOS via autotools/CMake;
+ * added automatic installation of Boost.Json;
+ * added support for static code analyzer Clang-Tidy.
+ - Revised and updated documentation
+ [#199](https://github.com/nzbgetcom/nzbget/commit/c93b551b1fa0afae458c1e78485c6d3eb6410514):
+ * moved CONTRIBUTING.md to docs/;
+ * split INSTALLATION.md to POSIX.md, WINDOWS.md and HOW_TO_USE.md;
+ * added Extensions docs;
+ * added an instruction on how CPPCheck can be used with CMake.
+ - Docker: support native unrar building
+ [#231](https://github.com/nzbgetcom/nzbget/commit/cc6fb8b21a0332aac81b94e466c909e06743235b).
+
+ - Bug fixes:
+ - Fixed buffer overflow in CString::AppendFmtV
+ [#208](https://github.com/nzbgetcom/nzbget/commit/c8c59277cfcf085596e124209a3b07e56ada00dc);
+ - Fixed files/dirs permissions overridden by the unpacker
+ [#229](https://github.com/nzbgetcom/nzbget/commit/4c92e423fe54a9194b84a9dcd7942865a7df72ec);
+ - Fixed dynamic addition of extension options
+ [#209](https://github.com/nzbgetcom/nzbget/commit/d49dc6dc426f71d87626820afd45348643a41458);
+ - Removed changing ownership at the beginning of container start
+ [#218](https://github.com/nzbgetcom/nzbget/commit/36dad2f793a5503871f39eca3d7dbdfa945824ee);
+ - Docker: fixed fresh install
+ [#219](https://github.com/nzbgetcom/nzbget/commit/a95269e8e15f42196a1314603fb802f3bedd4fa8).
+
nzbget-23.0
- Features:
- Extension Manager
@@ -56,7 +116,7 @@ nzbget-23.0
- "Strict" -> test failed;
- "Minimal" -> test failed;
- "None" -> test passed";
- - Bug fixies:
+ - Bug fixes:
- possible memory corruption
[#148](https://github.com/nzbgetcom/nzbget/commit/16ab25d5780c8100309c44eb60d505afe5c528db);
- For developers:
@@ -1989,7 +2049,7 @@ nzbget-11.0:
selected files of source download;
- new command in web-interface in edit download dialog
on page ;
- - new action in remote command <--edit/-E>;
+ - new action `` in remote command <--edit/-E>;
- new action in JSON-/XML-RPC method ;
- added support for manual par-check:
- if option is set to and a damaged download
@@ -2551,7 +2611,7 @@ nzbget-0.7.0:
post-processing parameters for nzb-file;
- redesigned server pool and par-checker to avoid using of semaphores
(which are very platform specific);
- - added subcommand to remote commands <--pause/-P> and <--unpause/-U> to
+ - added subcommand `` to remote commands <--pause/-P> and <--unpause/-U> to
pause/unpause the scanning of incoming nzb-directory;
- added commands and for scheduler option
;
@@ -2668,7 +2728,7 @@ nzbget-0.6.0:
the same purpose; updated example configuration file and example
postprocess-script to indicate new method of passing arguments via
environment variables;
- - added subcommands , and to command line switch <-L/--list>,
+ - added subcommands , and `` to command line switch <-L/--list>,
which prints list of files, groups or only status info respectively;
extended binary communication protocol to transfer nzb-infos in addition
to file-infos;
diff --git a/INSTALLATION.md b/INSTALLATION.md
index 2dd0fdefc..ff1d0448b 100644
--- a/INSTALLATION.md
+++ b/INSTALLATION.md
@@ -1,508 +1,8 @@
-# NZBGet installation
+## Platforms
-This is a short documentation. For more information please
-visit NZBGet home page at
- https://nzbget.com
+Articles below provide detailed information on manual compilation of NZBGet for specific platforms:
-Contents
---------
-1. About NZBGet
-2. Supported OS
-3. Prerequisites on POSIX
-4. Installation on POSIX
-5. Compiling on Windows
-6. Configuration
-7. Usage
-8. Authors
-9. Copyright
-10. Contact
-
-
-## 1. About NZBGet
-
-NZBGet is a binary newsgrabber, which downloads files from usenet
-based on information given in nzb-files. NZBGet can be used in
-standalone and in server/client modes. In standalone mode you
-pass a nzb-file as parameter in command-line, NZBGet downloads
-listed files and then exits.
-
-In server/client mode NZBGet runs as server in background.
-Then you use client to send requests to server. The sample requests
-are: download nzb-file, list files in queue, etc.
-
-There is also a built-in web-interface. The server has RPC-support
-and can be controlled from third party applications too.
-
-Standalone-tool, server and client are all contained in only one
-executable file "nzbget". The mode in which the program works
-depends on command-line parameters passed to the program.
-
-## 2. Supported OS
-
-NZBGet is written in C++ and works on Windows, OS X, Linux and
-most POSIX-conform OS'es.
-
-Clients and servers running on different OS'es may communicate with
-each other. For example, you can use NZBGet as client on Windows to
-control your NZBGet-server running on Linux.
-
-The download-section of NZBGet web-site provides binary files
-for Windows, OS X and Linux. For most POSIX-systems you need to compile
-the program yourself.
-
-If you have downloaded binaries you can just jump to section
-"Configuration".
-
-## 3. Prerequisites on POSIX
-
-NZBGet is developed on a linux-system, but it runs on other
-POSIX platforms.
-
-NZBGet absolutely needs the following libraries:
-
- - libstdc++ (usually part of compiler)
- - [libxml2](https://gitlab.gnome.org/GNOME/libxml2/-/wikis/home)
- - [Boost.JSON](https://www.boost.org/doc/libs/1_84_0/libs/json/doc/html/index.html)
- - [Boost.Optional](https://www.boost.org/doc/libs/1_84_0/libs/optional/doc/html/index.html)
-
-And the following libraries are optional:
-
- For curses-output-mode (enabled by default):
- - libcurses (usually part of commercial systems)
- or (better)
- - [libncurses](https://invisible-island.net/ncurses)
-
- For encrypted connections (TLS/SSL):
- - [OpenSSL](https://www.openssl.org)
-
- or
- - [GnuTLS](https://gnutls.org)
-
- For gzip support in web-server and web-client (enabled by default):
- - [zlib](https://www.zlib.net/)
-
- For configuration:
- - [autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html)
- - [autoconf](https://www.gnu.org/software/autoconf/)
-
- For managing package dependencies:
- - [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/)
-
- For tests:
- - [Boost.Test](https://www.boost.org/doc/libs/1_84_0/libs/test/doc/html/index.html)
-
-All these libraries are included in modern POSIX distributions and
-should be available as installable packages. Please note that you also
-need the developer packages for these libraries too, they package names
-have often suffix "dev" or "devel". On other systems you may need to
-download the libraries at the given URLs and compile them (see hints below).
-
-## 4. Installation on POSIX
-
-Installation from the source distribution archive (nzbget-VERSION.tar.gz):
-
- - untar the nzbget-source via
- tar -zxf nzbget-VERSION.tar.gz
-
- - change into nzbget-directory via
- cd nzbget-VERSION
-
- - configure it via
-
- autoreconf --install
-
- ./configure
-
- (maybe you have to tell configure, where to find some libraries.
- ./configure --help is your friend!
- also see "Configure-options" later)
-
- - in a case you don't have root access or want to install the program
- in your home directory use the configure parameter --prefix, e. g.:
-
- ./configure --prefix ~/usr
-
- - compile it via
- make
-
- - to install system wide become root via:
- su
-
- - install it via:
- make install
-
- - install configuration files into /etc via:
- make install-conf
-
- (you can skip this step if you intend to store configuration
- files in a non-standard location)
-
-### Configure-options
----------------------
-You may run configure with additional arguments:
-
- --disable-curses - to make without curses-support. Use this option
- if you can not use curses/ncurses.
-
- --disable-parcheck - to make without parcheck-support. Use this option
- if you have troubles when compiling par2-module.
-
- --with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library
- should be used for encrypted server connections.
-
- --disable-tls - to make without TLS/SSL support. Use this option if
- you can not neither OpenSSL nor GnuTLS.
-
- --disable-gzip - to make without gzip support. Use this option
- if you can not use zlib.
-
- --enable-debug - to build in debug-mode, if you want to see and log
- debug-messages.
-
-### Optional package: par-check
--------------------------------
-NZBGet can check and repair downloaded files for you. For this purpose
-it uses library par2.
-
-For your convenience the source code of libpar2 is integrated into
-NZBGet’s source tree and is compiled automatically when you make NZBGet.
-
-In a case errors occur during this process the inclusion of par2-module
-can be disabled using configure option "--disable-parcheck":
-
- ./configure --disable-parcheck
-
-### Optional package: curses
-----------------------------
-For curses-outputmode you need ncurses or curses on your system.
-If you do not have one of them you can download and compile ncurses yourself.
-Following configure-parameters may be useful:
-
- --with-libcurses-includes=/path/to/curses/includes
- --with-libcurses-libraries=/path/to/curses/libraries
-
-If you are not able to use curses or ncurses or do not want them you can
-make the program without support for curses using option "--disable-curses":
-
- ./configure --disable-curses
-
-### Optional package: TLS
--------------------------
-To enable encrypted server connections (TLS/SSL) you need to build the program
-with TLS/SSL support. NZBGet can use two libraries: OpenSSL or GnuTLS.
-Configure-script checks which library is installed and use it. If both are
-available it gives the precedence to OpenSSL. You may override that with
-the option --with-tlslib=(OpenSSL, GnuTLS). For example to build with GnuTLS:
-
- ./configure --with-tlslib= GnuTLS
-
-Following configure-parameters may be useful:
-
- --with-libtls-includess=/path/to/gnutls/includes
- --with-libtls-libraries=/path/to/gnutls/libraries
-
- --with-openssl-includess=/path/to/openssl/includes
- --with-openssl-libraries=/path/to/openssl/libraries
-
-If none of these libraries is available you can make the program without
-TLS/SSL support using option "--disable-tls":
-
- ./configure --disable-tls
-
-## 5. Compiling on Windows
-
-NZBGet is developed using MS Visual Studio 2015 (Community Edition). The project
-file is provided.
-
-To compile the program with TLS/SSL support you need either OpenSSL or GnuTLS:
- - [OpenSSL](https://www.openssl.org)
-
- or
- - [GnuTLS](https://gnutls.org/)
-
-Also required are:
- - [Regex](https://regexlib.com/)
- - [Zlib](https://gnuwin32.sourceforge.net/packages/zlib.htm)
- - [libxml2](https://gitlab.gnome.org/GNOME/libxml2/-/wikis/home)
- - [Boost.JSON](https://www.boost.org/doc/libs/1_84_0/libs/json/doc/html/index.html)
- - [Boost.Optional](https://www.boost.org/doc/libs/1_84_0/libs/optional/doc/html/index.html)
-
-For tests:
- - [Boost.Test](https://www.boost.org/doc/libs/1_84_0/libs/test/doc/html/index.html)
-
-We recommend using [vcpkg](https://vcpkg.io/) to install dependencies.
-
-## 6. Configuration
-
-NZBGet needs a configuration file.
-
-An example configuration file is provided in "nzbget.conf", which
-is installed into "/share/nzbget" (where depends on
-system configuration and configure options - typically "/usr/local",
-"/usr" or "/opt"). The installer adjusts the file according to your
-system paths. If you have performed the installation step
-"make install-conf" this file is already copied to "/etc" and
-NZBGet finds it automatically. If you install the program manually
-from a binary archive you have to copy the file from "/share/nzbget"
-to one of the locations listed below.
-
-Open the file in a text editor and modify it accodring to your needs.
-
-You need to set at least the option "MAINDIR" and one news server in
-configuration file. The file has comments on how to use each option.
-
-The program looks for configuration file in following standard
-locations (in this order):
-
-On POSIX systems:
- /nzbget.conf
- ~/.nzbget
- /etc/nzbget.conf
- /usr/etc/nzbget.conf
- /usr/local/etc/nzbget.conf
- /opt/etc/nzbget.conf
-
-On Windows:
- \nzbget.conf
-
-If you put the configuration file in other place, you can use command-
-line switch "-c " to point the program to correct location.
-
-In special cases you can run program without configuration file using
-switch "-n". You need to use switch "-o" to pass required configuration
-options via command-line.
-
-## 7. Usage
-
-NZBGet can be used in either standalone mode which downloads a single file
-or as a server which is able to queue up numerous download requests.
-
-TIP for Windows users: NZBGet is controlled via various command line
-parameters. For easier using there is a simple shell script included
-in "nzbget-shell.bat". Start this script from Windows Explorer and you will
-be running a command shell with PATH adjusted to find NZBGet executable.
-Then you can type all commands without full path to nzbget.exe.
-
-### Standalone mode:
---------------------
-
-nzbget
-
-### Server mode:
-----------------
-
-First start the nzbget-server:
-
- - in console mode:
-
- nzbget -s
-
- - or in daemon mode (POSIX only):
-
- nzbget -D
-
- - or as a service (Windowx only, firstly install the service with command
- "nzbget -install"):
-
- net start NZBGet
-
-To stop server use:
-
- nzbget -Q
-
-TIP for POSIX users: with included script "nzbgetd" you can use standard
-commands to control daemon:
-
- nzbgetd start
- nzbgetd stop
- etc.
-
-When NZBGet is started in console server mode it displays a message that
-it is ready to receive download requests. In daemon mode it doesn't print any
-messages to console since it runs in background.
-
-When the server is running it is possible to queue up downloads. This can be
-done either in terminal with "nzbget -A " or by uploading
-a nzb-file into server's monitor-directory (/nzb by default).
-
-To check the status of server start client and connect it to server:
-
- nzbget -C
-
-The client have three different (display) outputmodes, which you can select
-in configuration file (on client computer) or in command line. Try them:
-
- nzbget -o outputmode=log -C
-
- nzbget -o outputmode=color -C
-
- nzbget -o outputmode=curses -C
-
-To list files in server's queue:
-
- nzbget -L
-
-It prints something like:
-
- [1] nzbname\filename1.rar (50.00 MB)
- [2] nzbname\filename1.r01 (50.00 MB)
- [3] another-nzb\filename3.r01 (100.00 MB)
- [4] another-nzb\filename3.r02 (100.00 MB)
-
-This is the list of individual files listed within nzb-file. To print
-the list of nzb-files (without content) add G-modifier to the list command:
-
- [1] nzbname (4.56 GB)
- [2] another-nzb (4.20 GB)
-
-The numbers in square braces are ID's of files or groups in queue.
-They can be used in edit-command. For example to move file with
-ID 2 to the top of queue:
-
- nzbget -E T 2
-
-or to pause files with IDs from 10 to 20:
-
- nzbget -E P 10-20
-
-or to delete files from queue:
-
- nzbget -E D 3 10-15 20-21 16
-
-The edit-command has also a group-mode which affects all files from the
-same nzb-file. You need to pass an ID of the group. For example to delete
-the whole group 1:
-
- nzbget -E G D 1
-
-The switch "o" is useful to override options in configuration files.
-For example:
-
- nzbget -o reloadqueue=no -o dupecheck=no -o parcheck=yes -s
-
-or:
-
- nzbget -o createlog=no -C
-
-### Running client & server on seperate machines:
--------------------------------------------------
-
-Since nzbget communicates via TCP/IP it's possible to have a server running on
-one computer and adding downloads via a client on another computer.
-
-Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
-IP of the server (default is localhost which means client and server runs on
-same computer)
-
-### Security warning
---------------------
-
-NZBGet communicates via unsecured socket connections. This makes it vulnerable.
-Although server checks the password passed by client, this password is still
-transmitted in unsecured way. For this reason it is highly recommended
-to configure your Firewall to not expose the port used by NZBGet to WAN.
-
-If you need to control server from WAN it is better to connect to server's
-terminal via SSH (POSIX) or remote desktop (Windows) and then run
-nzbget-client-commands in this terminal.
-
-### Post processing scripts
----------------------------
-
-After the download of nzb-file is completed nzbget can call post-processing
-scripts, defined in configuration file.
-
-Example post-processing scripts are provided in directory "scripts".
-
-To use the scripts copy them into your local directory and set options
-, and .
-
-For information on writing your own post-processing scripts please
-visit NZBGet web site.
-
-### Web-interface
------------------
-
-NZBGet has a built-in web-server providing the access to the program
-functions via web-interface.
-
-To activate web-interface set the option "WebDir" to the path with
-web-interface files. If you install using "make install-conf" as
-described above the option is set automatically. If you install using
-binary files you should check if the option is set correctly.
-
-To access web-interface from your web-browser use the server address
-and port defined in NZBGet configuration file in options "ControlIP" and
-"ControlPort". For example:
-
- http://localhost:6789/
-
-For login credentials type username and the password defined by
-options "ControlUsername" (default "nzbget") and "ControlPassword"
-(default "tegbzn6789").
-
-In a case your browser forget credentials, to prevent typing them each
-time, there is a workaround - use URL in the form:
-
- http://localhost:6789/username:password/
-
-Please note, that in this case the password is saved in a bookmark or in
-browser history in plain text and is easy to find by persons having
-access to your computer.
-
-## 8. Authors
-
-NZBGet is developed and maintained by Andrey Prygunkov
-(hugbug@users.sourceforge.net).
-
-The original project was initially created by Sven Henkel
-(sidddy@users.sourceforge.net) in 2004 and later developed by
-Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
-In 2007 the abandoned project was overtaken by Andrey Prygunkov.
-Since then the program has been completely rewritten.
-
-NZBGet distribution archive includes additional components
-written by other authors:
-
-Par2:
- Peter Brian Clements
-
-Par2 library API:
- Francois Lesueur
-
-jQuery:
- John Resig
- The Dojo Foundation
-
-Bootstrap:
- Twitter, Inc
-
-Raphaël:
- Dmitry Baranovskiy
- Sencha Labs
-
-Elycharts:
- Void Labs s.n.c.
-
-iconSweets:
- Yummygum
-
-Boost:
- Boost organization and wider Boost community
-
-
-## 9. Copyright
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-The complete content of license is provided in file COPYING.
-
-Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
-
-## 10. Contact
-
-If you encounter any problem, feel free to contact us
- https://nzbget.com/contact/
+ - [POSIX](docs/POSIX.md)
+ - [Windows](docs/WINDOWS.md)
+ - [Synology](synology/build-info.md)
+ - [QNAP](qnap/build-info.md)
diff --git a/Makefile.am b/Makefile.am
index b853f8f8a..1871a03fa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -332,7 +332,7 @@ linux_FILES = \
doc_FILES = \
INSTALLATION.md \
- ChangeLog \
+ ChangeLog.md \
COPYING
par2doc_FILES = \
@@ -352,6 +352,8 @@ webui_FILES = \
webui/messages.js \
webui/status.js \
webui/style.css \
+ webui/light-theme.css \
+ webui/dark-theme.css \
webui/upload.js \
webui/util.js \
webui/config.js \
@@ -365,8 +367,7 @@ webui_FILES = \
webui/lib/raphael.min.js \
webui/lib/elycharts.js \
webui/lib/elycharts.min.js \
- webui/img/house-16.ico \
- webui/img/download-16.ico \
+ webui/lib/material-icons.woff2 \
webui/img/icons.png \
webui/img/icons-2x.png \
webui/img/transmit.gif \
diff --git a/README.md b/README.md
index 6a4356d97..524d01045 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,7 @@
[![License](https://img.shields.io/badge/license-GPL-blue.svg)](http://www.gnu.org/licenses/)
![GitHub release (by tag)](https://img.shields.io/github/downloads/nzbgetcom/nzbget/v22.0/total?label=v22.0)
![GitHub release (by tag)](https://img.shields.io/github/downloads/nzbgetcom/nzbget/v23.0/total?label=v23.0)
+![GitHub release (by tag)](https://img.shields.io/github/downloads/nzbgetcom/nzbget/v24.0/total?label=v24.0)
![docker pulls](https://img.shields.io/docker/pulls/nzbgetcom/nzbget.svg)
[![linux build](https://github.com/nzbgetcom/nzbget/actions/workflows/linux.yml/badge.svg?branch=main)](https://github.com/nzbgetcom/nzbget/actions/workflows/linux.yml)
@@ -36,7 +37,9 @@ More information available at https://nzbget.com
We provide a easy-to-use installer for each platform we support.
Please download binaries from our [releases](https://github.com/nzbgetcom/nzbget/tags) page.
-We also provide a docker image for popular architectures. [Docker readme](docker/README.md)
+Linux DEB/RPM packages are available from [releases](https://github.com/nzbgetcom/nzbget/tags) page or from DEB/RPM [repositories](https://nzbgetcom.github.io).
+
+Docker images are available for x86-64 / arm64 / armv7 architectures. [Docker readme](docker/README.md)
Synology packages are available as SynoCommunity packages and SPK packages. [Synology readme](synology/README.md)
@@ -52,25 +55,26 @@ QNAP packages are available as native packages and buildroot packages. [QNAP rea
`Windows`: Windows 7 and later, 32 or 64 Bit.
-`Linux`: Linux kernel 2.6 and later, x86 (32 or 64 Bit), ARM 32-bit (armel armhf), ARM 64-bit (aarch64)
+`Linux`: Linux kernel 2.6 and later, x86 (32 or 64 Bit), ARM 32-bit (armel armhf), ARM 64-bit (aarch64), MIPS (mipseb mipsel), PowerPC (ppc6xx ppc500), RISC-V 64-bit (riscv64)
-`macOS`: macOS 10.13 High Sierra and later, Intel / Apple Silicon.
+`macOS`: X64 binary: macOS Mojave 10.14+, Universal (Intel / Apple Silicon) binary: macOS Monterey 12+
## Building from sources
[General instructions](INSTALLATION.md)
-[Linux cross-compiling / buildroot](linux/build-info.md)
-
-[macOS](osx/build-info.md)
+## Extensions
+ - [V1 (NZBGet v22 and below)](docs/extensions/EXTENSIONS_LEGACY.md)
+ - [V2 (NZBGet v23 and above)](docs/extensions/EXTENSIONS.md)
-[Synology](synology/build-info.md)
+## Brief introduction on how to use NZBGet
+ - [How to use](docs/HOW_TO_USE.md)
## Contribution
Contributions are very welcome - not only from developers, but from our users too - please don't hesitate to participate in [discussions](https://github.com/nzbgetcom/nzbget/discussions) or [create a new discussion](https://github.com/nzbgetcom/nzbget/discussions/new/choose)
-For more information - see [Contributing](CONTRIBUTING.md).
+For more information - see [Contributing](docs/CONTRIBUTING.md).
## Donate
diff --git a/cmake/boost.cmake b/cmake/boost.cmake
new file mode 100644
index 000000000..7d517368e
--- /dev/null
+++ b/cmake/boost.cmake
@@ -0,0 +1,19 @@
+set(ROOT ${CMAKE_BINARY_DIR}/boost/)
+
+ExternalProject_add(
+ boost
+ PREFIX boost
+ URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz
+ TLS_VERIFY TRUE
+ BUILD_IN_SOURCE TRUE
+ GIT_SHALLOW TRUE
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
+ CONFIGURE_COMMAND ${ROOT}src/boost/bootstrap.sh
+ --with-libraries=json
+ --prefix=${ROOT}build
+ BUILD_COMMAND ${ROOT}src/boost/b2 link=static
+ INSTALL_COMMAND ${ROOT}src/boost/b2 install
+)
+
+set(LIBS ${LIBS} ${ROOT}build/lib/libboost_json.a)
+set(INCLUDES ${INCLUDES} ${ROOT}build/include/)
diff --git a/cmake/config.h.in b/cmake/config.h.in
new file mode 100644
index 000000000..44783d29a
--- /dev/null
+++ b/cmake/config.h.in
@@ -0,0 +1,168 @@
+/* Name of package */
+#cmakedefine PACKAGE "@PACKAGE@"
+
+/* Version number of package */
+#cmakedefine VERSION "@VERSION@@VERSION_SUFFIX@"
+
+/* Define to the address where bug reports for this package should be sent. */
+#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
+
+/* Define to 1 to not use curses */
+#cmakedefine DISABLE_CURSES
+
+/* Define to 1 to disable gzip-support */
+#cmakedefine DISABLE_GZIP
+
+/* Define to 1 to not use libxml2, only for development purposes */
+#cmakedefine DISABLE_LIBXML2
+
+/* Define to 1 to disable par-verification and repair */
+#cmakedefine DISABLE_PARCHECK
+
+/* Define to 1 to not use TLS/SSL */
+#cmakedefine DISABLE_TLS
+
+/* Define to 1 to install an empty signal handler for SIGCHLD */
+#cmakedefine SIGCHLD_HANDLER @SIGCHLD_HANDLER@
+
+/* Define to the name of macro which returns the name of function being
+ compiled */
+#cmakedefine FUNCTION_MACRO_NAME @FUNCTION_MACRO_NAME@
+
+/* Define to 1 to create stacktrace on segmentation faults */
+#cmakedefine HAVE_BACKTRACE @HAVE_BACKTRACE@
+
+/* Define to 1 if ctime_r takes 2 arguments */
+#cmakedefine HAVE_CTIME_R_2 @HAVE_CTIME_R_2@
+
+/* Define to 1 if ctime_r takes 3 arguments */
+#cmakedefine HAVE_CTIME_R_3 @HAVE_CTIME_R_3@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_CURSES_H @HAVE_CURSES_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_ENDIAN_H @HAVE_ENDIAN_H@
+
+/* Define to 1 if fdatasync is supported */
+#cmakedefine HAVE_FDATASYNC @HAVE_FDATASYNC@
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#cmakedefine HAVE_FSEEKO @HAVE_FSEEKO@
+
+/* Define to 1 if F_FULLFSYNC is supported */
+#cmakedefine HAVE_FULLFSYNC @HAVE_FULLFSYNC@
+
+/* Define to 1 if getaddrinfo is supported */
+#cmakedefine HAVE_GETADDRINFO @HAVE_GETADDRINFO@
+
+/* Define to 1 if gethostbyname_r is supported */
+#cmakedefine HAVE_GETHOSTBYNAME_R @HAVE_GETHOSTBYNAME_R@
+
+/* Define to 1 if gethostbyname_r takes 3 arguments */
+#cmakedefine HAVE_GETHOSTBYNAME_R_3 @HAVE_GETHOSTBYNAME_R_3@
+
+/* Define to 1 if gethostbyname_r takes 5 arguments */
+#cmakedefine HAVE_GETHOSTBYNAME_R_5 @HAVE_GETHOSTBYNAME_R_5@
+
+/* Define to 1 if gethostbyname_r takes 6 arguments */
+#cmakedefine HAVE_GETHOSTBYNAME_R_6 @HAVE_GETHOSTBYNAME_R_6@
+
+/* Define to 1 if you have the `getopt' function. */
+#cmakedefine HAVE_GETOPT @HAVE_GETOPT@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_GETOPT_H @HAVE_GETOPT_H@
+
+/* Define to 1 if getopt_long is supported */
+#cmakedefine HAVE_GETOPT_LONG @HAVE_GETOPT_LONG@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_INTTYPES_H @HAVE_INTTYPES_H@
+
+/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
+#cmakedefine HAVE_LIBGNUTLS @HAVE_LIBGNUTLS@
+
+/* Define to 1 if lockf is supported */
+#cmakedefine HAVE_LOCKF @HAVE_LOCKF@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_NCURSES_H @HAVE_NCURSES_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_NCURSES_NCURSES_H @HAVE_NCURSES_NCURSES_H@
+
+/* Define to 1 to use Nettle library for decryption. */
+#cmakedefine HAVE_NETTLE @HAVE_NETTLE@
+
+/* Define to 1 to use OpenSSL library for TLS/SSL-support and decryption. */
+#cmakedefine HAVE_OPENSSL @HAVE_OPENSSL@
+
+/* Define to 1 if pthread_cancel is supported */
+#cmakedefine HAVE_PTHREAD_CANCEL @HAVE_PTHREAD_CANCEL@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_REGEX_H @HAVE_REGEX_H@
+
+/* Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h */
+#cmakedefine HAVE_SC_NPROCESSORS_ONLN @HAVE_SC_NPROCESSORS_ONLN@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_STDIO_H @HAVE_STDIO_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_STRINGS_H @HAVE_STRINGS_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_STRING_H @HAVE_STRING_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_SYS_PRCTL_H @HAVE_SYS_PRCTL_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@
+
+/* Define to 1 if you have the header file. */
+#cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@
+
+/* Define to 1 if variadic macros are supported */
+#cmakedefine HAVE_VARIADIC_MACROS @HAVE_VARIADIC_MACROS@
+
+/* Define to 1 if OpenSSL supports function "X509_check_host" */
+#cmakedefine HAVE_X509_CHECK_HOST @HAVE_X509_CHECK_HOST@
+
+/* Determine what socket length (socklen_t) data type is */
+#cmakedefine SOCKLEN_T @SOCKLEN_T@
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#cmakedefine _LARGEFILE_SOURCE @_LARGEFILE_SOURCE@
+
+/* Define for large files, on AIX-style hosts. */
+#cmakedefine _LARGE_FILES @_LARGE_FILES@
+
+/* Define to `unsigned int' if does not define. */
+#cmakedefine size_t @size_t@
+
+/* Define if AMD64 */
+#cmakedefine __amd64__
+
+/* Define if i686 */
+#cmakedefine __i686__
+
+/* Use 32BIT TIME_T */
+#cmakedefine _USE_32BIT_TIME_T
+
+/* detection of memory leaks */
+#cmakedefine _CRTDBG_MAP_ALLOC
diff --git a/cmake/install.cmake b/cmake/install.cmake
new file mode 100644
index 000000000..f34c1ab31
--- /dev/null
+++ b/cmake/install.cmake
@@ -0,0 +1,43 @@
+set(DOC_FILES
+ ${CMAKE_SOURCE_DIR}/ChangeLog.md
+ ${CMAKE_SOURCE_DIR}/COPYING
+)
+set(SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/${PACKAGE})
+set(CONF_FILE ${CMAKE_SOURCE_DIR}/nzbget.conf)
+set(WEBUI_DIR ${CMAKE_SOURCE_DIR}/webui)
+set(DOC_FILES_DEST ${SHARE_DIR}/doc)
+set(CONF_FILE_DEST ${SHARE_DIR})
+set(WEBUI_DIR_DEST ${SHARE_DIR})
+set(BIN_FILE_DEST ${CMAKE_INSTALL_PREFIX}/bin)
+
+install(TARGETS ${PACKAGE} PERMISSIONS
+ OWNER_EXECUTE
+ OWNER_WRITE
+ OWNER_READ
+ GROUP_READ
+ GROUP_EXECUTE
+ WORLD_READ
+ WORLD_EXECUTE
+ DESTINATION ${BIN_FILE_DEST})
+install(FILES ${DOC_FILES} DESTINATION ${DOC_FILES_DEST})
+install(DIRECTORY ${WEBUI_DIR} DESTINATION ${WEBUI_DIR_DEST})
+
+file(READ ${CONF_FILE} CONFIG_CONTENT)
+string(REPLACE "WebDir=" "WebDir=${WEBUI_DIR_DEST}/webui" MODIFIED_CONFIG_CONTENT "${CONFIG_CONTENT}")
+string(REPLACE "ConfigTemplate=" "ConfigTemplate=${CONF_FILE_DEST}/nzbget.conf" MODIFIED_CONFIG_CONTENT "${MODIFIED_CONFIG_CONTENT}")
+file(WRITE ${CMAKE_BINARY_DIR}/nzbget.conf "${MODIFIED_CONFIG_CONTENT}")
+install(FILES ${CMAKE_BINARY_DIR}/nzbget.conf DESTINATION ${CONF_FILE_DEST})
+
+if(NOT EXISTS ${CMAKE_INSTALL_PREFIX}/etc/nzbget.conf)
+ install(FILES ${CMAKE_BINARY_DIR}/nzbget.conf DESTINATION ${CMAKE_INSTALL_PREFIX}/etc)
+else()
+ message(STATUS "nzbget.conf is already installed in ${CMAKE_INSTALL_PREFIX}/etc")
+ message(STATUS "If you want to overwrite it, then do it manually with caution")
+endif()
+
+add_custom_target(uninstall
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${DOC_FILES_DEST}
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${SHARE_DIR}
+ COMMAND ${CMAKE_COMMAND} -E remove ${BIN_FILE_DEST}/${PACKAGE}
+ COMMENT "Uninstalling" ${PACKAGE}
+)
diff --git a/cmake/posix.cmake b/cmake/posix.cmake
new file mode 100644
index 000000000..ff46d04e2
--- /dev/null
+++ b/cmake/posix.cmake
@@ -0,0 +1,339 @@
+option(ENABLE_STATIC "Build static executable")
+option(ENABLE_TESTS "Build tests")
+option(ENABLE_CLANG_TIDY "Enable Clang-Tidy static analizer")
+option(DISABLE_TLS "Disable TLS")
+option(DISABLE_CURSES "Disable curses")
+option(DISABLE_GZIP "Disable gzip")
+option(DISABLE_PARCHECK "Disable parcheck")
+option(USE_OPENSSL "Use OpenSSL" ON)
+option(USE_GNUTLS "Use GnuTLS" OFF)
+
+if(NOT DISABLE_TLS AND USE_GNUTLS)
+ set(USE_OPENSSL OFF)
+endif()
+
+if(DISABLE_TLS)
+ set(USE_GNUTLS OFF)
+ set(USE_OPENSSL OFF)
+endif()
+
+message(STATUS "TOOLCHAIN OPTIONS:")
+message(STATUS " SYSTEM NAME ${CMAKE_SYSTEM_NAME}")
+message(STATUS " SYSTEM PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}")
+message(STATUS "BUILD OPTIONS:")
+message(STATUS " BUILD TYPE: ${CMAKE_BUILD_TYPE}")
+message(STATUS " ENABLE STATIC: ${ENABLE_STATIC}")
+message(STATUS " ENABLE TESTS: ${ENABLE_TESTS}")
+message(STATUS " DISABLE TLS: ${DISABLE_TLS}")
+message(STATUS " - OPENSSL: ${USE_OPENSSL}")
+message(STATUS " - GNUTLS: ${USE_GNUTLS}")
+message(STATUS " DISABLE CURSES: ${DISABLE_CURSES}")
+message(STATUS " DISABLE GZIP: ${DISABLE_GZIP}")
+message(STATUS " DISABLE PARCHECK: ${DISABLE_PARCHECK}")
+
+if(ENABLE_CLANG_TIDY)
+ set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*)
+endif()
+
+if(ENABLE_STATIC)
+ # due to the error "ld: library not found for -crt0.o" when using Apple Clang with "-static"
+ if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-static" CACHE STRING "" FORCE)
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -static" CACHE STRING "" FORCE)
+ endif()
+ set(BUILD_SHARED_LIBS OFF)
+ set(LIBS $ENV{LIBS})
+ set(INCLUDES $ENV{INCLUDES})
+ include(${CMAKE_SOURCE_DIR}/lib/sources.cmake)
+else()
+ find_package(Threads REQUIRED)
+ find_package(LibXml2 REQUIRED)
+
+ set(LIBS ${LIBS} Threads::Threads LibXml2::LibXml2)
+ set(INCLUDES ${INCLUDES} ${LIBXML2_INCLUDE_DIR})
+
+ if(NOT DISABLE_TLS)
+ if(USE_OPENSSL AND NOT USE_GNUTLS)
+ find_package(OpenSSL REQUIRED)
+ set(LIBS ${LIBS} OpenSSL::SSL OpenSSL::Crypto)
+ set(INCLUDES ${INCLUDES} ${OPENSSL_INCLUDE_DIR})
+ elseif(USE_GNUTLS)
+ find_package(GnuTLS REQUIRED)
+ find_library(NETTLE_LIBRARY NAMES nettle libnettle)
+ set(INCLUDES ${INCLUDES} ${GNUTLS_INCLUDE_DIRS})
+ set(LIBS ${LIBS} GnuTLS::GnuTLS ${NETTLE_LIBRARY})
+ endif()
+ endif()
+
+ if(NOT DISABLE_CURSES)
+ set(CURSES_NEED_NCURSES TRUE)
+ find_package(Curses REQUIRED)
+ set(INCLUDES ${INCLUDES} ${CURSES_INCLUDE_DIRS})
+ set(LIBS ${LIBS} ${CURSES_LIBRARIES})
+ endif()
+
+ if(NOT DISABLE_GZIP)
+ find_package(ZLIB REQUIRED)
+ set(INCLUDES ${INCLUDES} ${ZLIB_INCLUDE_DIRS})
+ set(LIBS ${LIBS} ZLIB::ZLIB)
+ endif()
+
+ find_package(Boost COMPONENTS json)
+
+ if(NOT Boost_JSON_FOUND)
+ message(STATUS "The Boost library will be installed from github")
+ include(ExternalProject)
+
+ include(${CMAKE_SOURCE_DIR}/cmake/boost.cmake)
+
+ include(${CMAKE_SOURCE_DIR}/lib/sources.cmake)
+
+ add_dependencies(${PACKAGE} boost)
+ add_dependencies(yencode boost)
+ add_dependencies(par2 boost)
+ add_dependencies(regex boost)
+ else()
+ set(LIBS ${LIBS} Boost::json)
+ set(INCLUDES ${INCLUDES} ${Boost_INCLUDE_DIR})
+
+ include(${CMAKE_SOURCE_DIR}/lib/sources.cmake)
+ endif()
+endif()
+
+include(CheckIncludeFiles)
+include(CheckLibraryExists)
+include(CheckSymbolExists)
+include(CheckFunctionExists)
+include(CheckTypeSize)
+include(CheckCSourceCompiles)
+include(CheckCXXSourceCompiles)
+
+check_include_files(sys/prctl.h HAVE_SYS_PRCTL_H)
+check_include_files(regex.h HAVE_REGEX_H)
+check_include_files(endian.h HAVE_ENDIAN_H)
+check_include_files(getopt.h HAVE_GETOPT_H)
+check_include_file(inttypes.h HAVE_INTTYPES_H)
+check_include_file(stdint.h HAVE_STDINT_H)
+check_include_file(stdio.h HAVE_STDIO_H)
+check_include_file(stdlib.h HAVE_STDLIB_H)
+check_include_file(strings.h HAVE_STRINGS_H)
+check_include_file(string.h HAVE_STRING_H)
+check_include_file(sys/stat.h HAVE_SYS_STAT_H)
+check_include_file(unistd.h HAVE_UNISTD_H)
+
+check_library_exists(pthread pthread_create "" HAVE_PTHREAD_CREATE)
+check_library_exists(socket socket "" HAVE_SOCKET)
+check_library_exists(nsl inet_addr "" HAVE_INET_ADDR)
+check_library_exists(resolv hstrerror "" HAVE_HSTRERROR)
+
+check_symbol_exists(lockf unistd.h HAVE_LOCKF)
+check_symbol_exists(pthread_cancel pthread.h HAVE_PTHREAD_CANCEL)
+check_symbol_exists(F_FULLFSYNC fcntl.h HAVE_FULLFSYNC)
+
+check_function_exists(getopt_long HAVE_GETOPT_LONG)
+check_function_exists(fdatasync HAVE_FDATASYNC)
+
+set(SIGCHLD_HANDLER 1)
+
+if(USE_OPENSSL)
+ set(HAVE_OPENSSL 1)
+endif()
+
+if(USE_GNUTLS)
+ set(HAVE_LIBGNUTLS 1)
+ set(HAVE_NETTLE 1)
+endif()
+
+if(NOT DISABLE_CURSES)
+ set(HAVE_NCURSES_H 1)
+endif()
+
+if(NOT DISABLED_PARCHECK)
+ check_type_size(size_t SIZE_T)
+ check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO)
+ check_function_exists(getopt HAVE_GETOPT)
+else()
+ set(DISABLED_PARCHECK 1)
+endif()
+
+# check ctime_r
+check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ time_t clock;
+ char buf[26];
+ ctime_r(&clock, buf, 26);
+ return 0;
+ }" HAVE_CTIME_R_3)
+
+if(NOT HAVE_CTIME_R_3)
+ check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ time_t clock;
+ char buf[26];
+ ctime_r(&clock, buf);
+ return 0;
+ }" HAVE_CTIME_R_2)
+endif()
+if(NOT HAVE_CTIME_R_2)
+ message(FATAL_ERROR "ctime_r function not found")
+endif()
+
+check_function_exists(getaddrinfo HAVE_GETADDRINFO)
+if(NOT HAVE_GETADDRINFO)
+ check_library_exists(nsl getaddrinfo "" HAVE_GETADDRINFO)
+endif()
+
+# check gethostbyname_r, if getaddrinfo is not available
+if(NOT HAVE_GETADDRINFO)
+ check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ char* szHost;
+ struct hostent hinfobuf;
+ char* strbuf;
+ int h_errnop;
+ struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop);
+ return 0;
+ }" HAVE_GETHOSTBYNAME_R_5)
+if(NOT HAVE_GETHOSTBYNAME_R_5)
+ check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ char* szHost;
+ struct hostent* hinfo;
+ struct hostent hinfobuf;
+ char* strbuf;
+ int h_errnop;
+ int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop);
+ return 0;
+ }" HAVE_GETHOSTBYNAME_R_6)
+if(NOT HAVE_GETHOSTBYNAME_R_6)
+ check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ char* szHost;
+ struct hostent hinfo;
+ struct hostent_data hinfobuf;
+ int err = gethostbyname_r(szHost, &hinfo, &hinfobuf);
+ return 0;
+ }" HAVE_GETHOSTBYNAME_R_3)
+if(NOT HAVE_GETHOSTBYNAME_R_3)
+ message(FATAL_ERROR "gethostbyname_r function not found")
+endif()
+endif()
+endif()
+if (NOT HAVE_GETHOSTBYNAME_R_3)
+ set(HAVE_GETHOSTBYNAME_R 1)
+ check_library_exists(nsl gethostbyname_r "" HAVE_GETHOSTBYNAME_R)
+endif()
+endif()
+
+# Determine what socket length (socklen_t) data type is
+check_cxx_source_compiles("
+ #include
+ #include
+ #include
+ int main()
+ {
+ (void)getsockopt (1, 1, 1, NULL, (socklen_t*)NULL);
+ }" SOCKLEN)
+if(SOCKLEN)
+ set(SOCKLEN_T socklen_t)
+else()
+ check_cxx_source_compiles("
+ #include
+ #include
+ #include
+ int main()
+ {
+ (void)getsockopt (1, 1, 1, NULL, (size_t*)NULL);
+ }" SOCKLEN)
+if(SOCKLEN)
+ set(SOCKLEN_T size_t)
+else()
+ check_cxx_source_compiles("
+ #include
+ #include
+ #include
+ int main()
+ {
+ (void)getsockopt (1, 1, 1, NULL, (int*)NULL);
+ }" SOCKLEN)
+if(SOCKLEN)
+ set(SOCKLEN_T int)
+else()
+ set(SOCKLEN_T int)
+endif()
+endif()
+endif()
+
+# Check CPU cores via sysconf
+check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ int a = _SC_NPROCESSORS_ONLN;
+ }" HAVE_SC_NPROCESSORS_ONLN)
+
+# Check TLS/SSL
+if(USE_OPENSSL AND NOT ENABLE_STATIC)
+ check_library_exists(OpenSSL::Crypto X509_check_host "" HAVE_X509_CHECK_HOST)
+elseif(USE_OPENSSL AND ENABLE_STATIC)
+ set(HAVE_X509_CHECK_HOST 1)
+endif()
+
+check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ printf(\"%s\", __FUNCTION__);
+ return 0;
+ }" FUNCTION_MACRO_NAME_ONE)
+
+check_cxx_source_compiles("
+ #include
+ int main()
+ {
+ printf(\"%s\", __func__);
+ return 0;
+ }" FUNCTION_MACRO_NAME_TWO)
+
+if(FUNCTION_MACRO_NAME_ONE)
+ set(FUNCTION_MACRO_NAME __FUNCTION__)
+elseif (FUNCTION_MACRO_NAME_TWO)
+ set(FUNCTION_MACRO_NAME __func__)
+endif()
+
+check_cxx_source_compiles("
+ #define macro(...) macrofunc(__VA_ARGS__)
+ int macrofunc(int a, int b) { return a + b; }
+ int main()
+ {
+ int a = macro(1, 2);
+ return 0;
+ }" HAVE_VARIADIC_MACROS)
+
+if(HAVE_VARIADIC_MACROS)
+ set(DHAVE_VARIADIC_MACROS 1)
+endif()
+
+check_cxx_source_compiles("
+ #include
+ #include
+ #include
+ int main()
+ {
+ void* array[100];
+ size_t size;
+ char** strings;
+ size = backtrace(array, 100);
+ strings = backtrace_symbols(array, size);
+ return 0;
+ }" HAVE_BACKTRACE)
diff --git a/cmake/toolchain.cmake b/cmake/toolchain.cmake
new file mode 100644
index 000000000..329786886
--- /dev/null
+++ b/cmake/toolchain.cmake
@@ -0,0 +1,19 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR ${ARCH})
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
+set(CMAKE_AR ${TOOLCHAIN_PREFIX}-ar)
+set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}-as)
+set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
+set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
+set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}-ld)
+set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}-objcopy)
+set(CMAKE_RANLIB ${TOOLCHAIN_PREFIX}-ranlib)
+set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}-size)
+set(CMAKE_STRIP ${TOOLCHAIN_PREFIX}-strip)
+
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
diff --git a/cmake/windows.cmake b/cmake/windows.cmake
new file mode 100644
index 000000000..bfc265bd7
--- /dev/null
+++ b/cmake/windows.cmake
@@ -0,0 +1,57 @@
+option(ENABLE_TESTS "Enable tests")
+option(DISABLE_TLS "Disable TLS")
+
+message(STATUS "TOOLCHAIN OPTIONS:")
+message(STATUS " SYSTEM NAME ${CMAKE_SYSTEM_NAME}")
+message(STATUS " SYSTEM PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}")
+message(STATUS " TARGET TRIPLET ${VCPKG_TARGET_TRIPLET}")
+message(STATUS "BUILD OPTIONS:")
+message(STATUS " BUILD TYPE: ${CMAKE_BUILD_TYPE}")
+message(STATUS " ENABLE TESTS: ${ENABLE_TESTS}")
+message(STATUS " DISABLE TLS: ${DISABLE_TLS}")
+
+set(Boost_USE_STATIC_LIBS ON)
+
+find_package(Threads REQUIRED)
+find_package(LibXml2 REQUIRED)
+find_package(Boost REQUIRED COMPONENTS json)
+
+set(LIBS Threads::Threads Boost::json LibXml2::LibXml2)
+set(INCLUDES ${Boost_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR})
+
+if(NOT DISABLE_TLS)
+ find_package(OpenSSL REQUIRED)
+ set(HAVE_OPENSSL 1)
+ set(HAVE_X509_CHECK_HOST 1)
+ set(LIBS ${LIBS} OpenSSL::SSL OpenSSL::Crypto)
+ set(INCLUDES ${INCLUDES} ${OPENSSL_INCLUDE_DIR})
+endif()
+
+find_package(ZLIB REQUIRED)
+set(LIBS ${LIBS} ZLIB::ZLIB)
+set(INCLUDES ${INCLUDES} ${ZLIB_INCLUDE_DIRS})
+
+include(${CMAKE_SOURCE_DIR}/lib/sources.cmake)
+set(INCLUDES ${INCLUDES}
+ ${CMAKE_SOURCE_DIR}/daemon/windows
+ ${CMAKE_SOURCE_DIR}/windows/resources
+)
+
+set(FUNCTION_MACRO_NAME __FUNCTION__)
+set(HAVE_CTIME_R_3 1)
+set(HAVE_VARIADIC_MACROS 1)
+set(HAVE_GETADDRINFO 1)
+set(SOCKLEN_T socklen_t)
+set(HAVE_REGEX_H 1)
+set(HAVE_STDINT_H 1)
+
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(__amd64__ 1)
+else()
+ set(__i686__ 1)
+ set(_USE_32BIT_TIME_T 1)
+endif()
+
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(_CRTDBG_MAP_ALLOC 1)
+endif()
diff --git a/cmake_config.h.in b/cmake_config.h.in
deleted file mode 100644
index aa35cd479..000000000
--- a/cmake_config.h.in
+++ /dev/null
@@ -1,5 +0,0 @@
-/* Name of package */
-#cmakedefine PACKAGE "@PACKAGE@"
-
-/* Version number of package */
-#cmakedefine VERSION "@VERSION@"
diff --git a/configure.ac b/configure.ac
index b4a8ca9c9..5966bc22d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,7 +21,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
-AC_INIT(nzbget, 23.0, https://github.com/nzbgetcom/nzbget/issues)
+AC_INIT(nzbget, 24.0, https://github.com/nzbgetcom/nzbget/issues)
AC_CONFIG_AUX_DIR(posix)
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign subdir-objects])
diff --git a/daemon/extension/Extension.cpp b/daemon/extension/Extension.cpp
index 044fbad22..ae81be81c 100644
--- a/daemon/extension/Extension.cpp
+++ b/daemon/extension/Extension.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See .
*
- * Copyright (C) 2023 Denis
+ * Copyright (C) 2023-2024 Denis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -79,11 +79,21 @@ namespace Extension
m_version = std::move(version);
}
+ void Script::SetNzbgetMinVersion(std::string version)
+ {
+ m_nzbgetMinVersion = std::move(version);
+ }
+
const char* Script::GetVersion() const
{
return m_version.c_str();
}
+ const char* Script::GetNzbgetMinVersion() const
+ {
+ return m_nzbgetMinVersion.c_str();
+ }
+
void Script::SetLicense(std::string license)
{
m_license = std::move(license);
@@ -230,6 +240,7 @@ namespace Extension
json["Homepage"] = script.GetHomepage();
json["License"] = script.GetLicense();
json["Version"] = script.GetVersion();
+ json["NZBGetMinVersion"] = script.GetNzbgetMinVersion();
json["PostScript"] = script.GetPostScript();
json["ScanScript"] = script.GetScanScript();
json["QueueScript"] = script.GetQueueScript();
@@ -256,6 +267,9 @@ namespace Extension
optionJson["Name"] = option.name;
optionJson["DisplayName"] = option.displayName;
+ optionJson["Section"] = option.section.name;
+ optionJson["Multi"] = option.section.multi;
+ optionJson["Prefix"] = option.section.prefix;
if (const std::string* val = boost::variant2::get_if(&option.value))
{
@@ -296,7 +310,9 @@ namespace Extension
commandJson["Name"] = command.name;
commandJson["DisplayName"] = command.displayName;
commandJson["Action"] = command.action;
-
+ commandJson["Section"] = command.section.name;
+ commandJson["Multi"] = command.section.multi;
+ commandJson["Prefix"] = command.section.prefix;
for (const auto& line : command.description)
{
@@ -330,6 +346,7 @@ namespace Extension
AddNewNode(structNode, "Homepage", "string", script.GetHomepage());
AddNewNode(structNode, "License", "string", script.GetLicense());
AddNewNode(structNode, "Version", "string", script.GetVersion());
+ AddNewNode(structNode, "NZBGetMinVersion", "string", script.GetNzbgetMinVersion());
AddNewNode(structNode, "PostScript", "boolean", BoolToStr(script.GetPostScript()));
AddNewNode(structNode, "ScanScript", "boolean", BoolToStr(script.GetScanScript()));
@@ -358,6 +375,9 @@ namespace Extension
AddNewNode(commandsNode, "Name", "string", command.name.c_str());
AddNewNode(commandsNode, "DisplayName", "string", command.displayName.c_str());
AddNewNode(commandsNode, "Action", "string", command.action.c_str());
+ AddNewNode(commandsNode, "Multi", "boolean", BoolToStr(command.section.multi));
+ AddNewNode(commandsNode, "Section", "string", command.section.name.c_str());
+ AddNewNode(commandsNode, "Prefix", "string", command.section.prefix.c_str());
xmlNodePtr descriptionNode = xmlNewNode(NULL, BAD_CAST "Description");
for (const std::string& line : command.description)
@@ -372,6 +392,9 @@ namespace Extension
{
AddNewNode(optionsNode, "Name", "string", option.name.c_str());
AddNewNode(optionsNode, "DisplayName", "string", option.displayName.c_str());
+ AddNewNode(optionsNode, "Multi", "boolean", BoolToStr(option.section.multi));
+ AddNewNode(optionsNode, "Section", "string", option.section.name.c_str());
+ AddNewNode(optionsNode, "Prefix", "string", option.section.prefix.c_str());
if (const std::string* val = boost::variant2::get_if(&option.value))
{
@@ -416,21 +439,18 @@ namespace Extension
return result;
}
- namespace
+ void AddNewNode(xmlNodePtr rootNode, const char* name, const char* type, const char* value)
{
- void AddNewNode(xmlNodePtr rootNode, const char* name, const char* type, const char* value)
- {
- xmlNodePtr memberNode = xmlNewNode(NULL, BAD_CAST "member");
- xmlNodePtr valueNode = xmlNewNode(NULL, BAD_CAST "value");
- xmlNewChild(memberNode, NULL, BAD_CAST "name", BAD_CAST name);
- xmlNewChild(valueNode, NULL, BAD_CAST type, BAD_CAST value);
- xmlAddChild(memberNode, valueNode);
- xmlAddChild(rootNode, memberNode);
- }
+ xmlNodePtr memberNode = xmlNewNode(NULL, BAD_CAST "member");
+ xmlNodePtr valueNode = xmlNewNode(NULL, BAD_CAST "value");
+ xmlNewChild(memberNode, NULL, BAD_CAST "name", BAD_CAST name);
+ xmlNewChild(valueNode, NULL, BAD_CAST type, BAD_CAST value);
+ xmlAddChild(memberNode, valueNode);
+ xmlAddChild(rootNode, memberNode);
+ }
- const char* BoolToStr(bool value)
- {
- return value ? "true" : "false";
- }
+ const char* BoolToStr(bool value)
+ {
+ return value ? "true" : "false";
}
}
diff --git a/daemon/extension/Extension.h b/daemon/extension/Extension.h
index 6e3e7ef11..85cc504d2 100644
--- a/daemon/extension/Extension.h
+++ b/daemon/extension/Extension.h
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See .
*
- * Copyright (C) 2023 Denis
+ * Copyright (C) 2023-2024 Denis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ namespace Extension
bool feed = false;
};
- class Script
+ class Script final
{
public:
Script() = default;
@@ -54,7 +54,9 @@ namespace Extension
void SetAuthor(std::string author);
const char* GetAuthor() const;
void SetVersion(std::string version);
+ void SetNzbgetMinVersion(std::string version);
const char* GetVersion() const;
+ const char* GetNzbgetMinVersion() const;
void SetLicense(std::string license);
const char* GetLicense() const;
void SetHomepage(std::string homepage);
@@ -91,6 +93,7 @@ namespace Extension
std::string m_rootDir;
std::string m_author;
std::string m_version;
+ std::string m_nzbgetMinVersion;
std::string m_homepage;
std::string m_license;
std::string m_name;
@@ -107,11 +110,8 @@ namespace Extension
std::string ToJsonStr(const Script& script);
std::string ToXmlStr(const Script& script);
- namespace
- {
- void AddNewNode(xmlNodePtr rootNode, const char* name, const char* type, const char* value);
- const char* BoolToStr(bool value);
- }
+ void AddNewNode(xmlNodePtr rootNode, const char* name, const char* type, const char* value);
+ const char* BoolToStr(bool value);
}
#endif
diff --git a/daemon/extension/ExtensionLoader.cpp b/daemon/extension/ExtensionLoader.cpp
index c5135f7a4..2ed93337d 100644
--- a/daemon/extension/ExtensionLoader.cpp
+++ b/daemon/extension/ExtensionLoader.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See .
*
- * Copyright (C) 2023 Denis
+ * Copyright (C) 2023-2024 Denis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
namespace ExtensionLoader
{
+ const char* DEFAULT_SECTION_NAME = "options";
const char* BEGIN_SCRIPT_SIGNATURE = "### NZBGET ";
const char* BEGIN_SCRIPT_COMMANDS_AND_OTPIONS = "### OPTIONS";
const char* POST_SCRIPT_SIGNATURE = "POST-PROCESSING";
@@ -83,10 +84,12 @@ namespace ExtensionLoader
{
continue;
}
+
if (!inBeforeConfig && !strncmp(line.c_str(), DEFINITION_SIGNATURE, DEFINITION_SIGNATURE_LEN))
{
inBeforeConfig = true;
}
+
if (!inBeforeConfig && !inConfig)
{
continue;
@@ -149,6 +152,7 @@ namespace ExtensionLoader
continue;
}
+ // if "OPTIONS" and other sections, e.g.: ### OPTIONS or ### CATEGORIES
if (!strncmp(line.c_str(), BEGIN_SCRIPT_COMMANDS_AND_OTPIONS, BEGIN_SCRIPT_COMMANDS_AND_OTPIONS_LEN))
{
ParseOptionsAndCommands(file, options, commands);
@@ -181,244 +185,248 @@ namespace ExtensionLoader
return true;
}
- namespace
+
+ void RemoveTailAndTrim(std::string& str, const char* tail)
{
- void RemoveTailAndTrim(std::string& str, const char* tail)
+ size_t tailIdx = str.find(tail);
+ if (tailIdx != std::string::npos)
{
- size_t tailIdx = str.find(tail);
- if (tailIdx != std::string::npos)
- {
- str.erase(tailIdx);
- }
- Util::TrimRight(str);
+ str.erase(tailIdx);
}
+ Util::TrimRight(str);
+ }
- void BuildDisplayName(Extension::Script& script)
- {
- BString<1024> shortName = script.GetName();
- if (char* ext = strrchr(shortName, '.')) *ext = '\0'; // strip file extension
+ void BuildDisplayName(Extension::Script& script)
+ {
+ BString<1024> shortName = script.GetName();
+ if (char* ext = strrchr(shortName, '.')) *ext = '\0'; // strip file extension
- const char* displayName = FileSystem::BaseFileName(shortName);
+ const char* displayName = FileSystem::BaseFileName(shortName);
- script.SetDisplayName(displayName);
- }
+ script.SetDisplayName(displayName);
+ }
- void ParseOptionsAndCommands(
- std::ifstream& file,
- std::vector& options,
- std::vector& commands)
+ void ParseOptionsAndCommands(
+ std::ifstream& file,
+ std::vector& options,
+ std::vector& commands)
+ {
+ std::vector selectOpts;
+ std::vector description;
+ std::string currSectionName = DEFAULT_SECTION_NAME;
+
+ std::string line;
+ while (std::getline(file, line))
{
- std::vector selectOpts;
- std::vector description;
+ if (strstr(line.c_str(), END_SCRIPT_SIGNATURE))
+ {
+ break;
+ }
+
+ if (line.empty())
+ {
+ continue;
+ }
+
+ if (!strncmp(line.c_str(), DEFINITION_SIGNATURE, DEFINITION_SIGNATURE_LEN))
+ {
+ currSectionName = line.substr(DEFINITION_SIGNATURE_LEN + 1);
+ RemoveTailAndTrim(currSectionName, "###");
+ continue;
+ }
+
+ size_t selectStartIdx = line.rfind("(");
+ size_t selectEndIdx = line.rfind(")");
+ bool hasSelectOptions = description.empty()
+ && !strncmp(line.c_str(), "# ", 2)
+ && selectStartIdx != std::string::npos
+ && selectEndIdx != std::string::npos
+ && selectEndIdx == line.length() - 2;
- std::string line;
- while (std::getline(file, line))
+ // e.g. # Description (Always, OnFailure) or # Description (1-65535) or # Description (1, 2-5, 10).
+ if (hasSelectOptions)
{
- if (strstr(line.c_str(), END_SCRIPT_SIGNATURE))
+ std::string selectOptionsStr = line.substr(selectStartIdx + 1, selectEndIdx - selectStartIdx - 1);
+ auto result = ExtractElements(selectOptionsStr);
+ auto& selectOptions = result.first;
+ auto& delimiter = result.second;
+ bool canBeNum = delimiter == "-";
+
+ if (delimiter.empty()) // doesn't have
{
- break;
+ description.push_back(line.substr(2));
+ continue;
}
- if (line.empty())
+ if (canBeNum)
{
- continue;
+ description.push_back(line.substr(2));
+ }
+ else
+ {
+ description.push_back(line.substr(2, selectStartIdx - 3) + ".");
}
- size_t selectStartIdx = line.rfind("(");
- size_t selectEndIdx = line.rfind(")");
- bool hasSelectOptions = selectStartIdx != std::string::npos
- && description.empty()
- && selectEndIdx != std::string::npos
- && selectEndIdx != std::string::npos
- && !strncmp(line.c_str(), "# ", 2);
+ selectOpts = GetSelectOptions(selectOptions, canBeNum);
+ continue;
+ }
- // e.g. # Description (Always, OnFailure) or # Description (1-65535) or # Description (1, 2-5, 10).
- if (hasSelectOptions)
- {
- std::string selectOptionsStr = line.substr(selectStartIdx + 1, selectEndIdx - selectStartIdx - 1);
- auto result = ExtractElements(selectOptionsStr);
- auto& selectOptions = result.first;
- auto& delimiter = result.second;
- bool canBeNum = delimiter == "-";
-
- if (delimiter.empty()) // doesn't have
- {
- description.push_back(line.substr(2));
- continue;
- }
+ if (!strncmp(line.c_str(), "# ", 2))
+ {
+ description.push_back(line.substr(2));
+ continue;
+ }
- if (canBeNum)
- {
- description.push_back(line.substr(2));
- }
- else
- {
- description.push_back(line.substr(2, selectStartIdx - 3) + ".");
- }
+ if (strncmp(line.c_str(), "# ", 2))
+ {
+ // if option, e.g. #ConnectionTest=Send
+ size_t eqPos = line.find("=");
+ // if command, e.g. #ConnectionTest@Send Test E-Mail
+ size_t atPos = line.find("@");
- selectOpts = GetSelectOptions(selectOptions, canBeNum);
+ if (atPos != std::string::npos && eqPos == std::string::npos)
+ {
+ ManifestFile::Command command{};
+ command.action = line.substr(atPos + 1);
+ Util::Trim(command.action);
+ ParseSectionAndSet(command, currSectionName, line, atPos);
+ command.description = std::move(description);
+ commands.push_back(std::move(command));
+ description.clear();
+ selectOpts.clear();
continue;
}
- if (!strncmp(line.c_str(), "# ", 2))
+ if (eqPos != std::string::npos)
{
- description.push_back(line.substr(2));
+ ManifestFile::Option option{};
+ ParseSectionAndSet(option, currSectionName, line, eqPos);
+ bool canBeNum = !selectOpts.empty() && boost::variant2::get_if(&selectOpts[0]);
+ std::string value = line.substr(eqPos + 1);
+ Util::Trim(value);
+ option.value = GetSelectOpt(value, canBeNum);
+ option.description = std::move(description);
+ option.select = std::move(selectOpts);
+ options.push_back(std::move(option));
+ description.clear();
+ selectOpts.clear();
continue;
}
+ }
+ }
+ }
- if (strncmp(line.c_str(), "# ", 2))
- {
- // if option, e.g. #ConnectionTest=Send
- size_t eqPos = line.find("=");
- // if command, e.g. #ConnectionTest@Send Test E-Mail
- size_t atPos = line.find("@");
-
- if (atPos != std::string::npos && eqPos == std::string::npos)
- {
- ManifestFile::Command command;
- std::string name = line.substr(1, atPos - 1);
- std::string action = line.substr(atPos + 1);
- Util::Trim(action);
- Util::Trim(name);
- command.action = std::move(action);
- command.name = std::move(name);
- command.description = std::move(description);
- command.displayName = command.name;
- commands.push_back(std::move(command));
- description.clear();
- selectOpts.clear();
- continue;
- }
+ std::vector
+ GetSelectOptions(const std::vector& opts, bool canBeNum)
+ {
+ std::vector selectOpts;
- if (eqPos != std::string::npos)
- {
- ManifestFile::Option option;
- std::string name = line.substr(1, eqPos - 1);
- std::string value = line.substr(eqPos + 1);
- Util::Trim(value);
- Util::Trim(name);
- bool canBeNum = !selectOpts.empty() && boost::variant2::get_if(&selectOpts[0]);
- option.value = std::move(GetSelectOpt(value, canBeNum));
- option.name = std::move(name);
- option.description = std::move(description);
- option.select = std::move(selectOpts);
- option.displayName = option.name;
- options.push_back(std::move(option));
- description.clear();
- selectOpts.clear();
- continue;
- }
- }
- }
+ for (const auto& option : opts)
+ {
+ selectOpts.push_back(GetSelectOpt(option, canBeNum));
}
+ return selectOpts;
+ }
- std::vector
- GetSelectOptions(const std::vector& opts, bool canBeNum)
+ ManifestFile::SelectOption GetSelectOpt(const std::string& val, bool canBeNum)
+ {
+ if (!canBeNum)
{
- std::vector selectOpts;
+ return ManifestFile::SelectOption(val);
+ }
- for (const auto& option : opts)
- {
- selectOpts.push_back(GetSelectOpt(option, canBeNum));
- }
- return selectOpts;
+ auto result = Util::StrToNum(val);
+ if (result.has_value())
+ {
+ return ManifestFile::SelectOption(result.get());
}
- ManifestFile::SelectOption GetSelectOpt(const std::string& val, bool canBeNum)
+ return ManifestFile::SelectOption(val);
+ }
+
+ std::pair, std::string>
+ ExtractElements(const std::string& str)
+ {
+ std::vector elements;
+ std::string word;
+
+ for (size_t i = 0; i < str.size(); ++i)
{
- if (!canBeNum)
+ if (i == (str.size() - 1))
{
- return ManifestFile::SelectOption(val);
+ word += str[i];
+ elements.push_back(std::move(word));
+ break;
}
- auto result = Util::StrToNum(val);
- if (result.has_value())
+ if (str[i] == ',')
{
- return ManifestFile::SelectOption(result.get());
+ elements.push_back(std::move(word));
+ word.clear();
+ continue;
}
- return ManifestFile::SelectOption(val);
- }
-
- std::pair, std::string>
- ExtractElements(const std::string& str)
- {
- std::vector elements;
- std::string word;
-
- for (size_t i = 0; i < str.size(); ++i)
+ if (str[i] == ' ' && word.empty())
{
- if (i == str.size() - 1)
- {
- word += str[i];
- elements.push_back(std::move(word));
- break;
- }
-
- if (str[i] == ',')
- {
- elements.push_back(std::move(word));
- word.clear();
- continue;
- }
+ continue;
+ }
- if (str[i] == ' ' && word.empty())
- {
- continue;
- }
+ if (str[i] == ' ' && !word.empty())
+ {
+ elements.clear();
+ elements.push_back(str);
+ return std::make_pair(std::move(elements), "");
+ }
- if (str[i] == ' ' && !word.empty())
+ if (str[i] == '-' && elements.empty())
+ {
+ std::string restWord;
+ for (size_t j = i + 1; j < str.size(); ++j)
{
- elements.clear();
- elements.push_back(str);
- return std::make_pair(std::move(elements), "");
- }
+ if (j >= str.size() - 1)
+ {
+ restWord += str[j];
+ elements.push_back(std::move(word));
+ elements.push_back(std::move(restWord));
+ return std::make_pair(std::move(elements), "-");
+ }
- if (str[i] == '-' && elements.empty())
- {
- std::string restWord;
- for (size_t j = i + 1; j < str.size(); ++j)
+ if (str[j] == ' ')
{
- if (j >= str.size() - 1)
- {
- restWord += str[j];
- elements.push_back(std::move(word));
- elements.push_back(std::move(restWord));
- return std::make_pair(std::move(elements), "-");
- }
-
- if (str[j] == ' ')
- {
- elements.push_back(str);
- return std::make_pair(std::move(elements), "");
- }
-
- if (str[j] == ',')
- {
- word += '-' + restWord;
- elements.push_back(std::move(word));
- word.clear();
- i = j;
- break;
- }
+ elements.push_back(str);
+ return std::make_pair(std::move(elements), "");
+ }
- restWord += str[j];
+ if (str[j] == ',')
+ {
+ word += '-' + restWord;
+ elements.push_back(std::move(word));
+ word.clear();
+ i = j;
+ break;
}
- continue;
+ restWord += str[j];
}
- word += str[i];
+ continue;
}
- return std::make_pair(std::move(elements), ",");
+ word += str[i];
}
+
+ if (elements.size() == 1)
+ {
+ return std::make_pair(std::move(elements), "");
+ }
+
+ return std::make_pair(std::move(elements), ",");
}
}
bool V2::Load(Extension::Script& script, const char* location, const char* rootDir)
{
- ManifestFile::Manifest manifest;
+ ManifestFile::Manifest manifest{};
if (!ManifestFile::Load(manifest, location))
return false;
@@ -430,6 +438,7 @@ namespace ExtensionLoader
script.SetHomepage(std::move(manifest.homepage));
script.SetLicense(std::move(manifest.license));
script.SetVersion(std::move(manifest.version));
+ script.SetNzbgetMinVersion(std::move(manifest.nzbgetMinVersion));
script.SetDisplayName(std::move(manifest.displayName));
script.SetName(std::move(manifest.name));
script.SetAbout(std::move(manifest.about));
@@ -443,17 +452,14 @@ namespace ExtensionLoader
return true;
}
- namespace
+ Extension::Kind GetScriptKind(const std::string& line)
{
- Extension::Kind GetScriptKind(const std::string& line)
- {
- Extension::Kind kind;
- kind.post = strstr(line.c_str(), POST_SCRIPT_SIGNATURE) != nullptr;
- kind.scan = strstr(line.c_str(), SCAN_SCRIPT_SIGNATURE) != nullptr;
- kind.queue = strstr(line.c_str(), QUEUE_SCRIPT_SIGNATURE) != nullptr;
- kind.scheduler = strstr(line.c_str(), SCHEDULER_SCRIPT_SIGNATURE) != nullptr;
- kind.feed = strstr(line.c_str(), FEED_SCRIPT_SIGNATURE) != nullptr;
- return kind;
- }
+ Extension::Kind kind;
+ kind.post = strstr(line.c_str(), POST_SCRIPT_SIGNATURE) != nullptr;
+ kind.scan = strstr(line.c_str(), SCAN_SCRIPT_SIGNATURE) != nullptr;
+ kind.queue = strstr(line.c_str(), QUEUE_SCRIPT_SIGNATURE) != nullptr;
+ kind.scheduler = strstr(line.c_str(), SCHEDULER_SCRIPT_SIGNATURE) != nullptr;
+ kind.feed = strstr(line.c_str(), FEED_SCRIPT_SIGNATURE) != nullptr;
+ return kind;
}
}
diff --git a/daemon/extension/ExtensionLoader.h b/daemon/extension/ExtensionLoader.h
index 1fb504c5e..07c5612d7 100644
--- a/daemon/extension/ExtensionLoader.h
+++ b/daemon/extension/ExtensionLoader.h
@@ -26,6 +26,7 @@
namespace ExtensionLoader
{
+ extern const char* DEFAULT_SECTION_NAME;
extern const char* BEGIN_SCRIPT_SIGNATURE;
extern const char* BEGIN_SCRIPT_COMMANDS_AND_OTPIONS;
extern const char* POST_SCRIPT_SIGNATURE;
@@ -47,20 +48,39 @@ namespace ExtensionLoader
namespace V1
{
bool Load(Extension::Script& script, const char* location, const char* rootDir);
- namespace
+
+ void ParseOptionsAndCommands(
+ std::ifstream& file,
+ std::vector& options,
+ std::vector& commands
+ );
+ std::vector
+ GetSelectOptions(const std::vector& opts, bool isDashDelim);
+ ManifestFile::SelectOption GetSelectOpt(const std::string& val, bool canBeNum);
+ void RemoveTailAndTrim(std::string& str, const char* tail);
+ void BuildDisplayName(Extension::Script& script);
+ std::pair, std::string>
+ ExtractElements(const std::string& str);
+ template
+ void ParseSectionAndSet(T& opt, std::string sectionName, const std::string& line, size_t sepPos)
{
- void ParseOptionsAndCommands(
- std::ifstream& file,
- std::vector& options,
- std::vector& commands
- );
- std::vector
- GetSelectOptions(const std::vector& opts, bool isDashDelim);
- ManifestFile::SelectOption GetSelectOpt(const std::string& val, bool canBeNum);
- void RemoveTailAndTrim(std::string& str, const char* tail);
- void BuildDisplayName(Extension::Script& script);
- std::pair, std::string>
- ExtractElements(const std::string& str);
+ opt.name = line.substr(1, sepPos - 1);
+ Util::Trim(opt.name);
+
+ ManifestFile::Section section{};
+ section.name = std::move(sectionName);
+
+ size_t digitPos = opt.name.find("1.");
+ if (digitPos != std::string::npos)
+ {
+ section.prefix = opt.name.substr(0, digitPos);
+ section.multi = true;
+ opt.name = opt.name.substr(digitPos + 2);
+
+ }
+
+ opt.section = std::move(section);
+ opt.displayName = opt.name;
}
}
@@ -69,10 +89,7 @@ namespace ExtensionLoader
bool Load(Extension::Script& script, const char* location, const char* rootDir);
}
- namespace
- {
- Extension::Kind GetScriptKind(const std::string& line);
- }
+ Extension::Kind GetScriptKind(const std::string& line);
}
#endif
diff --git a/daemon/extension/ExtensionManager.cpp b/daemon/extension/ExtensionManager.cpp
index 1fe0cad54..550ff4def 100644
--- a/daemon/extension/ExtensionManager.cpp
+++ b/daemon/extension/ExtensionManager.cpp
@@ -29,7 +29,7 @@
namespace ExtensionManager
{
- const Extensions& Manager::GetExtensions() const
+ const Extensions& Manager::GetExtensions() const &
{
std::shared_lock lock{m_mutex};
return m_extensions;
@@ -38,20 +38,21 @@ namespace ExtensionManager
std::pair
Manager::DownloadExtension(const std::string& url, const std::string& extName)
{
- BString<1024> tmpFileName;
- tmpFileName.Format("%s%cextension-%s.tmp.zip", g_Options->GetTempDir(), PATH_SEPARATOR, extName.c_str());
+ std::string tmpFileName = std::string(g_Options->GetTempDir())
+ + PATH_SEPARATOR
+ + "extension-" + extName + ".tmp.zip";
std::unique_ptr downloader = std::make_unique();
downloader->SetUrl(url.c_str());
downloader->SetForce(true);
downloader->SetRetry(false);
- downloader->SetOutputFilename(tmpFileName);
+ downloader->SetOutputFilename(tmpFileName.c_str());
downloader->SetInfoName(extName.c_str());
WebDownloader::EStatus status = downloader->DownloadWithRedirects(5);
downloader.reset();
- return std::make_pair(status, tmpFileName.Str());
+ return std::make_pair(status, std::move(tmpFileName));
}
boost::optional
@@ -111,16 +112,16 @@ namespace ExtensionManager
};
unpacker.SetArgs(std::move(args));
- int res = unpacker.Execute();
+ int ec = unpacker.Execute();
- if (res != 0)
+ if (ec < 0)
{
- if (!FileSystem::DeleteFile(filename.c_str()))
- {
- return "Failed to unpack and delete temp file: " + filename;
- }
+ return "Failed to unpack " + filename + ". Make sure that the path to 7-Zip is valid.";
+ }
- return "Failed to unpack " + filename;
+ if (ec > 0)
+ {
+ return "Failed to unpack " + filename + ". " + UnpackController::DecodeSevenZipExitCode(ec);
}
if (!FileSystem::DeleteFile(filename.c_str()))
diff --git a/daemon/extension/ExtensionManager.h b/daemon/extension/ExtensionManager.h
index 6a9c63f9a..f0c1c61d3 100644
--- a/daemon/extension/ExtensionManager.h
+++ b/daemon/extension/ExtensionManager.h
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See .
*
- * Copyright (C) 2023 Denis
+ * Copyright (C) 2023-2024 Denis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ namespace ExtensionManager
{
using Extensions = std::vector>;
- class Manager
+ class Manager final
{
public:
Manager() = default;
@@ -60,7 +60,7 @@ namespace ExtensionManager
std::pair
DownloadExtension(const std::string& url, const std::string& info);
- const Extensions& GetExtensions() const;
+ const Extensions& GetExtensions() const &;
private:
void LoadExtensionDir(const char* directory, bool isSubDir, const char* rootDir);
diff --git a/daemon/extension/ManifestFile.cpp b/daemon/extension/ManifestFile.cpp
index bed19e5ef..8a01e5bf1 100644
--- a/daemon/extension/ManifestFile.cpp
+++ b/daemon/extension/ManifestFile.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See .
*
- * Copyright (C) 2023 Denis
+ * Copyright (C) 2023-2024 Denis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
namespace ManifestFile
{
const char* MANIFEST_FILE = "manifest.json";
+ const char* DEFAULT_SECTION_NAME = "options";
bool Load(Manifest& manifest, const char* directory)
{
@@ -45,188 +46,263 @@ namespace ManifestFile
if (!ValidateAndSet(json, manifest))
return false;
+ CheckKeyAndSet(json, "nzbgetMinVersion", manifest.nzbgetMinVersion);
+
return true;
}
- namespace
+ bool ValidateAndSet(const Json::JsonObject& json, Manifest& manifest)
{
- bool ValidateAndSet(const Json::JsonObject& json, Manifest& manifest)
- {
- if (!CheckKeyAndSet(json, "author", manifest.author))
- return false;
+ if (!CheckKeyAndSet(json, "author", manifest.author))
+ return false;
- if (!CheckKeyAndSet(json, "main", manifest.main))
- return false;
+ if (!CheckKeyAndSet(json, "main", manifest.main))
+ return false;
- if (!CheckKeyAndSet(json, "homepage", manifest.homepage))
- return false;
+ if (!CheckKeyAndSet(json, "homepage", manifest.homepage))
+ return false;
- if (!CheckKeyAndSet(json, "about", manifest.about))
- return false;
+ if (!CheckKeyAndSet(json, "about", manifest.about))
+ return false;
- if (!CheckKeyAndSet(json, "version", manifest.version))
- return false;
+ if (!CheckKeyAndSet(json, "version", manifest.version))
+ return false;
- if (!CheckKeyAndSet(json, "name", manifest.name))
- return false;
+ if (!CheckKeyAndSet(json, "name", manifest.name))
+ return false;
- if (!CheckKeyAndSet(json, "displayName", manifest.displayName))
- return false;
+ if (!CheckKeyAndSet(json, "displayName", manifest.displayName))
+ return false;
- if (!CheckKeyAndSet(json, "kind", manifest.kind))
- return false;
+ if (!CheckKeyAndSet(json, "kind", manifest.kind))
+ return false;
- if (!CheckKeyAndSet(json, "license", manifest.license))
- return false;
+ if (!CheckKeyAndSet(json, "license", manifest.license))
+ return false;
- if (!CheckKeyAndSet(json, "taskTime", manifest.taskTime))
- return false;
+ if (!CheckKeyAndSet(json, "taskTime", manifest.taskTime))
+ return false;
- if (!CheckKeyAndSet(json, "queueEvents", manifest.queueEvents))
- return false;
+ if (!CheckKeyAndSet(json, "queueEvents", manifest.queueEvents))
+ return false;
- if (!ValidateOptionsAndSet(json, manifest.options))
- return false;
+ if (!ValidateOptionsAndSet(json, manifest.options))
+ return false;
- if (!ValidateCommandsAndSet(json, manifest.commands))
- return false;
+ if (!ValidateCommandsAndSet(json, manifest.commands))
+ return false;
- if (!ValidateTxtAndSet(json, manifest.description, "description"))
- return false;
+ ValidateSectionsAndSet(json, manifest.options, manifest.commands);
- if (!ValidateTxtAndSet(json, manifest.requirements, "requirements"))
- return false;
+ if (!ValidateTxtAndSet(json, manifest.description, "description"))
+ return false;
- return true;
- }
+ if (!ValidateTxtAndSet(json, manifest.requirements, "requirements"))
+ return false;
- bool ValidateCommandsAndSet(const Json::JsonObject& json, std::vector& commands)
- {
- auto rawCommands = json.if_contains("commands");
- if (!rawCommands || !rawCommands->is_array())
- return false;
+ return true;
+ }
- for (auto& value : rawCommands->as_array())
- {
- Json::JsonObject cmdJson = value.as_object();
- Command command;
+ bool ValidateCommandsAndSet(const Json::JsonObject& json, std::vector& commands)
+ {
+ auto rawCommands = json.if_contains("commands");
+ if (!rawCommands || !rawCommands->is_array())
+ return false;
- if (!CheckKeyAndSet(cmdJson, "name", command.name))
- continue;
+ for (auto& value : rawCommands->as_array())
+ {
+ Json::JsonObject cmdJson = value.as_object();
+ Command command{};
- if (!CheckKeyAndSet(cmdJson, "displayName", command.displayName))
- continue;
+ CheckKeyAndSet(cmdJson, "section", command.section.name, DEFAULT_SECTION_NAME);
- if (!ValidateTxtAndSet(cmdJson, command.description, "description"))
- continue;
+ if (!CheckKeyAndSet(cmdJson, "name", command.name))
+ continue;
- if (!CheckKeyAndSet(cmdJson, "action", command.action))
- continue;
+ if (!CheckKeyAndSet(cmdJson, "displayName", command.displayName))
+ continue;
- commands.emplace_back(std::move(command));
- }
+ if (!ValidateTxtAndSet(cmdJson, command.description, "description"))
+ continue;
- commands.shrink_to_fit();
+ if (!CheckKeyAndSet(cmdJson, "action", command.action))
+ continue;
- return true;
+ commands.emplace_back(std::move(command));
}
- bool ValidateOptionsAndSet(const Json::JsonObject& json, std::vector