Merge remote-tracking branch 'origin/master' into feature/10-ikvmnet-cli

This commit is contained in:
AliveDevil
2026-01-27 15:07:52 +01:00
1770 changed files with 23042 additions and 20173 deletions
+9
View File
@@ -22,6 +22,12 @@ indent_size = 2
[*.yml]
indent_size = 2
[*.{cs,vb}]
dotnet_sort_system_directives_first = true
[*.{cs,vb}]
dotnet_diagnostic.RS0030.severity = error
[*.java]
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
@@ -287,3 +293,6 @@ ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false
ij_java_wrap_semicolon_after_call_chain = false
ij_java_continuation_indent_size = 8
[*/Microsoft.Windows.CsWin32.SourceGenerator/*.cs]
generated_code = true
+6 -4
View File
@@ -9,6 +9,8 @@ jobs:
build:
permissions:
checks: write
pull-requests: write
statuses: write
contents: read
packages: read
runs-on: ${{ matrix.os }}
@@ -17,9 +19,9 @@ jobs:
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
@@ -44,5 +46,5 @@ jobs:
- name: Build with Maven
run: mvn --no-transfer-progress verify -DskipITs -DskipSign ${{ env.args }} --batch-mode -Drevision=0
- name: Publish Test Report
if: ${{ always() }}
uses: scacap/action-surefire-report@v1.9.0
if: github.event.pull_request.head.repo.fork == false
uses: scacap/action-surefire-report@v1.9.1
+3 -3
View File
@@ -14,9 +14,9 @@ jobs:
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up JDK 21
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
@@ -27,4 +27,4 @@ jobs:
run: mvn verify -pl protocols -am --no-transfer-progress --batch-mode --fail-at-end -Denforcer.skip
- name: Publish Test Report
if: ${{ always() }}
uses: scacap/action-surefire-report@v1.9.0
uses: scacap/action-surefire-report@v1.9.1
@@ -1,4 +1,4 @@
name: Publish Snapshot Artifacts
name: Publish Artifacts
on:
workflow_dispatch:
push:
@@ -16,11 +16,11 @@ jobs:
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
+2 -2
View File
@@ -47,13 +47,13 @@ jobs:
type=semver,pattern={{major}}.{{minor}},value=${{env.VERSION}}
type=semver,pattern={{major}},value=${{env.VERSION}}
- name: Set up JDK 21
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
cache: maven
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ env.TAG }}
fetch-depth: 0
+2 -2
View File
@@ -16,7 +16,7 @@ jobs:
working-directory: ./i18n/src/main/resources
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
@@ -29,7 +29,7 @@ jobs:
TX_HOSTNAME: ${{ secrets.TRANSIFEX_HOSTNAME }}
TX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
if: success()
with:
branch: i18n/
+44
View File
@@ -1,5 +1,49 @@
# Changelog
[9.3.1](https://github.com/iterate-ch/cyberduck/compare/release-9-3-0...release-9-3-1)
* [Bugfix] Validate tokens when using AWS S3 CLI connection profile (#17690)
[9.3.0](https://github.com/iterate-ch/cyberduck/compare/release-9-2-4...release-9-3-0)
* [Feature] Rewrite protocol implementation (Azure) ([#15967](https://trac.cyberduck.io/ticket/15967))
* [Feature] Connect with Multi-Bucket Application Keys that grant access to a specific group of buckets within an
account, including the option to limit access based on a single file prefix (
B2) ([#17139](https://trac.cyberduck.io/ticket/17139))
* [Feature] Connect with connection profile obtaining temporary credentials from AWS Security Token Service (STS) by
assuming role with optional Multi-Factor Authentication (MFA) input (
S3) ([#17437](https://trac.cyberduck.io/ticket/17437))
* [Feature] Connect with connection profile obtaining temporary credentials from AWS Security Token Service (STS) by
getting session token with optional Multi-Factor Authentication (MFA) input (
S3) ([#17506](https://trac.cyberduck.io/ticket/17506))
* [Bugfix] Fix race conditions in FTP socket closure that cause intermittent errors (FTP)
* [Bugfix] Default to read identity agent location from SSH_AUTH_SOCK environment variable with no custom
configuration (SFTP)
[9.2.4](https://github.com/iterate-ch/cyberduck/compare/release-9-2-3...release-9-2-4)
* [Bugfix] Unable to close connection window with "Cancel" (macOS) ([#17366](https://trac.cyberduck.io/ticket/17366))
* [Bugfix] Choose "Cancel" in upload prompt continues transfer (
macOS) ([#17358](https://trac.cyberduck.io/ticket/17358))
* [Bugfix] Change button styles for bottom bar of window (macOS) ([#17407](https://trac.cyberduck.io/ticket/17407))
* [Bugfix] Resumable uploads fail with Basic authentication (ownCloud)
* [Bugfix] Include "Add to My Files" shortcuts (OneDrive)
* [Bugfix] Exclude non-accessible "Personal Vault" (OneDrive) ([#17318](https://trac.cyberduck.io/ticket/17318))
* [Bugfix] Cache connection profiles loaded in Preferences → Profiles ([#17432](https://trac.cyberduck.io/ticket/17432))
[9.2.3](https://github.com/iterate-ch/cyberduck/compare/release-9-2-2...release-9-2-3)
* [Bugfix] Failure opening application (Windows)
[9.2.2](https://github.com/iterate-ch/cyberduck/compare/release-9-2-1...release-9-2-2)
* [Bugfix] Remove Dock Tile Plugin (macOS, Mac App Store)
[9.2.1](https://github.com/iterate-ch/cyberduck/compare/release-9-2-0...release-9-2-1)
* [Bugfix] Unable to enter username in login prompt (macOS)
[9.2.0](https://github.com/iterate-ch/cyberduck/compare/release-9-1-7...release-9-2-0)
* [Feature] Updated runtime (Windows) ([#16854](https://trac.cyberduck.io/ticket/16854))
* [Feature] Add plugin to set application icon (macOS) ([#17170](https://trac.cyberduck.io/ticket/17170))
* [Feature] Support for Mexico (Central) region (S3)
* [Feature] Support for Asia Pacific (Thailand) region (S3)
* [Feature] Support for Asia Pacific (Taipei) region (S3)
[9.1.7](https://github.com/iterate-ch/cyberduck/compare/release-9-1-6...release-9-1-7)
* [Bugfix] Timeout error when attempting to connect using public key authentication (SFTP) (Windows)
* [Bugfix] Use SHA-2 thumbprints to save trusted server certificates (Windows)
+206 -11
View File
@@ -16,8 +16,6 @@
4705DFF71C037AB5002466FA /* FinderSidebarService.m in Sources */ = {isa = PBXBuildFile; fileRef = 4705DFDD1C037AB5002466FA /* FinderSidebarService.m */; };
4705DFF81C037AB5002466FA /* FinderSidebarService.h in Headers */ = {isa = PBXBuildFile; fileRef = 4705DFDE1C037AB5002466FA /* FinderSidebarService.h */; };
4705DFF91C037AB5002466FA /* LaunchServicesApplicationFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4705DFDF1C037AB5002466FA /* LaunchServicesApplicationFinder.m */; };
4705DFFE1C037AB5002466FA /* FinderLocal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4705DFE41C037AB5002466FA /* FinderLocal.h */; };
4705DFFF1C037AB5002466FA /* FinderLocal.m in Sources */ = {isa = PBXBuildFile; fileRef = 4705DFE51C037AB5002466FA /* FinderLocal.m */; };
4705E0021C037AB5002466FA /* IOKitSleepPreventer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4705DFE81C037AB5002466FA /* IOKitSleepPreventer.h */; };
4705E0031C037AB5002466FA /* IOKitSleepPreventer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4705DFE91C037AB5002466FA /* IOKitSleepPreventer.m */; };
4705E0061C037AB5002466FA /* LaunchServicesApplicationFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4705DFEC1C037AB5002466FA /* LaunchServicesApplicationFinder.h */; };
@@ -178,6 +176,9 @@
4799A6492C3BC93A00563133 /* Deepbox.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4799A6472C3BC93A00563133 /* Deepbox.strings */; };
479C422A097AB2BB00DDE88A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 479C4229097AB2BB00DDE88A /* Credits.rtf */; };
479C63B022A167EA002CAA60 /* folderplus.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 479C63AF22A167EA002CAA60 /* folderplus.tiff */; };
47A0818D2DFF035E0074E7DC /* cyberduck-application-rect.icns in Resources */ = {isa = PBXBuildFile; fileRef = 475B22EE25B0DB17005D7887 /* cyberduck-application-rect.icns */; };
47A0818E2DFF035E0074E7DC /* cyberduck-application.icns in Resources */ = {isa = PBXBuildFile; fileRef = 47A082B0167A169D004458C7 /* cyberduck-application.icns */; };
47A0818F2DFF08A90074E7DC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D9E28C2DF9BB140088B042 /* main.swift */; };
47A082B1167A169D004458C7 /* cyberduck-application.icns in Resources */ = {isa = PBXBuildFile; fileRef = 47A082B0167A169D004458C7 /* cyberduck-application.icns */; };
47A40B321DEF1C09004E6084 /* cryptomator.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 47A40B311DEF1C09004E6084 /* cryptomator.tiff */; };
47A88FBB0B931D9C00DB9D87 /* Configuration.strings in Resources */ = {isa = PBXBuildFile; fileRef = 47A88FB90B931D9C00DB9D87 /* Configuration.strings */; };
@@ -225,6 +226,7 @@
47D93A151A2762A40006D4AE /* Credentials.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4751799C097943FA008F671F /* Credentials.strings */; };
47D93A161A2762A40006D4AE /* Status.strings in Resources */ = {isa = PBXBuildFile; fileRef = 47F0B7D2087AD1AE00439978 /* Status.strings */; };
47D93A171A2762A40006D4AE /* Support.strings in Resources */ = {isa = PBXBuildFile; fileRef = 47990FD0195C63AE009EBB7C /* Support.strings */; };
47D9E2902DF9BC850088B042 /* Cyberduck Dock Plugin.docktileplugin in CopyFiles */ = {isa = PBXBuildFile; fileRef = 47D9E2832DF9B98C0088B042 /* Cyberduck Dock Plugin.docktileplugin */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
47DE47952598F2D400B18BCB /* preferences-ftp.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 47DE47892598F2D300B18BCB /* preferences-ftp.pdf */; };
47DE47962598F2D400B18BCB /* preferences-connection.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 47DE478A2598F2D300B18BCB /* preferences-connection.pdf */; };
47DE47972598F2D400B18BCB /* preferences-browser.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 47DE478B2598F2D300B18BCB /* preferences-browser.pdf */; };
@@ -269,6 +271,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
47D9E28A2DF9BA2B0088B042 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 47D9E2822DF9B98C0088B042;
remoteInfo = "Cyberduck Dock Tile";
};
47FA2CEE1A28A10B0058614E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
@@ -319,6 +328,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
47D9E28F2DF9BB540088B042 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
47D9E2902DF9BC850088B042 /* Cyberduck Dock Plugin.docktileplugin in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -371,8 +390,6 @@
4705DFDD1C037AB5002466FA /* FinderSidebarService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FinderSidebarService.m; path = core/src/main/objc/FinderSidebarService.m; sourceTree = SOURCE_ROOT; };
4705DFDE1C037AB5002466FA /* FinderSidebarService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FinderSidebarService.h; path = core/src/main/objc/FinderSidebarService.h; sourceTree = SOURCE_ROOT; };
4705DFDF1C037AB5002466FA /* LaunchServicesApplicationFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LaunchServicesApplicationFinder.m; path = core/src/main/objc/LaunchServicesApplicationFinder.m; sourceTree = SOURCE_ROOT; };
4705DFE41C037AB5002466FA /* FinderLocal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FinderLocal.h; path = core/src/main/objc/FinderLocal.h; sourceTree = SOURCE_ROOT; };
4705DFE51C037AB5002466FA /* FinderLocal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FinderLocal.m; path = core/src/main/objc/FinderLocal.m; sourceTree = SOURCE_ROOT; };
4705DFE81C037AB5002466FA /* IOKitSleepPreventer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IOKitSleepPreventer.h; path = core/src/main/objc/IOKitSleepPreventer.h; sourceTree = SOURCE_ROOT; };
4705DFE91C037AB5002466FA /* IOKitSleepPreventer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IOKitSleepPreventer.m; path = core/src/main/objc/IOKitSleepPreventer.m; sourceTree = SOURCE_ROOT; };
4705DFEC1C037AB5002466FA /* LaunchServicesApplicationFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchServicesApplicationFinder.h; path = core/src/main/objc/LaunchServicesApplicationFinder.h; sourceTree = SOURCE_ROOT; };
@@ -1570,6 +1587,8 @@
47CF43D41508FCC000647729 /* ar */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; lineEnding = 0; name = ar; path = i18n/src/main/resources/ar.lproj/Transfer.strings; sourceTree = "<group>"; };
47D6FFE61CCFA54A005B01C5 /* CDToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDToolbarItem.m; path = osx/src/main/objc/CDToolbarItem.m; sourceTree = "<group>"; };
47D6FFE71CCFA54A005B01C5 /* CDToolbarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDToolbarItem.h; path = osx/src/main/objc/CDToolbarItem.h; sourceTree = "<group>"; };
47D9E2832DF9B98C0088B042 /* Cyberduck Dock Plugin.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cyberduck Dock Plugin.docktileplugin"; sourceTree = BUILT_PRODUCTS_DIR; };
47D9E28C2DF9BB140088B042 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
47DE47892598F2D300B18BCB /* preferences-ftp.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "preferences-ftp.pdf"; path = "toolbar/preferences-ftp.pdf"; sourceTree = "<group>"; };
47DE478A2598F2D300B18BCB /* preferences-connection.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "preferences-connection.pdf"; path = "toolbar/preferences-connection.pdf"; sourceTree = "<group>"; };
47DE478B2598F2D300B18BCB /* preferences-browser.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = "preferences-browser.pdf"; path = "toolbar/preferences-browser.pdf"; sourceTree = "<group>"; };
@@ -1812,6 +1831,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
47D9E2802DF9B98C0088B042 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -1821,6 +1847,7 @@
471D2C5505A2505600675163 /* Cyberduck.app */,
4739EF730856981400405FD3 /* libcore.dylib */,
47C2AD801A268C880047B67A /* duck.bundle */,
47D9E2832DF9B98C0088B042 /* Cyberduck Dock Plugin.docktileplugin */,
);
name = Products;
sourceTree = "<group>";
@@ -1831,6 +1858,7 @@
47FB79D51C2E9E67008D3B9C /* launcher.h */,
47FB79D61C2E9E67008D3B9C /* launcher.m */,
47FB79D71C2E9E67008D3B9C /* main.m */,
47D9E28D2DF9BB140088B042 /* docktile */,
47F85C1D1C0F102600FDA856 /* App */,
4741639206C4F58D00AC0BD2 /* Resources */,
474152D706C4F08A00AC0BD2 /* Core */,
@@ -1877,8 +1905,6 @@
4705DFDD1C037AB5002466FA /* FinderSidebarService.m */,
4705DFEC1C037AB5002466FA /* LaunchServicesApplicationFinder.h */,
4705DFDF1C037AB5002466FA /* LaunchServicesApplicationFinder.m */,
4705DFE41C037AB5002466FA /* FinderLocal.h */,
4705DFE51C037AB5002466FA /* FinderLocal.m */,
4705DFE81C037AB5002466FA /* IOKitSleepPreventer.h */,
4705DFE91C037AB5002466FA /* IOKitSleepPreventer.m */,
4705DFED1C037AB5002466FA /* LaunchServicesFileDescriptor.h */,
@@ -1893,7 +1919,6 @@
473A8DD5287424CA00CD2E12 /* WorkspaceSchemeHandlerProxy.m */,
);
name = Core;
path = source;
sourceTree = "<group>";
};
4741639206C4F58D00AC0BD2 /* Resources */ = {
@@ -2097,6 +2122,15 @@
name = toolbar;
sourceTree = "<group>";
};
47D9E28D2DF9BB140088B042 /* docktile */ = {
isa = PBXGroup;
children = (
47D9E28C2DF9BB140088B042 /* main.swift */,
);
name = docktile;
path = osx/docktile;
sourceTree = "<group>";
};
47F85C1D1C0F102600FDA856 /* App */ = {
isa = PBXGroup;
children = (
@@ -2128,7 +2162,6 @@
4705E0021C037AB5002466FA /* IOKitSleepPreventer.h in Headers */,
4705DFF81C037AB5002466FA /* FinderSidebarService.h in Headers */,
473A8DD6287424CA00CD2E12 /* WorkspaceSchemeHandlerProxy.h in Headers */,
4705DFFE1C037AB5002466FA /* FinderLocal.h in Headers */,
4705E0061C037AB5002466FA /* LaunchServicesApplicationFinder.h in Headers */,
4705E00D1C037AB5002466FA /* SystemConfigurationProxy.h in Headers */,
474B84411C15DB51004D562C /* Sandbox.h in Headers */,
@@ -2169,11 +2202,14 @@
47C2724412E99D8600A13758 /* Frameworks */,
470C646B1328CBDD00146FB6 /* CopyFiles */,
47C253691BF0D92800863EAF /* CopyFiles */,
47D9E28F2DF9BB540088B042 /* CopyFiles */,
47EFE4E11BED03E7009AC47B /* ShellScript */,
474E6E412E4E2CE200486097 /* ShellScript */,
);
buildRules = (
);
dependencies = (
47D9E28B2DF9BA2B0088B042 /* PBXTargetDependency */,
);
name = app;
productInstallPath = "$(USER_APPS_DIR)";
@@ -2219,6 +2255,25 @@
productReference = 47C2AD801A268C880047B67A /* duck.bundle */;
productType = "com.apple.product-type.bundle";
};
47D9E2822DF9B98C0088B042 /* docktile */ = {
isa = PBXNativeTarget;
buildConfigurationList = 47D9E2862DF9B98C0088B042 /* Build configuration list for PBXNativeTarget "docktile" */;
buildPhases = (
47D9E27F2DF9B98C0088B042 /* Sources */,
47D9E2802DF9B98C0088B042 /* Frameworks */,
47D9E2812DF9B98C0088B042 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = docktile;
packageProductDependencies = (
);
productName = "Cyberduck Dock Tile";
productReference = 47D9E2832DF9B98C0088B042 /* Cyberduck Dock Plugin.docktileplugin */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -2238,6 +2293,10 @@
CreatedOnToolsVersion = 6.1;
ProvisioningStyle = Manual;
};
47D9E2822DF9B98C0088B042 = {
CreatedOnToolsVersion = 16.4;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 47D286ED0855740D008142CF /* Build configuration list for PBXProject "Cyberduck" */;
@@ -2292,6 +2351,7 @@
471D2C0B05A2505500675163 /* app */,
4739EF670856981300405FD3 /* libcore */,
47C2AD7F1A268C880047B67A /* cli */,
47D9E2822DF9B98C0088B042 /* docktile */,
);
};
/* End PBXProject section */
@@ -2507,6 +2567,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
47D9E2812DF9B98C0088B042 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
47A0818D2DFF035E0074E7DC /* cyberduck-application-rect.icns in Resources */,
47A0818E2DFF035E0074E7DC /* cyberduck-application.icns in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -2524,6 +2593,23 @@
shellPath = /bin/sh;
shellScript = "find \"${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Contents/Resources\" \\( -name info.xib \\) -type f -delete\nfind \"${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Contents/Resources\" \\( -name classes.xib \\) -type f -delete\nfind \"${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Contents/Resources\" \\( -name data.dependency \\) -type f -delete\n";
};
474E6E412E4E2CE200486097 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"${CONFIGURATION}\" == 'Mac App Store' ]\nthen\nrm -rf \"${TARGET_BUILD_DIR}/${PLUGINS_FOLDER_PATH}/Cyberduck Dock Plugin.docktileplugin\"\nfi\n";
};
47EFE4E11BED03E7009AC47B /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -2556,7 +2642,6 @@
4705E00E1C037AB5002466FA /* SystemConfigurationProxy.m in Sources */,
47820E3920B58426006D0501 /* SystemConfigurationReachability.m in Sources */,
4785285F2AD6B10A0008184B /* AutofillDisabledSecureTextField.m in Sources */,
4705DFFF1C037AB5002466FA /* FinderLocal.m in Sources */,
4705E0031C037AB5002466FA /* IOKitSleepPreventer.m in Sources */,
473A8DD7287424CA00CD2E12 /* WorkspaceSchemeHandlerProxy.m in Sources */,
);
@@ -2586,9 +2671,22 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
47D9E27F2DF9B98C0088B042 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
47A0818F2DFF08A90074E7DC /* main.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
47D9E28B2DF9BA2B0088B042 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 47D9E2822DF9B98C0088B042 /* docktile */;
targetProxy = 47D9E28A2DF9BA2B0088B042 /* PBXContainerItemProxy */;
};
47FA2CEF1A28A10B0058614E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4739EF670856981300405FD3 /* libcore */;
@@ -4214,9 +4312,9 @@
"\"$(JAVA_HOME)/include\"/**",
"\"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Headers\"",
);
MACOSX_DEPLOYMENT_TARGET = 10.7;
SEPARATE_STRIP = YES;
STRIP_INSTALLED_PRODUCT = YES;
SWIFT_VERSION = 5.0;
WARNING_CFLAGS = (
"-Wall",
"-Wno-four-char-constants",
@@ -4225,6 +4323,94 @@
};
name = Release;
};
47D9E2842DF9B98C0088B042 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "Cyberduck Docktile Plugin";
INFOPLIST_KEY_NSHumanReadableCopyright = "${COPYRIGHT}";
INFOPLIST_KEY_NSPrincipalClass = CDDockTilePlugin;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = ch.sudo.cyberduck.docktile;
PRODUCT_NAME = "Cyberduck Dock Plugin";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
WRAPPER_EXTENSION = docktileplugin;
};
name = Release;
};
47D9E2852DF9B98C0088B042 /* Mac App Store */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "Cyberduck Docktile Plugin";
INFOPLIST_KEY_NSHumanReadableCopyright = "${COPYRIGHT}";
INFOPLIST_KEY_NSPrincipalClass = CDDockTilePlugin;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = ch.sudo.cyberduck.docktile;
PRODUCT_NAME = "Cyberduck Dock Plugin";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
WRAPPER_EXTENSION = docktileplugin;
};
name = "Mac App Store";
};
47EFE4D11BECFE81009AC47B /* Mac App Store */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -4267,9 +4453,9 @@
"\"$(JAVA_HOME)/include\"/**",
"\"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Headers\"",
);
MACOSX_DEPLOYMENT_TARGET = 10.7;
SEPARATE_STRIP = YES;
STRIP_INSTALLED_PRODUCT = YES;
SWIFT_VERSION = 5.0;
WARNING_CFLAGS = (
"-Wall",
"-Wno-four-char-constants",
@@ -4376,6 +4562,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
47D9E2862DF9B98C0088B042 /* Build configuration list for PBXNativeTarget "docktile" */ = {
isa = XCConfigurationList;
buildConfigurations = (
47D9E2842DF9B98C0088B042 /* Release */,
47D9E2852DF9B98C0088B042 /* Mac App Store */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+8
View File
@@ -23,4 +23,12 @@
<Import Project="$(BuildSupportDir)Cyberduck.Default.props" />
<PropertyGroup>
<SignToolExecutablePath>$(PATH)</SignToolExecutablePath>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)src\main\msbuild\BannedSymbols.*.txt" Link="%(Filename)%(Extension)" />
</ItemGroup>
</Project>
+40
View File
@@ -18,4 +18,44 @@
<Import Project="$(BuildSupportDir)Cyberduck.Default.targets" />
<PropertyGroup>
<_SignToolArgs Condition="'$(SignTool)'=='cng'">_SignToolArgsCNG</_SignToolArgs>
<_SignToolArgs Condition="'$(_SignToolArgs)'==''">_SignToolArgsCertificateStore</_SignToolArgs>
<SignToolArgsDependsOn>$(_SignToolArgs);SignToolExecutablePath</SignToolArgsDependsOn>
</PropertyGroup>
<Target Name="SignToolExecutablePath" Returns="$(SignToolExecutablePath);$(SignToolExecutable)">
<PropertyGroup>
<SignToolExecutable Condition="'$(SignToolExecutable)'==''">SignTool.exe</SignToolExecutable>
<SignToolExecutablePath>$(WindowsSdk_ExecutablePath);$(SignToolExecutablePath)</SignToolExecutablePath>
</PropertyGroup>
</Target>
<Target Name="SignToolArgsBase" Returns="$(SignToolArgsBase)">
<PropertyGroup>
<SignToolArgsBase>sign /d "Cyberduck" /fd sha256 /tr "http://timestamp.acs.microsoft.com" /td "sha256" /a</SignToolArgsBase>
</PropertyGroup>
</Target>
<Target Name="SignToolArgs" DependsOnTargets="$(SignToolArgsDependsOn)">
<Error Text="'$(SignTool)' unsupported." Condition="'$(_SignToolArgs)'==''" />
<PropertyGroup>
<SignToolArgs>"$(SignToolExecutable)" $(SignToolArgsBase)</SignToolArgs>
</PropertyGroup>
<ItemGroup>
<SignToolEnvironmentVariable Include="PATH=$([MSBuild]::Escape('$(SignToolExecutablePath)'))" />
</ItemGroup>
</Target>
<Target Name="_SignToolArgsCertificateStore" DependsOnTargets="SignToolArgsBase" Returns="$(SignToolArgsBase)">
<PropertyGroup>
<SignToolArgsBase>$(SignToolArgsBase) /sm /n "iterate GmbH"</SignToolArgsBase>
</PropertyGroup>
</Target>
<Target Name="_SignToolArgsCNG" DependsOnTargets="SignToolArgsBase" Returns="$(SignToolArgsBase)">
<PropertyGroup>
<SignToolArgsBase>$(SignToolArgsBase) /f "$(CyberduckDir)setup\cert\certificate.crt" /csp "$(SignToolCSP)" /kc "$(SignToolKC)"</SignToolArgsBase>
</PropertyGroup>
</Target>
</Project>
+5 -1
View File
@@ -23,7 +23,7 @@
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageVersion Include="DotNetProjects.Extended.Wpf.Toolkit" Version="5.0.100" />
<PackageVersion Include="ExceptionReporter " Version="2.4.2" />
<PackageVersion Include="IKVM" Version="8.11.2" />
<PackageVersion Include="IKVM" Version="8.13.3" />
<PackageVersion Include="iterate-ch.IKVM.MSBuild.SkipDuplicates" Version="1.1.0" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.1.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
@@ -49,6 +49,10 @@
<PackageVersion Include="WixToolset.Util.wixext" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
<GlobalPackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
</ItemGroup>
<PropertyGroup>
<PackageVersions>@(PackageVersion->'%(Identity)'->Replace('.', '_')->'Pkg%(Identity)Version=%(Version)')</PackageVersions>
</PropertyGroup>
-46
View File
@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2002-2019 iterate GmbH. All rights reserved.
~ https://cyberduck.io/
~
~ This program is free software; you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program 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 General Public License for more details.
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Update="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Update="DotNetProjects.Extended.Wpf.Toolkit" Version="5.0.100" />
<PackageReference Update="ExceptionReporter " Version="2.4.2" />
<PackageReference Update="IKVM" Version="8.11.0-dev.283" />
<PackageReference Update="iterate-ch.IKVM.MSBuild.SkipDuplicates" Version="1.1.0" />
<PackageReference Update="Microsoft.Build.Utilities.Core" Version="17.1.0" />
<PackageReference Update="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Update="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageReference Update="Microsoft.Windows.CsWin32" Version="0.3.106" />
<PackageReference Update="Microsoft.Windows.SDK.Contracts" Version="10.0.22000.196" />
<PackageReference Update="NUnit" Version="4.2.0" />
<PackageReference Update="NUnit.Console" Version="3.20.0" />
<PackageReference Update="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Update="ObjectListView.Official" Version="2.9.1" />
<PackageReference Update="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="1.2.3" />
<PackageReference Update="ReactiveUI" Version="17.1.50" />
<PackageReference Update="ReactiveUI.WPF" Version="17.1.50" />
<PackageReference Update="StructureMap" Version="2.6.4.1" />
<PackageReference Update="System.Configuration.ConfigurationManager" Version="8.0.0" />
<PackageReference Update="System.Memory" Version="4.5.5" />
<PackageReference Update="System.Runtime.Caching" Version="8.0.0" />
<PackageReference Update="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Update="WixToolset.Heat" Version="5.0.2" />
<GlobalPackageReference Include="MSBuildTasks" Version="1.5.0.235" GeneratePathProperty="true" />
</ItemGroup>
</Project>
+18 -1
View File
@@ -25,7 +25,11 @@ Source code is available licensed under the [GNU General Public License Version
## Localizations
Translations to new languages are welcome. We use [Transifex](https://www.transifex.com/cyberduck/cyberduck/dashboard/) to localize resources. Current available localizations are _English_, _Czech_, _Dutch_, _Finnish_, _French_, _German_, _Italian_, _Japanese_, _Korean_, _Norwegian_, _Portuguese_, _Slovak_, _Spanish_, _Chinese (Traditional & Simplified Han)_, _Russian_, _Swedish_, _Hungarian_, _Danish_, _Polish_, _Indonesian_, _Catalan_, _Welsh_, _Thai_, _Turkish_, _Hebrew_, _Latvian_, _Greek_, _Serbian_, _Georgian_ and _Slovenian_.
Translations to new languages are welcome. We use [Transifex](https://app.transifex.com/cyberduck/cyberduck/dashboard/)
to localize resources. Current available localizations are _English_, _Czech_, _Dutch_, _Finnish_, _French_, _German_,
_Italian_, _Japanese_, _Korean_, _Norwegian_, _Portuguese_, _Slovak_, _Spanish_, _Chinese (Traditional & Simplified
Han)_, _Russian_, _Swedish_, _Hungarian_, _Danish_, _Polish_, _Indonesian_, _Catalan_, _Welsh_, _Thai_, _Turkish_,
_Hebrew_, _Latvian_, _Greek_, _Serbian_, _Georgian_ and _Slovenian_.
Make sure to subscribe to the [localization mailing list](http://lists.cyberduck.ch/mailman/listinfo/cyberduck-localization).
@@ -116,6 +120,19 @@ Make sure that `MSBuild`, `mvn`, `ant` and `java` are on your `PATH`-environment
Additionally include the latest Windows Sdk-binary folder in your `PATH`-environment variable:
* `%ProgramFiles(x86)%\Windows Kits\10\bin\10.0.<Latest>.0\x64`
#### NuGet Configuration
To build on Windows, you need to configure NuGet with credentials to access GitHub Package Registry sources. Create a GitHub [personal access token (classic)](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry) with at least `read:packages` permissions.
Then, in the Cyberduck repository root directory, run the following commands in Terminal (PowerShell or Command Prompt):
```sh
dotnet nuget update source gh-ikvmnet -u "YourUsername" -p "YourPAT"
dotnet nuget update source gh-iterate-ch -u "YourUsername" -p "YourPAT"
```
Replace `YourUsername` with your GitHub username and `YourPAT` with your personal access token.
## Building
Run `mvn verify -DskipTests -DskipSign` to build without running any tests and skip codesign. Find build artifacts in
+4 -4
View File
@@ -18,11 +18,11 @@
<parent>
<groupId>ch.cyberduck</groupId>
<artifactId>parent</artifactId>
<version>9.2.0-SNAPSHOT</version>
<version>9.4.0-SNAPSHOT</version>
</parent>
<artifactId>azure</artifactId>
<properties>
<azure-storage-version>8.6.6</azure-storage-version>
<azure-storage-version>12.32.0</azure-storage-version>
</properties>
<dependencies>
@@ -39,8 +39,8 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>${azure-storage-version}</version>
<exclusions>
<exclusion>
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.Acl;
@@ -23,49 +20,42 @@ import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.AclPermission;
import ch.cyberduck.core.transfer.TransferStatus;
import org.jets3t.service.acl.Permission;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobContainerPermissions;
import com.microsoft.azure.storage.blob.BlobContainerPublicAccessType;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.models.BlobContainerAccessPolicies;
import com.azure.storage.blob.models.PublicAccessType;
/**
* By default, a container and any blobs within it may be accessed only by the owner of the storage account.
* If you want to give anonymous users read permissions to a container and its blobs, you can set the container
* permissions to allow public access. Anonymous users can read blobs within a publicly accessible container without authenticating the request.
* Containers provide the following options for managing container access:
* By default, a container and any blobs within it may be accessed only by the owner of the storage account. If you want
* to give anonymous users read permissions to a container and its blobs, you can set the container permissions to allow
* public access. Anonymous users can read blobs within a publicly accessible container without authenticating the
* request. Containers provide the following options for managing container access:
* <p/>
* Full public read access: Container and blob data can be read via anonymous request. Clients can enumerate
* blobs within the container via anonymous request, but cannot enumerate containers within the storage account.
* Full public read access: Container and blob data can be read via anonymous request. Clients can enumerate blobs
* within the container via anonymous request, but cannot enumerate containers within the storage account.
* <p/>
* Public read access for blobs only: Blob data within this container can be read via anonymous request, but
* container data is not available. Clients cannot enumerate blobs within the container via anonymous request.
* Public read access for blobs only: Blob data within this container can be read via anonymous request, but container
* data is not available. Clients cannot enumerate blobs within the container via anonymous request.
* <p/>
* No public read access: Container and blob data can be read by the account owner only.
*/
public class AzureAclPermissionFeature implements AclPermission {
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public AzureAclPermissionFeature(final AzureSession session, final OperationContext context) {
public AzureAclPermissionFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
@@ -75,8 +65,8 @@ public class AzureAclPermissionFeature implements AclPermission {
}
@Override
public List<Acl.User> getAvailableAclUsers() {
return new ArrayList<Acl.User>(Collections.singletonList(
public List<Acl.User> getAvailableAclUsers(final List<Path> files) {
return new ArrayList<>(Collections.singletonList(
new Acl.GroupUser(Acl.GroupUser.EVERYONE, false))
);
}
@@ -85,22 +75,21 @@ public class AzureAclPermissionFeature implements AclPermission {
public Acl getPermission(final Path file) throws BackgroundException {
try {
if(containerService.isContainer(file)) {
final CloudBlobContainer container = session.getClient()
.getContainerReference(containerService.getContainer(file).getName());
final BlobContainerPermissions permissions = container.downloadPermissions(null, null, context);
final BlobContainerClient client = session.getClient()
.getBlobContainerClient(containerService.getContainer(file).getName());
final BlobContainerAccessPolicies accessPolicy = client.getAccessPolicy();
final Acl acl = new Acl();
if(permissions.getPublicAccess().equals(BlobContainerPublicAccessType.BLOB)
|| permissions.getPublicAccess().equals(BlobContainerPublicAccessType.CONTAINER)) {
if(accessPolicy.getBlobAccessType() != null) {
if(accessPolicy.getBlobAccessType().equals(PublicAccessType.BLOB)
|| accessPolicy.getBlobAccessType().equals(PublicAccessType.CONTAINER)) {
acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE, false), new Acl.Role(Acl.Role.READ));
}
}
return acl;
}
return Acl.EMPTY;
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file);
}
}
@@ -109,23 +98,21 @@ public class AzureAclPermissionFeature implements AclPermission {
public void setPermission(final Path file, final TransferStatus status) throws BackgroundException {
try {
if(containerService.isContainer(file)) {
final CloudBlobContainer container = session.getClient()
.getContainerReference(containerService.getContainer(file).getName());
final BlobContainerPermissions permissions = container.downloadPermissions(null, null, context);
final BlobContainerClient client = session.getClient()
.getBlobContainerClient(containerService.getContainer(file).getName());
if(status.getAcl().asList().isEmpty()) {
client.setAccessPolicy(null, Collections.emptyList());
}
for(Acl.UserAndRole userAndRole : status.getAcl().asList()) {
if(userAndRole.getUser() instanceof Acl.GroupUser) {
if(userAndRole.getUser().getIdentifier().equals(Acl.GroupUser.EVERYONE)) {
permissions.setPublicAccess(BlobContainerPublicAccessType.BLOB);
client.setAccessPolicy(PublicAccessType.BLOB, Collections.emptyList());
}
}
}
container.uploadPermissions(permissions, null, null, context);
}
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Cannot change permissions of {0}", e, file);
}
}
@@ -1,24 +1,22 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.CancellingListProgressListener;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.ListProgressListener;
import ch.cyberduck.core.Path;
@@ -26,41 +24,36 @@ import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ListCanceledException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.AttributesAdapter;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.io.Checksum;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobContainerProperties;
import com.microsoft.azure.storage.blob.BlobProperties;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.models.BlobContainerProperties;
import com.azure.storage.blob.models.BlobItemProperties;
import com.azure.storage.blob.models.BlobProperties;
public class AzureAttributesFinderFeature implements AttributesFinder, AttributesAdapter<CloudBlob> {
public class AzureAttributesFinderFeature implements AttributesFinder, AttributesAdapter<BlobItemProperties> {
private static final Logger log = LogManager.getLogger(AzureAttributesFinderFeature.class);
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public static final String KEY_BLOB_TYPE = "blob_type";
public AzureAttributesFinderFeature(final AzureSession session, final OperationContext context) {
public AzureAttributesFinderFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
@@ -70,24 +63,21 @@ public class AzureAttributesFinderFeature implements AttributesFinder, Attribute
}
try {
if(containerService.isContainer(file)) {
final PathAttributes attributes = new PathAttributes();
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName());
container.downloadAttributes(null, null, context);
final BlobContainerProperties properties = container.getProperties();
attributes.setETag(properties.getEtag());
attributes.setModificationDate(properties.getLastModified().getTime());
final PathAttributes attributes = new DefaultPathAttributes();
final BlobContainerClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName());
final BlobContainerProperties properties = client.getProperties();
attributes.setETag(properties.getETag());
attributes.setModificationDate(properties.getLastModified().toInstant().toEpochMilli());
return attributes;
}
if(file.isFile() || file.isPlaceholder()) {
try {
final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlobReferenceFromServer(containerService.getKey(file));
final BlobRequestOptions options = new BlobRequestOptions();
blob.downloadAttributes(AccessCondition.generateEmptyCondition(), options, context);
return this.toAttributes(blob);
final BlobProperties properties = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file)).getBlockBlobClient().getProperties();
return this.toAttributes(properties);
}
catch(StorageException e) {
switch(e.getHttpStatusCode()) {
catch(HttpResponseException e) {
switch(e.getResponse().getStatusCode()) {
case HttpStatus.SC_NOT_FOUND:
if(file.isPlaceholder()) {
// Ignore failure and look for common prefix
@@ -98,9 +88,11 @@ public class AzureAttributesFinderFeature implements AttributesFinder, Attribute
}
}
}
// Check for common prefix
if(log.isDebugEnabled()) {
log.debug(String.format("Search for common prefix %s", file));
}
try {
new AzureObjectListService(session, context).list(file, new CancellingListProgressListener());
new AzureObjectListService(session).list(file, new CancellingListProgressListener());
return PathAttributes.EMPTY;
}
catch(ListCanceledException l) {
@@ -108,27 +100,33 @@ public class AzureAttributesFinderFeature implements AttributesFinder, Attribute
return PathAttributes.EMPTY;
}
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
@Override
public PathAttributes toAttributes(final CloudBlob blob) {
final PathAttributes attributes = new PathAttributes();
final BlobProperties properties = blob.getProperties();
attributes.setSize(properties.getLength());
attributes.setModificationDate(properties.getLastModified().getTime());
if(StringUtils.isNotBlank(properties.getContentMD5())) {
attributes.setChecksum(Checksum.parse(Hex.encodeHexString(Base64.decodeBase64(properties.getContentMD5()))));
public PathAttributes toAttributes(final BlobProperties properties) {
final PathAttributes attributes = new DefaultPathAttributes();
attributes.setSize(properties.getBlobSize());
attributes.setModificationDate(properties.getLastModified().toInstant().toEpochMilli());
if(properties.getContentMd5() != null) {
attributes.setChecksum(Checksum.parse(Hex.encodeHexString(properties.getContentMd5())));
}
attributes.setETag(properties.getEtag());
attributes.setETag(properties.getETag());
final Map<String, String> custom = new HashMap<>();
custom.put(AzureAttributesFinderFeature.KEY_BLOB_TYPE, properties.getBlobType().name());
attributes.setCustom(custom);
return attributes;
}
public PathAttributes toAttributes(final BlobItemProperties properties) {
final PathAttributes attributes = new DefaultPathAttributes();
attributes.setSize(properties.getContentLength());
attributes.setModificationDate(properties.getLastModified().toInstant().toEpochMilli());
attributes.setETag(properties.getETag());
if(properties.getContentMd5() != null) {
attributes.setChecksum(Checksum.parse(Hex.encodeHexString(properties.getContentMd5())));
}
return attributes;
}
}
@@ -1,24 +1,22 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2015 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.AttributedList;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.ListProgressListener;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
@@ -29,49 +27,35 @@ import ch.cyberduck.core.preferences.HostPreferencesFactory;
import java.util.EnumSet;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.ResultContinuation;
import com.microsoft.azure.storage.ResultSegment;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.ContainerListingDetails;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.models.BlobContainerItem;
import com.azure.storage.blob.models.BlobContainerListDetails;
import com.azure.storage.blob.models.ListBlobContainersOptions;
public class AzureContainerListService implements RootListService {
private final AzureSession session;
private final OperationContext context;
public AzureContainerListService(final AzureSession session, final OperationContext context) {
public AzureContainerListService(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public AttributedList<Path> list(final Path directory, final ListProgressListener listener) throws BackgroundException {
ResultSegment<CloudBlobContainer> result;
ResultContinuation token = null;
try {
final AttributedList<Path> containers = new AttributedList<>();
do {
final BlobRequestOptions options = new BlobRequestOptions();
result = session.getClient().listContainersSegmented(null, ContainerListingDetails.NONE,
HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"), token,
options, context);
for(CloudBlobContainer container : result.getResults()) {
final PathAttributes attributes = new PathAttributes();
attributes.setETag(container.getProperties().getEtag());
attributes.setModificationDate(container.getProperties().getLastModified().getTime());
containers.add(new Path(PathNormalizer.normalize(container.getName()),
EnumSet.of(Path.Type.volume, Path.Type.directory), attributes));
}
for(BlobContainerItem container : session.getClient().listBlobContainers(new ListBlobContainersOptions()
.setMaxResultsPerPage(HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"))
.setDetails(new BlobContainerListDetails().setRetrieveDeleted(false).setRetrieveMetadata(true)), null)) {
final PathAttributes attributes = new DefaultPathAttributes();
attributes.setETag(container.getProperties().getETag());
attributes.setModificationDate(container.getProperties().getLastModified().toInstant().toEpochMilli());
containers.add(new Path(PathNormalizer.normalize(container.getName()), EnumSet.of(Path.Type.volume, Path.Type.directory), attributes));
listener.chunk(directory, containers);
token = result.getContinuationToken();
}
while(result.getHasMoreResults());
return containers;
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Listing directory {0} failed", e, directory);
}
}
@@ -1,87 +1,74 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.ConnectionCallback;
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.exception.UnsupportedException;
import ch.cyberduck.core.features.Copy;
import ch.cyberduck.core.io.StreamListener;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.transfer.TransferStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.Optional;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.azure.core.exception.HttpResponseException;
import com.azure.core.util.polling.SyncPoller;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.models.BlobCopyInfo;
import com.azure.storage.blob.options.BlobBeginCopyOptions;
public class AzureCopyFeature implements Copy {
private static final Logger log = LogManager.getLogger(AzureCopyFeature.class);
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public AzureCopyFeature(final AzureSession session, final OperationContext context) {
public AzureCopyFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public Path copy(final Path source, final Path copy, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException {
try {
final CloudBlob target = session.getClient().getContainerReference(containerService.getContainer(copy).getName())
.getAppendBlobReference(containerService.getKey(copy));
final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(source).getName())
.getBlobReferenceFromServer(containerService.getKey(source));
final BlobRequestOptions options = new BlobRequestOptions();
options.setStoreBlobContentMD5(HostPreferencesFactory.get(session.getHost()).getBoolean("azure.upload.md5"));
final URI s = session.getHost().getCredentials().isTokenAuthentication() ?
URI.create(blob.getUri().toString() + session.getHost().getCredentials().getToken()) : blob.getUri();
final String id = target.startCopy(s,
AccessCondition.generateEmptyCondition(), AccessCondition.generateEmptyCondition(), options, context);
log.debug("Started copy for {} with copy operation ID {}", copy, id);
final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(copy).getName())
.getBlobClient(containerService.getKey(copy));
final SyncPoller<BlobCopyInfo, Void> poller = client.beginCopy(
new BlobBeginCopyOptions(session.getClient().getBlobContainerClient(containerService.getContainer(source).getName())
.getBlobClient(containerService.getKey(source)).getBlobUrl()).setPollInterval(Duration.ofSeconds(1)));
if(log.isDebugEnabled()) {
log.debug(String.format("Started copy for %s", copy));
}
poller.waitForCompletion();
listener.sent(status.getLength());
return copy;
return new Path(copy).withAttributes(PathAttributes.EMPTY);
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Cannot copy {0}", e, source);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
@Override
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
@@ -23,40 +20,30 @@ import ch.cyberduck.core.PasswordCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.transfer.TransferStatus;
import org.apache.http.HttpStatus;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.DeleteSnapshotsOption;
import com.azure.core.exception.HttpResponseException;
public class AzureDeleteFeature implements Delete {
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public AzureDeleteFeature(final AzureSession session, final OperationContext context) {
public AzureDeleteFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public void delete(final Map<Path, TransferStatus> files, final PasswordCallback prompt, final Callback callback) throws BackgroundException {
final List<Path> containers = new ArrayList<Path>();
final List<Path> containers = new ArrayList<>();
for(Path file : files.keySet()) {
if(containerService.isContainer(file)) {
containers.add(file);
@@ -64,13 +51,11 @@ public class AzureDeleteFeature implements Delete {
else {
callback.delete(file);
try {
final BlobRequestOptions options = new BlobRequestOptions();
session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlockBlobReference(containerService.getKey(file)).delete(
DeleteSnapshotsOption.INCLUDE_SNAPSHOTS, AccessCondition.generateEmptyCondition(), options, context);
session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file)).delete();
}
catch(StorageException e) {
switch(e.getHttpStatusCode()) {
catch(HttpResponseException e) {
switch(e.getResponse().getStatusCode()) {
case HttpStatus.SC_NOT_FOUND:
if(file.isPlaceholder()) {
// Ignore failure with no placeholder object found
@@ -79,24 +64,16 @@ public class AzureDeleteFeature implements Delete {
}
throw new AzureExceptionMappingService().map("Cannot delete {0}", e, file);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
}
for(Path file : containers) {
callback.delete(file);
try {
final BlobRequestOptions options = new BlobRequestOptions();
session.getClient().getContainerReference(containerService.getContainer(file).getName()).delete(
AccessCondition.generateEmptyCondition(), options, context);
session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).delete();
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Cannot delete {0}", e, file);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
}
}
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
@@ -23,8 +20,8 @@ import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InteroperabilityException;
import ch.cyberduck.core.exception.InvalidFilenameException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -33,52 +30,40 @@ import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.EnumSet;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.azure.core.exception.HttpResponseException;
public class AzureDirectoryFeature implements Directory<Void> {
private final AzureSession session;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
private final AzureSession session;
private final OperationContext context;
private Write<Void> writer;
public AzureDirectoryFeature(final AzureSession session, final OperationContext context) {
public AzureDirectoryFeature(final AzureSession session) {
this.session = session;
this.context = context;
this.writer = new AzureWriteFeature(session, context);
}
@Override
public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException {
public Path mkdir(final Write<Void> writer, final Path folder, final TransferStatus status) throws BackgroundException {
try {
final BlobRequestOptions options = new BlobRequestOptions();
if(containerService.isContainer(folder)) {
// Container name must be lower case.
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(folder).getName());
container.create(options, context);
return folder;
session.getClient().getBlobContainerClient(containerService.getContainer(folder).getName()).create();
return new Path(folder.getParent(), folder.getName(), folder.getType());
}
else {
final EnumSet<Path.Type> type = EnumSet.copyOf(folder.getType());
type.add(Path.Type.placeholder);
return new AzureTouchFeature(session, context).withWriter(writer).touch(folder.withType(type),
return new AzureTouchFeature(session).touch(writer, folder.withType(type),
status.setChecksum(writer.checksum(folder, status).compute(new NullInputStream(0L), status)));
}
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
catch(IllegalArgumentException e) {
throw new InteroperabilityException();
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Cannot create folder {0}", e, folder);
}
}
@@ -109,10 +94,4 @@ public class AzureDirectoryFeature implements Directory<Void> {
}
return true;
}
@Override
public Directory<Void> withWriter(final Write<Void> writer) {
this.writer = writer;
return this;
}
}
@@ -1,61 +1,57 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.AbstractExceptionMappingService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.LoginFailureException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService;
import ch.cyberduck.core.ssl.SSLExceptionMappingService;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.client.HttpResponseException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.net.ssl.SSLException;
import java.net.UnknownHostException;
import java.util.Map;
import com.microsoft.azure.storage.StorageException;
import com.azure.core.exception.HttpResponseException;
public class AzureExceptionMappingService extends AbstractExceptionMappingService<StorageException> {
private static final Logger log = LogManager.getLogger(AzureExceptionMappingService.class);
public class AzureExceptionMappingService extends AbstractExceptionMappingService<HttpResponseException> {
@Override
public BackgroundException map(final StorageException failure) {
log.warn("Map failure {}", failure.toString());
public BackgroundException map(final HttpResponseException failure) {
final StringBuilder buffer = new StringBuilder();
this.append(buffer, failure.getMessage());
if(ExceptionUtils.getRootCause(failure) instanceof UnknownHostException) {
return new NotfoundException(buffer.toString(), failure);
switch(failure.getResponse().getStatusCode()) {
case 403:
if(failure.getValue() instanceof Map) {
final Map messages = (Map) failure.getValue();
if(messages.containsKey("Message")) {
this.append(buffer, messages.get("Message").toString());
}
}
else {
this.append(buffer, failure.getMessage());
}
return new LoginFailureException(buffer.toString(), failure);
}
this.append(buffer, failure.getMessage());
for(Throwable cause : ExceptionUtils.getThrowableList(failure)) {
if(cause instanceof SSLException) {
return new SSLExceptionMappingService().map(buffer.toString(), (SSLException) cause);
}
}
switch(failure.getHttpStatusCode()) {
case 403:
return new LoginFailureException(buffer.toString(), failure);
default:
return new DefaultHttpResponseExceptionMappingService().map(new HttpResponseException(failure.getHttpStatusCode(), buffer.toString()));
}
return new DefaultHttpResponseExceptionMappingService().map(failure, buffer, failure.getResponse().getStatusCode());
}
}
@@ -1,56 +1,42 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.CancellingListProgressListener;
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.ListProgressListener;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ListCanceledException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Find;
import org.apache.http.HttpStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URISyntaxException;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
public class AzureFindFeature implements Find {
private static final Logger log = LogManager.getLogger(AzureFindFeature.class);
private final AzureSession session;
private final OperationContext context;
private final AzureAttributesFinderFeature attributes;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public AzureFindFeature(final AzureSession session, final OperationContext context) {
public AzureFindFeature(final AzureSession session) {
this.session = session;
this.context = context;
this.attributes = new AzureAttributesFinderFeature(session);
}
@Override
@@ -59,47 +45,13 @@ public class AzureFindFeature implements Find {
return true;
}
try {
try {
final boolean found;
if(containerService.isContainer(file)) {
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName());
return container.exists(null, null, context);
return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).exists();
}
if(file.isFile() || file.isPlaceholder()) {
try {
final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlobReferenceFromServer(containerService.getKey(file));
return blob.exists(null, null, context);
}
catch(StorageException e) {
switch(e.getHttpStatusCode()) {
case HttpStatus.SC_NOT_FOUND:
if(file.isPlaceholder()) {
// Ignore failure and look for common prefix
break;
}
default:
throw e;
}
}
}
log.debug("Search for common prefix {}", file);
// Check for common prefix
try {
new AzureObjectListService(session, context).list(file, new CancellingListProgressListener());
attributes.find(file, listener);
return true;
}
catch(ListCanceledException l) {
// Found common prefix
return true;
}
}
catch(StorageException e) {
throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file);
}
catch(URISyntaxException e) {
return false;
}
}
catch(NotfoundException e) {
return false;
}
@@ -1,7 +1,7 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2018 iterate GmbH. All rights reserved.
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
@@ -21,25 +21,21 @@ import ch.cyberduck.core.ListService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.BackgroundException;
import com.microsoft.azure.storage.OperationContext;
public class AzureListService implements ListService {
private final AzureSession session;
private final OperationContext context;
public AzureListService(final AzureSession session, final OperationContext context) {
public AzureListService(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public AttributedList<Path> list(final Path directory, final ListProgressListener listener) throws BackgroundException {
if(directory.isRoot()) {
return new AzureContainerListService(session, context).list(directory, listener);
return new AzureContainerListService(session).list(directory, listener);
}
else {
return new AzureObjectListService(session, context).list(directory, listener);
return new AzureObjectListService(session).list(directory, listener);
}
}
}
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.Path;
@@ -26,29 +23,25 @@ import ch.cyberduck.core.logging.LoggingConfiguration;
import java.util.Collections;
import java.util.EnumSet;
import com.microsoft.azure.storage.LoggingOperations;
import com.microsoft.azure.storage.LoggingProperties;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.ServiceProperties;
import com.microsoft.azure.storage.StorageException;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.models.BlobAnalyticsLogging;
import com.azure.storage.blob.models.BlobRetentionPolicy;
import com.azure.storage.blob.models.BlobServiceProperties;
public class AzureLoggingFeature implements Logging {
private final AzureSession session;
private final OperationContext context;
public AzureLoggingFeature(final AzureSession session, final OperationContext context) {
public AzureLoggingFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public LoggingConfiguration getConfiguration(final Path container) throws BackgroundException {
try {
final ServiceProperties properties = session.getClient().downloadServiceProperties(null, context);
final BlobServiceProperties properties = session.getClient().getProperties();
final LoggingConfiguration configuration = new LoggingConfiguration(
!properties.getLogging().getLogOperationTypes().isEmpty(),
properties.getLogging().isRead() || properties.getLogging().isWrite() || properties.getLogging().isDelete(),
"$logs"
);
// When you have configured Storage Logging to log request data from your storage account, it saves the log data
@@ -58,7 +51,7 @@ public class AzureLoggingFeature implements Logging {
);
return configuration;
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Cannot read container configuration", e);
}
}
@@ -66,18 +59,17 @@ public class AzureLoggingFeature implements Logging {
@Override
public void setConfiguration(final Path container, final LoggingConfiguration configuration) throws BackgroundException {
try {
final ServiceProperties properties = session.getClient().downloadServiceProperties(null, context);
final LoggingProperties l = new LoggingProperties();
if(configuration.isEnabled()) {
l.setLogOperationTypes(EnumSet.allOf(LoggingOperations.class));
final BlobServiceProperties properties = session.getClient().getProperties();
properties.setLogging(new BlobAnalyticsLogging()
.setVersion("2.0")
.setRetentionPolicy(new BlobRetentionPolicy().setEnabled(false))
.setDelete(configuration.isEnabled())
.setRead(configuration.isEnabled())
.setWrite(configuration.isEnabled())
);
session.getClient().setProperties(properties);
}
else {
l.setLogOperationTypes(EnumSet.noneOf(LoggingOperations.class));
}
properties.setLogging(l);
session.getClient().uploadServiceProperties(properties, null, context);
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Failure to write attributes of {0}", e, container);
}
}
@@ -1,28 +1,24 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Headers;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -30,34 +26,27 @@ import ch.cyberduck.core.transfer.TransferStatus;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHeaders;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobProperties;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.models.BlobHttpHeaders;
import com.azure.storage.blob.models.BlobProperties;
public class AzureMetadataFeature implements Headers {
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public AzureMetadataFeature(final AzureSession session, final OperationContext context) {
public AzureMetadataFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public Map<String, String> getDefault() {
public Map<String, String> getDefault(final Path file) {
return HostPreferencesFactory.get(session.getHost()).getMap("azure.metadata.default");
}
@@ -65,30 +54,23 @@ public class AzureMetadataFeature implements Headers {
public Map<String, String> getMetadata(final Path file) throws BackgroundException {
try {
if(containerService.isContainer(file)) {
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName());
container.downloadAttributes();
return container.getMetadata();
return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).getProperties().getMetadata();
}
else {
final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlobReferenceFromServer(containerService.getKey(file));
// Populates the blob properties and metadata
blob.downloadAttributes(null, null, context);
final Map<String, String> metadata = new HashMap<String, String>(blob.getMetadata());
final BlobProperties properties = blob.getProperties();
if(StringUtils.isNotBlank(properties.getCacheControl())) {
metadata.put(HttpHeaders.CACHE_CONTROL, properties.getCacheControl());
}
final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file));
final BlobProperties properties = client.getProperties();
final Map<String, String> metadata = properties.getMetadata();
if(StringUtils.isNotBlank(properties.getContentType())) {
metadata.put(HttpHeaders.CONTENT_TYPE, properties.getContentType());
}
if(StringUtils.isNotBlank(properties.getCacheControl())) {
metadata.put(HttpHeaders.CACHE_CONTROL, properties.getCacheControl());
}
return metadata;
}
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file);
}
}
@@ -96,42 +78,31 @@ public class AzureMetadataFeature implements Headers {
@Override
public void setMetadata(final Path file, final TransferStatus status) throws BackgroundException {
try {
final BlobRequestOptions options = new BlobRequestOptions();
if(containerService.isContainer(file)) {
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName());
container.setMetadata(new HashMap<>(status.getMetadata()));
container.uploadMetadata(AccessCondition.generateEmptyCondition(), options, context);
session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.setMetadata(new HashMap<>(status.getMetadata()));
}
else {
final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlobReferenceFromServer(containerService.getKey(file));
// Populates the blob properties and metadata
blob.downloadAttributes();
// Replace metadata
final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file));
final HashMap<String, String> pruned = new HashMap<>();
for(Map.Entry<String, String> m : status.getMetadata().entrySet()) {
final BlobProperties properties = blob.getProperties();
if(HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(m.getKey())) {
// Update properties
properties.setCacheControl(m.getValue());
client.setHttpHeaders(new BlobHttpHeaders().setCacheControl(m.getValue()));
continue;
}
if(HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(m.getKey())) {
// Update properties
properties.setContentType(m.getValue());
client.setHttpHeaders(new BlobHttpHeaders().setContentType(m.getValue()));
continue;
}
pruned.put(m.getKey(), m.getValue());
}
blob.setMetadata(pruned);
blob.uploadMetadata(AccessCondition.generateEmptyCondition(), options, context);
blob.uploadProperties();
client.setMetadata(pruned);
}
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Failure to write attributes of {0}", e, file);
}
}
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.ConnectionCallback;
@@ -29,16 +26,14 @@ import ch.cyberduck.core.transfer.TransferStatus;
import java.util.Collections;
import java.util.Optional;
import com.microsoft.azure.storage.OperationContext;
public class AzureMoveFeature implements Move {
private final AzureCopyFeature proxy;
private final AzureDeleteFeature delete;
public AzureMoveFeature(final AzureSession session, final OperationContext context) {
this.proxy = new AzureCopyFeature(session, context);
this.delete = new AzureDeleteFeature(session, context);
public AzureMoveFeature(final AzureSession session) {
this.proxy = new AzureCopyFeature(session);
this.delete = new AzureDeleteFeature(session);
}
@Override
@@ -49,7 +44,7 @@ public class AzureMoveFeature implements Move {
@Override
public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) throws BackgroundException {
final Path copy = proxy.copy(file, renamed, new TransferStatus().setLength(file.attributes().getSize()), connectionCallback, new DisabledStreamListener());
final Path copy = proxy.copy(file, renamed, status, connectionCallback, new DisabledStreamListener());
delete.delete(Collections.singletonList(file), connectionCallback, callback);
return copy;
}
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.AttributedList;
@@ -26,53 +23,43 @@ import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.PathNormalizer;
import ch.cyberduck.core.SimplePathPredicate;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.io.Checksum;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URISyntaxException;
import java.util.EnumSet;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.ResultContinuation;
import com.microsoft.azure.storage.ResultSegment;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobListingDetails;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlobDirectory;
import com.microsoft.azure.storage.blob.ListBlobItem;
import com.azure.core.exception.HttpResponseException;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.http.rest.PagedResponse;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.models.BlobItem;
import com.azure.storage.blob.models.BlobListDetails;
import com.azure.storage.blob.models.ListBlobsOptions;
public class AzureObjectListService implements ListService {
private static final Logger log = LogManager.getLogger(AzureObjectListService.class);
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
private final AzureAttributesFinderFeature attributes;
public AzureObjectListService(final AzureSession session, final OperationContext context) {
public AzureObjectListService(final AzureSession session) {
this.session = session;
this.context = context;
this.attributes = new AzureAttributesFinderFeature(session);
}
@Override
public AttributedList<Path> list(final Path directory, final ListProgressListener listener) throws BackgroundException {
try {
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(directory).getName());
final BlobContainerClient containerClient = session.getClient().getBlobContainerClient(containerService.getContainer(directory).getName());
final AttributedList<Path> children = new AttributedList<>();
ResultContinuation token = null;
ResultSegment<ListBlobItem> result;
PagedIterable<BlobItem> result;
String prefix = StringUtils.EMPTY;
if(!containerService.isContainer(directory)) {
prefix = containerService.getKey(directory);
@@ -81,47 +68,49 @@ public class AzureObjectListService implements ListService {
}
}
boolean hasDirectoryPlaceholder = containerService.isContainer(directory);
do {
final BlobRequestOptions options = new BlobRequestOptions();
result = container.listBlobsSegmented(prefix, false, EnumSet.noneOf(BlobListingDetails.class),
HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"), token, options, context);
for(ListBlobItem object : result.getResults()) {
if(new SimplePathPredicate(new Path(object.getUri().getPath(), EnumSet.of(Path.Type.directory))).test(directory)) {
log.debug("Skip placeholder key {}", object);
String continuationToken = null;
for(PagedResponse<BlobItem> response : containerClient.listBlobsByHierarchy(String.valueOf(Path.DELIMITER), new ListBlobsOptions()
.setDetails(new BlobListDetails().setRetrieveMetadata(true))
.setPrefix(prefix)
.setMaxResultsPerPage(HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize")), null).iterableByPage(continuationToken,
HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"))) {
for(BlobItem item : response.getElements()) {
if(StringUtils.equals(prefix, item.getName())) {
if(log.isDebugEnabled()) {
log.debug(String.format("Skip placeholder key %s", item));
}
hasDirectoryPlaceholder = true;
continue;
}
final PathAttributes attributes = new PathAttributes();
if(object instanceof CloudBlob) {
final CloudBlob blob = (CloudBlob) object;
attributes.setSize(blob.getProperties().getLength());
attributes.setModificationDate(blob.getProperties().getLastModified().getTime());
attributes.setETag(blob.getProperties().getEtag());
if(StringUtils.isNotBlank(blob.getProperties().getContentMD5())) {
attributes.setChecksum(Checksum.parse(Hex.encodeHexString(Base64.decodeBase64(blob.getProperties().getContentMD5()))));
final PathAttributes attr;
if(item.isPrefix()) {
attr = PathAttributes.EMPTY;
}
else {
attr = attributes.toAttributes(item.getProperties());
}
// A directory is designated by a delimiter character.
final EnumSet<Path.Type> types = object instanceof CloudBlobDirectory
final EnumSet<Path.Type> types = null != item.isPrefix() && item.isPrefix()
? EnumSet.of(Path.Type.directory, Path.Type.placeholder) : EnumSet.of(Path.Type.file);
final Path child = new Path(directory, PathNormalizer.name(object.getUri().getPath()), types, attributes);
final Path child = new Path(directory, PathNormalizer.name(item.getName()), types, attr);
children.add(child);
}
listener.chunk(directory, children);
token = result.getContinuationToken();
continuationToken = response.getContinuationToken();
if(StringUtils.isBlank(continuationToken)) {
break;
}
}
while(result.getHasMoreResults());
if(!hasDirectoryPlaceholder && children.isEmpty()) {
log.warn("No placeholder found for directory {}", directory);
if(log.isWarnEnabled()) {
log.warn(String.format("No placeholder found for directory %s", directory));
}
throw new NotfoundException(directory.getAbsolute());
}
return children;
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Listing directory {0} failed", e, directory);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
}
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.AbstractProtocol;
@@ -26,11 +23,17 @@ import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.Protocol;
import ch.cyberduck.core.Scheme;
import ch.cyberduck.core.synchronization.ChainedComparisonService;
import ch.cyberduck.core.synchronization.ChecksumComparisonService;
import ch.cyberduck.core.synchronization.ComparisonService;
import ch.cyberduck.core.synchronization.DefaultComparisonService;
import ch.cyberduck.core.synchronization.ETagComparisonService;
import ch.cyberduck.core.text.DefaultLexicographicOrderComparator;
import org.apache.commons.codec.binary.Base64;
import java.util.Comparator;
import com.microsoft.azure.storage.core.Base64;
import com.google.auto.service.AutoService;
@AutoService(Protocol.class)
@@ -80,7 +83,10 @@ public class AzureProtocol extends AbstractProtocol {
public boolean validate(final Credentials credentials, final LoginOptions options) {
if(super.validate(credentials, options)) {
if(options.password) {
return Base64.validateIsBase64String(credentials.getPassword());
if(credentials.getPassword().length() % 4 != 0) {
return false;
}
return Base64.isBase64(credentials.getPassword());
}
return true;
}
@@ -108,6 +114,9 @@ public class AzureProtocol extends AbstractProtocol {
if(type == PathContainerService.class) {
return (T) new DirectoryDelimiterPathContainerService();
}
if(type == ComparisonService.class) {
return (T) new DefaultComparisonService(new ChainedComparisonService(new ChecksumComparisonService(), new ETagComparisonService()), ComparisonService.disabled);
}
return super.getFeature(type);
}
}
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.ConnectionCallback;
@@ -23,86 +20,48 @@ import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Read;
import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.worker.DefaultExceptionMappingService;
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.io.input.ProxyInputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobInputStream;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.core.SR;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.models.BlobRange;
import com.azure.storage.blob.models.BlobRequestConditions;
public class AzureReadFeature implements Read {
private static final Logger log = LogManager.getLogger(AzureReadFeature.class);
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
public AzureReadFeature(final AzureSession session, final OperationContext context) {
public AzureReadFeature(final AzureSession session) {
this.session = session;
this.context = context;
}
@Override
public boolean offset(final Path file) {
return true;
}
@Override
public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
try {
final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlobReferenceFromServer(containerService.getKey(file));
if(0L == blob.getProperties().getLength()) {
return new NullInputStream(0L);
}
final BlobRequestOptions options = new BlobRequestOptions();
options.setConcurrentRequestCount(1);
final BlobInputStream in = blob.openInputStream(AccessCondition.generateEmptyCondition(), options, context);
if(status.isAppend()) {
try {
return StreamCopier.skip(in, status.getOffset());
return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file)).openInputStream(new BlobRange(status.getOffset()), new BlobRequestConditions());
}
catch(IndexOutOfBoundsException e) {
// If offset is invalid
throw new DefaultExceptionMappingService().map("Download {0} failed", e, file);
else {
return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file)).openInputStream();
}
}
return new ProxyInputStream(in) {
@Override
protected void handleIOException(final IOException e) throws IOException {
if(StringUtils.equals(SR.STREAM_CLOSED, e.getMessage())) {
log.warn("Ignore failure {}", e.getMessage());
return;
}
final Throwable cause = ExceptionUtils.getRootCause(e);
if(cause instanceof StorageException) {
throw new IOException(e.getMessage(), new AzureExceptionMappingService().map((StorageException) cause));
}
throw e;
}
};
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Download {0} failed", e, file);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
}
@@ -1,36 +1,31 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.CancellingListProgressListener;
import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostKeyCallback;
import ch.cyberduck.core.HostUrlProvider;
import ch.cyberduck.core.ListService;
import ch.cyberduck.core.LoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PreferencesUseragentProvider;
import ch.cyberduck.core.Scheme;
import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.azure.apache.ApacheHttpClient;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ListCanceledException;
import ch.cyberduck.core.exception.LoginFailureException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.AclPermission;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Copy;
@@ -45,46 +40,54 @@ import ch.cyberduck.core.features.Read;
import ch.cyberduck.core.features.Touch;
import ch.cyberduck.core.features.Upload;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.http.DisabledX509HostnameVerifier;
import ch.cyberduck.core.proxy.Proxy;
import ch.cyberduck.core.http.HttpSession;
import ch.cyberduck.core.proxy.ProxyFinder;
import ch.cyberduck.core.proxy.ProxyHostUrlProvider;
import ch.cyberduck.core.shared.DefaultHomeFinderService;
import ch.cyberduck.core.ssl.CustomTrustSSLProtocolSocketFactory;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DisabledX509TrustManager;
import ch.cyberduck.core.ssl.SSLSession;
import ch.cyberduck.core.ssl.X509KeyManager;
import ch.cyberduck.core.ssl.X509TrustManager;
import ch.cyberduck.core.threading.CancelCallback;
import org.apache.http.HttpHeaders;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.SendingRequestEvent;
import com.microsoft.azure.storage.StorageCredentials;
import com.microsoft.azure.storage.StorageCredentialsAccountAndKey;
import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature;
import com.microsoft.azure.storage.StorageEvent;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.azure.core.credential.AzureSasCredential;
import com.azure.core.exception.HttpResponseException;
import com.azure.core.http.HttpHeaderName;
import com.azure.core.http.HttpPipelineBuilder;
import com.azure.core.http.HttpPipelineCallContext;
import com.azure.core.http.HttpPipelineNextPolicy;
import com.azure.core.http.HttpPipelineNextSyncPolicy;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.policy.AddDatePolicy;
import com.azure.core.http.policy.AddHeadersPolicy;
import com.azure.core.http.policy.AzureSasCredentialPolicy;
import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
import com.azure.core.http.policy.RequestIdPolicy;
import com.azure.core.http.policy.UserAgentPolicy;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.blob.models.AccountKind;
import com.azure.storage.common.StorageSharedKeyCredential;
import com.azure.storage.common.implementation.Constants;
import com.azure.storage.common.policy.MetadataValidationPolicy;
import com.azure.storage.common.policy.RequestRetryOptions;
import com.azure.storage.common.policy.RequestRetryPolicy;
import com.azure.storage.common.policy.ResponseValidationPolicyBuilder;
import com.azure.storage.common.policy.StorageSharedKeyCredentialPolicy;
import reactor.core.publisher.Mono;
public class AzureSession extends SSLSession<CloudBlobClient> {
public class AzureSession extends HttpSession<BlobServiceClient> {
private static final Logger log = LogManager.getLogger(AzureSession.class);
private final OperationContext context
= new OperationContext();
private StorageEvent<SendingRequestEvent> listener;
private final CredentialsHttpPipelinePolicy authenticator
= new CredentialsHttpPipelinePolicy();
public AzureSession(final Host h) {
super(h, new DisabledX509TrustManager(), new DefaultX509KeyManager());
@@ -94,141 +97,149 @@ public class AzureSession extends SSLSession<CloudBlobClient> {
super(h, trust, key);
}
static {
HttpsURLConnection.setDefaultSSLSocketFactory(new CustomTrustSSLProtocolSocketFactory(new DisabledX509TrustManager(), new DefaultX509KeyManager()));
HttpsURLConnection.setDefaultHostnameVerifier(new DisabledX509HostnameVerifier());
HttpsURLConnection.setFollowRedirects(true);
@Override
protected BlobServiceClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) {
final HttpClientBuilder pool = builder.build(proxy, this, prompt);
return new BlobServiceClientBuilder()
.endpoint(new HostUrlProvider().withUsername(false).get(host))
.pipeline(new HttpPipelineBuilder()
.httpClient(new ApacheHttpClient(pool))
.policies(
new EmptyAuthenticationPolicy(),
new UserAgentPolicy(new PreferencesUseragentProvider().get()),
new RequestIdPolicy(),
new RequestRetryPolicy(new RequestRetryOptions()),
new AddDatePolicy(),
new AddHeadersPolicy(new com.azure.core.http.HttpHeaders(
Collections.singletonMap(HttpHeaders.USER_AGENT, new PreferencesUseragentProvider().get()))
),
new MetadataValidationPolicy(),
authenticator,
new ResponseValidationPolicyBuilder()
.addOptionalEcho(HttpHeaderName.fromString(Constants.HeaderConstants.CLIENT_REQUEST_ID))
.addOptionalEcho(HttpHeaderName.fromString(Constants.HeaderConstants.ENCRYPTION_KEY_SHA256))
.build()
)
.build())
.buildClient();
}
private static final class CredentialsHttpPipelinePolicy implements HttpPipelinePolicy {
private Credentials credentials = new Credentials();
public void setCredentials(final Credentials credentials) {
this.credentials = credentials;
}
@Override
protected CloudBlobClient connect(final ProxyFinder proxyfinder, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException {
try {
final StorageCredentials credentials;
if(host.getCredentials().isTokenAuthentication()) {
credentials = new StorageCredentialsSharedAccessSignature(host.getCredentials().getToken());
public Mono<HttpResponse> process(final HttpPipelineCallContext context, final HttpPipelineNextPolicy next) {
if(credentials.isTokenAuthentication()) {
return new AzureSasCredentialPolicy(new AzureSasCredential(
credentials.getToken())).process(context, next);
}
else {
credentials = new StorageCredentialsAccountAndKey(host.getCredentials().getUsername(), "null");
}
final URI uri = new URI(String.format("%s://%s", Scheme.https, host.getHostname()));
final CloudBlobClient client = new CloudBlobClient(uri, credentials);
client.setDirectoryDelimiter(String.valueOf(Path.DELIMITER));
context.setLoggingEnabled(true);
context.setLogger(LoggerFactory.getLogger(log.getName()));
context.setUserHeaders(new HashMap<>(Collections.singletonMap(
HttpHeaders.USER_AGENT, new PreferencesUseragentProvider().get()))
);
context.getSendingRequestEventHandler().addListener(listener = new StorageEvent<SendingRequestEvent>() {
@Override
public void eventOccurred(final SendingRequestEvent event) {
if(event.getConnectionObject() instanceof HttpsURLConnection) {
final HttpsURLConnection connection = (HttpsURLConnection) event.getConnectionObject();
connection.setSSLSocketFactory(new CustomTrustSSLProtocolSocketFactory(trust, key));
connection.setHostnameVerifier(new DisabledX509HostnameVerifier());
}
}
});
final Proxy proxy = proxyfinder.find(new ProxyHostUrlProvider().get(host));
switch(proxy.getType()) {
case SOCKS: {
log.info("Configured to use SOCKS proxy {}", proxyfinder);
final java.net.Proxy socksProxy = new java.net.Proxy(
java.net.Proxy.Type.SOCKS, new InetSocketAddress(proxy.getHostname(), proxy.getPort()));
context.setProxy(socksProxy);
break;
}
case HTTP:
case HTTPS: {
log.info("Configured to use HTTP proxy {}", proxyfinder);
final java.net.Proxy httpProxy = new java.net.Proxy(
java.net.Proxy.Type.HTTP, new InetSocketAddress(proxy.getHostname(), proxy.getPort()));
context.setProxy(httpProxy);
break;
}
}
return client;
}
catch(URISyntaxException e) {
throw new LoginFailureException(e.getMessage(), e);
return new StorageSharedKeyCredentialPolicy(new StorageSharedKeyCredential(
credentials.getUsername(), credentials.getPassword())).process(context, next);
}
}
@Override
public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException {
final StorageCredentials credentials = client.getCredentials();
if(host.getCredentials().isPasswordAuthentication()) {
// Update credentials
final StorageCredentialsAccountAndKey method = (StorageCredentialsAccountAndKey) credentials;
method.updateKey(host.getCredentials().getPassword());
}
// Fetch reference for directory to check login credentials
authenticator.setCredentials(host.getCredentials());
try {
new AzureListService(this, context).list(new DefaultHomeFinderService(this).find(), new CancellingListProgressListener());
final AccountKind kind = client.getAccountInfo().getAccountKind();
if(log.isInfoEnabled()) {
log.info(String.format("Connected to account of kind %s", kind));
}
catch(ListCanceledException e) {
// Success
}
catch(NotfoundException e) {
log.warn("Ignore failure {}", e.getMessage());
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map(e);
}
}
@Override
protected void logout() {
context.getSendingRequestEventHandler().removeListener(listener);
public void disconnect() throws BackgroundException {
try {
if(client != null) {
try {
((ApacheHttpClient) client.getHttpPipeline().getHttpClient()).getHttpClient().close();
}
catch(IOException e) {
throw new DefaultIOExceptionMappingService().map(e);
}
}
}
finally {
super.disconnect();
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T _getFeature(final Class<T> type) {
if(type == ListService.class) {
return (T) new AzureListService(this, context);
return (T) new AzureListService(this);
}
if(type == Read.class) {
return (T) new AzureReadFeature(this, context);
return (T) new AzureReadFeature(this);
}
if(type == Upload.class) {
return (T) new AzureUploadFeature(this, context);
return (T) new AzureUploadFeature(this);
}
if(type == Write.class) {
return (T) new AzureWriteFeature(this, context);
return (T) new AzureWriteFeature(this);
}
if(type == Directory.class) {
return (T) new AzureDirectoryFeature(this, context);
return (T) new AzureDirectoryFeature(this);
}
if(type == Delete.class) {
return (T) new AzureDeleteFeature(this, context);
return (T) new AzureDeleteFeature(this);
}
if(type == Headers.class) {
return (T) new AzureMetadataFeature(this, context);
return (T) new AzureMetadataFeature(this);
}
if(type == Metadata.class) {
return (T) new AzureMetadataFeature(this, context);
return (T) new AzureMetadataFeature(this);
}
if(type == Find.class) {
return (T) new AzureFindFeature(this, context);
return (T) new AzureFindFeature(this);
}
if(type == AttributesFinder.class) {
return (T) new AzureAttributesFinderFeature(this, context);
return (T) new AzureAttributesFinderFeature(this);
}
if(type == Logging.class) {
return (T) new AzureLoggingFeature(this, context);
return (T) new AzureLoggingFeature(this);
}
if(type == Move.class) {
return (T) new AzureMoveFeature(this, context);
return (T) new AzureMoveFeature(this);
}
if(type == Copy.class) {
return (T) new AzureCopyFeature(this, context);
return (T) new AzureCopyFeature(this);
}
if(type == Touch.class) {
return (T) new AzureTouchFeature(this, context);
return (T) new AzureTouchFeature(this);
}
if(type == UrlProvider.class) {
return (T) new AzureUrlProvider(this);
}
if(type == AclPermission.class) {
return (T) new AzureAclPermissionFeature(this, context);
return (T) new AzureAclPermissionFeature(this);
}
return super._getFeature(type);
}
private static final class EmptyAuthenticationPolicy extends BearerTokenAuthenticationPolicy {
public EmptyAuthenticationPolicy() {
super(request -> Mono.empty());
}
@Override
public HttpResponse processSync(final HttpPipelineCallContext context, final HttpPipelineNextSyncPolicy next) {
return next.processSync();
}
@Override
public Mono<HttpResponse> process(final HttpPipelineCallContext context, final HttpPipelineNextPolicy next) {
return next.process();
}
}
}
@@ -1,27 +1,25 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.shared.DefaultTouchFeature;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -29,12 +27,10 @@ import org.apache.commons.io.input.NullInputStream;
import java.text.MessageFormat;
import com.microsoft.azure.storage.OperationContext;
public class AzureTouchFeature extends DefaultTouchFeature<Void> {
public AzureTouchFeature(final AzureSession session, final OperationContext context) {
super(new AzureWriteFeature(session, context));
public AzureTouchFeature(final AzureSession session) {
super(session);
}
@Override
@@ -45,8 +41,7 @@ public class AzureTouchFeature extends DefaultTouchFeature<Void> {
}
@Override
public Path touch(final Path file, final TransferStatus status) throws BackgroundException {
status.setChecksum(write.checksum(file, status).compute(new NullInputStream(0L), status));
return super.touch(file, status);
public Path touch(final Write<Void> writer, final Path file, final TransferStatus status) throws BackgroundException {
return super.touch(writer, file, status.setChecksum(writer.checksum(file, status).compute(new NullInputStream(0L), status)));
}
}
@@ -22,25 +22,22 @@ import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.shared.DefaultUploadFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.blob.BlobType;
import com.azure.storage.blob.models.BlobType;
public class AzureUploadFeature extends DefaultUploadFeature {
public class AzureUploadFeature extends DefaultUploadFeature<Void> {
private final AzureSession session;
private final OperationContext context;
public AzureUploadFeature(final AzureSession session, final OperationContext context) {
super(new AzureWriteFeature(session, context));
public AzureUploadFeature(final AzureSession session) {
super(session);
this.session = session;
this.context = context;
}
@Override
public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException {
final Write.Append append = new Write.Append(status.isExists()).withStatus(status);
if(append.append) {
final PathAttributes attr = new AzureAttributesFinderFeature(session, context).find(file);
final PathAttributes attr = new AzureAttributesFinderFeature(session).find(file);
if(BlobType.APPEND_BLOB == BlobType.valueOf(attr.getCustom().get(AzureAttributesFinderFeature.KEY_BLOB_TYPE))) {
return append;
}
@@ -1,27 +1,26 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.DescriptiveUrl;
import ch.cyberduck.core.DescriptiveUrlBag;
import ch.cyberduck.core.DirectoryDelimiterPathContainerService;
import ch.cyberduck.core.HostPasswordStore;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.PasswordStoreFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.Scheme;
@@ -30,35 +29,46 @@ import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.UserDateFormatterFactory;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.Calendar;
import java.util.EnumSet;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.SharedAccessBlobPermissions;
import com.microsoft.azure.storage.blob.SharedAccessBlobPolicy;
import com.azure.storage.blob.implementation.util.BlobSasImplUtil;
import com.azure.storage.blob.sas.BlobSasPermission;
import com.azure.storage.blob.sas.BlobServiceSasSignatureValues;
import com.azure.storage.common.StorageSharedKeyCredential;
public class AzureUrlProvider implements UrlProvider {
private static final Logger log = LogManager.getLogger(AzureUrlProvider.class);
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
private final AzureSession session;
private final HostPasswordStore store;
public AzureUrlProvider(final AzureSession session) {
this(session, PasswordStoreFactory.get());
}
public AzureUrlProvider(final AzureSession session, final HostPasswordStore store) {
this.session = session;
this.store = store;
}
@Override
public DescriptiveUrlBag toUrl(final Path file, final EnumSet<DescriptiveUrl.Type> types) {
final DescriptiveUrlBag list = new DescriptiveUrlBag();
if(types.contains(DescriptiveUrl.Type.signed)) {
// In one hour
list.add(this.toSignedUrl(file, (int) TimeUnit.HOURS.toSeconds(1)));
// Default signed URL expiring in 24 hours.
@@ -70,6 +80,7 @@ public class AzureUrlProvider implements UrlProvider {
list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(30)));
// 1 Year
list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(365)));
}
return list;
}
@@ -77,17 +88,17 @@ public class AzureUrlProvider implements UrlProvider {
return new SharedAccessSignatureUrl(file, this.getExpiry(seconds));
}
protected Calendar getExpiry(final int seconds) {
protected Long getExpiry(final int seconds) {
final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
expiry.add(Calendar.SECOND, seconds);
return expiry;
return expiry.getTimeInMillis();
}
private final class SharedAccessSignatureUrl extends DescriptiveUrl {
private final Path file;
private final Calendar expiry;
private final Long expiry;
public SharedAccessSignatureUrl(final Path file, final Calendar expiry) {
public SharedAccessSignatureUrl(final Path file, final Long expiry) {
super(EMPTY);
this.file = file;
this.expiry = expiry;
@@ -95,38 +106,25 @@ public class AzureUrlProvider implements UrlProvider {
@Override
public String getUrl() {
try {
if(!session.isConnected()) {
final String secret = store.findLoginPassword(session.getHost());
if(StringUtils.isBlank(secret)) {
if(log.isWarnEnabled()) {
log.warn("No secret found in password store required to sign temporary URL");
}
return DescriptiveUrl.EMPTY.getUrl();
}
final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName());
final String token;
if(containerService.isContainer(file)) {
token = container.generateSharedAccessSignature(this.getPolicy(expiry), null);
}
else {
final CloudBlob blob = container.getBlobReferenceFromServer(containerService.getKey(file));
token = blob.generateSharedAccessSignature(this.getPolicy(expiry), null);
}
final String token = new BlobSasImplUtil(new BlobServiceSasSignatureValues(
OffsetDateTime.now().plus(Duration.ofMillis(expiry)), new BlobSasPermission().setReadPermission(true)), containerService.getContainer(file).getName())
.generateSas(new StorageSharedKeyCredential(session.getHost().getCredentials().getUsername(),
secret), null);
return String.format("%s://%s%s?%s",
Scheme.https.name(), session.getHost().getHostname(), URIEncoder.encode(file.getAbsolute()), token);
}
catch(InvalidKeyException | URISyntaxException | StorageException e) {
return DescriptiveUrl.EMPTY.getUrl();
}
}
private SharedAccessBlobPolicy getPolicy(final Calendar expiry) {
final SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy();
policy.setSharedAccessExpiryTime(expiry.getTime());
policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ));
return policy;
}
@Override
public String getHelp() {
return MessageFormat.format(LocaleFactory.localizedString("Expires {0}", "S3"),
UserDateFormatterFactory.get().getMediumFormat(expiry.getTimeInMillis()));
UserDateFormatterFactory.get().getMediumFormat(expiry));
}
@Override
@@ -1,21 +1,18 @@
package ch.cyberduck.core.azure;
/*
* Copyright (c) 2002-2014 David Kocher. All rights reserved.
* http://cyberduck.io/
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* Bug fixes, suggestions and comments should be sent to:
* feedback@cyberduck.io
*/
import ch.cyberduck.core.ConnectionCallback;
@@ -24,7 +21,8 @@ import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.io.Checksum;
import ch.cyberduck.core.io.ChecksumCompute;
@@ -33,47 +31,60 @@ import ch.cyberduck.core.io.HashAlgorithm;
import ch.cyberduck.core.io.StatusOutputStream;
import ch.cyberduck.core.io.VoidStatusOutputStream;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.preferences.Preferences;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.transfer.TransferStatus;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpHeaders;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobOutputStream;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.BlobType;
import com.microsoft.azure.storage.blob.CloudAppendBlob;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import com.microsoft.azure.storage.core.SR;
import com.azure.core.exception.HttpResponseException;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.models.BlobHttpHeaders;
import com.azure.storage.blob.models.BlobType;
import com.azure.storage.blob.options.AppendBlobCreateOptions;
import com.azure.storage.blob.options.BlockBlobOutputStreamOptions;
import com.azure.storage.blob.specialized.AppendBlobClient;
import com.azure.storage.blob.specialized.BlobOutputStream;
import com.azure.storage.blob.specialized.BlockBlobClient;
import com.azure.storage.common.implementation.Constants;
public class AzureWriteFeature implements Write<Void> {
private static final Logger log = LogManager.getLogger(AzureWriteFeature.class);
private final AzureSession session;
private final OperationContext context;
private final PathContainerService containerService
= new DirectoryDelimiterPathContainerService();
private final Preferences preferences
= PreferencesFactory.get();
private final BlobType blobType;
public AzureWriteFeature(final AzureSession session, final OperationContext context) {
this(session, BlobType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("azure.upload.blobtype")), context);
public AzureWriteFeature(final AzureSession session) {
this(session, BlobType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("azure.upload.blobtype")));
}
public AzureWriteFeature(final AzureSession session, final BlobType blobType, final OperationContext context) {
public AzureWriteFeature(final AzureSession session, final BlobType blobType) {
this.session = session;
this.blobType = blobType;
}
public AzureWriteFeature(final AzureSession session, final Find finder, final AttributesFinder attributes) {
this(session, finder, attributes, BlobType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("azure.upload.blobtype")));
}
public AzureWriteFeature(final AzureSession session, final Find finder, final AttributesFinder attributes, final BlobType blobType) {
this.session = session;
this.blobType = blobType;
this.context = context;
}
@Override
@@ -84,112 +95,110 @@ public class AzureWriteFeature implements Write<Void> {
@Override
public StatusOutputStream<Void> write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
try {
final CloudBlob blob;
final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file));
final BlobHttpHeaders headers = new BlobHttpHeaders();
if(StringUtils.isNotBlank(status.getMime())) {
headers.setContentType(status.getMime());
}
final Map<String, String> metadata = new HashMap<>(status.getMetadata());
for(final Map.Entry<String, String> m : metadata.entrySet()) {
if(HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(m.getKey())) {
// Update properties
headers.setCacheControl(m.getValue());
}
if(HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(m.getKey())) {
// Update properties
headers.setContentType(m.getValue());
}
}
metadata.remove(HttpHeaders.CACHE_CONTROL);
metadata.remove(HttpHeaders.CONTENT_TYPE);
final Checksum checksum = status.getChecksum();
if(Checksum.NONE != checksum) {
switch(checksum.algorithm) {
case md5:
try {
headers.setContentMd5(Hex.decodeHex(status.getChecksum().hash.toCharArray()));
}
catch(DecoderException e) {
// Ignore
}
break;
}
}
final BlobOutputStream out;
if(status.isExists()) {
if(HostPreferencesFactory.get(session.getHost()).getBoolean("azure.upload.snapshot")) {
session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlobReferenceFromServer(containerService.getKey(file)).createSnapshot();
if(preferences.getBoolean("azure.upload.snapshot")) {
session.getClient().getBlobContainerClient(containerService.getContainer(file).getName())
.getBlobClient(containerService.getKey(file)).createSnapshot();
}
if(status.isAppend()) {
// Existing append blob type
blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getAppendBlobReference(containerService.getKey(file));
out = client.getAppendBlobClient().getBlobOutputStream();
}
else {
// Existing block blob type
final PathAttributes attr = new AzureAttributesFinderFeature(session, context).find(file);
final PathAttributes attr = new AzureAttributesFinderFeature(session).find(file);
if(BlobType.APPEND_BLOB == BlobType.valueOf(attr.getCustom().get(AzureAttributesFinderFeature.KEY_BLOB_TYPE))) {
blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getAppendBlobReference(containerService.getKey(file));
out = client.getAppendBlobClient().getBlobOutputStream(true);
}
else {
blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlockBlobReference(containerService.getKey(file));
final BlockBlobOutputStreamOptions options = new BlockBlobOutputStreamOptions()
.setMetadata(metadata)
.setHeaders(headers)
.setMetadata(metadata);
out = client.getBlockBlobClient().getBlobOutputStream(options);
}
}
}
else {
// Create new blob with default type set in defaults
switch(blobType) {
case APPEND_BLOB:
blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getAppendBlobReference(containerService.getKey(file));
break;
default:
blob = session.getClient().getContainerReference(containerService.getContainer(file).getName())
.getBlockBlobReference(containerService.getKey(file));
}
}
if(StringUtils.isNotBlank(status.getMime())) {
blob.getProperties().setContentType(status.getMime());
}
// Add previous metadata when overwriting file
final HashMap<String, String> headers = new HashMap<>(status.getMetadata());
blob.setMetadata(headers);
// Remove additional headers not allowed in metadata and move to properties
if(headers.containsKey(HttpHeaders.CACHE_CONTROL)) {
blob.getProperties().setCacheControl(headers.get(HttpHeaders.CACHE_CONTROL));
headers.remove(HttpHeaders.CACHE_CONTROL);
}
if(headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
blob.getProperties().setContentType(headers.get(HttpHeaders.CONTENT_TYPE));
headers.remove(HttpHeaders.CONTENT_TYPE);
}
final Checksum checksum = status.getChecksum();
if(Checksum.NONE != checksum) {
switch(checksum.algorithm) {
case md5:
headers.remove(HttpHeaders.CONTENT_MD5);
blob.getProperties().setContentMD5(status.getChecksum().base64);
case APPEND_BLOB: {
final AppendBlobClient append = client.getAppendBlobClient();
final AppendBlobCreateOptions options = new AppendBlobCreateOptions()
.setMetadata(metadata)
.setHeaders(headers)
.setMetadata(metadata);
append.createWithResponse(options, null, null);
out = append.getBlobOutputStream();
break;
}
default: {
final BlockBlobClient block = client.getBlockBlobClient();
final BlockBlobOutputStreamOptions options = new BlockBlobOutputStreamOptions()
.setMetadata(metadata)
.setHeaders(headers)
.setMetadata(metadata);
out = block.getBlobOutputStream(options);
break;
}
final BlobRequestOptions options = new BlobRequestOptions();
options.setConcurrentRequestCount(1);
options.setStoreBlobContentMD5(HostPreferencesFactory.get(session.getHost()).getBoolean("azure.upload.md5"));
final BlobOutputStream out;
if(status.isAppend()) {
options.setStoreBlobContentMD5(false);
if(blob instanceof CloudAppendBlob) {
out = ((CloudAppendBlob) blob).openWriteExisting(AccessCondition.generateEmptyCondition(), options, context);
}
else {
throw new NotfoundException(String.format("Unexpected blob type for %s", blob.getName()));
}
}
else {
if(blob instanceof CloudAppendBlob) {
out = ((CloudAppendBlob) blob).openWriteNew(AccessCondition.generateEmptyCondition(), options, context);
}
else {
out = ((CloudBlockBlob) blob).openOutputStream(AccessCondition.generateEmptyCondition(), options, context);
}
}
return new VoidStatusOutputStream(out) {
@Override
protected void handleIOException(final IOException e) throws IOException {
if(StringUtils.equals(SR.STREAM_CLOSED, e.getMessage())) {
log.warn("Ignore failure {}", e.getMessage());
return;
public void close() throws IOException {
try {
super.close();
}
final Throwable cause = ExceptionUtils.getRootCause(e);
if(cause instanceof StorageException) {
throw new IOException(e.getMessage(), new AzureExceptionMappingService().map((StorageException) cause));
catch(RuntimeException e) {
this.handleIOException(new IOException(e.getMessage(), e));
}
}
@Override
protected void handleIOException(final IOException e) throws IOException {
if(StringUtils.equals(Constants.STREAM_CLOSED, e.getMessage())) {
log.warn(String.format("Ignore failure %s", e));
return;
}
throw e;
}
};
}
catch(StorageException e) {
catch(HttpResponseException e) {
throw new AzureExceptionMappingService().map("Upload {0} failed", e, file);
}
catch(URISyntaxException e) {
throw new NotfoundException(e.getMessage(), e);
}
}
@Override
public EnumSet<Flags> features(final Path file) {
return EnumSet.of(Flags.checksum, Flags.mime);
}
}
@@ -0,0 +1,85 @@
package ch.cyberduck.core.azure.apache;
/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*/
import com.azure.core.http.*;
import com.azure.core.util.FluxUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
public class ApacheHttpClient implements HttpClient {
private final CloseableHttpClient httpClient;
public ApacheHttpClient(final HttpClientBuilder builder) {
this.httpClient = builder.build();
}
public CloseableHttpClient getHttpClient() {
return httpClient;
}
public Mono<HttpResponse> send(final HttpRequest azureRequest) {
try {
ApacheHttpRequest apacheRequest = new ApacheHttpRequest(azureRequest.getHttpMethod(), azureRequest.getUrl(),
azureRequest.getHeaders());
Mono<byte[]> bodyMono = (azureRequest.getBody() != null)
? FluxUtil.collectBytesInByteBufferStream(azureRequest.getBody())
: Mono.just(new byte[0]);
return bodyMono.flatMap(bodyBytes -> {
apacheRequest.setEntity(new ByteArrayEntity(bodyBytes));
try {
return Mono.just(new ApacheHttpResponse(azureRequest, httpClient.execute(apacheRequest)));
} catch (IOException ex) {
return Mono.error(ex);
}
});
} catch (URISyntaxException e) {
return Mono.error(e);
}
}
private static final class ApacheHttpRequest extends HttpEntityEnclosingRequestBase {
private final String method;
private ApacheHttpRequest(HttpMethod method, URL url, HttpHeaders headers) throws URISyntaxException {
this.method = method.name();
setURI(url.toURI());
headers.stream().forEach(this::accept);
}
@Override
public String getMethod() {
return method;
}
private void accept(final HttpHeader header) {
if (!StringUtils.equalsIgnoreCase(header.getName(), HTTP.CONTENT_LEN)) {
this.addHeader(header.getName(), header.getValue());
}
}
}
}
@@ -0,0 +1,79 @@
package ch.cyberduck.core.azure.apache;
/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*/
import com.azure.core.http.HttpHeaders;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
final class ApacheHttpResponse extends HttpResponse {
private final int statusCode;
private final HttpHeaders headers;
private final HttpEntity entity;
protected ApacheHttpResponse(HttpRequest request, org.apache.http.HttpResponse apacheResponse) {
super(request);
this.statusCode = apacheResponse.getStatusLine().getStatusCode();
this.headers = new HttpHeaders();
Arrays.stream(apacheResponse.getAllHeaders())
.forEach(header -> headers.put(header.getName(), header.getValue()));
this.entity = apacheResponse.getEntity();
}
public int getStatusCode() {
return statusCode;
}
public String getHeaderValue(String s) {
return headers.getValue(s);
}
public HttpHeaders getHeaders() {
return headers;
}
public Flux<ByteBuffer> getBody() {
return getBodyAsByteArray().map(ByteBuffer::wrap).flux();
}
public Mono<byte[]> getBodyAsByteArray() {
if (null == entity) {
return Mono.empty();
}
try {
return Mono.just(EntityUtils.toByteArray(entity));
} catch (IOException e) {
return Mono.error(e);
}
}
public Mono<String> getBodyAsString() {
return getBodyAsByteArray().map(String::new);
}
public Mono<String> getBodyAsString(Charset charset) {
return getBodyAsByteArray().map(bytes -> new String(bytes, charset));
}
}
@@ -1,6 +1,4 @@
package ch.cyberduck.core.azure;
/*
package ch.cyberduck.core.azure;/*
* Copyright (c) 2002-2021 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
@@ -23,18 +21,13 @@ import ch.cyberduck.core.DisabledPasswordStore;
import ch.cyberduck.core.DisabledProgressListener;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.LoginConnectionService;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DisabledX509TrustManager;
import ch.cyberduck.test.VaultTest;
import org.junit.After;
import org.junit.Before;
import org.junit.runners.Parameterized;
import static org.junit.Assert.fail;
public class AbstractAzureTest extends VaultTest {
protected AzureSession session;
@@ -57,15 +50,8 @@ public class AbstractAzureTest extends VaultTest {
final Host host = new Host(new AzureProtocol(), "kahy9boj3eib.blob.core.windows.net", new Credentials(
PROPERTIES.get("azure.user"), PROPERTIES.get("azure.password")
));
session = new AzureSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() {
@Override
public Credentials prompt(final Host bookmark, final String username, final String title, final String reason, final LoginOptions options) {
fail(reason);
return null;
}
}, new DisabledHostKeyCallback(),
new DisabledPasswordStore(), new DisabledProgressListener());
login.check(session, new DisabledCancelCallback());
session = new AzureSession(host);
new LoginConnectionService(new DisabledLoginCallback(), new DisabledHostKeyCallback(),
new DisabledPasswordStore(), new DisabledProgressListener()).connect(session, new DisabledCancelCallback());
}
}
@@ -1,5 +1,6 @@
package ch.cyberduck.core.azure;
import ch.cyberduck.core.Acl;
import ch.cyberduck.core.AsciiRandomStringService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.NotfoundException;
@@ -10,6 +11,8 @@ import org.junit.experimental.categories.Category;
import java.util.EnumSet;
import static org.junit.Assert.assertEquals;
@Category(IntegrationTest.class)
public class AzureAclPermissionFeatureTest extends AbstractAzureTest {
@@ -17,7 +20,24 @@ public class AzureAclPermissionFeatureTest extends AbstractAzureTest {
@Test(expected = NotfoundException.class)
public void testReadNotFoundContainer() throws Exception {
final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory));
final AzureAclPermissionFeature f = new AzureAclPermissionFeature(session, null);
final AzureAclPermissionFeature f = new AzureAclPermissionFeature(session);
f.getPermission(container);
}
@Test
public void testWriteContainer() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
final AzureAclPermissionFeature f = new AzureAclPermissionFeature(session);
{
final Acl acl = new Acl();
acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE), new Acl.Role(Acl.Role.READ));
f.setPermission(container, acl);
assertEquals(acl, f.getPermission(container));
}
{
final Acl acl = new Acl();
f.setPermission(container, acl);
assertEquals(acl, f.getPermission(container));
}
}
}
@@ -9,6 +9,7 @@ import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.io.MD5ChecksumCompute;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -27,14 +28,14 @@ public class AzureAttributesFinderFeatureTest extends AbstractAzureTest {
@Test
public void testFindRoot() throws Exception {
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null);
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session);
assertEquals(PathAttributes.EMPTY, f.find(new Path("/", EnumSet.of(Path.Type.directory))));
}
@Test(expected = NotfoundException.class)
public void testNotFound() throws Exception {
final Path container = new Path(StringUtils.lowerCase(new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume));
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null);
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session);
f.find(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)));
}
@@ -42,49 +43,51 @@ public class AzureAttributesFinderFeatureTest extends AbstractAzureTest {
public void testFind() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null);
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus()
.setChecksum(new MD5ChecksumCompute().compute("")));
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session);
final PathAttributes attributes = f.find(test);
assertEquals(0L, attributes.getSize());
assertEquals("d41d8cd98f00b204e9800998ecf8427e", attributes.getChecksum().hash);
assertNotNull(attributes.getETag());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testFindContainer() throws Exception {
final Path container = new Path(new AlphanumericRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume));
new AzureDirectoryFeature(session, null).mkdir(container, new TransferStatus());
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null);
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), container, new TransferStatus());
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session);
final PathAttributes attributes = f.find(container);
assertNotEquals(PathAttributes.EMPTY, attributes);
assertNotNull(attributes.getETag());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testMissingPlaceholder() throws Exception {
final Path container = new AzureDirectoryFeature(session, null).mkdir(
new Path(new AlphanumericRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path container = new AzureDirectoryFeature(session).mkdir(
new AzureWriteFeature(session), new Path(new AlphanumericRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final String prefix = new AlphanumericRandomStringService().random();
final Path intermediate = new Path(container, prefix, EnumSet.of(Path.Type.directory));
final Path directory = new AzureDirectoryFeature(session, null).mkdir(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(directory));
final Path test = new AzureTouchFeature(session, null).touch(
new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null);
final Path directory = new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session).find(directory));
final Path test = new AzureTouchFeature(session).touch(
new AzureWriteFeature(session), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session);
final PathAttributes attributes = f.find(container);
assertNotEquals(PathAttributes.EMPTY, attributes);
assertNotNull(attributes.getETag());
assertNotNull(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test)));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertNotNull(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test)));
assertNotNull(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test)));
new AzureDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertNotNull(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test)));
// Still found as prefix
assertNotNull(new AzureObjectListService(session, null).list(container, new DisabledListProgressListener()).find(new DefaultPathPredicate(intermediate)));
assertNotNull(new AzureObjectListService(session, null).list(intermediate, new DisabledListProgressListener()).find(new DefaultPathPredicate(directory)));
assertNotNull(new AzureObjectListService(session).list(container, new DisabledListProgressListener()).find(new DefaultPathPredicate(intermediate)));
assertNotNull(new AzureObjectListService(session).list(intermediate, new DisabledListProgressListener()).find(new DefaultPathPredicate(directory)));
// Ignore 404 failures
assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session, null).find(directory));
assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session, null).find(intermediate));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session).find(directory));
assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session).find(intermediate));
new AzureDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -4,11 +4,14 @@ import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.DisabledConnectionCallback;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.exception.UnsupportedException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.io.DisabledStreamListener;
import ch.cyberduck.core.io.StreamCancelation;
import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.io.StreamProgress;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -31,10 +34,9 @@ public class AzureCopyFeatureTest extends AbstractAzureTest {
@Test
public void testCopy() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new AzureTouchFeature(session, null).touch(
new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
Thread.sleep(1000L);
final AzureCopyFeature feature = new AzureCopyFeature(session, null);
final Path test = new AzureTouchFeature(session).touch(
new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final AzureCopyFeature feature = new AzureCopyFeature(session);
assertThrows(UnsupportedException.class, () -> feature.preflight(container, Optional.of(test)));
try {
feature.preflight(container, Optional.of(test));
@@ -45,30 +47,30 @@ public class AzureCopyFeatureTest extends AbstractAzureTest {
}
final Path copy = feature.copy(test,
new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener());
assertEquals(test.attributes().getChecksum(), copy.attributes().getChecksum());
assertNotEquals(new AzureAttributesFinderFeature(session, null).find(test).getModificationDate(), new AzureAttributesFinderFeature(session, null).find(copy).getModificationDate());
assertTrue(new AzureFindFeature(session, null).find(test));
assertTrue(new AzureFindFeature(session, null).find(copy));
new AzureDeleteFeature(session, null).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertEquals(PathAttributes.EMPTY, copy.attributes());
final PathAttributes sourceAttr = new AzureAttributesFinderFeature(session).find(test);
final PathAttributes copyAttr = new AzureAttributesFinderFeature(session).find(copy);
assertNotEquals(sourceAttr.getETag(), copyAttr.getETag());
new AzureDeleteFeature(session).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testCopyToExistingFile() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
new AzureDirectoryFeature(session, null).mkdir(folder, new TransferStatus());
final Path test = new AzureTouchFeature(session, null).touch(
new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), folder, new TransferStatus());
final Path test = new AzureTouchFeature(session).touch(
new AzureWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final byte[] content = RandomUtils.nextBytes(1023);
final OutputStream out = new AzureWriteFeature(session, null).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out);
final Path copy = new AzureTouchFeature(session, null).touch(
new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new AzureCopyFeature(session, null).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener());
assertEquals(1023L, new AzureAttributesFinderFeature(session, null).find(copy).getSize());
final OutputStream out = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true).setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out);
final Path copy = new AzureTouchFeature(session).touch(
new AzureWriteFeature(session), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new AzureCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener());
assertEquals(1023L, new AzureAttributesFinderFeature(session).find(copy).getSize());
final Find find = new DefaultFindFeature(session);
assertTrue(find.find(test));
assertTrue(find.find(copy));
new AzureDeleteFeature(session, null).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -25,33 +25,33 @@ public class AzureDeleteFeatureTest extends AbstractAzureTest {
@Test(expected = NotfoundException.class)
public void testDeleteNotFoundBucket() throws Exception {
final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test(expected = NotfoundException.class)
public void testDeleteNotFoundKey() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testDeletePlaceholder() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(test));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session, null).find(test));
final Path test = new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session).find(test));
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session).find(test));
}
@Test
public void testDeleteKey() throws Exception {
final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume));
new AzureDirectoryFeature(session, null).mkdir(container, new TransferStatus());
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), container, new TransferStatus());
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(test));
new AzureDeleteFeature(session, null).delete(Arrays.asList(container, test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session, null).find(test));
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(test));
new AzureDeleteFeature(session).delete(Arrays.asList(container, test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session).find(test));
}
}
@@ -24,40 +24,42 @@ public class AzureDirectoryFeatureTest extends AbstractAzureTest {
@Test
public void testCreateContainer() throws Exception {
final AzureDirectoryFeature feature = new AzureDirectoryFeature(session, null);
final Path container = feature.mkdir(new Path(new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(container));
assertThrows(ConflictException.class, () -> feature.mkdir(container, new TransferStatus()));
new AzureTouchFeature(session, null).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session, null).find(container));
final AzureDirectoryFeature feature = new AzureDirectoryFeature(session);
final Path container = feature.mkdir(new AzureWriteFeature(session), new Path(new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session).find(container));
assertEquals(PathAttributes.EMPTY, container.attributes());
assertThrows(ConflictException.class, () -> feature.mkdir(new AzureWriteFeature(session), container, new TransferStatus()));
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session).find(container));
}
@Test(expected = InteroperabilityException.class)
public void testCreateContainerInvalidName() throws Exception {
final Path container = new Path("untitled folder", EnumSet.of(Path.Type.directory));
final AzureDirectoryFeature feature = new AzureDirectoryFeature(session, null);
final AzureDirectoryFeature feature = new AzureDirectoryFeature(session);
assertFalse(feature.isSupported(container.getParent(), container.getName()));
assertThrows(InvalidFilenameException.class, () -> feature.preflight(container.getParent(), container.getName()));
feature.mkdir(container, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(container));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session, null).find(container));
feature.mkdir(new AzureWriteFeature(session), container, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(container));
new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session).find(container));
}
@Test
public void testCreatePlaceholder() throws Exception {
final Path container = new Path("/cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
final Path placeholder = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(),
final Path placeholder = new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(),
EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(placeholder.getType().contains(Path.Type.placeholder));
assertTrue(new AzureFindFeature(session, null).find(placeholder));
assertTrue(new AzureFindFeature(session).find(placeholder));
assertEquals(PathAttributes.EMPTY, placeholder.attributes());
final Path file = new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(file, new TransferStatus());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), file, new TransferStatus());
new AzureDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback());
// Still find common prefix
assertTrue(new AzureFindFeature(session, null).find(placeholder));
assertEquals(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session, null).find(placeholder));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new AzureFindFeature(session).find(placeholder));
assertEquals(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session).find(placeholder));
new AzureDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -41,55 +41,56 @@ public class AzureFindFeatureTest extends AbstractAzureTest {
@Test
public void testFindNotFound() throws Exception {
assertFalse(new AzureFindFeature(session, null).find(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file))));
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
assertFalse(new AzureFindFeature(session).find(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file))));
}
@Test
public void testFindHome() throws Exception {
assertTrue(new AzureFindFeature(session, null).find(new DefaultHomeFinderService(session).find()));
assertTrue(new AzureFindFeature(session).find(new DefaultHomeFinderService(session).find()));
}
@Test
public void testFindDirectory() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new AzureDirectoryFeature(session, null).mkdir(
new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(folder));
assertFalse(new AzureFindFeature(session, null).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file))));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback());
final Path folder = new AzureDirectoryFeature(session).mkdir(
new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureFindFeature(session).find(folder));
assertFalse(new AzureFindFeature(session).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file))));
new AzureDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testFindFile() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(file, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(file));
assertFalse(new AzureFindFeature(session, null).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory))));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), file, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(file));
assertFalse(new AzureFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory))));
new AzureDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testFindCommonPrefix() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
assertTrue(new AzureFindFeature(session, null).find(container));
assertTrue(new AzureFindFeature(session).find(container));
final String prefix = new AlphanumericRandomStringService().random();
final Path intermediate = new Path(container, prefix, EnumSet.of(Path.Type.directory));
final Path test = new AzureTouchFeature(session, null).touch(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(test));
assertFalse(new AzureFindFeature(session, null).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory))));
assertTrue(new AzureFindFeature(session, null).find(intermediate));
final Path test = new AzureTouchFeature(session).touch(new AzureWriteFeature(session), new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new AzureFindFeature(session).find(test));
assertFalse(new AzureFindFeature(session).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory))));
assertTrue(new AzureFindFeature(session).find(intermediate));
// Ignore 404 for placeholder and search for common prefix
assertTrue(new AzureFindFeature(session, null).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder))));
assertTrue(new AzureObjectListService(session, null).list(intermediate,
assertTrue(new AzureFindFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder))));
assertTrue(new AzureObjectListService(session).list(intermediate,
new DisabledListProgressListener()).contains(test));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session, null).find(test));
assertFalse(new AzureFindFeature(session, null).find(intermediate));
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new AzureFindFeature(session).find(test));
assertFalse(new AzureFindFeature(session).find(intermediate));
final PathCache cache = new PathCache(1);
final Path directory = new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder));
assertFalse(new CachingFindFeature(session, cache).find(directory));
assertTrue(cache.isCached(directory.getParent()));
assertFalse(new AzureFindFeature(session, null).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder))));
assertFalse(new CachingFindFeature(session, cache, new AzureFindFeature(session)).find(directory));
assertFalse(cache.isCached(directory));
assertFalse(new AzureFindFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder))));
}
}
@@ -18,7 +18,7 @@ public class AzureLoggingFeatureTest extends AbstractAzureTest {
@Test
public void testSetConfiguration() throws Exception {
final Path container = new Path("/cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
final AzureLoggingFeature feature = new AzureLoggingFeature(session, null);
final AzureLoggingFeature feature = new AzureLoggingFeature(session);
feature.setConfiguration(container, new LoggingConfiguration(false));
assertFalse(feature.getConfiguration(container).isEnabled());
feature.setConfiguration(container, new LoggingConfiguration(true));
@@ -23,31 +23,27 @@ public class AzureMetadataFeatureTest extends AbstractAzureTest {
public void testSetMetadata() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
final TransferStatus status = new TransferStatus();
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
final String v = new AlphanumericRandomStringService().random();
final AzureMetadataFeature feature = new AzureMetadataFeature(session, null);
feature.setMetadata(test, status.setMetadata(Collections.singletonMap("Test", v)));
final Map<String, String> metadata = feature.getMetadata(test);
new AzureMetadataFeature(session).setMetadata(test, Collections.singletonMap("Test", v));
final Map<String, String> metadata = new AzureMetadataFeature(session).getMetadata(test);
assertFalse(metadata.isEmpty());
assertTrue(metadata.containsKey("Test"));
assertEquals(v, metadata.get("Test"));
feature.setMetadata(test, status.setMetadata(Collections.emptyMap()));
assertFalse(feature.getMetadata(test).containsKey("Test"));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testSetCacheControl() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
final AzureMetadataFeature service = new AzureMetadataFeature(session, null);
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
final AzureMetadataFeature service = new AzureMetadataFeature(session);
service.setMetadata(test, Collections.singletonMap("Cache-Control", "public, max-age=0"));
final Map<String, String> metadata = service.getMetadata(test);
assertFalse(metadata.isEmpty());
assertTrue(metadata.containsKey("Cache-Control"));
assertEquals("public, max-age=0", metadata.get("Cache-Control"));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -6,6 +6,8 @@ import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.synchronization.Comparison;
import ch.cyberduck.core.synchronization.ComparisonService;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -16,8 +18,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.Optional;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
@Category(IntegrationTest.class)
public class AzureMoveFeatureTest extends AbstractAzureTest {
@@ -25,20 +26,22 @@ public class AzureMoveFeatureTest extends AbstractAzureTest {
@Test
public void testMove() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new AzureTouchFeature(session, null).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(test));
final Path target = new AzureMoveFeature(session, null).move(test, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback());
assertFalse(new AzureFindFeature(session, null).find(test));
assertTrue(new AzureFindFeature(session, null).find(target));
final PathAttributes targetAttr = new AzureAttributesFinderFeature(session, null).find(target);
new AzureDeleteFeature(session, null).delete(Collections.<Path>singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback());
final Path test = new AzureTouchFeature(session).touch(new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final PathAttributes sourceAttr = new AzureAttributesFinderFeature(session).find(test);
assertTrue(new AzureFindFeature(session).find(test));
final Path target = new AzureMoveFeature(session).move(test, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback());
assertFalse(new AzureFindFeature(session).find(test));
assertTrue(new AzureFindFeature(session).find(target));
final PathAttributes targetAttr = new AzureAttributesFinderFeature(session).find(target);
assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, sourceAttr, targetAttr));
new AzureDeleteFeature(session).delete(Collections.<Path>singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testSupport() {
final Path c = new Path("/c", EnumSet.of(Path.Type.directory));
assertFalse(new AzureMoveFeature(session, null).isSupported(c, Optional.of(new Path("/d", EnumSet.of(Path.Type.directory)))));
assertFalse(new AzureMoveFeature(session).isSupported(c, Optional.of(new Path("/d", EnumSet.of(Path.Type.directory)))));
final Path cf = new Path("/c/f", EnumSet.of(Path.Type.directory));
assertTrue(new AzureMoveFeature(session, null).isSupported(cf, Optional.of(new Path("/c/f2", EnumSet.of(Path.Type.directory)))));
assertTrue(new AzureMoveFeature(session).isSupported(cf, Optional.of(new Path("/c/f2", EnumSet.of(Path.Type.directory)))));
}
}
@@ -6,9 +6,9 @@ import ch.cyberduck.core.DisabledListProgressListener;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.IndexedListProgressListener;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.io.HashAlgorithm;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -30,15 +30,15 @@ public class AzureObjectListServiceTest extends AbstractAzureTest {
@Test(expected = NotfoundException.class)
public void testListNotFoundFolder() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
new AzureObjectListService(session, null).list(new Path(container, "notfound", EnumSet.of(Path.Type.directory)), new DisabledListProgressListener());
new AzureObjectListService(session).list(new Path(container, "notfound", EnumSet.of(Path.Type.directory)), new DisabledListProgressListener());
}
@Test
public void testListEmptyFolder() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path folder = new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final AtomicBoolean callback = new AtomicBoolean();
assertTrue(new AzureObjectListService(session, null).list(folder, new DisabledListProgressListener() {
assertTrue(new AzureObjectListService(session).list(folder, new DisabledListProgressListener() {
@Override
public void chunk(final Path parent, final AttributedList<Path> list) {
assertNotSame(AttributedList.EMPTY, list);
@@ -46,45 +46,46 @@ public class AzureObjectListServiceTest extends AbstractAzureTest {
}
}).isEmpty());
assertTrue(callback.get());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test(expected = NotfoundException.class)
public void testListNotfoundContainer() throws Exception {
final Path container = new Path("notfound-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
new AzureObjectListService(session, null).list(container, new DisabledListProgressListener());
new AzureObjectListService(session).list(container, new DisabledListProgressListener());
}
@Test
public void testList() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path directory = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path directory = new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
assertTrue(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).isEmpty());
new AzureTouchFeature(session, null).touch(file, new TransferStatus());
final AttributedList<Path> list = new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener());
assertTrue(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).isEmpty());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), file, new TransferStatus());
final AttributedList<Path> list = new AzureObjectListService(session).list(directory, new DisabledListProgressListener());
assertFalse(list.isEmpty());
assertEquals(1, list.size());
assertTrue(list.contains(file));
assertEquals(HashAlgorithm.md5, list.get(0).attributes().getChecksum().algorithm);
final PathAttributes attributes = list.get(0).attributes();
assertEquals(attributes, new AzureAttributesFinderFeature(session).find(file));
assertSame(directory, list.get(0).getParent());
new AzureDeleteFeature(session, null).delete(Arrays.asList(file, directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Arrays.asList(file, directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testListLexicographicSortOrderAssumption() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
final Path directory = new AzureDirectoryFeature(session, null).mkdir(
new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).isEmpty());
final Path directory = new AzureDirectoryFeature(session).mkdir(
new AzureWriteFeature(session), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).isEmpty());
final List<String> files = Arrays.asList(
"Z", "aa", "0a", "a", "AAA", "B", "~$a", ".c"
);
for(String f : files) {
new AzureTouchFeature(session, null).touch(new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus());
}
files.sort(session.getHost().getProtocol().getListComparator());
final AttributedList<Path> list = new AzureObjectListService(session, null).list(directory, new IndexedListProgressListener() {
final AttributedList<Path> list = new AzureObjectListService(session).list(directory, new IndexedListProgressListener() {
@Override
public void message(final String message) {
//
@@ -97,8 +98,8 @@ public class AzureObjectListServiceTest extends AbstractAzureTest {
});
for(int i = 0; i < list.size(); i++) {
assertEquals(files.get(i), list.get(i).getName());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(list.get(i)), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(list.get(i)), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
new AzureDeleteFeature(session, null).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -6,7 +6,9 @@ import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.io.StreamCancelation;
import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.io.StreamProgress;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -31,34 +33,34 @@ public class AzureReadFeatureTest extends AbstractAzureTest {
public void testReadNotFound() throws Exception {
final TransferStatus status = new TransferStatus();
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
new AzureReadFeature(session, null).read(new Path(container, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback());
new AzureReadFeature(session).read(new Path(container, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback());
}
@Test
public void testReadZeroLength() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
final InputStream in = new AzureReadFeature(session, null).read(test, new TransferStatus().setLength(0L), new DisabledConnectionCallback());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
final InputStream in = new AzureReadFeature(session).read(test, new TransferStatus().setLength(0L), new DisabledConnectionCallback());
assertNotNull(in);
in.close();
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testReadRange() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
final byte[] content = RandomUtils.nextBytes(1023);
final OutputStream out = new AzureWriteFeature(session, null).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
final OutputStream out = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true).setLength(content.length), new DisabledConnectionCallback());
assertNotNull(out);
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out);
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out);
final TransferStatus status = new TransferStatus();
status.setLength(content.length);
status.setAppend(true);
status.setOffset(100L);
final InputStream in = new AzureReadFeature(session, null).read(test, status, new DisabledConnectionCallback());
final InputStream in = new AzureReadFeature(session).read(test, status, new DisabledConnectionCallback());
assertNotNull(in);
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length - 100);
new StreamCopier(status, status).transfer(in, buffer);
@@ -68,6 +70,6 @@ public class AzureReadFeatureTest extends AbstractAzureTest {
in.close();
// Test double close
in.close();
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -9,16 +9,26 @@ import ch.cyberduck.core.DisabledProgressListener;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.LoginConnectionService;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.Profile;
import ch.cyberduck.core.ProtocolFactory;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.AclPermission;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Touch;
import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DisabledX509TrustManager;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.util.Collections;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.*;
@Category(IntegrationTest.class)
@@ -33,10 +43,61 @@ public class AzureSessionTest extends AbstractAzureTest {
}
@Test
public void testConnect() throws Exception {
public void testConnect() {
assertTrue(session.isConnected());
}
@Test
public void testConnectSharedAccessSignature() throws Exception {
final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new AzureProtocol())));
final Profile profile = new ProfilePlistReader(factory).read(
this.getClass().getResourceAsStream("/Azure (Shared Access Signature Token).cyberduckprofile"));
final Host host = new Host(profile, "kahy9boj3eib.blob.core.windows.net", new Credentials(
PROPERTIES.get("azure.user"), null, PROPERTIES.get("azure.token")
));
final AzureSession session = new AzureSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() {
@Override
public Credentials prompt(final Host bookmark, final String username, final String title, final String reason, final LoginOptions options) {
fail(reason);
return null;
}
}, new DisabledHostKeyCallback(),
new DisabledPasswordStore(), new DisabledProgressListener());
login.connect(session, new DisabledCancelCallback());
session.close();
}
@Test
public void testConnectSharedAccessSignaturePrompt() throws Exception {
final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new AzureProtocol())));
final Profile profile = new ProfilePlistReader(factory).read(
this.getClass().getResourceAsStream("/Azure (Shared Access Signature Token).cyberduckprofile"));
final Host host = new Host(profile, "kahy9boj3eib.blob.core.windows.net", new Credentials(
null, null, "?sv=2017-07-29&ss=bfqt&srt=sco&sp=rwdlacup&se=2030-05-20T04:29:30Z&st=2018-05-09T20:29:30Z&spr=https&sig=invalid"));
final AzureSession session = new AzureSession(host);
final AtomicBoolean prompt = new AtomicBoolean();
final LoginConnectionService connect = new LoginConnectionService(new DisabledLoginCallback() {
@Override
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException {
if(prompt.get()) {
throw new LoginCanceledException();
}
try {
return new Credentials(StringUtils.EMPTY, PROPERTIES.get("azure.token"));
}
finally {
prompt.set(true);
}
}
}, new DisabledHostKeyCallback(),
new DisabledPasswordStore(), new DisabledProgressListener());
connect.connect(session, new DisabledCancelCallback());
assertTrue(session.isConnected());
connect.close(session);
assertFalse(session.isConnected());
}
@Test(expected = LoginCanceledException.class)
public void testConnectInvalidKey() throws Exception {
final Host host = new Host(new AzureProtocol(), "kahy9boj3eib.blob.core.windows.net", new Credentials(
@@ -47,7 +108,6 @@ public class AzureSessionTest extends AbstractAzureTest {
@Override
public Credentials prompt(final Host bookmark, String username, String title, String reason, LoginOptions options) throws LoginCanceledException {
assertEquals("Login kahy9boj3eib.blob.core.windows.net", title);
assertEquals("Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. Please contact your web hosting service provider for assistance.", reason);
return super.prompt(bookmark, username, title, reason, options);
}
}, new DisabledHostKeyCallback(),
@@ -64,7 +124,6 @@ public class AzureSessionTest extends AbstractAzureTest {
@Override
public Credentials prompt(final Host bookmark, String username, String title, String reason, LoginOptions options) throws LoginCanceledException {
assertEquals("Login kahy9boj3eib.blob.core.windows.net", title);
assertEquals("Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. Please contact your web hosting service provider for assistance.", reason);
return super.prompt(bookmark, username, title, reason, options);
}
}, new DisabledHostKeyCallback(),
@@ -36,14 +36,14 @@ public class AzureTouchFeatureTest extends AbstractAzureTest {
public void testTouchFileStartWithDot() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, String.format(".%s.", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testPreflightFilename() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final AzureTouchFeature feature = new AzureTouchFeature(session, null);
final AzureTouchFeature feature = new AzureTouchFeature(session);
feature.preflight(container, new AsciiRandomStringService().random());
feature.preflight(container, new AlphanumericRandomStringService().random());
feature.preflight(container, String.format("%s.suffix", new AlphanumericRandomStringService().random()));
@@ -1,9 +1,10 @@
package ch.cyberduck.core.azure;
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.DescriptiveUrl;
import ch.cyberduck.core.DescriptiveUrlBag;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.DisabledPasswordStore;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.features.Delete;
@@ -17,6 +18,7 @@ import java.util.Collections;
import java.util.EnumSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@Category(IntegrationTest.class)
public class AzureUrlProviderTest extends AbstractAzureTest {
@@ -24,21 +26,18 @@ public class AzureUrlProviderTest extends AbstractAzureTest {
@Test
public void testGet() throws Exception {
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, "f g", EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(test, new TransferStatus());
assertEquals(5, new AzureUrlProvider(session).toUrl(test).filter(DescriptiveUrl.Type.signed).size());
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), test, new TransferStatus());
final DescriptiveUrlBag urls = new AzureUrlProvider(session, new DisabledPasswordStore() {
@Override
public String findLoginPassword(final Host bookmark) {
return PROPERTIES.get("azure.password");
}
@Test
public void testDisconnected() throws Exception {
final Host host = new Host(new AzureProtocol(), "kahy9boj3eib.blob.core.windows.net", new Credentials(
PROPERTIES.get("azure.user"), PROPERTIES.get("azure.key")
));
final AzureSession session = new AzureSession(host);
final AzureUrlProvider provider = new AzureUrlProvider(session);
final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
assertEquals(DescriptiveUrl.EMPTY.getUrl(), provider.toUrl(file).find(DescriptiveUrl.Type.signed).getUrl());
}).toUrl(test).filter(DescriptiveUrl.Type.signed);
assertEquals(5, urls.size());
for(DescriptiveUrl url : urls) {
assertFalse(url.getUrl().isEmpty());
}
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -7,7 +7,9 @@ import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.io.MD5ChecksumCompute;
import ch.cyberduck.core.io.StreamCancelation;
import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.io.StreamProgress;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -24,8 +26,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.blob.BlobType;
import com.azure.storage.blob.models.BlobType;
import static org.junit.Assert.*;
@@ -34,8 +35,6 @@ public class AzureWriteFeatureTest extends AbstractAzureTest {
@Test
public void testWriteOverrideAppendBlob() throws Exception {
final OperationContext context
= new OperationContext();
final TransferStatus status = new TransferStatus();
status.setMime("text/plain");
final byte[] content = RandomUtils.nextBytes(513);
@@ -44,35 +43,33 @@ public class AzureWriteFeatureTest extends AbstractAzureTest {
status.setMetadata(Collections.singletonMap("Cache-Control", "public,max-age=86400"));
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final OutputStream out = new AzureWriteFeature(session, BlobType.APPEND_BLOB, context).write(test, status, new DisabledConnectionCallback());
final OutputStream out = new AzureWriteFeature(session, BlobType.APPEND_BLOB).write(test, status, new DisabledConnectionCallback());
assertNotNull(out);
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out);
assertTrue(new AzureFindFeature(session, context).find(test));
final PathAttributes attributes = new AzureAttributesFinderFeature(session, context).find(test);
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out);
assertTrue(new AzureFindFeature(session).find(test));
final PathAttributes attributes = new AzureAttributesFinderFeature(session).find(test);
assertEquals(content.length, attributes.getSize());
final Map<String, String> metadata = new AzureMetadataFeature(session, context).getMetadata(test);
final Map<String, String> metadata = new AzureMetadataFeature(session).getMetadata(test);
assertEquals("text/plain", metadata.get("Content-Type"));
assertEquals("public,max-age=86400", metadata.get("Cache-Control"));
final byte[] buffer = new byte[content.length];
final InputStream in = new AzureReadFeature(session, context).read(test, new TransferStatus(), new DisabledConnectionCallback());
final InputStream in = new AzureReadFeature(session).read(test, new TransferStatus(), new DisabledConnectionCallback());
IOUtils.readFully(in, buffer);
in.close();
assertArrayEquals(content, buffer);
final OutputStream overwrite = new AzureWriteFeature(session, context).write(test, new TransferStatus().setExists(true)
final OutputStream overwrite = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true)
.setLength("overwrite".getBytes(StandardCharsets.UTF_8).length).setMetadata(Collections.singletonMap("Content-Type", "text/plain")), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus())
new StreamCopier(StreamCancelation.noop, StreamProgress.noop)
.transfer(new ByteArrayInputStream("overwrite".getBytes(StandardCharsets.UTF_8)), overwrite);
overwrite.close();
// Test double close
overwrite.close();
assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session, context).find(test).getSize());
new AzureDeleteFeature(session, context).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session).find(test).getSize());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
public void testWriteOverrideBlockBlob() throws Exception {
final OperationContext context
= new OperationContext();
final TransferStatus status = new TransferStatus();
status.setMime("text/plain");
final byte[] content = RandomUtils.nextBytes(513);
@@ -81,28 +78,28 @@ public class AzureWriteFeatureTest extends AbstractAzureTest {
status.setMetadata(Collections.singletonMap("Cache-Control", "public,max-age=86400"));
final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final OutputStream out = new AzureWriteFeature(session, BlobType.BLOCK_BLOB, context).write(test, status, new DisabledConnectionCallback());
final OutputStream out = new AzureWriteFeature(session, BlobType.BLOCK_BLOB).write(test, status, new DisabledConnectionCallback());
assertNotNull(out);
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out);
assertTrue(new AzureFindFeature(session, context).find(test));
final PathAttributes attributes = new AzureAttributesFinderFeature(session, context).find(test);
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out);
assertTrue(new AzureFindFeature(session).find(test));
final PathAttributes attributes = new AzureAttributesFinderFeature(session).find(test);
assertEquals(content.length, attributes.getSize());
final Map<String, String> metadata = new AzureMetadataFeature(session, context).getMetadata(test);
final Map<String, String> metadata = new AzureMetadataFeature(session).getMetadata(test);
assertEquals("text/plain", metadata.get("Content-Type"));
assertEquals("public,max-age=86400", metadata.get("Cache-Control"));
final byte[] buffer = new byte[content.length];
final InputStream in = new AzureReadFeature(session, context).read(test, new TransferStatus(), new DisabledConnectionCallback());
final InputStream in = new AzureReadFeature(session).read(test, new TransferStatus(), new DisabledConnectionCallback());
IOUtils.readFully(in, buffer);
in.close();
assertArrayEquals(content, buffer);
final OutputStream overwrite = new AzureWriteFeature(session, context).write(test, new TransferStatus().setExists(true)
final OutputStream overwrite = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true)
.setLength("overwrite".getBytes(StandardCharsets.UTF_8).length).setMetadata(Collections.singletonMap("Content-Type", "text/plain")), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus())
new StreamCopier(StreamCancelation.noop, StreamProgress.noop)
.transfer(new ByteArrayInputStream("overwrite".getBytes(StandardCharsets.UTF_8)), overwrite);
overwrite.close();
// Test double close
overwrite.close();
assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session, context).find(test).getSize());
new AzureDeleteFeature(session, context).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session).find(test).getSize());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -22,9 +22,11 @@ import ch.cyberduck.core.Path;
import ch.cyberduck.core.azure.AbstractAzureTest;
import ch.cyberduck.core.azure.AzureDeleteFeature;
import ch.cyberduck.core.azure.AzureDirectoryFeature;
import ch.cyberduck.core.azure.AzureWriteFeature;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
@@ -53,10 +55,11 @@ public class AzureDirectoryFeatureTest extends AbstractAzureTest {
new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(test.getType().contains(Path.Type.placeholder));
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
@@ -67,8 +70,9 @@ public class AzureDirectoryFeatureTest extends AbstractAzureTest {
new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -16,25 +16,18 @@ package ch.cyberduck.core.cryptomator;
*/
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.AttributedList;
import ch.cyberduck.core.Cache;
import ch.cyberduck.core.CachingFindFeature;
import ch.cyberduck.core.DisabledListProgressListener;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.DisabledPasswordCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathCache;
import ch.cyberduck.core.azure.AbstractAzureTest;
import ch.cyberduck.core.azure.AzureDeleteFeature;
import ch.cyberduck.core.azure.AzureFindFeature;
import ch.cyberduck.core.azure.AzureObjectListService;
import ch.cyberduck.core.azure.AzureWriteFeature;
import ch.cyberduck.core.cryptomator.features.CryptoFindFeature;
import ch.cyberduck.core.cryptomator.features.CryptoListService;
import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.shared.DefaultTouchFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
@@ -48,9 +41,9 @@ import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -64,40 +57,10 @@ public class AzureListServiceTest extends AbstractAzureTest {
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
assertTrue(new CryptoListService(session, new AzureObjectListService(session, null), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new AzureWriteFeature(session, null)), new AzureWriteFeature(session, null), cryptomator).touch(test, new TransferStatus());
assertEquals(test, new CryptoListService(session, new AzureObjectListService(session, null), cryptomator).list(vault, new DisabledListProgressListener() {
@Override
public void cleanup(final Path directory, final AttributedList<Path> list, final Optional<BackgroundException> e) {
assertEquals(vault, directory);
for(Path f : list) {
assertTrue(f.getType().contains(Path.Type.decrypted));
}
}
@Override
public void chunk(final Path directory, final AttributedList<Path> list) {
assertEquals(vault, directory);
for(Path f : list) {
assertTrue(f.getType().contains(Path.Type.decrypted));
}
}
}).get(0));
{
final Cache<Path> cache = new PathCache(1);
assertTrue(new CachingFindFeature(session, cache, new CryptoFindFeature(session, new AzureFindFeature(session, null), cryptomator)).find(test));
assertFalse(cache.isCached(vault));
}
{
final Cache<Path> cache = new PathCache(1);
assertTrue(new CachingFindFeature(session, cache, new CryptoFindFeature(session, new DefaultFindFeature(session), cryptomator)).find(test));
assertTrue(cache.isCached(vault));
final AttributedList<Path> list = cache.get(vault);
assertFalse(list.isEmpty());
for(Path f : list) {
assertTrue(f.getType().contains(Path.Type.decrypted));
}
}
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new CryptoListService(session, new AzureObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
new CryptoTouchFeature<>(session, new DefaultTouchFeature<Void>(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), test, new TransferStatus());
assertEquals(test, new CryptoListService(session, new AzureObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).get(0));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -28,10 +28,12 @@ import ch.cyberduck.core.azure.AzureMoveFeature;
import ch.cyberduck.core.azure.AzureTouchFeature;
import ch.cyberduck.core.azure.AzureWriteFeature;
import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.features.Move;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
@@ -60,25 +62,25 @@ public class AzureMoveFeatureTest extends AbstractAzureTest {
new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path folder = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(
new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file = new CryptoTouchFeature<Void>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(
new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path folder = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file));
final Move move = cryptomator.getFeature(session, Move.class, new AzureMoveFeature(session, null));
final Move move = cryptomator.getFeature(session, Move.class, new AzureMoveFeature(session));
// rename file
final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file));
move.move(file, fileRenamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback());
assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(file));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(fileRenamed));
assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(file));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(fileRenamed));
// rename folder
final Path folderRenamed = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder));
move.move(folder, folderRenamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback());
assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folderRenamed));
assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folderRenamed));
final Path fileRenamedInRenamedFolder = new Path(folderRenamed, "f1", EnumSet.of(Path.Type.file));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(fileRenamedInRenamedFolder));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(fileRenamedInRenamedFolder));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(
fileRenamedInRenamedFolder, folderRenamed, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -25,6 +25,7 @@ import ch.cyberduck.core.azure.AzureFindFeature;
import ch.cyberduck.core.azure.AzureTouchFeature;
import ch.cyberduck.core.azure.AzureWriteFeature;
import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.shared.DefaultFindFeature;
@@ -58,12 +59,12 @@ public class AzureTouchFeatureTest extends AbstractAzureTest {
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(
new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status);
final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status);
assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize());
assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize());
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(test));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(test));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@Test
@@ -75,11 +76,11 @@ public class AzureTouchFeatureTest extends AbstractAzureTest {
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(
new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status);
final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status);
assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize());
assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test));
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -54,8 +54,6 @@ import java.io.OutputStream;
import java.util.Arrays;
import java.util.EnumSet;
import com.microsoft.azure.storage.OperationContext;
import static org.junit.Assert.*;
@Category(IntegrationTest.class)
@@ -72,7 +70,7 @@ public class AzureWriteFeatureTest extends AbstractAzureTest {
new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final CryptoWriteFeature<Void> writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session, null), cryptomator);
final CryptoWriteFeature<Void> writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator);
final FileHeader header = cryptomator.getFileHeaderCryptor().create();
status.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
status.setNonces(new RotatingNonceGenerator(cryptomator.getNonceSize(), cryptomator.numberOfChunks(content.length)));
@@ -82,14 +80,13 @@ public class AzureWriteFeatureTest extends AbstractAzureTest {
assertNotNull(out);
new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out);
out.close();
final OperationContext context = new OperationContext();
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(test));
final PathAttributes attributes = new CryptoListService(session, new AzureListService(session, context), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes();
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(test));
final PathAttributes attributes = new CryptoListService(session, new AzureListService(session), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes();
assertEquals(content.length, attributes.getSize());
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session, context), cryptomator).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(status, status).transfer(in, buffer);
assertArrayEquals(content, buffer.toByteArray());
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, context)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -23,7 +23,6 @@ import ch.cyberduck.core.DisabledProgressListener;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathCache;
import ch.cyberduck.core.azure.AbstractAzureTest;
import ch.cyberduck.core.azure.AzureDeleteFeature;
import ch.cyberduck.core.azure.AzureDirectoryFeature;
import ch.cyberduck.core.azure.AzureFindFeature;
import ch.cyberduck.core.azure.AzureReadFeature;
@@ -35,7 +34,10 @@ import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.io.StreamCancelation;
import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.io.StreamProgress;
import ch.cyberduck.core.pool.SessionPool;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.shared.DefaultTouchFeature;
@@ -63,7 +65,6 @@ import java.util.Collections;
import java.util.EnumSet;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -81,15 +82,15 @@ public class CopyWorkerTest extends AbstractAzureTest {
session.withRegistry(registry);
final byte[] content = RandomUtils.nextBytes(40500);
final TransferStatus status = new TransferStatus();
new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new AzureDeleteFeature(session, null), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), new CryptoWriteFeature<>(session, new AzureWriteFeature(session, null), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback()));
new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback());
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback()));
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(source));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(target));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(source));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(target));
final ByteArrayOutputStream out = new ByteArrayOutputStream(content.length);
assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(target, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out));
assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(target, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out));
assertArrayEquals(content, out.toByteArray());
new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
}
@@ -105,9 +106,11 @@ public class CopyWorkerTest extends AbstractAzureTest {
cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
new CryptoTouchFeature<Void>(session, new DefaultTouchFeature<Void>(new AzureWriteFeature(session, null)), new AzureWriteFeature(session, null), cryptomator).touch(source, new TransferStatus());
new CryptoTouchFeature<>(session, new DefaultTouchFeature<Void>(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), source, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source));
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(targetFolder, new TransferStatus());
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), targetFolder, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
@@ -119,7 +122,6 @@ public class CopyWorkerTest extends AbstractAzureTest {
@Test
public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
@@ -128,9 +130,11 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
new CryptoTouchFeature<Void>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(source, new TransferStatus());
new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), source, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source));
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(targetFolder, new TransferStatus());
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), targetFolder, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
@@ -149,20 +153,22 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(folder, new TransferStatus());
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), folder, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder));
new CryptoTouchFeature<Void>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(file, new TransferStatus());
new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), file, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file));
// copy file
final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file));
new CopyWorker(Collections.singletonMap(file, fileRenamed), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()).run(session);
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(file));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(fileRenamed));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(file));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(fileRenamed));
// copy folder
final Path folderRenamed = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
new CopyWorker(Collections.singletonMap(folder, folderRenamed), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()).run(session);
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folderRenamed));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folderRenamed));
final Path fileRenamedInRenamedFolder = new Path(folderRenamed, "f1", EnumSet.of(Path.Type.file));
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(fileRenamedInRenamedFolder));
registry.clear();
@@ -175,23 +181,24 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path cleartextFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final byte[] content = RandomUtils.nextBytes(40500);
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), new AzureWriteFeature(session, null).write(cleartextFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()));
assertTrue(new AzureFindFeature(session, null).find(cleartextFile));
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), new AzureWriteFeature(session).write(cleartextFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()));
assertTrue(new AzureFindFeature(session).find(cleartextFile));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final CryptoVault cryptomator = new CryptoVault(vault);
cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(encryptedFolder, new TransferStatus());
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), encryptedFolder, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder));
// copy file into vault
final CopyWorker worker = new CopyWorker(Collections.singletonMap(cleartextFile, encryptedFile), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(new AzureFindFeature(session, null).find(cleartextFile));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFile));
assertTrue(new AzureFindFeature(session).find(cleartextFile));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFile));
final ByteArrayOutputStream out = new ByteArrayOutputStream(content.length);
assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(encryptedFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out));
assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(encryptedFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out));
assertArrayEquals(content, out.toByteArray());
registry.clear();
new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
@@ -203,10 +210,10 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path cleartextFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path cleartextFile = new Path(cleartextFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureDirectoryFeature(session, null).mkdir(cleartextFolder, new TransferStatus());
new AzureTouchFeature(session, null).touch(cleartextFile, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(cleartextFolder));
assertTrue(new AzureFindFeature(session, null).find(cleartextFile));
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), cleartextFolder, new TransferStatus());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), cleartextFile, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(cleartextFolder));
assertTrue(new AzureFindFeature(session).find(cleartextFile));
final CryptoVault cryptomator = new CryptoVault(vault);
cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
@@ -218,8 +225,8 @@ public class CopyWorkerTest extends AbstractAzureTest {
worker.run(session);
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder));
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile));
assertTrue(new AzureFindFeature(session, null).find(cleartextFolder));
assertTrue(new AzureFindFeature(session, null).find(cleartextFile));
assertTrue(new AzureFindFeature(session).find(cleartextFolder));
assertTrue(new AzureFindFeature(session).find(cleartextFile));
registry.clear();
new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
}
@@ -229,23 +236,25 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path clearFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
new AzureDirectoryFeature(session, null).mkdir(clearFolder, new TransferStatus());
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), clearFolder, new TransferStatus());
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final CryptoVault cryptomator = new CryptoVault(vault);
cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(encryptedFolder, new TransferStatus());
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), encryptedFolder, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder));
new CryptoTouchFeature<Void>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(encryptedFile, new TransferStatus());
new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), encryptedFile, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile));
// move file outside vault
final Path cleartextFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(encryptedFile, cleartextFile), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFile));
assertTrue(new AzureFindFeature(session, null).find(cleartextFile));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFile));
assertTrue(new AzureFindFeature(session).find(cleartextFile));
registry.clear();
new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(vault, clearFolder), new DisabledProgressListener()).run(session);
}
@@ -260,19 +269,21 @@ public class CopyWorkerTest extends AbstractAzureTest {
cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(encryptedFolder, new TransferStatus());
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), encryptedFolder, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder));
new CryptoTouchFeature<Void>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(encryptedFile, new TransferStatus());
new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator), encryptedFile, new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile));
// copy directory outside vault
final Path cleartextFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(encryptedFolder, cleartextFolder), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFolder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFile));
assertTrue(new AzureFindFeature(session, null).find(cleartextFolder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFolder));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFile));
assertTrue(new AzureFindFeature(session).find(cleartextFolder));
final Path fileRenamed = new Path(cleartextFolder, encryptedFile.getName(), EnumSet.of(Path.Type.file));
assertTrue(new AzureFindFeature(session, null).find(fileRenamed));
assertTrue(new AzureFindFeature(session).find(fileRenamed));
registry.clear();
new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(cleartextFolder, vault), new DisabledProgressListener()).run(session);
}
@@ -35,7 +35,9 @@ import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.io.DisabledStreamListener;
import ch.cyberduck.core.io.StreamCancelation;
import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.io.StreamProgress;
import ch.cyberduck.core.local.DefaultLocalDirectoryFeature;
import ch.cyberduck.core.notification.DisabledNotificationService;
import ch.cyberduck.core.transfer.DisabledTransferErrorCallback;
@@ -98,26 +100,23 @@ public class CryptoAzureSingleTransferWorkerTest extends AbstractAzureTest {
public TransferAction prompt(final TransferItem file) {
return TransferAction.overwrite;
}
}, new DisabledTransferErrorCallback(),
new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()) {
}.run(session));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(dir1));
assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session, null)).find(file1).getSize());
}, new DisabledTransferErrorCallback(), new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()).run(session));
assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(dir1));
assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session)).find(file1).getSize());
{
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(in, buffer);
final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(in, buffer);
assertArrayEquals(content, buffer.toByteArray());
}
assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session, null)).find(file2).getSize());
assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session)).find(file2).getSize());
{
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(in, buffer);
final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(in, buffer);
assertArrayEquals(content, buffer.toByteArray());
}
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(file1, file2, dir1, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(file1, file2, dir1, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
localFile1.delete();
localFile2.delete();
localDirectory1.delete();
@@ -67,14 +67,13 @@ public class AzureSingleTransferWorkerTest extends AbstractAzureTest {
{
final byte[] content = RandomUtils.nextBytes(39864);
final TransferStatus writeStatus = new TransferStatus().setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus()));
final StatusOutputStream out = new AzureWriteFeature(session, null).write(test, writeStatus, new DisabledConnectionCallback());
final StatusOutputStream out = new AzureWriteFeature(session).write(test, writeStatus, new DisabledConnectionCallback());
assertNotNull(out);
new StreamCopier(writeStatus, writeStatus).withLimit((long) content.length).transfer(new ByteArrayInputStream(content), out);
out.close();
}
final byte[] content = RandomUtils.nextBytes(39864);
final TransferStatus writeStatus = new TransferStatus().setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus()));
final StatusOutputStream<Void> out = new AzureWriteFeature(session, null).write(test, writeStatus, new DisabledConnectionCallback());
final TransferStatus writeStatus = new TransferStatus().setExists(true).setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus()));
final StatusOutputStream<Void> out = new AzureWriteFeature(session).write(test, writeStatus, new DisabledConnectionCallback());
assertNotNull(out);
new StreamCopier(writeStatus, writeStatus).withLimit((long) content.length).transfer(new ByteArrayInputStream(content), out);
out.close();
@@ -85,11 +84,9 @@ public class AzureSingleTransferWorkerTest extends AbstractAzureTest {
return TransferAction.overwrite;
}
}, new DisabledTransferErrorCallback(),
new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()) {
}.run(session));
new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()).run(session));
assertArrayEquals(content, IOUtils.toByteArray(localFile.getInputStream()));
new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
localFile.delete();
}
}
@@ -25,6 +25,7 @@ import ch.cyberduck.core.azure.AbstractAzureTest;
import ch.cyberduck.core.azure.AzureDirectoryFeature;
import ch.cyberduck.core.azure.AzureFindFeature;
import ch.cyberduck.core.azure.AzureTouchFeature;
import ch.cyberduck.core.azure.AzureWriteFeature;
import ch.cyberduck.core.pool.SessionPool;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;
@@ -46,12 +47,12 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path source = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path target = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(source, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(source));
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), source, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(source));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(new AzureFindFeature(session, null).find(source));
assertTrue(new AzureFindFeature(session, null).find(target));
assertTrue(new AzureFindFeature(session).find(source));
assertTrue(new AzureFindFeature(session).find(target));
new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(source, target), new DisabledProgressListener()).run(session);
}
@@ -59,17 +60,17 @@ public class CopyWorkerTest extends AbstractAzureTest {
public void testCopyFileToDirectory() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureTouchFeature(session, null).touch(sourceFile, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(sourceFile));
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), sourceFile, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(sourceFile));
final Path targetFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path targetFile = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureDirectoryFeature(session, null).mkdir(targetFolder, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(targetFolder));
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), targetFolder, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(targetFolder));
// copy file into vault
final CopyWorker worker = new CopyWorker(Collections.singletonMap(sourceFile, targetFile), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(new AzureFindFeature(session, null).find(sourceFile));
assertTrue(new AzureFindFeature(session, null).find(targetFile));
assertTrue(new AzureFindFeature(session).find(sourceFile));
assertTrue(new AzureFindFeature(session).find(targetFile));
new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(sourceFile, targetFolder), new DisabledProgressListener()).run(session);
}
@@ -78,19 +79,19 @@ public class CopyWorkerTest extends AbstractAzureTest {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path sourceFile = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
new AzureDirectoryFeature(session, null).mkdir(folder, new TransferStatus());
new AzureTouchFeature(session, null).touch(sourceFile, new TransferStatus());
assertTrue(new AzureFindFeature(session, null).find(folder));
assertTrue(new AzureFindFeature(session, null).find(sourceFile));
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), folder, new TransferStatus());
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), sourceFile, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(folder));
assertTrue(new AzureFindFeature(session).find(sourceFile));
// move directory into vault
final Path targetFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path targetFile = new Path(targetFolder, sourceFile.getName(), EnumSet.of(Path.Type.file));
final CopyWorker worker = new CopyWorker(Collections.singletonMap(folder, targetFolder), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback());
worker.run(session);
assertTrue(new AzureFindFeature(session, null).find(targetFolder));
assertTrue(new AzureFindFeature(session, null).find(targetFile));
assertTrue(new AzureFindFeature(session, null).find(folder));
assertTrue(new AzureFindFeature(session, null).find(sourceFile));
assertTrue(new AzureFindFeature(session).find(targetFolder));
assertTrue(new AzureFindFeature(session).find(targetFile));
assertTrue(new AzureFindFeature(session).find(folder));
assertTrue(new AzureFindFeature(session).find(sourceFile));
new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(folder, targetFolder), new DisabledProgressListener()).run(session);
}
}
+3 -3
View File
@@ -19,13 +19,13 @@
<parent>
<artifactId>parent</artifactId>
<groupId>ch.cyberduck</groupId>
<version>9.2.0-SNAPSHOT</version>
<version>9.4.0-SNAPSHOT</version>
</parent>
<artifactId>backblaze</artifactId>
<packaging>jar</packaging>
<properties>
<b2-version>2.1.0</b2-version>
<b2-version>2.2.0</b2-version>
</properties>
<dependencyManagement>
@@ -33,7 +33,7 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250517</version>
<version>20251224</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -17,6 +17,7 @@ package ch.cyberduck.core.b2;
import ch.cyberduck.core.Acl;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.DefaultPathContainerService;
import ch.cyberduck.core.ListProgressListener;
import ch.cyberduck.core.Path;
@@ -68,9 +69,9 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
}
if(file.getType().contains(Path.Type.upload)) {
// Pending large file upload
final Write.Append append = new B2LargeUploadService(session, fileid, new B2WriteFeature(session, fileid)).append(file, new TransferStatus());
final Write.Append append = new B2LargeUploadService(session, fileid).append(file, new TransferStatus());
if(append.append) {
return new PathAttributes().setSize(append.offset);
return new DefaultPathAttributes().setSize(append.offset);
}
return PathAttributes.EMPTY;
}
@@ -103,7 +104,7 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
response = this.findFileInfo(file, fileid.getVersionId(file));
}
final PathAttributes attr = this.toAttributes(response);
if(attr.isDuplicate()) {
if(attr.isTrashed()) {
// Throw failure if latest version has hide marker set and lookup was without explicit version
if(StringUtils.isBlank(file.attributes().getVersionId())) {
log.debug("Latest version of {} is duplicate", file);
@@ -145,7 +146,7 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
}
protected PathAttributes toAttributes(final B2FileInfoResponse response) {
final PathAttributes attributes = new PathAttributes();
final PathAttributes attributes = new DefaultPathAttributes();
if(response.getFileInfo().containsKey(X_BZ_INFO_LARGE_FILE_SHA1)) {
attributes.setChecksum(Checksum.parse(response.getFileInfo().get(X_BZ_INFO_LARGE_FILE_SHA1)));
}
@@ -182,6 +183,8 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
switch(response.getAction()) {
case hide:
// File version marking the file as hidden, so that it will not show up in b2_list_file_names
attributes.setTrashed(true);
break;
case start:
// Large file has been started, but not finished or canceled
attributes.setHidden(true);
@@ -195,7 +198,7 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
}
protected PathAttributes toAttributes(final B2FileResponse response) {
final PathAttributes attributes = new PathAttributes();
final PathAttributes attributes = new DefaultPathAttributes();
attributes.setSize(response.getContentLength());
if(response.getFileInfo().containsKey(X_BZ_INFO_LARGE_FILE_SHA1)) {
attributes.setChecksum(Checksum.parse(response.getFileInfo().get(X_BZ_INFO_LARGE_FILE_SHA1)));
@@ -246,7 +249,7 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
}
protected PathAttributes toAttributes(final B2BucketResponse response) {
final PathAttributes attributes = new PathAttributes();
final PathAttributes attributes = new DefaultPathAttributes();
attributes.setVersionId(response.getBucketId());
attributes.setRegion(response.getBucketType().name());
switch(response.getBucketType()) {
@@ -257,7 +260,7 @@ public class B2AttributesFinderFeature implements AttributesFinder, AttributesAd
}
protected PathAttributes toAttributes(final B2FinishLargeFileResponse response) {
final PathAttributes attributes = new PathAttributes();
final PathAttributes attributes = new DefaultPathAttributes();
attributes.setSize(response.getContentLength());
if(response.getFileInfo().containsKey(X_BZ_INFO_LARGE_FILE_SHA1)) {
attributes.setChecksum(Checksum.parse(response.getFileInfo().get(X_BZ_INFO_LARGE_FILE_SHA1)));
@@ -0,0 +1,93 @@
package ch.cyberduck.core.b2;
/*
* Copyright (c) 2002-2016 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*/
import ch.cyberduck.core.Acl;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.AclPermission;
import ch.cyberduck.core.transfer.TransferStatus;
import org.jets3t.service.acl.Permission;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import synapticloop.b2.BucketType;
import synapticloop.b2.exception.B2ApiException;
import synapticloop.b2.response.B2BucketResponse;
public class B2BucketTypeAclFeature implements AclPermission {
private final PathContainerService containerService
= new B2PathContainerService();
private final B2Session session;
private final B2VersionIdProvider fileid;
public B2BucketTypeAclFeature(final B2Session session, final B2VersionIdProvider fileid) {
this.session = session;
this.fileid = fileid;
}
@Override
public Acl getPermission(final Path file) {
if(containerService.isContainer(file)) {
return containerService.getContainer(file).attributes().getAcl();
}
return Acl.EMPTY;
}
@Override
public void setPermission(final Path file, final TransferStatus status) throws BackgroundException {
if(containerService.isContainer(file)) {
try {
final BucketType bucketType = this.toBucketType(status.getAcl());
final B2BucketResponse response = session.getClient().updateBucket(fileid.getVersionId(containerService.getContainer(file)), bucketType);
status.setResponse(new B2AttributesFinderFeature(session, fileid).toAttributes(response));
}
catch(B2ApiException e) {
throw new B2ExceptionMappingService(fileid).map("Cannot change permissions of {0}", e, file);
}
catch(IOException e) {
throw new DefaultIOExceptionMappingService().map(e);
}
}
}
protected BucketType toBucketType(final Acl acl) {
return acl.asList().stream()
.filter(userAndRole -> userAndRole.getUser() instanceof Acl.GroupUser)
.anyMatch(userAndRole -> userAndRole.getUser().getIdentifier().equals(Acl.GroupUser.EVERYONE)) ? BucketType.allPublic : BucketType.allPrivate;
}
@Override
public List<Acl.User> getAvailableAclUsers(final List<Path> files) {
return new ArrayList<>(Collections.singletonList(
new Acl.GroupUser(Acl.GroupUser.EVERYONE, false))
);
}
@Override
public List<Acl.Role> getAvailableAclRoles(final List<Path> files) {
return Collections.singletonList(
new Acl.Role(Permission.PERMISSION_READ.toString()));
}
}
@@ -16,30 +16,19 @@ package ch.cyberduck.core.b2;
*/
import ch.cyberduck.core.Acl;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.AclPermission;
import ch.cyberduck.core.features.Location;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.transfer.TransferStatus;
import org.jets3t.service.acl.Permission;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import synapticloop.b2.BucketType;
import synapticloop.b2.exception.B2ApiException;
import synapticloop.b2.response.B2BucketResponse;
public class B2BucketTypeFeature implements AclPermission, Location {
public class B2BucketTypeFeature implements Location {
private final PathContainerService containerService
= new B2PathContainerService();
@@ -52,58 +41,19 @@ public class B2BucketTypeFeature implements AclPermission, Location {
this.fileid = fileid;
}
@Override
public Acl getPermission(final Path file) {
if(containerService.isContainer(file)) {
return containerService.getContainer(file).attributes().getAcl();
}
return Acl.EMPTY;
}
@Override
public void setPermission(final Path file, final TransferStatus status) throws BackgroundException {
if(containerService.isContainer(file)) {
try {
final BucketType bucketType = this.toBucketType(status.getAcl());
final B2BucketResponse response = session.getClient().updateBucket(fileid.getVersionId(containerService.getContainer(file)), bucketType);
status.setResponse(new B2AttributesFinderFeature(session, fileid).toAttributes(response));
}
catch(B2ApiException e) {
throw new B2ExceptionMappingService(fileid).map("Cannot change permissions of {0}", e, file);
}
catch(IOException e) {
throw new DefaultIOExceptionMappingService().map(e);
}
}
}
protected BucketType toBucketType(final Acl acl) {
return acl.asList().stream()
.filter(userAndRole -> userAndRole.getUser() instanceof Acl.GroupUser)
.filter(userAndRole -> userAndRole.getUser().getIdentifier().equals(Acl.GroupUser.EVERYONE))
.findAny().isPresent() ? BucketType.allPublic : BucketType.allPrivate;
.anyMatch(userAndRole -> userAndRole.getUser().getIdentifier().equals(Acl.GroupUser.EVERYONE)) ? BucketType.allPublic : BucketType.allPrivate;
}
@Override
public List<Acl.User> getAvailableAclUsers() {
return new ArrayList<>(Collections.singletonList(
new Acl.GroupUser(Acl.GroupUser.EVERYONE, false))
);
}
@Override
public List<Acl.Role> getAvailableAclRoles(final List<Path> files) {
return Collections.singletonList(
new Acl.Role(Permission.PERMISSION_READ.toString()));
}
@Override
public Name getDefault() {
public Name getDefault(final Path file) {
return new B2BucketTypeName(BucketType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("b2.bucket.acl.default")));
}
@Override
public Set<Name> getLocations() {
public Set<Name> getLocations(final Path file) {
final Set<Name> types = new LinkedHashSet<>();
types.add(new B2BucketTypeName(BucketType.allPrivate));
types.add(new B2BucketTypeName(BucketType.allPublic));
@@ -46,24 +46,18 @@ public class B2DirectoryFeature implements Directory<BaseB2Response> {
private final B2Session session;
private final B2VersionIdProvider fileid;
private Write<BaseB2Response> writer;
public B2DirectoryFeature(final B2Session session, final B2VersionIdProvider fileid) {
this(session, fileid, new B2WriteFeature(session, fileid));
}
public B2DirectoryFeature(final B2Session session, final B2VersionIdProvider fileid, final B2WriteFeature writer) {
this.session = session;
this.fileid = fileid;
this.writer = writer;
}
@Override
public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException {
public Path mkdir(final Write<BaseB2Response> writer, final Path folder, final TransferStatus status) throws BackgroundException {
try {
if(containerService.isContainer(folder)) {
final B2BucketResponse response = session.getClient().createBucket(containerService.getContainer(folder).getName(),
null == status.getRegion() ? BucketType.valueOf(new B2BucketTypeFeature(session, fileid).getDefault().getIdentifier()) : BucketType.valueOf(status.getRegion()));
null == status.getRegion() ? BucketType.valueOf(new B2BucketTypeFeature(session, fileid).getDefault(folder).getIdentifier()) : BucketType.valueOf(status.getRegion()));
final EnumSet<Path.Type> type = EnumSet.copyOf(folder.getType());
type.add(Path.Type.volume);
return new Path(folder).withType(type).withAttributes(new B2AttributesFinderFeature(session, fileid).toAttributes(response));
@@ -71,7 +65,7 @@ public class B2DirectoryFeature implements Directory<BaseB2Response> {
else {
final EnumSet<Path.Type> type = EnumSet.copyOf(folder.getType());
type.add(Path.Type.placeholder);
return new B2TouchFeature(session, fileid).touch(folder.withType(type), status
return new B2TouchFeature(session, fileid).touch(writer, folder.withType(type), status
.setMime(MimeTypeService.DEFAULT_CONTENT_TYPE)
.setChecksum(writer.checksum(folder, status).compute(new NullInputStream(0L), status)));
}
@@ -107,11 +101,5 @@ public class B2DirectoryFeature implements Directory<BaseB2Response> {
}
}
}
@Override
public B2DirectoryFeature withWriter(final Write<BaseB2Response> writer) {
this.writer = writer;
return this;
}
}
@@ -17,6 +17,7 @@ package ch.cyberduck.core.b2;
import ch.cyberduck.core.ConnectionCallback;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.PathContainerService;
@@ -129,7 +130,7 @@ public class B2LargeCopyFeature implements Copy {
session.getClient().finishLargeFileUpload(response.getFileId(), checksums.toArray(new String[checksums.size()]));
log.info("Finished large file upload {} with {} parts", target, completed.size());
fileid.cache(target, response.getFileId());
return new Path(target).withAttributes(new PathAttributes(source.attributes()).setVersionId(response.getFileId()));
return new Path(target).withAttributes(new DefaultPathAttributes(source.attributes()).setVersionId(response.getFileId()));
}
catch(B2ApiException e) {
throw new B2ExceptionMappingService(fileid).map("Cannot copy {0}", e, source);
@@ -26,7 +26,6 @@ import ch.cyberduck.core.ProgressListener;
import ch.cyberduck.core.concurrency.Interruptibles;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.features.Upload;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.http.HttpUploadFeature;
import ch.cyberduck.core.io.BandwidthThrottle;
@@ -80,24 +79,20 @@ public class B2LargeUploadService extends HttpUploadFeature<BaseB2Response, Mess
private final Long partSize;
private final Integer concurrency;
private Write<BaseB2Response> writer;
public B2LargeUploadService(final B2Session session, final B2VersionIdProvider fileid, final Write<BaseB2Response> writer) {
this(session, fileid, writer, HostPreferencesFactory.get(session.getHost()).getLong("b2.upload.largeobject.size"),
public B2LargeUploadService(final B2Session session, final B2VersionIdProvider fileid) {
this(session, fileid, HostPreferencesFactory.get(session.getHost()).getLong("b2.upload.largeobject.size"),
HostPreferencesFactory.get(session.getHost()).getInteger("b2.upload.largeobject.concurrency"));
}
public B2LargeUploadService(final B2Session session, final B2VersionIdProvider fileid, final Write<BaseB2Response> writer, final Long partSize, final Integer concurrency) {
super(writer);
public B2LargeUploadService(final B2Session session, final B2VersionIdProvider fileid, final Long partSize, final Integer concurrency) {
this.session = session;
this.fileid = fileid;
this.writer = writer;
this.partSize = partSize;
this.concurrency = concurrency;
}
@Override
public BaseB2Response upload(final Path file, final Local local, final BandwidthThrottle throttle,
public BaseB2Response upload(final Write<BaseB2Response> write, final Path file, final Local local, final BandwidthThrottle throttle,
final ProgressListener progress, final StreamListener streamListener, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
final long partSize;
if(file.getType().contains(Path.Type.encrypted)) {
@@ -107,11 +102,11 @@ public class B2LargeUploadService extends HttpUploadFeature<BaseB2Response, Mess
else {
partSize = this.partSize;
}
return this.upload(file, local, throttle, progress, streamListener, status, callback,
return this.upload(write, file, local, throttle, progress, streamListener, status, callback,
partSize < status.getLength() ? partSize : PreferencesFactory.get().getLong("b2.upload.largeobject.size.minimum"));
}
public BaseB2Response upload(final Path file, final Local local,
public BaseB2Response upload(final Write<BaseB2Response> write, final Path file, final Local local,
final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener, final TransferStatus status,
final ConnectionCallback callback, final Long partSize) throws BackgroundException {
final ThreadPool pool = ThreadPoolFactory.get("largeupload", concurrency);
@@ -175,7 +170,7 @@ public class B2LargeUploadService extends HttpUploadFeature<BaseB2Response, Mess
if(!skip) {
final long length = Math.min(Math.max((size / B2LargeUploadService.MAXIMUM_UPLOAD_PARTS), partSize), remaining);
// Submit to queue
parts.add(this.submit(pool, file, local, throttle, streamListener, status, fileId, partNumber, offset, length, callback));
parts.add(this.submit(pool, write, file, local, throttle, streamListener, status, fileId, partNumber, offset, length, callback));
log.debug("Part {} submitted with size {} and offset {}", partNumber, length, offset);
remaining -= length;
offset += length;
@@ -212,7 +207,7 @@ public class B2LargeUploadService extends HttpUploadFeature<BaseB2Response, Mess
}
}
private Future<B2UploadPartResponse> submit(final ThreadPool pool, final Path file, final Local local,
private Future<B2UploadPartResponse> submit(final ThreadPool pool, final Write<BaseB2Response> write, final Path file, final Local local,
final BandwidthThrottle throttle, final StreamListener listener,
final TransferStatus overall,
final String fileId, final int partNumber,
@@ -231,10 +226,10 @@ public class B2LargeUploadService extends HttpUploadFeature<BaseB2Response, Mess
requestParameters.put("fileId", fileId);
status.setParameters(requestParameters);
status.setHeader(overall.getHeader());
status.setChecksum(writer.checksum(file, status).compute(local.getInputStream(), status));
status.setChecksum(write.checksum(file, status).compute(local.getInputStream(), status));
status.setSegment(true);
status.setPart(partNumber);
return (B2UploadPartResponse) B2LargeUploadService.this.upload(file, local, throttle, counter, status, overall, status, callback);
return (B2UploadPartResponse) B2LargeUploadService.this.upload(write, file, local, throttle, counter, status, overall, status, callback);
}
}, overall, counter));
}
@@ -253,9 +248,4 @@ public class B2LargeUploadService extends HttpUploadFeature<BaseB2Response, Mess
return new Write.Append(false).withStatus(status);
}
@Override
public Upload<BaseB2Response> withWriter(final Write<BaseB2Response> writer) {
this.writer = writer;
return super.withWriter(writer);
}
}
@@ -21,38 +21,32 @@ import ch.cyberduck.core.ListService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.BackgroundException;
import java.util.Collections;
public class B2ListService implements ListService {
private final B2Session session;
private final B2VersionIdProvider fileid;
private Path bucket;
private final AttributedList<Path> buckets;
public B2ListService(final B2Session session, final B2VersionIdProvider fileid) {
this(session, fileid, AttributedList.EMPTY);
}
public B2ListService(final B2Session session, final B2VersionIdProvider fileid, final AttributedList<Path> buckets) {
this.session = session;
this.fileid = fileid;
this.buckets = buckets;
}
@Override
public AttributedList<Path> list(final Path directory, final ListProgressListener listener) throws BackgroundException {
if(directory.isRoot()) {
if(bucket != null) {
final AttributedList<Path> buckets = new AttributedList<>(Collections.singleton(bucket));
if(buckets.isEmpty()) {
return new B2BucketListService(session, fileid).list(directory, listener);
}
listener.chunk(directory, buckets);
return buckets;
}
return new B2BucketListService(session, fileid).list(directory, listener);
}
return new B2ObjectListService(session, fileid).list(directory, listener);
}
/**
* @param bucket When present, access is restricted to one bucket.
*/
public B2ListService withBucket(final Path bucket) {
this.bucket = bucket;
return this;
}
}
@@ -38,7 +38,7 @@ public class B2MetadataFeature implements Headers {
}
@Override
public Map<String, String> getDefault() {
public Map<String, String> getDefault(final Path file) {
return HostPreferencesFactory.get(session.getHost()).getMap("b2.metadata.default");
}
@@ -49,7 +49,7 @@ public class B2MoveFeature implements Move {
@Override
public Path move(final Path source, final Path target, final TransferStatus status, final Delete.Callback delete, final ConnectionCallback callback) throws BackgroundException {
final Path copy = proxy.copy(source, target, status.setLength(source.attributes().getSize()), callback, new DisabledStreamListener());
final Path copy = proxy.copy(source, target, status, callback, new DisabledStreamListener());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(source)), callback, delete);
return copy;
}
@@ -137,7 +137,7 @@ public class B2ObjectListService implements ListService {
if(StringUtils.isBlank(info.getFileId())) {
// Common prefix
final Path placeholder = new Path(directory.isDirectory() ? directory : directory.getParent(),
PathNormalizer.name(StringUtils.chomp(info.getFileName(), String.valueOf(Path.DELIMITER))),
PathNormalizer.name(StringUtils.removeEnd(info.getFileName(), String.valueOf(Path.DELIMITER))),
EnumSet.of(Path.Type.directory, Path.Type.placeholder));
objects.add(placeholder);
continue;
@@ -19,12 +19,13 @@ import ch.cyberduck.core.AbstractProtocol;
import ch.cyberduck.core.PathContainerService;
import ch.cyberduck.core.Protocol;
import ch.cyberduck.core.Scheme;
import ch.cyberduck.core.synchronization.ChecksumComparisonService;
import ch.cyberduck.core.synchronization.ComparisonService;
import ch.cyberduck.core.synchronization.DefaultComparisonService;
import ch.cyberduck.core.synchronization.VersionIdComparisonService;
import ch.cyberduck.core.text.DefaultLexicographicOrderComparator;
import java.util.Comparator;
import com.google.auto.service.AutoService;
@AutoService(Protocol.class)
@@ -97,7 +98,7 @@ public class B2Protocol extends AbstractProtocol {
return (T) new B2PathContainerService();
}
if(type == ComparisonService.class) {
return (T) new DefaultComparisonService(new VersionIdComparisonService(), ComparisonService.disabled);
return (T) new DefaultComparisonService(new ChecksumComparisonService(), ComparisonService.disabled);
}
return super.getFeature(type);
}
@@ -91,7 +91,7 @@ public class B2SearchFeature implements Search {
}
@Override
public EnumSet<Flags> features() {
public EnumSet<Flags> features(final Path workdir) {
return EnumSet.of(Flags.recursive);
}
}
@@ -15,7 +15,9 @@ package ch.cyberduck.core.b2;
* GNU General Public License for more details.
*/
import ch.cyberduck.core.AttributedList;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostKeyCallback;
import ch.cyberduck.core.ListService;
@@ -27,22 +29,19 @@ import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.*;
import ch.cyberduck.core.http.CustomServiceUnavailableRetryStrategy;
import ch.cyberduck.core.http.ExecutionCountServiceUnavailableRetryStrategy;
import ch.cyberduck.core.http.HttpSession;
import ch.cyberduck.core.preferences.HostPreferences;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.proxy.ProxyFinder;
import ch.cyberduck.core.ssl.X509KeyManager;
import ch.cyberduck.core.ssl.X509TrustManager;
import ch.cyberduck.core.threading.CancelCallback;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.EnumSet;
import java.util.stream.Collectors;
import synapticloop.b2.B2ApiClient;
import synapticloop.b2.exception.B2ApiException;
@@ -51,12 +50,10 @@ import synapticloop.b2.response.B2AuthorizeAccountResponse;
public class B2Session extends HttpSession<B2ApiClient> {
private static final Logger log = LogManager.getLogger(B2Session.class);
private final HostPreferences preferences = HostPreferencesFactory.get(host);
private B2ErrorResponseInterceptor retryHandler;
private final B2VersionIdProvider fileid = new B2VersionIdProvider(this);
private final B2ListService listService = new B2ListService(this, fileid);
private final AttributedList<Path> buckets = new AttributedList<>();
public B2Session(final Host host, final X509TrustManager trust, final X509KeyManager key) {
super(host, trust, key);
@@ -66,21 +63,22 @@ public class B2Session extends HttpSession<B2ApiClient> {
protected B2ApiClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) {
final HttpClientBuilder configuration = builder.build(proxy, this, prompt);
configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(
host, new ExecutionCountServiceUnavailableRetryStrategy(retryHandler = new B2ErrorResponseInterceptor(this, fileid))));
host, retryHandler = new B2ErrorResponseInterceptor(this, fileid)));
configuration.addInterceptorLast(retryHandler);
return new B2ApiClient(configuration.build());
}
@Override
public void logout() throws BackgroundException {
public void disconnect() throws BackgroundException {
try {
fileid.clear();
client.close();
}
catch(IOException e) {
throw new DefaultIOExceptionMappingService().map(e);
}
finally {
fileid.clear();
super.disconnect();
}
}
@@ -92,10 +90,10 @@ public class B2Session extends HttpSession<B2ApiClient> {
// Save tokens for 401 error response when expired
final B2AuthorizeAccountResponse response = client.authenticate(accountId, applicationKey);
// When present, access is restricted to one bucket
if(StringUtils.isNotBlank(response.getBucketId())) {
final PathAttributes attributes = new PathAttributes();
attributes.setVersionId(response.getBucketId());
listService.withBucket(new Path(PathNormalizer.normalize(response.getBucketName()), EnumSet.of(Path.Type.directory, Path.Type.volume), attributes));
if(!response.getBuckets().isEmpty()) {
buckets.addAll(response.getBuckets().entrySet().stream().map(entry ->
new Path(PathNormalizer.normalize(entry.getValue()), EnumSet.of(Path.Type.directory, Path.Type.volume),
new DefaultPathAttributes().setVersionId(entry.getKey()))).collect(Collectors.toSet()));
}
retryHandler.setTokens(accountId, applicationKey, response.getAuthorizationToken());
if(preferences.getBoolean("b2.upload.largeobject.auto")) {
@@ -120,7 +118,7 @@ public class B2Session extends HttpSession<B2ApiClient> {
@SuppressWarnings("unchecked")
public <T> T _getFeature(final Class<T> type) {
if(type == ListService.class) {
return (T) listService;
return (T) new B2ListService(this, fileid, buckets);
}
if(type == Touch.class) {
return (T) new B2TouchFeature(this, fileid);
@@ -162,7 +160,7 @@ public class B2Session extends HttpSession<B2ApiClient> {
return (T) new B2AttributesFinderFeature(this, fileid);
}
if(type == AclPermission.class) {
return (T) new B2BucketTypeFeature(this, fileid);
return (T) new B2BucketTypeAclFeature(this, fileid);
}
if(type == Location.class) {
return (T) new B2BucketTypeFeature(this, fileid);
@@ -17,7 +17,6 @@ package ch.cyberduck.core.b2;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.http.HttpUploadFeature;
import ch.cyberduck.core.io.Checksum;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
@@ -37,8 +36,7 @@ public class B2SingleUploadService extends HttpUploadFeature<BaseB2Response, Mes
private final B2Session session;
public B2SingleUploadService(final B2Session session, final Write<BaseB2Response> writer) {
super(writer);
public B2SingleUploadService(final B2Session session) {
this.session = session;
}
@@ -37,7 +37,6 @@ public class B2ThresholdUploadService implements Upload<BaseB2Response> {
private final B2Session session;
private final B2VersionIdProvider fileid;
private Write<BaseB2Response> writer;
private final Long threshold;
public B2ThresholdUploadService(final B2Session session, final B2VersionIdProvider fileid) {
@@ -47,35 +46,28 @@ public class B2ThresholdUploadService implements Upload<BaseB2Response> {
public B2ThresholdUploadService(final B2Session session, final B2VersionIdProvider fileid, final Long threshold) {
this.session = session;
this.fileid = fileid;
this.writer = new B2WriteFeature(session, fileid);
this.threshold = threshold;
}
@Override
public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException {
if(this.threshold(status)) {
return new B2LargeUploadService(session, fileid, writer).append(file, status);
return new B2LargeUploadService(session, fileid).append(file, status);
}
return new Write.Append(false).withStatus(status);
}
@Override
public BaseB2Response upload(final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener,
public BaseB2Response upload(final Write<BaseB2Response> write, final Path file, final Local local, final BandwidthThrottle throttle, final ProgressListener progress, final StreamListener streamListener,
final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
if(this.threshold(status)) {
return new B2LargeUploadService(session, fileid, writer).upload(file, local, throttle, progress, streamListener, status, callback);
return new B2LargeUploadService(session, fileid).upload(write, file, local, throttle, progress, streamListener, status, callback);
}
else {
return new B2SingleUploadService(session, writer).upload(file, local, throttle, progress, streamListener, status, callback);
return new B2SingleUploadService(session).upload(write, file, local, throttle, progress, streamListener, status, callback);
}
}
@Override
public Upload<BaseB2Response> withWriter(final Write<BaseB2Response> writer) {
this.writer = writer;
return this;
}
protected boolean threshold(final TransferStatus status) {
if(status.getLength() > threshold) {
if(status.getLength() > HostPreferencesFactory.get(session.getHost()).getLong("b2.upload.largeobject.size")) {
@@ -19,6 +19,7 @@ import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.shared.DefaultTouchFeature;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -31,12 +32,12 @@ import synapticloop.b2.response.BaseB2Response;
public class B2TouchFeature extends DefaultTouchFeature<BaseB2Response> {
public B2TouchFeature(final B2Session session, final B2VersionIdProvider fileid) {
super(new B2WriteFeature(session, fileid));
super(session);
}
@Override
public Path touch(final Path file, final TransferStatus status) throws BackgroundException {
return super.touch(file, status.setChecksum(write.checksum(file, status).compute(new NullInputStream(0L), status)));
public Path touch(final Write<BaseB2Response> writer, final Path file, final TransferStatus status) throws BackgroundException {
return super.touch(writer, file, status.setChecksum(writer.checksum(file, status).compute(new NullInputStream(0L), status)));
}
@Override
@@ -77,7 +77,7 @@ public class B2VersioningFeature implements Versioning {
return new B2ObjectListService(session, fileid).list(file, listener).filter(new NullFilter<Path>() {
@Override
public boolean accept(final Path f) {
return f.attributes().isDuplicate();
return f.attributes().isDuplicate() || f.attributes().isTrashed();
}
});
}
@@ -55,7 +55,7 @@ public class B2AttributesFinderFeatureTest extends AbstractB2Test {
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final long timestamp = System.currentTimeMillis();
new B2TouchFeature(session, fileid).touch(test, new TransferStatus().setModified(timestamp));
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), test, new TransferStatus().setModified(timestamp));
final B2AttributesFinderFeature f = new B2AttributesFinderFeature(session, fileid);
final PathAttributes attributes = f.find(test);
assertEquals(0L, attributes.getSize());
@@ -70,10 +70,10 @@ public class B2AttributesFinderFeatureTest extends AbstractB2Test {
public void testHideMarker() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final long timestamp = System.currentTimeMillis();
final TransferStatus status = new TransferStatus().setModified(timestamp);
final Path test = new B2TouchFeature(session, fileid).touch(new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status);
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), status);
assertNotNull(status.getResponse().getVersionId());
assertNotNull(test.attributes().getVersionId());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(test).withAttributes(PathAttributes.EMPTY)), new DisabledLoginCallback(), new Delete.DisabledCallback());
@@ -94,7 +94,7 @@ public class B2AttributesFinderFeatureTest extends AbstractB2Test {
public void testFindDirectory() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final B2AttributesFinderFeature f = new B2AttributesFinderFeature(session, fileid);
final PathAttributes attributes = f.find(directory);
assertNotNull(attributes);
@@ -113,7 +113,7 @@ public class B2AttributesFinderFeatureTest extends AbstractB2Test {
@Test
public void testFindBucket() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final B2AttributesFinderFeature f = new B2AttributesFinderFeature(session, fileid);
final PathAttributes attributes = f.find(bucket);
assertNotNull(attributes);
@@ -145,8 +145,8 @@ public class B2AttributesFinderFeatureTest extends AbstractB2Test {
public void testChangedFileId() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final String latestnodeid = test.attributes().getVersionId();
assertNotNull(latestnodeid);
// Assume previously seen but changed on server
@@ -41,7 +41,7 @@ public class B2AuthorizedUrlProviderTest extends AbstractB2Test {
final Path bucket = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2TouchFeature(session, fileid).touch(test, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), test, new TransferStatus());
final B2AuthorizedUrlProvider provider = new B2AuthorizedUrlProvider(session, fileid);
assertFalse(provider.isSupported(bucket, Share.Type.download));
final DescriptiveUrl url = provider.toDownloadUrl(test, Share.Sharee.world, null, new DisabledPasswordCallback());
@@ -36,7 +36,7 @@ public class B2BucketTypeFeatureTest extends AbstractB2Test {
@Test
public void testAllPrivate() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
new TransferStatus());
assertEquals("allPrivate", new B2BucketTypeFeature(session, fileid).getLocation(bucket).getIdentifier());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(bucket), new DisabledLoginCallback(), new Delete.DisabledCallback());
@@ -45,7 +45,7 @@ public class B2BucketTypeFeatureTest extends AbstractB2Test {
@Test
public void testAllPublic() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
new TransferStatus().setRegion("allPublic"));
assertEquals("allPublic", new B2BucketTypeFeature(session, fileid).getLocation(bucket).getIdentifier());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(bucket), new DisabledLoginCallback(), new Delete.DisabledCallback());
@@ -44,7 +44,7 @@ public class B2CopyFeatureTest extends AbstractB2Test {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final String name = new AlphanumericRandomStringService().random();
final Path test = new B2TouchFeature(session, fileid).touch(new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(test));
final B2CopyFeature feature = new B2CopyFeature(session, fileid);
assertThrows(UnsupportedException.class, () -> feature.preflight(container, Optional.of(test)));
@@ -66,10 +66,10 @@ public class B2CopyFeatureTest extends AbstractB2Test {
public void testCopyDifferentBucket() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path target = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
final Path target = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
new TransferStatus());
final String name = new AlphanumericRandomStringService().random();
final Path test = new B2TouchFeature(session, fileid).touch(new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(test));
final Path copy = new B2CopyFeature(session, fileid).copy(test, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener());
assertTrue(new B2FindFeature(session, fileid).find(new Path(container, name, EnumSet.of(Path.Type.file))));
@@ -81,10 +81,10 @@ public class B2CopyFeatureTest extends AbstractB2Test {
public void testCopyToExistingFile() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final String name = new AlphanumericRandomStringService().random();
final Path test = new B2TouchFeature(session, fileid).touch(new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path copy = new B2TouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path copy = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(new Path(folder, name, EnumSet.of(Path.Type.file))));
assertTrue(new B2FindFeature(session, fileid).find(copy));
new B2CopyFeature(session, fileid).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener());
@@ -48,10 +48,10 @@ public class B2DeleteFeatureTest extends AbstractB2Test {
@Test
public void testDeleteFileHide() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final TransferStatus status = new TransferStatus();
new B2TouchFeature(session, fileid).touch(test, status);
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), test, status);
final String versionId = status.getResponse().getVersionId();
assertNotNull(versionId);
// Hide
@@ -67,9 +67,9 @@ public class B2DeleteFeatureTest extends AbstractB2Test {
@Test
public void testDelete() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), "1"), EnumSet.of(Path.Type.file));
new B2TouchFeature(session, fileid).touch(file, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), file, new TransferStatus());
new B2DeleteFeature(session, fileid).delete(Arrays.asList(bucket, file), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new B2FindFeature(session, fileid).find(file));
assertFalse(new B2FindFeature(session, fileid).find(bucket));
@@ -78,9 +78,9 @@ public class B2DeleteFeatureTest extends AbstractB2Test {
@Test
public void testHideAlreadyDeleted() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), "1"), EnumSet.of(Path.Type.file));
new B2TouchFeature(session, fileid).touch(file, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), file, new TransferStatus());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new B2FindFeature(session, fileid).find(file));
try {
@@ -96,9 +96,9 @@ public class B2DeleteFeatureTest extends AbstractB2Test {
@Test
public void testHide() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), "1"), EnumSet.of(Path.Type.file));
new B2TouchFeature(session, fileid).touch(file, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), file, new TransferStatus());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(PathAttributes.EMPTY)), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new B2FindFeature(session, fileid).find(file));
assertFalse(new DefaultFindFeature(session).find(file));
@@ -109,8 +109,8 @@ public class B2DeleteFeatureTest extends AbstractB2Test {
@Test
public void testDeletePlaceholder() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), "1"), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, String.format("%s %s", new AlphanumericRandomStringService().random(), "1"), EnumSet.of(Path.Type.directory)), new TransferStatus());
new B2DeleteFeature(session, fileid).delete(Arrays.asList(bucket, directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new B2FindFeature(session, fileid).find(directory));
assertFalse(new B2FindFeature(session, fileid).find(bucket));
@@ -43,8 +43,8 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final B2DirectoryFeature feature = new B2DirectoryFeature(session, fileid);
assertTrue(feature.isSupported(bucket.getParent(), bucket.getName()));
feature.mkdir(bucket, new TransferStatus());
assertThrows(ConflictException.class, () -> feature.mkdir(bucket, new TransferStatus()));
feature.mkdir(new B2WriteFeature(session, fileid), bucket, new TransferStatus());
assertThrows(ConflictException.class, () -> feature.mkdir(new B2WriteFeature(session, fileid), bucket, new TransferStatus()));
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(bucket), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@@ -52,7 +52,7 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
public void testBucketExists() throws Exception {
final Path bucket = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
try {
new B2DirectoryFeature(session, new B2VersionIdProvider(session)).mkdir(bucket, new TransferStatus());
new B2DirectoryFeature(session, new B2VersionIdProvider(session)).mkdir(new B2WriteFeature(session, new B2VersionIdProvider(session)), bucket, new TransferStatus());
}
catch(ConflictException e) {
assertEquals("Bucket name is already in use. Please contact your web hosting service provider for assistance.", e.getDetail());
@@ -67,7 +67,7 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
assertFalse(new B2DirectoryFeature(session, fileid).isSupported(bucket.getParent(), bucket.getName()));
try {
new B2DirectoryFeature(session, fileid).mkdir(bucket, new TransferStatus());
new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), bucket, new TransferStatus());
}
catch(InteroperabilityException e) {
assertEquals("Invalid characters in bucketName: must be alphanumeric or '-'. Please contact your web hosting service provider for assistance.", e.getDetail());
@@ -81,7 +81,7 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
final Path bucket = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final String filename = new AlphanumericRandomStringService().random();
final Path directory = new B2DirectoryFeature(session, fileid, new B2WriteFeature(session, fileid)).mkdir(new Path(bucket, filename, EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, filename, EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(directory.getType().contains(Path.Type.placeholder));
assertTrue(new B2FindFeature(session, fileid).find(directory));
assertTrue(new DefaultFindFeature(session).find(directory));
@@ -102,8 +102,8 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
final long timestamp = 1509959502930L;
status.setModified(timestamp);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path directory = new B2DirectoryFeature(session, fileid, new B2WriteFeature(session, fileid)).mkdir(
new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status);
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(
new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), status);
assertEquals(timestamp, new B2AttributesFinderFeature(session, fileid).find(directory).getModificationDate());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@@ -46,7 +46,7 @@ public class B2FindFeatureTest extends AbstractB2Test {
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path file = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2TouchFeature(session, fileid).touch(file, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), file, new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(file));
assertFalse(new B2FindFeature(session, fileid).find(new Path(bucket, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file))));
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback());
@@ -70,7 +70,7 @@ public class B2FindFeatureTest extends AbstractB2Test {
assertTrue(new B2FindFeature(session, fileid).find(container));
final String prefix = new AlphanumericRandomStringService().random();
final Path intermediate = new Path(container, prefix, EnumSet.of(Path.Type.directory));
final Path test = new B2TouchFeature(session, fileid).touch(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(test));
assertFalse(new B2FindFeature(session, fileid).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory))));
assertTrue(new B2FindFeature(session, fileid).find(intermediate));
@@ -71,7 +71,7 @@ public class B2LargeCopyFeatureTest extends AbstractB2Test {
public void testCopyDifferentBucket() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path target = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
final Path target = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)),
new TransferStatus());
final String name = new AlphanumericRandomStringService().random();
final byte[] content = RandomUtils.nextBytes(6 * 1000 * 1000);
@@ -96,14 +96,14 @@ public class B2LargeCopyFeatureTest extends AbstractB2Test {
public void testCopyToExistingFile() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final String name = new AlphanumericRandomStringService().random();
final byte[] content = RandomUtils.nextBytes(6 * 1000 * 1000);
final Path test = new Path(folder, name, EnumSet.of(Path.Type.file));
final OutputStream out = new B2WriteFeature(session, fileid).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback());
new StreamCopier(new TransferStatus(), new TransferStatus().setLength(content.length)).transfer(new ByteArrayInputStream(content), out);
out.close();
final Path copy = new B2TouchFeature(session, fileid).touch(new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path copy = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(new Path(folder, name, EnumSet.of(Path.Type.file))));
assertTrue(new B2FindFeature(session, fileid).find(copy));
new B2LargeCopyFeature(session, fileid, 5 * 1000L * 1000L, 1).copy(test, copy,
@@ -80,10 +80,9 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
status.setChecksum(checksum);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final B2LargeUploadService upload = new B2LargeUploadService(session, fileid,
new B2WriteFeature(session, fileid), 5 * 1000L * 1000L, 5);
final B2LargeUploadService upload = new B2LargeUploadService(session, fileid, 5 * 1000L * 1000L, 5);
upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
upload.upload(new B2WriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
status, new DisabledConnectionCallback());
final PathAttributes attr = new B2AttributesFinderFeature(session, fileid).find(test);
assertNotEquals(Checksum.NONE, attr.getChecksum());
@@ -117,18 +116,17 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
final AtomicBoolean interrupt = new AtomicBoolean();
final BytecountStreamListener count = new BytecountStreamListener();
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final B2LargeUploadService service = new B2LargeUploadService(session, fileid,
new B2WriteFeature(session, fileid), 5 * 1000L * 1000L, 1) {
final B2LargeUploadService service = new B2LargeUploadService(session, fileid, 5 * 1000L * 1000L, 1) {
@Override
public BaseB2Response upload(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final StreamCancelation cancel, final StreamProgress progress, final ConnectionCallback callback) throws BackgroundException {
public BaseB2Response upload(final Write<BaseB2Response> write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final StreamCancelation cancel, final StreamProgress progress, final ConnectionCallback callback) throws BackgroundException {
if(!interrupt.get()) {
throw new ConnectionTimeoutException("Test");
}
return super.upload(file, local, throttle, listener, status, cancel, progress, callback);
return super.upload(write, file, local, throttle, listener, status, cancel, progress, callback);
}
};
try {
service.upload(test, new Local(System.getProperty("java.io.tmpdir"), name), new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback());
service.upload(new B2WriteFeature(session, fileid), test, new Local(System.getProperty("java.io.tmpdir"), name), new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback());
}
catch(BackgroundException e) {
// Expected
@@ -142,7 +140,7 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
assertTrue(resume.append);
assertEquals(0L, resume.offset, 0L);
final TransferStatus append = new TransferStatus().setAppend(true).setLength(content.length);
service.upload(test, local,
service.upload(new B2WriteFeature(session, fileid), test, local,
new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(), append,
new DisabledLoginCallback());
assertEquals(content.length, append.getResponse().getSize());
@@ -171,21 +169,20 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
status.setLength(content.length);
final AtomicBoolean interrupt = new AtomicBoolean();
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final B2LargeUploadService feature = new B2LargeUploadService(session, fileid,
new B2WriteFeature(session, fileid), 5 * 1000L * 1000L, 1) {
final B2LargeUploadService feature = new B2LargeUploadService(session, fileid, 5 * 1000L * 1000L, 1) {
@Override
public BaseB2Response upload(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final StreamCancelation cancel, final StreamProgress progress, final ConnectionCallback callback) throws BackgroundException {
public BaseB2Response upload(final Write<BaseB2Response> write, final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener, final TransferStatus status, final StreamCancelation cancel, final StreamProgress progress, final ConnectionCallback callback) throws BackgroundException {
if(!interrupt.get()) {
if(status.getOffset() >= 5L * 1000L * 1000L) {
throw new ConnectionTimeoutException("Test");
}
}
return super.upload(file, local, throttle, listener, status, cancel, progress, callback);
return super.upload(write, file, local, throttle, listener, status, cancel, progress, callback);
}
};
final BytecountStreamListener count = new BytecountStreamListener();
try {
feature.upload(test, new Local(System.getProperty("java.io.tmpdir"), name), new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback());
feature.upload(new B2WriteFeature(session, fileid), test, new Local(System.getProperty("java.io.tmpdir"), name), new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, status, new DisabledLoginCallback());
}
catch(BackgroundException e) {
// Expected
@@ -202,7 +199,7 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
assertTrue(new B2FindFeature(session, fileid).find(upload));
assertEquals(5 * 1000L * 1000L, new B2AttributesFinderFeature(session, fileid).find(upload).getSize(), 0L);
final TransferStatus append = new TransferStatus().setAppend(true).setLength(2L * 1000L * 1000L).setOffset(5 * 1000L * 1000L);
feature.upload(test, local,
feature.upload(new B2WriteFeature(session, fileid), test, local,
new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, append,
new DisabledLoginCallback());
assertEquals(6 * 1000L * 1000L, count.getSent());
@@ -38,7 +38,7 @@ public class B2LifecycleFeatureTest extends AbstractB2Test {
public void testSetConfiguration() throws Exception {
final Path bucket = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2DirectoryFeature(session, fileid).mkdir(bucket, new TransferStatus());
new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), bucket, new TransferStatus());
assertEquals(LifecycleConfiguration.empty(), new B2LifecycleFeature(session, fileid).getConfiguration(bucket));
new B2LifecycleFeature(session, fileid).setConfiguration(bucket, new LifecycleConfiguration(1, 30));
final LifecycleConfiguration configuration = new B2LifecycleFeature(session, fileid).getConfiguration(bucket);
@@ -19,7 +19,6 @@ import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.DisabledConnectionCallback;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.synchronization.Comparison;
import ch.cyberduck.core.synchronization.ComparisonService;
@@ -42,15 +41,15 @@ public class B2MoveFeatureTest extends AbstractB2Test {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final String name = new AlphanumericRandomStringService().random();
final Path test = new B2TouchFeature(session, fileid).touch(new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(container, name, EnumSet.of(Path.Type.file)), new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(test));
final Path target = new B2MoveFeature(session, fileid).move(test,
new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback());
assertNotEquals(test.attributes().getVersionId(), target.attributes().getVersionId());
assertFalse(new B2FindFeature(session, fileid).find(new Path(container, name, EnumSet.of(Path.Type.file))));
assertTrue(new B2FindFeature(session, fileid).find(target));
final PathAttributes targetAttr = new B2AttributesFinderFeature(session, fileid).find(target);
assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, target.attributes(), targetAttr));
assertEquals(target.attributes(), new B2AttributesFinderFeature(session, fileid).find(target));
assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, test.attributes(), target.attributes()));
new B2DeleteFeature(session, fileid).delete(Collections.<Path>singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
}
@@ -18,6 +18,7 @@ package ch.cyberduck.core.b2;
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.AsciiRandomStringService;
import ch.cyberduck.core.AttributedList;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.DisabledConnectionCallback;
import ch.cyberduck.core.DisabledListProgressListener;
import ch.cyberduck.core.DisabledLoginCallback;
@@ -66,7 +67,7 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListEmptyFolder() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final AtomicBoolean callback = new AtomicBoolean();
assertTrue(new B2ObjectListService(session, fileid).list(folder, new DisabledListProgressListener() {
@Override
@@ -83,8 +84,8 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListFolderNameDot() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, ".", EnumSet.of(Path.Type.directory)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, ".", EnumSet.of(Path.Type.directory)), new TransferStatus());
assertEquals(".", folder.getName());
assertTrue(new B2ObjectListService(session, fileid).list(folder, new DisabledListProgressListener()).isEmpty());
assertTrue(new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener()).contains(folder));
@@ -95,9 +96,9 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListFolderNameDotDot() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path test = new B2DirectoryFeature(session, fileid).mkdir(new Path(folder, "..", EnumSet.of(Path.Type.directory)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path test = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(folder, "..", EnumSet.of(Path.Type.directory)), new TransferStatus());
assertEquals("..", test.getName());
assertEquals(folder, test.getParent());
assertTrue(new B2ObjectListService(session, fileid).list(test, new DisabledListProgressListener()).isEmpty());
@@ -109,9 +110,9 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListFileNameDot() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new Path(folder, ".", EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder, ".", EnumSet.of(Path.Type.file)), new TransferStatus());
assertEquals(".", file.getName());
assertEquals(folder, file.getParent());
assertTrue(new B2ObjectListService(session, fileid).list(folder, new DisabledListProgressListener()).contains(file));
@@ -122,8 +123,8 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListFileNameDotDot() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new Path(bucket, "..", EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, "..", EnumSet.of(Path.Type.file)), new TransferStatus());
assertEquals(bucket, file.getParent());
assertEquals("..", file.getName());
assertTrue(new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener()).contains(file));
@@ -141,7 +142,7 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testList() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file));
final TransferStatus status = new TransferStatus().setLength(0L);
status.setChecksum(Checksum.parse("da39a3ee5e6b4b0d3255bfef95601890afd80709"));
@@ -162,9 +163,9 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListChunking() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file2 = new B2TouchFeature(session, fileid).touch(new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file2 = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final AttributedList<Path> list = new B2ObjectListService(session, fileid, 1, new VersioningConfiguration(true)).list(bucket, new DisabledListProgressListener());
assertTrue(list.contains(file1));
assertTrue(list.contains(file2));
@@ -174,7 +175,7 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
@Test
public void testListRevisions() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(
String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final String name = new AsciiRandomStringService().random();
final Path file = new Path(bucket, name, EnumSet.of(Path.Type.file));
@@ -216,12 +217,13 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
assertEquals(Long.valueOf(1L), list.find(path -> path.attributes().isDuplicate()).attributes().getRevision());
}
// Add hide marker
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
{
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener());
assertEquals(3, list.size());
for(Path f : list) {
assertTrue(f.attributes().isDuplicate());
assertTrue(f.attributes().isTrashed() || f.attributes().isDuplicate());
assertFalse(f.attributes().isTrashed() && f.attributes().isDuplicate());
}
assertTrue(new B2ObjectListService(session, fileid, 1, VersioningConfiguration.empty()).list(bucket, new DisabledListProgressListener()).isEmpty());
}
@@ -236,7 +238,7 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
}
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener());
assertEquals(list, new B2VersioningFeature(session, fileid).list(file, new DisabledListProgressListener()));
final Path other = new B2TouchFeature(session, fileid).touch(new Path(bucket, name + new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path other = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, name + new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final AttributedList<Path> versions = new B2VersioningFeature(session, fileid).list(file, new DisabledListProgressListener());
assertEquals(list, versions);
assertFalse(versions.contains(other));
@@ -249,8 +251,8 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
@Test
public void testListRevisionsNoVersioning() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid,
new B2WriteFeature(session, fileid)).mkdir(new Path(
final Path bucket = new B2DirectoryFeature(session, fileid
).mkdir(new B2WriteFeature(session, fileid), new Path(
String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final String name = new AsciiRandomStringService().random();
final Path file = new Path(bucket, name, EnumSet.of(Path.Type.file));
@@ -315,12 +317,12 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListFolder() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder1 = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path folder2 = new B2DirectoryFeature(session, fileid).mkdir(new Path(folder1, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new Path(folder1, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder1 = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path folder2 = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(folder1, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder1, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertNotNull(file1.attributes().getVersionId());
final Path file2 = new B2TouchFeature(session, fileid).touch(new Path(folder2, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file2 = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder2, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
assertNotNull(file2.attributes().getVersionId());
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(folder1, new DisabledListProgressListener());
// Including
@@ -336,14 +338,14 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
assertSame(folder1, list.find(new SimplePathPredicate(file1)).getParent());
assertSame(folder1, list.find(new SimplePathPredicate(folder2)).getParent());
// Nullify version to add delete marker
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(file2).withAttributes(new PathAttributes(file2.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(file2).withAttributes(new DefaultPathAttributes(file2.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new B2ObjectListService(session, fileid, 1, VersioningConfiguration.empty()).list(folder2, new DisabledListProgressListener()).isEmpty());
assertFalse(new B2ObjectListService(session, fileid).list(folder2, new DisabledListProgressListener()).isEmpty());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(folder2), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new B2ObjectListService(session, fileid).list(folder2, new DisabledListProgressListener()).contains(file2));
assertThrows(NotfoundException.class, () -> new B2ObjectListService(session, fileid, 1, VersioningConfiguration.empty()).list(folder2, new DisabledListProgressListener()));
// Nullify version to add delete marker
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(file1).withAttributes(new PathAttributes(file1.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(file1).withAttributes(new DefaultPathAttributes(file1.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new B2ObjectListService(session, fileid, 1, VersioningConfiguration.empty()).list(folder1, new DisabledListProgressListener()).isEmpty());
assertTrue(new B2ObjectListService(session, fileid).list(folder1, new DisabledListProgressListener()).contains(folder2));
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(folder1), new DisabledLoginCallback(), new Delete.DisabledCallback());
@@ -361,9 +363,9 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testDisplayFolderInBucketMissingPlaceholder() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder1 = new Path(bucket, "1-d", EnumSet.of(Path.Type.directory));
final Path file1 = new B2TouchFeature(session, fileid).touch(new Path(folder1, "2-f", EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder1, "2-f", EnumSet.of(Path.Type.file)), new TransferStatus());
assertNotNull(file1.attributes().getVersionId());
{
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener());
@@ -373,7 +375,7 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
assertFalse(foundFolder1.attributes().isDuplicate());
}
// Nullify version to add delete marker
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(file1).withAttributes(new PathAttributes(file1.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(new Path(file1).withAttributes(new DefaultPathAttributes(file1.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new B2ObjectListService(session, fileid, 1, VersioningConfiguration.empty()).list(bucket, new DisabledListProgressListener()).isEmpty());
{
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener());
@@ -390,10 +392,10 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testDisplayFolderInFolderMissingPlaceholder() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder1 = new Path(bucket, "1-d", EnumSet.of(Path.Type.directory));
final Path folder2 = new Path(folder1, "2-d", EnumSet.of(Path.Type.directory));
final Path file = new B2TouchFeature(session, fileid).touch(new Path(folder2, "31-f", EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(folder2, "31-f", EnumSet.of(Path.Type.file)), new TransferStatus());
assertNotNull(file.attributes().getVersionId());
{
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener());
@@ -410,7 +412,7 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
assertFalse(foundFolder2.attributes().isDuplicate());
}
// Nullify version to add delete marker
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(new PathAttributes(file.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(null))), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertTrue(new DefaultFindFeature(session).find(folder1, new DisabledListProgressListener()));
assertTrue(new B2ObjectListService(session, fileid).list(folder1, new DisabledListProgressListener()).contains(folder2));
assertTrue(new DefaultFindFeature(session).find(folder2, new DisabledListProgressListener()));
@@ -426,10 +428,10 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testIdenticalNamingFileFolder() throws Exception {
final Path bucket = new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2DirectoryFeature(session, fileid).mkdir(bucket, new TransferStatus());
new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), bucket, new TransferStatus());
final String name = new AsciiRandomStringService().random();
final Path folder1 = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, name, EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path folder1 = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, name, EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path file1 = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(bucket, new DisabledListProgressListener());
assertEquals(2, list.size());
assertTrue(list.contains(file1));
@@ -441,13 +443,13 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
public void testListLexicographicSortOrderAssumption() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path directory = new B2DirectoryFeature(session, fileid).mkdir(
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
assertTrue(new B2ObjectListService(session, fileid).list(directory, new DisabledListProgressListener()).isEmpty());
final List<String> files = Arrays.asList(
"Z", "aa", "0a", "a", "AAA", "B", "~$a", ".c"
);
for(String f : files) {
new B2TouchFeature(session, fileid).touch(new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus());
}
files.sort(session.getHost().getProtocol().getListComparator());
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(directory, new IndexedListProgressListener() {
@@ -75,7 +75,7 @@ public class B2ReadFeatureTest extends AbstractB2Test {
out.close();
final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
assertEquals(-1L, local.attributes().getSize());
new DefaultDownloadFeature(new B2ReadFeature(session, fileid)).download(file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED),
new DefaultDownloadFeature(session).download(new B2ReadFeature(session, fileid), file, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED),
new DisabledStreamListener(), new TransferStatus() {
@Override
public TransferStatus setLength(long length) {
@@ -120,7 +120,7 @@ public class B2ReadFeatureTest extends AbstractB2Test {
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(bucket, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2TouchFeature(session, fileid).touch(test, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), test, new TransferStatus());
final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
final byte[] content = RandomUtils.nextBytes(1000);
@@ -128,8 +128,8 @@ public class B2ReadFeatureTest extends AbstractB2Test {
assertNotNull(out);
IOUtils.write(content, out);
out.close();
final BaseB2Response reply = new B2SingleUploadService(session, new B2WriteFeature(session, fileid)).upload(
test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
final BaseB2Response reply = new B2SingleUploadService(session).upload(
new B2WriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
new TransferStatus().setLength(content.length),
new DisabledConnectionCallback());
final TransferStatus status = new TransferStatus();
@@ -152,7 +152,7 @@ public class B2ReadFeatureTest extends AbstractB2Test {
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(bucket, UUID.randomUUID().toString(), EnumSet.of(Path.Type.file));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2TouchFeature(session, fileid).touch(test, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), test, new TransferStatus());
final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
final byte[] content = RandomUtils.nextBytes(1000);
@@ -160,8 +160,8 @@ public class B2ReadFeatureTest extends AbstractB2Test {
assertNotNull(out);
IOUtils.write(content, out);
out.close();
new B2SingleUploadService(session, new B2WriteFeature(session, fileid)).upload(
test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
new B2SingleUploadService(session).upload(
new B2WriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
new TransferStatus().setLength(content.length),
new DisabledConnectionCallback());
final TransferStatus status = new TransferStatus();
@@ -200,8 +200,8 @@ public class B2ReadFeatureTest extends AbstractB2Test {
public void testChangedNodeId() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final String latestnodeid = test.attributes().getVersionId();
assertNotNull(latestnodeid);
// Assume previously seen but changed on server
@@ -44,7 +44,7 @@ public class B2SearchFeatureTest extends AbstractB2Test {
final String name = new AlphanumericRandomStringService().random();
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path file = new B2TouchFeature(session, fileid).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final B2SearchFeature feature = new B2SearchFeature(session, fileid);
assertNotNull(feature.search(bucket, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
assertNotNull(feature.search(bucket, new SearchFilter(StringUtils.upperCase(name)), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
@@ -60,7 +60,7 @@ public class B2SearchFeatureTest extends AbstractB2Test {
final String name = new AlphanumericRandomStringService().random();
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path file = new B2TouchFeature(session, fileid).touch(new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final B2SearchFeature feature = new B2SearchFeature(session, fileid);
assertNotNull(feature.search(bucket, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
assertNotNull(feature.search(new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume)), new SearchFilter(StringUtils.substring(name, 2)), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
@@ -73,8 +73,8 @@ public class B2SearchFeatureTest extends AbstractB2Test {
final String name = new AlphanumericRandomStringService().random();
final Path bucket = new Path("test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path workdir = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final Path workdir = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(workdir, name, EnumSet.of(Path.Type.file)), new TransferStatus());
final B2SearchFeature feature = new B2SearchFeature(session, fileid);
assertNotNull(feature.search(workdir, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
assertNotNull(feature.search(workdir, new SearchFilter(StringUtils.substring(name, 2)), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
@@ -83,9 +83,9 @@ public class B2SearchFeatureTest extends AbstractB2Test {
assertNotNull(result.find(new SimplePathPredicate(file)));
assertEquals(workdir, result.get(result.indexOf(file)).getParent());
}
final Path subdir = new B2DirectoryFeature(session, fileid).mkdir(new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path subdir = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(workdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertNull(feature.search(subdir, new SearchFilter(name), new DisabledListProgressListener()).find(new SimplePathPredicate(file)));
final Path filesubdir = new B2TouchFeature(session, fileid).touch(new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path filesubdir = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(subdir, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
{
final AttributedList<Path> result = feature.search(workdir, new SearchFilter(filesubdir.getName()), new DisabledListProgressListener());
assertNotNull(result.find(new SimplePathPredicate(filesubdir)));
@@ -61,8 +61,8 @@ public class B2SingleUploadServiceTest extends AbstractB2Test {
final Checksum checksum = new SHA1ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus());
status.setChecksum(checksum);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final B2SingleUploadService upload = new B2SingleUploadService(session, new B2WriteFeature(session, fileid));
upload.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
final B2SingleUploadService upload = new B2SingleUploadService(session);
upload.upload(new B2WriteFeature(session, fileid), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), new DisabledStreamListener(),
status, new DisabledConnectionCallback());
assertEquals(checksum, new B2AttributesFinderFeature(session, fileid).find(test).getChecksum());
status.validate();
@@ -40,7 +40,7 @@ public class B2UrlProviderTest extends AbstractB2Test {
final Path bucket = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path test = new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
new B2TouchFeature(session, fileid).touch(test, new TransferStatus());
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), test, new TransferStatus());
final B2UrlProvider provider = new B2UrlProvider(session);
assertEquals(0, provider.toUrl(bucket).size());
assertEquals(1, provider.toUrl(test).size());
@@ -16,6 +16,7 @@ package ch.cyberduck.core.b2;
*/
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.DisabledPasswordCallback;
import ch.cyberduck.core.Path;
@@ -40,8 +41,8 @@ public class B2VersionIdProviderTest extends AbstractB2Test {
@Test
public void getFileIdFile() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path file = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final String versionId = fileid.getVersionId(file);
assertNotNull(versionId);
assertEquals(file.attributes().getVersionId(), versionId);
@@ -52,7 +53,7 @@ public class B2VersionIdProviderTest extends AbstractB2Test {
catch(NotfoundException e) {
// Expected
}
final PathAttributes duplicate = new PathAttributes();
final PathAttributes duplicate = new DefaultPathAttributes();
duplicate.setVersionId("d");
duplicate.setDuplicate(true);
fileid.cache(new Path(file).withAttributes(duplicate), "d");
@@ -63,8 +64,8 @@ public class B2VersionIdProviderTest extends AbstractB2Test {
@Test
public void getFileIdDirectory() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), new Path(bucket, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertNotNull(fileid.getVersionId(folder));
new B2DeleteFeature(session, fileid).delete(Arrays.asList(folder, bucket), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@@ -72,14 +73,14 @@ public class B2VersionIdProviderTest extends AbstractB2Test {
@Test
public void testFileIdCollision() throws Exception {
final B2VersionIdProvider idProvider = new B2VersionIdProvider(session);
final Path bucket = new B2DirectoryFeature(session, idProvider).mkdir(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path bucket = new B2DirectoryFeature(session, idProvider).mkdir(new B2WriteFeature(session, idProvider), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path path2R = new Path(bucket, "2R", EnumSet.of(Path.Type.directory));
final Path path33 = new Path(bucket, "33", EnumSet.of(Path.Type.directory));
final Directory directoryFeature = new B2DirectoryFeature(session, idProvider);
final Path path2RWithId = directoryFeature.mkdir(path2R, new TransferStatus());
final Path path2RWithId = directoryFeature.mkdir(new B2WriteFeature(session, idProvider), path2R, new TransferStatus());
assertNotNull(path2RWithId.attributes().getVersionId());
final Path path33WithId = directoryFeature.mkdir(path33, new TransferStatus());
final Path path33WithId = directoryFeature.mkdir(new B2WriteFeature(session, idProvider), path33, new TransferStatus());
assertNotNull(path33WithId.attributes().getVersionId());
assertNotEquals(path2RWithId.attributes().getVersionId(), path33WithId.attributes().getVersionId());
@@ -17,6 +17,7 @@ package ch.cyberduck.core.b2;
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.AttributedList;
import ch.cyberduck.core.DefaultPathAttributes;
import ch.cyberduck.core.DisabledConnectionCallback;
import ch.cyberduck.core.DisabledListProgressListener;
import ch.cyberduck.core.DisabledLoginCallback;
@@ -48,9 +49,9 @@ public class B2VersioningFeatureTest extends AbstractB2Test {
public void testRevert() throws Exception {
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path room = new B2DirectoryFeature(session, fileid).mkdir(
new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path ignored = new B2TouchFeature(session, fileid).touch(new Path(room, String.format("%s-2", test.getName()), EnumSet.of(Path.Type.file)), new TransferStatus());
new B2WriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path test = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(room, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path ignored = new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), new Path(room, String.format("%s-2", test.getName()), EnumSet.of(Path.Type.file)), new TransferStatus());
{
// Make sure there is another versioned copy of a file not to be included when listing
final byte[] content = RandomUtils.nextBytes(245);
@@ -60,7 +61,7 @@ public class B2VersioningFeatureTest extends AbstractB2Test {
new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out);
}
assertTrue(new B2FindFeature(session, fileid).find(ignored));
final PathAttributes initialAttributes = new PathAttributes(test.attributes());
final PathAttributes initialAttributes = new DefaultPathAttributes(test.attributes());
final String initialVersion = test.attributes().getVersionId();
final byte[] content = RandomUtils.nextBytes(32769);
final TransferStatus status = new TransferStatus();
@@ -28,11 +28,13 @@ import ch.cyberduck.core.b2.B2DirectoryFeature;
import ch.cyberduck.core.b2.B2FindFeature;
import ch.cyberduck.core.b2.B2ObjectListService;
import ch.cyberduck.core.b2.B2VersionIdProvider;
import ch.cyberduck.core.b2.B2WriteFeature;
import ch.cyberduck.core.cryptomator.features.CryptoListService;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
@@ -65,7 +67,7 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(test.getType().contains(Path.Type.placeholder));
final String versionId = test.attributes().getVersionId();
assertNotNull(versionId);
@@ -88,7 +90,8 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
cryptomator.getFeature(session, Write.class, new B2WriteFeature(session, fileid)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test));
cryptomator.getFeature(session, Delete.class, new B2DeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
@@ -34,6 +34,7 @@ import ch.cyberduck.core.b2.B2WriteFeature;
import ch.cyberduck.core.cryptomator.features.CryptoBulkFeature;
import ch.cyberduck.core.cryptomator.features.CryptoReadFeature;
import ch.cyberduck.core.cryptomator.features.CryptoUploadFeature;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.features.Find;
@@ -63,6 +64,8 @@ import java.util.EnumSet;
import java.util.Map;
import java.util.UUID;
import synapticloop.b2.response.BaseB2Response;
import static org.junit.Assert.*;
@Category(IntegrationTest.class)
@@ -78,9 +81,9 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final CryptoUploadFeature service = new CryptoUploadFeature<>(session,
new B2LargeUploadService(session, fileid, new B2WriteFeature(session, fileid), 5000000L, 5),
new B2WriteFeature(session, fileid), cryptomator);
final CryptoUploadFeature<BaseB2Response> service = new CryptoUploadFeature<>(session,
new B2LargeUploadService(session, fileid, 5000000L, 5),
cryptomator);
final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
final byte[] content = RandomUtils.nextBytes(5242885);
IOUtils.write(content, local.getOutputStream(false));
@@ -90,7 +93,7 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
writeStatus.setLength(content.length);
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final BytecountStreamListener counter = new BytecountStreamListener();
service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), counter, writeStatus, new DisabledConnectionCallback());
service.upload(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), counter, writeStatus, new DisabledConnectionCallback());
assertEquals(content.length, counter.getSent());
assertTrue(writeStatus.isComplete());
assertTrue(cryptomator.getFeature(session, Find.class, new B2FindFeature(session, fileid)).find(test));
@@ -118,15 +121,15 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
final byte[] content = RandomUtils.nextBytes(length);
writeStatus.setLength(content.length);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final CryptoBulkFeature<Map<TransferItem, TransferStatus>> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new B2DeleteFeature(session, fileid), cryptomator);
final CryptoBulkFeature<Map<TransferItem, TransferStatus>> bulk = new CryptoBulkFeature<>(session, new DisabledBulkFeature(), cryptomator);
bulk.pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(test), writeStatus), new DisabledConnectionCallback());
final CryptoUploadFeature service = new CryptoUploadFeature<>(session,
new B2LargeUploadService(session, fileid, new B2WriteFeature(session, fileid), 5000000L, 5),
new B2WriteFeature(session, fileid), cryptomator);
final CryptoUploadFeature<BaseB2Response> service = new CryptoUploadFeature<>(session,
new B2LargeUploadService(session, fileid, 5000000L, 5),
cryptomator);
final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
IOUtils.write(content, local.getOutputStream(false));
final BytecountStreamListener counter = new BytecountStreamListener();
service.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), counter, writeStatus, new DisabledConnectionCallback());
service.upload(new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), counter, writeStatus, new DisabledConnectionCallback());
assertEquals(content.length, counter.getSent());
assertTrue(writeStatus.isComplete());
assertTrue(cryptomator.getFeature(session, Find.class, new B2FindFeature(session, fileid)).find(test));
@@ -28,6 +28,7 @@ import ch.cyberduck.core.b2.B2WriteFeature;
import ch.cyberduck.core.cryptomator.features.CryptoListService;
import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.cryptomator.features.CryptoVersionIdProvider;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.shared.DefaultTouchFeature;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -61,9 +62,9 @@ public class B2ListServiceTest extends AbstractB2Test {
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
assertTrue(new CryptoListService(session, new B2ListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
final Path test = new CryptoTouchFeature<BaseB2Response>(session, new DefaultTouchFeature<BaseB2Response>(new B2WriteFeature(session, fileid)
), new B2WriteFeature(session, fileid), cryptomator).touch(
new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature<BaseB2Response>(
session), cryptomator).touch(
new CryptoWriteFeature<>(session, new B2WriteFeature(session, fileid), cryptomator), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus());
test.attributes().setVersionId(new CryptoVersionIdProvider(session, fileid, cryptomator).getVersionId(test));
assertEquals(test, new CryptoListService(session, new B2ListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).get(0));
cryptomator.getFeature(session, Delete.class, new B2DeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());

Some files were not shown because too many files have changed in this diff Show More