diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index aa650fb0a..000000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,66 +0,0 @@
-version: 1.0.{build}
-image: Visual Studio 2017
-configuration: Debug
-platform: Any CPU
-environment:
- AUTOUPDATER_Duplicati_SKIP_UPDATE: 1
- COVERALLS_REPO_TOKEN:
- secure: szQsrkP5rvra8L60SomD73/dFvRwot0UuyA566zqzI2qmLOr+MhLN0AyC8BTbhYY
-before_build:
- - ps: $env:UNITTEST_BASEFOLDER = "$Env:APPVEYOR_BUILD_FOLDER" + "\testdata"
- - cmd: nuget restore Duplicati.sln
-build:
- project: Duplicati.sln
- verbosity: minimal
-test_script:
- - ps: |
- nuget install OpenCover -Version 4.7.922 -OutputDirectory packages
- nuget install coveralls.net -Version 0.7.0 -OutputDirectory packages
- nuget install NUnit.Runners -Version 3.10.0 -OutputDirectory packages
-
- $testDir = ".\Duplicati\UnitTest\bin\Debug"
- $tests = @("$testDir\Duplicati.UnitTest.dll")
- $testsPassed = $true
-
- foreach ($elem in $tests) {
- $testResultsFile = "$elem" + ".xml"
-
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe `
- -register:user `
- -target:.\packages\NUnit.ConsoleRunner.3.10.0\tools\nunit3-console.exe `
- "-targetargs:""$elem"" /result:""$testResultsFile""" `
- "-filter:+[Duplicati.Library.Main]* -[UnitTest]*" `
- -output:opencover.xml `
- -returntargetcode `
-
- # Keep track of test failures so that we can throw an exception after
- # uploading coverage and test results.
- $testsPassed = $testsPassed -and $?
-
- # Upload test results to AppVeyor.
- $wc = New-Object "System.Net.WebClient"
- $wc.UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\""$testResultsFile""))
- }
-
- if ($Env:COVERALLS_REPO_TOKEN) {
- $revision = git rev-parse HEAD
- $branch = git rev-parse --abbrev-ref HEAD
- $commitAuthor = git show --quiet --format="%aN" $revision
- $commitEmail = git show --quiet --format="%aE" $revision
- $commitMessage = git show --quiet --format="%s" $revision
-
- .\packages\coveralls.net.0.7.0\tools\csmacnz.Coveralls.exe `
- --opencover -i opencover.xml `
- --repoToken "$Env:COVERALLS_REPO_TOKEN" `
- --commitId "$revision" `
- --commitBranch "$branch" `
- --commitAuthor "$commitAuthor" `
- --commitEmail "$commitEmail" `
- --commitMessage "$commitMessage" `
- --useRelativePaths `
- --basePath .\Duplicati\UnitTest\bin\Debug
- }
-
- if (!$testsPassed) {
- throw "Tests failed."
- }
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..a64fc502b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+*.csproj text
+*.sh text eol=lf
+#The #! in rules won't work if CRLF
+/ReleaseBuilder/Resources/debian/* text eol=lf
+/ReleaseBuilder/Resources/fedora/*.sh text eol=lf
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug-report.md
similarity index 83%
rename from .github/ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE/bug-report.md
index a3647f601..541c55dcb 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -1,5 +1,15 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
-
+
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..0df43e091
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Questions & support
+ url: https://forum.duplicati.com
+ about: Please ask and answer questions here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..a6f653e0b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+
+
+**Describe the solution you'd like**
+
+
+**Describe alternatives you've considered**
+
+
+**Additional context**
+
diff --git a/.github/workflows/backendtests.yml b/.github/workflows/backendtests.yml
new file mode 100644
index 000000000..52e23dccf
--- /dev/null
+++ b/.github/workflows/backendtests.yml
@@ -0,0 +1,417 @@
+name: Backend Live Tests
+on: [pull_request, workflow_dispatch]
+concurrency:
+ group: duplicati-ci-livetests
+ cancel-in-progress: false
+jobs:
+ check_secrets:
+ runs-on: ubuntu-latest
+ outputs:
+ s3_secrets_available: "${{ steps.check_s3.outputs.available }}"
+ dropbox_secrets_available: "${{ steps.check_dropbox.outputs.available }}"
+ azure_secrets_available: "${{ steps.check_azure.outputs.available }}"
+ testcontainers_secrets_available: "${{ steps.check_testcontainers.outputs.available }}"
+ google_secrets_available: "${{ steps.check_google.outputs.available }}"
+ pcloud_secrets_available: "${{ steps.check_pcloud.outputs.available }}"
+ steps:
+ - id: check_pcloud
+ name: Check pCloud secrets
+ shell: bash
+ run: |
+ echo "Starting pCloud secrets check..."
+ if [[ -n "${{ secrets.TESTCREDENTIAL_PCLOUD_SERVER }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_PCLOUD_TOKEN }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_PCLOUD_FOLDER }}" ]]; then
+ echo "All pCloud secrets found"
+ echo "available=true" >> $GITHUB_OUTPUT
+ else
+ echo "Missing some pCloud secrets"
+ echo "available=false" >> $GITHUB_OUTPUT
+ fi
+ echo "pCloud check completed"
+ - id: check_s3
+ name: Check S3 secrets
+ shell: bash
+ run: |
+ echo "Starting S3 secrets check..."
+ if [[ -n "${{ secrets.TESTCREDENTIAL_S3_BUCKETNAME }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_S3_KEY }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_S3_REGION }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_S3_SECRET }}" ]]; then
+ echo "All S3 secrets found"
+ echo "available=true" >> $GITHUB_OUTPUT
+ else
+ echo "Missing some S3 secrets"
+ echo "available=false" >> $GITHUB_OUTPUT
+ fi
+ echo "S3 check completed"
+ - id: check_dropbox
+ name: Check Dropbox secrets
+ shell: bash
+ run: |
+ echo "Starting Dropbox secrets check..."
+ if [[ -n "${{ secrets.TESTCREDENTIAL_DROPBOX_FOLDER }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_DROPBOX_TOKEN }}" ]]; then
+ echo "All Dropbox secrets found"
+ echo "available=true" >> $GITHUB_OUTPUT
+ else
+ echo "Missing some Dropbox secrets"
+ echo "available=false" >> $GITHUB_OUTPUT
+ fi
+ echo "Dropbox check completed"
+ - id: check_azure
+ name: Check Azure secrets
+ shell: bash
+ run: |
+ echo "Starting Azure secrets check..."
+ if [[ -n "${{ secrets.TESTCREDENTIAL_AZURE_ACCOUNTNAME }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_AZURE_ACCESSKEY }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_AZURE_CONTAINERNAME }}" ]]; then
+ echo "All Azure secrets found"
+ echo "available=true" >> $GITHUB_OUTPUT
+ else
+ echo "Missing some Azure secrets"
+ echo "available=false" >> $GITHUB_OUTPUT
+ fi
+ echo "Azure check completed"
+ - id: check_testcontainers
+ name: Check TestContainers secret
+ shell: bash
+ run: |
+ echo "Starting TestContainers secrets check..."
+ if [[ -n "${{ secrets.TC_CLOUD_TOKEN }}" ]]; then
+ echo "All TestContainers secrets found"
+ echo "available=true" >> $GITHUB_OUTPUT
+ else
+ echo "Missing TestContainers token"
+ echo "available=false" >> $GITHUB_OUTPUT
+ fi
+ echo "Testcontainers check completed"
+
+ - id: check_google
+ name: Check Google secrets
+ shell: bash
+ run: |
+ echo "Starting Google secrets check..."
+ if [[ -n "${{ secrets.TESTCREDENTIAL_GOOGLEDRIVE_TOKEN }}" ]] && \
+ [[ -n "${{ secrets.TESTCREDENTIAL_GOOGLEDRIVE_FOLDER }}" ]]; then
+ echo "All Google secrets found"
+ echo "available=true" >> $GITHUB_OUTPUT
+ else
+ echo "Missing some Google secrets"
+ echo "available=false" >> $GITHUB_OUTPUT
+ fi
+ echo "Google check completed"
+
+ - name: Debug Output
+ shell: bash
+ run: |
+ echo "S3 Available: ${{ steps.check_s3.outputs.available }}"
+ echo "Dropbox Available: ${{ steps.check_dropbox.outputs.available }}"
+ echo "TestContainers Available: ${{ steps.check_testcontainers.outputs.available }}"
+ echo "Google Available: ${{ steps.check_google.outputs.available }}"
+ test_ftp:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.testcontainers_secrets_available == 'true'
+ name: FTP Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ fail-fast: false
+ max-parallel: 1
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Setup Testcontainers Cloud Client
+ uses: atomicjar/testcontainers-cloud-setup-action@v1
+ with:
+ token: "${{ secrets.TC_CLOUD_TOKEN }}"
+ - name: Run FTP tests
+ env:
+ TC_CLOUD_TOKEN: "${{ secrets.TC_CLOUD_TOKEN }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~FtpTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+
+ test_pcloud:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.pcloud_secrets_available == 'true'
+ name: pCloud Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ max-parallel: 1
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Run S3 tests
+ env:
+ TESTCREDENTIAL_PCLOUD_SERVER: "${{ secrets.TESTCREDENTIAL_PCLOUD_SERVER }}"
+ TESTCREDENTIAL_PCLOUD_TOKEN: "${{ secrets.TESTCREDENTIAL_PCLOUD_TOKEN }}"
+ TESTCREDENTIAL_PCLOUD_FOLDER: "${{ secrets.TESTCREDENTIAL_PCLOUD_FOLDER }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~pCloudTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+
+ test_s3:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.s3_secrets_available == 'true'
+ name: S3 Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ max-parallel: 1
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Run S3 tests
+ env:
+ TC_CLOUD_TOKEN: "${{ secrets.TC_CLOUD_TOKEN }}"
+ TESTCREDENTIAL_S3_BUCKETNAME: "${{ secrets.TESTCREDENTIAL_S3_BUCKETNAME }}"
+ TESTCREDENTIAL_S3_KEY: "${{ secrets.TESTCREDENTIAL_S3_KEY }}"
+ TESTCREDENTIAL_S3_REGION: "${{ secrets.TESTCREDENTIAL_S3_REGION }}"
+ TESTCREDENTIAL_S3_SECRET: "${{ secrets.TESTCREDENTIAL_S3_SECRET }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~S3Tests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ test_azure:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.azure_secrets_available == 'true'
+ name: Azure Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ max-parallel: 1
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Run Azure tests
+ env:
+ TESTCREDENTIAL_AZURE_ACCOUNTNAME: "${{ secrets.TESTCREDENTIAL_AZURE_ACCOUNTNAME }}"
+ TESTCREDENTIAL_AZURE_ACCESSKEY: "${{ secrets.TESTCREDENTIAL_AZURE_ACCESSKEY }}"
+ TESTCREDENTIAL_AZURE_CONTAINERNAME: "${{ secrets.TESTCREDENTIAL_AZURE_CONTAINERNAME }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~AzureTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ test_webdav:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.testcontainers_secrets_available == 'true'
+ name: Webdav Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ max-parallel: 1
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Setup Testcontainers Cloud Client
+ uses: atomicjar/testcontainers-cloud-setup-action@v1
+ with:
+ token: "${{ secrets.TC_CLOUD_TOKEN }}"
+ - name: Run Webdav tests
+ env:
+ TC_CLOUD_TOKEN: "${{ secrets.TC_CLOUD_TOKEN }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~WebDavTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ test_dropbox:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.dropbox_secrets_available == 'true'
+ name: DropBox Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ max-parallel: 1
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Run Dropbox tests
+ env:
+ TC_CLOUD_TOKEN: "${{ secrets.TC_CLOUD_TOKEN }}"
+ TESTCREDENTIAL_DROPBOX_FOLDER: "${{ secrets.TESTCREDENTIAL_DROPBOX_FOLDER }}"
+ TESTCREDENTIAL_DROPBOX_TOKEN: "${{ secrets.TESTCREDENTIAL_DROPBOX_TOKEN }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~DropBoxTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ test_ssh:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.testcontainers_secrets_available == 'true'
+ name: SSH Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ fail-fast: false
+ max-parallel: 1
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Setup Testcontainers Cloud Client
+ uses: atomicjar/testcontainers-cloud-setup-action@v1
+ with:
+ token: "${{ secrets.TC_CLOUD_TOKEN }}"
+ - name: Run SSH tests
+ env:
+ TC_CLOUD_TOKEN: "${{ secrets.TC_CLOUD_TOKEN }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~SshTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ test_googledrive:
+ needs: check_secrets
+ if: needs.check_secrets.outputs.google_secrets_available == 'true'
+ name: Google Drive Tests
+ runs-on: "${{ matrix.os }}"
+ strategy:
+ max-parallel: 1
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ steps:
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
+ - name: Checkout source
+ uses: actions/checkout@v4
+ - name: Restore NuGet dependencies
+ run: >-
+ dotnet restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Build project
+ run: >-
+ dotnet build --no-restore
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
+ - name: Run Googledrive tests
+ env:
+ TESTCREDENTIAL_GOOGLEDRIVE_TOKEN: "${{ secrets.TESTCREDENTIAL_GOOGLEDRIVE_TOKEN }}"
+ TESTCREDENTIAL_GOOGLEDRIVE_FOLDER: "${{ secrets.TESTCREDENTIAL_GOOGLEDRIVE_FOLDER }}"
+ run: >-
+ dotnet test --no-build --filter="ClassName~GoogleDriveTests"
+ --logger:"console;verbosity=detailed"
+ LiveTests/Duplicati.Backend.Tests/Duplicati.Backend.Tests.sln
diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml
new file mode 100644
index 000000000..977a0a9ea
--- /dev/null
+++ b/.github/workflows/freebsd.yml
@@ -0,0 +1,143 @@
+name: Freebsd
+on:
+ push:
+ branches: "**freebsd**"
+ pull_request:
+ branches: "**freebsd**"
+
+jobs:
+ zip:
+ runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v2
+ - name: Download dotnet
+ # See also https://github.com/sec/dotnet-core-freebsd-source-build
+ # See also https://github.com/Thefrank/dotnet-freebsd-crossbuild
+ run: curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/v6.0.401-11/dotnet-sdk-6.0.401-freebsd-x64.tar.gz -o dn.tgz
+ - name: Build
+ id: test
+ uses: vmactions/freebsd-vm@v0
+ with:
+ usesh: true
+ # sync: sshfs
+ prepare: pkg install -y curl libinotify libunwind git cmake ninja bash wget icu
+ run: |
+ #!/bin/bash
+ set -x
+ set -e
+ pwd
+ ls -lah
+ whoami
+ env
+ freebsd-version
+ df -h
+ dmesg | grep memory
+ swapinfo
+ dd if=/dev/zero of=/root/swap1 bs=1m count=20480
+ chmod 0600 /root/swap1
+ mdconfig -a -t vnode -f /root/swap1 -u 0
+ swapon /dev/md0
+ df -h
+ swapinfo
+
+ export DOTNET_CLI_TELEMETRY_OPTOUT=1
+ BUILD_DIR=`pwd`
+ cd /var/tmp
+ mkdir dn
+ cd dn
+ echo Unpacking .NET
+ tar -xzf ${BUILD_DIR}/dn.tgz
+ cd ${BUILD_DIR}
+ mkdir /var/tmp/nuget
+ export RVERSION=v6.0.401-11
+ export VERSION=6.0.9
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.AspNetCore.App.Runtime.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.AspNetCore.App.Runtime.freebsd-x64.${VERSION}.nupkg
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.NETCore.App.Host.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.NETCore.App.Host.freebsd-x64.${VERSION}.nupkg
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.NETCore.App.Runtime.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.NETCore.App.Runtime.freebsd-x64.${VERSION}.nupkg
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.NETCore.App.Crossgen2.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.NETCore.App.Crossgen2.freebsd-x64.${VERSION}.nupkg
+ /var/tmp/dn/dotnet nuget add source /var/tmp/nuget/
+
+ cp -R `pwd` /var/tmp/build
+ cd /var/tmp/build
+
+ echo Restore
+ /var/tmp/dn/dotnet restore --runtime=freebsd-x64 Duplicati.sln
+ echo Build
+ /var/tmp/dn/dotnet build Duplicati.sln
+ echo Publish
+ /var/tmp/dn/dotnet publish -c Release --runtime=freebsd-x64 -o publish Duplicati.sln
+
+ cp -R ./publish ${BUILD_DIR}/publish
+ - name: Save Artifacts
+ uses: actions/upload-artifact@v2
+ with:
+ name: duplicati-freebsd-x64
+ path: publish
+ tests:
+ runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v2
+ - name: Download dotnet
+ # See also https://github.com/sec/dotnet-core-freebsd-source-build
+ # See also https://github.com/Thefrank/dotnet-freebsd-crossbuild
+ run: curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/v6.0.401-11/dotnet-sdk-6.0.401-freebsd-x64.tar.gz -o dn.tgz
+ - name: Run test
+ id: test
+ uses: vmactions/freebsd-vm@v0
+ with:
+ usesh: true
+ # sync: sshfs
+ prepare: pkg install -y curl libinotify libunwind git cmake ninja bash wget icu
+ run: |
+ #!/bin/bash
+ set -x
+ set -e
+ pwd
+ ls -lah
+ whoami
+ env
+ freebsd-version
+ df -h
+ dmesg | grep memory
+ swapinfo
+ dd if=/dev/zero of=/root/swap1 bs=1m count=20480
+ chmod 0600 /root/swap1
+ mdconfig -a -t vnode -f /root/swap1 -u 0
+ swapon /dev/md0
+ df -h
+ swapinfo
+
+ export DOTNET_CLI_TELEMETRY_OPTOUT=1
+ BUILD_DIR=`pwd`
+ cd /var/tmp
+ mkdir dn
+ cd dn
+ echo Unpacking .NET
+ tar -xzf ${BUILD_DIR}/dn.tgz
+ cd ${BUILD_DIR}
+ mkdir /var/tmp/nuget
+ export RVERSION=v6.0.401-11
+ export VERSION=6.0.9
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.AspNetCore.App.Runtime.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.AspNetCore.App.Runtime.freebsd-x64.${VERSION}.nupkg
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.NETCore.App.Host.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.NETCore.App.Host.freebsd-x64.${VERSION}.nupkg
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.NETCore.App.Runtime.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.NETCore.App.Runtime.freebsd-x64.${VERSION}.nupkg
+ curl -L https://github.com/Thefrank/dotnet-freebsd-crossbuild/releases/download/${RVERSION}/Microsoft.NETCore.App.Crossgen2.freebsd-x64.${VERSION}.nupkg -o /var/tmp/nuget/Microsoft.NETCore.App.Crossgen2.freebsd-x64.${VERSION}.nupkg
+ /var/tmp/dn/dotnet nuget add source /var/tmp/nuget/
+
+ cp -R `pwd` /var/tmp/build
+ cd /var/tmp/build
+
+ echo Restore
+ /var/tmp/dn/dotnet restore --runtime=freebsd-x64 Duplicati.sln
+ echo Testing
+ mkdir -p ${BUILD_DIR}/duplicati_testdata/logs
+ mkdir -p /var/tmp/duplicati_testdata
+ ln -s ${BUILD_DIR}/duplicati_testdata/logs /var/tmp/duplicati_testdata/logs
+ export UNITTEST_BASEFOLDER=/var/tmp/duplicati_testdata
+ /var/tmp/dn/dotnet test --runtime=freebsd-x64 --verbosity minimal Duplicati.sln
+ - name: Archive Test Logs
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: test-logs
+ path: duplicati_testdata/logs/*
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..2ec105e2a
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,26 @@
+name: Close inactive issues
+on:
+ schedule:
+ - cron: "30 1 * * *"
+
+jobs:
+ close-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ actions: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ days-before-issue-stale: 15
+ days-before-issue-close: 15
+ stale-issue-label: "stale"
+ stale-issue-message: "This issue is stale because it has been open for 15 days with no activity."
+ close-issue-message: "This issue was closed because it has been inactive for 15 days since being marked as stale."
+ days-before-pr-stale: -1
+ days-before-pr-close: -1
+ exempt-all-assignees: true
+ only-labels: "pending user feedback"
+ exempt-issue-labels: "bug,enhancement,good first issue,backend enhancement,backend issue,backup corruption,bounty,bugreport attached,core logic,docker,filters,help wanted,linux,localization,MacOS,mono,performance issue,reproduced,server side,ssl/tls issue,Synology,tests,translation,UI,windows"
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ operations-per-run: 500
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 046fa2aa1..67e1244ea 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,9 +1,6 @@
name: Tests
-on: [push, pull_request]
-
-env:
- build_directory: "Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release"
+on: [pull_request, workflow_dispatch]
jobs:
unit_tests:
@@ -12,81 +9,29 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ ubuntu-latest, windows-latest, macos-latest ]
+ os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- - name: Add msbuild to PATH (Windows)
- if: matrix.os == 'windows-latest'
- uses: microsoft/setup-msbuild@v1.0.2
+ - name: Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.x
- - name: Install NuGet
- uses: nuget/setup-nuget@v1.0.5
+ - name: Checkout source
+ uses: actions/checkout@v4
- - name: Checkout Source
- uses: actions/checkout@v2.3.4
+ - name: Restore NuGet dependencies
+ run: dotnet restore Duplicati.sln
- - name: Restore NuGet Packages
- run: nuget restore Duplicati.sln
+ - name: Build Duplicati
+ run: dotnet build --no-restore Duplicati.sln
- - name: Install Additional Dependencies
- if: matrix.os == 'ubuntu-latest'
- run: sudo apt-get install gtk-sharp2
+ - name: Run unit tests
+ run: dotnet test --no-build --verbosity minimal Duplicati.sln
- - name: Build Duplicati (Linux and macOS)
- if: matrix.os != 'windows-latest'
- run: msbuild -p:Configuration=Release -p:DefineConstants=ENABLE_GTK Duplicati.sln
-
- - name: Build Duplicati (Windows)
- if: matrix.os == 'windows-latest'
- run: msbuild -p:Configuration=Release Duplicati.sln
-
- - name: Run Unit Tests (Linux and macOS)
- if: matrix.os != 'windows-latest'
- run: |
- nuget install NUnit.Runners -Version 3.12.0 -OutputDirectory nunit
- mono nunit/NUnit.ConsoleRunner.3.12.0/tools/nunit3-console.exe Duplicati/UnitTest/bin/Release/Duplicati.UnitTest.dll
-
- - name: Run Unit Tests (Windows)
- if: matrix.os == 'windows-latest'
- run: |
- nuget install NUnit.Runners -Version 3.12.0 -OutputDirectory nunit
- nunit/NUnit.ConsoleRunner.3.12.0/tools/nunit3-console.exe Duplicati/UnitTest/bin/Release/Duplicati.UnitTest.dll
-
- ui_tests:
- name: UI tests
+ selenium:
runs-on: ubuntu-latest
-
- # Some of these steps duplicate those in the unit_tests job above. This
- # can be cleaned up using a composite run steps action once it supports
- # what we need (https://github.com/actions/runner/issues/646).
steps:
- - name: Checkout Source
- uses: actions/checkout@v2.3.4
-
- - name: Install NuGet
- uses: nuget/setup-nuget@v1.0.5
-
- - name: Restore NuGet Packages
- run: nuget restore Duplicati.sln
-
- - name: Install Additional Dependencies
- run: sudo apt-get install gtk-sharp2
-
- - name: Build Duplicati
- run: msbuild -p:Configuration=Release -p:DefineConstants=ENABLE_GTK Duplicati.sln
-
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: 3.x
-
- - name: Install dependencies
- run: |
- pip3 install selenium
- pip3 install --upgrade urllib3
-
- - name: Run UI tests
- run: |
- cp -r Duplicati/Server/webroot "${{ env.build_directory }}"/webroot
- mono "${{ env.build_directory }}"/Duplicati.Server.exe &
- python3 guiTests/guiTest.py
+ - uses: actions/checkout@v4
+ - name: Selenium
+ run: pipeline/selenium/test.sh
diff --git a/.gitignore b/.gitignore
index 7ba91052e..769557ef3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,24 +4,20 @@ obj/
*.userprefs
*.user
*.suo
-Installer/Paraffin.exe
-Installer/account_info
Duplicati/Localization/compiled
Duplicati/Localization/*/
Duplicati/Localization/*.diff.csv
Duplicati/Localization/*.new.csv
Duplicati/Localization/*.updated.csv
Duplicati/Localization/report.*.xml
-Installer/incLocFiles.*.wxs
Duplicati/Server/webroot/extjs-*
-Installer/binfiles.wxs
-Installer/generated-keys.xml
-Installer/oem
-Updates/build
+Duplicati/Server/webroot/*/node_modules/
packages
+node_modules
changelog-news.txt
-binfiles.wxs
.idea/
.vs/
-
-Installer/debian/*.buildinfo
+/publish/
+ReleaseBuilder/build-temp/
+ReleaseBuilder/Resources/Windows/TrayIcon/binfiles.wxs
+ReleaseBuilder/Resources/Windows/Agent/binfiles.wxs
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 000000000..a7e9cd34f
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "stylelint-config-standard-less"
+}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 225209504..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,83 +0,0 @@
-language: minimal
-services: docker
-before_install:
- - sudo apt-get install moreutils
- - set -o pipefail
-addons:
- coverity_scan:
- project:
- name: "duplicati/duplicati"
- description: "Duplicati Build submitted via Travis CI"
- notification_email: kenneth@duplicati.com
- build_command_prepend: "msbuild /t:Clean"
- build_command: "msbuild /t:Rebuild"
- branch_pattern: coverity_scan
-env:
- global:
- # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
- # via the "travis encrypt" command using the project repo's public key
- - secure: "ijaQDcVcMo/utuP514FiDZVGR5LJbATWmYDmgRIIaI0YG2Rk6uuJp9XXDXr8sx66JWttPGgs7Htqfy50VKHdr7nsfyCQCRNGmqF+OJeUgAQMj5spxn9A7PZY9+GVhnXoJ06wvYjCv5MPeLBMDK/KpIM5Ny5IG+TKzQy4U7Dyg0riMUT61aqzxSjTGfHZPmaqBICJTzsRmeGHglemFUz/1B4NAN+IeAtXoH9YIfA2LjJfYmMLckPgFQLKN/tQRaGPWXh77y0qVgA++K8UVu5W3yGo3JXBPDdabGgKs9BPlfN5/Ixnkcy6ekAubDA/Aspur9x7/szj4/AnGMY6frNB33nQcnJQZ/51GYKr6CXCmjoJQuWc8ZKdEMqDn3w/kyEzvluSjAsJNRVboeWPE4VfpAeUBsjQ+TuEQi8WXhN8VJgZV7yCkVlZTVbhtOxMPsBiFb74lpWC2iQuWLJv9ED6vyXZVzZhNtfjXNXNZzIwYxbcd7I0ZQ8fmDstFnvBJveJby2ZRpECXTbc03eIUq1SAaVJJY2r47RD1PwVqM3xvUTljIqtKdyIz915mEOjqG1BnCDaiGHYJ4byuDDeIUZO9X70s60Gh4hupZA2Ruup0v7SfYgkto6aNcStd0NYd716Vd7hqXNTp6nu01L5YYJDY4h5sHyBXVXbxCKlUb01kUw="
- - USAGEREPORTER_Duplicati_LEVEL=none
- - AUTOUPDATER_Duplicati_SKIP_UPDATE=1
- - BUILD_DIR=${TRAVIS_BUILD_DIR}/../.build
- - TEST_DIR=${TRAVIS_BUILD_DIR}/../.test
- - ROOT_DIR="$TRAVIS_BUILD_DIR"
-
-# matrix expansion within jobs would be good:
-# https://github.com/travis-ci/travis-ci/issues/8295
-
-# There is one cache per branch, so concurrent builds on different branches possible!
-# DO NOT SET ENVIRONMENT VARIABLES WITHIN JOBS, OR CACHES WILL NOT BE SHARED AMONGST JOBS!
-
-
-jobs:
- include:
- - stage: build
- cache:
- directories:
- - $BUILD_DIR
- script:
- - ${ROOT_DIR}/pipeline/jobs/build_job.sh
-
-
- - stage: tests
- cache:
- directories:
- - $BUILD_DIR
- script:
- - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories BulkNormal
- - stage: tests
- cache:
- directories:
- - $BUILD_DIR
- script:
- - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories BulkNoSize
- - stage: tests
- cache:
- directories:
- - $BUILD_DIR
- script:
- - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories SVNDataLong,SVNData,RecoveryTool,ProblematicPath,SymLink
- - stage: tests
- cache:
- directories:
- - $BUILD_DIR
- script:
- - travis_wait ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories Border
- - stage: tests
- cache:
- directories:
- - $BUILD_DIR
- script:
- - ${ROOT_DIR}/pipeline/jobs/unittest_job.sh --testcategories Filter,Targeted,Purge,Serialization,WebApi,Utility,UriUtility,IO,ImportExport,Disruption,RestoreHandler,RepairHandler,DeleteHandler,Controller
-
- - stage: tests
- cache:
- directories:
- - $BUILD_DIR
- script:
- - ${ROOT_DIR}/pipeline/jobs/selenium_job.sh
- addons:
- sauce_connect:
- username: $SAUCE_USERNAME
- access_key: $SAUCE_ACCESS_KEY
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..ba69c97a3
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,119 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Launch TrayIcon executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.GUI.TrayIcon/bin/Debug/net8.0/Duplicati.GUI.TrayIcon",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch Server executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.Server/bin/Debug/net8.0/Duplicati.Server",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch CommandLine executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine/bin/Debug/net8.0/Duplicati.CommandLine",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch AutoUpdater executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine.AutoUpdater/bin/Debug/net8.0/Duplicati.CommandLine.AutoUpdater",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch RecoveryTool executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine.RecoveryTool/bin/Debug/net8.0/Duplicati.CommandLine.RecoveryTool",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch ServerUtil executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine.ServerUtil/bin/Debug/net8.0/Duplicati.CommandLine.ServerUtil",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch BackendTool executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine.BackendTool/bin/Debug/net8.0/Duplicati.CommandLine.BackendTool",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch Agent executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.Agent/bin/Debug/net8.0/Duplicati.Agent",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch BackendTester executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine.BackendTester/bin/Debug/net8.0/Duplicati.CommandLine.BackendTester",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "Launch SecretProvider executable",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceFolder}/Executables/net8/Duplicati.CommandLine.SecretTool/bin/Debug/net8.0/Duplicati.CommandLine.SecretTool",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..31c32bd34
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,24 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "shell",
+ "args": [
+ "build",
+ // Ask dotnet build to generate full paths for file names.
+ "/property:GenerateFullPaths=true",
+ // Do not generate summary otherwise it leads to duplicate errors in Problems panel
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "group": "build",
+ "presentation": {
+ "reveal": "silent"
+ },
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.csproj b/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.csproj
deleted file mode 100644
index d35b0d6ec..000000000
--- a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.csproj
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
- Debug
- AnyCPU
- {17E3EB29-87FD-4BE9-A531-8DAD8B295494}
- Exe
- False
- AutoUpdateBuilder
- AutoUpdateBuilder
- v4.7.1
-
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- false
-
-
- full
- true
- bin\Release
- prompt
- 4
- false
-
-
-
- packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
-
-
-
-
-
-
-
-
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}
- Duplicati.Library.Compression
-
-
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}
- Duplicati.Library.Encryption
-
-
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}
- Duplicati.Library.Interface
-
-
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}
- Duplicati.Library.Logging
-
-
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}
- Duplicati.Library.Utility
-
-
- {7E119745-1F62-43F0-936C-F312A1912C0B}
- Duplicati.Library.AutoUpdater
-
-
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}
- Duplicati.License
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln b/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln
deleted file mode 100644
index 667b5cf3a..000000000
--- a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln
+++ /dev/null
@@ -1,74 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoUpdateBuilder", "AutoUpdateBuilder.csproj", "{17E3EB29-87FD-4BE9-A531-8DAD8B295494}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Compression", "..\..\Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "..\..\Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Interface", "..\..\Duplicati\Library\Interface\Duplicati.Library.Interface.csproj", "{C5899F45-B0FF-483C-9D38-24A9FCAAB237}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Logging", "..\..\Duplicati\Library\Logging\Duplicati.Library.Logging.csproj", "{D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "..\..\Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{94484FDB-2EFA-4CF0-9BE6-A561157B4F87}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.AutoUpdater", "..\..\Duplicati\Library\AutoUpdater\Duplicati.Library.AutoUpdater.csproj", "{7E119745-1F62-43F0-936C-F312A1912C0B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.License", "..\..\Duplicati\License\Duplicati.License.csproj", "{4D012CB1-4B92-47F4-89B7-BF80A73A2E99}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Localization", "..\..\Duplicati\Library\Localization\Duplicati.Library.Localization.csproj", "{B68F2214-951F-4F78-8488-66E1ED3F50BF}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Common", "..\..\Duplicati\Library\Common\Duplicati.Library.Common.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACF}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Release|Any CPU.Build.0 = Release|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.Build.0 = Release|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Release|Any CPU.Build.0 = Release|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.Build.0 = Release|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.Build.0 = Release|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.Build.0 = Release|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|Any CPU.Build.0 = Release|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.Build.0 = Release|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.Build.0 = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = AutoUpdateBuilder.csproj
- EndGlobalSection
-EndGlobal
diff --git a/BuildTools/AutoUpdateBuilder/Program.cs b/BuildTools/AutoUpdateBuilder/Program.cs
deleted file mode 100644
index 4737406d2..000000000
--- a/BuildTools/AutoUpdateBuilder/Program.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Security.Cryptography;
-
-namespace AutoUpdateBuilder
-{
- public class Program
- {
- private static RSACryptoServiceProvider privkey;
-
- private static string keyfile;
- private static string keyfilepassword;
-
- private static void CompareToManifestPublicKey()
- {
- if (Duplicati.Library.AutoUpdater.AutoUpdateSettings.SignKey == null || privkey.ToXmlString(false) != Duplicati.Library.AutoUpdater.AutoUpdateSettings.SignKey.ToXmlString(false))
- {
- Console.WriteLine("The public key in the project is not the same as the public key from the file");
- Console.WriteLine("Try setting the key to: ");
- Console.WriteLine(privkey.ToXmlString(false));
- System.Environment.Exit(5);
- }
- }
-
- private static void LoadKeyFromFile()
- {
- using (var enc = new Duplicati.Library.Encryption.AESEncryption(keyfilepassword, new Dictionary()))
- using (var ms = new System.IO.MemoryStream())
- using (var fs = System.IO.File.OpenRead(keyfile))
- {
- enc.Decrypt(fs, ms);
- ms.Position = 0;
-
- using (var sr = new System.IO.StreamReader(ms))
- privkey.FromXmlString(sr.ReadToEnd());
- }
- }
-
- public static int Main(string[] _args)
- {
- var args = new List(_args);
- var opts = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args);
-
- opts.TryGetValue("input", out string inputfolder);
- opts.TryGetValue("output", out string outputfolder);
- opts.TryGetValue("allow-new-key", out string allowNewKey);
- opts.TryGetValue("keyfile", out keyfile);
- opts.TryGetValue("manifest", out string manifestfile);
- opts.TryGetValue("keyfile-password", out keyfilepassword);
-
- var usedoptions = new [] { "allow-new-key", "input", "output", "keyfile", "manifest", "keyfile-password" };
-
- if (string.IsNullOrWhiteSpace(inputfolder))
- {
- Console.WriteLine("Missing input folder");
- return 4;
- }
-
- if (string.IsNullOrWhiteSpace(outputfolder))
- {
- Console.WriteLine("Missing output folder");
- return 4;
- }
-
- if (string.IsNullOrWhiteSpace(keyfile))
- {
- Console.WriteLine("Missing keyfile");
- return 4;
- }
-
- if (!System.IO.Directory.Exists(inputfolder))
- {
- Console.WriteLine("Input folder not found");
- return 4;
- }
-
- if (string.IsNullOrWhiteSpace(keyfilepassword))
- {
- Console.WriteLine("Enter keyfile passphrase: ");
- keyfilepassword = Console.ReadLine().Trim();
- }
-
- if (!System.IO.File.Exists(keyfile))
- {
- Console.WriteLine("Keyfile not found, creating new");
- var newkey = RSA.Create().ToXmlString(true);
- using (var enc = new Duplicati.Library.Encryption.AESEncryption(keyfilepassword, new Dictionary()))
- using (var fs = System.IO.File.OpenWrite(keyfile))
- using (var ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(newkey)))
- enc.Encrypt(ms, fs);
- }
-
- if (!System.IO.Directory.Exists(outputfolder))
- System.IO.Directory.CreateDirectory(outputfolder);
-
- privkey = (RSACryptoServiceProvider) RSA.Create();
-
- LoadKeyFromFile();
-
- if (!Boolean.TryParse(allowNewKey, out Boolean newKeyAllowed) || !newKeyAllowed)
- {
- CompareToManifestPublicKey();
- }
-
- Duplicati.Library.AutoUpdater.UpdateInfo updateInfo;
-
- using (var fs = System.IO.File.OpenRead(manifestfile))
- using (var sr = new System.IO.StreamReader(fs))
- using (var jr = new Newtonsoft.Json.JsonTextReader(sr))
- updateInfo = new Newtonsoft.Json.JsonSerializer().Deserialize(jr);
-
- var isopts = new Dictionary(opts, StringComparer.InvariantCultureIgnoreCase);
- foreach (var usedopt in usedoptions)
- {
- isopts.Remove(usedopt);
- }
-
- foreach (var k in updateInfo.GetType().GetFields())
- {
- if (!isopts.ContainsKey(k.Name))
- {
- continue;
- }
- try
- {
- //Console.WriteLine("Setting {0} to {1}", k.Name, isopts[k.Name]);
- if (k.FieldType == typeof(string[]))
- k.SetValue(updateInfo, isopts[k.Name].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
- else if (k.FieldType == typeof(Version))
- k.SetValue(updateInfo, new Version(isopts[k.Name]));
- else if (k.FieldType == typeof(int))
- k.SetValue(updateInfo, int.Parse(isopts[k.Name]));
- else if (k.FieldType == typeof(long))
- k.SetValue(updateInfo, long.Parse(isopts[k.Name]));
- else
- k.SetValue(updateInfo, isopts[k.Name]);
- }
- catch (Exception ex)
- {
- Console.WriteLine("Failed setting {0} to {1}: {2}", k.Name, isopts[k.Name], ex.Message);
- }
-
- isopts.Remove(k.Name);
- }
-
- foreach (var opt in isopts)
- {
- Console.WriteLine("Warning! unused option: {0} = {1}", opt.Key, opt.Value);
- }
-
- using (var tf = new Duplicati.Library.Utility.TempFile())
- {
- using (var fs = System.IO.File.OpenWrite(tf))
- using (var tw = new System.IO.StreamWriter(fs))
- new Newtonsoft.Json.JsonSerializer().Serialize(tw, updateInfo);
-
- Duplicati.Library.AutoUpdater.UpdaterManager.CreateUpdatePackage(privkey, inputfolder, outputfolder, tf);
- }
-
- return 0;
- }
- }
-}
\ No newline at end of file
diff --git a/BuildTools/AutoUpdateBuilder/Properties/AssemblyInfo.cs b/BuildTools/AutoUpdateBuilder/Properties/AssemblyInfo.cs
deleted file mode 100644
index d053718e1..000000000
--- a/BuildTools/AutoUpdateBuilder/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("AutoUpdateBuilder")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("2.0.0.7")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
-
diff --git a/BuildTools/AutoUpdateBuilder/app.config b/BuildTools/AutoUpdateBuilder/app.config
deleted file mode 100644
index d7c60fd53..000000000
--- a/BuildTools/AutoUpdateBuilder/app.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/BuildTools/AutoUpdateBuilder/packages.config b/BuildTools/AutoUpdateBuilder/packages.config
deleted file mode 100644
index 5e9c8c41c..000000000
--- a/BuildTools/AutoUpdateBuilder/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/BuildTools/DependencyFinder/DependencyFinder.csproj b/BuildTools/DependencyFinder/DependencyFinder.csproj
deleted file mode 100644
index 4274d9a60..000000000
--- a/BuildTools/DependencyFinder/DependencyFinder.csproj
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
- Debug
- x86
- {3E891818-579A-4C06-8066-4C702BFA5921}
- Exe
- False
- DependencyFinder
- DependencyFinder
- v4.7.1
-
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- x86
- false
- ../../../../Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Debug
-
-
- full
- true
- bin\Release
- prompt
- 4
- true
- x86
-
-
-
- packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll
-
-
- packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll
-
-
- packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll
-
-
- packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/BuildTools/DependencyFinder/DependencyFinder.sln b/BuildTools/DependencyFinder/DependencyFinder.sln
deleted file mode 100644
index cd8aea67c..000000000
--- a/BuildTools/DependencyFinder/DependencyFinder.sln
+++ /dev/null
@@ -1,17 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyFinder", "DependencyFinder.csproj", "{3E891818-579A-4C06-8066-4C702BFA5921}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x86 = Debug|x86
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {3E891818-579A-4C06-8066-4C702BFA5921}.Debug|x86.ActiveCfg = Debug|x86
- {3E891818-579A-4C06-8066-4C702BFA5921}.Debug|x86.Build.0 = Debug|x86
- {3E891818-579A-4C06-8066-4C702BFA5921}.Release|x86.ActiveCfg = Release|x86
- {3E891818-579A-4C06-8066-4C702BFA5921}.Release|x86.Build.0 = Release|x86
- EndGlobalSection
-EndGlobal
diff --git a/BuildTools/DependencyFinder/Program.cs b/BuildTools/DependencyFinder/Program.cs
deleted file mode 100644
index e819cac0b..000000000
--- a/BuildTools/DependencyFinder/Program.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Mono.Cecil;
-using System.IO;
-using System.Linq;
-
-namespace DependencyFinder
-{
- class MainClass
- {
- public static void Main(string[] args)
- {
- var lst =
- Directory.EnumerateFiles(args[0], "*.exe", SearchOption.AllDirectories).Union(
- Directory.EnumerateFiles(args[0], "*.dll", SearchOption.AllDirectories))
- .Select(x => {
- try { return AssemblyDefinition.ReadAssembly(x); }
- catch { return null; }
- }).Where(x => x != null)
- .Distinct();
-
- PoC(lst, Console.Out, new [] { args[0] });
-
- }
-
- //Adapted from: https://stackoverflow.com/questions/9262464/tool-to-show-assembly-dependencies
- public static void PoC(IEnumerable assemblies, TextWriter writer, IEnumerable searchfolders)
- {
- var resolver = new DefaultAssemblyResolver();
- searchfolders.ToList().ForEach(x => resolver.AddSearchDirectory(x));
-
- //writer.WriteLine("digraph Dependencies {");
- var loaded = assemblies
- .SelectMany(a => a.Modules.Cast())
- .SelectMany(m => m.AssemblyReferences.Cast())
- .Distinct()
- .Select(asm => {
- var dllname = asm.Name + ".dll";
- //Console.WriteLine("Probing for {0}", dllname);
- try { return AssemblyDefinition.ReadAssembly(dllname); }
- catch { }
- try { return resolver.Resolve(asm.FullName); }
- catch { }
-
- return null;
- })
- .Where(assembly => assembly != null)
- .ToList();
-
- //loaded.ForEach(a => a.MainModule.ReadSymbols());
-
- loaded.Select(x => x.FullName).Distinct().OrderBy(x => x).ToList().ForEach(x => writer.WriteLine("{0}", x));
- /*loaded.ForEach(a =>
- {
- foreach (var r in a.MainModule.AssemblyReferences.Cast())
- writer.WriteLine(@"""{0}"" -> ""{1}"";", r.Name, a.Name.Name);
- } );*/
-
- //writer.WriteLine("}");
- }
-
-
- }
-}
diff --git a/BuildTools/DependencyFinder/Properties/AssemblyInfo.cs b/BuildTools/DependencyFinder/Properties/AssemblyInfo.cs
deleted file mode 100644
index 334f017e8..000000000
--- a/BuildTools/DependencyFinder/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("DependencyFinder")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("kenneth")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("2.0.0.7")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
-
diff --git a/BuildTools/DependencyFinder/app.config b/BuildTools/DependencyFinder/app.config
deleted file mode 100644
index d7c60fd53..000000000
--- a/BuildTools/DependencyFinder/app.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/BuildTools/DependencyFinder/packages.config b/BuildTools/DependencyFinder/packages.config
deleted file mode 100644
index 0cb2b0939..000000000
--- a/BuildTools/DependencyFinder/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/BuildTools/GnupgSigningTool/GnupgSigningTool.csproj b/BuildTools/GnupgSigningTool/GnupgSigningTool.csproj
deleted file mode 100644
index e416fc8c0..000000000
--- a/BuildTools/GnupgSigningTool/GnupgSigningTool.csproj
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
- Debug
- x86
- {72B910F2-8E80-4955-A0D6-ED4C35CEE665}
- Exe
- GnupgSigningTool
- GnupgSigningTool
- v4.7.1
-
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- x86
-
-
- true
- bin\Release
- prompt
- 4
- x86
-
-
- true
-
-
-
-
-
-
-
-
-
-
- {2cf2d90e-c25b-47ad-91e0-98451bab8058}
- Duplicati.Library.Encryption
-
-
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}
- Duplicati.Library.Utility
-
-
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}
- Duplicati.Library.Interface
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/BuildTools/GnupgSigningTool/GnupgSigningTool.sln b/BuildTools/GnupgSigningTool/GnupgSigningTool.sln
deleted file mode 100644
index 488ebd3d4..000000000
--- a/BuildTools/GnupgSigningTool/GnupgSigningTool.sln
+++ /dev/null
@@ -1,42 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29123.88
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GnupgSigningTool", "GnupgSigningTool.csproj", "{72B910F2-8E80-4955-A0D6-ED4C35CEE665}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "..\..\Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "..\..\Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{2CF2D90E-C25B-47AD-91E0-98451BAB8058}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Interface", "..\..\Duplicati\Library\Interface\Duplicati.Library.Interface.csproj", "{C5899F45-B0FF-483C-9D38-24A9FCAAB237}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x86 = Debug|x86
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {72B910F2-8E80-4955-A0D6-ED4C35CEE665}.Debug|x86.ActiveCfg = Debug|x86
- {72B910F2-8E80-4955-A0D6-ED4C35CEE665}.Debug|x86.Build.0 = Debug|x86
- {72B910F2-8E80-4955-A0D6-ED4C35CEE665}.Release|x86.ActiveCfg = Release|x86
- {72B910F2-8E80-4955-A0D6-ED4C35CEE665}.Release|x86.Build.0 = Release|x86
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|x86.ActiveCfg = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|x86.Build.0 = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|x86.ActiveCfg = Release|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|x86.Build.0 = Release|Any CPU
- {2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Debug|x86.Build.0 = Debug|Any CPU
- {2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Release|x86.ActiveCfg = Release|Any CPU
- {2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Release|x86.Build.0 = Release|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|x86.Build.0 = Debug|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|x86.ActiveCfg = Release|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {71CC6B1F-7F52-4059-8761-30B3D0501ECC}
- EndGlobalSection
-EndGlobal
diff --git a/BuildTools/GnupgSigningTool/Program.cs b/BuildTools/GnupgSigningTool/Program.cs
deleted file mode 100644
index 3fd35b5d9..000000000
--- a/BuildTools/GnupgSigningTool/Program.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace GnupgSigningTool
-{
- public static class Program
- {
-
- private static string keyfilepassword;
-
- private static string gpgkeypassphrase;
- private static string gpgkeyfile;
- private static string gpgpath;
- private static string gpgkeyid;
- private static bool useArmor;
-
- private static string inputFile;
- private static string signatureFile;
-
- private static void SpawnGPG()
- {
-
- var armorOption = useArmor ? "--armor" : "";
-
- var gpgArgument = string.Format("--pinentry-mode loopback --passphrase-fd 0 --batch --yes {0} -u \"{1}\" --output \"{2}\" --detach-sig \"{3}\"",
- armorOption,
- gpgkeyid,
- signatureFile,
- inputFile);
-
- var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
- {
- FileName = gpgpath,
- Arguments = gpgArgument,
- RedirectStandardInput = true,
- UseShellExecute = false
- });
-
- proc.StandardInput.WriteLine(gpgkeypassphrase);
-
- proc.WaitForExit();
- }
-
- private static void LoadGPGKeyIdAndPassphrase()
- {
- using (var enc = new Duplicati.Library.Encryption.AESEncryption(keyfilepassword, new Dictionary()))
- using (var ms = new System.IO.MemoryStream())
- using (var fs = System.IO.File.OpenRead(gpgkeyfile))
- {
- try
- {
- enc.Decrypt(fs, ms);
- }
- catch (System.Security.Cryptography.CryptographicException e)
- {
- throw new ArgumentException("Failed to decrypt gpg secret credentials file: {0}\n", e.Message);
- }
-
- ms.Position = 0;
-
- using (var sr = new System.IO.StreamReader(ms))
- {
- var lines = sr.ReadToEnd().Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
- gpgkeyid = lines[0];
- gpgkeypassphrase = lines[1];
- }
- }
- }
-
-
- public static void Main(string[] _args)
- {
- var args = new List(_args);
- var opts = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args);
-
- opts.TryGetValue("inputfile", out inputFile);
- opts.TryGetValue("signaturefile", out signatureFile);
- opts.TryGetValue("keyfile-password", out keyfilepassword);
- opts.TryGetValue("gpgkeyfile", out gpgkeyfile);
- opts.TryGetValue("gpgpath", out gpgpath);
- opts.TryGetValue("armor", out string armor);
-
- useArmor = Boolean.TryParse(armor, out useArmor) && useArmor;
-
- if (string.IsNullOrWhiteSpace(gpgkeyfile))
- {
- throw new ArgumentException("No gpgfile with encrypted credentials specified.");
- }
-
- if (!System.IO.File.Exists(gpgkeyfile))
- {
- throw new ArgumentException("Specified file with encrypted gpg credentials not found.");
- }
-
- LoadGPGKeyIdAndPassphrase();
-
- if (gpgkeyid is null || gpgkeypassphrase is null)
- {
- throw new ArgumentException("Could not fetch gpg key id or gpg passphrase.");
- }
-
- gpgpath = gpgpath ?? Duplicati.Library.Encryption.GPGEncryption.GetGpgProgramPath();
-
- SpawnGPG();
- }
- }
-}
diff --git a/BuildTools/GnupgSigningTool/app.config b/BuildTools/GnupgSigningTool/app.config
deleted file mode 100644
index d7c60fd53..000000000
--- a/BuildTools/GnupgSigningTool/app.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/BuildTools/LicenseUpdater/Fragments.cs b/BuildTools/LicenseUpdater/Fragments.cs
new file mode 100755
index 000000000..f53a635de
--- /dev/null
+++ b/BuildTools/LicenseUpdater/Fragments.cs
@@ -0,0 +1,84 @@
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.Text.RegularExpressions;
+
+namespace LicenseUpdater;
+
+public static class Fragments
+{
+ public static string NEW_LICENSE = @"Copyright (C) YYYY, The Duplicati Team
+https://duplicati.com, hello@duplicati.com
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the ""Software""),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.".Replace("YYYY", DateTime.UtcNow.Year.ToString());
+
+ public static RegexOptions RE_OPTS = RegexOptions.Singleline | RegexOptions.IgnoreCase;
+
+ public static Regex CS_REGION_MATCH = new Regex(@"(?#region Disclaimer / License(?.+(?:Free Software Foundation)[^#]+)#endregion)", RE_OPTS);
+
+ public static Regex CS_LGPL_MATCH = new Regex(@"(?//\s+Copyright \(C\) \d{4}, The Duplicati Team.+(?:Free Software Foundation).+Boston,\s+MA\s+0211\d-130\d\s+USA)", RE_OPTS);
+
+ public static Regex CS_MIT_MATCH = new Regex(@"(?//\s+Copyright \(C\) \d{4}, The Duplicati Team.+(?:copyright notice and this permission notice).+IN THE SOFTWARE\.)", RE_OPTS);
+
+ public static string GetLicenseTextWithPrefixedLines(string linePrefix = "// ")
+ => string.Join("\n", Fragments.NEW_LICENSE.Split("\n").Select(x => linePrefix + x).Append(string.Empty));
+
+ public static bool MatchAndReplace(ref string data)
+ {
+ foreach (var m in new[] { CS_REGION_MATCH, CS_MIT_MATCH })
+ {
+ var match = m.Match(data);
+ if (match.Success && match.Groups["license"].Success)
+ {
+ var g = match.Groups["license"];
+ data = string.Join(
+ string.Empty,
+
+ data.Substring(0, g.Index),
+ GetLicenseTextWithPrefixedLines(),
+ data.Substring(g.Index + g.Length)
+ );
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
+
+
diff --git a/BuildTools/LicenseUpdater/LicenseUpdater.csproj b/BuildTools/LicenseUpdater/LicenseUpdater.csproj
new file mode 100755
index 000000000..cc6ad9d95
--- /dev/null
+++ b/BuildTools/LicenseUpdater/LicenseUpdater.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ net8.0
+ license_upgrader
+ enable
+ enable
+ Copyright © 2024 Team Duplicati, MIT license
+
+
+
+
diff --git a/BuildTools/LicenseUpdater/LicenseUpdater.sln b/BuildTools/LicenseUpdater/LicenseUpdater.sln
new file mode 100755
index 000000000..35f42918d
--- /dev/null
+++ b/BuildTools/LicenseUpdater/LicenseUpdater.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.002.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LicenseUpdater", "LicenseUpdater.csproj", "{37205C47-20D1-4E7B-9AFB-B833C7FC949F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {37205C47-20D1-4E7B-9AFB-B833C7FC949F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {37205C47-20D1-4E7B-9AFB-B833C7FC949F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {37205C47-20D1-4E7B-9AFB-B833C7FC949F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {37205C47-20D1-4E7B-9AFB-B833C7FC949F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F9144130-7643-4C0E-9545-ECEF7EEF0423}
+ EndGlobalSection
+EndGlobal
diff --git a/BuildTools/LicenseUpdater/Program.cs b/BuildTools/LicenseUpdater/Program.cs
new file mode 100755
index 000000000..97603905a
--- /dev/null
+++ b/BuildTools/LicenseUpdater/Program.cs
@@ -0,0 +1,79 @@
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using LicenseUpdater;
+
+var cmdargs = Environment.GetCommandLineArgs();
+if (cmdargs.Length != 2)
+ throw new Exception($"Usage: dotnet run ");
+
+var startpath = Path.GetFullPath(Environment.GetCommandLineArgs().Skip(1).First());
+if (!Directory.Exists(startpath))
+ throw new Exception($"Start path not found: {startpath}");
+
+var target_extensions = new[] {
+ ".cs"
+ // ".html",
+ // ".js",
+ // ".css"
+}.ToHashSet(StringComparer.OrdinalIgnoreCase);
+
+File.WriteAllText(Path.Combine(startpath, "LICENSE.txt"), Fragments.GetLicenseTextWithPrefixedLines(string.Empty));
+
+var candidates = Directory.EnumerateFiles(startpath, "*", SearchOption.AllDirectories)
+ .Where(x => target_extensions.Contains(Path.GetExtension(x) ?? string.Empty));
+
+var blacklistSubFolders = new[] { "bin", "obj", "packages" };
+
+foreach (var file in candidates)
+{
+ if (string.Equals("AssemblyInfo.cs", Path.GetFileName(file), StringComparison.OrdinalIgnoreCase))
+ continue;
+ if (blacklistSubFolders.Any(x => file.Contains($"{Path.DirectorySeparatorChar}{x}{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase)))
+ continue;
+
+ if (new FileInfo(file).Length > 10 * 1024 * 1024)
+ {
+ Console.WriteLine($"{file} skipped due to size");
+ continue;
+ }
+
+ var data = File.ReadAllText(file);
+ if (Fragments.MatchAndReplace(ref data))
+ {
+ File.WriteAllText(file, data);
+ // Console.WriteLine($"{file} updated!");
+ continue;
+ }
+
+ if (string.Equals(Path.GetExtension(file), ".cs", StringComparison.OrdinalIgnoreCase) && !data.StartsWith("// Copyright", StringComparison.OrdinalIgnoreCase))
+ {
+ data = Fragments.GetLicenseTextWithPrefixedLines() + data;
+ File.WriteAllText(file, data);
+ // Console.WriteLine($"{file} updated!");
+ continue;
+ }
+
+ Console.WriteLine($"{file} skipped, no match");
+}
+
+
+
diff --git a/BuildTools/LicenseUpdater/README.md b/BuildTools/LicenseUpdater/README.md
new file mode 100644
index 000000000..0fa38f9bc
--- /dev/null
+++ b/BuildTools/LicenseUpdater/README.md
@@ -0,0 +1,9 @@
+# License Updater
+
+This folder contains a small tool that can update the license in all files, using the current year as the copyright year. It is intended to be run once a year, using the root repository folder as input:
+
+```
+dotnet run ../../
+```
+
+The resulting change can then be reviewed and committed.
\ No newline at end of file
diff --git a/BuildTools/UpdateVersionStamp/Program.cs b/BuildTools/UpdateVersionStamp/Program.cs
deleted file mode 100644
index 79c03cb58..000000000
--- a/BuildTools/UpdateVersionStamp/Program.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.IO;
-using System.Text.RegularExpressions;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-
-namespace UpdateVersionStamp
-{
- public static class Program
- {
- private static readonly string DIR_SEP = Path.DirectorySeparatorChar.ToString();
- private static readonly Dictionary FILEMAP;
-
- static Program()
- {
- var versionre = @"(?\d+\.\d+\.(\*|(\d+(\.(\*|\d+)))?))";
- FILEMAP = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
- FILEMAP.Add("AssemblyInfo.cs", new Regex(@"(\[assembly\: AssemblyVersion\(\""" + versionre + @"""\)\])|(\[assembly\: AssemblyFileVersion\(\""" + versionre + @"\""\)\])|(\[assembly\: AssemblyFileVersionAttribute\(\""" + versionre + @"\""\)\])"));
- FILEMAP.Add("UpgradeData.wxi", new Regex(@"\<\?define ProductVersion\=\""" + versionre + @"\"" \?\>"));
- FILEMAP.Add("AssemblyRedirects.xml", new Regex(@"newVersion\=\""" + versionre + @"\"""));
- FILEMAP.Add("index.html", new Regex(@"\?v\=" + versionre));
- FILEMAP.Add("login.html", new Regex(@"\?v\=" + versionre));
- FILEMAP.Add("app.js", new Regex(@"\?v\=" + versionre));
- }
-
- private class Options
- {
- public string sourcefolder = Path.GetFullPath(Environment.CurrentDirectory);
- public string ignorefilter = null;
- public string version = null;
- public string versiontag = null;
-
- public void Fixup()
- {
- sourcefolder = Duplicati.Library.Common.IO.Util.AppendDirSeparator(System.IO.Path.GetFullPath(sourcefolder.Replace("/", DIR_SEP)));
- if (ignorefilter != null)
- ignorefilter =ignorefilter.Replace("/", DIR_SEP);
- }
- }
-
- public static void Main(string[] _args)
- {
- List args = new List(_args);
- Dictionary options = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args);
- Options opt = new Options();
-
- foreach (FieldInfo fi in opt.GetType().GetFields())
- if (options.ContainsKey(fi.Name))
- fi.SetValue(opt, options[fi.Name]);
-
- opt.Fixup();
-
- Duplicati.Library.Utility.IFilter filter = null;
- if (!string.IsNullOrEmpty(opt.ignorefilter))
- filter = new Duplicati.Library.Utility.FilterExpression(opt.ignorefilter, false);
-
- Func isFile = (string x) => !x.EndsWith(DIR_SEP);
-
- var paths = Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(opt.sourcefolder)
- .Where(x => Duplicati.Library.Utility.FilterExpression.Matches(filter, x))
- .Where(x => isFile(x) && FILEMAP.ContainsKey(Path.GetFileName(x)))
- .Select(x =>
- {
- var m = FILEMAP[Path.GetFileName(x)].Match(File.ReadAllText(x));
- return m.Success ?
- new { File = x, Version = new Version(m.Groups["version"].Value.Replace("*", "0")), Display = m.Groups["version"].Value }
- : null;
- })
- .Where(x => x != null)
- .ToArray(); //No need to re-eval
-
- if (paths.Count() == 0)
- {
- Console.WriteLine("No files found to update...");
- return;
- }
-
- foreach (var p in paths)
- Console.WriteLine("{0}\t:{1}", p.Display, p.File);
-
- if (string.IsNullOrWhiteSpace(opt.version))
- {
- var maxv = paths.Select(x => x.Version).Max();
- opt.version = new Version(
- maxv.Major,
- maxv.Minor,
- maxv.Build,
- maxv.Revision).ToString();
- }
-
- //Sanity check
- var nv = new Version(opt.version).ToString(4);
-
- foreach (var p in paths)
- {
- var re = FILEMAP[Path.GetFileName(p.File)];
- var txt = File.ReadAllText(p.File);
- //var m = re.Match(txt).Groups["version"];
- txt = re.Replace(txt, (m) => {
- var t = m.Groups["version"];
- return m.Value.Replace(t.Value, nv);
- });
- File.WriteAllText(p.File, txt);
- }
-
- Console.WriteLine("Updated {0} files to version {1}", paths.Count(), opt.version);
- }
- }
-}
diff --git a/BuildTools/UpdateVersionStamp/Properties/AssemblyInfo.cs b/BuildTools/UpdateVersionStamp/Properties/AssemblyInfo.cs
deleted file mode 100644
index 8d0207127..000000000
--- a/BuildTools/UpdateVersionStamp/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-[assembly: AssemblyTitle("UpdateVersionStamp")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("2.0.0.7")]
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
-
diff --git a/BuildTools/UpdateVersionStamp/UpdateVersionStamp.csproj b/BuildTools/UpdateVersionStamp/UpdateVersionStamp.csproj
deleted file mode 100644
index 0a9333d13..000000000
--- a/BuildTools/UpdateVersionStamp/UpdateVersionStamp.csproj
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
- Debug
- AnyCPU
- 10.0.0
- 2.0
- {1920C3B0-98A0-410F-8972-9C19FA616FE5}
- Exe
- False
- UpdateVersionStamp
- UpdateVersionStamp
- v4.7.1
-
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- false
- --sourcefolder=../.. --version=2.0.0.0 --version-tag=20140405
- false
-
-
- full
- true
- bin\Release
- prompt
- 4
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}
- Duplicati.Library.Utility
-
-
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}
- Duplicati.Library.Common
-
-
-
-
-
-
diff --git a/BuildTools/UpdateVersionStamp/UpdateVersionStamp.sln b/BuildTools/UpdateVersionStamp/UpdateVersionStamp.sln
deleted file mode 100644
index 9bae29432..000000000
--- a/BuildTools/UpdateVersionStamp/UpdateVersionStamp.sln
+++ /dev/null
@@ -1,35 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateVersionStamp", "UpdateVersionStamp.csproj", "{1920C3B0-98A0-410F-8972-9C19FA616FE5}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "..\..\Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Common", "..\..\Duplicati\Library\Common\Duplicati.Library.Common.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACF}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {1920C3B0-98A0-410F-8972-9C19FA616FE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1920C3B0-98A0-410F-8972-9C19FA616FE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1920C3B0-98A0-410F-8972-9C19FA616FE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1920C3B0-98A0-410F-8972-9C19FA616FE5}.Release|Any CPU.Build.0 = Release|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.Build.0 = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = UpdateVersionStamp.csproj
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/BuildTools/UpdateVersionStamp/app.config b/BuildTools/UpdateVersionStamp/app.config
deleted file mode 100644
index d7c60fd53..000000000
--- a/BuildTools/UpdateVersionStamp/app.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/Duplicati CommandLine Only.sln b/Duplicati CommandLine Only.sln
deleted file mode 100644
index 60f91ed27..000000000
--- a/Duplicati CommandLine Only.sln
+++ /dev/null
@@ -1,324 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26730.12
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Main", "Duplicati\Library\Main\Duplicati.Library.Main.csproj", "{10D2D1B7-C664-41D8-9B3A-00040C3D421B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.File", "Duplicati\Library\Backend\File\Duplicati.Library.Backend.File.csproj", "{FC9B7611-836F-4127-8B44-A7C31F506807}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Compression", "Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{94484FDB-2EFA-4CF0-9BE6-A561157B4F87}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Logging", "Duplicati\Library\Logging\Duplicati.Library.Logging.csproj", "{D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine", "Duplicati\CommandLine\Duplicati.CommandLine.csproj", "{81765A64-3661-4E3E-B850-2F6F87A51F74}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Interface", "Duplicati\Library\Interface\Duplicati.Library.Interface.csproj", "{C5899F45-B0FF-483C-9D38-24A9FCAAB237}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.DynamicLoader", "Duplicati\Library\DynamicLoader\Duplicati.Library.DynamicLoader.csproj", "{0CA86ECF-5BEC-4909-B4F6-110A03B30B92}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Snapshots", "Duplicati\Library\Snapshots\Duplicati.Library.Snapshots.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACE}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Modules.Builtin", "Duplicati\Library\Modules\Builtin\Duplicati.Library.Modules.Builtin.csproj", "{52826615-7964-47FE-B4B3-1B2DBDF605B9}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.License", "Duplicati\License\Duplicati.License.csproj", "{4D012CB1-4B92-47F4-89B7-BF80A73A2E99}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.SSHv2", "Duplicati\Library\Backend\SSHv2\Duplicati.Library.Backend.SSHv2.csproj", "{FF2BF37C-E502-4C98-BEA0-701671DDFA08}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.WEBDAV", "Duplicati\Library\Backend\WEBDAV\Duplicati.Library.Backend.WEBDAV.csproj", "{BAE27510-8B5D-44B2-B33E-372A98908041}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.OneDrive", "Duplicati\Library\Backend\OneDrive\Duplicati.Library.Backend.OneDrive.csproj", "{CCD76347-7DC7-4B42-B7E1-E500E624CAC3}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.TahoeLAFS", "Duplicati\Library\Backend\TahoeLAFS\Duplicati.Library.Backend.TahoeLAFS.csproj", "{C0270709-2A40-43B5-8CF1-69581B9FA2A1}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.CloudFiles", "Duplicati\Library\Backend\CloudFiles\Duplicati.Library.Backend.CloudFiles.csproj", "{1BFAE226-8364-4086-825C-BB83F6F3EE4C}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.FTP", "Duplicati\Library\Backend\FTP\Duplicati.Library.Backend.FTP.csproj", "{F61679A9-E5DE-468A-B5A4-05F92D0143D2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.S3", "Duplicati\Library\Backend\S3\Duplicati.Library.Backend.S3.csproj", "{C03F6DFD-805A-4BE0-9338-64870ADDB4A2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.AzureBlob", "Duplicati\Library\Backend\AzureBlob\Duplicati.Library.Backend.AzureBlob.csproj", "{8E4CECFB-0413-4B00-AB93-78D1C3902BD5}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Localization", "Duplicati\Library\Localization\Duplicati.Library.Localization.csproj", "{B68F2214-951F-4F78-8488-66E1ED3F50BF}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.AutoUpdater", "Duplicati\Library\AutoUpdater\Duplicati.Library.AutoUpdater.csproj", "{7E119745-1F62-43F0-936C-F312A1912C0B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.SQLiteHelper", "Duplicati\Library\SQLiteHelper\Duplicati.Library.SQLiteHelper.csproj", "{2C838169-B187-4B09-8768-1C24C2521C8D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.UsageReporter", "Duplicati\Library\UsageReporter\Duplicati.Library.UsageReporter.csproj", "{BB014EA5-CE2C-4444-8D30-38983A0E8553}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.OAuthHelper", "Duplicati\Library\Backend\OAuthHelper\Duplicati.Library.OAuthHelper.csproj", "{D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.BackendTool", "Duplicati\CommandLine\BackendTool\Duplicati.CommandLine.BackendTool.csproj", "{2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.BackendTester", "Duplicati\CommandLine\BackendTester\Duplicati.CommandLine.BackendTester.csproj", "{E7280DCA-7776-4A73-B9B5-41FD77FC8799}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.GoogleServices", "Duplicati\Library\Backend\GoogleServices\Duplicati.Library.Backend.GoogleServices.csproj", "{5489181D-950C-44AF-873C-45EB0A3B6BD2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.OpenStack", "Duplicati\Library\Backend\OpenStack\Duplicati.Library.Backend.OpenStack.csproj", "{D9E4E686-423C-48EC-A392-404E7C00860C}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.HubiC", "Duplicati\Library\Backend\HubiC\Duplicati.Library.Backend.HubiC.csproj", "{D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.RecoveryTool", "Duplicati\CommandLine\RecoveryTool\Duplicati.CommandLine.RecoveryTool.csproj", "{4A010589-76E6-4F05-A5C4-4598D5DF11F8}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Box", "Duplicati\Library\Backend\Box\Duplicati.Library.Backend.Box.csproj", "{3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.AlternativeFTP", "Duplicati\Library\Backend\AlternativeFTP\Duplicati.Library.Backend.AlternativeFTP.csproj", "{3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Backblaze", "Duplicati\Library\Backend\Backblaze\Duplicati.Library.Backend.Backblaze.csproj", "{61C43D61-4368-4942-84A3-1EB623F4EF2A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Dropbox", "Duplicati\Library\Backend\Dropbox\Duplicati.Library.Backend.Dropbox.csproj", "{B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Mega", "Duplicati\Library\Backend\Mega\Duplicati.Library.Backend.Mega.csproj", "{6643A5AE-AB38-453F-ADCE-408E35A81A83}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.SharePoint", "Duplicati\Library\Backend\SharePoint\Duplicati.Library.Backend.SharePoint.csproj", "{59C8BBC5-6E42-46FB-AB3E-6C183A82459A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Tools", "Duplicati\Tools\Duplicati.Tools.csproj", "{0797AA22-C5DD-4950-BB60-34765AB8C6DD}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Sia", "Duplicati\Library\Backend\Sia\Duplicati.Library.Backend.Sia.csproj", "{32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Common", "Duplicati\Library\Common\Duplicati.Library.Common.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACF}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Jottacloud", "Duplicati\Library\Backend\Jottacloud\Duplicati.Library.Backend.Jottacloud.csproj", "{2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Rclone", "Duplicati\Library\Backend\Rclone\Duplicati.Library.Backend.Rclone.csproj", "{851A1CB8-3CEB-41B4-956F-34D760D2A8E5}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.Build.0 = Release|Any CPU
- {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {10D2D1B7-C664-41D8-9B3A-00040C3D421B}.Release|Any CPU.Build.0 = Release|Any CPU
- {FC9B7611-836F-4127-8B44-A7C31F506807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FC9B7611-836F-4127-8B44-A7C31F506807}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FC9B7611-836F-4127-8B44-A7C31F506807}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FC9B7611-836F-4127-8B44-A7C31F506807}.Release|Any CPU.Build.0 = Release|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.Build.0 = Release|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.Build.0 = Release|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.Build.0 = Release|Any CPU
- {81765A64-3661-4E3E-B850-2F6F87A51F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {81765A64-3661-4E3E-B850-2F6F87A51F74}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {81765A64-3661-4E3E-B850-2F6F87A51F74}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {81765A64-3661-4E3E-B850-2F6F87A51F74}.Release|Any CPU.Build.0 = Release|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|Any CPU.Build.0 = Release|Any CPU
- {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}.Release|Any CPU.Build.0 = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACE}.Release|Any CPU.Build.0 = Release|Any CPU
- {52826615-7964-47FE-B4B3-1B2DBDF605B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {52826615-7964-47FE-B4B3-1B2DBDF605B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {52826615-7964-47FE-B4B3-1B2DBDF605B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {52826615-7964-47FE-B4B3-1B2DBDF605B9}.Release|Any CPU.Build.0 = Release|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Release|Any CPU.Build.0 = Release|Any CPU
- {FF2BF37C-E502-4C98-BEA0-701671DDFA08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF2BF37C-E502-4C98-BEA0-701671DDFA08}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF2BF37C-E502-4C98-BEA0-701671DDFA08}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF2BF37C-E502-4C98-BEA0-701671DDFA08}.Release|Any CPU.Build.0 = Release|Any CPU
- {BAE27510-8B5D-44B2-B33E-372A98908041}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BAE27510-8B5D-44B2-B33E-372A98908041}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BAE27510-8B5D-44B2-B33E-372A98908041}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BAE27510-8B5D-44B2-B33E-372A98908041}.Release|Any CPU.Build.0 = Release|Any CPU
- {CCD76347-7DC7-4B42-B7E1-E500E624CAC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CCD76347-7DC7-4B42-B7E1-E500E624CAC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CCD76347-7DC7-4B42-B7E1-E500E624CAC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CCD76347-7DC7-4B42-B7E1-E500E624CAC3}.Release|Any CPU.Build.0 = Release|Any CPU
- {C0270709-2A40-43B5-8CF1-69581B9FA2A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C0270709-2A40-43B5-8CF1-69581B9FA2A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C0270709-2A40-43B5-8CF1-69581B9FA2A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C0270709-2A40-43B5-8CF1-69581B9FA2A1}.Release|Any CPU.Build.0 = Release|Any CPU
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}.Release|Any CPU.Build.0 = Release|Any CPU
- {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F61679A9-E5DE-468A-B5A4-05F92D0143D2}.Release|Any CPU.Build.0 = Release|Any CPU
- {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}.Release|Any CPU.Build.0 = Release|Any CPU
- {8E4CECFB-0413-4B00-AB93-78D1C3902BD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8E4CECFB-0413-4B00-AB93-78D1C3902BD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8E4CECFB-0413-4B00-AB93-78D1C3902BD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8E4CECFB-0413-4B00-AB93-78D1C3902BD5}.Release|Any CPU.Build.0 = Release|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.Build.0 = Release|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.Build.0 = Release|Any CPU
- {2C838169-B187-4B09-8768-1C24C2521C8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2C838169-B187-4B09-8768-1C24C2521C8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2C838169-B187-4B09-8768-1C24C2521C8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2C838169-B187-4B09-8768-1C24C2521C8D}.Release|Any CPU.Build.0 = Release|Any CPU
- {BB014EA5-CE2C-4444-8D30-38983A0E8553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BB014EA5-CE2C-4444-8D30-38983A0E8553}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BB014EA5-CE2C-4444-8D30-38983A0E8553}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BB014EA5-CE2C-4444-8D30-38983A0E8553}.Release|Any CPU.Build.0 = Release|Any CPU
- {D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}.Release|Any CPU.Build.0 = Release|Any CPU
- {2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}.Release|Any CPU.Build.0 = Release|Any CPU
- {E7280DCA-7776-4A73-B9B5-41FD77FC8799}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E7280DCA-7776-4A73-B9B5-41FD77FC8799}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E7280DCA-7776-4A73-B9B5-41FD77FC8799}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E7280DCA-7776-4A73-B9B5-41FD77FC8799}.Release|Any CPU.Build.0 = Release|Any CPU
- {5489181D-950C-44AF-873C-45EB0A3B6BD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5489181D-950C-44AF-873C-45EB0A3B6BD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5489181D-950C-44AF-873C-45EB0A3B6BD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5489181D-950C-44AF-873C-45EB0A3B6BD2}.Release|Any CPU.Build.0 = Release|Any CPU
- {D9E4E686-423C-48EC-A392-404E7C00860C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D9E4E686-423C-48EC-A392-404E7C00860C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D9E4E686-423C-48EC-A392-404E7C00860C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D9E4E686-423C-48EC-A392-404E7C00860C}.Release|Any CPU.Build.0 = Release|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Release|Any CPU.Build.0 = Release|Any CPU
- {4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Release|Any CPU.Build.0 = Release|Any CPU
- {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}.Release|Any CPU.Build.0 = Release|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Release|Any CPU.Build.0 = Release|Any CPU
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}.Release|Any CPU.Build.0 = Release|Any CPU
- {B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Release|Any CPU.Build.0 = Release|Any CPU
- {6643A5AE-AB38-453F-ADCE-408E35A81A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6643A5AE-AB38-453F-ADCE-408E35A81A83}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6643A5AE-AB38-453F-ADCE-408E35A81A83}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6643A5AE-AB38-453F-ADCE-408E35A81A83}.Release|Any CPU.Build.0 = Release|Any CPU
- {59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Release|Any CPU.Build.0 = Release|Any CPU
- {0797AA22-C5DD-4950-BB60-34765AB8C6DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0797AA22-C5DD-4950-BB60-34765AB8C6DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0797AA22-C5DD-4950-BB60-34765AB8C6DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0797AA22-C5DD-4950-BB60-34765AB8C6DD}.Release|Any CPU.Build.0 = Release|Any CPU
- {32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}.Release|Any CPU.Build.0 = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.Build.0 = Release|Any CPU
- {2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}.Release|Any CPU.Build.0 = Release|Any CPU
- {851A1CB8-3CEB-41B4-956F-34D760D2A8E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {851A1CB8-3CEB-41B4-956F-34D760D2A8E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {851A1CB8-3CEB-41B4-956F-34D760D2A8E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {851A1CB8-3CEB-41B4-956F-34D760D2A8E5}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {3221EB9F-EE08-460E-B236-CD594688FC5F}
- EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = Duplicati\CommandLine\Duplicati.CommandLine.csproj
- Policies = $0
- $0.StandardHeader = $1
- $1.Text = @ Copyright (C) 2013, The Duplicati Team\r\n http://www.duplicati.com, info@duplicati.com\n \n This library is free software; you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as\n published by the Free Software Foundation; either version 2.1 of the\n License, or (at your option) any later version.\n\n This library is distributed in the hope that it will be useful, but\n WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- $1.IncludeInNewFiles = True
- $0.DotNetNamingPolicy = $2
- $2.DirectoryNamespaceAssociation = PrefixedHierarchical
- $2.ResourceNamePolicy = FileFormatDefault
- $0.TextStylePolicy = $5
- $3.inheritsSet = VisualStudio
- $3.scope = text/plain
- $3.FileWidth = 120
- $3.EolMarker = Windows
- $3.inheritsScope = text/plain
- $0.CSharpFormattingPolicy = $4
- $4.IndentSwitchBody = True
- $4.AnonymousMethodBraceStyle = NextLine
- $4.PropertyBraceStyle = NextLine
- $4.PropertyGetBraceStyle = NextLine
- $4.PropertySetBraceStyle = NextLine
- $4.EventBraceStyle = NextLine
- $4.EventAddBraceStyle = NextLine
- $4.EventRemoveBraceStyle = NextLine
- $4.StatementBraceStyle = NextLine
- $4.ElseNewLinePlacement = NewLine
- $4.ElseIfNewLinePlacement = SameLine
- $4.CatchNewLinePlacement = NewLine
- $4.FinallyNewLinePlacement = NewLine
- $4.BeforeMethodDeclarationParentheses = False
- $4.BeforeMethodCallParentheses = False
- $4.BeforeConstructorDeclarationParentheses = False
- $4.BeforeIndexerDeclarationBracket = False
- $4.BeforeDelegateDeclarationParentheses = False
- $4.NewParentheses = False
- $4.ForParentheses = False
- $4.ForeachParentheses = False
- $4.LockParentheses = False
- $4.UsingParentheses = False
- $4.SpacesBeforeBrackets = False
- $4.inheritsSet = Mono
- $4.inheritsScope = text/x-csharp
- $4.scope = text/x-csharp
- $5.FileWidth = 120
- $5.EolMarker = Windows
- $5.inheritsSet = VisualStudio
- $5.inheritsScope = text/plain
- $5.scope = text/plain
- EndGlobalSection
- GlobalSection(SubversionScc) = preSolution
- Svn-Managed = True
- Manager = AnkhSVN - Subversion Support for Visual Studio
- EndGlobalSection
-EndGlobal
diff --git a/Duplicati.sln b/Duplicati.sln
index 5f90df87f..1c8c8caf1 100644
--- a/Duplicati.sln
+++ b/Duplicati.sln
@@ -1,111 +1,168 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29102.190
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Utility", "Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Main", "Duplicati\Library\Main\Duplicati.Library.Main.csproj", "{10D2D1B7-C664-41D8-9B3A-00040C3D421B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Main", "Duplicati\Library\Main\Duplicati.Library.Main.csproj", "{10D2D1B7-C664-41D8-9B3A-00040C3D421B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.File", "Duplicati\Library\Backend\File\Duplicati.Library.Backend.File.csproj", "{FC9B7611-836F-4127-8B44-A7C31F506807}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.File", "Duplicati\Library\Backend\File\Duplicati.Library.Backend.File.csproj", "{FC9B7611-836F-4127-8B44-A7C31F506807}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.FTP", "Duplicati\Library\Backend\FTP\Duplicati.Library.Backend.FTP.csproj", "{F61679A9-E5DE-468A-B5A4-05F92D0143D2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.FTP", "Duplicati\Library\Backend\FTP\Duplicati.Library.Backend.FTP.csproj", "{F61679A9-E5DE-468A-B5A4-05F92D0143D2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Compression", "Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Compression", "Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Logging", "Duplicati\Library\Logging\Duplicati.Library.Logging.csproj", "{D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Logging", "Duplicati\Library\Logging\Duplicati.Library.Logging.csproj", "{D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.S3", "Duplicati\Library\Backend\S3\Duplicati.Library.Backend.S3.csproj", "{C03F6DFD-805A-4BE0-9338-64870ADDB4A2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.S3", "Duplicati\Library\Backend\S3\Duplicati.Library.Backend.S3.csproj", "{C03F6DFD-805A-4BE0-9338-64870ADDB4A2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine", "Duplicati\CommandLine\Duplicati.CommandLine.csproj", "{81765A64-3661-4E3E-B850-2F6F87A51F74}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine", "Duplicati\CommandLine\CLI\Duplicati.CommandLine.csproj", "{81765A64-3661-4E3E-B850-2F6F87A51F74}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.WEBDAV", "Duplicati\Library\Backend\WEBDAV\Duplicati.Library.Backend.WEBDAV.csproj", "{BAE27510-8B5D-44B2-B33E-372A98908041}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.WEBDAV", "Duplicati\Library\Backend\WEBDAV\Duplicati.Library.Backend.WEBDAV.csproj", "{BAE27510-8B5D-44B2-B33E-372A98908041}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.CloudFiles", "Duplicati\Library\Backend\CloudFiles\Duplicati.Library.Backend.CloudFiles.csproj", "{1BFAE226-8364-4086-825C-BB83F6F3EE4C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.CloudFiles", "Duplicati\Library\Backend\CloudFiles\Duplicati.Library.Backend.CloudFiles.csproj", "{1BFAE226-8364-4086-825C-BB83F6F3EE4C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.BackendTester", "Duplicati\CommandLine\BackendTester\Duplicati.CommandLine.BackendTester.csproj", "{E7280DCA-7776-4A73-B9B5-41FD77FC8799}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.BackendTester", "Duplicati\CommandLine\BackendTester\Duplicati.CommandLine.BackendTester.csproj", "{E7280DCA-7776-4A73-B9B5-41FD77FC8799}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Interface", "Duplicati\Library\Interface\Duplicati.Library.Interface.csproj", "{C5899F45-B0FF-483C-9D38-24A9FCAAB237}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Interface", "Duplicati\Library\Interface\Duplicati.Library.Interface.csproj", "{C5899F45-B0FF-483C-9D38-24A9FCAAB237}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.DynamicLoader", "Duplicati\Library\DynamicLoader\Duplicati.Library.DynamicLoader.csproj", "{0CA86ECF-5BEC-4909-B4F6-110A03B30B92}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.DynamicLoader", "Duplicati\Library\DynamicLoader\Duplicati.Library.DynamicLoader.csproj", "{0CA86ECF-5BEC-4909-B4F6-110A03B30B92}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Snapshots", "Duplicati\Library\Snapshots\Duplicati.Library.Snapshots.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Snapshots", "Duplicati\Library\Snapshots\Duplicati.Library.Snapshots.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.License", "Duplicati\License\Duplicati.License.csproj", "{4D012CB1-4B92-47F4-89B7-BF80A73A2E99}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.License", "Duplicati\License\Duplicati.License.csproj", "{4D012CB1-4B92-47F4-89B7-BF80A73A2E99}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Modules.Builtin", "Duplicati\Library\Modules\Builtin\Duplicati.Library.Modules.Builtin.csproj", "{52826615-7964-47FE-B4B3-1B2DBDF605B9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Modules.Builtin", "Duplicati\Library\Modules\Builtin\Duplicati.Library.Modules.Builtin.csproj", "{52826615-7964-47FE-B4B3-1B2DBDF605B9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.TahoeLAFS", "Duplicati\Library\Backend\TahoeLAFS\Duplicati.Library.Backend.TahoeLAFS.csproj", "{C0270709-2A40-43B5-8CF1-69581B9FA2A1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.TahoeLAFS", "Duplicati\Library\Backend\TahoeLAFS\Duplicati.Library.Backend.TahoeLAFS.csproj", "{C0270709-2A40-43B5-8CF1-69581B9FA2A1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.OneDrive", "Duplicati\Library\Backend\OneDrive\Duplicati.Library.Backend.OneDrive.csproj", "{CCD76347-7DC7-4B42-B7E1-E500E624CAC3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.OneDrive", "Duplicati\Library\Backend\OneDrive\Duplicati.Library.Backend.OneDrive.csproj", "{CCD76347-7DC7-4B42-B7E1-E500E624CAC3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.SSHv2", "Duplicati\Library\Backend\SSHv2\Duplicati.Library.Backend.SSHv2.csproj", "{FF2BF37C-E502-4C98-BEA0-701671DDFA08}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.SSHv2", "Duplicati\Library\Backend\SSHv2\Duplicati.Library.Backend.SSHv2.csproj", "{FF2BF37C-E502-4C98-BEA0-701671DDFA08}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Server", "Duplicati\Server\Duplicati.Server.csproj", "{19E661D2-C5DA-4F35-B3EE-7586E5734B5F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Server", "Duplicati\Server\Duplicati.Server.csproj", "{19E661D2-C5DA-4F35-B3EE-7586E5734B5F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.GUI.TrayIcon", "Duplicati\GUI\Duplicati.GUI.TrayIcon\Duplicati.GUI.TrayIcon.csproj", "{17566860-3D98-4604-AA5B-47661F75609F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.GUI.TrayIcon", "Duplicati\GUI\Duplicati.GUI.TrayIcon\Duplicati.GUI.TrayIcon.csproj", "{17566860-3D98-4604-AA5B-47661F75609F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Server.Serialization", "Duplicati\Server\Duplicati.Server.Serialization\Duplicati.Server.Serialization.csproj", "{33FD1D24-C28F-4C71-933F-98F1586EA76C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Server.Serialization", "Duplicati\Server\Duplicati.Server.Serialization\Duplicati.Server.Serialization.csproj", "{33FD1D24-C28F-4C71-933F-98F1586EA76C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.BackendTool", "Duplicati\CommandLine\BackendTool\Duplicati.CommandLine.BackendTool.csproj", "{2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.BackendTool", "Duplicati\CommandLine\BackendTool\Duplicati.CommandLine.BackendTool.csproj", "{2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.SQLiteHelper", "Duplicati\Library\SQLiteHelper\Duplicati.Library.SQLiteHelper.csproj", "{2C838169-B187-4B09-8768-1C24C2521C8D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.SQLiteHelper", "Duplicati\Library\SQLiteHelper\Duplicati.Library.SQLiteHelper.csproj", "{2C838169-B187-4B09-8768-1C24C2521C8D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Localization", "Duplicati\Library\Localization\Duplicati.Library.Localization.csproj", "{B68F2214-951F-4F78-8488-66E1ED3F50BF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Localization", "Duplicati\Library\Localization\Duplicati.Library.Localization.csproj", "{B68F2214-951F-4F78-8488-66E1ED3F50BF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.AutoUpdater", "Duplicati\Library\AutoUpdater\Duplicati.Library.AutoUpdater.csproj", "{7E119745-1F62-43F0-936C-F312A1912C0B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.AutoUpdater", "Duplicati\Library\AutoUpdater\Duplicati.Library.AutoUpdater.csproj", "{7E119745-1F62-43F0-936C-F312A1912C0B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Service", "Duplicati\Service\Duplicati.Service.csproj", "{E93F3DE2-FF3A-4709-96A3-8190AA14FA25}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Service", "Duplicati\Service\Duplicati.Service.csproj", "{E93F3DE2-FF3A-4709-96A3-8190AA14FA25}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.AzureBlob", "Duplicati\Library\Backend\AzureBlob\Duplicati.Library.Backend.AzureBlob.csproj", "{8E4CECFB-0413-4B00-AB93-78D1C3902BD5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.AzureBlob", "Duplicati\Library\Backend\AzureBlob\Duplicati.Library.Backend.AzureBlob.csproj", "{8E4CECFB-0413-4B00-AB93-78D1C3902BD5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.UnitTest", "Duplicati\UnitTest\Duplicati.UnitTest.csproj", "{ECB63D1C-1724-442D-9228-DEABF14F2EA3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.UnitTest", "Duplicati\UnitTest\Duplicati.UnitTest.csproj", "{ECB63D1C-1724-442D-9228-DEABF14F2EA3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.OAuthHelper", "Duplicati\Library\Backend\OAuthHelper\Duplicati.Library.OAuthHelper.csproj", "{D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.OAuthHelper", "Duplicati\Library\Backend\OAuthHelper\Duplicati.Library.OAuthHelper.csproj", "{D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.GoogleServices", "Duplicati\Library\Backend\GoogleServices\Duplicati.Library.Backend.GoogleServices.csproj", "{5489181D-950C-44AF-873C-45EB0A3B6BD2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.GoogleServices", "Duplicati\Library\Backend\GoogleServices\Duplicati.Library.Backend.GoogleServices.csproj", "{5489181D-950C-44AF-873C-45EB0A3B6BD2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.OpenStack", "Duplicati\Library\Backend\OpenStack\Duplicati.Library.Backend.OpenStack.csproj", "{D9E4E686-423C-48EC-A392-404E7C00860C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.OpenStack", "Duplicati\Library\Backend\OpenStack\Duplicati.Library.Backend.OpenStack.csproj", "{D9E4E686-423C-48EC-A392-404E7C00860C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.HubiC", "Duplicati\Library\Backend\HubiC\Duplicati.Library.Backend.HubiC.csproj", "{D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.RecoveryTool", "Duplicati\CommandLine\RecoveryTool\Duplicati.CommandLine.RecoveryTool.csproj", "{4A010589-76E6-4F05-A5C4-4598D5DF11F8}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.RecoveryTool", "Duplicati\CommandLine\RecoveryTool\Duplicati.CommandLine.RecoveryTool.csproj", "{4A010589-76E6-4F05-A5C4-4598D5DF11F8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Backblaze", "Duplicati\Library\Backend\Backblaze\Duplicati.Library.Backend.Backblaze.csproj", "{61C43D61-4368-4942-84A3-1EB623F4EF2A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Backblaze", "Duplicati\Library\Backend\Backblaze\Duplicati.Library.Backend.Backblaze.csproj", "{61C43D61-4368-4942-84A3-1EB623F4EF2A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Mega", "Duplicati\Library\Backend\Mega\Duplicati.Library.Backend.Mega.csproj", "{6643A5AE-AB38-453F-ADCE-408E35A81A83}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Mega", "Duplicati\Library\Backend\Mega\Duplicati.Library.Backend.Mega.csproj", "{6643A5AE-AB38-453F-ADCE-408E35A81A83}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Box", "Duplicati\Library\Backend\Box\Duplicati.Library.Backend.Box.csproj", "{3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Box", "Duplicati\Library\Backend\Box\Duplicati.Library.Backend.Box.csproj", "{3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.UsageReporter", "Duplicati\Library\UsageReporter\Duplicati.Library.UsageReporter.csproj", "{BB014EA5-CE2C-4444-8D30-38983A0E8553}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.UsageReporter", "Duplicati\Library\UsageReporter\Duplicati.Library.UsageReporter.csproj", "{BB014EA5-CE2C-4444-8D30-38983A0E8553}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.SharePoint", "Duplicati\Library\Backend\SharePoint\Duplicati.Library.Backend.SharePoint.csproj", "{59C8BBC5-6E42-46FB-AB3E-6C183A82459A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.SharePoint", "Duplicati\Library\Backend\SharePoint\Duplicati.Library.Backend.SharePoint.csproj", "{59C8BBC5-6E42-46FB-AB3E-6C183A82459A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Dropbox", "Duplicati\Library\Backend\Dropbox\Duplicati.Library.Backend.Dropbox.csproj", "{B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsService", "Duplicati\WindowsService\WindowsService.csproj", "{BEF7AF9A-3978-4F90-8592-198BF6EA6C6B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Tools", "Duplicati\Tools\Duplicati.Tools.csproj", "{0797AA22-C5DD-4950-BB60-34765AB8C6DD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.AlternativeFTP", "Duplicati\Library\Backend\AlternativeFTP\Duplicati.Library.Backend.AlternativeFTP.csproj", "{3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Jottacloud", "Duplicati\Library\Backend\Jottacloud\Duplicati.Library.Backend.Jottacloud.csproj", "{2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Dropbox", "Duplicati\Library\Backend\Dropbox\Duplicati.Library.Backend.Dropbox.csproj", "{B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Sia", "Duplicati\Library\Backend\Sia\Duplicati.Library.Backend.Sia.csproj", "{32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Tools", "Duplicati\Tools\Duplicati.Tools.csproj", "{0797AA22-C5DD-4950-BB60-34765AB8C6DD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Rclone", "Duplicati\Library\Backend\Rclone\Duplicati.Library.Backend.Rclone.csproj", "{851A1CB8-3CEB-41B4-956F-34D760D2A8E5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Jottacloud", "Duplicati\Library\Backend\Jottacloud\Duplicati.Library.Backend.Jottacloud.csproj", "{2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Common", "Duplicati\Library\Common\Duplicati.Library.Common.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Sia", "Duplicati\Library\Backend\Sia\Duplicati.Library.Backend.Sia.csproj", "{32A74526-3E5F-413A-8CB4-1EFDAD4C8B91}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Encryption", "Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{2CF2D90E-C25B-47AD-91E0-98451BAB8058}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Rclone", "Duplicati\Library\Backend\Rclone\Duplicati.Library.Backend.Rclone.csproj", "{851A1CB8-3CEB-41B4-956F-34D760D2A8E5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Storj", "Duplicati\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj", "{E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Common", "Duplicati\Library\Common\Duplicati.Library.Common.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.TencentCOS", "Duplicati\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj", "{545DD6D4-9476-42D6-B51C-A28E000C489E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.ConfigurationImporter", "Duplicati\CommandLine\ConfigurationImporter\Duplicati.CommandLine.ConfigurationImporter.csproj", "{B93E3BF0-DAA7-49B7-B07D-0559C5816735}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Executables", "Executables", "{FA88A246-EF8E-46E3-90AF-539B8C0A6ADE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{2CF2D90E-C25B-47AD-91E0-98451BAB8058}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net8", "net8", "{6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Storj", "Duplicati\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj", "{AE035E01-C917-4F13-A35E-78F21C1A2F17}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.AutoUpdater", "Executables\net8\Duplicati.CommandLine.AutoUpdater\Duplicati.CommandLine.AutoUpdater.csproj", "{95B7DD83-2C5A-4F1E-8EA7-39654B2B236A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.TencentCOS", "Duplicati\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj", "{545DD6D4-9476-42D6-B51C-A28E000C489E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Service", "Executables\net8\Duplicati.Service\Duplicati.Service.csproj", "{34149709-F3ED-4FB5-A087-43EB195C948B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Tardigrade", "Duplicati\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj", "{9A04CB37-DA72-4008-9703-3AC5191974E9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.BackendTester", "Executables\net8\Duplicati.CommandLine.BackendTester\Duplicati.CommandLine.BackendTester.csproj", "{2F1C0C8D-5C15-4BC0-811F-87F2C98D9790}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.BackendTool", "Executables\net8\Duplicati.CommandLine.BackendTool\Duplicati.CommandLine.BackendTool.csproj", "{31FA5B9B-4CD6-4BF3-B7A9-12C1E30DCAF6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.RecoveryTool", "Executables\net8\Duplicati.CommandLine.RecoveryTool\Duplicati.CommandLine.RecoveryTool.csproj", "{0FFC557E-1B84-46A2-B6E8-06064FA7EA58}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.GUI.TrayIcon", "Executables\net8\Duplicati.GUI.TrayIcon\Duplicati.GUI.TrayIcon.csproj", "{AF32C621-30DC-40F5-8CE6-DD69053068E9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Server", "Executables\net8\Duplicati.Server\Duplicati.Server.csproj", "{55EEEBD2-CE45-45D6-9838-958F1C7354E4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.WindowsService", "Executables\net8\Duplicati.WindowsService\Duplicati.WindowsService.csproj", "{5D20B150-C445-47BE-8CE8-C9F74F19A4F2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine", "Executables\net8\Duplicati.CommandLine\Duplicati.CommandLine.csproj", "{0F5A1F4E-25FA-4D02-920D-CA2138498081}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Idrivee2", "Duplicati\Library\Backend\Idrivee2\Duplicati.Library.Backend.Idrivee2.csproj", "{6B594D23-B629-465C-B799-70EE9E56C218}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C03F6DFD-805A-4BE0-9338-64870ADDB4A2} = {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Backends", "Backends", "{E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Helper Libraries", "Helper Libraries", "{566EBBDA-19A4-4056-A615-D901D57D2439}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Implementation", "Implementation", "{D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backends", "Duplicati\Library\Backends\Duplicati.Library.Backends.csproj", "{5290E237-C2CD-48F2-99D2-817F9C2163C8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.WebserverCore", "Duplicati\WebserverCore\Duplicati.WebserverCore.csproj", "{5A702CEE-DB36-4153-BD94-D8CF867E75A9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.RestAPI", "Duplicati\Library\RestAPI\Duplicati.Library.RestAPI.csproj", "{C1D4D665-23A3-4216-9CD1-D67AE9AAAA4C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.AliyunOSS", "Duplicati\Library\Backend\AliyunOSS\Duplicati.Library.Backend.AliyunOSS.csproj", "{4EB3DABC-D412-4C12-8876-41A1427A389E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.SharpAESCrypt", "Executables\net8\Duplicati.CommandLine.SharpAESCrypt\Duplicati.CommandLine.SharpAESCrypt.csproj", "{FE6FD36C-E171-4599-8D55-62DA579C0864}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.Snapshots", "Executables\net8\Duplicati.CommandLine.Snapshots\Duplicati.CommandLine.Snapshots.csproj", "{0364E724-1929-445E-9145-90A70B01DDC0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.ServerUtil", "Duplicati\CommandLine\ServerUtil\Duplicati.CommandLine.ServerUtil.csproj", "{5AF834B1-D227-4A98-9377-6BFA6BCF99A7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.ServerUtil", "Executables\net8\Duplicati.CommandLine.ServerUtil\Duplicati.CommandLine.ServerUtil.csproj", "{C09A7DE2-F0F3-4FA4-B36C-A0DE7844739B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.WindowsService", "Duplicati\WindowsService\Duplicati.WindowsService.csproj", "{3476A88B-4123-45F4-AC96-700B747367EB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Agent", "Duplicati\Agent\Duplicati.Agent.csproj", "{0C1AD03C-89D8-4C47-82FF-25D5470055B4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Agent", "Executables\net8\Duplicati.Agent\Duplicati.Agent.csproj", "{A2957269-C11F-44CE-B355-CC9BA342295D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.RemoteControl", "Duplicati\Library\RemoteControl\Duplicati.Library.RemoteControl.csproj", "{BB3A6E17-FC2E-42E3-B697-10D0972AFE81}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.SecretProvider", "Duplicati\Library\SecretProvider\Duplicati.Library.SecretProvider.csproj", "{37A0B1B9-32F3-47C8-86E9-C10507C4ED4C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.SecretTool", "Duplicati\CommandLine\SecretTool\Duplicati.CommandLine.SecretTool.csproj", "{D6417FD3-1645-4AB4-9F42-A1EC4A262B37}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.SecretTool", "Executables\net8\Duplicati.CommandLine.SecretTool\Duplicati.CommandLine.SecretTool.csproj", "{E1D32EF7-368E-4148-9367-B328E78C871B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.pCloud", "Duplicati\Library\Backend\pCloud\Duplicati.Library.Backend.pCloud.csproj", "{CA1A0EE8-DFFF-42AF-B15D-EF538357A2DE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Crashlog", "Duplicati\Library\Crashlog\Duplicati.Library.Crashlog.csproj", "{8ACA2736-3C69-4FA5-BCF9-5EDF50CAF332}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -241,10 +298,6 @@ Global
{D9E4E686-423C-48EC-A392-404E7C00860C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9E4E686-423C-48EC-A392-404E7C00860C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9E4E686-423C-48EC-A392-404E7C00860C}.Release|Any CPU.Build.0 = Release|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}.Release|Any CPU.Build.0 = Release|Any CPU
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -269,14 +322,6 @@ Global
{59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{59C8BBC5-6E42-46FB-AB3E-6C183A82459A}.Release|Any CPU.Build.0 = Release|Any CPU
- {BEF7AF9A-3978-4F90-8592-198BF6EA6C6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BEF7AF9A-3978-4F90-8592-198BF6EA6C6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BEF7AF9A-3978-4F90-8592-198BF6EA6C6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BEF7AF9A-3978-4F90-8592-198BF6EA6C6B}.Release|Any CPU.Build.0 = Release|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}.Release|Any CPU.Build.0 = Release|Any CPU
{B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -301,37 +346,193 @@ Global
{D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.Build.0 = Release|Any CPU
- {B93E3BF0-DAA7-49B7-B07D-0559C5816735}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B93E3BF0-DAA7-49B7-B07D-0559C5816735}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B93E3BF0-DAA7-49B7-B07D-0559C5816735}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B93E3BF0-DAA7-49B7-B07D-0559C5816735}.Release|Any CPU.Build.0 = Release|Any CPU
{2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CF2D90E-C25B-47AD-91E0-98451BAB8058}.Release|Any CPU.Build.0 = Release|Any CPU
- {AE035E01-C917-4F13-A35E-78F21C1A2F17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AE035E01-C917-4F13-A35E-78F21C1A2F17}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AE035E01-C917-4F13-A35E-78F21C1A2F17}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AE035E01-C917-4F13-A35E-78F21C1A2F17}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Release|Any CPU.Build.0 = Release|Any CPU
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Release|Any CPU.Build.0 = Release|Any CPU
- {9A04CB37-DA72-4008-9703-3AC5191974E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9A04CB37-DA72-4008-9703-3AC5191974E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9A04CB37-DA72-4008-9703-3AC5191974E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9A04CB37-DA72-4008-9703-3AC5191974E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {95B7DD83-2C5A-4F1E-8EA7-39654B2B236A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {95B7DD83-2C5A-4F1E-8EA7-39654B2B236A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {95B7DD83-2C5A-4F1E-8EA7-39654B2B236A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {95B7DD83-2C5A-4F1E-8EA7-39654B2B236A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34149709-F3ED-4FB5-A087-43EB195C948B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34149709-F3ED-4FB5-A087-43EB195C948B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34149709-F3ED-4FB5-A087-43EB195C948B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34149709-F3ED-4FB5-A087-43EB195C948B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F1C0C8D-5C15-4BC0-811F-87F2C98D9790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F1C0C8D-5C15-4BC0-811F-87F2C98D9790}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F1C0C8D-5C15-4BC0-811F-87F2C98D9790}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F1C0C8D-5C15-4BC0-811F-87F2C98D9790}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31FA5B9B-4CD6-4BF3-B7A9-12C1E30DCAF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31FA5B9B-4CD6-4BF3-B7A9-12C1E30DCAF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31FA5B9B-4CD6-4BF3-B7A9-12C1E30DCAF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31FA5B9B-4CD6-4BF3-B7A9-12C1E30DCAF6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0FFC557E-1B84-46A2-B6E8-06064FA7EA58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0FFC557E-1B84-46A2-B6E8-06064FA7EA58}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0FFC557E-1B84-46A2-B6E8-06064FA7EA58}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0FFC557E-1B84-46A2-B6E8-06064FA7EA58}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF32C621-30DC-40F5-8CE6-DD69053068E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF32C621-30DC-40F5-8CE6-DD69053068E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF32C621-30DC-40F5-8CE6-DD69053068E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF32C621-30DC-40F5-8CE6-DD69053068E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55EEEBD2-CE45-45D6-9838-958F1C7354E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55EEEBD2-CE45-45D6-9838-958F1C7354E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55EEEBD2-CE45-45D6-9838-958F1C7354E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55EEEBD2-CE45-45D6-9838-958F1C7354E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D20B150-C445-47BE-8CE8-C9F74F19A4F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D20B150-C445-47BE-8CE8-C9F74F19A4F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D20B150-C445-47BE-8CE8-C9F74F19A4F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D20B150-C445-47BE-8CE8-C9F74F19A4F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0F5A1F4E-25FA-4D02-920D-CA2138498081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0F5A1F4E-25FA-4D02-920D-CA2138498081}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0F5A1F4E-25FA-4D02-920D-CA2138498081}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0F5A1F4E-25FA-4D02-920D-CA2138498081}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B594D23-B629-465C-B799-70EE9E56C218}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B594D23-B629-465C-B799-70EE9E56C218}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B594D23-B629-465C-B799-70EE9E56C218}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B594D23-B629-465C-B799-70EE9E56C218}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5290E237-C2CD-48F2-99D2-817F9C2163C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5290E237-C2CD-48F2-99D2-817F9C2163C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5290E237-C2CD-48F2-99D2-817F9C2163C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5290E237-C2CD-48F2-99D2-817F9C2163C8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A702CEE-DB36-4153-BD94-D8CF867E75A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5A702CEE-DB36-4153-BD94-D8CF867E75A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A702CEE-DB36-4153-BD94-D8CF867E75A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5A702CEE-DB36-4153-BD94-D8CF867E75A9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C1D4D665-23A3-4216-9CD1-D67AE9AAAA4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1D4D665-23A3-4216-9CD1-D67AE9AAAA4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C1D4D665-23A3-4216-9CD1-D67AE9AAAA4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C1D4D665-23A3-4216-9CD1-D67AE9AAAA4C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4EB3DABC-D412-4C12-8876-41A1427A389E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4EB3DABC-D412-4C12-8876-41A1427A389E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4EB3DABC-D412-4C12-8876-41A1427A389E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4EB3DABC-D412-4C12-8876-41A1427A389E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FE6FD36C-E171-4599-8D55-62DA579C0864}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FE6FD36C-E171-4599-8D55-62DA579C0864}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FE6FD36C-E171-4599-8D55-62DA579C0864}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FE6FD36C-E171-4599-8D55-62DA579C0864}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0364E724-1929-445E-9145-90A70B01DDC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0364E724-1929-445E-9145-90A70B01DDC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0364E724-1929-445E-9145-90A70B01DDC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0364E724-1929-445E-9145-90A70B01DDC0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5AF834B1-D227-4A98-9377-6BFA6BCF99A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5AF834B1-D227-4A98-9377-6BFA6BCF99A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5AF834B1-D227-4A98-9377-6BFA6BCF99A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5AF834B1-D227-4A98-9377-6BFA6BCF99A7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C09A7DE2-F0F3-4FA4-B36C-A0DE7844739B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C09A7DE2-F0F3-4FA4-B36C-A0DE7844739B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C09A7DE2-F0F3-4FA4-B36C-A0DE7844739B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C09A7DE2-F0F3-4FA4-B36C-A0DE7844739B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3476A88B-4123-45F4-AC96-700B747367EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3476A88B-4123-45F4-AC96-700B747367EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3476A88B-4123-45F4-AC96-700B747367EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3476A88B-4123-45F4-AC96-700B747367EB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C1AD03C-89D8-4C47-82FF-25D5470055B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C1AD03C-89D8-4C47-82FF-25D5470055B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C1AD03C-89D8-4C47-82FF-25D5470055B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C1AD03C-89D8-4C47-82FF-25D5470055B4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A2957269-C11F-44CE-B355-CC9BA342295D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A2957269-C11F-44CE-B355-CC9BA342295D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A2957269-C11F-44CE-B355-CC9BA342295D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A2957269-C11F-44CE-B355-CC9BA342295D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BB3A6E17-FC2E-42E3-B697-10D0972AFE81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BB3A6E17-FC2E-42E3-B697-10D0972AFE81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BB3A6E17-FC2E-42E3-B697-10D0972AFE81}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BB3A6E17-FC2E-42E3-B697-10D0972AFE81}.Release|Any CPU.Build.0 = Release|Any CPU
+ {37A0B1B9-32F3-47C8-86E9-C10507C4ED4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {37A0B1B9-32F3-47C8-86E9-C10507C4ED4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {37A0B1B9-32F3-47C8-86E9-C10507C4ED4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {37A0B1B9-32F3-47C8-86E9-C10507C4ED4C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6417FD3-1645-4AB4-9F42-A1EC4A262B37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6417FD3-1645-4AB4-9F42-A1EC4A262B37}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6417FD3-1645-4AB4-9F42-A1EC4A262B37}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6417FD3-1645-4AB4-9F42-A1EC4A262B37}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E1D32EF7-368E-4148-9367-B328E78C871B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1D32EF7-368E-4148-9367-B328E78C871B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1D32EF7-368E-4148-9367-B328E78C871B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1D32EF7-368E-4148-9367-B328E78C871B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CA1A0EE8-DFFF-42AF-B15D-EF538357A2DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA1A0EE8-DFFF-42AF-B15D-EF538357A2DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CA1A0EE8-DFFF-42AF-B15D-EF538357A2DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CA1A0EE8-DFFF-42AF-B15D-EF538357A2DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8ACA2736-3C69-4FA5-BCF9-5EDF50CAF332}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8ACA2736-3C69-4FA5-BCF9-5EDF50CAF332}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8ACA2736-3C69-4FA5-BCF9-5EDF50CAF332}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8ACA2736-3C69-4FA5-BCF9-5EDF50CAF332}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {FC9B7611-836F-4127-8B44-A7C31F506807} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {F61679A9-E5DE-468A-B5A4-05F92D0143D2} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {19ECCE09-B5EB-406C-8C57-BAC66997D469} = {566EBBDA-19A4-4056-A615-D901D57D2439}
+ {C03F6DFD-805A-4BE0-9338-64870ADDB4A2} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {81765A64-3661-4E3E-B850-2F6F87A51F74} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {BAE27510-8B5D-44B2-B33E-372A98908041} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {1BFAE226-8364-4086-825C-BB83F6F3EE4C} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {E7280DCA-7776-4A73-B9B5-41FD77FC8799} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {0CA86ECF-5BEC-4909-B4F6-110A03B30B92} = {566EBBDA-19A4-4056-A615-D901D57D2439}
+ {C0270709-2A40-43B5-8CF1-69581B9FA2A1} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {CCD76347-7DC7-4B42-B7E1-E500E624CAC3} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {FF2BF37C-E502-4C98-BEA0-701671DDFA08} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {19E661D2-C5DA-4F35-B3EE-7586E5734B5F} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {17566860-3D98-4604-AA5B-47661F75609F} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {2AF960C0-357D-4D44-A3D5-8B6E89DB0F11} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {2C838169-B187-4B09-8768-1C24C2521C8D} = {566EBBDA-19A4-4056-A615-D901D57D2439}
+ {E93F3DE2-FF3A-4709-96A3-8190AA14FA25} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {8E4CECFB-0413-4B00-AB93-78D1C3902BD5} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {5489181D-950C-44AF-873C-45EB0A3B6BD2} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {D9E4E686-423C-48EC-A392-404E7C00860C} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {4A010589-76E6-4F05-A5C4-4598D5DF11F8} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {61C43D61-4368-4942-84A3-1EB623F4EF2A} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {6643A5AE-AB38-453F-ADCE-408E35A81A83} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {59C8BBC5-6E42-46FB-AB3E-6C183A82459A} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {2CD5DBC3-3DA6-432D-BA97-F0B8D24501C2} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {32A74526-3E5F-413A-8CB4-1EFDAD4C8B91} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {851A1CB8-3CEB-41B4-956F-34D760D2A8E5} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {545DD6D4-9476-42D6-B51C-A28E000C489E} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3} = {FA88A246-EF8E-46E3-90AF-539B8C0A6ADE}
+ {95B7DD83-2C5A-4F1E-8EA7-39654B2B236A} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {34149709-F3ED-4FB5-A087-43EB195C948B} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {2F1C0C8D-5C15-4BC0-811F-87F2C98D9790} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {31FA5B9B-4CD6-4BF3-B7A9-12C1E30DCAF6} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {0FFC557E-1B84-46A2-B6E8-06064FA7EA58} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {AF32C621-30DC-40F5-8CE6-DD69053068E9} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {55EEEBD2-CE45-45D6-9838-958F1C7354E4} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {5D20B150-C445-47BE-8CE8-C9F74F19A4F2} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {0F5A1F4E-25FA-4D02-920D-CA2138498081} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {6B594D23-B629-465C-B799-70EE9E56C218} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B} = {FA88A246-EF8E-46E3-90AF-539B8C0A6ADE}
+ {FE6FD36C-E171-4599-8D55-62DA579C0864} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {0364E724-1929-445E-9145-90A70B01DDC0} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {5AF834B1-D227-4A98-9377-6BFA6BCF99A7} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {C09A7DE2-F0F3-4FA4-B36C-A0DE7844739B} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {3476A88B-4123-45F4-AC96-700B747367EB} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {0C1AD03C-89D8-4C47-82FF-25D5470055B4} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {A2957269-C11F-44CE-B355-CC9BA342295D} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {D6417FD3-1645-4AB4-9F42-A1EC4A262B37} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B}
+ {E1D32EF7-368E-4148-9367-B328E78C871B} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3}
+ {CA1A0EE8-DFFF-42AF-B15D-EF538357A2DE} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4}
+ {8ACA2736-3C69-4FA5-BCF9-5EDF50CAF332} = {566EBBDA-19A4-4056-A615-D901D57D2439}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B40BAFE-D862-4397-9495-8F5EAF5CE80C}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
$0.StandardHeader = $1
- $1.Text = @ Copyright (C) ${Year}, The Duplicati Team\n http://www.duplicati.com, info@duplicati.com\n \n This library is free software; you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as\n published by the Free Software Foundation; either version 2.1 of the\n License, or (at your option) any later version.\n\n This library is distributed in the hope that it will be useful, but\n WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ $1.Text = @ Copyright (C) ${Year}, The Duplicati Team\nhttps://duplicati.com, hello@duplicati.com\n\nPermission is hereby granted, free of charge, to any person obtaining a \ncopy of this software and associated documentation files (the "Software"), \nto deal in the Software without restriction, including without limitation \nthe rights to use, copy, modify, merge, publish, distribute, sublicense, \nand/or sell copies of the Software, and to permit persons to whom the \nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in \nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS \nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING \nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \nDEALINGS IN THE SOFTWARE.\n
$0.DotNetNamingPolicy = $2
$2.DirectoryNamespaceAssociation = PrefixedHierarchical
$0.TextStylePolicy = $5
@@ -340,8 +541,4 @@ Global
$5.EolMarker = Windows
$5.scope = text/plain
EndGlobalSection
- GlobalSection(SubversionScc) = preSolution
- Svn-Managed = True
- Manager = AnkhSVN - Subversion Support for Visual Studio
- EndGlobalSection
EndGlobal
diff --git a/Duplicati/Agent/ConsoleLogDestination.cs b/Duplicati/Agent/ConsoleLogDestination.cs
new file mode 100644
index 000000000..57566d891
--- /dev/null
+++ b/Duplicati/Agent/ConsoleLogDestination.cs
@@ -0,0 +1,30 @@
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Duplicati.Library.Logging;
+
+namespace Duplicati.Agent;
+
+public class ConsoleLogDestination : ILogDestination
+{
+ public void WriteMessage(LogEntry entry)
+ => Console.WriteLine(entry.AsString(true));
+}
diff --git a/Duplicati/Agent/Duplicati.Agent.csproj b/Duplicati/Agent/Duplicati.Agent.csproj
new file mode 100644
index 000000000..a00d5580a
--- /dev/null
+++ b/Duplicati/Agent/Duplicati.Agent.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+ Duplicati.Agent.Implementation
+ Copyright © 2024 Team Duplicati, MIT license
+ Duplicati.Agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/Duplicati/Agent/Program.cs b/Duplicati/Agent/Program.cs
new file mode 100644
index 000000000..55feb9789
--- /dev/null
+++ b/Duplicati/Agent/Program.cs
@@ -0,0 +1,502 @@
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.CommandLine;
+using System.CommandLine.Builder;
+using System.CommandLine.NamingConventionBinder;
+using System.CommandLine.Parsing;
+using System.Reactive.Linq;
+using System.Security.Cryptography;
+using Duplicati.Library.Encryption;
+using Duplicati.Library.Interface;
+using Duplicati.Library.Logging;
+using Duplicati.Library.Main;
+using Duplicati.Library.RemoteControl;
+using Duplicati.Library.RestAPI;
+using Duplicati.Library.Utility;
+using Duplicati.Server;
+using Duplicati.WebserverCore.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Duplicati.Agent;
+
+public static class Program
+{
+ ///
+ /// The log tag for messages from this class
+ ///
+ private static readonly string LogTag = Log.LogTagFromType(typeof(Program));
+
+ ///
+ /// Create a random pre-shared key for the agent
+ ///
+ private static string? PreSharedKey = null;
+
+
+ ///
+ /// Commandline arguments for the agent
+ ///
+ /// The server URL to connect to
+ /// The file to use for the agent settings
+ /// The passphrase for the agent settings file
+ /// The interface to listen on for the webserver
+ /// The port to listen on for the webserver
+ /// The password for the webserver, random if none supplied
+ /// The encryption key for the database settings
+ /// The Windows event log to write to
+ /// Disable database encryption
+ /// Reset the JWT configuration
+ /// The allowed hostnames for the webserver
+ /// Only allow API access to the webserver
+ /// Disable signin tokens for the webserver
+ /// Enable ping-pong keepalive
+ /// Disable the pre-shared key
+ /// Keep the webserver password
+ /// The secret provider to use
+ /// The secret provider cache level
+ /// The secret provider pattern
+ private sealed record CommandLineArguments(
+ string AgentRegistrationUrl,
+ FileInfo AgentSettingsFile,
+ string? AgentSettingsFilePassphrase,
+ bool AgentRegisterOnly,
+ string WebserviceListenInterface,
+ string WebservicePort,
+ string WebservicePassword,
+ string? SettingsEncryptionKey,
+ string WindowsEventLog,
+ bool DisableDbEncryption,
+ bool WebserviceResetJwtConfig,
+ string WebserviceAllowedHostnames,
+ bool WebserviceApiOnly,
+ bool WebserviceDisableSigninTokens,
+ bool PingPongKeepalive,
+ bool DisablePreSharedKey,
+ bool KeepWebservicePassword,
+ string? SecretProvider,
+ SecretProviderHelper.CachingLevel SecretProviderCache,
+ string SecretProviderPattern
+ );
+
+ [STAThread]
+ public static Task Main(string[] args)
+ {
+ Library.AutoUpdater.PreloadSettingsLoader.ConfigurePreloadSettings(ref args, Library.AutoUpdater.PackageHelper.NamedExecutable.Agent);
+
+ var runcmd = new Command("run", "Runs the agent")
+ {
+ new Option("--agent-registration-url", description: "The server URL to connect to", getDefaultValue: () => RegisterForRemote.DefaultRegisterationUrl),
+ new Option("--agent-settings-file", description: "The file to use for the agent settings", getDefaultValue: () => new FileInfo(Settings.DefaultSettingsFile)),
+ new Option("--agent-settings-file-passphrase", description: "The passphrase for the agent settings file", getDefaultValue: () => null),
+ new Option("--agent-register-only", description: "Only register the agent, then exit", getDefaultValue: () => false),
+ new Option("--webservice-listen-interface", description: "The interface to listen on for the webserver", getDefaultValue: () => "loopback"),
+ new Option("--webservice-port", description: "The port to listen on for the webserver", getDefaultValue: () => "8210"),
+ new Option("--webservice-password", description: "The password for the webserver, not set, or set to \"random\", a random value is used.", getDefaultValue: () => null),
+ new Option("--settings-encryption-key", description: "The encryption key for the database settings", getDefaultValue: () => null),
+ new Option("--windows-eventlog", description: "The Windows event log to use as source.", getDefaultValue: () => ""),
+ new Option("--disable-db-encryption", description: "Disable database encryption", getDefaultValue: () => false),
+ new Option("--webservice-reset-jwt-config", description: "Reset the JWT configuration", getDefaultValue: () => true),
+ new Option("--webservice-allowed-hostnames", description: "The allowed hostnames for the webserver", getDefaultValue: () => "127.0.0.1"),
+ new Option("--webservice-api-only", description: "Only allow API access to the webserver", getDefaultValue: () => true),
+ new Option("--webservice-disable-signin-tokens", description: "Disable signin tokens for the webserver", getDefaultValue: () => true),
+ new Option("--ping-pong-keepalive", description: "Enable ping-pong keepalive", getDefaultValue: () => false),
+ new Option("--disable-pre-shared-key", description: "Disable the pre-shared key that prevents outside access to the webserver", getDefaultValue: () => false),
+ new Option("--keep-webservice-password", description: "Disables the random password assigned to the webserver on startup if no password is provided", getDefaultValue: () => false),
+ new Option("--secret-provider", description: "The secret provider to use", getDefaultValue: () => null),
+ new Option("--secret-provider-cache", description: "The secret provider cache level", getDefaultValue: () => SecretProviderHelper.CachingLevel.None),
+ new Option("--secret-provider-pattern", description: "The secret provider pattern", getDefaultValue: () => SecretProviderHelper.DEFAULT_PATTERN),
+ };
+ runcmd.Handler = CommandHandler.Create(RunAgent);
+
+ var clearCommand = new Command("clear", "Clears the agent settings")
+ {
+ new Option("--agent-settings-file", description: "The file to use for the agent settings", getDefaultValue: () => new FileInfo(Settings.DefaultSettingsFile)),
+ };
+ clearCommand.Handler = CommandHandler.Create(ClearAgentSettings);
+
+ var registerCommand = new Command("register", "Registers the agent and exits")
+ {
+ new Argument("agent-registration-url", description: "The server URL to connect to", getDefaultValue: () => RegisterForRemote.DefaultRegisterationUrl) { Arity = ArgumentArity.ZeroOrOne },
+ new Option("--agent-settings-file", description: "The file to use for the agent settings", getDefaultValue: () => new FileInfo(Settings.DefaultSettingsFile)),
+ new Option("--agent-settings-file-passphrase", description: "The passphrase for the agent settings file", getDefaultValue: () => null),
+ new Option("--secret-provider", description: "The secret provider to use", getDefaultValue: () => null),
+ new Option("--secret-provider-pattern", description: "The secret provider pattern", getDefaultValue: () => SecretProviderHelper.DEFAULT_PATTERN),
+ };
+ registerCommand.Handler = CommandHandler.Create(RegisterAgent);
+
+ var rootcmd = new RootCommand("Duplicati Agent")
+ {
+ runcmd,
+ clearCommand,
+ registerCommand
+ };
+ // rootcmd.Handler = CommandHandler.Create(RunAgent);
+
+ return new CommandLineBuilder(rootcmd)
+ .UseDefaults()
+ .UseExceptionHandler((ex, context) =>
+ {
+ if (ex is UserInformationException userInformationException)
+ {
+ Console.WriteLine("ErrorID: {0}", userInformationException.HelpID);
+ Console.WriteLine("Message: {0}", userInformationException.Message);
+ context.ExitCode = 2;
+ }
+ else
+ {
+ Console.WriteLine("Exception: " + ex);
+ context.ExitCode = 1;
+ }
+ })
+ .Build()
+ .InvokeAsync(args);
+ }
+
+ ///
+ /// Clears any local agent settings
+ ///
+ /// The file to clear
+ /// The exit code
+ private static Task ClearAgentSettings(FileInfo agentSettingsFile)
+ {
+ if (File.Exists(agentSettingsFile.FullName))
+ File.Delete(agentSettingsFile.FullName);
+ return Task.FromResult(0);
+ }
+
+ ///
+ /// Registers the agent with the remote server
+ ///
+ /// The URL to register the agent with
+ /// The file to save the settings to
+ /// The passphrase for the settings file
+ /// The secret provider to use
+ /// The secret provider pattern
+ ///
+ private static Task RegisterAgent(string agentRegistrationUrl, FileInfo agentSettingsFile, string? agentSettingsFilePassphrase, string? secretProvider, string secretProviderPattern)
+ => RunAgent(new CommandLineArguments(
+ AgentRegistrationUrl: string.IsNullOrWhiteSpace(agentRegistrationUrl)
+ ? RegisterForRemote.DefaultRegisterationUrl
+ : agentRegistrationUrl,
+ AgentSettingsFile: agentSettingsFile,
+ AgentSettingsFilePassphrase: agentSettingsFilePassphrase,
+ AgentRegisterOnly: true,
+ WebserviceListenInterface: "loopback",
+ WebservicePort: "8210",
+ WebservicePassword: null!,
+ SettingsEncryptionKey: null,
+ WindowsEventLog: "",
+ DisableDbEncryption: false,
+ WebserviceResetJwtConfig: false,
+ WebserviceAllowedHostnames: "",
+ WebserviceApiOnly: true,
+ WebserviceDisableSigninTokens: true,
+ PingPongKeepalive: false,
+ DisablePreSharedKey: false,
+ KeepWebservicePassword: false,
+ SecretProvider: secretProvider,
+ SecretProviderCache: SecretProviderHelper.CachingLevel.None,
+ SecretProviderPattern: secretProviderPattern
+ ));
+
+ ///
+ /// Runs the agent
+ ///
+ /// The configuration from the commandline
+ /// The exit code
+ private static async Task RunAgent(CommandLineArguments agentConfig)
+ {
+ // Read the environment variable for the encryption key
+ if (string.IsNullOrWhiteSpace(agentConfig.SettingsEncryptionKey))
+ agentConfig = agentConfig with { SettingsEncryptionKey = Environment.GetEnvironmentVariable(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME) };
+
+ // Apply the secret provider, if present
+ if (!string.IsNullOrWhiteSpace(agentConfig.SecretProvider))
+ {
+ // We can set any string properties on the agent configuration
+ var agentProps = agentConfig.GetType().GetProperties()
+ .Where(x => x.CanRead && x.CanWrite)
+ .Where(x => !x.Name.StartsWith(nameof(CommandLineArguments.SecretProvider)))
+ .Where(x => x.PropertyType == typeof(string) || Nullable.GetUnderlyingType(x.PropertyType) == typeof(string))
+ .ToDictionary(x => x.Name, x => x);
+
+ // Grab potential options from the agent configuration
+ var options = agentProps.Select(x => (x.Key, Value: x.Value.GetValue(agentConfig)?.ToString()))
+ .Where(x => x.Value != null)
+ .ToDictionary(x => x.Key, x => x.Value);
+
+ // Patch with the secret provider options
+ options["secret-provider"] = agentConfig.SecretProvider;
+ options["secret-provider-cache"] = agentConfig.SecretProviderCache.ToString();
+ options["secret-provider-pattern"] = agentConfig.SecretProviderPattern;
+
+ FIXMEGlobal.SecretProvider = await SecretProviderHelper.ApplySecretProviderAsync([], [], options, TempFolder.SystemTempPath, null, CancellationToken.None);
+
+ // Apply the secret provider to the agent configuration
+ foreach (var prop in agentProps)
+ if (options.TryGetValue(prop.Key, out string? value))
+ prop.Value.SetValue(agentConfig, value);
+ }
+
+ if (agentConfig.KeepWebservicePassword)
+ {
+ // Check for conflicting options, require no password
+ if (!string.IsNullOrWhiteSpace(agentConfig.WebservicePassword))
+ throw new UserInformationException("Cannot use --keep-webservice-password with a provided password", "KeepWebservicePasswordWithPassword");
+ }
+ else
+ {
+ // Prevent access to the webserver interface from anything but the agent
+ if (string.Equals("random", agentConfig.WebservicePassword, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(agentConfig.WebservicePassword))
+ agentConfig = agentConfig with { WebservicePassword = System.Security.Cryptography.RandomNumberGenerator.GetHexString(128) };
+ }
+
+ // Set the pre-shared key for the agent
+ if (!agentConfig.DisablePreSharedKey)
+ WebserverCore.Middlewares.PreSharedKeyFilter.PreSharedKey = PreSharedKey = RandomNumberGenerator.GetHexString(128);
+
+ using var cts = new CancellationTokenSource();
+
+ var target = new ControllerMultiLogTarget(new ConsoleLogDestination(), LogMessageType.Information, null);
+ using (Log.StartScope(target))
+ {
+ if (OperatingSystem.IsWindows() && !string.IsNullOrWhiteSpace(agentConfig.WindowsEventLog))
+ {
+ if (!WindowsEventLogSource.SourceExists(agentConfig.WindowsEventLog))
+ {
+ Console.WriteLine($"The Windows event log {agentConfig.WindowsEventLog} does not exist, creating it");
+ try { WindowsEventLogSource.CreateEventSource(agentConfig.WindowsEventLog); }
+ catch (Exception ex) { Console.WriteLine($"Failed to create the Windows event log {agentConfig.WindowsEventLog}: {ex.Message}"); }
+ }
+
+ if (WindowsEventLogSource.SourceExists(agentConfig.WindowsEventLog))
+ target.AddTarget(new WindowsEventLogSource(agentConfig.WindowsEventLog), LogMessageType.Information, null);
+ }
+
+ Log.WriteMessage(LogMessageType.Information, LogTag, "AgentStarting", "Starting agent");
+
+ var settings = await Register(agentConfig.AgentRegistrationUrl, agentConfig.AgentSettingsFile.FullName, agentConfig.AgentSettingsFilePassphrase, cts.Token);
+
+ if (agentConfig.AgentRegisterOnly)
+ {
+ Log.WriteMessage(LogMessageType.Information, LogTag, "ClientRegistered", "Client registered, exiting");
+ return 0;
+ }
+
+ var t = await Task.WhenAny(
+ Task.Run(() => StartLocalServer(agentConfig, settings, cts.Token)),
+ KeepRemoteConnection.Start(
+ settings.ServerUrl,
+ settings.JWT,
+ settings.CertificateUrl,
+ settings.ServerCertificates,
+ cts.Token,
+ c => ReKey(agentConfig, c),
+ (m) => OnMessage(m, agentConfig)
+ )
+ );
+
+ await t;
+
+ return 0;
+ }
+ }
+
+ private static async Task OnMessage(KeepRemoteConnection.CommandMessage message, CommandLineArguments agentConfig)
+ {
+ Log.WriteMessage(LogMessageType.Verbose, LogTag, "OnMessage", "Received message: {0}", message);
+
+ var provider = FIXMEGlobal.Provider.GetRequiredService();
+ var token = provider.CreateAccessToken("agent", provider.TemporaryFamilyId, TimeSpan.FromMinutes(2));
+ using var httpClient = FIXMEGlobal.Provider.GetRequiredService().CreateClient();
+ httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
+ httpClient.BaseAddress = new System.Uri($"http://127.0.0.1:{agentConfig.WebservicePort}");
+
+ if (!string.IsNullOrWhiteSpace(WebserverCore.Middlewares.PreSharedKeyFilter.PreSharedKey))
+ httpClient.DefaultRequestHeaders.Add(WebserverCore.Middlewares.PreSharedKeyFilter.HeaderName, PreSharedKey);
+
+ await message.Handle(httpClient);
+ }
+
+ private static Task ReKey(CommandLineArguments agentConfig, ClaimedClientData keydata)
+ {
+ Log.WriteMessage(LogMessageType.Verbose, LogTag, "ReKey", "Rekeying the settings");
+ var settings = Settings.Load(agentConfig.AgentSettingsFile.FullName, agentConfig.AgentSettingsFilePassphrase);
+ if (!string.IsNullOrWhiteSpace(keydata.JWT) && settings.JWT != keydata.JWT)
+ settings = settings with { JWT = keydata.JWT };
+ if (keydata.ServerCertificates != null && keydata.ServerCertificates.Any())
+ settings = settings with { ServerCertificates = keydata.ServerCertificates };
+
+ // Only allow re-keying if the settings encryption key is not set
+ if (!FIXMEGlobal.SettingsEncryptionKeyProvidedExternally)
+ {
+ if (!string.IsNullOrWhiteSpace(keydata.LocalEncryptionKey) && settings.SettingsEncryptionKey != keydata.LocalEncryptionKey)
+ {
+ // Log.WriteMessage(LogMessageType.Verbose, LogTag, "ReKey", "Changing the local settings encryption key");
+ // TODO: Implement changing the database encryption key
+ // FIXMEGlobal.Provider.GetRequiredService().ChangeDbKey(keydata.LocalEncryptionKey);
+ // settings = settings with { SettingsEncryptionKey = keydata.LocalEncryptionKey };
+ }
+ }
+ settings.Save();
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Runs the server, restarting on crashes
+ ///
+ /// The commandline arguments passed to the server
+ /// The cancellation token to use for the process
+ /// An awaitable task
+ private static async Task RunServer(string[] args, CancellationToken cancellationToken)
+ {
+ cancellationToken.Register(() =>
+ {
+ Server.Program.ApplicationExitEvent.Set();
+ });
+
+ var lastRestart = DateTime.Now;
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ try
+ {
+ lastRestart = DateTime.Now;
+ Server.Program.Main(args);
+ }
+ catch (Exception ex)
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ {
+ var nextRestart = lastRestart.AddSeconds(30);
+ var waitTime = nextRestart - DateTime.Now;
+ if (waitTime.TotalSeconds < 0)
+ waitTime = TimeSpan.FromSeconds(0);
+ Log.WriteMessage(LogMessageType.Error, LogTag, "ServerCrash", ex, "Server crashed, restarting in {0} seconds", waitTime.TotalSeconds);
+ if (waitTime.TotalSeconds > 0)
+ await Task.Delay(waitTime);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Registers the machine with the remote server
+ ///
+ /// The URL to register the machine with
+ /// The file to save the settings to
+ /// The passphrase for the settings file
+ /// The cancellation token to use for the process
+ /// The settings for the machine
+ private static async Task Register(string registrationUrl, string settingsFile, string? settingsPasshrase, CancellationToken cancellationToken)
+ {
+ var settings = Settings.Load(settingsFile, settingsPasshrase);
+ if (string.IsNullOrWhiteSpace(settings.JWT))
+ {
+ Log.WriteMessage(LogMessageType.Information, LogTag, "ClientNoJWT", "No JWT found in settings, starting in registration mode");
+ using (var registration = new RegisterForRemote(registrationUrl, null, cancellationToken))
+ {
+ var registerClientData = await registration.Register();
+ if (registerClientData.RegistrationData != null)
+ {
+ Log.WriteMessage(LogMessageType.Information, LogTag, "ClientRegistered", $"Machine registered, claim it by visiting: {registerClientData.RegistrationData.ClaimLink}");
+
+ try
+ {
+ Library.Utility.UrlUtility.OpenURL(registerClientData.RegistrationData.ClaimLink);
+ }
+ catch (Exception ex)
+ {
+ Log.WriteMessage(LogMessageType.Warning, LogTag, "ClientClaimLink", ex, "Failed to open the claim link in system browser");
+ }
+ }
+ var claimedClientData = await registration.Claim();
+
+ Log.WriteMessage(LogMessageType.Information, LogTag, "ClientClaimed", "Machine claimed, saving JWT");
+ settings = settings with
+ {
+ JWT = claimedClientData.JWT,
+ ServerUrl = claimedClientData.ServerUrl,
+ SettingsEncryptionKey = claimedClientData.LocalEncryptionKey,
+ ServerCertificates = claimedClientData.ServerCertificates,
+ CertificateUrl = claimedClientData.CertificateUrl
+ };
+
+ settings.Save();
+ }
+ }
+
+ return settings;
+ }
+
+ ///
+ /// Starts the local webserver in locked down mode, restarting on crashes
+ ///
+ /// The agent configuration
+ /// The settings for the agent
+ /// The cancellation token that stops the server
+ /// An awaitable task
+ private static async Task StartLocalServer(CommandLineArguments agentConfig, Settings settings, CancellationToken cancellationToken)
+ {
+ // TODO: Look into pipes for Kestrel to prevent network access
+
+ string? settingsEncryptionKey = null;
+
+ // Prefer the local key for the settings encryption
+ if (!string.IsNullOrWhiteSpace(agentConfig.SettingsEncryptionKey))
+ settingsEncryptionKey = agentConfig.SettingsEncryptionKey;
+ // Otherwise use server provided key
+ else if (!string.IsNullOrWhiteSpace(settings.SettingsEncryptionKey))
+ settingsEncryptionKey = settings.SettingsEncryptionKey;
+
+ // Helper method to support empty arguments
+ static string EncodeOption(string option, string? value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ return "";
+
+ return $"{option}={value}";
+ }
+
+ // Lock down the instance, reset tokens and password,
+ // and forward relevant settings from agent to the webserver
+ var args = new[] {
+ EncodeOption("--webservice-listen-interface", agentConfig.WebserviceListenInterface),
+ EncodeOption("--webservice-password", agentConfig.WebservicePassword),
+ EncodeOption("--webservice-port", agentConfig.WebservicePort),
+ EncodeOption("--webservice-reset-jwt-config", agentConfig.WebserviceResetJwtConfig.ToString()),
+ EncodeOption("--webservice-allowed-hostnames", agentConfig.WebserviceAllowedHostnames),
+ EncodeOption("--webservice-api-only", agentConfig.WebserviceApiOnly.ToString()),
+ EncodeOption("--webservice-disable-signin-tokens", agentConfig.WebserviceDisableSigninTokens.ToString()),
+ EncodeOption("--disable-db-encryption", agentConfig.DisableDbEncryption.ToString()),
+ EncodeOption("--settings-encryption-key", settingsEncryptionKey),
+ }
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .ToArray();
+
+ // Set the global origin
+ FIXMEGlobal.Origin = "Agent";
+
+ // Start the server
+ await RunServer(args, cancellationToken);
+ }
+}
+
diff --git a/Duplicati/Agent/Settings.cs b/Duplicati/Agent/Settings.cs
new file mode 100644
index 000000000..17bac95fd
--- /dev/null
+++ b/Duplicati/Agent/Settings.cs
@@ -0,0 +1,157 @@
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Duplicati.Library.Encryption;
+using Duplicati.Library.Interface;
+using Duplicati.Library.Main;
+using Duplicati.Library.RemoteControl;
+
+namespace Duplicati.Agent;
+
+///
+/// Settings for the agent
+///
+/// The filename of the settings
+/// The JWT used for authentication
+/// The URL of the server
+/// The encryption key for the local settings
+/// The server certificates
+/// The encryption key to use for agent settings
+public sealed record Settings(
+ string Filename,
+ string JWT,
+ string ServerUrl,
+ string CertificateUrl,
+ string? SettingsEncryptionKey,
+ IEnumerable ServerCertificates,
+ EncryptedFieldHelper.KeyInstance? Key
+)
+{
+ ///
+ /// Gets the default settings file path
+ ///
+ public static readonly string DefaultSettingsFile = GetSettingsPath();
+ ///
+ /// Gets the default settings file name
+ ///
+ public const string DefaultSettingsFilename = "agent.json";
+
+ ///
+ /// The encryption key to use for agent settings
+ ///
+ private static readonly EncryptedFieldHelper.KeyInstance? DefaultKey = null;
+
+ ///
+ /// Shared JSON serialization options
+ ///
+ private static readonly JsonSerializerOptions JsonOptions = new()
+ {
+ WriteIndented = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
+ PropertyNameCaseInsensitive = true
+ };
+
+ ///
+ /// Loads the settings from a file
+ ///
+ /// The path to the file
+ /// The loaded settings
+ public static Settings Load(string path, string? key)
+ {
+ var keyInstance = string.IsNullOrWhiteSpace(key)
+ ? DefaultKey
+ : EncryptedFieldHelper.KeyInstance.CreateKey(key);
+
+ var tmp = (File.Exists(path) ? JsonSerializer.Deserialize(File.ReadAllText(path), JsonOptions) : null)
+ ?? new Settings(path, string.Empty, string.Empty, string.Empty, string.Empty, Array.Empty(), null);
+
+ try
+ {
+ return tmp with
+ {
+ Filename = path,
+ JWT = EncryptedFieldHelper.Decrypt(tmp.JWT, keyInstance),
+ SettingsEncryptionKey = EncryptedFieldHelper.Decrypt(tmp.SettingsEncryptionKey, keyInstance),
+ Key = keyInstance
+ };
+ }
+ catch (SettingsEncryptionKeyMismatchException sek)
+ {
+ throw new UserInformationException("Invalid settings key provided", "InvalidAgentSettingsKey", sek);
+ }
+ catch (SettingsEncryptionKeyMissingException sek)
+ {
+ throw new UserInformationException("Settings file is encrypted but key is missing", "AgentSettingsKeyMissing", sek);
+ }
+ }
+
+ ///
+ /// Saves the settings
+ ///
+ public void Save()
+ {
+ var tmp = this with { Key = null, Filename = null! };
+ tmp = Key == null || Key.IsBlacklisted
+ ? tmp
+ : tmp with
+ {
+ JWT = EncryptFieldIfPossible(JWT, Key),
+ SettingsEncryptionKey = EncryptFieldIfPossible(SettingsEncryptionKey, Key)
+ };
+
+ File.WriteAllText(Filename, JsonSerializer.Serialize(tmp, JsonOptions));
+ }
+
+ ///
+ /// Encrypts a field if possible
+ ///
+ /// The value to encrypt
+ /// The encrypted value
+ [return: NotNullIfNotNull("value")]
+ private static string? EncryptFieldIfPossible(string? value, EncryptedFieldHelper.KeyInstance? key)
+ {
+ if (key == null || key.IsBlacklisted)
+ return value;
+ if (value == null)
+ return null;
+ if (string.IsNullOrWhiteSpace(value))
+ return string.Empty;
+ return EncryptedFieldHelper.Encrypt(value, key);
+ }
+
+ ///
+ /// Gets the default settings file path
+ ///
+ /// The path to the settings file
+ private static string GetSettingsPath()
+ {
+ var folder = DatabaseLocator.GetDefaultStorageFolderWithDebugSupport(DefaultSettingsFilename);
+ if (!Directory.Exists(folder))
+ Directory.CreateDirectory(folder);
+
+ return Path.Combine(folder, DefaultSettingsFilename);
+ }
+}
diff --git a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
index 69d035db8..f71a978e1 100644
--- a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
+++ b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
@@ -1,218 +1,31 @@
-
-
-
- Debug
- AnyCPU
- {E7280DCA-7776-4A73-B9B5-41FD77FC8799}
- Exe
- False
- Properties
- Duplicati.CommandLine.BackendTester
- Duplicati.CommandLine.BackendTester
- 512
-
-
- 3.5
-
- Duplicati.snk
- v4.7.1
-
- false
- true
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- ~/.config/dupl-unittest.txt
-
-
-
-
-
- false
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Designer
-
-
-
-
- {8E4CECFB-0413-4B00-AB93-78D1C3902BD5}
- Duplicati.Library.Backend.AzureBlob
-
-
- {b20a7cee-9c5b-47b9-8b76-bc85adfe8493}
- Duplicati.Library.Backend.Dropbox
-
-
- {2cd5dbc3-3da6-432d-ba97-f0b8d24501c2}
- Duplicati.Library.Backend.Jottacloud
-
-
- {851a1cb8-3ceb-41b4-956f-34d760d2a8e5}
- Duplicati.Library.Backend.Rclone
-
-
- {59C8BBC5-6E42-46FB-AB3E-6C183A82459A}
- Duplicati.Library.Backend.SharePoint
-
-
- {32a74526-3e5f-413a-8cb4-1efdad4c8b91}
- Duplicati.Library.Backend.Sia
-
-
- {ae035e01-c917-4f13-a35e-78f21c1a2f17}
- Duplicati.Library.Backend.Storj
-
-
- {9a04cb37-da72-4008-9703-3ac5191974e9}
- Duplicati.Library.Backend.Tardigrade
-
-
- {545dd6d4-9476-42d6-b51c-a28e000c489e}
- Duplicati.Library.Backend.TencentCOS
-
-
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}
- Duplicati.Library.Utility
-
-
- {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}
- Duplicati.Library.DynamicLoader
-
-
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}
- Duplicati.Library.Interface
-
-
- {7E119745-1F62-43F0-936C-F312A1912C0B}
- Duplicati.Library.AutoUpdater
-
-
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}
- Duplicati.License
-
-
- {CCD76347-7DC7-4B42-B7E1-E500E624CAC3}
- Duplicati.Library.Backend.OneDrive
-
-
- {C0270709-2A40-43B5-8CF1-69581B9FA2A1}
- Duplicati.Library.Backend.TahoeLAFS
-
-
- {5489181D-950C-44AF-873C-45EB0A3B6BD2}
- Duplicati.Library.Backend.GoogleServices
-
-
- {D9E4E686-423C-48EC-A392-404E7C00860C}
- Duplicati.Library.Backend.OpenStack
-
-
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}
- Duplicati.Library.Backend.HubiC
-
-
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}
- Duplicati.Library.Backend.CloudFiles
-
-
- {FC9B7611-836F-4127-8B44-A7C31F506807}
- Duplicati.Library.Backend.File
-
-
- {F61679A9-E5DE-468A-B5A4-05F92D0143D2}
- Duplicati.Library.Backend.FTP
-
-
- {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}
- Duplicati.Library.Backend.S3
-
-
- {FF2BF37C-E502-4C98-BEA0-701671DDFA08}
- Duplicati.Library.Backend.SSHv2
-
-
- {BAE27510-8B5D-44B2-B33E-372A98908041}
- Duplicati.Library.Backend.WEBDAV
-
-
- {52826615-7964-47FE-B4B3-1B2DBDF605B9}
- Duplicati.Library.Modules.Builtin
-
-
- {D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}
- Duplicati.Library.OAuthHelper
-
-
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}
- Duplicati.Library.Localization
-
-
- {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}
- Duplicati.Library.Logging
-
-
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}
- Duplicati.Library.Backend.Backblaze
-
-
- {6643A5AE-AB38-453F-ADCE-408E35A81A83}
- Duplicati.Library.Backend.Mega
-
-
- {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}
- Duplicati.Library.Backend.Box
-
-
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}
- Duplicati.Library.Backend.AlternativeFTP
-
-
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}
- Duplicati.Library.Common
-
-
- {19ECCE09-B5EB-406C-8C57-BAC66997D469}
- Duplicati.Library.Compression
-
-
- {2C838169-B187-4B09-8768-1C24C2521C8D}
- Duplicati.Library.SQLiteHelper
-
-
-
-
-
\ No newline at end of file
+
+
+
+ net8.0
+ A backend debugging tool for Duplicati
+ Duplicati.CommandLine.BackendTester.Implementation
+ Copyright © 2024 Team Duplicati, MIT license
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/Duplicati/CommandLine/BackendTester/Duplicati.snk b/Duplicati/CommandLine/BackendTester/Duplicati.snk
deleted file mode 100644
index e0c1e2dd8..000000000
Binary files a/Duplicati/CommandLine/BackendTester/Duplicati.snk and /dev/null differ
diff --git a/Duplicati/CommandLine/BackendTester/NonSeekableStream.cs b/Duplicati/CommandLine/BackendTester/NonSeekableStream.cs
index f60dc495d..448d86e0c 100644
--- a/Duplicati/CommandLine/BackendTester/NonSeekableStream.cs
+++ b/Duplicati/CommandLine/BackendTester/NonSeekableStream.cs
@@ -1,22 +1,24 @@
-#region Disclaimer / License
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
-// version 2.1 of the License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-//
-#endregion
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
using System;
using System.Collections.Generic;
using System.Text;
diff --git a/Duplicati/CommandLine/BackendTester/Program.cs b/Duplicati/CommandLine/BackendTester/Program.cs
index 7ec611cb4..a6cb3325c 100644
--- a/Duplicati/CommandLine/BackendTester/Program.cs
+++ b/Duplicati/CommandLine/BackendTester/Program.cs
@@ -1,22 +1,24 @@
-#region Disclaimer / License
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
-// version 2.1 of the License, or (at your option) any later version.
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-//
-#endregion
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
using System;
using System.Collections.Generic;
using System.Text;
@@ -24,19 +26,13 @@ using Duplicati.Library.Interface;
using System.Linq;
using System.Globalization;
using System.Threading;
+using Duplicati.Library.Utility;
+using Duplicati.StreamUtil;
namespace Duplicati.CommandLine.BackendTester
{
- class Program
+ public class Program
{
-
- ///
- /// Used to maintain a reference to initialized system settings.
- ///
- #pragma warning disable CS0414 // The private field `Duplicati.CommandLine.BackendTester.Program.SystemSettings' is assigned but its value is never used
- private static IDisposable SystemSettings;
- #pragma warning restore CS0414 // The private field `Duplicati.CommandLine.BackendTester.Program.SystemSettings' is assigned but its value is never used
-
class TempFile
{
public readonly string remotefilename;
@@ -61,16 +57,12 @@ namespace Duplicati.CommandLine.BackendTester
/// The main entry point for the application.
///
[STAThread]
- public static int Main(string[] args)
- {
- Duplicati.Library.AutoUpdater.UpdaterManager.IgnoreWebrootFolder = true;
- return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args);
- }
-
- public static void RealMain(string[] _args)
+ public static int Main(string[] _args)
{
try
{
+ Library.AutoUpdater.PreloadSettingsLoader.ConfigurePreloadSettings(ref _args, Library.AutoUpdater.PackageHelper.NamedExecutable.BackendTester);
+
if (_args.Length == 1)
{
try
@@ -78,8 +70,8 @@ namespace Duplicati.CommandLine.BackendTester
var p = Environment.ExpandEnvironmentVariables(_args[0]);
if (System.IO.File.Exists(p))
_args = (from x in System.IO.File.ReadLines(p)
- where !string.IsNullOrWhiteSpace(x) && !x.Trim().StartsWith("#", StringComparison.Ordinal)
- select x.Trim()
+ where !string.IsNullOrWhiteSpace(x) && !x.Trim().StartsWith("#", StringComparison.Ordinal)
+ select x.Trim()
).ToArray();
}
catch
@@ -105,13 +97,13 @@ namespace Duplicati.CommandLine.BackendTester
foreach (string s in lines)
Console.WriteLine(s);
- return;
+ return 0;
}
if (options.ContainsKey("tempdir") && !string.IsNullOrEmpty(options["tempdir"]))
Library.Utility.SystemContextSettings.DefaultTempPath = options["tempdir"];
-
- SystemSettings = Duplicati.Library.Utility.SystemContextSettings.StartSession();
+
+ using var SystemSettings = Duplicati.Library.Utility.SystemContextSettings.StartSession();
if (!options.ContainsKey("auth_password") && !string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("AUTH_PASSWORD")))
options["auth_password"] = System.Environment.GetEnvironmentVariable("AUTH_PASSWORD");
@@ -127,19 +119,22 @@ namespace Duplicati.CommandLine.BackendTester
{
Console.WriteLine("Starting run no {0}", i);
if (!Run(args, options, i == 0))
- return;
+ return 1;
}
Console.WriteLine("Unittest complete!");
+ return 0;
}
catch (Exception ex)
{
Console.WriteLine("Unittest failed: " + ex);
}
+
+ return 1;
}
static bool Run(List args, Dictionary options, bool first)
{
- Library.Interface.IBackend backend = Library.DynamicLoader.BackendLoader.GetBackend(args[0], options);
+ var backend = Library.DynamicLoader.BackendLoader.GetBackend(args[0], options);
if (backend == null)
{
Console.WriteLine("Unsupported backend");
@@ -148,23 +143,21 @@ namespace Duplicati.CommandLine.BackendTester
return false;
}
- string allowedChars = ValidFilenameChars;
+ var allowedChars = ValidFilenameChars;
if (options.ContainsKey("extended-chars"))
{
allowedChars += String.IsNullOrEmpty(options["extended-chars"]) ? ExtendedChars : options["extended-chars"];
}
- bool autoCreateFolders = Library.Utility.Utility.ParseBoolOption(options, "auto-create-folder");
+ var autoCreateFolders = Library.Utility.Utility.ParseBoolOption(options, "auto-create-folder");
- string disabledModulesValue;
- string enabledModulesValue;
- options.TryGetValue("enable-module", out enabledModulesValue);
- options.TryGetValue("disable-module", out disabledModulesValue);
- string[] enabledModules = enabledModulesValue == null ? new string[0] : enabledModulesValue.Trim().ToLower(CultureInfo.InvariantCulture).Split(',');
- string[] disabledModules = disabledModulesValue == null ? new string[0] : disabledModulesValue.Trim().ToLower(CultureInfo.InvariantCulture).Split(',');
+ options.TryGetValue("enable-module", out var enabledModulesValue);
+ options.TryGetValue("disable-module", out var disabledModulesValue);
+ var enabledModules = enabledModulesValue == null ? new string[0] : enabledModulesValue.Trim().ToLower(CultureInfo.InvariantCulture).Split(',');
+ var disabledModules = disabledModulesValue == null ? new string[0] : disabledModulesValue.Trim().ToLower(CultureInfo.InvariantCulture).Split(',');
- List loadedModules = new List();
- foreach (Library.Interface.IGenericModule m in Library.DynamicLoader.GenericLoader.Modules)
+ var loadedModules = new List();
+ foreach (var m in Library.DynamicLoader.GenericLoader.Modules)
if (!disabledModules.Contains(m.Key, StringComparer.OrdinalIgnoreCase) && (m.LoadAsDefault || enabledModules.Contains(m.Key, StringComparer.OrdinalIgnoreCase)))
{
m.Configure(options);
@@ -176,7 +169,7 @@ namespace Duplicati.CommandLine.BackendTester
IEnumerable curlist = null;
try
{
- backend.Test();
+ backend.TestAsync(CancellationToken.None).Await();
curlist = backend.List();
}
catch (FolderMissingException)
@@ -185,7 +178,7 @@ namespace Duplicati.CommandLine.BackendTester
{
try
{
- backend.CreateFolder();
+ backend.CreateFolderAsync(CancellationToken.None).Await();
curlist = backend.List();
}
catch (Exception ex)
@@ -205,28 +198,35 @@ namespace Duplicati.CommandLine.BackendTester
if (Library.Utility.Utility.ParseBoolOption(options, "force"))
{
Console.WriteLine("Auto clean, removing file: {0}", fe.Name);
- backend.Delete(fe.Name);
+ backend.DeleteAsync(fe.Name, CancellationToken.None).Await();
continue;
}
else
Console.WriteLine("Specify the --force flag to actually delete files");
- Console.WriteLine("*** Remote folder is not empty, aborting");
+ var fileCount = curlist.Where(x => !x.IsFolder).Count();
+ var filenames = curlist.Where(x => !x.IsFolder).Select(x => x.Name).Take(10).ToList();
+ Console.WriteLine($"*** Remote folder contains {fileCount} file(s), aborting");
+ Console.WriteLine($"*** First {filenames.Count} file(s): {Environment.NewLine}{string.Join(Environment.NewLine, filenames)}");
+ if (fileCount > filenames.Count)
+ Console.WriteLine($"*** ... and {fileCount - filenames.Count} more file(s)");
return false;
}
- int number_of_files = 10;
- int min_file_size = 1024;
- int max_file_size = 1024 * 1024 * 50;
- int min_filename_size = 5;
- int max_filename_size = 80;
- bool disableStreaming = Library.Utility.Utility.ParseBoolOption(options, "disable-streaming-transfers");
- bool skipOverwriteTest = Library.Utility.Utility.ParseBoolOption(options, "skip-overwrite-test");
- bool trimFilenameSpaces = Library.Utility.Utility.ParseBoolOption(options, "trim-filename-spaces");
+ var number_of_files = 10;
+ var min_file_size = 1024;
+ var max_file_size = 1024 * 1024 * 50;
+ var min_filename_size = 5;
+ var max_filename_size = 80;
+ var disableStreaming = Library.Utility.Utility.ParseBoolOption(options, "disable-streaming-transfers");
+ var skipOverwriteTest = Library.Utility.Utility.ParseBoolOption(options, "skip-overwrite-test");
+ var trimFilenameSpaces = Library.Utility.Utility.ParseBoolOption(options, "trim-filename-spaces");
+ var waitAfterUpload = TimeSpan.Zero;
+ var waitAfterDelete = TimeSpan.Zero;
- long throttleUpload = 0;
- if (options.TryGetValue("throttle-upload", out string throttleUploadString))
+ var throttleUpload = 0L;
+ if (options.TryGetValue("throttle-upload", out var throttleUploadString))
{
if (!(backend is IStreamingBackend) || disableStreaming)
{
@@ -236,8 +236,8 @@ namespace Duplicati.CommandLine.BackendTester
throttleUpload = Duplicati.Library.Utility.Sizeparser.ParseSize(throttleUploadString, "kb");
}
- long throttleDownload = 0;
- if (options.TryGetValue("throttle-download", out string throttleDownloadString))
+ var throttleDownload = 0L;
+ if (options.TryGetValue("throttle-download", out var throttleDownloadString))
{
if (!(backend is IStreamingBackend) || disableStreaming)
{
@@ -247,6 +247,17 @@ namespace Duplicati.CommandLine.BackendTester
throttleDownload = Duplicati.Library.Utility.Sizeparser.ParseSize(throttleDownloadString, "kb");
}
+ var readWriteTimeout = (int)TimeSpan.FromSeconds(10).TotalMilliseconds;
+ if (backend is ITimeoutExemptBackend)
+ readWriteTimeout = Timeout.Infinite;
+
+ // Allow overriding the timeout for the backend here, even if timeouts are disabled
+ if (options.TryGetValue("read-write-timeout", out var readWriteTimeoutString))
+ readWriteTimeout = (int)Timeparser.ParseTimeSpan(readWriteTimeoutString).TotalMilliseconds;
+
+ if (readWriteTimeout <= 0)
+ readWriteTimeout = Timeout.Infinite;
+
if (options.ContainsKey("number-of-files"))
number_of_files = int.Parse(options["number-of-files"]);
if (options.ContainsKey("min-file-size"))
@@ -259,21 +270,26 @@ namespace Duplicati.CommandLine.BackendTester
if (options.ContainsKey("max-filename-length"))
max_filename_size = int.Parse(options["max-filename-length"]);
- Random rnd = new Random();
- System.Security.Cryptography.SHA256 sha = System.Security.Cryptography.SHA256.Create();
+ if (options.ContainsKey("wait-after-upload"))
+ waitAfterUpload = Timeparser.ParseTimeSpan(options["wait-after-upload"]);
+ if (options.ContainsKey("wait-after-delete"))
+ waitAfterDelete = Timeparser.ParseTimeSpan(options["wait-after-delete"]);
+
+ var rnd = new Random();
+ var sha = System.Security.Cryptography.SHA256.Create();
//Create random files
- using (Library.Utility.TempFolder tf = new Duplicati.Library.Utility.TempFolder())
+ using (var tf = new Duplicati.Library.Utility.TempFolder())
{
- List files = new List();
+ var files = new List();
for (int i = 0; i < number_of_files; i++)
{
- string filename = CreateRandomRemoteFileName(min_filename_size, max_filename_size, allowedChars, trimFilenameSpaces, rnd);
+ var filename = CreateRandomRemoteFileName(min_filename_size, max_filename_size, allowedChars, trimFilenameSpaces, rnd);
- string localfilename = CreateRandomFile(tf, i, min_file_size, max_file_size, rnd);
+ var localfilename = CreateRandomFile(tf, i, min_file_size, max_file_size, rnd);
//Calculate local hash and length
- using (System.IO.FileStream fs = new System.IO.FileStream(localfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ using (var fs = new System.IO.FileStream(localfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read))
files.Add(new TempFile(filename, localfilename, sha.ComputeHash(fs), fs.Length));
}
@@ -281,16 +297,16 @@ namespace Duplicati.CommandLine.BackendTester
if (!skipOverwriteTest)
{
Console.WriteLine("Uploading wrong files ...");
- using (Library.Utility.TempFile dummy = Library.Utility.TempFile.WrapExistingFile(CreateRandomFile(tf, files.Count, 1024, 2048, rnd)))
+ using (var dummy = Library.Utility.TempFile.WrapExistingFile(CreateRandomFile(tf, files.Count, 1024, 2048, rnd)))
{
- using (System.IO.FileStream fs = new System.IO.FileStream(dummy, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ using (var fs = new System.IO.FileStream(dummy, System.IO.FileMode.Open, System.IO.FileAccess.Read))
dummyFileHash = sha.ComputeHash(fs);
//Upload a dummy file for entry 0 and the last one, they will be replaced by the real files afterwards
//We upload entry 0 twice just to try to freak any internal cache list
- Uploadfile(dummy, 0, files[0].remotefilename, backend, disableStreaming, throttleUpload);
- Uploadfile(dummy, 0, files[0].remotefilename, backend, disableStreaming, throttleUpload);
- Uploadfile(dummy, files.Count - 1, files[files.Count - 1].remotefilename, backend, disableStreaming, throttleUpload);
+ Uploadfile(dummy, 0, files[0].remotefilename, backend, disableStreaming, throttleUpload, readWriteTimeout);
+ Uploadfile(dummy, 0, files[0].remotefilename, backend, disableStreaming, throttleUpload, readWriteTimeout);
+ Uploadfile(dummy, files.Count - 1, files[files.Count - 1].remotefilename, backend, disableStreaming, throttleUpload, readWriteTimeout);
}
}
@@ -298,12 +314,11 @@ namespace Duplicati.CommandLine.BackendTester
Console.WriteLine("Uploading files ...");
for (int i = 0; i < files.Count; i++)
- Uploadfile(files[i].localfilename, i, files[i].remotefilename, backend, disableStreaming, throttleUpload);
+ Uploadfile(files[i].localfilename, i, files[i].remotefilename, backend, disableStreaming, throttleUpload, readWriteTimeout);
TempFile originalRenamedFile = null;
string renamedFileNewName = null;
- IRenameEnabledBackend renameEnabledBackend = backend as IRenameEnabledBackend;
- if (renameEnabledBackend != null)
+ if (backend is IRenameEnabledBackend renameEnabledBackend)
{
// Rename the second file in the list, if there are more than one. If not, just do the first one.
int renameIndex = files.Count > 1 ? 1 : 0;
@@ -313,18 +328,24 @@ namespace Duplicati.CommandLine.BackendTester
Console.WriteLine("Renaming file {0} from {1} to {2}", renameIndex, originalRenamedFile.remotefilename, renamedFileNewName);
- renameEnabledBackend.Rename(originalRenamedFile.remotefilename, renamedFileNewName);
+ renameEnabledBackend.RenameAsync(originalRenamedFile.remotefilename, renamedFileNewName, CancellationToken.None).Await();
files[renameIndex] = new TempFile(renamedFileNewName, originalRenamedFile.localfilename, originalRenamedFile.hash, originalRenamedFile.length);
}
+ if (waitAfterUpload > TimeSpan.Zero)
+ {
+ Console.WriteLine("Waiting {0} after upload", waitAfterUpload);
+ Thread.Sleep(waitAfterUpload);
+ }
+
Console.WriteLine("Verifying file list ...");
curlist = backend.List();
- foreach (Library.Interface.IFileEntry fe in curlist)
+ foreach (var fe in curlist)
if (!fe.IsFolder)
{
bool found = false;
- foreach (TempFile tx in files)
+ foreach (var tx in files)
if (tx.remotefilename == fe.Name)
{
if (tx.found)
@@ -349,7 +370,7 @@ namespace Duplicati.CommandLine.BackendTester
}
}
- foreach (TempFile tx in files)
+ foreach (var tx in files)
if (!tx.found)
Console.WriteLine("*** File with name {0} was uploaded but not found afterwards", tx.remotefilename);
@@ -357,7 +378,7 @@ namespace Duplicati.CommandLine.BackendTester
for (int i = 0; i < files.Count; i++)
{
- using (Duplicati.Library.Utility.TempFile cf = new Duplicati.Library.Utility.TempFile())
+ using (var cf = new Duplicati.Library.Utility.TempFile())
{
Exception e = null;
Console.Write("Downloading file {0} ... ", i);
@@ -366,13 +387,14 @@ namespace Duplicati.CommandLine.BackendTester
{
if (backend is IStreamingBackend streamingBackend && !disableStreaming)
{
- using (System.IO.FileStream fs = new System.IO.FileStream(cf, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
- using (Library.Utility.ThrottledStream ts = new Library.Utility.ThrottledStream(fs, throttleDownload, throttleDownload))
- using (NonSeekableStream nss = new NonSeekableStream(ts))
- streamingBackend.Get(files[i].remotefilename, nss);
+ using (var fs = new System.IO.FileStream(cf, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
+ using (var timeoutStream = new TimeoutObservingStream(fs) { WriteTimeout = readWriteTimeout })
+ using (var ts = new Library.Utility.ThrottledStream(timeoutStream, throttleDownload, throttleDownload))
+ using (var nss = new NonSeekableStream(ts))
+ streamingBackend.GetAsync(files[i].remotefilename, nss, timeoutStream.TimeoutToken).Await();
}
else
- backend.Get(files[i].remotefilename, cf);
+ backend.GetAsync(files[i].remotefilename, cf, CancellationToken.None).Await();
e = null;
}
@@ -388,7 +410,7 @@ namespace Duplicati.CommandLine.BackendTester
Console.Write("Checking hash ... ");
- using (System.IO.FileStream fs = new System.IO.FileStream(cf, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ using (var fs = new System.IO.FileStream(cf, System.IO.FileMode.Open, System.IO.FileAccess.Read))
if (Convert.ToBase64String(sha.ComputeHash(fs)) != Convert.ToBase64String(files[i].hash))
{
if (dummyFileHash != null && Convert.ToBase64String(sha.ComputeHash(fs)) == Convert.ToBase64String(dummyFileHash))
@@ -403,15 +425,21 @@ namespace Duplicati.CommandLine.BackendTester
Console.WriteLine("Deleting files...");
- foreach (TempFile tx in files)
- try { backend.Delete(tx.remotefilename); }
+ foreach (var tx in files)
+ try { backend.DeleteAsync(tx.remotefilename, CancellationToken.None).Await(); }
catch (Exception ex)
{
Console.WriteLine("*** Failed to delete file {0}, message: {1}", tx.remotefilename, ex);
}
+ if (waitAfterDelete > TimeSpan.Zero)
+ {
+ Console.WriteLine("Waiting {0} after delete", waitAfterDelete);
+ Thread.Sleep(waitAfterDelete);
+ }
+
curlist = backend.List();
- foreach (Library.Interface.IFileEntry fe in curlist)
+ foreach (var fe in curlist)
if (!fe.IsFolder)
{
Console.WriteLine("*** Remote folder contains {0} after cleanup", fe.Name);
@@ -419,12 +447,12 @@ namespace Duplicati.CommandLine.BackendTester
// Test some error cases
Console.WriteLine("Checking retrieval of non-existent file...");
- bool caughtExpectedException = false;
+ var caughtExpectedException = false;
try
{
- using (Duplicati.Library.Utility.TempFile tempFile = new Duplicati.Library.Utility.TempFile())
+ using (var tempFile = new Duplicati.Library.Utility.TempFile())
{
- backend.Get(string.Format("NonExistentFile-{0}", Guid.NewGuid()), tempFile.Name);
+ backend.GetAsync(string.Format("NonExistentFile-{0}", Guid.NewGuid()), tempFile.Name, CancellationToken.None).Await();
}
}
catch (FileMissingException)
@@ -444,15 +472,14 @@ namespace Duplicati.CommandLine.BackendTester
}
// Test quota retrieval
- IQuotaEnabledBackend quotaEnabledBackend = backend as IQuotaEnabledBackend;
- if (quotaEnabledBackend != null)
+ if (backend is IQuotaEnabledBackend quotaEnabledBackend)
{
Console.WriteLine("Checking quota...");
IQuotaInfo quota = null;
bool noException;
try
{
- quota = quotaEnabledBackend.Quota;
+ quota = quotaEnabledBackend.GetQuotaInfoAsync(CancellationToken.None).Await();
noException = true;
}
catch (Exception ex)
@@ -479,7 +506,7 @@ namespace Duplicati.CommandLine.BackendTester
Console.WriteLine("Checking DNS names used by this backend...");
try
{
- string[] dnsNames = backend.DNSName;
+ var dnsNames = backend.GetDNSNamesAsync(CancellationToken.None).Await();
if (dnsNames != null)
{
foreach (string dnsName in dnsNames)
@@ -499,7 +526,7 @@ namespace Duplicati.CommandLine.BackendTester
}
finally
{
- foreach (Library.Interface.IGenericModule m in loadedModules)
+ foreach (var m in loadedModules)
if (m is IDisposable disposable)
disposable.Dispose();
}
@@ -507,7 +534,7 @@ namespace Duplicati.CommandLine.BackendTester
return true;
}
- private static void Uploadfile(string localfilename, int i, string remotefilename, IBackend backend, bool disableStreaming, long throttle)
+ private static void Uploadfile(string localfilename, int i, string remotefilename, IBackend backend, bool disableStreaming, long throttle, int readWriteTimeout)
{
Console.Write("Uploading file {0}, {1} ... ", i, Duplicati.Library.Utility.Utility.FormatSizeString(new System.IO.FileInfo(localfilename).Length));
Exception e = null;
@@ -516,13 +543,14 @@ namespace Duplicati.CommandLine.BackendTester
{
if (backend is IStreamingBackend streamingBackend && !disableStreaming)
{
- using (System.IO.FileStream fs = new System.IO.FileStream(localfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
- using (Library.Utility.ThrottledStream ts = new Library.Utility.ThrottledStream(fs, throttle, throttle))
- using (NonSeekableStream nss = new NonSeekableStream(ts))
- streamingBackend.PutAsync(remotefilename, nss, CancellationToken.None).Wait();
+ using (var fs = new System.IO.FileStream(localfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
+ using (var timeoutStream = new TimeoutObservingStream(fs) { ReadTimeout = readWriteTimeout })
+ using (var ts = new Library.Utility.ThrottledStream(timeoutStream, throttle, throttle))
+ using (var nss = new NonSeekableStream(ts))
+ streamingBackend.PutAsync(remotefilename, nss, timeoutStream.TimeoutToken).Await();
}
else
- backend.PutAsync(remotefilename, localfilename, CancellationToken.None).Wait();
+ backend.PutAsync(remotefilename, localfilename, CancellationToken.None).Await();
e = null;
}
@@ -596,11 +624,13 @@ namespace Duplicati.CommandLine.BackendTester
new CommandLineArgument("max-file-size", CommandLineArgument.ArgumentType.Size, "The maximum allowed file size", "File sizes are chosen at random, this value is the upper bound", "50mb"),
new CommandLineArgument("min-filename-length", CommandLineArgument.ArgumentType.Integer, "The minimum allowed filename length", "File name lengths are chosen at random, this value is the lower bound", "5"),
new CommandLineArgument("max-filename-length", CommandLineArgument.ArgumentType.Integer, "The minimum allowed filename length", "File name lengths are chosen at random, this value is the upper bound", "80"),
- new CommandLineArgument("trim-filename-spaces", CommandLineArgument.ArgumentType.Boolean, "Trims whitespace from filenames", "A value that indicates if whitespace should be trimmed from the ends of randomly generated filenames", "false"),
- new CommandLineArgument("auto-create-folder", CommandLineArgument.ArgumentType.Boolean, "Allows automatic folder creation", "A value that indicates if missing folders are created automatically", "false"),
- new CommandLineArgument("skip-overwrite-test", CommandLineArgument.ArgumentType.Boolean, "Bypasses the overwrite test", "A value that indicates if dummy files should be uploaded prior to uploading the real files", "false"),
- new CommandLineArgument("auto-clean", CommandLineArgument.ArgumentType.Boolean, "Removes any files found in target folder", "A value that indicates if all files in the target folder should be deleted before starting the first test", "false"),
- new CommandLineArgument("force", CommandLineArgument.ArgumentType.Boolean, "Activates file deletion", "A value that indicates if existing files should really be deleted when using auto-clean", "false"),
+ new CommandLineArgument("trim-filename-spaces", CommandLineArgument.ArgumentType.Boolean, "Trim whitespace from filenames", "A value that indicates if whitespace should be trimmed from the ends of randomly generated filenames", "false"),
+ new CommandLineArgument("auto-create-folder", CommandLineArgument.ArgumentType.Boolean, "Allow automatic folder creation", "A value that indicates if missing folders are created automatically", "false"),
+ new CommandLineArgument("skip-overwrite-test", CommandLineArgument.ArgumentType.Boolean, "Bypass the overwrite test", "A value that indicates if dummy files should be uploaded prior to uploading the real files", "false"),
+ new CommandLineArgument("auto-clean", CommandLineArgument.ArgumentType.Boolean, "Remove any files found in target folder", "A value that indicates if all files in the target folder should be deleted before starting the first test", "false"),
+ new CommandLineArgument("force", CommandLineArgument.ArgumentType.Boolean, "Activate file deletion", "A value that indicates if existing files should really be deleted when using auto-clean", "false"),
+ new CommandLineArgument("wait-after-upload", CommandLineArgument.ArgumentType.Timespan, "Wait after all uploads", "A value that indicates how long to wait after all files are uploaded, to account for the backends eventual consistency", "0s"),
+ new CommandLineArgument("wait-after-delete", CommandLineArgument.ArgumentType.Timespan, "Wait after all deletes", "A value that indicates how long to wait after each delete operation, to account for the backends eventual consistency", "0s"),
});
}
}
diff --git a/Duplicati/CommandLine/BackendTester/Properties/AssemblyInfo.cs b/Duplicati/CommandLine/BackendTester/Properties/AssemblyInfo.cs
deleted file mode 100644
index 747b0ed7e..000000000
--- a/Duplicati/CommandLine/BackendTester/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-#region Disclaimer / License
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
-// version 2.1 of the License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-//
-#endregion
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("CommandLine.BackendTester")]
-[assembly: AssemblyDescription("A backend debugging tool for Duplicati")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Duplicati Team")]
-[assembly: AssemblyProduct("CommandLine.BackendTester")]
-[assembly: AssemblyCopyright("Copyright © Duplicati Team 2015")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("53df9eb0-a6e8-49a2-838d-b2a2c4a8d41f")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("2.0.0.7")]
-[assembly: AssemblyVersion("2.0.0.7")]
-[assembly: AssemblyFileVersion("2.0.0.7")]
diff --git a/Duplicati/CommandLine/BackendTester/app.config b/Duplicati/CommandLine/BackendTester/app.config
deleted file mode 100644
index f8e90b9d2..000000000
--- a/Duplicati/CommandLine/BackendTester/app.config
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
index f344da03f..93d68ae44 100644
--- a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
+++ b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
@@ -1,171 +1,24 @@
-
-
-
- Debug
- AnyCPU
- {2AF960C0-357D-4D44-A3D5-8B6E89DB0F11}
- Exe
- False
- Duplicati.CommandLine.BackendTool
- Duplicati.CommandLine.BackendTool
- ..\Duplicati.snk
- v4.7.1
-
- false
- true
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- false
- false
-
-
-
-
-
-
-
- none
- true
- bin\Release
- prompt
- 4
- true
- false
-
-
-
-
-
-
-
-
-
-
-
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}
- Duplicati.Library.Backend.CloudFiles
-
-
- {8E4CECFB-0413-4B00-AB93-78D1C3902BD5}
- Duplicati.Library.Backend.AzureBlob
-
-
- {FC9B7611-836F-4127-8B44-A7C31F506807}
- Duplicati.Library.Backend.File
-
-
- {F61679A9-E5DE-468A-B5A4-05F92D0143D2}
- Duplicati.Library.Backend.FTP
-
-
- {2cd5dbc3-3da6-432d-ba97-f0b8d24501c2}
- Duplicati.Library.Backend.Jottacloud
-
-
- {851a1cb8-3ceb-41b4-956f-34d760d2a8e5}
- Duplicati.Library.Backend.Rclone
-
-
- {C03F6DFD-805A-4BE0-9338-64870ADDB4A2}
- Duplicati.Library.Backend.S3
-
-
- {59C8BBC5-6E42-46FB-AB3E-6C183A82459A}
- Duplicati.Library.Backend.SharePoint
-
-
- {32a74526-3e5f-413a-8cb4-1efdad4c8b91}
- Duplicati.Library.Backend.Sia
-
-
- {FF2BF37C-E502-4C98-BEA0-701671DDFA08}
- Duplicati.Library.Backend.SSHv2
-
-
- {C0270709-2A40-43B5-8CF1-69581B9FA2A1}
- Duplicati.Library.Backend.TahoeLAFS
-
-
- {ae035e01-c917-4f13-a35e-78f21c1a2f17}
- Duplicati.Library.Backend.Storj
-
-
- {9a04cb37-da72-4008-9703-3ac5191974e9}
- Duplicati.Library.Backend.Tardigrade
-
-
- {545dd6d4-9476-42d6-b51c-a28e000c489e}
- Duplicati.Library.Backend.TencentCOS
-
-
- {BAE27510-8B5D-44B2-B33E-372A98908041}
- Duplicati.Library.Backend.WEBDAV
-
-
- {0CA86ECF-5BEC-4909-B4F6-110A03B30B92}
- Duplicati.Library.DynamicLoader
-
-
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}
- Duplicati.Library.Interface
-
-
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}
- Duplicati.Library.Utility
-
-
- {7E119745-1F62-43F0-936C-F312A1912C0B}
- Duplicati.Library.AutoUpdater
-
-
- {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}
- Duplicati.License
-
-
- {CCD76347-7DC7-4B42-B7E1-E500E624CAC3}
- Duplicati.Library.Backend.OneDrive
-
-
- {5489181D-950C-44AF-873C-45EB0A3B6BD2}
- Duplicati.Library.Backend.GoogleServices
-
-
- {D9E4E686-423C-48EC-A392-404E7C00860C}
- Duplicati.Library.Backend.OpenStack
-
-
- {D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}
- Duplicati.Library.Backend.HubiC
-
-
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}
- Duplicati.Library.Backend.Backblaze
-
-
- {6643A5AE-AB38-453F-ADCE-408E35A81A83}
- Duplicati.Library.Backend.Mega
-
-
- {3FF7DD0B-5284-4BF9-97D9-1E4417FDABB2}
- Duplicati.Library.Backend.Box
-
-
- {3F47CB0C-CEE1-447F-9C26-7F8E419E3E3F}
- Duplicati.Library.Backend.AlternativeFTP
-
-
- {B20A7CEE-9C5B-47B9-8B76-BC85ADFE8493}
- Duplicati.Library.Backend.Dropbox
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+ net8.0
+ Duplicati.CommandLine.BackendTool.Implementation
+ Copyright © 2024 Team Duplicati, MIT license
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/Duplicati/CommandLine/BackendTool/Program.cs b/Duplicati/CommandLine/BackendTool/Program.cs
index c72cee92d..5af128c64 100644
--- a/Duplicati/CommandLine/BackendTool/Program.cs
+++ b/Duplicati/CommandLine/BackendTool/Program.cs
@@ -1,26 +1,30 @@
-#region Disclaimer / License
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
-// version 2.1 of the License, or (at your option) any later version.
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-//
-#endregion
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
using Duplicati.Library.Interface;
+using Duplicati.Library.Utility;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading;
namespace Duplicati.CommandLine.BackendTool
@@ -31,17 +35,13 @@ namespace Duplicati.CommandLine.BackendTool
/// The main entry point for the application.
///
[STAThread]
- public static int Main(string[] args)
- {
- Duplicati.Library.AutoUpdater.UpdaterManager.IgnoreWebrootFolder = true;
- return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args);
- }
-
- public static int RealMain(string[] _args)
+ public static int Main(string[] _args)
{
bool debugoutput = false;
try
{
+ Library.AutoUpdater.PreloadSettingsLoader.ConfigurePreloadSettings(ref _args, Library.AutoUpdater.PackageHelper.NamedExecutable.BackendTool);
+
List args = new List(_args);
Dictionary options = Library.Utility.CommandLineParser.ExtractOptions(args);
@@ -80,8 +80,8 @@ namespace Duplicati.CommandLine.BackendTool
{
Console.WriteLine("Unsupported command: {0}", args[0]);
Console.WriteLine();
- }
-
+ }
+
Console.WriteLine("Usage: ://:@ [filename]");
Console.WriteLine("Example: LIST ftp://user:pass@server/folder");
Console.WriteLine();
@@ -90,19 +90,41 @@ namespace Duplicati.CommandLine.BackendTool
return 200;
}
-
- using(var backend = Library.DynamicLoader.BackendLoader.GetBackend(args[1], options))
+
+ var modules = (from n in Library.DynamicLoader.GenericLoader.Modules
+ where n is Library.Interface.IConnectionModule
+ select n).ToArray();
+
+ var uri = new Library.Utility.Uri(args[1]);
+ var qp = uri.QueryParameters;
+
+ var backendOpts = new Dictionary();
+ foreach (var k in qp.Keys.Cast())
+ backendOpts[k] = qp[k];
+
+ foreach (var k in backendOpts.Keys)
+ {
+ options.Remove(k);
+ }
+
+ foreach (var n in modules)
+ {
+ n.Configure(options);
+ n.Configure(backendOpts);
+ }
+
+ using (var backend = Library.DynamicLoader.BackendLoader.GetBackend(args[1], options))
{
if (backend == null)
throw new UserInformationException("Backend not supported", "InvalidBackend");
-
+
if (command == "list")
{
if (args.Count != 2)
throw new UserInformationException(string.Format("too many arguments: {0}", string.Join(",", args)), "BackendToolTooManyArguments");
Console.WriteLine("{0}\t{1}\t{2}\t{3}", "Name", "Dir/File", "LastChange", "Size");
-
- foreach(var e in backend.List())
+
+ foreach (var e in backend.List())
Console.WriteLine("{0}\t{1}\t{2}\t{3}", e.Name, e.IsFolder ? "Dir" : "File", e.LastModification, e.Size < 0 ? "" : Library.Utility.Utility.FormatSizeString(e.Size));
return 0;
@@ -112,8 +134,8 @@ namespace Duplicati.CommandLine.BackendTool
if (args.Count != 2)
throw new UserInformationException(string.Format("too many arguments: {0}", string.Join(",", args)), "BackendToolTooManyArguments");
- backend.CreateFolder();
-
+ backend.CreateFolderAsync(CancellationToken.None).Await();
+
return 0;
}
else if (command == "delete")
@@ -122,8 +144,8 @@ namespace Duplicati.CommandLine.BackendTool
throw new UserInformationException("DELETE requires a filename argument", "BackendToolDeleteRequiresAnArgument");
if (args.Count > 3)
throw new Exception(string.Format("too many arguments: {0}", string.Join(",", args)));
- backend.Delete(Path.GetFileName(args[2]));
-
+ backend.DeleteAsync(Path.GetFileName(args[2]), CancellationToken.None).Await();
+
return 0;
}
else if (command == "get")
@@ -134,25 +156,24 @@ namespace Duplicati.CommandLine.BackendTool
throw new UserInformationException(string.Format("too many arguments: {0}", string.Join(",", args)), "BackendToolTooManyArguments");
if (File.Exists(args[2]))
throw new UserInformationException("File already exists, not overwriting!", "BackendToolFileAlreadyExists");
- backend.Get(Path.GetFileName(args[2]), args[2]);
-
+ backend.GetAsync(Path.GetFileName(args[2]), args[2], CancellationToken.None).Await();
+
return 0;
}
else if (command == "put")
{
if (args.Count < 3)
- throw new UserInformationException("PUT requires a filename argument","BackendToolPutRequiresAndArgument");
+ throw new UserInformationException("PUT requires a filename argument", "BackendToolPutRequiresAndArgument");
if (args.Count > 3)
throw new UserInformationException(string.Format("too many arguments: {0}", string.Join(",", args)), "BackendToolTooManyArguments");
-
- backend.PutAsync(Path.GetFileName(args[2]), args[2], CancellationToken.None).Wait();
-
+
+ backend.PutAsync(Path.GetFileName(args[2]), args[2], CancellationToken.None).Await();
+
return 0;
}
-
+
throw new Exception("Internal error");
}
-
}
catch (Exception ex)
{
diff --git a/Duplicati/CommandLine/BackendTool/Properties/AssemblyInfo.cs b/Duplicati/CommandLine/BackendTool/Properties/AssemblyInfo.cs
deleted file mode 100644
index 1c2c1b525..000000000
--- a/Duplicati/CommandLine/BackendTool/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2015, The Duplicati Team
-
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("Duplicati.CommandLine.BackendTool")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("2.0.0.7")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
-
diff --git a/Duplicati/CommandLine/BackendTool/app.config b/Duplicati/CommandLine/BackendTool/app.config
deleted file mode 100644
index bf21025c0..000000000
--- a/Duplicati/CommandLine/BackendTool/app.config
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Duplicati/CommandLine/Commands.cs b/Duplicati/CommandLine/CLI/Commands.cs
similarity index 91%
rename from Duplicati/CommandLine/Commands.cs
rename to Duplicati/CommandLine/CLI/Commands.cs
index 74523762c..f56f1982a 100644
--- a/Duplicati/CommandLine/Commands.cs
+++ b/Duplicati/CommandLine/CLI/Commands.cs
@@ -1,25 +1,31 @@
-// Copyright (C) 2015, The Duplicati Team
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
using Duplicati.Library.Common.IO;
using Duplicati.Library.Utility;
@@ -34,8 +40,9 @@ namespace Duplicati.CommandLine
private readonly System.Threading.ManualResetEvent m_readyEvent;
private readonly System.Threading.ManualResetEvent m_finishEvent;
private readonly ConsoleOutput m_output;
- private System.Threading.Thread m_thread;
private readonly TimeSpan m_updateFrequency;
+ private Task m_task;
+ private readonly CancellationTokenSource m_cancellationTokenSource;
public PeriodicOutput(ConsoleOutput messageSink, TimeSpan updateFrequency)
{
@@ -43,10 +50,8 @@ namespace Duplicati.CommandLine
m_readyEvent = new System.Threading.ManualResetEvent(false);
m_finishEvent = new System.Threading.ManualResetEvent(false);
m_updateFrequency = updateFrequency;
- m_thread = new System.Threading.Thread(this.ThreadMain);
- m_thread.IsBackground = true;
- m_thread.Name = "Periodic console writer";
- m_thread.Start();
+ m_cancellationTokenSource = new CancellationTokenSource();
+ m_task = Task.Run(() => this.ThreadMain(m_cancellationTokenSource.Token), m_cancellationTokenSource.Token);
}
public void SetReady() { m_readyEvent.Set(); }
@@ -54,13 +59,13 @@ namespace Duplicati.CommandLine
public bool Join(TimeSpan wait)
{
- if (m_thread != null)
- return m_thread.Join(wait);
+ if (m_task != null)
+ return this.m_task.Wait(wait);
return true;
}
- private void ThreadMain()
+ private void ThreadMain(CancellationToken cancellationToken)
{
m_readyEvent.WaitOne();
if (m_finishEvent.WaitOne(TimeSpan.FromMilliseconds(10), true))
@@ -69,7 +74,7 @@ namespace Duplicati.CommandLine
var last_count = -1L;
var finished = false;
- while (true)
+ while (!cancellationToken.IsCancellationRequested)
{
float progress;
long filesprocessed;
@@ -106,22 +111,22 @@ namespace Duplicati.CommandLine
public void Dispose()
{
- if (m_thread != null)
+ if (m_task != null)
{
try
{
m_finishEvent.Set();
m_readyEvent.Set();
- if (m_thread != null && m_thread.IsAlive)
+ if (m_task != null)
{
- m_thread.Abort();
- m_thread.Join(500);
+ m_cancellationTokenSource.Cancel();
+ m_task.Wait(500);
}
}
finally
{
- m_thread = null;
+ m_task = null;
}
}
}
@@ -129,13 +134,15 @@ namespace Duplicati.CommandLine
public static int Examples(TextWriter outwriter, Action setup, List args, Dictionary options, Library.Utility.IFilter filter)
{
- Duplicati.CommandLine.Help.PrintUsage(outwriter, "example", options);
+ using (new Duplicati.Library.Main.LocaleChange(options))
+ Duplicati.CommandLine.Help.PrintUsage(outwriter, "example", options);
return 0;
}
public static int Help(TextWriter outwriter, Action setup, List args, Dictionary options, Library.Utility.IFilter filter)
{
- Duplicati.CommandLine.Help.PrintUsage(outwriter, args.Count > 1? args[1] : "help", options);
+ using (new Duplicati.Library.Main.LocaleChange(options))
+ Duplicati.CommandLine.Help.PrintUsage(outwriter, args.Count > 1 ? args[1] : "help", options);
return 0;
}
@@ -170,8 +177,8 @@ namespace Duplicati.CommandLine
options["no-encryption"] = "true";
}
- using(var console = new ConsoleOutput(outwriter, options))
- using(var i = new Library.Main.Controller(backend, options, console))
+ using (var console = new ConsoleOutput(outwriter, options))
+ using (var i = new Library.Main.Controller(backend, options, console))
{
setup(i);
i.ListAffected(args, res =>
@@ -254,7 +261,7 @@ namespace Duplicati.CommandLine
if (!containsSeparators && arg.StartsWith("@", StringComparison.Ordinal))
{
// Convert to Regexp filter and prefix with ".*/"
- return $"[.*{Utility.ConvertLiteralToRegExp(Util.DirectorySeparatorString + arg.Substring(1))}]";
+ return $"[.*{Library.Utility.Utility.ConvertLiteralToRegExp(Util.DirectorySeparatorString + arg.Substring(1))}]";
}
else if (!containsSeparators && !containsWildcards && !arg.StartsWith("[", StringComparison.Ordinal))
{
@@ -285,7 +292,7 @@ namespace Duplicati.CommandLine
if (endsWithSeparator && arg.StartsWith("@", StringComparison.Ordinal))
{
// Convert to Regexp filter and suffix with ".*"
- return $"[{Utility.ConvertLiteralToRegExp(arg.Substring(1))}.*]";
+ return $"[{Library.Utility.Utility.ConvertLiteralToRegExp(arg.Substring(1))}.*]";
}
else if (endsWithSeparator && !containsWildcards && !arg.StartsWith("[", StringComparison.Ordinal))
{
@@ -319,7 +326,7 @@ namespace Duplicati.CommandLine
filter = new Duplicati.Library.Utility.FilterExpression();
using (var console = new ConsoleOutput(outwriter, options))
- using(var i = new Library.Main.Controller(args[0], options, console))
+ using (var i = new Library.Main.Controller(args[0], options, console))
{
setup(i);
var backend = args[0];
@@ -407,7 +414,7 @@ namespace Duplicati.CommandLine
{
outwriter.WriteLine("Listing filesets:");
- foreach(var e in res.Filesets)
+ foreach (var e in res.Filesets)
{
if (e.FileCount >= 0)
outwriter.WriteLine("{0}\t: {1} ({2} files, {3})", e.Version, e.Time, e.FileCount, Library.Utility.Utility.FormatSizeString(e.FileSizes));
@@ -439,10 +446,10 @@ namespace Duplicati.CommandLine
else
{
outwriter.WriteLine("Listing files and versions:");
- foreach(var e in res.Files)
+ foreach (var e in res.Files)
{
outwriter.WriteLine(e.Path);
- foreach(var nx in res.Filesets.Zip(e.Sizes, (a, b) => new { Index = a.Version, Time = a.Time, Size = b } ))
+ foreach (var nx in res.Filesets.Zip(e.Sizes, (a, b) => new { Index = a.Version, Time = a.Time, Size = b }))
outwriter.WriteLine("{0}\t: {1} {2}", nx.Index, nx.Time, nx.Size < 0 ? " - " : Library.Utility.Utility.FormatSizeString(nx.Size));
outwriter.WriteLine();
@@ -466,7 +473,7 @@ namespace Duplicati.CommandLine
}
using (var console = new ConsoleOutput(outwriter, options))
- using(var i = new Library.Main.Controller(args[0], options, console))
+ using (var i = new Library.Main.Controller(args[0], options, console))
{
setup(i);
args.RemoveAt(0);
@@ -483,7 +490,7 @@ namespace Duplicati.CommandLine
else
outwriter.WriteLine(Strings.Program.DeletedBackups);
- foreach(var f in res.DeletedSets)
+ foreach (var f in res.DeletedSets)
outwriter.WriteLine("{0}: {1}", f.Item1, f.Item2);
}
}
@@ -520,7 +527,7 @@ namespace Duplicati.CommandLine
args = SuffixArgsWithAsterisk(PrefixArgsWithAsterisk(args)).ToList();
- using(var output = new ConsoleOutput(outwriter, options))
+ using (var output = new ConsoleOutput(outwriter, options))
using (var i = new Library.Main.Controller(backend, options, output))
{
output.MessageEvent(string.Format("Restore started at {0}", DateTime.Now));
@@ -575,15 +582,6 @@ namespace Duplicati.CommandLine
output.MessageEvent(string.Format("Restored {0} ({1}) files to {2}", res.RestoredFiles, Library.Utility.Utility.FormatSizeString(res.SizeOfRestoredFiles), string.IsNullOrEmpty(restorePath) ? "original path" : restorePath));
output.MessageEvent(string.Format("Duration of restore: {0:hh\\:mm\\:ss}", res.Duration));
- if (res.RestoredFiles > 0 && !Library.Main.Utility.SuppressDonationMessages)
- {
- output.MessageEvent("***********************************************");
- output.MessageEvent("Did we help save your files? If so, please support Duplicati with a donation. We suggest 10€ for private use and 100€ for commercial use.");
- output.MessageEvent("");
- output.MessageEvent("https://www.duplicati.com/donate/");
- output.MessageEvent("***********************************************");
- }
-
if (output.FullResults)
Library.Utility.Utility.PrintSerializeObject(res, outwriter);
@@ -664,7 +662,7 @@ namespace Duplicati.CommandLine
}
var parsedStats = result.BackendStatistics as Duplicati.Library.Interface.IParsedBackendStatistics;
- output.MessageEvent(string.Format(" Duration of backup: {0:hh\\:mm\\:ss}", result.Duration));
+ output.MessageEvent(string.Format(" Duration of backup: {0:c}", result.Duration));
if (parsedStats != null)
{
if (parsedStats.KnownFileCount > 0)
@@ -721,7 +719,7 @@ namespace Duplicati.CommandLine
if (args.Count != 1)
return PrintWrongNumberOfArguments(outwriter, args, 1);
- using(var console = new ConsoleOutput(outwriter, options))
+ using (var console = new ConsoleOutput(outwriter, options))
using (var i = new Library.Main.Controller(args[0], options, console))
{
setup(i);
@@ -770,7 +768,7 @@ namespace Duplicati.CommandLine
}
else
{
- foreach(var n in result.Verifications)
+ foreach (var n in result.Verifications)
{
var changecount = n.Value.Count();
if (changecount == 0)
@@ -1005,7 +1003,7 @@ namespace Duplicati.CommandLine
}
using (var console = new ConsoleOutput(outwriter, options))
- using(var i = new Library.Main.Controller("dummy://", options, console))
+ using (var i = new Library.Main.Controller("dummy://", options, console))
{
setup(i);
var result = i.TestFilter(args.ToArray(), filter);
diff --git a/Duplicati/CommandLine/ConsoleOutput.cs b/Duplicati/CommandLine/CLI/ConsoleOutput.cs
similarity index 67%
rename from Duplicati/CommandLine/ConsoleOutput.cs
rename to Duplicati/CommandLine/CLI/ConsoleOutput.cs
index efb1d9988..cd08a49da 100644
--- a/Duplicati/CommandLine/ConsoleOutput.cs
+++ b/Duplicati/CommandLine/CLI/ConsoleOutput.cs
@@ -1,20 +1,24 @@
-// Copyright (C) 2015, The Duplicati Team
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.IO;
@@ -59,19 +63,19 @@ namespace Duplicati.CommandLine
switch (action)
{
case BackendActionType.Put:
- Output.WriteLine(" Uploading file ({0}) ...", Library.Utility.Utility.FormatSizeString(size));
+ Output.WriteLine(" Uploading file {0} ({1}) ...", path, Library.Utility.Utility.FormatSizeString(size));
break;
case BackendActionType.Get:
- Output.WriteLine(" Downloading file ({0}) ...", size < 0 ? "unknown" : Library.Utility.Utility.FormatSizeString(size));
+ Output.WriteLine(" Downloading file {0} ({1}) ...", path, size < 0 ? "unknown" : Library.Utility.Utility.FormatSizeString(size));
break;
case BackendActionType.List:
- Output.WriteLine(" Listing remote folder ...");
+ Output.WriteLine(" Listing remote folder {0}...", path);
break;
case BackendActionType.CreateFolder:
- Output.WriteLine(" Creating remote folder ...");
+ Output.WriteLine(" Creating remote folder {0} ...", path);
break;
case BackendActionType.Delete:
- Output.WriteLine(" Deleting file {0}{1} ...", path, size < 0 ? "" : (" (" + Library.Utility.Utility.FormatSizeString(size) + ")"));
+ Output.WriteLine(" Deleting file {0} {1} ...", path, size < 0 ? "" : (" (" + Library.Utility.Utility.FormatSizeString(size) + ")"));
break;
}
}
@@ -79,7 +83,7 @@ namespace Duplicati.CommandLine
public void SetBackendProgress(IBackendProgress progress)
{
- // Do nothing. Implementation needed for IMessageSink interface.
+ // Do nothing. Implementation needed for IMessageSink interface.
}
public void SetOperationProgress(IOperationProgress progress)
diff --git a/Duplicati/CommandLine/CLI/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/CLI/Duplicati.CommandLine.csproj
new file mode 100644
index 000000000..250eed3c9
--- /dev/null
+++ b/Duplicati/CommandLine/CLI/Duplicati.CommandLine.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net8.0
+ A commandline version of Duplicati
+ Duplicati.CommandLine.Implementation
+ TrayWarning.ico
+ Copyright © 2024 Team Duplicati, MIT license
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/Duplicati/CommandLine/Help.cs b/Duplicati/CommandLine/CLI/Help.cs
similarity index 81%
rename from Duplicati/CommandLine/Help.cs
rename to Duplicati/CommandLine/CLI/Help.cs
index 57eaa10cf..9c5a6bc17 100644
--- a/Duplicati/CommandLine/Help.cs
+++ b/Duplicati/CommandLine/CLI/Help.cs
@@ -1,9 +1,32 @@
-using System;
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
-using Duplicati.Library.Common;
+using Duplicati.Library.AutoUpdater;
+using Duplicati.Library.DynamicLoader;
using FilterGroup = Duplicati.Library.Utility.FilterGroup;
namespace Duplicati.CommandLine
@@ -11,18 +34,17 @@ namespace Duplicati.CommandLine
public static class Help
{
private static readonly Dictionary _document;
- private const string RESOURCE_NAME = "Duplicati.CommandLine.help.txt";
+ private const string RESOURCE_NAME = "help.txt";
private static readonly System.Text.RegularExpressions.Regex NAMEDOPTION_REGEX = new System.Text.RegularExpressions.Regex("\\%OPTION\\:(?[^\\%]+)\\%");
static Help()
{
- _document = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- using (System.IO.StreamReader sr = new System.IO.StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(RESOURCE_NAME)))
+ _document = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ using (var sr = new StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(typeof(Help), RESOURCE_NAME)))
{
List keywords = new List();
StringBuilder sb = new StringBuilder();
- foreach(var line in sr.ReadToEnd().Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.None))
+ foreach (var line in sr.ReadToEnd().Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.None))
{
if (line.Trim().StartsWith("#", StringComparison.Ordinal))
continue;
@@ -32,14 +54,14 @@ namespace Duplicati.CommandLine
if (sb.Length > 0)
{
string s = sb.ToString();
- foreach(var k in keywords)
+ foreach (var k in keywords)
_document[k] = s;
keywords.Clear();
sb.Clear();
}
- string[] elems = line.Split(new string[] {" ", "\t"}, StringSplitOptions.RemoveEmptyEntries);
+ string[] elems = line.Split(new string[] { " ", "\t" }, StringSplitOptions.RemoveEmptyEntries);
if (elems.Length >= 2 && string.Equals(elems[elems.Length - 2], "help", StringComparison.OrdinalIgnoreCase))
keywords.Add(elems[elems.Length - 1]);
else if (elems.Length == 3 && string.Equals(elems[elems.Length - 1], "help", StringComparison.OrdinalIgnoreCase))
@@ -54,7 +76,7 @@ namespace Duplicati.CommandLine
if (sb.Length > 0)
{
string s = sb.ToString();
- foreach(var k in keywords)
+ foreach (var k in keywords)
_document[k] = s;
}
}
@@ -67,8 +89,8 @@ namespace Duplicati.CommandLine
//Force translation off if this is from the commandline
if (Program.FROM_COMMANDLINE)
{
- System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
- System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture;
+ CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
+ CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
}
}
catch
@@ -88,20 +110,19 @@ namespace Duplicati.CommandLine
topic = "include";
}
-
if (_document.ContainsKey(topic))
{
string tp = _document[topic];
Library.Main.Options opts = new Library.Main.Options(new Dictionary());
+ tp = tp.Replace("%CLI_EXE%", PackageHelper.GetExecutableName(PackageHelper.NamedExecutable.CommandLine));
tp = tp.Replace("%VERSION%", License.VersionNumbers.Version);
tp = tp.Replace("%BACKENDS%", string.Join(", ", Library.DynamicLoader.BackendLoader.Keys));
- tp = tp.Replace("%MONO%", Library.Utility.Utility.IsMono ? "mono " : "");
- tp = tp.Replace("%APP_PATH%", System.IO.Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location));
+ tp = tp.Replace("%APP_PATH%", Path.Combine(UpdaterManager.INSTALLATIONDIR, PackageHelper.GetExecutableName(PackageHelper.NamedExecutable.CommandLine)));
tp = tp.Replace("%PATH_SEPARATOR%", System.IO.Path.PathSeparator.ToString());
- tp = tp.Replace("%EXAMPLE_SOURCE_PATH%", Platform.IsClientPosix ? "/source" : @"D:\source");
- tp = tp.Replace("%EXAMPLE_SOURCE_FILE%", Platform.IsClientPosix ? "/source/myfile.txt" : @"D:\source\file.txt");
- tp = tp.Replace("%EXAMPLE_RESTORE_PATH%", Platform.IsClientPosix ? "/restore" : @"D:\restore");
+ tp = tp.Replace("%EXAMPLE_SOURCE_PATH%", !OperatingSystem.IsWindows() ? "/source" : @"D:\source");
+ tp = tp.Replace("%EXAMPLE_SOURCE_FILE%", !OperatingSystem.IsWindows() ? "/source/myfile.txt" : @"D:\source\file.txt");
+ tp = tp.Replace("%EXAMPLE_RESTORE_PATH%", !OperatingSystem.IsWindows() ? "/restore" : @"D:\restore");
tp = tp.Replace("%ENCRYPTIONMODULES%", string.Join(", ", Library.DynamicLoader.EncryptionLoader.Keys));
tp = tp.Replace("%COMPRESSIONMODULES%", string.Join(", ", Library.DynamicLoader.CompressionLoader.Keys));
tp = tp.Replace("%DEFAULTENCRYPTIONMODULE%", opts.EncryptionModule);
@@ -111,7 +132,7 @@ namespace Duplicati.CommandLine
tp = tp.Replace("%FILTER_GROUPS_SHORT%", string.Join(Environment.NewLine + " ", metaGroupNames.Concat(Enum.GetNames(typeof(FilterGroup)).Except(metaGroupNames, StringComparer.OrdinalIgnoreCase).OrderBy(x => x, StringComparer.OrdinalIgnoreCase)).Select(group => "{" + group + "}")));
tp = tp.Replace("%FILTER_GROUPS_LONG%", Library.Utility.FilterGroups.GetOptionDescriptions(4, true));
- if (Platform.IsClientWindows)
+ if (OperatingSystem.IsWindows())
{
// These properties are only valid for Windows
tp = tp.Replace("%EXAMPLE_WILDCARD_DRIVE_SOURCE_PATH%", @"*:\source");
@@ -184,7 +205,7 @@ namespace Duplicati.CommandLine
tp = tp.Replace("%ALLOPTIONS%", string.Join(Environment.NewLine, lines.ToArray()));
}
-
+
if (tp.Contains("%MODULEOPTIONS%"))
{
//Figure out which module we are in
@@ -239,8 +260,20 @@ namespace Duplicati.CommandLine
}
}
+ if (tp.Contains("%SECRETPROVIDERS%"))
+ {
+ var lines = new List();
+ foreach (var module in SecretProviderLoader.Keys)
+ {
+ var metadata = SecretProviderLoader.GetProviderMetadata(module);
+ lines.Add($"- {module}: {metadata.DisplayName}");
+ }
+
+ tp = tp.Replace("%SECRETPROVIDERS%", string.Join(Environment.NewLine, lines.ToArray()));
+ }
+
if (NAMEDOPTION_REGEX.IsMatch(tp))
- tp = NAMEDOPTION_REGEX.Replace(tp, new Matcher().MathEvaluator);
+ tp = NAMEDOPTION_REGEX.Replace(tp, new Matcher().MathEvaluator);
PrintFormatted(outwriter, tp.Split(new string[] { Environment.NewLine }, StringSplitOptions.None));
}
@@ -279,6 +312,14 @@ namespace Duplicati.CommandLine
break;
}
+ if (lines.Count == 0)
+ foreach (Duplicati.Library.Interface.ISecretProvider mod in SecretProviderLoader.Modules)
+ if (string.Equals(mod.Key, topic, StringComparison.OrdinalIgnoreCase))
+ {
+ PrintSecretProvider(mod, lines);
+ break;
+ }
+
if (lines.Count == 0)
PrintArgumentIfFound(new Matcher().Values, topic, lines);
@@ -374,18 +415,30 @@ namespace Duplicati.CommandLine
lines.Add("");
}
+ private static void PrintSecretProvider(Duplicati.Library.Interface.ISecretProvider mod, List lines)
+ {
+ lines.Add($"{mod.Key}: {mod.Description}");
+ if (mod.SupportedCommands != null && mod.SupportedCommands.Count > 0)
+ {
+ lines.Add(" " + Strings.Program.SupportedOptionsHeader);
+ foreach (Library.Interface.ICommandLineArgument arg in mod.SupportedCommands)
+ Library.Interface.CommandLineArgument.PrintArgument(lines, arg, " ");
+ }
+ lines.Add("");
+ }
+
private static void PrintFormatted(TextWriter outwriter, IEnumerable lines)
{
int windowWidth = 80;
-
- try
+
+ try
{
// This can go wrong if we have no attached console
if (outwriter == Console.Out)
- windowWidth = Math.Max(12, Console.WindowWidth == 0 ? 80 : Console.WindowWidth);
+ windowWidth = Math.Max(12, Console.WindowWidth == 0 ? 80 : Console.WindowWidth);
}
catch { }
-
+
foreach (string s in lines)
{
if (string.IsNullOrEmpty(s) || s.Trim().Length == 0)
@@ -411,11 +464,15 @@ namespace Duplicati.CommandLine
len -= leadingSpaces.Length;
if (len < c.Length)
{
- int ix = c.LastIndexOf(" ", len, StringComparison.Ordinal);
+ int ix = c.LastIndexOf(' ', len);
if (ix > 0)
len = ix;
}
+ var lfix = c.IndexOf('\n');
+ if (lfix >= 0 && lfix < len)
+ len = lfix + 1;
+
outwriter.WriteLine(leadingSpaces + c.Substring(0, len).Trim());
c = c.Remove(0, len);
if (extraIndent)
@@ -451,7 +508,7 @@ namespace Duplicati.CommandLine
foreach (Duplicati.Library.Interface.IGenericModule mod in Library.DynamicLoader.GenericLoader.Modules)
if (mod.SupportedCommands != null)
foundArgs.Add(mod.SupportedCommands);
-
+
foreach (IEnumerable arglst in foundArgs)
if (arglst != null)
{
@@ -489,7 +546,7 @@ namespace Duplicati.CommandLine
return "";
List lines = new List();
- foreach(Duplicati.Library.Interface.ICommandLineArgument arg in args)
+ foreach (Duplicati.Library.Interface.ICommandLineArgument arg in args)
if (!arg.Deprecated)
lines.Add(PrintArgSimple(arg, arg.Name));
diff --git a/Duplicati/CommandLine/Program.cs b/Duplicati/CommandLine/CLI/Program.cs
similarity index 82%
rename from Duplicati/CommandLine/Program.cs
rename to Duplicati/CommandLine/CLI/Program.cs
index 512d091c0..ea6be1fb9 100644
--- a/Duplicati/CommandLine/Program.cs
+++ b/Duplicati/CommandLine/CLI/Program.cs
@@ -1,29 +1,32 @@
-#region Disclaimer / License
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Lesser General Public
-// License as published by the Free Software Foundation; either
-// version 2.1 of the License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-//
-#endregion
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
using System;
using System.Collections.Generic;
using System.Linq;
using Duplicati.Library.Localization.Short;
using System.IO;
using Duplicati.Library.Interface;
-using Duplicati.Library.Logging;
+using Duplicati.Library.AutoUpdater;
+using Duplicati.Library.Crashlog;
namespace Duplicati.CommandLine
{
@@ -36,27 +39,27 @@ namespace Duplicati.CommandLine
///
/// The main entry point for the application.
///
- [STAThread]
public static int Main(string[] args)
{
- Duplicati.Library.AutoUpdater.UpdaterManager.IgnoreWebrootFolder = true;
- return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args);
- }
+ PreloadSettingsLoader.ConfigurePreloadSettings(ref args, PackageHelper.NamedExecutable.CommandLine);
- public static int RealMain(string[] args)
- {
Library.UsageReporter.Reporter.Initialize();
FROM_COMMANDLINE = true;
try
{
+ CrashlogHelper.OnUnobservedTaskException += LogUnobservedTaskExceptions;
return RunCommandLine(Console.Out, Console.Error, c => { }, args);
}
finally
{
Library.UsageReporter.Reporter.ShutDown();
+ CrashlogHelper.OnUnobservedTaskException -= LogUnobservedTaskExceptions;
}
}
+ private static void LogUnobservedTaskExceptions(Exception ex) => Console.Error.WriteLine("UnobservedTaskException: {0}", ex);
+
+
private static Dictionary, List, Dictionary, Library.Utility.IFilter, int>> CommandMap
{
get
@@ -90,7 +93,7 @@ namespace Duplicati.CommandLine
["systeminfo"] = Commands.SystemInfo,
["send-mail"] = Commands.SendMail
};
-
+
return knownCommands;
}
}
@@ -99,7 +102,7 @@ namespace Duplicati.CommandLine
private static int ShowChangeLog(TextWriter outwriter)
{
- var path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "changelog.txt");
+ var path = System.IO.Path.Combine(UpdaterManager.INSTALLATIONDIR, "changelog.txt");
outwriter.WriteLine(System.IO.File.ReadAllText(path));
return 0;
}
@@ -112,18 +115,28 @@ namespace Duplicati.CommandLine
if (update != null && update.Version != Library.AutoUpdater.UpdaterManager.SelfVersion.Version)
{
- outwriter.WriteLine("Found update \"{0}\", downloading ...", update.Displayname);
- long lastpg = 0;
- Library.AutoUpdater.UpdaterManager.DownloadAndUnpackUpdate(update, f =>
+ var package = update.FindPackage();
+ if (package == null)
{
- var npg = (long)(f * 100);
- if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100))
+ outwriter.WriteLine($"Failed to locate a matching package for this machine, please visit this link and select the correct package: {update.GetGenericUpdatePageUrl()}");
+ }
+ else
+ {
+ var filename = Path.GetFullPath(package.GetFilename());
+ outwriter.WriteLine("Downloading update \"{0}\" to {1} ...", update.Displayname, filename);
+
+ long lastpg = 0;
+ Library.AutoUpdater.UpdaterManager.DownloadUpdate(update, package, filename, f =>
{
- lastpg = npg;
- outwriter.WriteLine("Downloading {0}% ...", npg);
- }
- });
- outwriter.WriteLine("Update \"{0}\" ({1}) installed, using on next launch", update.Displayname, update.Version);
+ var npg = (long)(f * 100);
+ if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100))
+ {
+ lastpg = npg;
+ outwriter.WriteLine("Downloading {0}% ...", npg);
+ }
+ });
+ outwriter.WriteLine("Update \"{0}\" ({1}) downloaded", update.Displayname, update.Version);
+ }
}
}
diff --git a/Duplicati/CommandLine/Strings.cs b/Duplicati/CommandLine/CLI/Strings.cs
similarity index 59%
rename from Duplicati/CommandLine/Strings.cs
rename to Duplicati/CommandLine/CLI/Strings.cs
index 5f47b9272..3e65ab22e 100644
--- a/Duplicati/CommandLine/Strings.cs
+++ b/Duplicati/CommandLine/CLI/Strings.cs
@@ -1,38 +1,60 @@
+// Copyright (C) 2024, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
using Duplicati.Library.Localization.Short;
-namespace Duplicati.CommandLine.Strings {
- internal static class Program {
+namespace Duplicati.CommandLine.Strings
+{
+ internal static class Program
+ {
public static string DeleteCommandNeedsOptions(string commandname, string[] options) { return LC.L(@"The command {0} needs at least one of the following options set: {1}", commandname, string.Join(", ", options)); }
public static string WrongNumberOfCommandsError_v2(int actualcommands, int expectedcommands, string[] commands) { return LC.L(@"Found {0} commands but expected {1}, commands:
{2}", actualcommands, expectedcommands, string.Join(System.Environment.NewLine, commands ?? new string[0])); }
public static string InvalidCommandError(string commandname) { return LC.L(@"Command not supported: {0}", commandname); }
- public static string NoFilesetsMatching { get { return LC.L(@"No filesets matched the criteria"); } }
+ public static string NoFilesetsMatching { get { return LC.L(@"No filesets matched the criteria."); } }
public static string WouldDeleteBackups { get { return LC.L(@"The following filesets would be deleted:"); } }
public static string DeletedBackups { get { return LC.L(@"These filesets were deleted:"); } }
public static string SupportedBackendsHeader { get { return LC.L(@"Supported backends:"); } }
public static string SupportedCompressionModulesHeader { get { return LC.L(@"Supported compression modules:"); } }
public static string SupportedEncryptionModulesHeader { get { return LC.L(@"Supported encryption modules:"); } }
public static string SupportedOptionsHeader { get { return LC.L(@"Supported options:"); } }
- public static string ModuleIsLoadedAutomatically { get { return LC.L(@"Module is loaded automatically, use --disable-module to prevent this"); } }
- public static string ModuleIsNotLoadedAutomatically { get { return LC.L(@"Module is not loaded automatically, use --enable-module to load it"); } }
+ public static string ModuleIsLoadedAutomatically { get { return LC.L(@"Module is loaded automatically. Use --{0} to prevent this.", "disable-module"); } }
+ public static string ModuleIsNotLoadedAutomatically { get { return LC.L(@"Module is not loaded automatically Use --{0} to load it.", "enable-module"); } }
public static string GenericModulesHeader { get { return LC.L(@"Supported generic modules:"); } }
public static string FailedToParseParametersFileError(string path, string message) { return LC.L(@"Unable to read the parameters file ""{0}"", reason: {1}", path, message); }
- public static string FiltersCannotBeUsedWithFileError2 { get { return LC.L(@"Filters cannot be specified on the commandline if filters are also present in the parameter file. Use the special --{0}, --{1}, or --{2} options to specify filters inside the parameter file. Each filter must be prefixed with either a + or a -, and multiple filters must be joined with {3}", "replace-filter", "append-filter", "prepend-filter", System.IO.Path.PathSeparator); } }
- public static string InternalOptionUsedError(string optionname) { return LC.L(@"The option --{0} was supplied, but it is reserved for internal use and may not be set on the commandline", optionname); }
- public static string ParametersFileOptionLong2 { get { return LC.L(@"This option can be used to store some or all of the options given to the commandline client. The file must be a plain text file, UTF-8 encoding is preferred. Each line in the file should be of the format --option=value. The special options --{0} and --{1} can be used to override the localpath and the remote destination uri, respectively. The options in this file take precedence over the options provided on the commandline. You cannot specify filters in both the file and on the commandline. Instead, you can use the special --{2}, --{3}, or --{4} options to specify filters inside the parameter file. Each filter must be prefixed with either a + or a -, and multiple filters must be joined with {5} ", "source", "target", "replace-filter", "append-filter", "prepend-filter", System.IO.Path.PathSeparator); } }
+ public static string FiltersCannotBeUsedWithFileError2 { get { return LC.L(@"Filters cannot be specified on the commandline if filters are also present in the parameter file. Use the special --{0}, --{1}, or --{2} options to specify filters inside the parameter file. Each filter must be prefixed with either a + or a -, and multiple filters must be joined with {3}.", "replace-filter", "append-filter", "prepend-filter", System.IO.Path.PathSeparator); } }
+ public static string InternalOptionUsedError(string optionname) { return LC.L(@"The option --{0} was supplied, but it is reserved for internal use and may not be set on the commandline.", optionname); }
+ public static string ParametersFileOptionLong2 { get { return LC.L(@"Use this option to store some or all of the options given to the commandline client. The file must be a plain text file, and UTF-8 encoding is preferred. Each line in the file should be of the format --option=value. Use the special options --{0} and --{1} to override the localpath and the remote destination uri, respectively. The options in this file take precedence over the options provided on the commandline. You cannot specify filters in both the file and on the commandline. Instead, you can use the special --{2}, --{3}, or --{4} options to specify filters inside the parameter file. Each filter must be prefixed with either a + or a -, and multiple filters must be joined with {5}.", "source", "target", "replace-filter", "append-filter", "prepend-filter", System.IO.Path.PathSeparator); } }
public static string ParametersFileOptionShort { get { return LC.L(@"Path to a file with parameters"); } }
public static string UnhandledException(string message) { return LC.L(@"An error occured: {0}", message); }
public static string UnhandledInnerException(string message) { return LC.L(@"The inner error message is: {0}", message); }
- public static string IncludeLong { get { return LC.L(@"Include files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to include all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {{Applications}}."); } }
+ public static string IncludeLong { get { return LC.L(@"Include files that match this filter. The special character * means any number of character, and the special character ? means any single character. Use *.txt to include all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, e.g. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, e.g. {{Applications}}."); } }
public static string IncludeShort { get { return LC.L(@"Include files"); } }
- public static string ExcludeLong { get { return LC.L(@"Exclude files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to exclude all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {{TemporaryFiles}}."); } }
+ public static string ExcludeLong { get { return LC.L(@"Exclude files that match this filter. The special character * means any number of character, and the special character ? means any single character. Use *.txt to exclude all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, e.g. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, e.g. {{TemporaryFiles}}."); } }
public static string ExcludeShort { get { return LC.L(@"Exclude files"); } }
- public static string ControlFilesOptionShort { get { return LC.L(@"If this option is used with a backup operation, it is interpreted as a list of files to add to the filesets. When used with list or restore, it will list or restore the control files instead of the normal files."); } }
- public static string ControlFilesOptionLong { get { return LC.L(@"Use control files"); } }
+ public static string ControlFilesOptionLong { get { return LC.L(@"If this option is used with a backup operation, it is interpreted as a list of files to add to the filesets. When used with list or restore, it will list or restore the control files instead of the normal files."); } }
+ public static string ControlFilesOptionShort { get { return LC.L(@"Use control files"); } }
public static string QuietConsoleOptionLong { get { return LC.L(@"If this option is set, progress reports and other messages that would normally go to the console will be redirected to the log."); } }
public static string QuietConsoleOptionShort { get { return LC.L(@"Disable console output"); } }
- public static string SkippingSourceArgumentsOnNonBackupOperation { get { return @"The --source argument was specified in the parameter file, but the current operation is not a backup operation, so the argument is ignored"; } }
-
-
+ public static string SkippingSourceArgumentsOnNonBackupOperation { get { return @"The --source argument was specified in the parameter file, but the current operation is not a backup operation, so the argument is ignored."; } }
+
// ReSharper disable once UnusedMember.Global
// This is a placeholder message that is intended to be used with the code
// for each error and log message. The idea is that the commandline will
diff --git a/Duplicati/CommandLine/TrayWarning.ico b/Duplicati/CommandLine/CLI/TrayWarning.ico
similarity index 100%
rename from Duplicati/CommandLine/TrayWarning.ico
rename to Duplicati/CommandLine/CLI/TrayWarning.ico
diff --git a/Duplicati/CommandLine/help.txt b/Duplicati/CommandLine/CLI/help.txt
similarity index 84%
rename from Duplicati/CommandLine/help.txt
rename to Duplicati/CommandLine/CLI/help.txt
index c4a42da76..78057c774 100644
--- a/Duplicati/CommandLine/help.txt
+++ b/Duplicati/CommandLine/CLI/help.txt
@@ -9,17 +9,18 @@
> duplicati.commandline.exe
> duplicati.commandline.exe help
-See duplicati.commandline.exe help for more information.
+See %CLI_EXE% help for more information.
General: example, changelog
Commands: backup, find, restore, delete, compact, test, compare, purge, vacuum
Repair: repair, affected, list-broken-files, purge-broken-files
Debug: debug, logging, create-report, test-filters, system-info, send-mail
Targets: %BACKENDS%
Modules: %ENCRYPTIONMODULES%, %COMPRESSIONMODULES%, %GENERICMODULES%
- Formats: date, time, size, encryption, compression
+ Formats: date, time, size, decimal, encryption, compression
Advanced: mail, advanced, returncodes, filter, filter-groups,